ASoC: rsnd: add Multi channel support
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Thu, 17 Dec 2015 03:00:10 +0000 (03:00 +0000)
committerMark Brown <broonie@kernel.org>
Tue, 22 Dec 2015 23:58:28 +0000 (23:58 +0000)
This patch adds Multi channel support on Renesas R-Car sound.
This patch is tested on Salvator-X board, but it can't use
Multi channel, because supported format is different between
codec chip and R-Car.
Thus, it was tested on board which doesn't mount codec chip,
with oscilloscope.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/ssi.c
sound/soc/sh/rcar/ssiu.c

index 162e94c8305c06cc523bea39b2b584ecbac8b4a2..8ee0fa91e4a067e622435d937378bee5288f6c53 100644 (file)
@@ -308,3 +308,21 @@ Example: simple sound card for TDM
                        sound-dai = <&xxx>;
                };
        };
+
+Example: simple sound card for Multi channel
+
+&rcar_sound {
+       pinctrl-0 = <&sound_pins &sound_clk_pins>;
+       pinctrl-names = "default";
+
+       /* Single DAI */
+       #sound-dai-cells = <0>;
+
+       status = "okay";
+
+       rcar_sound,dai {
+               dai0 {
+                       playback = <&ssi0 &ssi1 &ssi2 &src0 &dvc0>;
+               };
+       };
+};
index 7781cef634d4a3f27de5f0cd6c7c1b6f9dae9a8b..ca05a0a95a4dd95aa89d6528398c1c2bd43a4ab2 100644 (file)
@@ -215,7 +215,11 @@ int rsnd_get_slot_num(struct rsnd_dai_stream *io)
 int rsnd_get_slot_width(struct rsnd_dai_stream *io)
 {
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
-       int chan = runtime->channels / rsnd_get_slot_num(io);
+       int chan = runtime->channels;
+
+       /* Multi channel Mode */
+       if (rsnd_ssi_multi_slaves(io))
+               chan /= rsnd_get_slot_num(io);
 
        /* TDM Extend Mode needs 8ch */
        if (chan == 6)
index 7c5485e46fd711613ed5e54eb417c579b6433091..c7aee9e59e8642a8aae806139e94479d7e0a5e62 100644 (file)
@@ -226,6 +226,9 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
        const static struct rsnd_regmap_field_conf conf_ssiu[] = {
                RSND_GEN_S_REG(SSI_MODE0,       0x800),
                RSND_GEN_S_REG(SSI_MODE1,       0x804),
+               RSND_GEN_S_REG(SSI_MODE2,       0x808),
+               RSND_GEN_S_REG(SSI_CONTROL,     0x810),
+
                /* FIXME: it needs SSI_MODE2/3 in the future */
                RSND_GEN_M_REG(SSI_BUSIF_MODE,  0x0,    0x80),
                RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4,    0x80),
index f803e140e733d055fb7336ff3f340a67081fa20d..317dd793149a7feb5279e2f665385a564c594469 100644 (file)
@@ -47,6 +47,8 @@ enum rsnd_reg {
        RSND_REG_SSI_MODE,              /* Gen2 only */
        RSND_REG_SSI_MODE0,
        RSND_REG_SSI_MODE1,
+       RSND_REG_SSI_MODE2,
+       RSND_REG_SSI_CONTROL,
        RSND_REG_SSI_CTRL,              /* Gen2 only */
        RSND_REG_SSI_BUSIF_MODE,        /* Gen2 only */
        RSND_REG_SSI_BUSIF_ADINR,       /* Gen2 only */
@@ -181,7 +183,10 @@ enum rsnd_mod_type {
        RSND_MOD_CTU,
        RSND_MOD_CMD,
        RSND_MOD_SRC,
-       RSND_MOD_SSIP, /* SSI parent */
+       RSND_MOD_SSIM3,         /* SSI multi 3 */
+       RSND_MOD_SSIM2,         /* SSI multi 2 */
+       RSND_MOD_SSIM1,         /* SSI multi 1 */
+       RSND_MOD_SSIP,          /* SSI parent */
        RSND_MOD_SSI,
        RSND_MOD_SSIU,
        RSND_MOD_MAX,
@@ -542,6 +547,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
 int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
 int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
+u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io);
 
 #define rsnd_ssi_is_pin_sharing(io)    \
        __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
@@ -549,10 +555,9 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
 
 #define rsnd_ssi_of_node(priv)                                         \
        of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
-#define rsnd_parse_connect_ssi(rdai, playback, capture)                        \
-       rsnd_parse_connect_common(rdai, rsnd_ssi_mod_get,               \
-                                 rsnd_ssi_of_node(rsnd_rdai_to_priv(rdai)), \
-                                 playback, capture)
+void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
+                           struct device_node *playback,
+                           struct device_node *capture);
 
 /*
  *     R-Car SSIU
index 0b91692c5a667a75d74dc827743c472af73de8e0..7db05fdfb656a1c4e331baa21f0f5a12ed41284d 100644 (file)
@@ -96,6 +96,7 @@ struct rsnd_ssi {
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_ssi_mode_flags(p) ((p)->flags)
 #define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
+#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io))
 
 int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
 {
@@ -171,6 +172,41 @@ static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod)
        return 0;
 }
 
+u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
+{
+       struct rsnd_mod *mod;
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       struct rsnd_priv *priv = rsnd_io_to_priv(io);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       enum rsnd_mod_type types[] = {
+               RSND_MOD_SSIM1,
+               RSND_MOD_SSIM2,
+               RSND_MOD_SSIM3,
+       };
+       int i, mask;
+
+       switch (runtime->channels) {
+       case 2: /* Multi channel is not needed for Stereo */
+               return 0;
+       case 6:
+               break;
+       default:
+               dev_err(dev, "unsupported channel\n");
+               return 0;
+       }
+
+       mask = 0;
+       for (i = 0; i < ARRAY_SIZE(types); i++) {
+               mod = rsnd_io_to_mod(io, types[i]);
+               if (!mod)
+                       continue;
+
+               mask |= 1 << rsnd_mod_id(mod);
+       }
+
+       return mask;
+}
+
 static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
                                     struct rsnd_dai_stream *io)
 {
@@ -194,6 +230,9 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
        if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
                return 0;
 
+       if (rsnd_ssi_is_multi_slave(mod, io))
+               return 0;
+
        if (ssi->usrcnt > 1) {
                if (ssi->rate != rate) {
                        dev_err(dev, "SSI parent/child should use same rate\n");
@@ -437,8 +476,14 @@ static int __rsnd_ssi_start(struct rsnd_mod *mod,
 
        cr  =   ssi->cr_own     |
                ssi->cr_clk     |
-               ssi->cr_mode    |
-               EN;
+               ssi->cr_mode;
+
+       /*
+        * EN will be set via SSIU :: SSI_CONTROL
+        * if Multi channel mode
+        */
+       if (!rsnd_ssi_multi_slaves(io))
+               cr |= EN;
 
        rsnd_mod_write(mod, SSICR, cr);
        rsnd_mod_write(mod, SSIWSR, ssi->wsr);
@@ -609,6 +654,13 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        int ret;
 
+       /*
+        * SSIP/SSIU/IRQ are not needed on
+        * SSI Multi slaves
+        */
+       if (rsnd_ssi_is_multi_slave(mod, io))
+               return 0;
+
        rsnd_ssi_parent_attach(mod, io, priv);
 
        ret = rsnd_ssiu_attach(io, mod);
@@ -641,6 +693,13 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
        int dma_id = 0; /* not needed */
        int ret;
 
+       /*
+        * SSIP/SSIU/IRQ/DMA are not needed on
+        * SSI Multi slaves
+        */
+       if (rsnd_ssi_is_multi_slave(mod, io))
+               return 0;
+
        ret = rsnd_ssi_common_probe(mod, io, priv);
        if (ret)
                return ret;
@@ -732,6 +791,57 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = {
 /*
  *             ssi mod function
  */
+static void rsnd_ssi_connect(struct rsnd_mod *mod,
+                            struct rsnd_dai_stream *io)
+{
+       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+       enum rsnd_mod_type types[] = {
+               RSND_MOD_SSI,
+               RSND_MOD_SSIM1,
+               RSND_MOD_SSIM2,
+               RSND_MOD_SSIM3,
+       };
+       enum rsnd_mod_type type;
+       int i;
+
+       /* try SSI -> SSIM1 -> SSIM2 -> SSIM3 */
+       for (i = 0; i < ARRAY_SIZE(types); i++) {
+               type = types[i];
+               if (!rsnd_io_to_mod(io, type)) {
+                       rsnd_dai_connect(mod, io, type);
+                       rsnd_set_slot(rdai, 2 * (i + 1), (i + 1));
+                       return;
+               }
+       }
+}
+
+void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
+                           struct device_node *playback,
+                           struct device_node *capture)
+{
+       struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+       struct device_node *node;
+       struct device_node *np;
+       struct rsnd_mod *mod;
+       int i;
+
+       node = rsnd_ssi_of_node(priv);
+       if (!node)
+               return;
+
+       i = 0;
+       for_each_child_of_node(node, np) {
+               mod = rsnd_ssi_mod_get(priv, i);
+               if (np == playback)
+                       rsnd_ssi_connect(mod, &rdai->playback);
+               if (np == capture)
+                       rsnd_ssi_connect(mod, &rdai->capture);
+               i++;
+       }
+
+       of_node_put(node);
+}
+
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
 {
        if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
index 7ae05a7621ae3be645d2ae692b170cdd8e113886..3fe9e08e81a3eb482915ba7da7f64b01766c7a09 100644 (file)
@@ -27,8 +27,11 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
                          struct rsnd_priv *priv)
 {
        struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+       u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io);
        int use_busif = rsnd_ssi_use_busif(io);
        int id = rsnd_mod_id(mod);
+       u32 mask1, val1;
+       u32 mask2, val2;
 
        /*
         * SSI_MODE0
@@ -38,6 +41,9 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
        /*
         * SSI_MODE1
         */
+       mask1 = (1 << 4) | (1 << 20);   /* mask sync bit */
+       mask2 = (1 << 4);               /* mask sync bit */
+       val1  = val2  = 0;
        if (rsnd_ssi_is_pin_sharing(io)) {
                int shift = -1;
 
@@ -51,15 +57,36 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
                case 4:
                        shift = 16;
                        break;
+               default:
+                       return -EINVAL;
                }
 
-               if (shift >= 0)
-                       rsnd_mod_bset(mod, SSI_MODE1,
-                                     0x3 << shift,
-                                     rsnd_rdai_is_clk_master(rdai) ?
-                                     0x2 << shift : 0x1 << shift);
+               mask1 |= 0x3 << shift;
+               val1 = rsnd_rdai_is_clk_master(rdai) ?
+                       0x2 << shift : 0x1 << shift;
+
+       } else if (multi_ssi_slaves) {
+
+               mask2 |= 0x00000007;
+               mask1 |= 0x0000000f;
+
+               switch (multi_ssi_slaves) {
+               case 0x0206: /* SSI0/1/2/9 */
+                       val2 = (1 << 4) | /* SSI0129 sync */
+                               rsnd_rdai_is_clk_master(rdai) ? 0x2 : 0x1;
+                       /* fall through */
+               case 0x0006: /* SSI0/1/2 */
+                       val1 = rsnd_rdai_is_clk_master(rdai) ?
+                               0xa : 0x5;
+
+                       if (!val2)  /* SSI012 sync */
+                               val1 |= (1 << 4);
+               }
        }
 
+       rsnd_mod_bset(mod, SSI_MODE1, mask1, val1);
+       rsnd_mod_bset(mod, SSI_MODE2, mask2, val2);
+
        return 0;
 }
 
@@ -104,8 +131,13 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
                                struct rsnd_dai_stream *io,
                                struct rsnd_priv *priv)
 {
-       if (rsnd_ssi_use_busif(io))
-               rsnd_mod_write(mod, SSI_CTRL, 0x1);
+       if (!rsnd_ssi_use_busif(io))
+               return 0;
+
+       rsnd_mod_write(mod, SSI_CTRL, 0x1);
+
+       if (rsnd_ssi_multi_slaves(io))
+               rsnd_mod_write(mod, SSI_CONTROL, 0x1);
 
        return 0;
 }
@@ -114,8 +146,13 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
                               struct rsnd_dai_stream *io,
                               struct rsnd_priv *priv)
 {
-       if (rsnd_ssi_use_busif(io))
-               rsnd_mod_write(mod, SSI_CTRL, 0);
+       if (!rsnd_ssi_use_busif(io))
+               return 0;
+
+       rsnd_mod_write(mod, SSI_CTRL, 0);
+
+       if (rsnd_ssi_multi_slaves(io))
+               rsnd_mod_write(mod, SSI_CONTROL, 0);
 
        return 0;
 }