]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - sound/soc/sh/rcar/core.c
Merge remote-tracking branches 'asoc/topic/pxa', 'asoc/topic/qcom', 'asoc/topic/rcar...
[mirror_ubuntu-bionic-kernel.git] / sound / soc / sh / rcar / core.c
index deed48ef28b832821010f7192961aaa9af4ba84b..02b4b085b8d77f57398a13c35214a49445d1ed77 100644 (file)
 #define RSND_RATES SNDRV_PCM_RATE_8000_96000
 #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 
-static const struct rsnd_of_data rsnd_of_data_gen1 = {
-       .flags = RSND_GEN1,
-};
-
-static const struct rsnd_of_data rsnd_of_data_gen2 = {
-       .flags = RSND_GEN2,
-};
-
 static const struct of_device_id rsnd_of_match[] = {
-       { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 },
-       { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 },
-       { .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */
+       { .compatible = "renesas,rcar_sound-gen1", .data = (void *)RSND_GEN1 },
+       { .compatible = "renesas,rcar_sound-gen2", .data = (void *)RSND_GEN2 },
+       { .compatible = "renesas,rcar_sound-gen3", .data = (void *)RSND_GEN2 }, /* gen2 compatible */
        {},
 };
 MODULE_DEVICE_TABLE(of, rsnd_of_match);
 
 /*
- *     rsnd_platform functions
+ *     rsnd_mod functions
  */
-#define rsnd_platform_call(priv, dai, func, param...)  \
-       (!(priv->info->func) ? 0 :              \
-        priv->info->func(param))
-
-#define rsnd_is_enable_path(io, name) \
-       ((io)->info ? (io)->info->name : NULL)
-#define rsnd_info_id(priv, io, name) \
-       ((io)->info->name - priv->info->name##_info)
-
 void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
 {
        if (mod->type != type) {
@@ -138,9 +121,6 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
        }
 }
 
-/*
- *     rsnd_mod functions
- */
 char *rsnd_mod_name(struct rsnd_mod *mod)
 {
        if (!mod || !mod->ops)
@@ -192,19 +172,16 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod,
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_dai_stream *io;
        struct rsnd_dai *rdai;
-       int i, j;
-
-       for_each_rsnd_dai(rdai, priv, j) {
+       int i;
 
-               for (i = 0; i < RSND_MOD_MAX; i++) {
-                       io = &rdai->playback;
-                       if (mod == io->mod[i])
-                               callback(mod, io);
+       for_each_rsnd_dai(rdai, priv, i) {
+               io = &rdai->playback;
+               if (mod == io->mod[mod->type])
+                       callback(mod, io);
 
-                       io = &rdai->capture;
-                       if (mod == io->mod[i])
-                               callback(mod, io);
-               }
+               io = &rdai->capture;
+               if (mod == io->mod[mod->type])
+                       callback(mod, io);
        }
 }
 
@@ -214,6 +191,43 @@ int rsnd_io_is_working(struct rsnd_dai_stream *io)
        return !!io->substream;
 }
 
+void rsnd_set_slot(struct rsnd_dai *rdai,
+                  int slots, int num)
+{
+       rdai->slots     = slots;
+       rdai->slots_num = num;
+}
+
+int rsnd_get_slot(struct rsnd_dai_stream *io)
+{
+       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+
+       return rdai->slots;
+}
+
+int rsnd_get_slot_num(struct rsnd_dai_stream *io)
+{
+       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+
+       return rdai->slots_num;
+}
+
+int rsnd_get_slot_width(struct rsnd_dai_stream *io)
+{
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(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)
+               chan = 8;
+
+       return chan;
+}
+
 /*
  *     ADINR function
  */
@@ -222,21 +236,17 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        struct device *dev = rsnd_priv_to_dev(priv);
-       u32 adinr = runtime->channels;
 
        switch (runtime->sample_bits) {
        case 16:
-               adinr |= (8 << 16);
-               break;
+               return 8 << 16;
        case 32:
-               adinr |= (0 << 16);
-               break;
-       default:
-               dev_warn(dev, "not supported sample bits\n");
-               return 0;
+               return 0 << 16;
        }
 
-       return adinr;
+       dev_warn(dev, "not supported sample bits\n");
+
+       return 0;
 }
 
 u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
@@ -267,13 +277,22 @@ u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
  */
 u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
 {
-       struct rsnd_mod *src = rsnd_io_to_mod_src(io);
        struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
-       struct rsnd_mod *target = src ? src : ssi;
+       struct rsnd_mod *target;
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        u32 val = 0x76543210;
        u32 mask = ~0;
 
+       if (rsnd_io_is_play(io)) {
+               struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+
+               target = src ? src : ssi;
+       } else {
+               struct rsnd_mod *cmd = rsnd_io_to_mod_cmd(io);
+
+               target = cmd ? cmd : ssi;
+       }
+
        mask <<= runtime->channels * 4;
        val = val & mask;
 
@@ -300,20 +319,22 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
 /*
  *     rsnd_dai functions
  */
-#define rsnd_mod_call(mod, io, func, param...)                 \
+#define rsnd_mod_call(idx, io, func, param...)                 \
 ({                                                             \
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);         \
+       struct rsnd_mod *mod = (io)->mod[idx];                  \
        struct device *dev = rsnd_priv_to_dev(priv);            \
+       u32 *status = (io)->mod_status + idx;                   \
        u32 mask = 0xF << __rsnd_mod_shift_##func;                      \
-       u8 val  = (mod->status >> __rsnd_mod_shift_##func) & 0xF;       \
+       u8 val  = (*status >> __rsnd_mod_shift_##func) & 0xF;           \
        u8 add  = ((val + __rsnd_mod_add_##func) & 0xF);                \
        int ret = 0;                                                    \
        int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
-       mod->status = (mod->status & ~mask) +                           \
+       *status = (*status & ~mask) +                                   \
                (add << __rsnd_mod_shift_##func);                       \
        dev_dbg(dev, "%s[%d]\t0x%08x %s\n",                             \
                rsnd_mod_name(mod), rsnd_mod_id(mod),                   \
-               mod->status, call ? #func : "");                        \
+               *status, call ? #func : "");                            \
        if (call)                                                       \
                ret = (mod)->ops->func(mod, io, param);                 \
        ret;                                                            \
@@ -327,13 +348,14 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
                mod = (io)->mod[i];                             \
                if (!mod)                                       \
                        continue;                               \
-               ret |= rsnd_mod_call(mod, io, fn, param);       \
+               ret |= rsnd_mod_call(i, io, fn, param);         \
        }                                                       \
        ret;                                                    \
 })
 
-static int rsnd_dai_connect(struct rsnd_mod *mod,
-                           struct rsnd_dai_stream *io)
+int rsnd_dai_connect(struct rsnd_mod *mod,
+                    struct rsnd_dai_stream *io,
+                    enum rsnd_mod_type type)
 {
        struct rsnd_priv *priv;
        struct device *dev;
@@ -341,10 +363,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
        if (!mod)
                return -EIO;
 
+       if (io->mod[type])
+               return -EINVAL;
+
        priv = rsnd_mod_to_priv(mod);
        dev = rsnd_priv_to_dev(priv);
 
-       io->mod[mod->type] = mod;
+       io->mod[type] = mod;
 
        dev_dbg(dev, "%s[%d] is connected to io (%s)\n",
                rsnd_mod_name(mod), rsnd_mod_id(mod),
@@ -354,9 +379,10 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
 }
 
 static void rsnd_dai_disconnect(struct rsnd_mod *mod,
-                               struct rsnd_dai_stream *io)
+                               struct rsnd_dai_stream *io,
+                               enum rsnd_mod_type type)
 {
-       io->mod[mod->type] = NULL;
+       io->mod[type] = NULL;
 }
 
 struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id)
@@ -469,7 +495,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
        struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
        struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
        struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
-       int ssi_id = rsnd_mod_id(rsnd_io_to_mod_ssi(io));
        int ret;
        unsigned long flags;
 
@@ -479,10 +504,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
        case SNDRV_PCM_TRIGGER_START:
                rsnd_dai_stream_init(io, substream);
 
-               ret = rsnd_platform_call(priv, dai, start, ssi_id);
-               if (ret < 0)
-                       goto dai_trigger_end;
-
                ret = rsnd_dai_call(init, io, priv);
                if (ret < 0)
                        goto dai_trigger_end;
@@ -496,8 +517,6 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 
                ret |= rsnd_dai_call(quit, io, priv);
 
-               ret |= rsnd_platform_call(priv, dai, stop, ssi_id);
-
                rsnd_dai_stream_quit(io);
                break;
        default:
@@ -567,332 +586,157 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        return 0;
 }
 
-static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
-       .trigger        = rsnd_soc_dai_trigger,
-       .set_fmt        = rsnd_soc_dai_set_fmt,
-};
-
-#define rsnd_path_add(priv, io, type)                          \
-({                                                             \
-       struct rsnd_mod *mod;                                   \
-       int ret = 0;                                            \
-       int id = -1;                                            \
-                                                               \
-       if (rsnd_is_enable_path(io, type)) {                    \
-               id = rsnd_info_id(priv, io, type);              \
-               if (id >= 0) {                                  \
-                       mod = rsnd_##type##_mod_get(priv, id);  \
-                       ret = rsnd_dai_connect(mod, io);        \
-               }                                               \
-       }                                                       \
-       ret;                                                    \
-})
-
-#define rsnd_path_remove(priv, io, type)                       \
-{                                                              \
-       struct rsnd_mod *mod;                                   \
-       int id = -1;                                            \
-                                                               \
-       if (rsnd_is_enable_path(io, type)) {                    \
-               id = rsnd_info_id(priv, io, type);              \
-               if (id >= 0) {                                  \
-                       mod = rsnd_##type##_mod_get(priv, id);  \
-                       rsnd_dai_disconnect(mod, io);           \
-               }                                               \
-       }                                                       \
-}
-
-void rsnd_path_parse(struct rsnd_priv *priv,
-                    struct rsnd_dai_stream *io)
+static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai,
+                                    u32 tx_mask, u32 rx_mask,
+                                    int slots, int slot_width)
 {
-       struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
-       struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
-       struct rsnd_mod *src = rsnd_io_to_mod_src(io);
-       struct rsnd_mod *cmd;
+       struct rsnd_priv *priv = rsnd_dai_to_priv(dai);
+       struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
        struct device *dev = rsnd_priv_to_dev(priv);
-       u32 data;
 
-       /* Gen1 is not supported */
-       if (rsnd_is_gen1(priv))
-               return;
-
-       if (!mix && !dvc)
-               return;
-
-       if (mix) {
-               struct rsnd_dai *rdai;
-               int i;
-               u32 path[] = {
-                       [0] = 0,
-                       [1] = 1 << 0,
-                       [2] = 0,
-                       [3] = 0,
-                       [4] = 0,
-                       [5] = 1 << 8
-               };
-
-               /*
-                * it is assuming that integrater is well understanding about
-                * data path. Here doesn't check impossible connection,
-                * like src2 + src5
-                */
-               data = 0;
-               for_each_rsnd_dai(rdai, priv, i) {
-                       io = &rdai->playback;
-                       if (mix == rsnd_io_to_mod_mix(io))
-                               data |= path[rsnd_mod_id(src)];
-
-                       io = &rdai->capture;
-                       if (mix == rsnd_io_to_mod_mix(io))
-                               data |= path[rsnd_mod_id(src)];
-               }
-
-               /*
-                * We can't use ctu = rsnd_io_ctu() here.
-                * Since, ID of dvc/mix are 0 or 1 (= same as CMD number)
-                * but ctu IDs are 0 - 7 (= CTU00 - CTU13)
-                */
-               cmd = mix;
-       } else {
-               u32 path[] = {
-                       [0] = 0x30000,
-                       [1] = 0x30001,
-                       [2] = 0x40000,
-                       [3] = 0x10000,
-                       [4] = 0x20000,
-                       [5] = 0x40100
-               };
-
-               data = path[rsnd_mod_id(src)];
-
-               cmd = dvc;
+       switch (slots) {
+       case 6:
+               /* TDM Extend Mode */
+               rsnd_set_slot(rdai, slots, 1);
+               break;
+       default:
+               dev_err(dev, "unsupported TDM slots (%d)\n", slots);
+               return -EINVAL;
        }
 
-       dev_dbg(dev, "ctu/mix path = 0x%08x", data);
-
-       rsnd_mod_write(cmd, CMD_ROUTE_SLCT, data);
-
-       rsnd_mod_write(cmd, CMD_CTRL, 0x10);
+       return 0;
 }
 
-static int rsnd_path_init(struct rsnd_priv *priv,
-                         struct rsnd_dai *rdai,
-                         struct rsnd_dai_stream *io)
-{
-       int ret;
-
-       /*
-        * Gen1 is created by SRU/SSI, and this SRU is base module of
-        * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU)
-        *
-        * Easy image is..
-        *      Gen1 SRU = Gen2 SCU + SSIU + etc
-        *
-        * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is
-        * using fixed path.
-        */
-
-       /* SSI */
-       ret = rsnd_path_add(priv, io, ssi);
-       if (ret < 0)
-               return ret;
-
-       /* SRC */
-       ret = rsnd_path_add(priv, io, src);
-       if (ret < 0)
-               return ret;
+static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
+       .trigger        = rsnd_soc_dai_trigger,
+       .set_fmt        = rsnd_soc_dai_set_fmt,
+       .set_tdm_slot   = rsnd_soc_set_dai_tdm_slot,
+};
 
-       /* CTU */
-       ret = rsnd_path_add(priv, io, ctu);
-       if (ret < 0)
-               return ret;
+void rsnd_parse_connect_common(struct rsnd_dai *rdai,
+               struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
+               struct device_node *node,
+               struct device_node *playback,
+               struct device_node *capture)
+{
+       struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
+       struct device_node *np;
+       struct rsnd_mod *mod;
+       int i;
 
-       /* MIX */
-       ret = rsnd_path_add(priv, io, mix);
-       if (ret < 0)
-               return ret;
+       if (!node)
+               return;
 
-       /* DVC */
-       ret = rsnd_path_add(priv, io, dvc);
-       if (ret < 0)
-               return ret;
+       i = 0;
+       for_each_child_of_node(node, np) {
+               mod = mod_get(priv, i);
+               if (np == playback)
+                       rsnd_dai_connect(mod, &rdai->playback, mod->type);
+               if (np == capture)
+                       rsnd_dai_connect(mod, &rdai->capture, mod->type);
+               i++;
+       }
 
-       return ret;
+       of_node_put(node);
 }
 
-static void rsnd_of_parse_dai(struct platform_device *pdev,
-                             const struct rsnd_of_data *of_data,
-                             struct rsnd_priv *priv)
+static int rsnd_dai_probe(struct rsnd_priv *priv)
 {
-       struct device_node *dai_node,   *dai_np;
-       struct device_node *ssi_node,   *ssi_np;
-       struct device_node *src_node,   *src_np;
-       struct device_node *ctu_node,   *ctu_np;
-       struct device_node *mix_node,   *mix_np;
-       struct device_node *dvc_node,   *dvc_np;
+       struct device_node *dai_node;
+       struct device_node *dai_np;
        struct device_node *playback, *capture;
-       struct rsnd_dai_platform_info *dai_info;
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
-       struct device *dev = &pdev->dev;
-       int nr, i;
-       int dai_i, ssi_i, src_i, ctu_i, mix_i, dvc_i;
-
-       if (!of_data)
-               return;
-
-       dai_node = of_get_child_by_name(dev->of_node, "rcar_sound,dai");
-       if (!dai_node)
-               return;
+       struct rsnd_dai_stream *io_playback;
+       struct rsnd_dai_stream *io_capture;
+       struct snd_soc_dai_driver *rdrv, *drv;
+       struct rsnd_dai *rdai;
+       struct device *dev = rsnd_priv_to_dev(priv);
+       int nr, dai_i, io_i;
+       int ret;
 
+       dai_node = rsnd_dai_of_node(priv);
        nr = of_get_child_count(dai_node);
-       if (!nr)
-               return;
+       if (!nr) {
+               ret = -EINVAL;
+               goto rsnd_dai_probe_done;
+       }
 
-       dai_info = devm_kzalloc(dev,
-                               sizeof(struct rsnd_dai_platform_info) * nr,
-                               GFP_KERNEL);
-       if (!dai_info) {
-               dev_err(dev, "dai info allocation error\n");
-               return;
+       rdrv = devm_kzalloc(dev, sizeof(*rdrv) * nr, GFP_KERNEL);
+       rdai = devm_kzalloc(dev, sizeof(*rdai) * nr, GFP_KERNEL);
+       if (!rdrv || !rdai) {
+               ret = -ENOMEM;
+               goto rsnd_dai_probe_done;
        }
 
-       info->dai_info_nr       = nr;
-       info->dai_info          = dai_info;
-
-       ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi");
-       src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src");
-       ctu_node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu");
-       mix_node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
-       dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
-
-#define mod_parse(name)                                                        \
-if (name##_node) {                                                     \
-       struct rsnd_##name##_platform_info *name##_info;                \
-                                                                       \
-       name##_i = 0;                                                   \
-       for_each_child_of_node(name##_node, name##_np) {                \
-               name##_info = info->name##_info + name##_i;             \
-                                                                       \
-               if (name##_np == playback)                              \
-                       dai_info->playback.name = name##_info;          \
-               if (name##_np == capture)                               \
-                       dai_info->capture.name = name##_info;           \
-                                                                       \
-               name##_i++;                                             \
-       }                                                               \
-}
+       priv->rdai_nr   = nr;
+       priv->daidrv    = rdrv;
+       priv->rdai      = rdai;
 
        /*
         * parse all dai
         */
        dai_i = 0;
        for_each_child_of_node(dai_node, dai_np) {
-               dai_info = info->dai_info + dai_i;
-
-               for (i = 0;; i++) {
-
-                       playback = of_parse_phandle(dai_np, "playback", i);
-                       capture  = of_parse_phandle(dai_np, "capture", i);
+               rdai            = rsnd_rdai_get(priv, dai_i);
+               drv             = rdrv + dai_i;
+               io_playback     = &rdai->playback;
+               io_capture      = &rdai->capture;
+
+               snprintf(rdai->name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", dai_i);
+
+               rdai->priv      = priv;
+               drv->name       = rdai->name;
+               drv->ops        = &rsnd_soc_dai_ops;
+
+               snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE,
+                        "DAI%d Playback", dai_i);
+               drv->playback.rates             = RSND_RATES;
+               drv->playback.formats           = RSND_FMTS;
+               drv->playback.channels_min      = 2;
+               drv->playback.channels_max      = 6;
+               drv->playback.stream_name       = rdai->playback.name;
+
+               snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE,
+                        "DAI%d Capture", dai_i);
+               drv->capture.rates              = RSND_RATES;
+               drv->capture.formats            = RSND_FMTS;
+               drv->capture.channels_min       = 2;
+               drv->capture.channels_max       = 6;
+               drv->capture.stream_name        = rdai->capture.name;
+
+               rdai->playback.rdai             = rdai;
+               rdai->capture.rdai              = rdai;
+               rsnd_set_slot(rdai, 2, 1); /* default */
+
+               for (io_i = 0;; io_i++) {
+                       playback = of_parse_phandle(dai_np, "playback", io_i);
+                       capture  = of_parse_phandle(dai_np, "capture", io_i);
 
                        if (!playback && !capture)
                                break;
 
-                       mod_parse(ssi);
-                       mod_parse(src);
-                       mod_parse(ctu);
-                       mod_parse(mix);
-                       mod_parse(dvc);
+                       rsnd_parse_connect_ssi(rdai, playback, capture);
+                       rsnd_parse_connect_src(rdai, playback, capture);
+                       rsnd_parse_connect_ctu(rdai, playback, capture);
+                       rsnd_parse_connect_mix(rdai, playback, capture);
+                       rsnd_parse_connect_dvc(rdai, playback, capture);
 
                        of_node_put(playback);
                        of_node_put(capture);
                }
 
                dai_i++;
-       }
-}
-
-static int rsnd_dai_probe(struct platform_device *pdev,
-                         const struct rsnd_of_data *of_data,
-                         struct rsnd_priv *priv)
-{
-       struct snd_soc_dai_driver *drv;
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
-       struct rsnd_dai *rdai;
-       struct rsnd_ssi_platform_info *pmod, *cmod;
-       struct device *dev = rsnd_priv_to_dev(priv);
-       int dai_nr;
-       int i;
-
-       rsnd_of_parse_dai(pdev, of_data, priv);
 
-       dai_nr = info->dai_info_nr;
-       if (!dai_nr) {
-               dev_err(dev, "no dai\n");
-               return -EIO;
+               dev_dbg(dev, "%s (%s/%s)\n", rdai->name,
+                       rsnd_io_to_mod_ssi(io_playback) ? "play"    : " -- ",
+                       rsnd_io_to_mod_ssi(io_capture) ? "capture" : "  --   ");
        }
 
-       drv  = devm_kzalloc(dev, sizeof(*drv)  * dai_nr, GFP_KERNEL);
-       rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL);
-       if (!drv || !rdai) {
-               dev_err(dev, "dai allocate failed\n");
-               return -ENOMEM;
-       }
-
-       priv->rdai_nr   = dai_nr;
-       priv->daidrv    = drv;
-       priv->rdai      = rdai;
+       ret = 0;
 
-       for (i = 0; i < dai_nr; i++) {
+rsnd_dai_probe_done:
+       of_node_put(dai_node);
 
-               pmod = info->dai_info[i].playback.ssi;
-               cmod = info->dai_info[i].capture.ssi;
-
-               /*
-                *      init rsnd_dai
-                */
-               snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i);
-               rdai[i].priv = priv;
-
-               /*
-                *      init snd_soc_dai_driver
-                */
-               drv[i].name     = rdai[i].name;
-               drv[i].ops      = &rsnd_soc_dai_ops;
-               if (pmod) {
-                       snprintf(rdai[i].playback.name, RSND_DAI_NAME_SIZE,
-                                "DAI%d Playback", i);
-
-                       drv[i].playback.rates           = RSND_RATES;
-                       drv[i].playback.formats         = RSND_FMTS;
-                       drv[i].playback.channels_min    = 2;
-                       drv[i].playback.channels_max    = 2;
-                       drv[i].playback.stream_name     = rdai[i].playback.name;
-
-                       rdai[i].playback.info = &info->dai_info[i].playback;
-                       rdai[i].playback.rdai = rdai + i;
-                       rsnd_path_init(priv, &rdai[i], &rdai[i].playback);
-               }
-               if (cmod) {
-                       snprintf(rdai[i].capture.name, RSND_DAI_NAME_SIZE,
-                                "DAI%d Capture", i);
-
-                       drv[i].capture.rates            = RSND_RATES;
-                       drv[i].capture.formats          = RSND_FMTS;
-                       drv[i].capture.channels_min     = 2;
-                       drv[i].capture.channels_max     = 2;
-                       drv[i].capture.stream_name      = rdai[i].capture.name;
-
-                       rdai[i].capture.info = &info->dai_info[i].capture;
-                       rdai[i].capture.rdai = rdai + i;
-                       rsnd_path_init(priv, &rdai[i], &rdai[i].capture);
-               }
-
-               dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name,
-                       pmod ? "play"    : " -- ",
-                       cmod ? "capture" : "  --   ");
-       }
-
-       return 0;
+       return ret;
 }
 
 /*
@@ -1033,14 +877,13 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
                            void (*update)(struct rsnd_dai_stream *io,
                                           struct rsnd_mod *mod))
 {
-       struct snd_soc_card *soc_card = rtd->card;
        struct snd_card *card = rtd->card->snd_card;
        struct snd_kcontrol *kctrl;
        struct snd_kcontrol_new knew = {
                .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name           = name,
                .info           = rsnd_kctrl_info,
-               .index          = rtd - soc_card->rtd,
+               .index          = rtd->num,
                .get            = rsnd_kctrl_get,
                .put            = rsnd_kctrl_put,
                .private_value  = (unsigned long)cfg,
@@ -1077,10 +920,14 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod,
                     void (*update)(struct rsnd_dai_stream *io,
                                    struct rsnd_mod *mod),
                     struct rsnd_kctrl_cfg_m *_cfg,
+                    int ch_size,
                     u32 max)
 {
+       if (ch_size > RSND_DVC_CHANNELS)
+               return -EINVAL;
+
        _cfg->cfg.max   = max;
-       _cfg->cfg.size  = RSND_DVC_CHANNELS;
+       _cfg->cfg.size  = ch_size;
        _cfg->cfg.val   = _cfg->val;
        return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
 }
@@ -1161,6 +1008,9 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
 
        ret = rsnd_dai_call(probe, io, priv);
        if (ret == -EAGAIN) {
+               struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+               int i;
+
                /*
                 * Fallback to PIO mode
                 */
@@ -1175,10 +1025,12 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
                rsnd_dai_call(remove, io, priv);
 
                /*
-                * remove SRC/DVC from DAI,
+                * remove all mod from io
+                * and, re connect ssi
                 */
-               rsnd_path_remove(priv, io, src);
-               rsnd_path_remove(priv, io, dvc);
+               for (i = 0; i < RSND_MOD_MAX; i++)
+                       rsnd_dai_disconnect((io)->mod[i], io, i);
+               rsnd_dai_connect(ssi_mod, io, RSND_MOD_SSI);
 
                /*
                 * fallback
@@ -1200,33 +1052,25 @@ static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
  */
 static int rsnd_probe(struct platform_device *pdev)
 {
-       struct rcar_snd_info *info;
        struct rsnd_priv *priv;
        struct device *dev = &pdev->dev;
        struct rsnd_dai *rdai;
        const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev);
-       const struct rsnd_of_data *of_data;
-       int (*probe_func[])(struct platform_device *pdev,
-                           const struct rsnd_of_data *of_data,
-                           struct rsnd_priv *priv) = {
+       int (*probe_func[])(struct rsnd_priv *priv) = {
                rsnd_gen_probe,
                rsnd_dma_probe,
                rsnd_ssi_probe,
+               rsnd_ssiu_probe,
                rsnd_src_probe,
                rsnd_ctu_probe,
                rsnd_mix_probe,
                rsnd_dvc_probe,
+               rsnd_cmd_probe,
                rsnd_adg_probe,
                rsnd_dai_probe,
        };
        int ret, i;
 
-       info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info),
-                           GFP_KERNEL);
-       if (!info)
-               return -ENOMEM;
-       of_data = of_id->data;
-
        /*
         *      init priv data
         */
@@ -1237,14 +1081,14 @@ static int rsnd_probe(struct platform_device *pdev)
        }
 
        priv->pdev      = pdev;
-       priv->info      = info;
+       priv->flags     = (unsigned long)of_id->data;
        spin_lock_init(&priv->lock);
 
        /*
         *      init each module
         */
        for (i = 0; i < ARRAY_SIZE(probe_func); i++) {
-               ret = probe_func[i](pdev, of_data, priv);
+               ret = probe_func[i](priv);
                if (ret)
                        return ret;
        }
@@ -1297,13 +1141,15 @@ static int rsnd_remove(struct platform_device *pdev)
 {
        struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
        struct rsnd_dai *rdai;
-       void (*remove_func[])(struct platform_device *pdev,
-                             struct rsnd_priv *priv) = {
+       void (*remove_func[])(struct rsnd_priv *priv) = {
                rsnd_ssi_remove,
+               rsnd_ssiu_remove,
                rsnd_src_remove,
                rsnd_ctu_remove,
                rsnd_mix_remove,
                rsnd_dvc_remove,
+               rsnd_cmd_remove,
+               rsnd_adg_remove,
        };
        int ret = 0, i;
 
@@ -1315,7 +1161,7 @@ static int rsnd_remove(struct platform_device *pdev)
        }
 
        for (i = 0; i < ARRAY_SIZE(remove_func); i++)
-               remove_func[i](pdev, priv);
+               remove_func[i](priv);
 
        snd_soc_unregister_component(&pdev->dev);
        snd_soc_unregister_platform(&pdev->dev);