Commit e7d850dd authored by Kuninori Morimoto's avatar Kuninori Morimoto Committed by Mark Brown

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: default avatarKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent c7f69ab5
......@@ -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)
{
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)
static void rsnd_ssi_master_clk_stop(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;
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);
struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
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 (0 == ssi->usrcnt) {
/*
* disable all IRQ,
* and, wait all data was sent
*/
cr = ssi->cr_own |
ssi->cr_clk;
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);
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);
}
if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
return;
rsnd_mod_power_off(mod);
if (ssi->usrcnt > 1)
return;
ssi->chan = 0;
}
ssi->cr_clk = 0;
ssi->rate = 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;
/*
* disable all IRQ,
* and, wait all data was sent
*/
cr = ssi->cr_own |
ssi->cr_clk;
rsnd_ssi_hw_start(ssi, io);
rsnd_mod_write(mod, SSICR, cr | EN);
rsnd_ssi_status_check(mod, DIRQ);
rsnd_ssi_irq_enable(mod);
/*
* 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);
rsnd_ssi_record_error(ssi);
rsnd_ssi_hw_stop(io, ssi);
/*
* don't stop if not last user
* see also
* rsnd_ssi_start
* rsnd_ssi_interrupt
*/
if (ssi->usrcnt > 1)
return 0;
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment