ASoC: rsnd: use mod base common method on SSI-parent
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Mon, 26 Oct 2015 08:43:57 +0000 (08:43 +0000)
committerMark Brown <broonie@kernel.org>
Mon, 16 Nov 2015 10:09:29 +0000 (10:09 +0000)
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>
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/ssi.c

index 81c789f..599dfb6 100644 (file)
@@ -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)
index bb08d66..3e81471 100644 (file)
@@ -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;