Merge remote-tracking branches 'asoc/topic/pxa', 'asoc/topic/qcom', 'asoc/topic/rcar...
authorMark Brown <broonie@kernel.org>
Wed, 23 Dec 2015 00:23:46 +0000 (00:23 +0000)
committerMark Brown <broonie@kernel.org>
Wed, 23 Dec 2015 00:23:46 +0000 (00:23 +0000)
29 files changed:
Documentation/devicetree/bindings/sound/inno-rk3036.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt
Documentation/devicetree/bindings/sound/rockchip-i2s.txt
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/inno_rk3036.c [new file with mode: 0644]
sound/soc/codecs/inno_rk3036.h [new file with mode: 0644]
sound/soc/pxa/brownstone.c
sound/soc/qcom/lpass-cpu.c
sound/soc/rockchip/rockchip_i2s.c
sound/soc/rockchip/rockchip_max98090.c
sound/soc/rockchip/rockchip_rt5645.c
sound/soc/sh/Kconfig
sound/soc/sh/rcar/Makefile
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/cmd.c [new file with mode: 0644]
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/ctu.c
sound/soc/sh/rcar/dma.c
sound/soc/sh/rcar/dvc.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/mix.c
sound/soc/sh/rcar/rcar_snd.h [deleted file]
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/rsrc-card.c
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c
sound/soc/sh/rcar/ssiu.c [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/sound/inno-rk3036.txt b/Documentation/devicetree/bindings/sound/inno-rk3036.txt
new file mode 100644 (file)
index 0000000..758de8e
--- /dev/null
@@ -0,0 +1,20 @@
+Inno audio codec for RK3036
+
+Inno audio codec is integrated inside RK3036 SoC.
+
+Required properties:
+- compatible : Should be "rockchip,rk3036-codec".
+- reg : The registers of codec.
+- clock-names : Should be "acodec_pclk".
+- clocks : The clock of codec.
+- rockchip,grf : The phandle of grf device node.
+
+Example:
+
+       acodec: acodec-ana@20030000 {
+               compatible = "rk3036-codec";
+               reg = <0x20030000 0x4000>;
+               rockchip,grf = <&grf>;
+               clock-names = "acodec_pclk";
+               clocks = <&cru ACLK_VCODEC>;
+       };
index c57cbd6..8ee0fa9 100644 (file)
@@ -7,8 +7,11 @@ Required properties:
                                  "renesas,rcar_sound-gen3" if generation3
                                  Examples with soctypes are:
                                    - "renesas,rcar_sound-r8a7778" (R-Car M1A)
+                                   - "renesas,rcar_sound-r8a7779" (R-Car H1)
                                    - "renesas,rcar_sound-r8a7790" (R-Car H2)
                                    - "renesas,rcar_sound-r8a7791" (R-Car M2-W)
+                                   - "renesas,rcar_sound-r8a7793" (R-Car M2-N)
+                                   - "renesas,rcar_sound-r8a7794" (R-Car E2)
                                    - "renesas,rcar_sound-r8a7795" (R-Car H3)
 - reg                          : Should contain the register physical address.
                                  required register is
@@ -34,6 +37,8 @@ Required properties:
                                  see below for detail.
 - #sound-dai-cells             : it must be 0 if your system is using single DAI
                                  it must be 1 if your system is using multi  DAI
+
+Optional properties:
 - #clock-cells                 : it must be 0 if your system has audio_clkout
                                  it must be 1 if your system has audio_clkout0/1/2/3
 - clock-frequency              : for all audio_clkout0/1/2/3
@@ -244,3 +249,80 @@ rcar_sound: sound@ec500000 {
                };
        };
 };
+
+Example: simple sound card
+
+       rsnd_ak4643: sound {
+               compatible = "simple-audio-card";
+
+               simple-audio-card,format = "left_j";
+               simple-audio-card,bitclock-master = <&sndcodec>;
+               simple-audio-card,frame-master = <&sndcodec>;
+
+               sndcpu: simple-audio-card,cpu {
+                       sound-dai = <&rcar_sound>;
+               };
+
+               sndcodec: simple-audio-card,codec {
+                       sound-dai = <&ak4643>;
+                       clocks = <&audio_clock>;
+               };
+       };
+
+&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 &src2 &dvc0>;
+                       capture  = <&ssi1 &src3 &dvc1>;
+               };
+       };
+};
+
+&ssi1 {
+       shared-pin;
+};
+
+Example: simple sound card for TDM
+
+       rsnd_tdm: sound {
+               compatible = "simple-audio-card";
+
+               simple-audio-card,format = "left_j";
+               simple-audio-card,bitclock-master = <&sndcodec>;
+               simple-audio-card,frame-master = <&sndcodec>;
+
+               sndcpu: simple-audio-card,cpu {
+                       sound-dai = <&rcar_sound>;
+                       dai-tdm-slot-num = <6>;
+               };
+
+               sndcodec: simple-audio-card,codec {
+                       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 962748a..2b2caa2 100644 (file)
@@ -4,8 +4,8 @@ Renesas Sampling Rate Convert Sound Card specifies audio DAI connections of SoC
 
 Required properties:
 
-- compatible                           : "renesas,rsrc-card,<board>"
-                                         Examples with soctypes are:
+- compatible                           : "renesas,rsrc-card{,<board>}"
+                                         Examples with boards are:
                                            - "renesas,rsrc-card"
                                            - "renesas,rsrc-card,lager"
                                            - "renesas,rsrc-card,koelsch"
index 2267d24..b7f3a93 100644 (file)
@@ -19,6 +19,7 @@ Required properties:
 - clock-names: should contain followings:
    - "i2s_hclk": clock for I2S BUS
    - "i2s_clk" : clock for I2S controller
+- rockchip,playback-channels: max playback channels, if not set, 8 channels default.
 - rockchip,capture-channels: max capture channels, if not set, 2 channels default.
 
 Example for rk3288 I2S controller:
@@ -31,5 +32,6 @@ i2s@ff890000 {
        dma-names = "tx", "rx";
        clock-names = "i2s_hclk", "i2s_clk";
        clocks = <&cru HCLK_I2S0>, <&cru SCLK_I2S0>;
+       rockchip,playback-channels = <8>;
        rockchip,capture-channels = <2>;
 };
index 08433a8..784468e 100644 (file)
@@ -70,6 +70,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_GTM601
        select SND_SOC_HDAC_HDMI
        select SND_SOC_ICS43432
+       select SND_SOC_INNO_RK3036
        select SND_SOC_ISABELLE if I2C
        select SND_SOC_JZ4740_CODEC
        select SND_SOC_LM4857 if I2C
@@ -492,6 +493,9 @@ config SND_SOC_HDAC_HDMI
 config SND_SOC_ICS43432
        tristate
 
+config SND_SOC_INNO_RK3036
+       tristate "Inno codec driver for RK3036 SoC"
+
 config SND_SOC_ISABELLE
         tristate
 
index fdf5b31..44d8958 100644 (file)
@@ -63,6 +63,7 @@ snd-soc-es8328-spi-objs := es8328-spi.o
 snd-soc-gtm601-objs := gtm601.o
 snd-soc-hdac-hdmi-objs := hdac_hdmi.o
 snd-soc-ics43432-objs := ics43432.o
+snd-soc-inno-rk3036-objs := inno_rk3036.o
 snd-soc-isabelle-objs := isabelle.o
 snd-soc-jz4740-codec-objs := jz4740.o
 snd-soc-l3-objs := l3.o
@@ -264,6 +265,7 @@ obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
 obj-$(CONFIG_SND_SOC_GTM601)    += snd-soc-gtm601.o
 obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
 obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o
+obj-$(CONFIG_SND_SOC_INNO_RK3036)      += snd-soc-inno-rk3036.o
 obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
diff --git a/sound/soc/codecs/inno_rk3036.c b/sound/soc/codecs/inno_rk3036.c
new file mode 100644 (file)
index 0000000..9b6e884
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+ * Driver of Inno codec for rk3036 by Rockchip Inc.
+ *
+ * Author: Rockchip Inc.
+ * Author: Zheng ShunQian<zhengsq@rock-chips.com>
+ */
+
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dai.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/io.h>
+
+#include "inno_rk3036.h"
+
+struct rk3036_codec_priv {
+       void __iomem *base;
+       struct clk *pclk;
+       struct regmap *regmap;
+       struct device *dev;
+};
+
+static const DECLARE_TLV_DB_MINMAX(rk3036_codec_hp_tlv, -39, 0);
+
+static int rk3036_codec_antipop_info(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+
+       return 0;
+}
+
+static int rk3036_codec_antipop_get(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       int val, ret, regval;
+
+       ret = snd_soc_component_read(component, INNO_R09, &regval);
+       if (ret)
+               return ret;
+       val = ((regval >> INNO_R09_HPL_ANITPOP_SHIFT) &
+              INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
+       ucontrol->value.integer.value[0] = val;
+
+       val = ((regval >> INNO_R09_HPR_ANITPOP_SHIFT) &
+              INNO_R09_HP_ANTIPOP_MSK) == INNO_R09_HP_ANTIPOP_ON;
+       ucontrol->value.integer.value[1] = val;
+
+       return 0;
+}
+
+static int rk3036_codec_antipop_put(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       int val, ret, regmsk;
+
+       val = (ucontrol->value.integer.value[0] ?
+              INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
+             INNO_R09_HPL_ANITPOP_SHIFT;
+       val |= (ucontrol->value.integer.value[1] ?
+               INNO_R09_HP_ANTIPOP_ON : INNO_R09_HP_ANTIPOP_OFF) <<
+              INNO_R09_HPR_ANITPOP_SHIFT;
+
+       regmsk = INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPL_ANITPOP_SHIFT |
+                INNO_R09_HP_ANTIPOP_MSK << INNO_R09_HPR_ANITPOP_SHIFT;
+
+       ret = snd_soc_component_update_bits(component, INNO_R09,
+                                           regmsk, val);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+#define SOC_RK3036_CODEC_ANTIPOP_DECL(xname) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = rk3036_codec_antipop_info, .get = rk3036_codec_antipop_get, \
+       .put = rk3036_codec_antipop_put, }
+
+static const struct snd_kcontrol_new rk3036_codec_dapm_controls[] = {
+       SOC_DOUBLE_R_RANGE_TLV("Headphone Volume", INNO_R07, INNO_R08,
+               INNO_HP_GAIN_SHIFT, INNO_HP_GAIN_N39DB,
+               INNO_HP_GAIN_0DB, 0, rk3036_codec_hp_tlv),
+       SOC_DOUBLE("Zero Cross Switch", INNO_R06, INNO_R06_VOUTL_CZ_SHIFT,
+               INNO_R06_VOUTR_CZ_SHIFT, 1, 0),
+       SOC_DOUBLE("Headphone Switch", INNO_R09, INNO_R09_HPL_MUTE_SHIFT,
+               INNO_R09_HPR_MUTE_SHIFT, 1, 0),
+       SOC_RK3036_CODEC_ANTIPOP_DECL("Anti-pop Switch"),
+};
+
+static const struct snd_kcontrol_new rk3036_codec_hpl_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DAC Left Out Switch", INNO_R09,
+                       INNO_R09_DACL_SWITCH_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new rk3036_codec_hpr_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DAC Right Out Switch", INNO_R09,
+                       INNO_R09_DACR_SWITCH_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new rk3036_codec_hpl_switch_controls[] = {
+       SOC_DAPM_SINGLE("HP Left Out Switch", INNO_R05,
+                       INNO_R05_HPL_WORK_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new rk3036_codec_hpr_switch_controls[] = {
+       SOC_DAPM_SINGLE("HP Right Out Switch", INNO_R05,
+                       INNO_R05_HPR_WORK_SHIFT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget rk3036_codec_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY_S("DAC PWR", 1, INNO_R06,
+                             INNO_R06_DAC_EN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DACL VREF", 2, INNO_R04,
+                             INNO_R04_DACL_VREF_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DACR VREF", 2, INNO_R04,
+                             INNO_R04_DACR_VREF_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DACL HiLo VREF", 3, INNO_R06,
+                             INNO_R06_DACL_HILO_VREF_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DACR HiLo VREF", 3, INNO_R06,
+                             INNO_R06_DACR_HILO_VREF_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DACR CLK", 3, INNO_R04,
+                             INNO_R04_DACR_CLK_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DACL CLK", 3, INNO_R04,
+                             INNO_R04_DACL_CLK_SHIFT, 0, NULL, 0),
+
+       SND_SOC_DAPM_DAC("DACL", "Left Playback", INNO_R04,
+                        INNO_R04_DACL_SW_SHIFT, 0),
+       SND_SOC_DAPM_DAC("DACR", "Right Playback", INNO_R04,
+                        INNO_R04_DACR_SW_SHIFT, 0),
+
+       SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+               rk3036_codec_hpl_mixer_controls,
+               ARRAY_SIZE(rk3036_codec_hpl_mixer_controls)),
+       SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+               rk3036_codec_hpr_mixer_controls,
+               ARRAY_SIZE(rk3036_codec_hpr_mixer_controls)),
+
+       SND_SOC_DAPM_PGA("HP Left Out", INNO_R05,
+                        INNO_R05_HPL_EN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("HP Right Out", INNO_R05,
+                        INNO_R05_HPR_EN_SHIFT, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("HP Left Switch",  SND_SOC_NOPM, 0, 0,
+                          rk3036_codec_hpl_switch_controls,
+                          ARRAY_SIZE(rk3036_codec_hpl_switch_controls)),
+       SND_SOC_DAPM_MIXER("HP Right Switch",  SND_SOC_NOPM, 0, 0,
+                          rk3036_codec_hpr_switch_controls,
+                          ARRAY_SIZE(rk3036_codec_hpr_switch_controls)),
+
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_route rk3036_codec_dapm_routes[] = {
+       {"DACL VREF", NULL, "DAC PWR"},
+       {"DACR VREF", NULL, "DAC PWR"},
+       {"DACL HiLo VREF", NULL, "DAC PWR"},
+       {"DACR HiLo VREF", NULL, "DAC PWR"},
+       {"DACL CLK", NULL, "DAC PWR"},
+       {"DACR CLK", NULL, "DAC PWR"},
+
+       {"DACL", NULL, "DACL VREF"},
+       {"DACL", NULL, "DACL HiLo VREF"},
+       {"DACL", NULL, "DACL CLK"},
+       {"DACR", NULL, "DACR VREF"},
+       {"DACR", NULL, "DACR HiLo VREF"},
+       {"DACR", NULL, "DACR CLK"},
+
+       {"Left Headphone Mixer", "DAC Left Out Switch", "DACL"},
+       {"Right Headphone Mixer", "DAC Right Out Switch", "DACR"},
+       {"HP Left Out", NULL, "Left Headphone Mixer"},
+       {"HP Right Out", NULL, "Right Headphone Mixer"},
+
+       {"HP Left Switch", "HP Left Out Switch", "HP Left Out"},
+       {"HP Right Switch", "HP Right Out Switch", "HP Right Out"},
+
+       {"HPL", NULL, "HP Left Switch"},
+       {"HPR", NULL, "HP Right Switch"},
+};
+
+static int rk3036_codec_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       unsigned int reg01_val = 0,  reg02_val = 0, reg03_val = 0;
+
+       dev_dbg(codec->dev, "rk3036_codec dai set fmt : %08x\n", fmt);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               reg01_val |= INNO_R01_PINDIR_IN_SLAVE |
+                            INNO_R01_I2SMODE_SLAVE;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               reg01_val |= INNO_R01_PINDIR_OUT_MASTER |
+                            INNO_R01_I2SMODE_MASTER;
+               break;
+       default:
+               dev_err(codec->dev, "invalid fmt\n");
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               reg02_val |= INNO_R02_DACM_PCM;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               reg02_val |= INNO_R02_DACM_I2S;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               reg02_val |= INNO_R02_DACM_RJM;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               reg02_val |= INNO_R02_DACM_LJM;
+               break;
+       default:
+               dev_err(codec->dev, "set dai format failed\n");
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               reg02_val |= INNO_R02_LRCP_NORMAL;
+               reg03_val |= INNO_R03_BCP_NORMAL;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               reg02_val |= INNO_R02_LRCP_REVERSAL;
+               reg03_val |= INNO_R03_BCP_REVERSAL;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               reg02_val |= INNO_R02_LRCP_REVERSAL;
+               reg03_val |= INNO_R03_BCP_NORMAL;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               reg02_val |= INNO_R02_LRCP_NORMAL;
+               reg03_val |= INNO_R03_BCP_REVERSAL;
+               break;
+       default:
+               dev_err(codec->dev, "set dai format failed\n");
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, INNO_R01, INNO_R01_I2SMODE_MSK |
+                           INNO_R01_PINDIR_MSK, reg01_val);
+       snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
+                           INNO_R02_DACM_MSK, reg02_val);
+       snd_soc_update_bits(codec, INNO_R03, INNO_R03_BCP_MSK, reg03_val);
+
+       return 0;
+}
+
+static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
+                                     struct snd_pcm_hw_params *hw_params,
+                                     struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       unsigned int reg02_val = 0, reg03_val = 0;
+
+       switch (params_format(hw_params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               reg02_val |= INNO_R02_VWL_16BIT;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               reg02_val |= INNO_R02_VWL_20BIT;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               reg02_val |= INNO_R02_VWL_24BIT;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               reg02_val |= INNO_R02_VWL_32BIT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       reg02_val |= INNO_R02_LRCP_NORMAL;
+       reg03_val |= INNO_R03_FWL_32BIT | INNO_R03_DACR_WORK;
+
+       snd_soc_update_bits(codec, INNO_R02, INNO_R02_LRCP_MSK |
+                           INNO_R02_VWL_MSK, reg02_val);
+       snd_soc_update_bits(codec, INNO_R03, INNO_R03_DACR_MSK |
+                           INNO_R03_FWL_MSK, reg03_val);
+       return 0;
+}
+
+#define RK3036_CODEC_RATES (SNDRV_PCM_RATE_8000  | \
+                           SNDRV_PCM_RATE_16000 | \
+                           SNDRV_PCM_RATE_32000 | \
+                           SNDRV_PCM_RATE_44100 | \
+                           SNDRV_PCM_RATE_48000 | \
+                           SNDRV_PCM_RATE_96000)
+
+#define RK3036_CODEC_FMTS (SNDRV_PCM_FMTBIT_S16_LE  | \
+                          SNDRV_PCM_FMTBIT_S20_3LE | \
+                          SNDRV_PCM_FMTBIT_S24_LE  | \
+                          SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops rk3036_codec_dai_ops = {
+       .set_fmt        = rk3036_codec_dai_set_fmt,
+       .hw_params      = rk3036_codec_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
+       {
+               .name = "rk3036-codec-dai",
+               .playback = {
+                       .stream_name = "Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = RK3036_CODEC_RATES,
+                       .formats = RK3036_CODEC_FMTS,
+               },
+               .ops = &rk3036_codec_dai_ops,
+               .symmetric_rates = 1,
+       },
+};
+
+static void rk3036_codec_reset(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, INNO_R00,
+                     INNO_R00_CSR_RESET | INNO_R00_CDCR_RESET);
+       snd_soc_write(codec, INNO_R00,
+                     INNO_R00_CSR_WORK | INNO_R00_CDCR_WORK);
+}
+
+static int rk3036_codec_probe(struct snd_soc_codec *codec)
+{
+       rk3036_codec_reset(codec);
+       return 0;
+}
+
+static int rk3036_codec_remove(struct snd_soc_codec *codec)
+{
+       rk3036_codec_reset(codec);
+       return 0;
+}
+
+static int rk3036_codec_set_bias_level(struct snd_soc_codec *codec,
+                                      enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_STANDBY:
+               /* set a big current for capacitor charging. */
+               snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
+               /* start precharge */
+               snd_soc_write(codec, INNO_R06, INNO_R06_DAC_PRECHARGE);
+
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               /* set a big current for capacitor discharging. */
+               snd_soc_write(codec, INNO_R10, INNO_R10_MAX_CUR);
+               /* start discharge. */
+               snd_soc_write(codec, INNO_R06, INNO_R06_DAC_DISCHARGE);
+
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver rk3036_codec_driver = {
+       .probe                  = rk3036_codec_probe,
+       .remove                 = rk3036_codec_remove,
+       .set_bias_level         = rk3036_codec_set_bias_level,
+       .controls               = rk3036_codec_dapm_controls,
+       .num_controls           = ARRAY_SIZE(rk3036_codec_dapm_controls),
+       .dapm_routes            = rk3036_codec_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(rk3036_codec_dapm_routes),
+       .dapm_widgets           = rk3036_codec_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(rk3036_codec_dapm_widgets),
+};
+
+static const struct regmap_config rk3036_codec_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+};
+
+#define GRF_SOC_CON0           0x00140
+#define GRF_ACODEC_SEL         (BIT(10) | BIT(16 + 10))
+
+static int rk3036_codec_platform_probe(struct platform_device *pdev)
+{
+       struct rk3036_codec_priv *priv;
+       struct device_node *of_node = pdev->dev.of_node;
+       struct resource *res;
+       void __iomem *base;
+       struct regmap *grf;
+       int ret;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       priv->base = base;
+       priv->regmap = devm_regmap_init_mmio(&pdev->dev, priv->base,
+                                            &rk3036_codec_regmap_config);
+       if (IS_ERR(priv->regmap)) {
+               dev_err(&pdev->dev, "init regmap failed\n");
+               return PTR_ERR(priv->regmap);
+       }
+
+       grf = syscon_regmap_lookup_by_phandle(of_node, "rockchip,grf");
+       if (IS_ERR(grf)) {
+               dev_err(&pdev->dev, "needs 'rockchip,grf' property\n");
+               return PTR_ERR(grf);
+       }
+       ret = regmap_write(grf, GRF_SOC_CON0, GRF_ACODEC_SEL);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not write to GRF: %d\n", ret);
+               return ret;
+       }
+
+       priv->pclk = devm_clk_get(&pdev->dev, "acodec_pclk");
+       if (IS_ERR(priv->pclk))
+               return PTR_ERR(priv->pclk);
+
+       ret = clk_prepare_enable(priv->pclk);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "failed to enable clk\n");
+               return ret;
+       }
+
+       priv->dev = &pdev->dev;
+       dev_set_drvdata(&pdev->dev, priv);
+
+       ret = snd_soc_register_codec(&pdev->dev, &rk3036_codec_driver,
+                                    rk3036_codec_dai_driver,
+                                    ARRAY_SIZE(rk3036_codec_dai_driver));
+       if (ret) {
+               clk_disable_unprepare(priv->pclk);
+               dev_set_drvdata(&pdev->dev, NULL);
+       }
+
+       return ret;
+}
+
+static int rk3036_codec_platform_remove(struct platform_device *pdev)
+{
+       struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
+
+       snd_soc_unregister_codec(&pdev->dev);
+       clk_disable_unprepare(priv->pclk);
+
+       return 0;
+}
+
+static const struct of_device_id rk3036_codec_of_match[] = {
+       { .compatible = "rockchip,rk3036-codec", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, rk3036_codec_of_match);
+
+static struct platform_driver rk3036_codec_platform_driver = {
+       .driver = {
+               .name = "rk3036-codec-platform",
+               .of_match_table = of_match_ptr(rk3036_codec_of_match),
+       },
+       .probe = rk3036_codec_platform_probe,
+       .remove = rk3036_codec_platform_remove,
+};
+
+module_platform_driver(rk3036_codec_platform_driver);
+
+MODULE_AUTHOR("Rockchip Inc.");
+MODULE_DESCRIPTION("Rockchip rk3036 codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/inno_rk3036.h b/sound/soc/codecs/inno_rk3036.h
new file mode 100644 (file)
index 0000000..da759c6
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Driver of Inno Codec for rk3036 by Rockchip Inc.
+ *
+ * Author: Zheng ShunQian<zhengsq@rock-chips.com>
+ */
+
+#ifndef _INNO_RK3036_CODEC_H
+#define _INNO_RK3036_CODEC_H
+
+/* codec registers */
+#define INNO_R00       0x00
+#define INNO_R01       0x0c
+#define INNO_R02       0x10
+#define INNO_R03       0x14
+#define INNO_R04       0x88
+#define INNO_R05       0x8c
+#define INNO_R06       0x90
+#define INNO_R07       0x94
+#define INNO_R08       0x98
+#define INNO_R09       0x9c
+#define INNO_R10       0xa0
+
+/* register bit filed */
+#define INNO_R00_CSR_RESET             (0x0 << 0) /*codec system reset*/
+#define INNO_R00_CSR_WORK              (0x1 << 0)
+#define INNO_R00_CDCR_RESET            (0x0 << 1) /*codec digital core reset*/
+#define INNO_R00_CDCR_WORK             (0x1 << 1)
+#define INNO_R00_PRB_DISABLE           (0x0 << 6) /*power reset bypass*/
+#define INNO_R00_PRB_ENABLE            (0x1 << 6)
+
+#define INNO_R01_I2SMODE_MSK           (0x1 << 4)
+#define INNO_R01_I2SMODE_SLAVE         (0x0 << 4)
+#define INNO_R01_I2SMODE_MASTER                (0x1 << 4)
+#define INNO_R01_PINDIR_MSK            (0x1 << 5)
+#define INNO_R01_PINDIR_IN_SLAVE       (0x0 << 5) /*direction of pin*/
+#define INNO_R01_PINDIR_OUT_MASTER     (0x1 << 5)
+
+#define INNO_R02_LRS_MSK               (0x1 << 2)
+#define INNO_R02_LRS_NORMAL            (0x0 << 2) /*DAC Left Right Swap*/
+#define INNO_R02_LRS_SWAP              (0x1 << 2)
+#define INNO_R02_DACM_MSK              (0x3 << 3)
+#define INNO_R02_DACM_PCM              (0x3 << 3) /*DAC Mode*/
+#define INNO_R02_DACM_I2S              (0x2 << 3)
+#define INNO_R02_DACM_LJM              (0x1 << 3)
+#define INNO_R02_DACM_RJM              (0x0 << 3)
+#define INNO_R02_VWL_MSK               (0x3 << 5)
+#define INNO_R02_VWL_32BIT             (0x3 << 5) /*1/2Frame Valid Word Len*/
+#define INNO_R02_VWL_24BIT             (0x2 << 5)
+#define INNO_R02_VWL_20BIT             (0x1 << 5)
+#define INNO_R02_VWL_16BIT             (0x0 << 5)
+#define INNO_R02_LRCP_MSK              (0x1 << 7)
+#define INNO_R02_LRCP_NORMAL           (0x0 << 7) /*Left Right Polarity*/
+#define INNO_R02_LRCP_REVERSAL         (0x1 << 7)
+
+#define INNO_R03_BCP_MSK               (0x1 << 0)
+#define INNO_R03_BCP_NORMAL            (0x0 << 0) /*DAC bit clock polarity*/
+#define INNO_R03_BCP_REVERSAL          (0x1 << 0)
+#define INNO_R03_DACR_MSK              (0x1 << 1)
+#define INNO_R03_DACR_RESET            (0x0 << 1) /*DAC Reset*/
+#define INNO_R03_DACR_WORK             (0x1 << 1)
+#define INNO_R03_FWL_MSK               (0x3 << 2)
+#define INNO_R03_FWL_32BIT             (0x3 << 2) /*1/2Frame Word Length*/
+#define INNO_R03_FWL_24BIT             (0x2 << 2)
+#define INNO_R03_FWL_20BIT             (0x1 << 2)
+#define INNO_R03_FWL_16BIT             (0x0 << 2)
+
+#define INNO_R04_DACR_SW_SHIFT         0
+#define INNO_R04_DACL_SW_SHIFT         1
+#define INNO_R04_DACR_CLK_SHIFT                2
+#define INNO_R04_DACL_CLK_SHIFT                3
+#define INNO_R04_DACR_VREF_SHIFT       4
+#define INNO_R04_DACL_VREF_SHIFT       5
+
+#define INNO_R05_HPR_EN_SHIFT          0
+#define INNO_R05_HPL_EN_SHIFT          1
+#define INNO_R05_HPR_WORK_SHIFT                2
+#define INNO_R05_HPL_WORK_SHIFT                3
+
+#define INNO_R06_VOUTR_CZ_SHIFT                0
+#define INNO_R06_VOUTL_CZ_SHIFT                1
+#define INNO_R06_DACR_HILO_VREF_SHIFT  2
+#define INNO_R06_DACL_HILO_VREF_SHIFT  3
+#define INNO_R06_DAC_EN_SHIFT          5
+
+#define INNO_R06_DAC_PRECHARGE         (0x0 << 4) /*PreCharge control for DAC*/
+#define INNO_R06_DAC_DISCHARGE         (0x1 << 4)
+
+#define INNO_HP_GAIN_SHIFT             0
+/* Gain of output, 1.5db step: -39db(0x0) ~ 0db(0x1a) ~ 6db(0x1f) */
+#define INNO_HP_GAIN_0DB               0x1a
+#define INNO_HP_GAIN_N39DB             0x0
+
+#define INNO_R09_HP_ANTIPOP_MSK                0x3
+#define INNO_R09_HP_ANTIPOP_OFF                0x1
+#define INNO_R09_HP_ANTIPOP_ON         0x2
+#define INNO_R09_HPR_ANITPOP_SHIFT     0
+#define INNO_R09_HPL_ANITPOP_SHIFT     2
+#define INNO_R09_HPR_MUTE_SHIFT                4
+#define INNO_R09_HPL_MUTE_SHIFT                5
+#define INNO_R09_DACR_SWITCH_SHIFT     6
+#define INNO_R09_DACL_SWITCH_SHIFT     7
+
+#define INNO_R10_CHARGE_SEL_CUR_400I_YES       (0x0 << 0)
+#define INNO_R10_CHARGE_SEL_CUR_400I_NO                (0x1 << 0)
+#define INNO_R10_CHARGE_SEL_CUR_260I_YES       (0x0 << 1)
+#define INNO_R10_CHARGE_SEL_CUR_260I_NO                (0x1 << 1)
+#define INNO_R10_CHARGE_SEL_CUR_130I_YES       (0x0 << 2)
+#define INNO_R10_CHARGE_SEL_CUR_130I_NO                (0x1 << 2)
+#define INNO_R10_CHARGE_SEL_CUR_100I_YES       (0x0 << 3)
+#define INNO_R10_CHARGE_SEL_CUR_100I_NO                (0x1 << 3)
+#define INNO_R10_CHARGE_SEL_CUR_050I_YES       (0x0 << 4)
+#define INNO_R10_CHARGE_SEL_CUR_050I_NO                (0x1 << 4)
+#define INNO_R10_CHARGE_SEL_CUR_027I_YES       (0x0 << 5)
+#define INNO_R10_CHARGE_SEL_CUR_027I_NO                (0x1 << 5)
+
+#define INNO_R10_MAX_CUR (INNO_R10_CHARGE_SEL_CUR_400I_YES | \
+                         INNO_R10_CHARGE_SEL_CUR_260I_YES | \
+                         INNO_R10_CHARGE_SEL_CUR_130I_YES | \
+                         INNO_R10_CHARGE_SEL_CUR_100I_YES | \
+                         INNO_R10_CHARGE_SEL_CUR_050I_YES | \
+                         INNO_R10_CHARGE_SEL_CUR_027I_YES)
+
+#endif
index 6147e86..416ea64 100644 (file)
@@ -63,8 +63,7 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
                sysclk    = params_rate(params) * 512;
                sspa_mclk = params_rate(params) * 64;
        }
-       sspa_div = freq_out;
-       do_div(sspa_div, sspa_mclk);
+       sspa_div = freq_out / sspa_mclk;
 
        snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0);
        snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk);
index e5101e0..00b6c9d 100644 (file)
@@ -355,6 +355,7 @@ static struct regmap_config lpass_cpu_regmap_config = {
        .readable_reg = lpass_cpu_regmap_readable,
        .volatile_reg = lpass_cpu_regmap_volatile,
        .cache_type = REGCACHE_FLAT,
+       .val_format_endian = REGMAP_ENDIAN_LITTLE,
 };
 
 int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
index 58ee645..8b0a588 100644 (file)
@@ -34,13 +34,7 @@ struct rk_i2s_dev {
 
        struct regmap *regmap;
 
-/*
- * Used to indicate the tx/rx status.
- * I2S controller hopes to start the tx and rx together,
- * also to stop them when they are both try to stop.
-*/
-       bool tx_start;
-       bool rx_start;
+       bool is_master_mode;
 };
 
 static int i2s_runtime_suspend(struct device *dev)
@@ -81,37 +75,29 @@ static void rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
                                   I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_ENABLE);
 
                regmap_update_bits(i2s->regmap, I2S_XFER,
-                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START,
-                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START);
-
-               i2s->tx_start = true;
+                                  I2S_XFER_TXS_START,
+                                  I2S_XFER_TXS_START);
        } else {
-               i2s->tx_start = false;
-
                regmap_update_bits(i2s->regmap, I2S_DMACR,
                                   I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE);
 
-               if (!i2s->rx_start) {
-                       regmap_update_bits(i2s->regmap, I2S_XFER,
-                                          I2S_XFER_TXS_START |
-                                          I2S_XFER_RXS_START,
-                                          I2S_XFER_TXS_STOP |
-                                          I2S_XFER_RXS_STOP);
+               regmap_update_bits(i2s->regmap, I2S_XFER,
+                                  I2S_XFER_TXS_START,
+                                  I2S_XFER_TXS_STOP);
 
-                       regmap_update_bits(i2s->regmap, I2S_CLR,
-                                          I2S_CLR_TXC | I2S_CLR_RXC,
-                                          I2S_CLR_TXC | I2S_CLR_RXC);
+               regmap_update_bits(i2s->regmap, I2S_CLR,
+                                  I2S_CLR_TXC,
+                                  I2S_CLR_TXC);
 
-                       regmap_read(i2s->regmap, I2S_CLR, &val);
+               regmap_read(i2s->regmap, I2S_CLR, &val);
 
-                       /* Should wait for clear operation to finish */
-                       while (val) {
-                               regmap_read(i2s->regmap, I2S_CLR, &val);
-                               retry--;
-                               if (!retry) {
-                                       dev_warn(i2s->dev, "fail to clear\n");
-                                       break;
-                               }
+               /* Should wait for clear operation to finish */
+               while (val & I2S_CLR_TXC) {
+                       regmap_read(i2s->regmap, I2S_CLR, &val);
+                       retry--;
+                       if (!retry) {
+                               dev_warn(i2s->dev, "fail to clear\n");
+                               break;
                        }
                }
        }
@@ -127,37 +113,29 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
                                   I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_ENABLE);
 
                regmap_update_bits(i2s->regmap, I2S_XFER,
-                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START,
-                                  I2S_XFER_TXS_START | I2S_XFER_RXS_START);
-
-               i2s->rx_start = true;
+                                  I2S_XFER_RXS_START,
+                                  I2S_XFER_RXS_START);
        } else {
-               i2s->rx_start = false;
-
                regmap_update_bits(i2s->regmap, I2S_DMACR,
                                   I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE);
 
-               if (!i2s->tx_start) {
-                       regmap_update_bits(i2s->regmap, I2S_XFER,
-                                          I2S_XFER_TXS_START |
-                                          I2S_XFER_RXS_START,
-                                          I2S_XFER_TXS_STOP |
-                                          I2S_XFER_RXS_STOP);
+               regmap_update_bits(i2s->regmap, I2S_XFER,
+                                  I2S_XFER_RXS_START,
+                                  I2S_XFER_RXS_STOP);
 
-                       regmap_update_bits(i2s->regmap, I2S_CLR,
-                                          I2S_CLR_TXC | I2S_CLR_RXC,
-                                          I2S_CLR_TXC | I2S_CLR_RXC);
+               regmap_update_bits(i2s->regmap, I2S_CLR,
+                                  I2S_CLR_RXC,
+                                  I2S_CLR_RXC);
 
-                       regmap_read(i2s->regmap, I2S_CLR, &val);
+               regmap_read(i2s->regmap, I2S_CLR, &val);
 
-                       /* Should wait for clear operation to finish */
-                       while (val) {
-                               regmap_read(i2s->regmap, I2S_CLR, &val);
-                               retry--;
-                               if (!retry) {
-                                       dev_warn(i2s->dev, "fail to clear\n");
-                                       break;
-                               }
+               /* Should wait for clear operation to finish */
+               while (val & I2S_CLR_RXC) {
+                       regmap_read(i2s->regmap, I2S_CLR, &val);
+                       retry--;
+                       if (!retry) {
+                               dev_warn(i2s->dev, "fail to clear\n");
+                               break;
                        }
                }
        }
@@ -174,9 +152,11 @@ static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
        case SND_SOC_DAIFMT_CBS_CFS:
                /* Set source clock in Master mode */
                val = I2S_CKR_MSS_MASTER;
+               i2s->is_master_mode = true;
                break;
        case SND_SOC_DAIFMT_CBM_CFM:
                val = I2S_CKR_MSS_SLAVE;
+               i2s->is_master_mode = false;
                break;
        default:
                return -EINVAL;
@@ -228,6 +208,26 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
        struct rk_i2s_dev *i2s = to_info(dai);
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        unsigned int val = 0;
+       unsigned int mclk_rate, bclk_rate, div_bclk, div_lrck;
+
+       if (i2s->is_master_mode) {
+               mclk_rate = clk_get_rate(i2s->mclk);
+               bclk_rate = 2 * 32 * params_rate(params);
+               if (bclk_rate && mclk_rate % bclk_rate)
+                       return -EINVAL;
+
+               div_bclk = mclk_rate / bclk_rate;
+               div_lrck = bclk_rate / params_rate(params);
+               regmap_update_bits(i2s->regmap, I2S_CKR,
+                                  I2S_CKR_MDIV_MASK,
+                                  I2S_CKR_MDIV(div_bclk));
+
+               regmap_update_bits(i2s->regmap, I2S_CKR,
+                                  I2S_CKR_TSD_MASK |
+                                  I2S_CKR_RSD_MASK,
+                                  I2S_CKR_TSD(div_lrck) |
+                                  I2S_CKR_RSD(div_lrck));
+       }
 
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S8:
@@ -451,6 +451,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
 {
        struct device_node *node = pdev->dev.of_node;
        struct rk_i2s_dev *i2s;
+       struct snd_soc_dai_driver *soc_dai;
        struct resource *res;
        void __iomem *regs;
        int ret;
@@ -511,17 +512,26 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
                        goto err_pm_disable;
        }
 
-       /* refine capture channels */
+       soc_dai = devm_kzalloc(&pdev->dev,
+                              sizeof(*soc_dai), GFP_KERNEL);
+       if (!soc_dai)
+               return -ENOMEM;
+
+       memcpy(soc_dai, &rockchip_i2s_dai, sizeof(*soc_dai));
+       if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) {
+               if (val >= 2 && val <= 8)
+                       soc_dai->playback.channels_max = val;
+       }
+
        if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) {
                if (val >= 2 && val <= 8)
-                       rockchip_i2s_dai.capture.channels_max = val;
-               else
-                       rockchip_i2s_dai.capture.channels_max = 2;
+                       soc_dai->capture.channels_max = val;
        }
 
        ret = devm_snd_soc_register_component(&pdev->dev,
                                              &rockchip_i2s_component,
-                                             &rockchip_i2s_dai, 1);
+                                             soc_dai, 1);
+
        if (ret) {
                dev_err(&pdev->dev, "Could not register DAI\n");
                goto err_suspend;
index 26567b1..5436102 100644 (file)
@@ -80,11 +80,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
        switch (params_rate(params)) {
        case 8000:
        case 16000:
+       case 24000:
+       case 32000:
        case 48000:
+       case 64000:
        case 96000:
                mclk = 12288000;
                break;
+       case 11025:
+       case 22050:
        case 44100:
+       case 88200:
                mclk = 11289600;
                break;
        default:
index 68c62e4..440a802 100644 (file)
@@ -79,11 +79,17 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
        switch (params_rate(params)) {
        case 8000:
        case 16000:
+       case 24000:
+       case 32000:
        case 48000:
+       case 64000:
        case 96000:
                mclk = 12288000;
                break;
+       case 11025:
+       case 22050:
        case 44100:
+       case 88200:
                mclk = 11289600;
                break;
        default:
index 206d1ed..c9902a6 100644 (file)
@@ -36,7 +36,6 @@ config SND_SOC_SH4_SIU
 
 config SND_SOC_RCAR
        tristate "R-Car series SRU/SCU/SSIU/SSI support"
-       depends on DMA_OF
        depends on COMMON_CLK
        select SND_SIMPLE_CARD
        select REGMAP_MMIO
index 8b25850..a89ddf7 100644 (file)
@@ -1,4 +1,4 @@
-snd-soc-rcar-objs      := core.o gen.o dma.o adg.o ssi.o src.o ctu.o mix.o dvc.o
+snd-soc-rcar-objs      := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o
 obj-$(CONFIG_SND_SOC_RCAR)     += snd-soc-rcar.o
 
 snd-soc-rsrc-card-objs := rsrc-card.o
index 2a5b3a2..6d3ef36 100644 (file)
@@ -68,8 +68,8 @@ static u32 rsnd_adg_calculate_rbgx(unsigned long div)
 
 static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
 {
-       struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
-       int id = rsnd_mod_id(mod);
+       struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+       int id = rsnd_mod_id(ssi_mod);
        int ws = id;
 
        if (rsnd_ssi_is_pin_sharing(io)) {
@@ -90,13 +90,13 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
        return (0x6 + ws) << 8;
 }
 
-int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
                                 struct rsnd_dai_stream *io)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(cmd_mod);
        struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
        struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
-       int id = rsnd_mod_id(mod);
+       int id = rsnd_mod_id(cmd_mod);
        int shift = (id % 2) ? 16 : 0;
        u32 mask, val;
 
@@ -242,68 +242,6 @@ int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
        return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
 }
 
-int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
-                                 struct rsnd_mod *mod,
-                                 unsigned int src_rate,
-                                 unsigned int dst_rate)
-{
-       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
-       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
-       struct device *dev = rsnd_priv_to_dev(priv);
-       int idx, sel, div, shift;
-       u32 mask, val;
-       int id = rsnd_mod_id(mod);
-       unsigned int sel_rate [] = {
-               clk_get_rate(adg->clk[CLKA]),   /* 000: CLKA */
-               clk_get_rate(adg->clk[CLKB]),   /* 001: CLKB */
-               clk_get_rate(adg->clk[CLKC]),   /* 010: CLKC */
-               0,                              /* 011: MLBCLK (not used) */
-               adg->rbga_rate_for_441khz,      /* 100: RBGA */
-               adg->rbgb_rate_for_48khz,       /* 101: RBGB */
-       };
-
-       /* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */
-       for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
-               for (div  = 128,        idx = 0;
-                    div <= 2048;
-                    div *= 2,          idx++) {
-                       if (src_rate == sel_rate[sel] / div) {
-                               val = (idx << 4) | sel;
-                               goto find_rate;
-                       }
-               }
-       }
-       dev_err(dev, "can't find convert src clk\n");
-       return -EINVAL;
-
-find_rate:
-       shift   = (id % 4) * 8;
-       mask    = 0xFF << shift;
-       val     = val << shift;
-
-       dev_dbg(dev, "adg convert src clk = %02x\n", val);
-
-       switch (id / 4) {
-       case 0:
-               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val);
-               break;
-       case 1:
-               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val);
-               break;
-       case 2:
-               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val);
-               break;
-       }
-
-       /*
-        * Gen1 doesn't need dst_rate settings,
-        * since it uses SSI WS pin.
-        * see also rsnd_src_set_route_if_gen1()
-        */
-
-       return 0;
-}
-
 static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
@@ -337,20 +275,16 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
        }
 }
 
-int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod)
+int rsnd_adg_ssi_clk_stop(struct rsnd_mod *ssi_mod)
 {
-       /*
-        * "mod" = "ssi" here.
-        * we can get "ssi id" from mod
-        */
-       rsnd_adg_set_ssi_clk(mod, 0);
+       rsnd_adg_set_ssi_clk(ssi_mod, 0);
 
        return 0;
 }
 
-int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
+int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
        struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
        struct device *dev = rsnd_priv_to_dev(priv);
        struct clk *clk;
@@ -394,14 +328,10 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
 
 found_clock:
 
-       /*
-        * This "mod" = "ssi" here.
-        * we can get "ssi id" from mod
-        */
-       rsnd_adg_set_ssi_clk(mod, data);
+       rsnd_adg_set_ssi_clk(ssi_mod, data);
 
        dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod),
+               rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod),
                data, rate);
 
        return 0;
@@ -418,15 +348,20 @@ static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
                [CLKC]  = "clk_c",
                [CLKI]  = "clk_i",
        };
-       int i;
+       int i, ret;
 
        for (i = 0; i < CLKMAX; i++) {
                clk = devm_clk_get(dev, clk_name[i]);
                adg->clk[i] = IS_ERR(clk) ? NULL : clk;
        }
 
-       for_each_rsnd_clk(clk, adg, i)
+       for_each_rsnd_clk(clk, adg, i) {
+               ret = clk_prepare_enable(clk);
+               if (ret < 0)
+                       dev_warn(dev, "can't use clk %d\n", i);
+
                dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+       }
 }
 
 static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
@@ -437,7 +372,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
        struct device *dev = rsnd_priv_to_dev(priv);
        struct device_node *np = dev->of_node;
        u32 ckr, rbgx, rbga, rbgb;
-       u32 rate, req_rate, div;
+       u32 rate, req_rate = 0, div;
        uint32_t count = 0;
        unsigned long req_48kHz_rate, req_441kHz_rate;
        int i;
@@ -572,9 +507,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
                ckr, rbga, rbgb);
 }
 
-int rsnd_adg_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv)
+int rsnd_adg_probe(struct rsnd_priv *priv)
 {
        struct rsnd_adg *adg;
        struct device *dev = rsnd_priv_to_dev(priv);
@@ -600,3 +533,14 @@ int rsnd_adg_probe(struct platform_device *pdev,
 
        return 0;
 }
+
+void rsnd_adg_remove(struct rsnd_priv *priv)
+{
+       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct clk *clk;
+       int i;
+
+       for_each_rsnd_clk(clk, adg, i) {
+               clk_disable_unprepare(clk);
+       }
+}
diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c
new file mode 100644 (file)
index 0000000..cd1f064
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Renesas R-Car CMD support
+ *
+ * Copyright (C) 2015 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "rsnd.h"
+
+struct rsnd_cmd {
+       struct rsnd_mod mod;
+};
+
+#define CMD_NAME "cmd"
+
+#define rsnd_cmd_nr(priv) ((priv)->cmd_nr)
+#define for_each_rsnd_cmd(pos, priv, i)                                        \
+       for ((i) = 0;                                                   \
+            ((i) < rsnd_cmd_nr(priv)) &&                               \
+                    ((pos) = (struct rsnd_cmd *)(priv)->cmd + i);      \
+            i++)
+
+static int rsnd_cmd_init(struct rsnd_mod *mod,
+                        struct rsnd_dai_stream *io,
+                        struct rsnd_priv *priv)
+{
+       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 device *dev = rsnd_priv_to_dev(priv);
+       u32 data;
+
+       if (!mix && !dvc)
+               return 0;
+
+       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)];
+               }
+
+       } else {
+               u32 path[] = {
+                       [0] = 0x30000,
+                       [1] = 0x30001,
+                       [2] = 0x40000,
+                       [3] = 0x10000,
+                       [4] = 0x20000,
+                       [5] = 0x40100
+               };
+
+               data = path[rsnd_mod_id(src)];
+       }
+
+       dev_dbg(dev, "ctu/mix path = 0x%08x", data);
+
+       rsnd_mod_write(mod, CMD_ROUTE_SLCT, data);
+       rsnd_mod_write(mod, CMD_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
+
+       rsnd_adg_set_cmd_timsel_gen2(mod, io);
+
+       return 0;
+}
+
+static int rsnd_cmd_start(struct rsnd_mod *mod,
+                         struct rsnd_dai_stream *io,
+                         struct rsnd_priv *priv)
+{
+       rsnd_mod_write(mod, CMD_CTRL, 0x10);
+
+       return 0;
+}
+
+static int rsnd_cmd_stop(struct rsnd_mod *mod,
+                        struct rsnd_dai_stream *io,
+                        struct rsnd_priv *priv)
+{
+       rsnd_mod_write(mod, CMD_CTRL, 0);
+
+       return 0;
+}
+
+static struct rsnd_mod_ops rsnd_cmd_ops = {
+       .name   = CMD_NAME,
+       .init   = rsnd_cmd_init,
+       .start  = rsnd_cmd_start,
+       .stop   = rsnd_cmd_stop,
+};
+
+int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id)
+{
+       struct rsnd_priv *priv = rsnd_io_to_priv(io);
+       struct rsnd_mod *mod = rsnd_cmd_mod_get(priv, id);
+
+       return rsnd_dai_connect(mod, io, mod->type);
+}
+
+struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id)
+{
+       if (WARN_ON(id < 0 || id >= rsnd_cmd_nr(priv)))
+               id = 0;
+
+       return rsnd_mod_get((struct rsnd_cmd *)(priv->cmd) + id);
+}
+
+int rsnd_cmd_probe(struct rsnd_priv *priv)
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_cmd *cmd;
+       int i, nr, ret;
+
+       /* This driver doesn't support Gen1 at this point */
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       /* same number as DVC */
+       nr = priv->dvc_nr;
+       if (!nr)
+               return 0;
+
+       cmd = devm_kzalloc(dev, sizeof(*cmd) * nr, GFP_KERNEL);
+       if (!cmd)
+               return -ENOMEM;
+
+       priv->cmd_nr    = nr;
+       priv->cmd       = cmd;
+
+       for_each_rsnd_cmd(cmd, priv, i) {
+               ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
+                                   &rsnd_cmd_ops, NULL, RSND_MOD_CMD, i);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+void rsnd_cmd_remove(struct rsnd_priv *priv)
+{
+       struct rsnd_cmd *cmd;
+       int i;
+
+       for_each_rsnd_cmd(cmd, priv, i) {
+               rsnd_mod_quit(rsnd_mod_get(cmd));
+       }
+}
index e1da565..02b4b08 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;
 }
 
 /*
@@ -1076,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);
 }
@@ -1160,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
                 */
@@ -1174,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
@@ -1199,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
         */
@@ -1236,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;
        }
@@ -1296,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;
 
@@ -1314,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);
index 3cb214a..d53a225 100644 (file)
@@ -13,7 +13,6 @@
 #define CTU_NAME "ctu"
 
 struct rsnd_ctu {
-       struct rsnd_ctu_platform_info *info; /* rcar_snd.h */
        struct rsnd_mod mod;
 };
 
@@ -24,6 +23,7 @@ struct rsnd_ctu {
                     ((pos) = (struct rsnd_ctu *)(priv)->ctu + i);      \
             i++)
 
+#define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id)
 #define rsnd_ctu_initialize_lock(mod)  __rsnd_ctu_initialize_lock(mod, 1)
 #define rsnd_ctu_initialize_unlock(mod)        __rsnd_ctu_initialize_lock(mod, 0)
 static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
@@ -31,6 +31,13 @@ static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
        rsnd_mod_write(mod, CTU_CTUIR, enable);
 }
 
+static int rsnd_ctu_probe_(struct rsnd_mod *mod,
+                          struct rsnd_dai_stream *io,
+                          struct rsnd_priv *priv)
+{
+       return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4);
+}
+
 static int rsnd_ctu_init(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
@@ -57,6 +64,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod,
 
 static struct rsnd_mod_ops rsnd_ctu_ops = {
        .name           = CTU_NAME,
+       .probe          = rsnd_ctu_probe_,
        .init           = rsnd_ctu_init,
        .quit           = rsnd_ctu_quit,
 };
@@ -66,51 +74,13 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
                id = 0;
 
-       return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id);
+       return rsnd_mod_get(rsnd_ctu_get(priv, id));
 }
 
-static void rsnd_of_parse_ctu(struct platform_device *pdev,
-                      const struct rsnd_of_data *of_data,
-                      struct rsnd_priv *priv)
+int rsnd_ctu_probe(struct rsnd_priv *priv)
 {
        struct device_node *node;
-       struct rsnd_ctu_platform_info *ctu_info;
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
-       struct device *dev = &pdev->dev;
-       int nr;
-
-       if (!of_data)
-               return;
-
-       node = of_get_child_by_name(dev->of_node, "rcar_sound,ctu");
-       if (!node)
-               return;
-
-       nr = of_get_child_count(node);
-       if (!nr)
-               goto rsnd_of_parse_ctu_end;
-
-       ctu_info = devm_kzalloc(dev,
-                               sizeof(struct rsnd_ctu_platform_info) * nr,
-                               GFP_KERNEL);
-       if (!ctu_info) {
-               dev_err(dev, "ctu info allocation error\n");
-               goto rsnd_of_parse_ctu_end;
-       }
-
-       info->ctu_info          = ctu_info;
-       info->ctu_info_nr       = nr;
-
-rsnd_of_parse_ctu_end:
-       of_node_put(node);
-
-}
-
-int rsnd_ctu_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv)
-{
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+       struct device_node *np;
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_ctu *ctu;
        struct clk *clk;
@@ -121,20 +91,30 @@ int rsnd_ctu_probe(struct platform_device *pdev,
        if (rsnd_is_gen1(priv))
                return 0;
 
-       rsnd_of_parse_ctu(pdev, of_data, priv);
+       node = rsnd_ctu_of_node(priv);
+       if (!node)
+               return 0; /* not used is not error */
 
-       nr = info->ctu_info_nr;
-       if (!nr)
-               return 0;
+       nr = of_get_child_count(node);
+       if (!nr) {
+               ret = -EINVAL;
+               goto rsnd_ctu_probe_done;
+       }
 
        ctu = devm_kzalloc(dev, sizeof(*ctu) * nr, GFP_KERNEL);
-       if (!ctu)
-               return -ENOMEM;
+       if (!ctu) {
+               ret = -ENOMEM;
+               goto rsnd_ctu_probe_done;
+       }
 
        priv->ctu_nr    = nr;
        priv->ctu       = ctu;
 
-       for_each_rsnd_ctu(ctu, priv, i) {
+       i = 0;
+       ret = 0;
+       for_each_child_of_node(node, np) {
+               ctu = rsnd_ctu_get(priv, i);
+
                /*
                 * CTU00, CTU01, CTU02, CTU03 => CTU0
                 * CTU10, CTU11, CTU12, CTU13 => CTU1
@@ -143,22 +123,27 @@ int rsnd_ctu_probe(struct platform_device *pdev,
                         CTU_NAME, i / 4);
 
                clk = devm_clk_get(dev, name);
-               if (IS_ERR(clk))
-                       return PTR_ERR(clk);
-
-               ctu->info = &info->ctu_info[i];
+               if (IS_ERR(clk)) {
+                       ret = PTR_ERR(clk);
+                       goto rsnd_ctu_probe_done;
+               }
 
                ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
                                    clk, RSND_MOD_CTU, i);
                if (ret)
-                       return ret;
+                       goto rsnd_ctu_probe_done;
+
+               i++;
        }
 
-       return 0;
+
+rsnd_ctu_probe_done:
+       of_node_put(node);
+
+       return ret;
 }
 
-void rsnd_ctu_remove(struct platform_device *pdev,
-                    struct rsnd_priv *priv)
+void rsnd_ctu_remove(struct rsnd_priv *priv)
 {
        struct rsnd_ctu *ctu;
        int i;
index 5d084d0..418e6fd 100644 (file)
 /* PDMACHCR */
 #define PDMACHCR_DE            (1 << 0)
 
+
+struct rsnd_dmaen {
+       struct dma_chan         *chan;
+};
+
+struct rsnd_dmapp {
+       int                     dmapp_id;
+       u32                     chcr;
+};
+
+struct rsnd_dma {
+       struct rsnd_mod         mod;
+       dma_addr_t              src_addr;
+       dma_addr_t              dst_addr;
+       union {
+               struct rsnd_dmaen en;
+               struct rsnd_dmapp pp;
+       } dma;
+};
+
 struct rsnd_dma_ctrl {
        void __iomem *base;
+       int dmaen_num;
        int dmapp_num;
 };
 
-struct rsnd_dma_ops {
-       char *name;
-       void (*start)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
-       void (*stop)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
-       int (*init)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id,
-                   struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
-       void (*quit)(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
-};
-
 #define rsnd_priv_to_dmac(p)   ((struct rsnd_dma_ctrl *)(p)->dma)
+#define rsnd_mod_to_dma(_mod) container_of((_mod), struct rsnd_dma, mod)
+#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
+#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
 
 /*
  *             Audio DMAC
@@ -77,18 +92,24 @@ static void rsnd_dmaen_complete(void *data)
        rsnd_mod_interrupt(mod, __rsnd_dmaen_complete);
 }
 
-static void rsnd_dmaen_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
+static int rsnd_dmaen_stop(struct rsnd_mod *mod,
+                          struct rsnd_dai_stream *io,
+                          struct rsnd_priv *priv)
 {
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
        struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
 
        dmaengine_terminate_all(dmaen->chan);
+
+       return 0;
 }
 
-static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
+static int rsnd_dmaen_start(struct rsnd_mod *mod,
+                           struct rsnd_dai_stream *io,
+                           struct rsnd_priv *priv)
 {
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
        struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
-       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct snd_pcm_substream *substream = io->substream;
        struct device *dev = rsnd_priv_to_dev(priv);
        struct dma_async_tx_descriptor *desc;
@@ -103,18 +124,20 @@ static void rsnd_dmaen_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
 
        if (!desc) {
                dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
-               return;
+               return -EIO;
        }
 
        desc->callback          = rsnd_dmaen_complete;
-       desc->callback_param    = mod;
+       desc->callback_param    = rsnd_mod_get(dma);
 
        if (dmaengine_submit(desc) < 0) {
                dev_err(dev, "dmaengine_submit() fail\n");
-               return;
+               return -EIO;
        }
 
        dma_async_issue_pending(dmaen->chan);
+
+       return 0;
 }
 
 struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
@@ -152,12 +175,29 @@ static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_dai_stream *io,
                return rsnd_mod_dma_req(io, mod_to);
 }
 
-static int rsnd_dmaen_init(struct rsnd_dai_stream *io,
+static int rsnd_dmaen_remove(struct rsnd_mod *mod,
+                             struct rsnd_dai_stream *io,
+                             struct rsnd_priv *priv)
+{
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
+       struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+
+       if (dmaen->chan)
+               dma_release_channel(dmaen->chan);
+
+       dmaen->chan = NULL;
+
+       return 0;
+}
+
+static int rsnd_dmaen_attach(struct rsnd_dai_stream *io,
                           struct rsnd_dma *dma, int id,
                           struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
 {
+       struct rsnd_mod *mod = rsnd_mod_get(dma);
        struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
        struct rsnd_priv *priv = rsnd_io_to_priv(io);
+       struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
        struct device *dev = rsnd_priv_to_dev(priv);
        struct dma_slave_config cfg = {};
        int is_play = rsnd_io_is_play(io);
@@ -191,18 +231,20 @@ static int rsnd_dmaen_init(struct rsnd_dai_stream *io,
        cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
        cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 
-       dev_dbg(dev, "%s %pad -> %pad\n",
-               dma->ops->name,
+       dev_dbg(dev, "%s[%d] %pad -> %pad\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod),
                &cfg.src_addr, &cfg.dst_addr);
 
        ret = dmaengine_slave_config(dmaen->chan, &cfg);
        if (ret < 0)
-               goto rsnd_dma_init_err;
+               goto rsnd_dma_attach_err;
+
+       dmac->dmaen_num++;
 
        return 0;
 
-rsnd_dma_init_err:
-       rsnd_dma_quit(io, dma);
+rsnd_dma_attach_err:
+       rsnd_dmaen_remove(mod, io, priv);
 rsnd_dma_channel_err:
 
        /*
@@ -214,22 +256,11 @@ rsnd_dma_channel_err:
        return -EAGAIN;
 }
 
-static void rsnd_dmaen_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
-{
-       struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
-
-       if (dmaen->chan)
-               dma_release_channel(dmaen->chan);
-
-       dmaen->chan = NULL;
-}
-
-static struct rsnd_dma_ops rsnd_dmaen_ops = {
+static struct rsnd_mod_ops rsnd_dmaen_ops = {
        .name   = "audmac",
        .start  = rsnd_dmaen_start,
        .stop   = rsnd_dmaen_stop,
-       .init   = rsnd_dmaen_init,
-       .quit   = rsnd_dmaen_quit,
+       .remove = rsnd_dmaen_remove,
 };
 
 /*
@@ -307,7 +338,7 @@ static u32 rsnd_dmapp_get_chcr(struct rsnd_dai_stream *io,
         (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id))
 static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
 {
-       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+       struct rsnd_mod *mod = rsnd_mod_get(dma);
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
        struct device *dev = rsnd_priv_to_dev(priv);
@@ -319,38 +350,48 @@ static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg)
 
 static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg)
 {
-       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+       struct rsnd_mod *mod = rsnd_mod_get(dma);
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
 
        return ioread32(rsnd_dmapp_addr(dmac, dma, reg));
 }
 
-static void rsnd_dmapp_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
+static int rsnd_dmapp_stop(struct rsnd_mod *mod,
+                          struct rsnd_dai_stream *io,
+                          struct rsnd_priv *priv)
 {
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
        int i;
 
        rsnd_dmapp_write(dma, 0, PDMACHCR);
 
        for (i = 0; i < 1024; i++) {
                if (0 == rsnd_dmapp_read(dma, PDMACHCR))
-                       return;
+                       return 0;
                udelay(1);
        }
+
+       return -EIO;
 }
 
-static void rsnd_dmapp_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
+static int rsnd_dmapp_start(struct rsnd_mod *mod,
+                           struct rsnd_dai_stream *io,
+                           struct rsnd_priv *priv)
 {
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
        struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
 
        rsnd_dmapp_write(dma, dma->src_addr,    PDMASAR);
        rsnd_dmapp_write(dma, dma->dst_addr,    PDMADAR);
        rsnd_dmapp_write(dma, dmapp->chcr,      PDMACHCR);
+
+       return 0;
 }
 
-static int rsnd_dmapp_init(struct rsnd_dai_stream *io,
-                          struct rsnd_dma *dma, int id,
-                          struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
+static int rsnd_dmapp_attach(struct rsnd_dai_stream *io,
+                            struct rsnd_dma *dma, int id,
+                            struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
 {
        struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma);
        struct rsnd_priv *priv = rsnd_io_to_priv(io);
@@ -362,19 +403,16 @@ static int rsnd_dmapp_init(struct rsnd_dai_stream *io,
 
        dmac->dmapp_num++;
 
-       rsnd_dmapp_stop(io, dma);
-
        dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n",
                dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr);
 
        return 0;
 }
 
-static struct rsnd_dma_ops rsnd_dmapp_ops = {
+static struct rsnd_mod_ops rsnd_dmapp_ops = {
        .name   = "audmac-pp",
        .start  = rsnd_dmapp_start,
        .stop   = rsnd_dmapp_stop,
-       .init   = rsnd_dmapp_init,
        .quit   = rsnd_dmapp_stop,
 };
 
@@ -497,13 +535,12 @@ static dma_addr_t rsnd_dma_addr(struct rsnd_dai_stream *io,
 }
 
 #define MOD_MAX (RSND_MOD_MAX + 1) /* +Memory */
-static void rsnd_dma_of_path(struct rsnd_dma *dma,
+static void rsnd_dma_of_path(struct rsnd_mod *this,
                             struct rsnd_dai_stream *io,
                             int is_play,
                             struct rsnd_mod **mod_from,
                             struct rsnd_mod **mod_to)
 {
-       struct rsnd_mod *this = rsnd_dma_to_mod(dma);
        struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
        struct rsnd_mod *src = rsnd_io_to_mod_src(io);
        struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io);
@@ -513,7 +550,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
        struct rsnd_mod *mod_start, *mod_end;
        struct rsnd_priv *priv = rsnd_mod_to_priv(this);
        struct device *dev = rsnd_priv_to_dev(priv);
-       int nr, i;
+       int nr, i, idx;
 
        if (!ssi)
                return;
@@ -542,23 +579,24 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
        mod_start       = (is_play) ? NULL : ssi;
        mod_end         = (is_play) ? ssi  : NULL;
 
-       mod[0] = mod_start;
+       idx = 0;
+       mod[idx++] = mod_start;
        for (i = 1; i < nr; i++) {
                if (src) {
-                       mod[i] = src;
+                       mod[idx++] = src;
                        src = NULL;
                } else if (ctu) {
-                       mod[i] = ctu;
+                       mod[idx++] = ctu;
                        ctu = NULL;
                } else if (mix) {
-                       mod[i] = mix;
+                       mod[idx++] = mix;
                        mix = NULL;
                } else if (dvc) {
-                       mod[i] = dvc;
+                       mod[idx++] = dvc;
                        dvc = NULL;
                }
        }
-       mod[i] = mod_end;
+       mod[idx] = mod_end;
 
        /*
         *              | SSI | SRC |
@@ -567,8 +605,8 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
         * !is_play     |  *  |  o  |
         */
        if ((this == ssi) == (is_play)) {
-               *mod_from       = mod[nr - 1];
-               *mod_to         = mod[nr];
+               *mod_from       = mod[idx - 1];
+               *mod_to         = mod[idx];
        } else {
                *mod_from       = mod[0];
                *mod_to         = mod[1];
@@ -576,7 +614,7 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
 
        dev_dbg(dev, "module connection (this is %s[%d])\n",
                rsnd_mod_name(this), rsnd_mod_id(this));
-       for (i = 0; i <= nr; i++) {
+       for (i = 0; i <= idx; i++) {
                dev_dbg(dev, "  %s[%d]%s\n",
                       rsnd_mod_name(mod[i]), rsnd_mod_id(mod[i]),
                       (mod[i] == *mod_from) ? " from" :
@@ -584,36 +622,22 @@ static void rsnd_dma_of_path(struct rsnd_dma *dma,
        }
 }
 
-void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
-{
-       dma->ops->stop(io, dma);
-}
-
-void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
-{
-       dma->ops->start(io, dma);
-}
-
-void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma)
-{
-       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-       struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
-
-       if (!dmac)
-               return;
-
-       dma->ops->quit(io, dma);
-}
-
-int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id)
+struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
+                                struct rsnd_mod *mod, int id)
 {
+       struct rsnd_mod *dma_mod;
        struct rsnd_mod *mod_from = NULL;
        struct rsnd_mod *mod_to = NULL;
        struct rsnd_priv *priv = rsnd_io_to_priv(io);
        struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+       struct rsnd_dma *dma;
        struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_mod_ops *ops;
+       enum rsnd_mod_type type;
+       int (*attach)(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id,
+                     struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
        int is_play = rsnd_io_is_play(io);
+       int ret, dma_id;
 
        /*
         * DMA failed. try to PIO mode
@@ -622,35 +646,64 @@ int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id)
         *      rsnd_rdai_continuance_probe()
         */
        if (!dmac)
-               return -EAGAIN;
+               return ERR_PTR(-EAGAIN);
 
-       rsnd_dma_of_path(dma, io, is_play, &mod_from, &mod_to);
+       dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+       if (!dma)
+               return ERR_PTR(-ENOMEM);
+
+       rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to);
 
        dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
        dma->dst_addr = rsnd_dma_addr(io, mod_to,   is_play, 0);
 
        /* for Gen2 */
-       if (mod_from && mod_to)
-               dma->ops = &rsnd_dmapp_ops;
-       else
-               dma->ops = &rsnd_dmaen_ops;
+       if (mod_from && mod_to) {
+               ops     = &rsnd_dmapp_ops;
+               attach  = rsnd_dmapp_attach;
+               dma_id  = dmac->dmapp_num;
+               type    = RSND_MOD_AUDMAPP;
+       } else {
+               ops     = &rsnd_dmaen_ops;
+               attach  = rsnd_dmaen_attach;
+               dma_id  = dmac->dmaen_num;
+               type    = RSND_MOD_AUDMA;
+       }
 
        /* for Gen1, overwrite */
-       if (rsnd_is_gen1(priv))
-               dma->ops = &rsnd_dmaen_ops;
+       if (rsnd_is_gen1(priv)) {
+               ops     = &rsnd_dmaen_ops;
+               attach  = rsnd_dmaen_attach;
+               dma_id  = dmac->dmaen_num;
+               type    = RSND_MOD_AUDMA;
+       }
+
+       dma_mod = rsnd_mod_get(dma);
+
+       ret = rsnd_mod_init(priv, dma_mod,
+                           ops, NULL, type, dma_id);
+       if (ret < 0)
+               return ERR_PTR(ret);
 
-       dev_dbg(dev, "%s %s[%d] -> %s[%d]\n",
-               dma->ops->name,
+       dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
+               rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod),
                rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
                rsnd_mod_name(mod_to),   rsnd_mod_id(mod_to));
 
-       return dma->ops->init(io, dma, id, mod_from, mod_to);
+       ret = attach(io, dma, id, mod_from, mod_to);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       ret = rsnd_dai_connect(dma_mod, io, type);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       return rsnd_mod_get(dma);
 }
 
-int rsnd_dma_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv)
+int rsnd_dma_probe(struct rsnd_priv *priv)
 {
+       struct platform_device *pdev = rsnd_priv_to_pdev(priv);
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_dma_ctrl *dmac;
        struct resource *res;
index 58f6909..d45ffe4 100644 (file)
@@ -15,7 +15,6 @@
 #define DVC_NAME "dvc"
 
 struct rsnd_dvc {
-       struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
        struct rsnd_mod mod;
        struct rsnd_kctrl_cfg_m volume;
        struct rsnd_kctrl_cfg_m mute;
@@ -24,6 +23,7 @@ struct rsnd_dvc {
        struct rsnd_kctrl_cfg_s rdown;  /* Ramp Rate Down */
 };
 
+#define rsnd_dvc_get(priv, id) ((struct rsnd_dvc *)(priv->dvc) + id)
 #define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
 #define rsnd_dvc_of_node(priv) \
        of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
@@ -64,79 +64,142 @@ static const char * const dvc_ramp_rate[] = {
        "0.125 dB/8192 steps",   /* 10111 */
 };
 
-static void rsnd_dvc_soft_reset(struct rsnd_mod *mod)
+static void rsnd_dvc_activation(struct rsnd_mod *mod)
 {
        rsnd_mod_write(mod, DVC_SWRSR, 0);
        rsnd_mod_write(mod, DVC_SWRSR, 1);
 }
 
-#define rsnd_dvc_initialize_lock(mod)  __rsnd_dvc_initialize_lock(mod, 1)
-#define rsnd_dvc_initialize_unlock(mod)        __rsnd_dvc_initialize_lock(mod, 0)
-static void __rsnd_dvc_initialize_lock(struct rsnd_mod *mod, u32 enable)
+static void rsnd_dvc_halt(struct rsnd_mod *mod)
 {
-       rsnd_mod_write(mod, DVC_DVUIR, enable);
+       rsnd_mod_write(mod, DVC_DVUIR, 1);
+       rsnd_mod_write(mod, DVC_SWRSR, 0);
 }
 
-static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
-                                  struct rsnd_mod *mod)
+#define rsnd_dvc_get_vrpdr(dvc) (dvc->rup.val << 8 | dvc->rdown.val)
+#define rsnd_dvc_get_vrdbr(dvc) (0x3ff - (dvc->volume.val[0] >> 13))
+
+static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
+                                             struct rsnd_mod *mod)
 {
        struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
        u32 val[RSND_DVC_CHANNELS];
-       u32 dvucr = 0;
-       u32 mute = 0;
        int i;
 
-       for (i = 0; i < dvc->mute.cfg.size; i++)
-               mute |= (!!dvc->mute.cfg.val[i]) << i;
+       /* Enable Ramp */
+       if (dvc->ren.val)
+               for (i = 0; i < RSND_DVC_CHANNELS; i++)
+                       val[i] = dvc->volume.cfg.max;
+       else
+               for (i = 0; i < RSND_DVC_CHANNELS; i++)
+                       val[i] = dvc->volume.val[i];
 
-       /* Disable DVC Register access */
-       rsnd_mod_write(mod, DVC_DVUER, 0);
+       /* Enable Digital Volume */
+       rsnd_mod_write(mod, DVC_VOL0R, val[0]);
+       rsnd_mod_write(mod, DVC_VOL1R, val[1]);
+       rsnd_mod_write(mod, DVC_VOL2R, val[2]);
+       rsnd_mod_write(mod, DVC_VOL3R, val[3]);
+       rsnd_mod_write(mod, DVC_VOL4R, val[4]);
+       rsnd_mod_write(mod, DVC_VOL5R, val[5]);
+       rsnd_mod_write(mod, DVC_VOL6R, val[6]);
+       rsnd_mod_write(mod, DVC_VOL7R, val[7]);
+}
+
+static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
+                                struct rsnd_mod *mod)
+{
+       struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+       u32 adinr = 0;
+       u32 dvucr = 0;
+       u32 vrctr = 0;
+       u32 vrpdr = 0;
+       u32 vrdbr = 0;
+
+       adinr = rsnd_get_adinr_bit(mod, io) |
+               rsnd_get_adinr_chan(mod, io);
+
+       /* Enable Digital Volume, Zero Cross Mute Mode */
+       dvucr |= 0x101;
 
        /* Enable Ramp */
        if (dvc->ren.val) {
                dvucr |= 0x10;
 
-               /* Digital Volume Max */
-               for (i = 0; i < RSND_DVC_CHANNELS; i++)
-                       val[i] = dvc->volume.cfg.max;
-
-               rsnd_mod_write(mod, DVC_VRCTR, 0xff);
-               rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 |
-                                              dvc->rdown.val);
                /*
                 * FIXME !!
                 * use scale-downed Digital Volume
                 * as Volume Ramp
                 * 7F FFFF -> 3FF
                 */
-               rsnd_mod_write(mod, DVC_VRDBR,
-                              0x3ff - (dvc->volume.val[0] >> 13));
-
-       } else {
-               for (i = 0; i < RSND_DVC_CHANNELS; i++)
-                       val[i] = dvc->volume.val[i];
+               vrctr = 0xff;
+               vrpdr = rsnd_dvc_get_vrpdr(dvc);
+               vrdbr = rsnd_dvc_get_vrdbr(dvc);
        }
 
-       /* Enable Digital Volume */
-       dvucr |= 0x100;
-       rsnd_mod_write(mod, DVC_VOL0R, val[0]);
-       rsnd_mod_write(mod, DVC_VOL1R, val[1]);
+       /* Initialize operation */
+       rsnd_mod_write(mod, DVC_DVUIR, 1);
+
+       /* General Information */
+       rsnd_mod_write(mod, DVC_ADINR, adinr);
+       rsnd_mod_write(mod, DVC_DVUCR, dvucr);
+
+       /* Volume Ramp Parameter */
+       rsnd_mod_write(mod, DVC_VRCTR, vrctr);
+       rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
+       rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
+
+       /* Digital Volume Function Parameter */
+       rsnd_dvc_volume_parameter(io, mod);
+
+       /* cancel operation */
+       rsnd_mod_write(mod, DVC_DVUIR, 0);
+}
+
+static void rsnd_dvc_volume_update(struct rsnd_dai_stream *io,
+                                  struct rsnd_mod *mod)
+{
+       struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+       u32 zcmcr = 0;
+       u32 vrpdr = 0;
+       u32 vrdbr = 0;
+       int i;
+
+       for (i = 0; i < dvc->mute.cfg.size; i++)
+               zcmcr |= (!!dvc->mute.cfg.val[i]) << i;
 
-       /*  Enable Mute */
-       if (mute) {
-               dvucr |= 0x1;
-               rsnd_mod_write(mod, DVC_ZCMCR, mute);
+       if (dvc->ren.val) {
+               vrpdr = rsnd_dvc_get_vrpdr(dvc);
+               vrdbr = rsnd_dvc_get_vrdbr(dvc);
        }
 
-       rsnd_mod_write(mod, DVC_DVUCR, dvucr);
+       /* Disable DVC Register access */
+       rsnd_mod_write(mod, DVC_DVUER, 0);
+
+       /* Zero Cross Mute Function */
+       rsnd_mod_write(mod, DVC_ZCMCR, zcmcr);
+
+       /* Volume Ramp Function */
+       rsnd_mod_write(mod, DVC_VRPDR, vrpdr);
+       rsnd_mod_write(mod, DVC_VRDBR, vrdbr);
+       /* add DVC_VRWTR here */
+
+       /* Digital Volume Function Parameter */
+       rsnd_dvc_volume_parameter(io, mod);
 
        /* Enable DVC Register access */
        rsnd_mod_write(mod, DVC_DVUER, 1);
 }
 
-static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
-                               struct rsnd_dai_stream *io,
-                               struct rsnd_priv *priv)
+static int rsnd_dvc_probe_(struct rsnd_mod *mod,
+                          struct rsnd_dai_stream *io,
+                          struct rsnd_priv *priv)
+{
+       return rsnd_cmd_attach(io, rsnd_mod_id(mod));
+}
+
+static int rsnd_dvc_remove_(struct rsnd_mod *mod,
+                           struct rsnd_dai_stream *io,
+                           struct rsnd_priv *priv)
 {
        struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
 
@@ -155,19 +218,12 @@ static int rsnd_dvc_init(struct rsnd_mod *mod,
 {
        rsnd_mod_power_on(mod);
 
-       rsnd_dvc_soft_reset(mod);
-
-       rsnd_dvc_initialize_lock(mod);
-
-       rsnd_path_parse(priv, io);
+       rsnd_dvc_activation(mod);
 
-       rsnd_mod_write(mod, DVC_ADINR, rsnd_get_adinr_bit(mod, io));
+       rsnd_dvc_volume_init(io, mod);
 
-       /* ch0/ch1 Volume */
        rsnd_dvc_volume_update(io, mod);
 
-       rsnd_adg_set_cmd_timsel_gen2(mod, io);
-
        return 0;
 }
 
@@ -175,27 +231,9 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
-       rsnd_mod_power_off(mod);
+       rsnd_dvc_halt(mod);
 
-       return 0;
-}
-
-static int rsnd_dvc_start(struct rsnd_mod *mod,
-                         struct rsnd_dai_stream *io,
-                         struct rsnd_priv *priv)
-{
-       rsnd_dvc_initialize_unlock(mod);
-
-       rsnd_mod_write(mod, CMD_CTRL, 0x10);
-
-       return 0;
-}
-
-static int rsnd_dvc_stop(struct rsnd_mod *mod,
-                        struct rsnd_dai_stream *io,
-                        struct rsnd_priv *priv)
-{
-       rsnd_mod_write(mod, CMD_CTRL, 0);
+       rsnd_mod_power_off(mod);
 
        return 0;
 }
@@ -206,6 +244,7 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
 {
        struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
        int is_play = rsnd_io_is_play(io);
+       int slots = rsnd_get_slot(io);
        int ret;
 
        /* Volume */
@@ -213,7 +252,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
                        is_play ?
                        "DVC Out Playback Volume" : "DVC In Capture Volume",
                        rsnd_dvc_volume_update,
-                       &dvc->volume, 0x00800000 - 1);
+                       &dvc->volume, slots,
+                       0x00800000 - 1);
        if (ret < 0)
                return ret;
 
@@ -222,7 +262,8 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
                        is_play ?
                        "DVC Out Mute Switch" : "DVC In Mute Switch",
                        rsnd_dvc_volume_update,
-                       &dvc->mute, 1);
+                       &dvc->mute,  slots,
+                       1);
        if (ret < 0)
                return ret;
 
@@ -269,11 +310,10 @@ static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_dai_stream *io,
 static struct rsnd_mod_ops rsnd_dvc_ops = {
        .name           = DVC_NAME,
        .dma_req        = rsnd_dvc_dma_req,
-       .remove         = rsnd_dvc_remove_gen2,
+       .probe          = rsnd_dvc_probe_,
+       .remove         = rsnd_dvc_remove_,
        .init           = rsnd_dvc_init,
        .quit           = rsnd_dvc_quit,
-       .start          = rsnd_dvc_start,
-       .stop           = rsnd_dvc_stop,
        .pcm_new        = rsnd_dvc_pcm_new,
 };
 
@@ -282,50 +322,13 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
                id = 0;
 
-       return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id);
+       return rsnd_mod_get(rsnd_dvc_get(priv, id));
 }
 
-static void rsnd_of_parse_dvc(struct platform_device *pdev,
-                             const struct rsnd_of_data *of_data,
-                             struct rsnd_priv *priv)
+int rsnd_dvc_probe(struct rsnd_priv *priv)
 {
        struct device_node *node;
-       struct rsnd_dvc_platform_info *dvc_info;
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
-       struct device *dev = &pdev->dev;
-       int nr;
-
-       if (!of_data)
-               return;
-
-       node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
-       if (!node)
-               return;
-
-       nr = of_get_child_count(node);
-       if (!nr)
-               goto rsnd_of_parse_dvc_end;
-
-       dvc_info = devm_kzalloc(dev,
-                               sizeof(struct rsnd_dvc_platform_info) * nr,
-                               GFP_KERNEL);
-       if (!dvc_info) {
-               dev_err(dev, "dvc info allocation error\n");
-               goto rsnd_of_parse_dvc_end;
-       }
-
-       info->dvc_info          = dvc_info;
-       info->dvc_info_nr       = nr;
-
-rsnd_of_parse_dvc_end:
-       of_node_put(node);
-}
-
-int rsnd_dvc_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv)
-{
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+       struct device_node *np;
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_dvc *dvc;
        struct clk *clk;
@@ -336,40 +339,54 @@ int rsnd_dvc_probe(struct platform_device *pdev,
        if (rsnd_is_gen1(priv))
                return 0;
 
-       rsnd_of_parse_dvc(pdev, of_data, priv);
+       node = rsnd_dvc_of_node(priv);
+       if (!node)
+               return 0; /* not used is not error */
 
-       nr = info->dvc_info_nr;
-       if (!nr)
-               return 0;
+       nr = of_get_child_count(node);
+       if (!nr) {
+               ret = -EINVAL;
+               goto rsnd_dvc_probe_done;
+       }
 
        dvc     = devm_kzalloc(dev, sizeof(*dvc) * nr, GFP_KERNEL);
-       if (!dvc)
-               return -ENOMEM;
+       if (!dvc) {
+               ret = -ENOMEM;
+               goto rsnd_dvc_probe_done;
+       }
 
        priv->dvc_nr    = nr;
        priv->dvc       = dvc;
 
-       for_each_rsnd_dvc(dvc, priv, i) {
+       i = 0;
+       ret = 0;
+       for_each_child_of_node(node, np) {
+               dvc = rsnd_dvc_get(priv, i);
+
                snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d",
                         DVC_NAME, i);
 
                clk = devm_clk_get(dev, name);
-               if (IS_ERR(clk))
-                       return PTR_ERR(clk);
-
-               dvc->info = &info->dvc_info[i];
+               if (IS_ERR(clk)) {
+                       ret = PTR_ERR(clk);
+                       goto rsnd_dvc_probe_done;
+               }
 
                ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
                              clk, RSND_MOD_DVC, i);
                if (ret)
-                       return ret;
+                       goto rsnd_dvc_probe_done;
+
+               i++;
        }
 
-       return 0;
+rsnd_dvc_probe_done:
+       of_node_put(node);
+
+       return ret;
 }
 
-void rsnd_dvc_remove(struct platform_device *pdev,
-                    struct rsnd_priv *priv)
+void rsnd_dvc_remove(struct rsnd_priv *priv)
 {
        struct rsnd_dvc *dvc;
        int i;
index edcf4cc..ea24247 100644 (file)
@@ -31,29 +31,33 @@ struct rsnd_gen {
 
        /* RSND_REG_MAX base */
        struct regmap_field *regs[RSND_REG_MAX];
+       const char *reg_name[RSND_REG_MAX];
 };
 
 #define rsnd_priv_to_gen(p)    ((struct rsnd_gen *)(p)->gen)
+#define rsnd_reg_name(gen, id) ((gen)->reg_name[id])
 
 struct rsnd_regmap_field_conf {
        int idx;
        unsigned int reg_offset;
        unsigned int id_offset;
+       const char *reg_name;
 };
 
-#define RSND_REG_SET(id, offset, _id_offset)   \
+#define RSND_REG_SET(id, offset, _id_offset, n)        \
 {                                              \
        .idx = id,                              \
        .reg_offset = offset,                   \
        .id_offset = _id_offset,                \
+       .reg_name = n,                          \
 }
 /* single address mapping */
 #define RSND_GEN_S_REG(id, offset)     \
-       RSND_REG_SET(RSND_REG_##id, offset, 0)
+       RSND_REG_SET(RSND_REG_##id, offset, 0, #id)
 
 /* multi address mapping */
 #define RSND_GEN_M_REG(id, offset, _id_offset) \
-       RSND_REG_SET(RSND_REG_##id, offset, _id_offset)
+       RSND_REG_SET(RSND_REG_##id, offset, _id_offset, #id)
 
 /*
  *             basic function
@@ -83,8 +87,9 @@ u32 rsnd_read(struct rsnd_priv *priv,
 
        regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
 
-       dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
+       dev_dbg(dev, "r %s[%d] - %-18s (%4d) : %08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod),
+               rsnd_reg_name(gen, reg), reg, val);
 
        return val;
 }
@@ -99,10 +104,11 @@ void rsnd_write(struct rsnd_priv *priv,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return;
 
-       dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
-
        regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
+
+       dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod),
+               rsnd_reg_name(gen, reg), reg, data);
 }
 
 void rsnd_force_write(struct rsnd_priv *priv,
@@ -115,10 +121,11 @@ void rsnd_force_write(struct rsnd_priv *priv,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return;
 
-       dev_dbg(dev, "w %s[%d] - %4d : %08x\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
-
        regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data);
+
+       dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod),
+               rsnd_reg_name(gen, reg), reg, data);
 }
 
 void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
@@ -130,11 +137,13 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return;
 
-       dev_dbg(dev, "b %s[%d] - %4d : %08x/%08x\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask);
-
        regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
                                  mask, data);
+
+       dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod),
+               rsnd_reg_name(gen, reg), reg, data, mask);
+
 }
 
 phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id)
@@ -150,7 +159,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
                                 int id_size,
                                 int reg_id,
                                 const char *name,
-                                struct rsnd_regmap_field_conf *conf,
+                                const struct rsnd_regmap_field_conf *conf,
                                 int conf_size)
 {
        struct platform_device *pdev = rsnd_priv_to_pdev(priv);
@@ -203,6 +212,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
 
                /* RSND_REG_MAX base */
                gen->regs[conf[i].idx] = regs;
+               gen->reg_name[conf[i].idx] = conf[i].reg_name;
        }
 
        return 0;
@@ -211,25 +221,31 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
 /*
  *             Gen2
  */
-static int rsnd_gen2_probe(struct platform_device *pdev,
-                          struct rsnd_priv *priv)
+static int rsnd_gen2_probe(struct rsnd_priv *priv)
 {
-       struct rsnd_regmap_field_conf conf_ssiu[] = {
+       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),
                RSND_GEN_M_REG(SSI_BUSIF_DALIGN,0x8,    0x80),
+               RSND_GEN_M_REG(SSI_MODE,        0xc,    0x80),
                RSND_GEN_M_REG(SSI_CTRL,        0x10,   0x80),
                RSND_GEN_M_REG(SSI_INT_ENABLE,  0x18,   0x80),
        };
-       struct rsnd_regmap_field_conf conf_scu[] = {
-               RSND_GEN_M_REG(SRC_BUSIF_MODE,  0x0,    0x20),
+
+       const static struct rsnd_regmap_field_conf conf_scu[] = {
+               RSND_GEN_M_REG(SRC_I_BUSIF_MODE,0x0,    0x20),
+               RSND_GEN_M_REG(SRC_O_BUSIF_MODE,0x4,    0x20),
                RSND_GEN_M_REG(SRC_BUSIF_DALIGN,0x8,    0x20),
                RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc,    0x20),
                RSND_GEN_M_REG(SRC_CTRL,        0x10,   0x20),
                RSND_GEN_M_REG(SRC_INT_ENABLE0, 0x18,   0x20),
+               RSND_GEN_M_REG(CMD_BUSIF_DALIGN,0x188,  0x20),
                RSND_GEN_M_REG(CMD_ROUTE_SLCT,  0x18c,  0x20),
                RSND_GEN_M_REG(CMD_CTRL,        0x190,  0x20),
                RSND_GEN_S_REG(SCU_SYS_STATUS0, 0x1c8),
@@ -266,9 +282,15 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
                RSND_GEN_M_REG(DVC_VRDBR,       0xe20,  0x100),
                RSND_GEN_M_REG(DVC_VOL0R,       0xe28,  0x100),
                RSND_GEN_M_REG(DVC_VOL1R,       0xe2c,  0x100),
+               RSND_GEN_M_REG(DVC_VOL2R,       0xe30,  0x100),
+               RSND_GEN_M_REG(DVC_VOL3R,       0xe34,  0x100),
+               RSND_GEN_M_REG(DVC_VOL4R,       0xe38,  0x100),
+               RSND_GEN_M_REG(DVC_VOL5R,       0xe3c,  0x100),
+               RSND_GEN_M_REG(DVC_VOL6R,       0xe40,  0x100),
+               RSND_GEN_M_REG(DVC_VOL7R,       0xe44,  0x100),
                RSND_GEN_M_REG(DVC_DVUER,       0xe48,  0x100),
        };
-       struct rsnd_regmap_field_conf conf_adg[] = {
+       const static struct rsnd_regmap_field_conf conf_adg[] = {
                RSND_GEN_S_REG(BRRA,            0x00),
                RSND_GEN_S_REG(BRRB,            0x04),
                RSND_GEN_S_REG(SSICKR,          0x08),
@@ -288,7 +310,7 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
                RSND_GEN_S_REG(SRCOUT_TIMSEL4,  0x58),
                RSND_GEN_S_REG(CMDOUT_TIMSEL,   0x5c),
        };
-       struct rsnd_regmap_field_conf conf_ssi[] = {
+       const static struct rsnd_regmap_field_conf conf_ssi[] = {
                RSND_GEN_M_REG(SSICR,           0x00,   0x40),
                RSND_GEN_M_REG(SSISR,           0x04,   0x40),
                RSND_GEN_M_REG(SSITDR,          0x08,   0x40),
@@ -317,65 +339,30 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
  *             Gen1
  */
 
-static int rsnd_gen1_probe(struct platform_device *pdev,
-                          struct rsnd_priv *priv)
+static int rsnd_gen1_probe(struct rsnd_priv *priv)
 {
-       struct rsnd_regmap_field_conf conf_sru[] = {
-               RSND_GEN_S_REG(SRC_ROUTE_SEL,   0x00),
-               RSND_GEN_S_REG(SRC_TMG_SEL0,    0x08),
-               RSND_GEN_S_REG(SRC_TMG_SEL1,    0x0c),
-               RSND_GEN_S_REG(SRC_TMG_SEL2,    0x10),
-               RSND_GEN_S_REG(SRC_ROUTE_CTRL,  0xc0),
-               RSND_GEN_S_REG(SSI_MODE0,       0xD0),
-               RSND_GEN_S_REG(SSI_MODE1,       0xD4),
-               RSND_GEN_M_REG(SRC_BUSIF_MODE,  0x20,   0x4),
-               RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0x50,   0x8),
-               RSND_GEN_M_REG(SRC_SWRSR,       0x200,  0x40),
-               RSND_GEN_M_REG(SRC_SRCIR,       0x204,  0x40),
-               RSND_GEN_M_REG(SRC_ADINR,       0x214,  0x40),
-               RSND_GEN_M_REG(SRC_IFSCR,       0x21c,  0x40),
-               RSND_GEN_M_REG(SRC_IFSVR,       0x220,  0x40),
-               RSND_GEN_M_REG(SRC_SRCCR,       0x224,  0x40),
-               RSND_GEN_M_REG(SRC_MNFSR,       0x228,  0x40),
-               /*
-                * ADD US
-                *
-                * SRC_STATUS
-                * SRC_INT_EN
-                * SCU_SYS_STATUS0
-                * SCU_SYS_STATUS1
-                * SCU_SYS_INT_EN0
-                * SCU_SYS_INT_EN1
-                */
-       };
-       struct rsnd_regmap_field_conf conf_adg[] = {
+       const static struct rsnd_regmap_field_conf conf_adg[] = {
                RSND_GEN_S_REG(BRRA,            0x00),
                RSND_GEN_S_REG(BRRB,            0x04),
                RSND_GEN_S_REG(SSICKR,          0x08),
                RSND_GEN_S_REG(AUDIO_CLK_SEL0,  0x0c),
                RSND_GEN_S_REG(AUDIO_CLK_SEL1,  0x10),
-               RSND_GEN_S_REG(AUDIO_CLK_SEL3,  0x18),
-               RSND_GEN_S_REG(AUDIO_CLK_SEL4,  0x1c),
-               RSND_GEN_S_REG(AUDIO_CLK_SEL5,  0x20),
        };
-       struct rsnd_regmap_field_conf conf_ssi[] = {
+       const static struct rsnd_regmap_field_conf conf_ssi[] = {
                RSND_GEN_M_REG(SSICR,           0x00,   0x40),
                RSND_GEN_M_REG(SSISR,           0x04,   0x40),
                RSND_GEN_M_REG(SSITDR,          0x08,   0x40),
                RSND_GEN_M_REG(SSIRDR,          0x0c,   0x40),
                RSND_GEN_M_REG(SSIWSR,          0x20,   0x40),
        };
-       int ret_sru;
        int ret_adg;
        int ret_ssi;
 
-       ret_sru  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, "sru", conf_sru);
        ret_adg  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg);
        ret_ssi  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi);
-       if (ret_sru  < 0 ||
-           ret_adg  < 0 ||
+       if (ret_adg  < 0 ||
            ret_ssi  < 0)
-               return ret_sru | ret_adg | ret_ssi;
+               return ret_adg | ret_ssi;
 
        return 0;
 }
@@ -383,28 +370,12 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
 /*
  *             Gen
  */
-static void rsnd_of_parse_gen(struct platform_device *pdev,
-                             const struct rsnd_of_data *of_data,
-                             struct rsnd_priv *priv)
-{
-       struct rcar_snd_info *info = priv->info;
-
-       if (!of_data)
-               return;
-
-       info->flags = of_data->flags;
-}
-
-int rsnd_gen_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv)
+int rsnd_gen_probe(struct rsnd_priv *priv)
 {
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_gen *gen;
        int ret;
 
-       rsnd_of_parse_gen(pdev, of_data, priv);
-
        gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
        if (!gen) {
                dev_err(dev, "GEN allocate failed\n");
@@ -415,9 +386,9 @@ int rsnd_gen_probe(struct platform_device *pdev,
 
        ret = -ENODEV;
        if (rsnd_is_gen1(priv))
-               ret = rsnd_gen1_probe(pdev, priv);
+               ret = rsnd_gen1_probe(priv);
        else if (rsnd_is_gen2(priv))
-               ret = rsnd_gen2_probe(pdev, priv);
+               ret = rsnd_gen2_probe(priv);
 
        if (ret < 0)
                dev_err(dev, "unknown generation R-Car sound device\n");
index 953dd0b..65542b6 100644 (file)
 #define MIX_NAME "mix"
 
 struct rsnd_mix {
-       struct rsnd_mix_platform_info *info; /* rcar_snd.h */
        struct rsnd_mod mod;
 };
 
+#define rsnd_mix_get(priv, id) ((struct rsnd_mix *)(priv->mix) + id)
 #define rsnd_mix_nr(priv) ((priv)->mix_nr)
 #define for_each_rsnd_mix(pos, priv, i)                                        \
        for ((i) = 0;                                                   \
@@ -24,58 +24,77 @@ struct rsnd_mix {
                     ((pos) = (struct rsnd_mix *)(priv)->mix + i);      \
             i++)
 
-
-static void rsnd_mix_soft_reset(struct rsnd_mod *mod)
+static void rsnd_mix_activation(struct rsnd_mod *mod)
 {
        rsnd_mod_write(mod, MIX_SWRSR, 0);
        rsnd_mod_write(mod, MIX_SWRSR, 1);
 }
 
-#define rsnd_mix_initialize_lock(mod)  __rsnd_mix_initialize_lock(mod, 1)
-#define rsnd_mix_initialize_unlock(mod)        __rsnd_mix_initialize_lock(mod, 0)
-static void __rsnd_mix_initialize_lock(struct rsnd_mod *mod, u32 enable)
+static void rsnd_mix_halt(struct rsnd_mod *mod)
+{
+       rsnd_mod_write(mod, MIX_MIXIR, 1);
+       rsnd_mod_write(mod, MIX_SWRSR, 0);
+}
+
+static void rsnd_mix_volume_parameter(struct rsnd_dai_stream *io,
+                                     struct rsnd_mod *mod)
 {
-       rsnd_mod_write(mod, MIX_MIXIR, enable);
+       rsnd_mod_write(mod, MIX_MDBAR, 0);
+       rsnd_mod_write(mod, MIX_MDBBR, 0);
+       rsnd_mod_write(mod, MIX_MDBCR, 0);
+       rsnd_mod_write(mod, MIX_MDBDR, 0);
+}
+
+static void rsnd_mix_volume_init(struct rsnd_dai_stream *io,
+                                struct rsnd_mod *mod)
+{
+       rsnd_mod_write(mod, MIX_MIXIR, 1);
+
+       /* General Information */
+       rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
+
+       /* volume step */
+       rsnd_mod_write(mod, MIX_MIXMR, 0);
+       rsnd_mod_write(mod, MIX_MVPDR, 0);
+
+       /* common volume parameter */
+       rsnd_mix_volume_parameter(io, mod);
+
+       rsnd_mod_write(mod, MIX_MIXIR, 0);
 }
 
 static void rsnd_mix_volume_update(struct rsnd_dai_stream *io,
                                  struct rsnd_mod *mod)
 {
-
        /* Disable MIX dB setting */
        rsnd_mod_write(mod, MIX_MDBER, 0);
 
-       rsnd_mod_write(mod, MIX_MDBAR, 0);
-       rsnd_mod_write(mod, MIX_MDBBR, 0);
-       rsnd_mod_write(mod, MIX_MDBCR, 0);
-       rsnd_mod_write(mod, MIX_MDBDR, 0);
+       /* common volume parameter */
+       rsnd_mix_volume_parameter(io, mod);
 
        /* Enable MIX dB setting */
        rsnd_mod_write(mod, MIX_MDBER, 1);
 }
 
+static int rsnd_mix_probe_(struct rsnd_mod *mod,
+                          struct rsnd_dai_stream *io,
+                          struct rsnd_priv *priv)
+{
+       return rsnd_cmd_attach(io, rsnd_mod_id(mod));
+}
+
 static int rsnd_mix_init(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
        rsnd_mod_power_on(mod);
 
-       rsnd_mix_soft_reset(mod);
-
-       rsnd_mix_initialize_lock(mod);
-
-       rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
-
-       rsnd_path_parse(priv, io);
+       rsnd_mix_activation(mod);
 
-       /* volume step */
-       rsnd_mod_write(mod, MIX_MIXMR, 0);
-       rsnd_mod_write(mod, MIX_MVPDR, 0);
+       rsnd_mix_volume_init(io, mod);
 
        rsnd_mix_volume_update(io, mod);
 
-       rsnd_mix_initialize_unlock(mod);
-
        return 0;
 }
 
@@ -83,6 +102,8 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
+       rsnd_mix_halt(mod);
+
        rsnd_mod_power_off(mod);
 
        return 0;
@@ -90,6 +111,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
 
 static struct rsnd_mod_ops rsnd_mix_ops = {
        .name           = MIX_NAME,
+       .probe          = rsnd_mix_probe_,
        .init           = rsnd_mix_init,
        .quit           = rsnd_mix_quit,
 };
@@ -99,51 +121,13 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
                id = 0;
 
-       return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id);
+       return rsnd_mod_get(rsnd_mix_get(priv, id));
 }
 
-static void rsnd_of_parse_mix(struct platform_device *pdev,
-                             const struct rsnd_of_data *of_data,
-                             struct rsnd_priv *priv)
+int rsnd_mix_probe(struct rsnd_priv *priv)
 {
        struct device_node *node;
-       struct rsnd_mix_platform_info *mix_info;
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
-       struct device *dev = &pdev->dev;
-       int nr;
-
-       if (!of_data)
-               return;
-
-       node = of_get_child_by_name(dev->of_node, "rcar_sound,mix");
-       if (!node)
-               return;
-
-       nr = of_get_child_count(node);
-       if (!nr)
-               goto rsnd_of_parse_mix_end;
-
-       mix_info = devm_kzalloc(dev,
-                               sizeof(struct rsnd_mix_platform_info) * nr,
-                               GFP_KERNEL);
-       if (!mix_info) {
-               dev_err(dev, "mix info allocation error\n");
-               goto rsnd_of_parse_mix_end;
-       }
-
-       info->mix_info          = mix_info;
-       info->mix_info_nr       = nr;
-
-rsnd_of_parse_mix_end:
-       of_node_put(node);
-
-}
-
-int rsnd_mix_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv)
-{
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+       struct device_node *np;
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_mix *mix;
        struct clk *clk;
@@ -154,40 +138,54 @@ int rsnd_mix_probe(struct platform_device *pdev,
        if (rsnd_is_gen1(priv))
                return 0;
 
-       rsnd_of_parse_mix(pdev, of_data, priv);
+       node = rsnd_mix_of_node(priv);
+       if (!node)
+               return 0; /* not used is not error */
 
-       nr = info->mix_info_nr;
-       if (!nr)
-               return 0;
+       nr = of_get_child_count(node);
+       if (!nr) {
+               ret = -EINVAL;
+               goto rsnd_mix_probe_done;
+       }
 
        mix     = devm_kzalloc(dev, sizeof(*mix) * nr, GFP_KERNEL);
-       if (!mix)
-               return -ENOMEM;
+       if (!mix) {
+               ret = -ENOMEM;
+               goto rsnd_mix_probe_done;
+       }
 
        priv->mix_nr    = nr;
        priv->mix       = mix;
 
-       for_each_rsnd_mix(mix, priv, i) {
+       i = 0;
+       ret = 0;
+       for_each_child_of_node(node, np) {
+               mix = rsnd_mix_get(priv, i);
+
                snprintf(name, MIX_NAME_SIZE, "%s.%d",
                         MIX_NAME, i);
 
                clk = devm_clk_get(dev, name);
-               if (IS_ERR(clk))
-                       return PTR_ERR(clk);
-
-               mix->info = &info->mix_info[i];
+               if (IS_ERR(clk)) {
+                       ret = PTR_ERR(clk);
+                       goto rsnd_mix_probe_done;
+               }
 
                ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
                                    clk, RSND_MOD_MIX, i);
                if (ret)
-                       return ret;
+                       goto rsnd_mix_probe_done;
+
+               i++;
        }
 
-       return 0;
+rsnd_mix_probe_done:
+       of_node_put(node);
+
+       return ret;
 }
 
-void rsnd_mix_remove(struct platform_device *pdev,
-                    struct rsnd_priv *priv)
+void rsnd_mix_remove(struct rsnd_priv *priv)
 {
        struct rsnd_mix *mix;
        int i;
diff --git a/sound/soc/sh/rcar/rcar_snd.h b/sound/soc/sh/rcar/rcar_snd.h
deleted file mode 100644 (file)
index d8e33d3..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Renesas R-Car SRU/SCU/SSIU/SSI support
- *
- * Copyright (C) 2013 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef RCAR_SND_H
-#define RCAR_SND_H
-
-
-#define RSND_GEN1_SRU  0
-#define RSND_GEN1_ADG  1
-#define RSND_GEN1_SSI  2
-
-#define RSND_GEN2_SCU  0
-#define RSND_GEN2_ADG  1
-#define RSND_GEN2_SSIU 2
-#define RSND_GEN2_SSI  3
-
-#define RSND_BASE_MAX  4
-
-/*
- * flags
- *
- * 0xAB000000
- *
- * A : clock sharing settings
- * B : SSI direction
- */
-#define RSND_SSI_CLK_PIN_SHARE         (1 << 31)
-#define RSND_SSI_NO_BUSIF              (1 << 30) /* SSI+DMA without BUSIF */
-
-#define RSND_SSI(_dma_id, _irq, _flags)                \
-{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
-#define RSND_SSI_UNUSED \
-{ .dma_id = -1, .irq = -1, .flags = 0 }
-
-struct rsnd_ssi_platform_info {
-       int dma_id;
-       int irq;
-       u32 flags;
-};
-
-#define RSND_SRC(rate, _dma_id)                                                \
-{ .convert_rate = rate, .dma_id = _dma_id, }
-#define RSND_SRC_UNUSED                                \
-{ .convert_rate = 0, .dma_id = -1, }
-
-struct rsnd_src_platform_info {
-       u32 convert_rate; /* sampling rate convert */
-       int dma_id; /* for Gen2 SCU */
-       int irq;
-};
-
-/*
- * flags
- */
-struct rsnd_ctu_platform_info {
-       u32 flags;
-};
-
-struct rsnd_mix_platform_info {
-       u32 flags;
-};
-
-struct rsnd_dvc_platform_info {
-       u32 flags;
-};
-
-struct rsnd_dai_path_info {
-       struct rsnd_ssi_platform_info *ssi;
-       struct rsnd_src_platform_info *src;
-       struct rsnd_ctu_platform_info *ctu;
-       struct rsnd_mix_platform_info *mix;
-       struct rsnd_dvc_platform_info *dvc;
-};
-
-struct rsnd_dai_platform_info {
-       struct rsnd_dai_path_info playback;
-       struct rsnd_dai_path_info capture;
-};
-
-/*
- * flags
- *
- * 0x0000000A
- *
- * A : generation
- */
-#define RSND_GEN_MASK  (0xF << 0)
-#define RSND_GEN1      (1 << 0) /* fixme */
-#define RSND_GEN2      (2 << 0) /* fixme */
-
-struct rcar_snd_info {
-       u32 flags;
-       struct rsnd_ssi_platform_info *ssi_info;
-       int ssi_info_nr;
-       struct rsnd_src_platform_info *src_info;
-       int src_info_nr;
-       struct rsnd_ctu_platform_info *ctu_info;
-       int ctu_info_nr;
-       struct rsnd_mix_platform_info *mix_info;
-       int mix_info_nr;
-       struct rsnd_dvc_platform_info *dvc_info;
-       int dvc_info_nr;
-       struct rsnd_dai_platform_info *dai_info;
-       int dai_info_nr;
-       int (*start)(int id);
-       int (*stop)(int id);
-};
-
-#endif
index 0853298..317dd79 100644 (file)
 #include <sound/soc.h>
 #include <sound/pcm_params.h>
 
-#include "rcar_snd.h"
+#define RSND_GEN1_SRU  0
+#define RSND_GEN1_ADG  1
+#define RSND_GEN1_SSI  2
+
+#define RSND_GEN2_SCU  0
+#define RSND_GEN2_ADG  1
+#define RSND_GEN2_SSIU 2
+#define RSND_GEN2_SSI  3
+
+#define RSND_BASE_MAX  4
 
 /*
  *     pseudo register
  * see gen1/gen2 for detail
  */
 enum rsnd_reg {
-       /* SRU/SCU/SSIU */
+       /* SCU (SRC/SSIU/MIX/CTU/DVC) */
+       RSND_REG_SSI_MODE,              /* Gen2 only */
        RSND_REG_SSI_MODE0,
        RSND_REG_SSI_MODE1,
-       RSND_REG_SRC_BUSIF_MODE,
+       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 */
+       RSND_REG_SSI_BUSIF_DALIGN,      /* Gen2 only */
+       RSND_REG_SSI_INT_ENABLE,        /* Gen2 only */
+       RSND_REG_SRC_I_BUSIF_MODE,
+       RSND_REG_SRC_O_BUSIF_MODE,
        RSND_REG_SRC_ROUTE_MODE0,
        RSND_REG_SRC_SWRSR,
        RSND_REG_SRC_SRCIR,
@@ -45,9 +63,29 @@ enum rsnd_reg {
        RSND_REG_SRC_IFSCR,
        RSND_REG_SRC_IFSVR,
        RSND_REG_SRC_SRCCR,
+       RSND_REG_SRC_CTRL,              /* Gen2 only */
+       RSND_REG_SRC_BSDSR,             /* Gen2 only */
+       RSND_REG_SRC_BSISR,             /* Gen2 only */
+       RSND_REG_SRC_INT_ENABLE0,       /* Gen2 only */
+       RSND_REG_SRC_BUSIF_DALIGN,      /* Gen2 only */
+       RSND_REG_SRCIN_TIMSEL0,         /* Gen2 only */
+       RSND_REG_SRCIN_TIMSEL1,         /* Gen2 only */
+       RSND_REG_SRCIN_TIMSEL2,         /* Gen2 only */
+       RSND_REG_SRCIN_TIMSEL3,         /* Gen2 only */
+       RSND_REG_SRCIN_TIMSEL4,         /* Gen2 only */
+       RSND_REG_SRCOUT_TIMSEL0,        /* Gen2 only */
+       RSND_REG_SRCOUT_TIMSEL1,        /* Gen2 only */
+       RSND_REG_SRCOUT_TIMSEL2,        /* Gen2 only */
+       RSND_REG_SRCOUT_TIMSEL3,        /* Gen2 only */
+       RSND_REG_SRCOUT_TIMSEL4,        /* Gen2 only */
        RSND_REG_SCU_SYS_STATUS0,
+       RSND_REG_SCU_SYS_STATUS1,       /* Gen2 only */
        RSND_REG_SCU_SYS_INT_EN0,
+       RSND_REG_SCU_SYS_INT_EN1,       /* Gen2 only */
+       RSND_REG_CMD_CTRL,              /* Gen2 only */
+       RSND_REG_CMD_BUSIF_DALIGN,      /* Gen2 only */
        RSND_REG_CMD_ROUTE_SLCT,
+       RSND_REG_CMDOUT_TIMSEL,         /* Gen2 only */
        RSND_REG_CTU_CTUIR,
        RSND_REG_CTU_ADINR,
        RSND_REG_MIX_SWRSR,
@@ -67,14 +105,25 @@ enum rsnd_reg {
        RSND_REG_DVC_ZCMCR,
        RSND_REG_DVC_VOL0R,
        RSND_REG_DVC_VOL1R,
+       RSND_REG_DVC_VOL2R,
+       RSND_REG_DVC_VOL3R,
+       RSND_REG_DVC_VOL4R,
+       RSND_REG_DVC_VOL5R,
+       RSND_REG_DVC_VOL6R,
+       RSND_REG_DVC_VOL7R,
        RSND_REG_DVC_DVUER,
+       RSND_REG_DVC_VRCTR,             /* Gen2 only */
+       RSND_REG_DVC_VRPDR,             /* Gen2 only */
+       RSND_REG_DVC_VRDBR,             /* Gen2 only */
 
        /* ADG */
        RSND_REG_BRRA,
        RSND_REG_BRRB,
        RSND_REG_SSICKR,
+       RSND_REG_DIV_EN,                /* Gen2 only */
        RSND_REG_AUDIO_CLK_SEL0,
        RSND_REG_AUDIO_CLK_SEL1,
+       RSND_REG_AUDIO_CLK_SEL2,        /* Gen2 only */
 
        /* SSI */
        RSND_REG_SSICR,
@@ -83,83 +132,9 @@ enum rsnd_reg {
        RSND_REG_SSIRDR,
        RSND_REG_SSIWSR,
 
-       /* SHARE see below */
-       RSND_REG_SHARE01,
-       RSND_REG_SHARE02,
-       RSND_REG_SHARE03,
-       RSND_REG_SHARE04,
-       RSND_REG_SHARE05,
-       RSND_REG_SHARE06,
-       RSND_REG_SHARE07,
-       RSND_REG_SHARE08,
-       RSND_REG_SHARE09,
-       RSND_REG_SHARE10,
-       RSND_REG_SHARE11,
-       RSND_REG_SHARE12,
-       RSND_REG_SHARE13,
-       RSND_REG_SHARE14,
-       RSND_REG_SHARE15,
-       RSND_REG_SHARE16,
-       RSND_REG_SHARE17,
-       RSND_REG_SHARE18,
-       RSND_REG_SHARE19,
-       RSND_REG_SHARE20,
-       RSND_REG_SHARE21,
-       RSND_REG_SHARE22,
-       RSND_REG_SHARE23,
-       RSND_REG_SHARE24,
-       RSND_REG_SHARE25,
-       RSND_REG_SHARE26,
-       RSND_REG_SHARE27,
-       RSND_REG_SHARE28,
-       RSND_REG_SHARE29,
-
        RSND_REG_MAX,
 };
 
-/* Gen1 only */
-#define RSND_REG_SRC_ROUTE_SEL         RSND_REG_SHARE01
-#define RSND_REG_SRC_TMG_SEL0          RSND_REG_SHARE02
-#define RSND_REG_SRC_TMG_SEL1          RSND_REG_SHARE03
-#define RSND_REG_SRC_TMG_SEL2          RSND_REG_SHARE04
-#define RSND_REG_SRC_ROUTE_CTRL                RSND_REG_SHARE05
-#define RSND_REG_SRC_MNFSR             RSND_REG_SHARE06
-#define RSND_REG_AUDIO_CLK_SEL3                RSND_REG_SHARE07
-#define RSND_REG_AUDIO_CLK_SEL4                RSND_REG_SHARE08
-#define RSND_REG_AUDIO_CLK_SEL5                RSND_REG_SHARE09
-
-/* Gen2 only */
-#define RSND_REG_SRC_CTRL              RSND_REG_SHARE01
-#define RSND_REG_SSI_CTRL              RSND_REG_SHARE02
-#define RSND_REG_SSI_BUSIF_MODE                RSND_REG_SHARE03
-#define RSND_REG_SSI_BUSIF_ADINR       RSND_REG_SHARE04
-#define RSND_REG_SSI_INT_ENABLE                RSND_REG_SHARE05
-#define RSND_REG_SRC_BSDSR             RSND_REG_SHARE06
-#define RSND_REG_SRC_BSISR             RSND_REG_SHARE07
-#define RSND_REG_DIV_EN                        RSND_REG_SHARE08
-#define RSND_REG_SRCIN_TIMSEL0         RSND_REG_SHARE09
-#define RSND_REG_SRCIN_TIMSEL1         RSND_REG_SHARE10
-#define RSND_REG_SRCIN_TIMSEL2         RSND_REG_SHARE11
-#define RSND_REG_SRCIN_TIMSEL3         RSND_REG_SHARE12
-#define RSND_REG_SRCIN_TIMSEL4         RSND_REG_SHARE13
-#define RSND_REG_SRCOUT_TIMSEL0                RSND_REG_SHARE14
-#define RSND_REG_SRCOUT_TIMSEL1                RSND_REG_SHARE15
-#define RSND_REG_SRCOUT_TIMSEL2                RSND_REG_SHARE16
-#define RSND_REG_SRCOUT_TIMSEL3                RSND_REG_SHARE17
-#define RSND_REG_SRCOUT_TIMSEL4                RSND_REG_SHARE18
-#define RSND_REG_AUDIO_CLK_SEL2                RSND_REG_SHARE19
-#define RSND_REG_CMD_CTRL              RSND_REG_SHARE20
-#define RSND_REG_CMDOUT_TIMSEL         RSND_REG_SHARE21
-#define RSND_REG_SSI_BUSIF_DALIGN      RSND_REG_SHARE22
-#define RSND_REG_DVC_VRCTR             RSND_REG_SHARE23
-#define RSND_REG_DVC_VRPDR             RSND_REG_SHARE24
-#define RSND_REG_DVC_VRDBR             RSND_REG_SHARE25
-#define RSND_REG_SCU_SYS_STATUS1       RSND_REG_SHARE26
-#define RSND_REG_SCU_SYS_INT_EN1       RSND_REG_SHARE27
-#define RSND_REG_SRC_INT_ENABLE0       RSND_REG_SHARE28
-#define RSND_REG_SRC_BUSIF_DALIGN      RSND_REG_SHARE29
-
-struct rsnd_of_data;
 struct rsnd_priv;
 struct rsnd_mod;
 struct rsnd_dai;
@@ -187,43 +162,13 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
 u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
 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);
-void rsnd_path_parse(struct rsnd_priv *priv,
-                    struct rsnd_dai_stream *io);
 
 /*
  *     R-Car DMA
  */
-struct rsnd_dma;
-
-struct rsnd_dmaen {
-       struct dma_chan         *chan;
-};
-
-struct rsnd_dmapp {
-       int                     dmapp_id;
-       u32                     chcr;
-};
-
-struct rsnd_dma {
-       struct rsnd_dma_ops     *ops;
-       dma_addr_t              src_addr;
-       dma_addr_t              dst_addr;
-       union {
-               struct rsnd_dmaen en;
-               struct rsnd_dmapp pp;
-       } dma;
-};
-#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
-#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
-#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
-
-void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
-void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
-int rsnd_dma_init(struct rsnd_dai_stream *io, struct rsnd_dma *dma, int id);
-void rsnd_dma_quit(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
-int rsnd_dma_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
+                              struct rsnd_mod *mod, int id);
+int rsnd_dma_probe(struct rsnd_priv *priv);
 struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
                                          struct rsnd_mod *mod, char *name);
 
@@ -231,11 +176,19 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
  *     R-Car sound mod
  */
 enum rsnd_mod_type {
-       RSND_MOD_DVC = 0,
+       RSND_MOD_AUDMAPP,
+       RSND_MOD_AUDMA,
+       RSND_MOD_DVC,
        RSND_MOD_MIX,
        RSND_MOD_CTU,
+       RSND_MOD_CMD,
        RSND_MOD_SRC,
+       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,
 };
 
@@ -278,10 +231,8 @@ struct rsnd_mod {
        int id;
        enum rsnd_mod_type type;
        struct rsnd_mod_ops *ops;
-       struct rsnd_dma dma;
        struct rsnd_priv *priv;
        struct clk *clk;
-       u32 status;
 };
 /*
  * status
@@ -328,7 +279,6 @@ struct rsnd_mod {
 #define __rsnd_mod_call_hw_params      0
 
 #define rsnd_mod_to_priv(mod) ((mod)->priv)
-#define rsnd_mod_to_dma(mod) (&(mod)->dma)
 #define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1)
 #define rsnd_mod_power_on(mod) clk_enable((mod)->clk)
 #define rsnd_mod_power_off(mod)        clk_disable((mod)->clk)
@@ -347,6 +297,17 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
 void rsnd_mod_interrupt(struct rsnd_mod *mod,
                        void (*callback)(struct rsnd_mod *mod,
                                         struct rsnd_dai_stream *io));
+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);
+
+void rsnd_set_slot(struct rsnd_dai *rdai,
+                  int slots, int slots_total);
+int rsnd_get_slot(struct rsnd_dai_stream *io);
+int rsnd_get_slot_width(struct rsnd_dai_stream *io);
+int rsnd_get_slot_num(struct rsnd_dai_stream *io);
 
 /*
  *     R-Car sound DAI
@@ -358,6 +319,7 @@ struct rsnd_dai_stream {
        struct rsnd_mod *mod[RSND_MOD_MAX];
        struct rsnd_dai_path_info *info; /* rcar_snd.h */
        struct rsnd_dai *rdai;
+       u32 mod_status[RSND_MOD_MAX];
        int byte_pos;
        int period_pos;
        int byte_per_period;
@@ -365,10 +327,12 @@ 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)
 #define rsnd_io_to_mod_dvc(io) rsnd_io_to_mod((io), RSND_MOD_DVC)
+#define rsnd_io_to_mod_cmd(io) rsnd_io_to_mod((io), RSND_MOD_CMD)
 #define rsnd_io_to_rdai(io)    ((io)->rdai)
 #define rsnd_io_to_priv(io)    (rsnd_rdai_to_priv(rsnd_io_to_rdai(io)))
 #define rsnd_io_is_play(io)    (&rsnd_io_to_rdai(io)->playback == io)
@@ -382,6 +346,9 @@ struct rsnd_dai {
        struct rsnd_dai_stream capture;
        struct rsnd_priv *priv;
 
+       int slots;
+       int slots_num;
+
        unsigned int clk_master:1;
        unsigned int bit_clk_inv:1;
        unsigned int frm_clk_inv:1;
@@ -403,33 +370,28 @@ struct rsnd_dai *rsnd_rdai_get(struct rsnd_priv *priv, int id);
 bool rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
 void rsnd_dai_period_elapsed(struct rsnd_dai_stream *io);
 int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
+int rsnd_dai_connect(struct rsnd_mod *mod,
+                    struct rsnd_dai_stream *io,
+                    enum rsnd_mod_type type);
+#define rsnd_dai_of_node(priv)                                         \
+       of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dai")
 
 /*
  *     R-Car Gen1/Gen2
  */
-int rsnd_gen_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv);
+int rsnd_gen_probe(struct rsnd_priv *priv);
 void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
                               struct rsnd_mod *mod,
                               enum rsnd_reg reg);
 phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id);
 
-#define rsnd_is_gen1(s)                (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1)
-#define rsnd_is_gen2(s)                (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)
-
 /*
  *     R-Car ADG
  */
 int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
 int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
-int rsnd_adg_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv);
-int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
-                                 struct rsnd_mod *mod,
-                                 unsigned int src_rate,
-                                 unsigned int dst_rate);
+int rsnd_adg_probe(struct rsnd_priv *priv);
+void rsnd_adg_remove(struct rsnd_priv *priv);
 int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
                                  struct rsnd_dai_stream *io,
                                  unsigned int src_rate,
@@ -442,15 +404,14 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
 /*
  *     R-Car sound priv
  */
-struct rsnd_of_data {
-       u32 flags;
-};
-
 struct rsnd_priv {
 
        struct platform_device *pdev;
-       struct rcar_snd_info *info;
        spinlock_t lock;
+       unsigned long flags;
+#define RSND_GEN_MASK  (0xF << 0)
+#define RSND_GEN1      (1 << 0)
+#define RSND_GEN2      (2 << 0)
 
        /*
         * below value will be filled on rsnd_gen_probe()
@@ -473,6 +434,12 @@ struct rsnd_priv {
        void *ssi;
        int ssi_nr;
 
+       /*
+        * below value will be filled on rsnd_ssiu_probe()
+        */
+       void *ssiu;
+       int ssiu_nr;
+
        /*
         * below value will be filled on rsnd_src_probe()
         */
@@ -497,6 +464,12 @@ struct rsnd_priv {
        void *dvc;
        int dvc_nr;
 
+       /*
+        * below value will be filled on rsnd_cmd_probe()
+        */
+       void *cmd;
+       int cmd_nr;
+
        /*
         * below value will be filled on rsnd_dai_probe()
         */
@@ -507,7 +480,9 @@ struct rsnd_priv {
 
 #define rsnd_priv_to_pdev(priv)        ((priv)->pdev)
 #define rsnd_priv_to_dev(priv) (&(rsnd_priv_to_pdev(priv)->dev))
-#define rsnd_priv_to_info(priv)        ((priv)->info)
+
+#define rsnd_is_gen1(priv)     (((priv)->flags & RSND_GEN_MASK) == RSND_GEN1)
+#define rsnd_is_gen2(priv)     (((priv)->flags & RSND_GEN_MASK) == RSND_GEN2)
 
 /*
  *     rsnd_kctrl
@@ -523,7 +498,7 @@ struct rsnd_kctrl_cfg {
        struct snd_kcontrol *kctrl;
 };
 
-#define RSND_DVC_CHANNELS      2
+#define RSND_DVC_CHANNELS      8
 struct rsnd_kctrl_cfg_m {
        struct rsnd_kctrl_cfg cfg;
        u32 val[RSND_DVC_CHANNELS];
@@ -544,6 +519,7 @@ 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);
 int rsnd_kctrl_new_s(struct rsnd_mod *mod,
                     struct rsnd_dai_stream *io,
@@ -566,70 +542,93 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod,
 /*
  *     R-Car SSI
  */
-int rsnd_ssi_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv);
-void rsnd_ssi_remove(struct platform_device *pdev,
-                    struct rsnd_priv *priv);
+int rsnd_ssi_probe(struct rsnd_priv *priv);
+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))
 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")
+void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
+                           struct device_node *playback,
+                           struct device_node *capture);
+
+/*
+ *     R-Car SSIU
+ */
+int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
+                    struct rsnd_mod *mod);
+int rsnd_ssiu_probe(struct rsnd_priv *priv);
+void rsnd_ssiu_remove(struct rsnd_priv *priv);
+
 /*
  *     R-Car SRC
  */
-int rsnd_src_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv);
-void rsnd_src_remove(struct platform_device *pdev,
-                    struct rsnd_priv *priv);
+int rsnd_src_probe(struct rsnd_priv *priv);
+void rsnd_src_remove(struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
 unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
                                   struct rsnd_dai_stream *io,
                                   struct snd_pcm_runtime *runtime);
-int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
-                       struct rsnd_dai_stream *io,
-                       int use_busif);
-int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
-                      struct rsnd_dai_stream *io);
-int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod);
-int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod);
+#define rsnd_src_of_node(priv)                                         \
+       of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
+#define rsnd_parse_connect_src(rdai, playback, capture)                        \
+       rsnd_parse_connect_common(rdai, rsnd_src_mod_get,               \
+                                 rsnd_src_of_node(rsnd_rdai_to_priv(rdai)), \
+                                                  playback, capture)
 
 /*
  *     R-Car CTU
  */
-int rsnd_ctu_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv);
-
-void rsnd_ctu_remove(struct platform_device *pdev,
-                    struct rsnd_priv *priv);
+int rsnd_ctu_probe(struct rsnd_priv *priv);
+void rsnd_ctu_remove(struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
+#define rsnd_ctu_of_node(priv)                                         \
+       of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu")
+#define rsnd_parse_connect_ctu(rdai, playback, capture)                        \
+       rsnd_parse_connect_common(rdai, rsnd_ctu_mod_get,               \
+                                 rsnd_ctu_of_node(rsnd_rdai_to_priv(rdai)), \
+                                                  playback, capture)
 
 /*
  *     R-Car MIX
  */
-int rsnd_mix_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv);
-
-void rsnd_mix_remove(struct platform_device *pdev,
-                    struct rsnd_priv *priv);
+int rsnd_mix_probe(struct rsnd_priv *priv);
+void rsnd_mix_remove(struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id);
+#define rsnd_mix_of_node(priv)                                         \
+       of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,mix")
+#define rsnd_parse_connect_mix(rdai, playback, capture)                        \
+       rsnd_parse_connect_common(rdai, rsnd_mix_mod_get,               \
+                                 rsnd_mix_of_node(rsnd_rdai_to_priv(rdai)), \
+                                                  playback, capture)
 
 /*
  *     R-Car DVC
  */
-int rsnd_dvc_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv);
-void rsnd_dvc_remove(struct platform_device *pdev,
-                    struct rsnd_priv *priv);
+int rsnd_dvc_probe(struct rsnd_priv *priv);
+void rsnd_dvc_remove(struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
+#define rsnd_dvc_of_node(priv)                                         \
+       of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
+#define rsnd_parse_connect_dvc(rdai, playback, capture)                        \
+       rsnd_parse_connect_common(rdai, rsnd_dvc_mod_get,               \
+                                 rsnd_dvc_of_node(rsnd_rdai_to_priv(rdai)), \
+                                                  playback, capture)
+
+/*
+ *     R-Car CMD
+ */
+int rsnd_cmd_probe(struct rsnd_priv *priv);
+void rsnd_cmd_remove(struct rsnd_priv *priv);
+int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id);
+struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id);
 
 #ifdef DEBUG
 void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
index 94d23d8..8a357fd 100644 (file)
@@ -48,8 +48,11 @@ MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
 
 #define DAI_NAME_NUM   32
 struct rsrc_card_dai {
-       unsigned int fmt;
        unsigned int sysclk;
+       unsigned int tx_slot_mask;
+       unsigned int rx_slot_mask;
+       int slots;
+       int slot_width;
        struct clk *clk;
        char dai_name[DAI_NAME_NUM];
 };
@@ -110,18 +113,22 @@ static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
                                rtd->cpu_dai :
                                rtd->codec_dai;
 
-       if (dai_props->fmt) {
-               ret = snd_soc_dai_set_fmt(dai, dai_props->fmt);
+       if (dai_props->sysclk) {
+               ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0);
                if (ret && ret != -ENOTSUPP) {
-                       dev_err(dai->dev, "set_fmt error\n");
+                       dev_err(dai->dev, "set_sysclk error\n");
                        goto err;
                }
        }
 
-       if (dai_props->sysclk) {
-               ret = snd_soc_dai_set_sysclk(dai, 0, dai_props->sysclk, 0);
+       if (dai_props->slots) {
+               ret = snd_soc_dai_set_tdm_slot(dai,
+                                              dai_props->tx_slot_mask,
+                                              dai_props->rx_slot_mask,
+                                              dai_props->slots,
+                                              dai_props->slot_width);
                if (ret && ret != -ENOTSUPP) {
-                       dev_err(dai->dev, "set_sysclk error\n");
+                       dev_err(dai->dev, "set_tdm_slot error\n");
                        goto err;
                }
        }
@@ -148,14 +155,13 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
 }
 
 static int rsrc_card_parse_daifmt(struct device_node *node,
-                                 struct device_node *np,
+                                 struct device_node *codec,
                                  struct rsrc_card_priv *priv,
-                                 int idx, bool is_fe)
+                                 struct snd_soc_dai_link *dai_link,
+                                 unsigned int *retfmt)
 {
-       struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
        struct device_node *bitclkmaster = NULL;
        struct device_node *framemaster = NULL;
-       struct device_node *codec = is_fe ? NULL : np;
        unsigned int daifmt;
 
        daifmt = snd_soc_of_parse_daifmt(node, NULL,
@@ -172,11 +178,11 @@ static int rsrc_card_parse_daifmt(struct device_node *node,
                daifmt |= (codec == framemaster) ?
                        SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
 
-       dai_props->fmt  = daifmt;
-
        of_node_put(bitclkmaster);
        of_node_put(framemaster);
 
+       *retfmt = daifmt;
+
        return 0;
 }
 
@@ -198,6 +204,15 @@ static int rsrc_card_parse_links(struct device_node *np,
        if (ret)
                return ret;
 
+       /* Parse TDM slot */
+       ret = snd_soc_of_parse_tdm_slot(np,
+                                       &dai_props->tx_slot_mask,
+                                       &dai_props->rx_slot_mask,
+                                       &dai_props->slots,
+                                       &dai_props->slot_width);
+       if (ret)
+               return ret;
+
        if (is_fe) {
                /* BE is dummy */
                dai_link->codec_of_node         = NULL;
@@ -208,7 +223,9 @@ static int rsrc_card_parse_links(struct device_node *np,
                dai_link->dynamic               = 1;
                dai_link->dpcm_merged_format    = 1;
                dai_link->cpu_of_node           = args.np;
-               snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name);
+               ret = snd_soc_of_get_dai_name(np, &dai_link->cpu_dai_name);
+               if (ret < 0)
+                       return ret;
 
                /* set dai_name */
                snprintf(dai_props->dai_name, DAI_NAME_NUM, "fe.%s",
@@ -240,7 +257,9 @@ static int rsrc_card_parse_links(struct device_node *np,
                dai_link->no_pcm                = 1;
                dai_link->be_hw_params_fixup    = rsrc_card_be_hw_params_fixup;
                dai_link->codec_of_node         = args.np;
-               snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name);
+               ret = snd_soc_of_get_dai_name(np, &dai_link->codec_dai_name);
+               if (ret < 0)
+                       return ret;
 
                /* additional name prefix */
                if (of_data) {
@@ -305,23 +324,16 @@ static int rsrc_card_parse_clk(struct device_node *np,
        return 0;
 }
 
-static int rsrc_card_dai_link_of(struct device_node *node,
-                                struct device_node *np,
-                                struct rsrc_card_priv *priv,
-                                int idx)
+static int rsrc_card_dai_sub_link_of(struct device_node *node,
+                                    struct device_node *np,
+                                    struct rsrc_card_priv *priv,
+                                    int idx, bool is_fe)
 {
        struct device *dev = rsrc_priv_to_dev(priv);
+       struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx);
        struct rsrc_card_dai *dai_props = rsrc_priv_to_props(priv, idx);
-       bool is_fe = false;
        int ret;
 
-       if (0 == strcmp(np->name, "cpu"))
-               is_fe = true;
-
-       ret = rsrc_card_parse_daifmt(node, np, priv, idx, is_fe);
-       if (ret < 0)
-               return ret;
-
        ret = rsrc_card_parse_links(np, priv, idx, is_fe);
        if (ret < 0)
                return ret;
@@ -332,12 +344,54 @@ static int rsrc_card_dai_link_of(struct device_node *node,
 
        dev_dbg(dev, "\t%s / %04x / %d\n",
                dai_props->dai_name,
-               dai_props->fmt,
+               dai_link->dai_fmt,
                dai_props->sysclk);
 
        return ret;
 }
 
+static int rsrc_card_dai_link_of(struct device_node *node,
+                                struct rsrc_card_priv *priv)
+{
+       struct snd_soc_dai_link *dai_link;
+       struct device_node *np;
+       unsigned int daifmt = 0;
+       int ret, i;
+       bool is_fe;
+
+       /* find 1st codec */
+       i = 0;
+       for_each_child_of_node(node, np) {
+               dai_link = rsrc_priv_to_link(priv, i);
+
+               if (strcmp(np->name, "codec") == 0) {
+                       ret = rsrc_card_parse_daifmt(node, np, priv,
+                                                    dai_link, &daifmt);
+                       if (ret < 0)
+                               return ret;
+                       break;
+               }
+               i++;
+       }
+
+       i = 0;
+       for_each_child_of_node(node, np) {
+               dai_link = rsrc_priv_to_link(priv, i);
+               dai_link->dai_fmt = daifmt;
+
+               is_fe = false;
+               if (strcmp(np->name, "cpu") == 0)
+                       is_fe = true;
+
+               ret = rsrc_card_dai_sub_link_of(node, np, priv, i, is_fe);
+               if (ret < 0)
+                       return ret;
+               i++;
+       }
+
+       return 0;
+}
+
 static int rsrc_card_parse_of(struct device_node *node,
                              struct rsrc_card_priv *priv,
                              struct device *dev)
@@ -345,9 +399,8 @@ static int rsrc_card_parse_of(struct device_node *node,
        const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
        struct rsrc_card_dai *props;
        struct snd_soc_dai_link *links;
-       struct device_node *np;
        int ret;
-       int i, num;
+       int num;
 
        if (!node)
                return -EINVAL;
@@ -388,13 +441,9 @@ static int rsrc_card_parse_of(struct device_node *node,
                priv->snd_card.name ? priv->snd_card.name : "",
                priv->convert_rate);
 
-       i = 0;
-       for_each_child_of_node(node, np) {
-               ret = rsrc_card_dai_link_of(node, np, priv, i);
-               if (ret < 0)
-                       return ret;
-               i++;
-       }
+       ret = rsrc_card_dai_link_of(node, priv);
+       if (ret < 0)
+               return ret;
 
        if (!priv->snd_card.name)
                priv->snd_card.name = priv->snd_card.dai_link->name;
index 68b439e..5eda056 100644 (file)
 #define OUF_SRC(id)    ((1 << (id + 16)) | (1 << id))
 
 struct rsnd_src {
-       struct rsnd_src_platform_info *info; /* rcar_snd.h */
        struct rsnd_mod mod;
+       struct rsnd_mod *dma;
        struct rsnd_kctrl_cfg_s sen;  /* sync convert enable */
        struct rsnd_kctrl_cfg_s sync; /* sync convert */
        u32 convert_rate; /* sampling rate convert */
        int err;
+       int irq;
 };
 
 #define RSND_SRC_NAME_SIZE 16
 
+#define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id)
+#define rsnd_src_to_dma(src) ((src)->dma)
 #define rsnd_src_nr(priv) ((priv)->src_nr)
 #define rsnd_enable_sync_convert(src) ((src)->sen.val)
-#define rsnd_src_of_node(priv) \
-       of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
 
 #define rsnd_mod_to_src(_mod)                          \
        container_of((_mod), struct rsnd_src, mod)
@@ -69,67 +70,16 @@ struct rsnd_src {
  *        |-----------------|
  */
 
-/*
- *     How to use SRC bypass mode for debugging
- *
- * SRC has bypass mode, and it is useful for debugging.
- * In Gen2 case,
- * SRCm_MODE controls whether SRC is used or not
- * SSI_MODE0 controls whether SSIU which receives SRC data
- * is used or not.
- * Both SRCm_MODE/SSI_MODE0 settings are needed if you use SRC,
- * but SRC bypass mode needs SSI_MODE0 only.
- *
- * This driver request
- * struct rsnd_src_platform_info {
- *     u32 convert_rate;
- *     int dma_id;
- * }
- *
- * rsnd_src_convert_rate() indicates
- * above convert_rate, and it controls
- * whether SRC is used or not.
- *
- * ex) doesn't use SRC
- * static struct rsnd_dai_platform_info rsnd_dai = {
- *     .playback = { .ssi = &rsnd_ssi[0], },
- * };
- *
- * ex) uses SRC
- * static struct rsnd_src_platform_info rsnd_src[] = {
- *     RSND_SCU(48000, 0),
- *     ...
- * };
- * static struct rsnd_dai_platform_info rsnd_dai = {
- *     .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] },
- * };
- *
- * ex) uses SRC bypass mode
- * static struct rsnd_src_platform_info rsnd_src[] = {
- *     RSND_SCU(0, 0),
- *     ...
- * };
- * static struct rsnd_dai_platform_info rsnd_dai = {
- *     .playback = { .ssi = &rsnd_ssi[0], .src = &rsnd_src[0] },
- * };
- *
- */
-
-/*
- *             Gen1/Gen2 common functions
- */
-static void rsnd_src_soft_reset(struct rsnd_mod *mod)
+static void rsnd_src_activation(struct rsnd_mod *mod)
 {
        rsnd_mod_write(mod, SRC_SWRSR, 0);
        rsnd_mod_write(mod, SRC_SWRSR, 1);
 }
 
-
-#define rsnd_src_initialize_lock(mod)  __rsnd_src_initialize_lock(mod, 1)
-#define rsnd_src_initialize_unlock(mod)        __rsnd_src_initialize_lock(mod, 0)
-static void __rsnd_src_initialize_lock(struct rsnd_mod *mod, u32 enable)
+static void rsnd_src_halt(struct rsnd_mod *mod)
 {
-       rsnd_mod_write(mod, SRC_SRCIR, enable);
+       rsnd_mod_write(mod, SRC_SRCIR, 1);
+       rsnd_mod_write(mod, SRC_SWRSR, 0);
 }
 
 static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
@@ -143,99 +93,6 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
                                        is_play ? "rx" : "tx");
 }
 
-int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
-                       struct rsnd_dai_stream *io,
-                       int use_busif)
-{
-       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-       int ssi_id = rsnd_mod_id(ssi_mod);
-
-       /*
-        * SSI_MODE0
-        */
-       rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id),
-                     !use_busif << ssi_id);
-
-       /*
-        * SSI_MODE1
-        */
-       if (rsnd_ssi_is_pin_sharing(io)) {
-               int shift = -1;
-               switch (ssi_id) {
-               case 1:
-                       shift = 0;
-                       break;
-               case 2:
-                       shift = 2;
-                       break;
-               case 4:
-                       shift = 16;
-                       break;
-               }
-
-               if (shift >= 0)
-                       rsnd_mod_bset(ssi_mod, SSI_MODE1,
-                                     0x3 << shift,
-                                     rsnd_rdai_is_clk_master(rdai) ?
-                                     0x2 << shift : 0x1 << shift);
-       }
-
-       /*
-        * DMA settings for SSIU
-        */
-       if (use_busif) {
-               u32 val = rsnd_get_dalign(ssi_mod, io);
-
-               rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR,
-                              rsnd_get_adinr_bit(ssi_mod, io));
-               rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE,  1);
-               rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1);
-
-               rsnd_mod_write(ssi_mod, SSI_BUSIF_DALIGN, val);
-       }
-
-       return 0;
-}
-
-int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
-                      struct rsnd_dai_stream *io)
-{
-       /*
-        * DMA settings for SSIU
-        */
-       rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
-
-       return 0;
-}
-
-int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod)
-{
-       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
-
-       if (rsnd_is_gen1(priv))
-               return 0;
-
-       /* enable SSI interrupt if Gen2 */
-       rsnd_mod_write(ssi_mod, SSI_INT_ENABLE,
-                      rsnd_ssi_is_dma_mode(ssi_mod) ?
-                      0x0e000000 : 0x0f000000);
-
-       return 0;
-}
-
-int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod)
-{
-       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
-
-       if (rsnd_is_gen1(priv))
-               return 0;
-
-       /* disable SSI interrupt if Gen2 */
-       rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000);
-
-       return 0;
-}
-
 static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
                                 struct rsnd_src *src)
 {
@@ -283,34 +140,6 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
        return rate;
 }
 
-static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
-                                    struct rsnd_dai_stream *io)
-{
-       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-       u32 convert_rate = rsnd_src_convert_rate(io, src);
-       u32 fsrate = 0;
-
-       if (convert_rate)
-               fsrate = 0x0400000 / convert_rate * runtime->rate;
-
-       /* Set channel number and output bit length */
-       rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr_bit(mod, io));
-
-       /* Enable the initial value of IFS */
-       if (fsrate) {
-               rsnd_mod_write(mod, SRC_IFSCR, 1);
-
-               /* Set initial value of IFS */
-               rsnd_mod_write(mod, SRC_IFSVR, fsrate);
-       }
-
-       /* use DMA transfer */
-       rsnd_mod_write(mod, SRC_BUSIF_MODE, 1);
-
-       return 0;
-}
-
 static int rsnd_src_hw_params(struct rsnd_mod *mod,
                              struct rsnd_dai_stream *io,
                              struct snd_pcm_substream *substream,
@@ -319,9 +148,6 @@ static int rsnd_src_hw_params(struct rsnd_mod *mod,
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        struct snd_soc_pcm_runtime *fe = substream->private_data;
 
-       /* default value (mainly for non-DT) */
-       src->convert_rate = src->info->convert_rate;
-
        /*
         * SRC assumes that it is used under DPCM if user want to use
         * sampling rate convert. Then, SRC should be FE.
@@ -347,250 +173,112 @@ static int rsnd_src_hw_params(struct rsnd_mod *mod,
        return 0;
 }
 
-static int rsnd_src_init(struct rsnd_mod *mod,
-                        struct rsnd_priv *priv)
-{
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-
-       rsnd_mod_power_on(mod);
-
-       rsnd_src_soft_reset(mod);
-
-       rsnd_src_initialize_lock(mod);
-
-       src->err = 0;
-
-       /* reset sync convert_rate */
-       src->sync.val = 0;
-
-       return 0;
-}
-
-static int rsnd_src_quit(struct rsnd_mod *mod,
-                        struct rsnd_dai_stream *io,
-                        struct rsnd_priv *priv)
+static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
+                                     struct rsnd_mod *mod)
 {
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       struct rsnd_src *src = rsnd_mod_to_src(mod);
+       u32 convert_rate = rsnd_src_convert_rate(io, src);
+       u32 ifscr, fsrate, adinr;
+       u32 cr, route;
+       u32 bsdsr, bsisr;
+       uint ratio;
 
-       rsnd_mod_power_off(mod);
-
-       if (src->err)
-               dev_warn(dev, "%s[%d] under/over flow err = %d\n",
-                        rsnd_mod_name(mod), rsnd_mod_id(mod), src->err);
-
-       src->convert_rate = 0;
-
-       /* reset sync convert_rate */
-       src->sync.val = 0;
-
-       return 0;
-}
-
-static int rsnd_src_start(struct rsnd_mod *mod)
-{
-       rsnd_src_initialize_unlock(mod);
-
-       return 0;
-}
-
-static int rsnd_src_stop(struct rsnd_mod *mod)
-{
-       /* nothing to do */
-       return 0;
-}
+       if (!runtime)
+               return;
 
-/*
- *             Gen1 functions
- */
-static int rsnd_src_set_route_gen1(struct rsnd_dai_stream *io,
-                                  struct rsnd_mod *mod)
-{
-       struct src_route_config {
-               u32 mask;
-               int shift;
-       } routes[] = {
-               { 0xF,  0, }, /* 0 */
-               { 0xF,  4, }, /* 1 */
-               { 0xF,  8, }, /* 2 */
-               { 0x7, 12, }, /* 3 */
-               { 0x7, 16, }, /* 4 */
-               { 0x7, 20, }, /* 5 */
-               { 0x7, 24, }, /* 6 */
-               { 0x3, 28, }, /* 7 */
-               { 0x3, 30, }, /* 8 */
-       };
-       u32 mask;
-       u32 val;
-       int id;
+       /* 6 - 1/6 are very enough ratio for SRC_BSDSR */
+       if (!convert_rate)
+               ratio = 0;
+       else if (convert_rate > runtime->rate)
+               ratio = 100 * convert_rate / runtime->rate;
+       else
+               ratio = 100 * runtime->rate / convert_rate;
 
-       id = rsnd_mod_id(mod);
-       if (id < 0 || id >= ARRAY_SIZE(routes))
-               return -EIO;
+       if (ratio > 600) {
+               dev_err(dev, "FSO/FSI ratio error\n");
+               return;
+       }
 
        /*
-        * SRC_ROUTE_SELECT
+        *      SRC_ADINR
         */
-       val = rsnd_io_is_play(io) ? 0x1 : 0x2;
-       val = val               << routes[id].shift;
-       mask = routes[id].mask  << routes[id].shift;
-
-       rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val);
-
-       return 0;
-}
-
-static int rsnd_src_set_convert_timing_gen1(struct rsnd_dai_stream *io,
-                                           struct rsnd_mod *mod)
-{
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
-       u32 convert_rate = rsnd_src_convert_rate(io, src);
-       u32 mask;
-       u32 val;
-       int shift;
-       int id = rsnd_mod_id(mod);
-       int ret;
+       adinr = rsnd_get_adinr_bit(mod, io) |
+               rsnd_get_adinr_chan(mod, io);
 
        /*
-        * SRC_TIMING_SELECT
+        *      SRC_IFSCR / SRC_IFSVR
         */
-       shift   = (id % 4) * 8;
-       mask    = 0x1F << shift;
+       ifscr = 0;
+       fsrate = 0;
+       if (convert_rate) {
+               ifscr = 1;
+               fsrate = 0x0400000 / convert_rate * runtime->rate;
+       }
 
        /*
-        * ADG is used as source clock if SRC was used,
-        * then, SSI WS is used as destination clock.
-        * SSI WS is used as source clock if SRC is not used
-        * (when playback, source/destination become reverse when capture)
+        *      SRC_SRCCR / SRC_ROUTE_MODE0
         */
-       ret = 0;
+       cr      = 0x00011110;
+       route   = 0x0;
        if (convert_rate) {
-               /* use ADG */
-               val = 0;
-               ret = rsnd_adg_set_convert_clk_gen1(priv, mod,
-                                                   runtime->rate,
-                                                   convert_rate);
-       } else if (8 == id) {
-               /* use SSI WS, but SRU8 is special */
-               val = id << shift;
-       } else {
-               /* use SSI WS */
-               val = (id + 1) << shift;
-       }
+               route   = 0x1;
 
-       if (ret < 0)
-               return ret;
+               if (rsnd_enable_sync_convert(src)) {
+                       cr |= 0x1;
+                       route |= rsnd_io_is_play(io) ?
+                               (0x1 << 24) : (0x1 << 25);
+               }
+       }
 
-       switch (id / 4) {
-       case 0:
-               rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val);
-               break;
-       case 1:
-               rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val);
+       /*
+        * SRC_BSDSR / SRC_BSISR
+        */
+       switch (rsnd_mod_id(mod)) {
+       case 5:
+       case 6:
+       case 7:
+       case 8:
+               bsdsr = 0x02400000; /* 6 - 1/6 */
+               bsisr = 0x00100060; /* 6 - 1/6 */
                break;
-       case 2:
-               rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val);
+       default:
+               bsdsr = 0x01800000; /* 6 - 1/6 */
+               bsisr = 0x00100060 ;/* 6 - 1/6 */
                break;
        }
 
-       return 0;
-}
-
-static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
-                                         struct rsnd_dai_stream *io)
-{
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-       int ret;
-
-       ret = rsnd_src_set_convert_rate(mod, io);
-       if (ret < 0)
-               return ret;
-
-       /* Select SRC mode (fixed value) */
-       rsnd_mod_write(mod, SRC_SRCCR, 0x00010110);
-
-       /* Set the restriction value of the FS ratio (98%) */
-       rsnd_mod_write(mod, SRC_MNFSR,
-                      rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
-
-       /* Gen1/Gen2 are not compatible */
-       if (rsnd_src_convert_rate(io, src))
-               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
-
-       /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
-
-       return 0;
-}
-
-static int rsnd_src_init_gen1(struct rsnd_mod *mod,
-                             struct rsnd_dai_stream *io,
-                             struct rsnd_priv *priv)
-{
-       int ret;
-
-       ret = rsnd_src_init(mod, priv);
-       if (ret < 0)
-               return ret;
-
-       ret = rsnd_src_set_route_gen1(io, mod);
-       if (ret < 0)
-               return ret;
-
-       ret = rsnd_src_set_convert_rate_gen1(mod, io);
-       if (ret < 0)
-               return ret;
-
-       ret = rsnd_src_set_convert_timing_gen1(io, mod);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static int rsnd_src_start_gen1(struct rsnd_mod *mod,
-                              struct rsnd_dai_stream *io,
-                              struct rsnd_priv *priv)
-{
-       int id = rsnd_mod_id(mod);
-
-       rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id));
-
-       return rsnd_src_start(mod);
-}
-
-static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
-                             struct rsnd_dai_stream *io,
-                             struct rsnd_priv *priv)
-{
-       int id = rsnd_mod_id(mod);
+       rsnd_mod_write(mod, SRC_SRCIR, 1);      /* initialize */
+       rsnd_mod_write(mod, SRC_ADINR, adinr);
+       rsnd_mod_write(mod, SRC_IFSCR, ifscr);
+       rsnd_mod_write(mod, SRC_IFSVR, fsrate);
+       rsnd_mod_write(mod, SRC_SRCCR, cr);
+       rsnd_mod_write(mod, SRC_BSDSR, bsdsr);
+       rsnd_mod_write(mod, SRC_BSISR, bsisr);
+       rsnd_mod_write(mod, SRC_SRCIR, 0);      /* cancel initialize */
 
-       rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0);
+       rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
+       rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1);
+       rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1);
+       rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
 
-       return rsnd_src_stop(mod);
+       if (convert_rate)
+               rsnd_adg_set_convert_clk_gen2(mod, io,
+                                             runtime->rate,
+                                             convert_rate);
+       else
+               rsnd_adg_set_convert_timing_gen2(mod, io);
 }
 
-static struct rsnd_mod_ops rsnd_src_gen1_ops = {
-       .name   = SRC_NAME,
-       .dma_req = rsnd_src_dma_req,
-       .init   = rsnd_src_init_gen1,
-       .quit   = rsnd_src_quit,
-       .start  = rsnd_src_start_gen1,
-       .stop   = rsnd_src_stop_gen1,
-       .hw_params = rsnd_src_hw_params,
-};
-
-/*
- *             Gen2 functions
- */
-#define rsnd_src_irq_enable_gen2(mod)  rsnd_src_irq_ctrol_gen2(mod, 1)
-#define rsnd_src_irq_disable_gen2(mod) rsnd_src_irq_ctrol_gen2(mod, 0)
-static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable)
+#define rsnd_src_irq_enable(mod)  rsnd_src_irq_ctrol(mod, 1)
+#define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0)
+static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        u32 sys_int_val, int_val, sys_int_mask;
-       int irq = src->info->irq;
+       int irq = src->irq;
        int id = rsnd_mod_id(mod);
 
        sys_int_val =
@@ -600,7 +288,7 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable)
        /*
         * IRQ is not supported on non-DT
         * see
-        *      rsnd_src_probe_gen2()
+        *      rsnd_src_probe_()
         */
        if ((irq <= 0) || !enable) {
                sys_int_val = 0;
@@ -620,7 +308,7 @@ static void rsnd_src_irq_ctrol_gen2(struct rsnd_mod *mod, int enable)
        rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val);
 }
 
-static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod)
+static void rsnd_src_status_clear(struct rsnd_mod *mod)
 {
        u32 val = OUF_SRC(rsnd_mod_id(mod));
 
@@ -628,7 +316,7 @@ static void rsnd_src_error_clear_gen2(struct rsnd_mod *mod)
        rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val);
 }
 
-static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod)
+static bool rsnd_src_record_error(struct rsnd_mod *mod)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        u32 val0, val1;
@@ -652,22 +340,16 @@ static bool rsnd_src_error_record_gen2(struct rsnd_mod *mod)
                ret = true;
        }
 
-       /* clear error static */
-       rsnd_src_error_clear_gen2(mod);
-
        return ret;
 }
 
-static int _rsnd_src_start_gen2(struct rsnd_mod *mod,
-                               struct rsnd_dai_stream *io)
+static int rsnd_src_start(struct rsnd_mod *mod,
+                         struct rsnd_dai_stream *io,
+                         struct rsnd_priv *priv)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        u32 val;
 
-       val = rsnd_get_dalign(mod, io);
-
-       rsnd_mod_write(mod, SRC_BUSIF_DALIGN, val);
-
        /*
         * WORKAROUND
         *
@@ -678,247 +360,149 @@ static int _rsnd_src_start_gen2(struct rsnd_mod *mod,
 
        rsnd_mod_write(mod, SRC_CTRL, val);
 
-       rsnd_src_error_clear_gen2(mod);
-
-       rsnd_src_start(mod);
+       return 0;
+}
 
-       rsnd_src_irq_enable_gen2(mod);
+static int rsnd_src_stop(struct rsnd_mod *mod,
+                        struct rsnd_dai_stream *io,
+                        struct rsnd_priv *priv)
+{
+       /*
+        * stop SRC output only
+        * see rsnd_src_quit
+        */
+       rsnd_mod_write(mod, SRC_CTRL, 0x01);
 
        return 0;
 }
 
-static int _rsnd_src_stop_gen2(struct rsnd_mod *mod)
+static int rsnd_src_init(struct rsnd_mod *mod,
+                        struct rsnd_dai_stream *io,
+                        struct rsnd_priv *priv)
 {
-       rsnd_src_irq_disable_gen2(mod);
+       struct rsnd_src *src = rsnd_mod_to_src(mod);
 
-       rsnd_mod_write(mod, SRC_CTRL, 0);
+       rsnd_mod_power_on(mod);
+
+       rsnd_src_activation(mod);
+
+       rsnd_src_set_convert_rate(io, mod);
 
-       rsnd_src_error_record_gen2(mod);
+       rsnd_src_status_clear(mod);
+
+       rsnd_src_irq_enable(mod);
+
+       src->err = 0;
 
-       return rsnd_src_stop(mod);
+       /* reset sync convert_rate */
+       src->sync.val = 0;
+
+       return 0;
 }
 
-static void __rsnd_src_interrupt_gen2(struct rsnd_mod *mod,
-                                     struct rsnd_dai_stream *io)
+static int rsnd_src_quit(struct rsnd_mod *mod,
+                        struct rsnd_dai_stream *io,
+                        struct rsnd_priv *priv)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-
-       spin_lock(&priv->lock);
+       struct rsnd_src *src = rsnd_mod_to_src(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
 
-       /* ignore all cases if not working */
-       if (!rsnd_io_is_working(io))
-               goto rsnd_src_interrupt_gen2_out;
+       rsnd_src_irq_disable(mod);
 
-       if (rsnd_src_error_record_gen2(mod)) {
-               struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-               struct rsnd_src *src = rsnd_mod_to_src(mod);
-               struct device *dev = rsnd_priv_to_dev(priv);
+       /* stop both out/in */
+       rsnd_mod_write(mod, SRC_CTRL, 0);
 
-               dev_dbg(dev, "%s[%d] restart\n",
-                       rsnd_mod_name(mod), rsnd_mod_id(mod));
+       rsnd_src_halt(mod);
 
-               _rsnd_src_stop_gen2(mod);
-               if (src->err < 1024)
-                       _rsnd_src_start_gen2(mod, io);
-               else
-                       dev_warn(dev, "no more SRC restart\n");
-       }
+       rsnd_mod_power_off(mod);
 
-rsnd_src_interrupt_gen2_out:
-       spin_unlock(&priv->lock);
-}
+       if (src->err)
+               dev_warn(dev, "%s[%d] under/over flow err = %d\n",
+                        rsnd_mod_name(mod), rsnd_mod_id(mod), src->err);
 
-static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
-{
-       struct rsnd_mod *mod = data;
+       src->convert_rate = 0;
 
-       rsnd_mod_interrupt(mod, __rsnd_src_interrupt_gen2);
+       /* reset sync convert_rate */
+       src->sync.val = 0;
 
-       return IRQ_HANDLED;
+       return 0;
 }
 
-static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
-                                         struct rsnd_dai_stream *io)
+static void __rsnd_src_interrupt(struct rsnd_mod *mod,
+                                struct rsnd_dai_stream *io)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-       struct device *dev = rsnd_priv_to_dev(priv);
-       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        struct rsnd_src *src = rsnd_mod_to_src(mod);
-       u32 convert_rate = rsnd_src_convert_rate(io, src);
-       u32 cr, route;
-       uint ratio;
-       int ret;
+       struct device *dev = rsnd_priv_to_dev(priv);
 
-       /* 6 - 1/6 are very enough ratio for SRC_BSDSR */
-       if (!convert_rate)
-               ratio = 0;
-       else if (convert_rate > runtime->rate)
-               ratio = 100 * convert_rate / runtime->rate;
-       else
-               ratio = 100 * runtime->rate / convert_rate;
+       spin_lock(&priv->lock);
 
-       if (ratio > 600) {
-               dev_err(dev, "FSO/FSI ratio error\n");
-               return -EINVAL;
-       }
+       /* ignore all cases if not working */
+       if (!rsnd_io_is_working(io))
+               goto rsnd_src_interrupt_out;
 
-       ret = rsnd_src_set_convert_rate(mod, io);
-       if (ret < 0)
-               return ret;
+       if (rsnd_src_record_error(mod)) {
 
-       cr      = 0x00011110;
-       route   = 0x0;
-       if (convert_rate) {
-               route   = 0x1;
+               dev_dbg(dev, "%s[%d] restart\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
 
-               if (rsnd_enable_sync_convert(src)) {
-                       cr |= 0x1;
-                       route |= rsnd_io_is_play(io) ?
-                               (0x1 << 24) : (0x1 << 25);
-               }
+               rsnd_src_stop(mod, io, priv);
+               rsnd_src_start(mod, io, priv);
        }
 
-       rsnd_mod_write(mod, SRC_SRCCR, cr);
-       rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
+       if (src->err > 1024) {
+               rsnd_src_irq_disable(mod);
 
-       switch (rsnd_mod_id(mod)) {
-       case 5:
-       case 6:
-       case 7:
-       case 8:
-               rsnd_mod_write(mod, SRC_BSDSR, 0x02400000);
-               break;
-       default:
-               rsnd_mod_write(mod, SRC_BSDSR, 0x01800000);
-               break;
+               dev_warn(dev, "no more %s[%d] restart\n",
+                        rsnd_mod_name(mod), rsnd_mod_id(mod));
        }
 
-       rsnd_mod_write(mod, SRC_BSISR, 0x00100060);
+       rsnd_src_status_clear(mod);
+rsnd_src_interrupt_out:
 
-       return 0;
+       spin_unlock(&priv->lock);
 }
 
-static int rsnd_src_set_convert_timing_gen2(struct rsnd_dai_stream *io,
-                                           struct rsnd_mod *mod)
+static irqreturn_t rsnd_src_interrupt(int irq, void *data)
 {
-       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-       u32 convert_rate = rsnd_src_convert_rate(io, src);
-       int ret;
+       struct rsnd_mod *mod = data;
 
-       if (convert_rate)
-               ret = rsnd_adg_set_convert_clk_gen2(mod, io,
-                                                   runtime->rate,
-                                                   convert_rate);
-       else
-               ret = rsnd_adg_set_convert_timing_gen2(mod, io);
+       rsnd_mod_interrupt(mod, __rsnd_src_interrupt);
 
-       return ret;
+       return IRQ_HANDLED;
 }
 
-static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
-                              struct rsnd_dai_stream *io,
-                              struct rsnd_priv *priv)
+static int rsnd_src_probe_(struct rsnd_mod *mod,
+                          struct rsnd_dai_stream *io,
+                          struct rsnd_priv *priv)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
-       int irq = src->info->irq;
+       int irq = src->irq;
        int ret;
 
        if (irq > 0) {
                /*
                 * IRQ is not supported on non-DT
                 * see
-                *      rsnd_src_irq_enable_gen2()
+                *      rsnd_src_irq_enable()
                 */
                ret = devm_request_irq(dev, irq,
-                                      rsnd_src_interrupt_gen2,
+                                      rsnd_src_interrupt,
                                       IRQF_SHARED,
                                       dev_name(dev), mod);
                if (ret)
                        return ret;
        }
 
-       ret = rsnd_dma_init(io,
-                           rsnd_mod_to_dma(mod),
-                           src->info->dma_id);
+       src->dma = rsnd_dma_attach(io, mod, 0);
+       if (IS_ERR(src->dma))
+               return PTR_ERR(src->dma);
 
        return ret;
 }
 
-static int rsnd_src_remove_gen2(struct rsnd_mod *mod,
-                               struct rsnd_dai_stream *io,
-                               struct rsnd_priv *priv)
-{
-       rsnd_dma_quit(io, rsnd_mod_to_dma(mod));
-
-       return 0;
-}
-
-static int rsnd_src_init_gen2(struct rsnd_mod *mod,
-                             struct rsnd_dai_stream *io,
-                             struct rsnd_priv *priv)
-{
-       int ret;
-
-       ret = rsnd_src_init(mod, priv);
-       if (ret < 0)
-               return ret;
-
-       ret = rsnd_src_set_convert_rate_gen2(mod, io);
-       if (ret < 0)
-               return ret;
-
-       ret = rsnd_src_set_convert_timing_gen2(io, mod);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
-static int rsnd_src_start_gen2(struct rsnd_mod *mod,
-                              struct rsnd_dai_stream *io,
-                              struct rsnd_priv *priv)
-{
-       rsnd_dma_start(io, rsnd_mod_to_dma(mod));
-
-       return _rsnd_src_start_gen2(mod, io);
-}
-
-static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
-                             struct rsnd_dai_stream *io,
-                             struct rsnd_priv *priv)
-{
-       int ret;
-
-       ret = _rsnd_src_stop_gen2(mod);
-
-       rsnd_dma_stop(io, rsnd_mod_to_dma(mod));
-
-       return ret;
-}
-
-static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io,
-                                     struct rsnd_mod *mod)
-{
-       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-       u32 convert_rate = rsnd_src_convert_rate(io, src);
-       u32 fsrate;
-
-       if (!runtime)
-               return;
-
-       if (!convert_rate)
-               convert_rate = runtime->rate;
-
-       fsrate = 0x0400000 / convert_rate * runtime->rate;
-
-       /* update IFS */
-       rsnd_mod_write(mod, SRC_IFSVR, fsrate);
-}
-
-static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
+static int rsnd_src_pcm_new(struct rsnd_mod *mod,
                            struct rsnd_dai_stream *io,
                            struct snd_soc_pcm_runtime *rtd)
 {
@@ -950,7 +534,7 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
                               rsnd_io_is_play(io) ?
                               "SRC Out Rate Switch" :
                               "SRC In Rate Switch",
-                              rsnd_src_reconvert_update,
+                              rsnd_src_set_convert_rate,
                               &src->sen, 1);
        if (ret < 0)
                return ret;
@@ -959,23 +543,22 @@ static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
                               rsnd_io_is_play(io) ?
                               "SRC Out Rate" :
                               "SRC In Rate",
-                              rsnd_src_reconvert_update,
+                              rsnd_src_set_convert_rate,
                               &src->sync, 192000);
 
        return ret;
 }
 
-static struct rsnd_mod_ops rsnd_src_gen2_ops = {
+static struct rsnd_mod_ops rsnd_src_ops = {
        .name   = SRC_NAME,
        .dma_req = rsnd_src_dma_req,
-       .probe  = rsnd_src_probe_gen2,
-       .remove = rsnd_src_remove_gen2,
-       .init   = rsnd_src_init_gen2,
+       .probe  = rsnd_src_probe_,
+       .init   = rsnd_src_init,
        .quit   = rsnd_src_quit,
-       .start  = rsnd_src_start_gen2,
-       .stop   = rsnd_src_stop_gen2,
+       .start  = rsnd_src_start,
+       .stop   = rsnd_src_stop,
        .hw_params = rsnd_src_hw_params,
-       .pcm_new = rsnd_src_pcm_new_gen2,
+       .pcm_new = rsnd_src_pcm_new,
 };
 
 struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
@@ -983,113 +566,78 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
                id = 0;
 
-       return rsnd_mod_get((struct rsnd_src *)(priv->src) + id);
+       return rsnd_mod_get(rsnd_src_get(priv, id));
 }
 
-static void rsnd_of_parse_src(struct platform_device *pdev,
-                             const struct rsnd_of_data *of_data,
-                             struct rsnd_priv *priv)
+int rsnd_src_probe(struct rsnd_priv *priv)
 {
-       struct device_node *src_node;
+       struct device_node *node;
        struct device_node *np;
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
-       struct rsnd_src_platform_info *src_info;
-       struct device *dev = &pdev->dev;
-       int nr, i;
-
-       if (!of_data)
-               return;
-
-       src_node = rsnd_src_of_node(priv);
-       if (!src_node)
-               return;
-
-       nr = of_get_child_count(src_node);
-       if (!nr)
-               goto rsnd_of_parse_src_end;
-
-       src_info = devm_kzalloc(dev,
-                               sizeof(struct rsnd_src_platform_info) * nr,
-                               GFP_KERNEL);
-       if (!src_info) {
-               dev_err(dev, "src info allocation error\n");
-               goto rsnd_of_parse_src_end;
-       }
-
-       info->src_info          = src_info;
-       info->src_info_nr       = nr;
-
-       i = 0;
-       for_each_child_of_node(src_node, np) {
-               src_info[i].irq = irq_of_parse_and_map(np, 0);
-
-               i++;
-       }
-
-rsnd_of_parse_src_end:
-       of_node_put(src_node);
-}
-
-int rsnd_src_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv)
-{
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_src *src;
-       struct rsnd_mod_ops *ops;
        struct clk *clk;
        char name[RSND_SRC_NAME_SIZE];
        int i, nr, ret;
 
-       ops = NULL;
-       if (rsnd_is_gen1(priv)) {
-               ops = &rsnd_src_gen1_ops;
-               dev_warn(dev, "Gen1 support will be removed soon\n");
-       }
-       if (rsnd_is_gen2(priv))
-               ops = &rsnd_src_gen2_ops;
-       if (!ops) {
-               dev_err(dev, "unknown Generation\n");
-               return -EIO;
-       }
+       /* This driver doesn't support Gen1 at this point */
+       if (rsnd_is_gen1(priv))
+               return 0;
 
-       rsnd_of_parse_src(pdev, of_data, priv);
+       node = rsnd_src_of_node(priv);
+       if (!node)
+               return 0; /* not used is not error */
 
-       /*
-        * init SRC
-        */
-       nr      = info->src_info_nr;
-       if (!nr)
-               return 0;
+       nr = of_get_child_count(node);
+       if (!nr) {
+               ret = -EINVAL;
+               goto rsnd_src_probe_done;
+       }
 
        src     = devm_kzalloc(dev, sizeof(*src) * nr, GFP_KERNEL);
-       if (!src)
-               return -ENOMEM;
+       if (!src) {
+               ret = -ENOMEM;
+               goto rsnd_src_probe_done;
+       }
 
        priv->src_nr    = nr;
        priv->src       = src;
 
-       for_each_rsnd_src(src, priv, i) {
+       i = 0;
+       for_each_child_of_node(node, np) {
+               src = rsnd_src_get(priv, i);
+
                snprintf(name, RSND_SRC_NAME_SIZE, "%s.%d",
                         SRC_NAME, i);
 
-               clk = devm_clk_get(dev, name);
-               if (IS_ERR(clk))
-                       return PTR_ERR(clk);
+               src->irq = irq_of_parse_and_map(np, 0);
+               if (!src->irq) {
+                       ret = -EINVAL;
+                       goto rsnd_src_probe_done;
+               }
 
-               src->info = &info->src_info[i];
+               clk = devm_clk_get(dev, name);
+               if (IS_ERR(clk)) {
+                       ret = PTR_ERR(clk);
+                       goto rsnd_src_probe_done;
+               }
 
-               ret = rsnd_mod_init(priv, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i);
+               ret = rsnd_mod_init(priv, rsnd_mod_get(src),
+                                   &rsnd_src_ops, clk, RSND_MOD_SRC, i);
                if (ret)
-                       return ret;
+                       goto rsnd_src_probe_done;
+
+               i++;
        }
 
-       return 0;
+       ret = 0;
+
+rsnd_src_probe_done:
+       of_node_put(node);
+
+       return ret;
 }
 
-void rsnd_src_remove(struct platform_device *pdev,
-                    struct rsnd_priv *priv)
+void rsnd_src_remove(struct rsnd_priv *priv)
 {
        struct rsnd_src *src;
        int i;
index 1427ec2..7db05fd 100644 (file)
@@ -24,7 +24,9 @@
 #define        OIEN            (1 << 26)       /* Overflow Interrupt Enable */
 #define        IIEN            (1 << 25)       /* Idle Mode Interrupt Enable */
 #define        DIEN            (1 << 24)       /* Data Interrupt Enable */
-
+#define        CHNL_4          (1 << 22)       /* Channels */
+#define        CHNL_6          (2 << 22)       /* Channels */
+#define        CHNL_8          (3 << 22)       /* Channels */
 #define        DWL_8           (0 << 19)       /* Data Word Length */
 #define        DWL_16          (1 << 19)       /* Data Word Length */
 #define        DWL_18          (2 << 19)       /* Data Word Length */
@@ -39,6 +41,7 @@
 #define        SCKP            (1 << 13)       /* Serial Bit Clock Polarity */
 #define        SWSP            (1 << 12)       /* Serial WS Polarity */
 #define        SDTA            (1 << 10)       /* Serial Data Alignment */
+#define        PDTA            (1 <<  9)       /* Parallel Data Alignment */
 #define        DEL             (1 <<  8)       /* Serial Data Delay */
 #define        CKDV(v)         (v <<  4)       /* Serial Clock Division Ratio */
 #define        TRMD            (1 <<  1)       /* Transmit/Receive Mode Select */
  * SSIWSR
  */
 #define CONT           (1 << 8)        /* WS Continue Function */
+#define WS_MODE                (1 << 0)        /* WS Mode */
 
 #define SSI_NAME "ssi"
 
 struct rsnd_ssi {
-       struct rsnd_ssi_platform_info *info; /* rcar_snd.h */
        struct rsnd_ssi *parent;
        struct rsnd_mod mod;
+       struct rsnd_mod *dma;
 
+       u32 flags;
        u32 cr_own;
        u32 cr_clk;
+       u32 cr_mode;
+       u32 wsr;
        int chan;
+       int rate;
        int err;
+       int irq;
        unsigned int usrcnt;
 };
 
+/* flags */
+#define RSND_SSI_CLK_PIN_SHARE         (1 << 0)
+#define RSND_SSI_NO_BUSIF              (1 << 1) /* SSI+DMA without BUSIF */
+
 #define for_each_rsnd_ssi(pos, priv, i)                                        \
        for (i = 0;                                                     \
             (i < rsnd_ssi_nr(priv)) &&                                 \
                ((pos) = ((struct rsnd_ssi *)(priv)->ssi + i));         \
             i++)
 
+#define rsnd_ssi_get(priv, id) ((struct rsnd_ssi *)(priv->ssi) + id)
+#define rsnd_ssi_to_dma(mod) ((ssi)->dma)
 #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_of_node(priv) \
-       of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
+#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)
 {
@@ -103,6 +115,16 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
        return use_busif;
 }
 
+static void rsnd_ssi_status_clear(struct rsnd_mod *mod)
+{
+       rsnd_mod_write(mod, SSISR, 0);
+}
+
+static u32 rsnd_ssi_status_get(struct rsnd_mod *mod)
+{
+       return rsnd_mod_read(mod, SSISR);
+}
+
 static void rsnd_ssi_status_check(struct rsnd_mod *mod,
                                  u32 bit)
 {
@@ -112,7 +134,7 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,
        int i;
 
        for (i = 0; i < 1024; i++) {
-               status = rsnd_mod_read(mod, SSISR);
+               status = rsnd_ssi_status_get(mod);
                if (status & bit)
                        return;
 
@@ -122,13 +144,79 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,
        dev_warn(dev, "status check failed\n");
 }
 
+static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       /* enable SSI interrupt if Gen2 */
+       rsnd_mod_write(ssi_mod, SSI_INT_ENABLE,
+                      rsnd_ssi_is_dma_mode(ssi_mod) ?
+                      0x0e000000 : 0x0f000000);
+
+       return 0;
+}
+
+static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       /* disable SSI interrupt if Gen2 */
+       rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000);
+
+       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)
 {
        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 slots = rsnd_get_slot_width(io);
        int j, ret;
        int ssi_clk_mul_table[] = {
                1, 2, 4, 8, 16, 6, 12,
@@ -136,6 +224,24 @@ 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 (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");
+                       return -EINVAL;
+               }
+
+               return 0;
+       }
+
        /*
         * Find best clock, and try to start ADG
         */
@@ -143,15 +249,18 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
 
                /*
                 * this driver is assuming that
-                * system word is 64fs (= 2 x 32bit)
+                * system word is 32bit x slots
                 * see rsnd_ssi_init()
                 */
-               main_rate = rate * 32 * 2 * ssi_clk_mul_table[j];
+               main_rate = rate * 32 * slots * ssi_clk_mul_table[j];
 
                ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
                if (0 == ret) {
                        ssi->cr_clk     = FORCE | SWL_32 |
                                SCKD | SWSD | CKDV(j);
+                       ssi->wsr = CONT;
+
+                       ssi->rate = rate;
 
                        dev_dbg(dev, "%s[%d] outputs %u Hz\n",
                                rsnd_mod_name(mod),
@@ -165,113 +274,91 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
        return -EIO;
 }
 
-static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
+static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
+                                    struct rsnd_dai_stream *io)
 {
+       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);
+
+       if (!rsnd_rdai_is_clk_master(rdai))
+               return;
+
+       if (ssi_parent_mod && !rsnd_ssi_is_parent(mod, io))
+               return;
+
+       if (ssi->usrcnt > 1)
+               return;
+
+       ssi->cr_clk     = 0;
+       ssi->rate       = 0;
 
-       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 int rsnd_ssi_config_init(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);
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       u32 cr_own;
        u32 cr_mode;
-       u32 cr;
+       u32 wsr;
+       int is_tdm;
 
-       if (0 == ssi->usrcnt) {
-               rsnd_mod_power_on(mod);
+       is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0;
 
-               if (rsnd_rdai_is_clk_master(rdai)) {
-                       struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
+       /*
+        * always use 32bit system word.
+        * see also rsnd_ssi_master_clk_enable()
+        */
+       cr_own = FORCE | SWL_32 | PDTA;
 
-                       if (ssi_parent)
-                               rsnd_ssi_hw_start(ssi_parent, io);
-                       else
-                               rsnd_ssi_master_clk_start(ssi, io);
-               }
+       if (rdai->bit_clk_inv)
+               cr_own |= SCKP;
+       if (rdai->frm_clk_inv ^ is_tdm)
+               cr_own |= SWSP;
+       if (rdai->data_alignment)
+               cr_own |= SDTA;
+       if (rdai->sys_delay)
+               cr_own |= DEL;
+       if (rsnd_io_is_play(io))
+               cr_own |= TRMD;
+
+       switch (runtime->sample_bits) {
+       case 16:
+               cr_own |= DWL_16;
+               break;
+       case 32:
+               cr_own |= DWL_24;
+               break;
+       default:
+               return -EINVAL;
        }
 
-       if (rsnd_ssi_is_dma_mode(mod)) {
+       if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) {
                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_mod_write(mod, SSISR, 0);
-
-       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__);
-               return;
+       /*
+        * TDM Extend Mode
+        * see
+        *      rsnd_ssiu_init_gen2()
+        */
+       wsr = ssi->wsr;
+       if (is_tdm) {
+               wsr     |= WS_MODE;
+               cr_own  |= CHNL_8;
        }
 
-       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);
+       ssi->cr_own     = cr_own;
+       ssi->cr_mode    = cr_mode;
+       ssi->wsr        = wsr;
 
-                       if (ssi_parent)
-                               rsnd_ssi_hw_stop(io, ssi_parent);
-                       else
-                               rsnd_ssi_master_clk_stop(ssi);
-               }
-
-               rsnd_mod_power_off(mod);
-
-               ssi->chan = 0;
-       }
-
-       dev_dbg(dev, "%s[%d] hw stopped\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod));
+       return 0;
 }
 
 /*
@@ -282,49 +369,30 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
                         struct rsnd_priv *priv)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
-       u32 cr;
+       int ret;
 
-       cr = FORCE;
+       ssi->usrcnt++;
 
-       /*
-        * always use 32bit system word for easy clock calculation.
-        * see also rsnd_ssi_master_clk_enable()
-        */
-       cr |= SWL_32;
+       rsnd_mod_power_on(mod);
 
-       /*
-        * init clock settings for SSICR
-        */
-       switch (runtime->sample_bits) {
-       case 16:
-               cr |= DWL_16;
-               break;
-       case 32:
-               cr |= DWL_24;
-               break;
-       default:
-               return -EIO;
-       }
+       ret = rsnd_ssi_master_clk_start(ssi, io);
+       if (ret < 0)
+               return ret;
 
-       if (rdai->bit_clk_inv)
-               cr |= SCKP;
-       if (rdai->frm_clk_inv)
-               cr |= SWSP;
-       if (rdai->data_alignment)
-               cr |= SDTA;
-       if (rdai->sys_delay)
-               cr |= DEL;
-       if (rsnd_io_is_play(io))
-               cr |= TRMD;
+       if (rsnd_ssi_is_parent(mod, io))
+               return 0;
+
+       ret = rsnd_ssi_config_init(ssi, io);
+       if (ret < 0)
+               return ret;
 
-       /*
-        * set ssi parameter
-        */
-       ssi->cr_own     = cr;
        ssi->err        = -1; /* ignore 1st error */
 
+       /* clear error status */
+       rsnd_ssi_status_clear(mod);
+
+       rsnd_ssi_irq_enable(mod);
+
        return 0;
 }
 
@@ -335,6 +403,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);
@@ -342,6 +413,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;
 }
 
@@ -351,14 +435,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.
@@ -367,39 +450,83 @@ 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;
 }
 
-static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
+static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi)
 {
        struct rsnd_mod *mod = rsnd_mod_get(ssi);
+       u32 status = rsnd_ssi_status_get(mod);
 
        /* under/over flow error */
-       if (status & (UIRQ | OIRQ)) {
+       if (status & (UIRQ | OIRQ))
                ssi->err++;
 
-               /* clear error status */
-               rsnd_mod_write(mod, SSISR, 0);
-       }
+       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 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);
+
+       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;
 
-       rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io));
+       /*
+        * 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_src_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;
 }
@@ -410,15 +537,16 @@ static int rsnd_ssi_stop(struct rsnd_mod *mod,
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 
-       rsnd_src_ssi_irq_disable(mod);
-
-       rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
-
-       rsnd_ssi_hw_stop(io, ssi);
-
-       rsnd_src_ssiu_stop(mod, io);
+       /*
+        * 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,
@@ -426,6 +554,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
        int is_dma = rsnd_ssi_is_dma_mode(mod);
        u32 status;
        bool elapsed = false;
@@ -436,7 +565,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
        if (!rsnd_io_is_working(io))
                goto rsnd_ssi_interrupt_out;
 
-       status = rsnd_mod_read(mod, SSISR);
+       status = rsnd_ssi_record_error(ssi);
 
        /* PIO only */
        if (!is_dma && (status & DIRQ)) {
@@ -459,23 +588,24 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
 
        /* DMA only */
        if (is_dma && (status & (UIRQ | OIRQ))) {
-               struct device *dev = rsnd_priv_to_dev(priv);
-
                /*
                 * restart SSI
                 */
                dev_dbg(dev, "%s[%d] restart\n",
                        rsnd_mod_name(mod), rsnd_mod_id(mod));
 
-               rsnd_ssi_stop(mod, io, priv);
-               if (ssi->err < 1024)
-                       rsnd_ssi_start(mod, io, priv);
-               else
-                       dev_warn(dev, "no more SSI restart\n");
+               __rsnd_ssi_stop(mod, io, priv);
+               __rsnd_ssi_start(mod, io, priv);
        }
 
-       rsnd_ssi_record_error(ssi, status);
+       if (ssi->err > 1024) {
+               rsnd_ssi_irq_disable(mod);
 
+               dev_warn(dev, "no more %s[%d] restart\n",
+                        rsnd_mod_name(mod), rsnd_mod_id(mod));
+       }
+
+       rsnd_ssi_status_clear(mod);
 rsnd_ssi_interrupt_out:
        spin_unlock(&priv->lock);
 
@@ -495,15 +625,49 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 /*
  *             SSI PIO
  */
-static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
-                             struct rsnd_dai_stream *io,
-                             struct rsnd_priv *priv)
+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)
 {
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        int ret;
 
-       ret = devm_request_irq(dev, ssi->info->irq,
+       /*
+        * 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);
+       if (ret < 0)
+               return ret;
+
+       ret = devm_request_irq(dev, ssi->irq,
                               rsnd_ssi_interrupt,
                               IRQF_SHARED,
                               dev_name(dev), mod);
@@ -513,7 +677,7 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
 
 static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
        .name   = SSI_NAME,
-       .probe  = rsnd_ssi_pio_probe,
+       .probe  = rsnd_ssi_common_probe,
        .init   = rsnd_ssi_init,
        .quit   = rsnd_ssi_quit,
        .start  = rsnd_ssi_start,
@@ -526,20 +690,23 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
                              struct rsnd_priv *priv)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct device *dev = rsnd_priv_to_dev(priv);
-       int dma_id = ssi->info->dma_id;
+       int dma_id = 0; /* not needed */
        int ret;
 
-       ret = devm_request_irq(dev, ssi->info->irq,
-                              rsnd_ssi_interrupt,
-                              IRQF_SHARED,
-                              dev_name(dev), mod);
+       /*
+        * 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;
 
-       ret = rsnd_dma_init(
-               io, rsnd_mod_to_dma(mod),
-               dma_id);
+       ssi->dma = rsnd_dma_attach(io, mod, dma_id);
+       if (IS_ERR(ssi->dma))
+               return PTR_ERR(ssi->dma);
 
        return ret;
 }
@@ -550,9 +717,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
-       int irq = ssi->info->irq;
-
-       rsnd_dma_quit(io, rsnd_mod_to_dma(mod));
+       int irq = ssi->irq;
 
        /* PIO will request IRQ again */
        devm_free_irq(dev, irq, mod);
@@ -581,32 +746,6 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod,
        return 0;
 }
 
-static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
-                             struct rsnd_dai_stream *io,
-                             struct rsnd_priv *priv)
-{
-       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
-
-       rsnd_dma_start(io, dma);
-
-       rsnd_ssi_start(mod, io, priv);
-
-       return 0;
-}
-
-static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
-                            struct rsnd_dai_stream *io,
-                            struct rsnd_priv *priv)
-{
-       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
-
-       rsnd_ssi_stop(mod, io, priv);
-
-       rsnd_dma_stop(io, dma);
-
-       return 0;
-}
-
 static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
                                         struct rsnd_mod *mod)
 {
@@ -630,8 +769,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
        .remove = rsnd_ssi_dma_remove,
        .init   = rsnd_ssi_init,
        .quit   = rsnd_ssi_quit,
-       .start  = rsnd_ssi_dma_start,
-       .stop   = rsnd_ssi_dma_stop,
+       .start  = rsnd_ssi_start,
+       .stop   = rsnd_ssi_stop,
        .fallback = rsnd_ssi_fallback,
        .hw_params = rsnd_ssi_hw_params,
 };
@@ -652,110 +791,76 @@ static struct rsnd_mod_ops rsnd_ssi_non_ops = {
 /*
  *             ssi mod function
  */
-struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
+static void rsnd_ssi_connect(struct rsnd_mod *mod,
+                            struct rsnd_dai_stream *io)
 {
-       if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
-               id = 0;
-
-       return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id);
-}
-
-int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
-{
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(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;
+       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;
 
-       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;
+       /* 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;
+               }
        }
 }
 
-
-static void rsnd_of_parse_ssi(struct platform_device *pdev,
-                             const struct rsnd_of_data *of_data,
-                             struct rsnd_priv *priv)
+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_ssi_platform_info *ssi_info;
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
-       struct device *dev = &pdev->dev;
-       int nr, i;
+       struct rsnd_mod *mod;
+       int i;
 
        node = rsnd_ssi_of_node(priv);
        if (!node)
                return;
 
-       nr = of_get_child_count(node);
-       if (!nr)
-               goto rsnd_of_parse_ssi_end;
-
-       ssi_info = devm_kzalloc(dev,
-                               sizeof(struct rsnd_ssi_platform_info) * nr,
-                               GFP_KERNEL);
-       if (!ssi_info) {
-               dev_err(dev, "ssi info allocation error\n");
-               goto rsnd_of_parse_ssi_end;
-       }
-
-       info->ssi_info          = ssi_info;
-       info->ssi_info_nr       = nr;
-
-       i = -1;
+       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++;
+       }
 
-               ssi_info = info->ssi_info + i;
-
-               /*
-                * pin settings
-                */
-               if (of_get_property(np, "shared-pin", NULL))
-                       ssi_info->flags |= RSND_SSI_CLK_PIN_SHARE;
+       of_node_put(node);
+}
 
-               /*
-                * irq
-                */
-               ssi_info->irq = irq_of_parse_and_map(np, 0);
+struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
+{
+       if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
+               id = 0;
 
-               /*
-                * DMA
-                */
-               ssi_info->dma_id = of_get_property(np, "pio-transfer", NULL) ?
-                       0 : 1;
+       return rsnd_mod_get(rsnd_ssi_get(priv, id));
+}
 
-               if (of_get_property(np, "no-busif", NULL))
-                       ssi_info->flags |= RSND_SSI_NO_BUSIF;
-       }
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 
-rsnd_of_parse_ssi_end:
-       of_node_put(node);
+       return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
 }
 
-int rsnd_ssi_probe(struct platform_device *pdev,
-                  const struct rsnd_of_data *of_data,
-                  struct rsnd_priv *priv)
+int rsnd_ssi_probe(struct rsnd_priv *priv)
 {
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);
-       struct rsnd_ssi_platform_info *pinfo;
+       struct device_node *node;
+       struct device_node *np;
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_mod_ops *ops;
        struct clk *clk;
@@ -763,50 +868,73 @@ int rsnd_ssi_probe(struct platform_device *pdev,
        char name[RSND_SSI_NAME_SIZE];
        int i, nr, ret;
 
-       rsnd_of_parse_ssi(pdev, of_data, priv);
+       node = rsnd_ssi_of_node(priv);
+       if (!node)
+               return -EINVAL;
+
+       nr = of_get_child_count(node);
+       if (!nr) {
+               ret = -EINVAL;
+               goto rsnd_ssi_probe_done;
+       }
 
-       /*
-        *      init SSI
-        */
-       nr      = info->ssi_info_nr;
        ssi     = devm_kzalloc(dev, sizeof(*ssi) * nr, GFP_KERNEL);
-       if (!ssi)
-               return -ENOMEM;
+       if (!ssi) {
+               ret = -ENOMEM;
+               goto rsnd_ssi_probe_done;
+       }
 
        priv->ssi       = ssi;
        priv->ssi_nr    = nr;
 
-       for_each_rsnd_ssi(ssi, priv, i) {
-               pinfo = &info->ssi_info[i];
+       i = 0;
+       for_each_child_of_node(node, np) {
+               ssi = rsnd_ssi_get(priv, i);
 
                snprintf(name, RSND_SSI_NAME_SIZE, "%s.%d",
                         SSI_NAME, i);
 
                clk = devm_clk_get(dev, name);
-               if (IS_ERR(clk))
-                       return PTR_ERR(clk);
+               if (IS_ERR(clk)) {
+                       ret = PTR_ERR(clk);
+                       goto rsnd_ssi_probe_done;
+               }
 
-               ssi->info       = pinfo;
+               if (of_get_property(np, "shared-pin", NULL))
+                       ssi->flags |= RSND_SSI_CLK_PIN_SHARE;
+
+               if (of_get_property(np, "no-busif", NULL))
+                       ssi->flags |= RSND_SSI_NO_BUSIF;
+
+               ssi->irq = irq_of_parse_and_map(np, 0);
+               if (!ssi->irq) {
+                       ret = -EINVAL;
+                       goto rsnd_ssi_probe_done;
+               }
 
                ops = &rsnd_ssi_non_ops;
-               if (pinfo->dma_id > 0)
-                       ops = &rsnd_ssi_dma_ops;
-               else if (rsnd_ssi_pio_available(ssi))
+               if (of_get_property(np, "pio-transfer", NULL))
                        ops = &rsnd_ssi_pio_ops;
+               else
+                       ops = &rsnd_ssi_dma_ops;
 
                ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
                                    RSND_MOD_SSI, i);
                if (ret)
-                       return ret;
+                       goto rsnd_ssi_probe_done;
 
-               rsnd_ssi_parent_setup(priv, ssi);
+               i++;
        }
 
-       return 0;
+       ret = 0;
+
+rsnd_ssi_probe_done:
+       of_node_put(node);
+
+       return ret;
 }
 
-void rsnd_ssi_remove(struct platform_device *pdev,
-                    struct rsnd_priv *priv)
+void rsnd_ssi_remove(struct rsnd_priv *priv)
 {
        struct rsnd_ssi *ssi;
        int i;
diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c
new file mode 100644 (file)
index 0000000..3fe9e08
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Renesas R-Car SSIU support
+ *
+ * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "rsnd.h"
+
+#define SSIU_NAME "ssiu"
+
+struct rsnd_ssiu {
+       struct rsnd_mod mod;
+};
+
+#define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr)
+#define for_each_rsnd_ssiu(pos, priv, i)                               \
+       for (i = 0;                                                     \
+            (i < rsnd_ssiu_nr(priv)) &&                                \
+                    ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i));  \
+            i++)
+
+static int rsnd_ssiu_init(struct rsnd_mod *mod,
+                         struct rsnd_dai_stream *io,
+                         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
+        */
+       rsnd_mod_bset(mod, SSI_MODE0, (1 << id), !use_busif << id);
+
+       /*
+        * 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;
+
+               switch (id) {
+               case 1:
+                       shift = 0;
+                       break;
+               case 2:
+                       shift = 2;
+                       break;
+               case 4:
+                       shift = 16;
+                       break;
+               default:
+                       return -EINVAL;
+               }
+
+               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;
+}
+
+static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = {
+       .name   = SSIU_NAME,
+       .init   = rsnd_ssiu_init,
+};
+
+static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
+                              struct rsnd_dai_stream *io,
+                              struct rsnd_priv *priv)
+{
+       int ret;
+
+       ret = rsnd_ssiu_init(mod, io, priv);
+       if (ret < 0)
+               return ret;
+
+       if (rsnd_get_slot_width(io) >= 6) {
+               /*
+                * TDM Extend Mode
+                * see
+                *      rsnd_ssi_config_init()
+                */
+               rsnd_mod_write(mod, SSI_MODE, 0x1);
+       }
+
+       if (rsnd_ssi_use_busif(io)) {
+               u32 val = rsnd_get_dalign(mod, io);
+
+               rsnd_mod_write(mod, SSI_BUSIF_ADINR,
+                              rsnd_get_adinr_bit(mod, io) |
+                              rsnd_get_adinr_chan(mod, io));
+               rsnd_mod_write(mod, SSI_BUSIF_MODE,  1);
+               rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val);
+       }
+
+       return 0;
+}
+
+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))
+               return 0;
+
+       rsnd_mod_write(mod, SSI_CTRL, 0x1);
+
+       if (rsnd_ssi_multi_slaves(io))
+               rsnd_mod_write(mod, SSI_CONTROL, 0x1);
+
+       return 0;
+}
+
+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))
+               return 0;
+
+       rsnd_mod_write(mod, SSI_CTRL, 0);
+
+       if (rsnd_ssi_multi_slaves(io))
+               rsnd_mod_write(mod, SSI_CONTROL, 0);
+
+       return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = {
+       .name   = SSIU_NAME,
+       .init   = rsnd_ssiu_init_gen2,
+       .start  = rsnd_ssiu_start_gen2,
+       .stop   = rsnd_ssiu_stop_gen2,
+};
+
+static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id)
+{
+       if (WARN_ON(id < 0 || id >= rsnd_ssiu_nr(priv)))
+               id = 0;
+
+       return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id);
+}
+
+int rsnd_ssiu_attach(struct rsnd_dai_stream *io,
+                    struct rsnd_mod *ssi_mod)
+{
+       struct rsnd_priv *priv = rsnd_io_to_priv(io);
+       struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, rsnd_mod_id(ssi_mod));
+
+       rsnd_mod_confirm_ssi(ssi_mod);
+
+       return rsnd_dai_connect(mod, io, mod->type);
+}
+
+int rsnd_ssiu_probe(struct rsnd_priv *priv)
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_ssiu *ssiu;
+       static struct rsnd_mod_ops *ops;
+       int i, nr, ret;
+
+       /* same number to SSI */
+       nr      = priv->ssi_nr;
+       ssiu    = devm_kzalloc(dev, sizeof(*ssiu) * nr, GFP_KERNEL);
+       if (!ssiu)
+               return -ENOMEM;
+
+       priv->ssiu      = ssiu;
+       priv->ssiu_nr   = nr;
+
+       if (rsnd_is_gen1(priv))
+               ops = &rsnd_ssiu_ops_gen1;
+       else
+               ops = &rsnd_ssiu_ops_gen2;
+
+       for_each_rsnd_ssiu(ssiu, priv, i) {
+               ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
+                                   ops, NULL, RSND_MOD_SSIU, i);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+void rsnd_ssiu_remove(struct rsnd_priv *priv)
+{
+       struct rsnd_ssiu *ssiu;
+       int i;
+
+       for_each_rsnd_ssiu(ssiu, priv, i) {
+               rsnd_mod_quit(rsnd_mod_get(ssiu));
+       }
+}