// SPDX-License-Identifier: GPL-2.0
//
// Renesas R-Car SSIU/SSI support
//
// Copyright (C) 2013 Renesas Solutions Corp.
// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
//
// Based on fsi.c
// Kuninori Morimoto <morimoto.kuninori@renesas.com>
/*
* you can enable below define if you don't need
* SSI interrupt status debug message when debugging
* see rsnd_dbg_irq_status()
*
* #define RSND_DEBUG_NO_IRQ_STATUS 1
*/
#include <sound/simple_card_utils.h>
#include <linux/delay.h>
#include "rsnd.h"
#define RSND_SSI_NAME_SIZE 16
/*
* SSICR
*/
#define FORCE (1 << 31) /* Fixed */
#define DMEN (1 << 28) /* DMA Enable */
#define UIEN (1 << 27) /* Underflow Interrupt Enable */
#define OIEN (1 << 26) /* Overflow Interrupt Enable */
#define IIEN (1 << 25) /* Idle Mode Interrupt Enable */
#define DIEN (1 << 24) /* Data Interrupt Enable */
#define CHNL_4 (1 << 22) /* Channels */
#define CHNL_6 (2 << 22) /* Channels */
#define CHNL_8 (3 << 22) /* Channels */
#define DWL_MASK (7 << 19) /* Data Word Length mask */
#define DWL_8 (0 << 19) /* Data Word Length */
#define DWL_16 (1 << 19) /* Data Word Length */
#define DWL_18 (2 << 19) /* Data Word Length */
#define DWL_20 (3 << 19) /* Data Word Length */
#define DWL_22 (4 << 19) /* Data Word Length */
#define DWL_24 (5 << 19) /* Data Word Length */
#define DWL_32 (6 << 19) /* Data Word Length */
/*
* System word length
*/
#define SWL_16 (1 << 16) /* R/W System Word Length */
#define SWL_24 (2 << 16) /* R/W System Word Length */
#define SWL_32 (3 << 16) /* R/W System Word Length */
#define SCKD (1 << 15) /* Serial Bit Clock Direction */
#define SWSD (1 << 14) /* Serial WS Direction */
#define SCKP (1 << 13) /* Serial Bit Clock Polarity */
#define SWSP (1 << 12) /* Serial WS Polarity */
#define SDTA (1 << 10) /* Serial Data Alignment */
#define PDTA (1 << 9) /* Parallel Data Alignment */
#define DEL (1 << 8) /* Serial Data Delay */
#define CKDV(v) (v << 4) /* Serial Clock Division Ratio */
#define TRMD (1 << 1) /* Transmit/Receive Mode Select */
#define EN (1 << 0) /* SSI Module Enable */
/*
* SSISR
*/
#define UIRQ (1 << 27) /* Underflow Error Interrupt Status */
#define OIRQ (1 << 26) /* Overflow Error Interrupt Status */
#define IIRQ (1 << 25) /* Idle Mode Interrupt Status */
#define DIRQ (1 << 24) /* Data Interrupt Status Flag */
/*
* SSIWSR
*/
#define CONT (1 << 8) /* WS Continue Function */
#define WS_MODE (1 << 0) /* WS Mode */
#define SSI_NAME "ssi"
struct rsnd_ssi {
struct rsnd_mod mod;
u32 flags;
u32 cr_own;
u32 cr_clk;
u32 cr_mode;
u32 cr_en;
u32 wsr;
int chan;
int rate;
int irq;
unsigned int usrcnt;
/* for PIO */
int byte_pos;
int byte_per_period;
int next_period_byte;
};
/* flags */
#define RSND_SSI_CLK_PIN_SHARE (1 << 0)
#define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */
#define RSND_SSI_HDMI0 (1 << 2) /* for HDMI0 */
#define RSND_SSI_HDMI1 (1 << 3) /* for HDMI1 */
#define RSND_SSI_PROBED (1 << 4)
#define for_each_rsnd_ssi(pos, priv, i) \
for (i = 0; \
(i < rsnd_ssi_nr(priv)) && \
((pos) = ((struct rsnd_ssi *)(priv)->ssi + i)); \
i++)
#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id)
#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
#define rsnd_ssi_is_multi_slave(mod, io) \
(rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod)))
#define rsnd_ssi_is_run_mods(mod, io) \
(rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
#define rsnd_ssi_can_output_clk(mod) (!__rsnd_ssi_is_pin_sharing(mod))
int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
if (rsnd_flags_has(ssi, RSND_SSI_HDMI0))
return RSND_SSI_HDMI_PORT0;
if (rsnd_flags_has(ssi, RSND_SSI_HDMI1))
return RSND_SSI_HDMI_PORT1;
return 0;
}
int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
{
struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
int use_busif = 0;
if (!rsnd_ssi_is_dma_mode(mod))
return 0;
if (!(rsnd_flags_has(ssi, RSND_SSI_NO_BUSIF)))
use_busif = 1;
if (rsnd_io_to_mod_src(io))
use_busif = 1;
return use_busif;
}
int rsnd_ssi_get_busif(struct rsnd_dai_stream *io)
{
return 0; /* BUSIF0 only for now */
}
static void rsnd_ssi_stat