ASoC: rsnd: use mod base common method on SSI-parent
Renesas sound needs many devices (SSI/SSIU/SRC/CTU/MIX/DVC/CMD/AudioDMAC/AudioDMACpp). SSI/SRC/CTU/MIX/DVC are implemented as module. SSI parent, SSIU are implemented as part of SSI CMD is implemented as part of CTU/MIX/DVC AudioDMAC/AudioDMACpp are implemented as part of SSI/SRC It is nice sense that these all devices are implemented as mod. This patch makes SSI parent mod base common method Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
parent
c7f69ab536
commit
e7d850dd10
@ -211,6 +211,7 @@ enum rsnd_mod_type {
|
||||
RSND_MOD_CMD,
|
||||
RSND_MOD_SRC,
|
||||
RSND_MOD_SSIU,
|
||||
RSND_MOD_SSIP, /* SSI parent */
|
||||
RSND_MOD_SSI,
|
||||
RSND_MOD_MAX,
|
||||
};
|
||||
@ -339,6 +340,7 @@ struct rsnd_dai_stream {
|
||||
};
|
||||
#define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL)
|
||||
#define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI)
|
||||
#define rsnd_io_to_mod_ssip(io) rsnd_io_to_mod((io), RSND_MOD_SSIP)
|
||||
#define rsnd_io_to_mod_src(io) rsnd_io_to_mod((io), RSND_MOD_SRC)
|
||||
#define rsnd_io_to_mod_ctu(io) rsnd_io_to_mod((io), RSND_MOD_CTU)
|
||||
#define rsnd_io_to_mod_mix(io) rsnd_io_to_mod((io), RSND_MOD_MIX)
|
||||
|
@ -67,7 +67,9 @@ struct rsnd_ssi {
|
||||
|
||||
u32 cr_own;
|
||||
u32 cr_clk;
|
||||
u32 cr_mode;
|
||||
int chan;
|
||||
int rate;
|
||||
int err;
|
||||
unsigned int usrcnt;
|
||||
};
|
||||
@ -82,9 +84,9 @@ struct rsnd_ssi {
|
||||
#define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
|
||||
#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
|
||||
#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
|
||||
#define rsnd_ssi_parent(ssi) ((ssi)->parent)
|
||||
#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
|
||||
#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
|
||||
#define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
|
||||
#define rsnd_ssi_of_node(priv) \
|
||||
of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
|
||||
|
||||
@ -168,7 +170,9 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
|
||||
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
||||
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
||||
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
||||
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
|
||||
int j, ret;
|
||||
int ssi_clk_mul_table[] = {
|
||||
1, 2, 4, 8, 16, 6, 12,
|
||||
@ -176,6 +180,21 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
|
||||
unsigned int main_rate;
|
||||
unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);
|
||||
|
||||
if (!rsnd_rdai_is_clk_master(rdai))
|
||||
return 0;
|
||||
|
||||
if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
|
||||
return 0;
|
||||
|
||||
if (ssi->usrcnt > 1) {
|
||||
if (ssi->rate != rate) {
|
||||
dev_err(dev, "SSI parent/child should use same rate\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find best clock, and try to start ADG
|
||||
*/
|
||||
@ -193,6 +212,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
|
||||
ssi->cr_clk = FORCE | SWL_32 |
|
||||
SCKD | SWSD | CKDV(j);
|
||||
|
||||
ssi->rate = rate;
|
||||
|
||||
rsnd_mod_write(mod, SSIWSR, CONT);
|
||||
|
||||
dev_dbg(dev, "%s[%d] outputs %u Hz\n",
|
||||
rsnd_mod_name(mod),
|
||||
rsnd_mod_id(mod), rate);
|
||||
@ -205,113 +228,26 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
|
||||
static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
|
||||
struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
||||
|
||||
ssi->cr_clk = 0;
|
||||
rsnd_adg_ssi_clk_stop(mod);
|
||||
}
|
||||
|
||||
static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
|
||||
struct rsnd_dai_stream *io)
|
||||
{
|
||||
struct rsnd_priv *priv = rsnd_io_to_priv(io);
|
||||
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
||||
u32 cr_mode;
|
||||
u32 cr;
|
||||
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
|
||||
|
||||
if (0 == ssi->usrcnt) {
|
||||
rsnd_mod_power_on(mod);
|
||||
|
||||
if (rsnd_rdai_is_clk_master(rdai)) {
|
||||
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
|
||||
|
||||
if (ssi_parent)
|
||||
rsnd_ssi_hw_start(ssi_parent, io);
|
||||
else
|
||||
rsnd_ssi_master_clk_start(ssi, io);
|
||||
}
|
||||
}
|
||||
|
||||
if (rsnd_ssi_is_dma_mode(mod)) {
|
||||
cr_mode = UIEN | OIEN | /* over/under run */
|
||||
DMEN; /* DMA : enable DMA */
|
||||
} else {
|
||||
cr_mode = DIEN; /* PIO : enable Data interrupt */
|
||||
}
|
||||
|
||||
cr = ssi->cr_own |
|
||||
ssi->cr_clk |
|
||||
cr_mode |
|
||||
EN;
|
||||
|
||||
rsnd_mod_write(mod, SSICR, cr);
|
||||
|
||||
/* enable WS continue */
|
||||
if (rsnd_rdai_is_clk_master(rdai))
|
||||
rsnd_mod_write(mod, SSIWSR, CONT);
|
||||
|
||||
/* clear error status */
|
||||
rsnd_ssi_status_clear(mod);
|
||||
|
||||
ssi->usrcnt++;
|
||||
|
||||
dev_dbg(dev, "%s[%d] hw started\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
}
|
||||
|
||||
static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
|
||||
{
|
||||
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
||||
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
|
||||
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
u32 cr;
|
||||
|
||||
if (0 == ssi->usrcnt) {
|
||||
dev_err(dev, "%s called without starting\n", __func__);
|
||||
if (!rsnd_rdai_is_clk_master(rdai))
|
||||
return;
|
||||
}
|
||||
|
||||
ssi->usrcnt--;
|
||||
if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
|
||||
return;
|
||||
|
||||
if (0 == ssi->usrcnt) {
|
||||
/*
|
||||
* disable all IRQ,
|
||||
* and, wait all data was sent
|
||||
*/
|
||||
cr = ssi->cr_own |
|
||||
ssi->cr_clk;
|
||||
if (ssi->usrcnt > 1)
|
||||
return;
|
||||
|
||||
rsnd_mod_write(mod, SSICR, cr | EN);
|
||||
rsnd_ssi_status_check(mod, DIRQ);
|
||||
ssi->cr_clk = 0;
|
||||
ssi->rate = 0;
|
||||
|
||||
/*
|
||||
* disable SSI,
|
||||
* and, wait idle state
|
||||
*/
|
||||
rsnd_mod_write(mod, SSICR, cr); /* disabled all */
|
||||
rsnd_ssi_status_check(mod, IIRQ);
|
||||
|
||||
if (rsnd_rdai_is_clk_master(rdai)) {
|
||||
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
|
||||
|
||||
if (ssi_parent)
|
||||
rsnd_ssi_hw_stop(io, ssi_parent);
|
||||
else
|
||||
rsnd_ssi_master_clk_stop(ssi);
|
||||
}
|
||||
|
||||
rsnd_mod_power_off(mod);
|
||||
|
||||
ssi->chan = 0;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s[%d] hw stopped\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
rsnd_adg_ssi_clk_stop(mod);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -325,6 +261,18 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
|
||||
struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
|
||||
struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
|
||||
u32 cr;
|
||||
int ret;
|
||||
|
||||
ssi->usrcnt++;
|
||||
|
||||
rsnd_mod_power_on(mod);
|
||||
|
||||
ret = rsnd_ssi_master_clk_start(ssi, io);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (rsnd_ssi_is_parent(mod, io))
|
||||
return 0;
|
||||
|
||||
cr = FORCE;
|
||||
|
||||
@ -359,12 +307,24 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
|
||||
if (rsnd_io_is_play(io))
|
||||
cr |= TRMD;
|
||||
|
||||
/*
|
||||
* set ssi parameter
|
||||
*/
|
||||
ssi->cr_own = cr;
|
||||
|
||||
if (rsnd_ssi_is_dma_mode(mod)) {
|
||||
cr = UIEN | OIEN | /* over/under run */
|
||||
DMEN; /* DMA : enable DMA */
|
||||
} else {
|
||||
cr = DIEN; /* PIO : enable Data interrupt */
|
||||
}
|
||||
|
||||
ssi->cr_mode = cr;
|
||||
|
||||
ssi->err = -1; /* ignore 1st error */
|
||||
|
||||
/* clear error status */
|
||||
rsnd_ssi_status_clear(mod);
|
||||
|
||||
rsnd_ssi_irq_enable(mod);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -375,6 +335,9 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
struct device *dev = rsnd_priv_to_dev(priv);
|
||||
|
||||
if (rsnd_ssi_is_parent(mod, io))
|
||||
goto rsnd_ssi_quit_end;
|
||||
|
||||
if (ssi->err > 0)
|
||||
dev_warn(dev, "%s[%d] under/over flow err = %d\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod), ssi->err);
|
||||
@ -382,6 +345,19 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
|
||||
ssi->cr_own = 0;
|
||||
ssi->err = 0;
|
||||
|
||||
rsnd_ssi_irq_disable(mod);
|
||||
|
||||
rsnd_ssi_quit_end:
|
||||
rsnd_ssi_master_clk_stop(ssi, io);
|
||||
|
||||
rsnd_mod_power_off(mod);
|
||||
|
||||
ssi->usrcnt--;
|
||||
|
||||
if (ssi->usrcnt < 0)
|
||||
dev_err(dev, "%s[%d] usrcnt error\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -391,14 +367,13 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
|
||||
int chan = params_channels(params);
|
||||
|
||||
/*
|
||||
* Already working.
|
||||
* It will happen if SSI has parent/child connection.
|
||||
*/
|
||||
if (ssi->usrcnt) {
|
||||
if (ssi->usrcnt > 1) {
|
||||
/*
|
||||
* it is error if child <-> parent SSI uses
|
||||
* different channels.
|
||||
@ -407,11 +382,7 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* It will be removed on rsnd_ssi_hw_stop */
|
||||
ssi->chan = chan;
|
||||
if (ssi_parent)
|
||||
return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io,
|
||||
substream, params);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -432,15 +403,59 @@ static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi)
|
||||
return status;
|
||||
}
|
||||
|
||||
static int __rsnd_ssi_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
u32 cr;
|
||||
|
||||
cr = ssi->cr_own |
|
||||
ssi->cr_clk |
|
||||
ssi->cr_mode |
|
||||
EN;
|
||||
|
||||
rsnd_mod_write(mod, SSICR, cr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rsnd_ssi_start(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
/*
|
||||
* no limit to start
|
||||
* see also
|
||||
* rsnd_ssi_stop
|
||||
* rsnd_ssi_interrupt
|
||||
*/
|
||||
return __rsnd_ssi_start(mod, io, priv);
|
||||
}
|
||||
|
||||
static int __rsnd_ssi_stop(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
u32 cr;
|
||||
|
||||
rsnd_ssi_hw_start(ssi, io);
|
||||
/*
|
||||
* disable all IRQ,
|
||||
* and, wait all data was sent
|
||||
*/
|
||||
cr = ssi->cr_own |
|
||||
ssi->cr_clk;
|
||||
|
||||
rsnd_ssi_irq_enable(mod);
|
||||
rsnd_mod_write(mod, SSICR, cr | EN);
|
||||
rsnd_ssi_status_check(mod, DIRQ);
|
||||
|
||||
/*
|
||||
* disable SSI,
|
||||
* and, wait idle state
|
||||
*/
|
||||
rsnd_mod_write(mod, SSICR, cr); /* disabled all */
|
||||
rsnd_ssi_status_check(mod, IIRQ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -451,13 +466,16 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod,
|
||||
{
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
|
||||
rsnd_ssi_irq_disable(mod);
|
||||
/*
|
||||
* don't stop if not last user
|
||||
* see also
|
||||
* rsnd_ssi_start
|
||||
* rsnd_ssi_interrupt
|
||||
*/
|
||||
if (ssi->usrcnt > 1)
|
||||
return 0;
|
||||
|
||||
rsnd_ssi_record_error(ssi);
|
||||
|
||||
rsnd_ssi_hw_stop(io, ssi);
|
||||
|
||||
return 0;
|
||||
return __rsnd_ssi_stop(mod, io, priv);
|
||||
}
|
||||
|
||||
static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
|
||||
@ -505,8 +523,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
|
||||
dev_dbg(dev, "%s[%d] restart\n",
|
||||
rsnd_mod_name(mod), rsnd_mod_id(mod));
|
||||
|
||||
rsnd_ssi_stop(mod, io, priv);
|
||||
rsnd_ssi_start(mod, io, priv);
|
||||
__rsnd_ssi_stop(mod, io, priv);
|
||||
__rsnd_ssi_start(mod, io, priv);
|
||||
}
|
||||
|
||||
if (ssi->err > 1024) {
|
||||
@ -535,6 +553,27 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
|
||||
/*
|
||||
* SSI PIO
|
||||
*/
|
||||
static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
{
|
||||
if (!__rsnd_ssi_is_pin_sharing(mod))
|
||||
return;
|
||||
|
||||
switch (rsnd_mod_id(mod)) {
|
||||
case 1:
|
||||
case 2:
|
||||
rsnd_dai_connect(rsnd_ssi_mod_get(priv, 0), io, RSND_MOD_SSIP);
|
||||
break;
|
||||
case 4:
|
||||
rsnd_dai_connect(rsnd_ssi_mod_get(priv, 3), io, RSND_MOD_SSIP);
|
||||
break;
|
||||
case 8:
|
||||
rsnd_dai_connect(rsnd_ssi_mod_get(priv, 7), io, RSND_MOD_SSIP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
|
||||
struct rsnd_dai_stream *io,
|
||||
struct rsnd_priv *priv)
|
||||
@ -543,6 +582,8 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
|
||||
struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
|
||||
int ret;
|
||||
|
||||
rsnd_ssi_parent_attach(mod, io, priv);
|
||||
|
||||
ret = rsnd_ssiu_attach(io, mod);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@ -679,28 +720,6 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
|
||||
return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
|
||||
}
|
||||
|
||||
static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
|
||||
{
|
||||
struct rsnd_mod *mod = rsnd_mod_get(ssi);
|
||||
|
||||
if (!__rsnd_ssi_is_pin_sharing(mod))
|
||||
return;
|
||||
|
||||
switch (rsnd_mod_id(mod)) {
|
||||
case 1:
|
||||
case 2:
|
||||
ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0));
|
||||
break;
|
||||
case 4:
|
||||
ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 3));
|
||||
break;
|
||||
case 8:
|
||||
ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 7));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void rsnd_of_parse_ssi(struct platform_device *pdev,
|
||||
const struct rsnd_of_data *of_data,
|
||||
struct rsnd_priv *priv)
|
||||
@ -810,8 +829,6 @@ int rsnd_ssi_probe(struct platform_device *pdev,
|
||||
RSND_MOD_SSI, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rsnd_ssi_parent_setup(priv, ssi);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user