summaryrefslogtreecommitdiffstats
path: root/sound/soc/sh/rcar/ssi.c
diff options
context:
space:
mode:
authorKuninori Morimoto2015-10-26 09:43:57 +0100
committerMark Brown2015-11-16 11:09:29 +0100
commite7d850dd10f4e61b728495a87ce096509843315f (patch)
tree79ada253671266c6554bb1a6682a27dba5dd7067 /sound/soc/sh/rcar/ssi.c
parentASoC: rsnd: use mod base common method on SSIU (diff)
downloadkernel-qcow2-linux-e7d850dd10f4e61b728495a87ce096509843315f.tar.gz
kernel-qcow2-linux-e7d850dd10f4e61b728495a87ce096509843315f.tar.xz
kernel-qcow2-linux-e7d850dd10f4e61b728495a87ce096509843315f.zip
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>
Diffstat (limited to 'sound/soc/sh/rcar/ssi.c')
-rw-r--r--sound/soc/sh/rcar/ssi.c301
1 files changed, 159 insertions, 142 deletions
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index bb08d6624d7d..3e814711f301 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -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;