Merge remote-tracking branches 'asoc/topic/omap', 'asoc/topic/qcom', 'asoc/topic...
authorMark Brown <broonie@kernel.org>
Sun, 12 Apr 2015 18:49:11 +0000 (19:49 +0100)
committerMark Brown <broonie@kernel.org>
Sun, 12 Apr 2015 18:49:11 +0000 (19:49 +0100)
29 files changed:
Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/storm.txt [new file with mode: 0644]
MAINTAINERS
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/codecs/rt286.c
sound/soc/omap/Kconfig
sound/soc/omap/n810.c
sound/soc/omap/omap-hdmi-audio.c
sound/soc/qcom/Kconfig [new file with mode: 0644]
sound/soc/qcom/Makefile [new file with mode: 0644]
sound/soc/qcom/lpass-cpu.c [new file with mode: 0644]
sound/soc/qcom/lpass-lpaif-ipq806x.h [new file with mode: 0644]
sound/soc/qcom/lpass-platform.c [new file with mode: 0644]
sound/soc/qcom/lpass.h [new file with mode: 0644]
sound/soc/qcom/storm.c [new file with mode: 0644]
sound/soc/sh/Kconfig
sound/soc/sh/rcar/Makefile
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/dma.c [new file with mode: 0644]
sound/soc/sh/rcar/dvc.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/rsrc-card.c [new file with mode: 0644]
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c

diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt
new file mode 100644 (file)
index 0000000..e00732d
--- /dev/null
@@ -0,0 +1,43 @@
+* Qualcomm Technologies LPASS CPU DAI
+
+This node models the Qualcomm Technologies Low-Power Audio SubSystem (LPASS).
+
+Required properties:
+
+- compatible           : "qcom,lpass-cpu"
+- clocks               : Must contain an entry for each entry in clock-names.
+- clock-names          : A list which must include the following entries:
+                               * "ahbix-clk"
+                               * "mi2s-osr-clk"
+                               * "mi2s-bit-clk"
+- interrupts           : Must contain an entry for each entry in
+                         interrupt-names.
+- interrupt-names      : A list which must include the following entries:
+                               * "lpass-irq-lpaif"
+- pinctrl-N            : One property must exist for each entry in
+                         pinctrl-names.  See ../pinctrl/pinctrl-bindings.txt
+                         for details of the property values.
+- pinctrl-names                : Must contain a "default" entry.
+- reg                  : Must contain an address for each entry in reg-names.
+- reg-names            : A list which must include the following entries:
+                               * "lpass-lpaif"
+
+Optional properties:
+
+- qcom,adsp            : Phandle for the audio DSP node
+
+Example:
+
+lpass@28100000 {
+       compatible = "qcom,lpass-cpu";
+       clocks = <&lcc AHBIX_CLK>, <&lcc MI2S_OSR_CLK>, <&lcc MI2S_BIT_CLK>;
+       clock-names = "ahbix-clk", "mi2s-osr-clk", "mi2s-bit-clk";
+       interrupts = <0 85 1>;
+       interrupt-names = "lpass-irq-lpaif";
+       pinctrl-names = "default", "idle";
+       pinctrl-0 = <&mi2s_default>;
+       pinctrl-1 = <&mi2s_idle>;
+       reg = <0x28100000 0x10000>;
+       reg-names = "lpass-lpaif";
+       qcom,adsp = <&adsp>;
+};
index 2dd690bc19cc6436914a1d6ccdf03ed6f36b6c0b..f316ce1f214a03e396750652df9b911ee6df48ea 100644 (file)
@@ -29,9 +29,17 @@ SSI subnode properties:
 - shared-pin                   : if shared clock pin
 - pio-transfer                 : use PIO transfer mode
 - no-busif                     : BUSIF is not ussed when [mem -> SSI] via DMA case
+- dma                          : Should contain Audio DMAC entry
+- dma-names                    : SSI  case "rx"  (=playback), "tx"  (=capture)
+                                 SSIU case "rxu" (=playback), "txu" (=capture)
 
 SRC subnode properties:
-no properties at this point
+- dma                          : Should contain Audio DMAC entry
+- dma-names                    : "rx" (=playback), "tx" (=capture)
+
+DVC subnode properties:
+- dma                          : Should contain Audio DMAC entry
+- dma-names                    : "tx" (=playback/capture)
 
 DAI subnode properties:
 - playback                     : list of playback modules
@@ -45,56 +53,145 @@ rcar_sound: rcar_sound@ec500000 {
        reg =   <0 0xec500000 0 0x1000>, /* SCU */
                <0 0xec5a0000 0 0x100>,  /* ADG */
                <0 0xec540000 0 0x1000>, /* SSIU */
-               <0 0xec541000 0 0x1280>; /* SSI */
+               <0 0xec541000 0 0x1280>, /* SSI */
+               <0 0xec740000 0 0x200>;  /* Audio DMAC peri peri*/
+       reg-names = "scu", "adg", "ssiu", "ssi", "audmapp";
+
+       clocks = <&mstp10_clks R8A7790_CLK_SSI_ALL>,
+               <&mstp10_clks R8A7790_CLK_SSI9>, <&mstp10_clks R8A7790_CLK_SSI8>,
+               <&mstp10_clks R8A7790_CLK_SSI7>, <&mstp10_clks R8A7790_CLK_SSI6>,
+               <&mstp10_clks R8A7790_CLK_SSI5>, <&mstp10_clks R8A7790_CLK_SSI4>,
+               <&mstp10_clks R8A7790_CLK_SSI3>, <&mstp10_clks R8A7790_CLK_SSI2>,
+               <&mstp10_clks R8A7790_CLK_SSI1>, <&mstp10_clks R8A7790_CLK_SSI0>,
+               <&mstp10_clks R8A7790_CLK_SCU_SRC9>, <&mstp10_clks R8A7790_CLK_SCU_SRC8>,
+               <&mstp10_clks R8A7790_CLK_SCU_SRC7>, <&mstp10_clks R8A7790_CLK_SCU_SRC6>,
+               <&mstp10_clks R8A7790_CLK_SCU_SRC5>, <&mstp10_clks R8A7790_CLK_SCU_SRC4>,
+               <&mstp10_clks R8A7790_CLK_SCU_SRC3>, <&mstp10_clks R8A7790_CLK_SCU_SRC2>,
+               <&mstp10_clks R8A7790_CLK_SCU_SRC1>, <&mstp10_clks R8A7790_CLK_SCU_SRC0>,
+               <&mstp10_clks R8A7790_CLK_SCU_DVC0>, <&mstp10_clks R8A7790_CLK_SCU_DVC1>,
+               <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, <&m2_clk>;
+       clock-names = "ssi-all",
+                       "ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5",
+                       "ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0",
+                       "src.9", "src.8", "src.7", "src.6", "src.5",
+                       "src.4", "src.3", "src.2", "src.1", "src.0",
+                       "dvc.0", "dvc.1",
+                       "clk_a", "clk_b", "clk_c", "clk_i";
 
        rcar_sound,dvc {
-               dvc0: dvc@0 { };
-               dvc1: dvc@1 { };
+               dvc0: dvc@0 {
+                       dmas = <&audma0 0xbc>;
+                       dma-names = "tx";
+               };
+               dvc1: dvc@1 {
+                       dmas = <&audma0 0xbe>;
+                       dma-names = "tx";
+               };
        };
 
        rcar_sound,src {
-               src0: src@0 { };
-               src1: src@1 { };
-               src2: src@2 { };
-               src3: src@3 { };
-               src4: src@4 { };
-               src5: src@5 { };
-               src6: src@6 { };
-               src7: src@7 { };
-               src8: src@8 { };
-               src9: src@9 { };
+               src0: src@0 {
+                       interrupts = <0 352 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x85>, <&audma1 0x9a>;
+                       dma-names = "rx", "tx";
+               };
+               src1: src@1 {
+                       interrupts = <0 353 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x87>, <&audma1 0x9c>;
+                       dma-names = "rx", "tx";
+               };
+               src2: src@2 {
+                       interrupts = <0 354 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x89>, <&audma1 0x9e>;
+                       dma-names = "rx", "tx";
+               };
+               src3: src@3 {
+                       interrupts = <0 355 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x8b>, <&audma1 0xa0>;
+                       dma-names = "rx", "tx";
+               };
+               src4: src@4 {
+                       interrupts = <0 356 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x8d>, <&audma1 0xb0>;
+                       dma-names = "rx", "tx";
+               };
+               src5: src@5 {
+                       interrupts = <0 357 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x8f>, <&audma1 0xb2>;
+                       dma-names = "rx", "tx";
+               };
+               src6: src@6 {
+                       interrupts = <0 358 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x91>, <&audma1 0xb4>;
+                       dma-names = "rx", "tx";
+               };
+               src7: src@7 {
+                       interrupts = <0 359 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x93>, <&audma1 0xb6>;
+                       dma-names = "rx", "tx";
+               };
+               src8: src@8 {
+                       interrupts = <0 360 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x95>, <&audma1 0xb8>;
+                       dma-names = "rx", "tx";
+               };
+               src9: src@9 {
+                       interrupts = <0 361 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x97>, <&audma1 0xba>;
+                       dma-names = "rx", "tx";
+               };
        };
 
        rcar_sound,ssi {
                ssi0: ssi@0 {
                        interrupts = <0 370 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x01>, <&audma1 0x02>, <&audma0 0x15>, <&audma1 0x16>;
+                       dma-names = "rx", "tx", "rxu", "txu";
                };
                ssi1: ssi@1 {
                        interrupts = <0 371 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x03>, <&audma1 0x04>, <&audma0 0x49>, <&audma1 0x4a>;
+                       dma-names = "rx", "tx", "rxu", "txu";
                };
                ssi2: ssi@2 {
                        interrupts = <0 372 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x05>, <&audma1 0x06>, <&audma0 0x63>, <&audma1 0x64>;
+                       dma-names = "rx", "tx", "rxu", "txu";
                };
                ssi3: ssi@3 {
                        interrupts = <0 373 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x07>, <&audma1 0x08>, <&audma0 0x6f>, <&audma1 0x70>;
+                       dma-names = "rx", "tx", "rxu", "txu";
                };
                ssi4: ssi@4 {
                        interrupts = <0 374 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x09>, <&audma1 0x0a>, <&audma0 0x71>, <&audma1 0x72>;
+                       dma-names = "rx", "tx", "rxu", "txu";
                };
                ssi5: ssi@5 {
                        interrupts = <0 375 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x0b>, <&audma1 0x0c>, <&audma0 0x73>, <&audma1 0x74>;
+                       dma-names = "rx", "tx", "rxu", "txu";
                };
                ssi6: ssi@6 {
                        interrupts = <0 376 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x0d>, <&audma1 0x0e>, <&audma0 0x75>, <&audma1 0x76>;
+                       dma-names = "rx", "tx", "rxu", "txu";
                };
                ssi7: ssi@7 {
                        interrupts = <0 377 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x0f>, <&audma1 0x10>, <&audma0 0x79>, <&audma1 0x7a>;
+                       dma-names = "rx", "tx", "rxu", "txu";
                };
                ssi8: ssi@8 {
                        interrupts = <0 378 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x11>, <&audma1 0x12>, <&audma0 0x7b>, <&audma1 0x7c>;
+                       dma-names = "rx", "tx", "rxu", "txu";
                };
                ssi9: ssi@9 {
                        interrupts = <0 379 IRQ_TYPE_LEVEL_HIGH>;
+                       dmas = <&audma0 0x13>, <&audma1 0x14>, <&audma0 0x7d>, <&audma1 0x7e>;
+                       dma-names = "rx", "tx", "rxu", "txu";
                };
        };
 
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt
new file mode 100644 (file)
index 0000000..c641550
--- /dev/null
@@ -0,0 +1,67 @@
+Renesas Sampling Rate Convert Sound Card:
+
+Renesas Sampling Rate Convert Sound Card specifies audio DAI connections of SoC <-> codec.
+
+Required properties:
+
+- compatible                           : "renesas,rsrc-card,<board>"
+                                         Examples with soctypes are:
+                                           - "renesas,rsrc-card,lager"
+                                           - "renesas,rsrc-card,koelsch"
+Optional properties:
+
+- card_name                            : User specified audio sound card name, one string
+                                         property.
+- cpu                                  : CPU   sub-node
+- codec                                        : CODEC sub-node
+
+Optional subnode properties:
+
+- format                               : CPU/CODEC common audio format.
+                                         "i2s", "right_j", "left_j" , "dsp_a"
+                                         "dsp_b", "ac97", "pdm", "msb", "lsb"
+- frame-master                         : Indicates dai-link frame master.
+                                         phandle to a cpu or codec subnode.
+- bitclock-master                      : Indicates dai-link bit clock master.
+                                         phandle to a cpu or codec subnode.
+- bitclock-inversion                   : bool property. Add this if the
+                                         dai-link uses bit clock inversion.
+- frame-inversion                      : bool property. Add this if the
+                                         dai-link uses frame clock inversion.
+- convert-rate                         : platform specified sampling rate convert
+
+Required CPU/CODEC subnodes properties:
+
+- sound-dai                            : phandle and port of CPU/CODEC
+
+Optional CPU/CODEC subnodes properties:
+
+- clocks / system-clock-frequency      : specify subnode's clock if needed.
+                                         it can be specified via "clocks" if system has
+                                         clock node (= common clock), or "system-clock-frequency"
+                                         (if system doens't support common clock)
+                                         If a clock is specified, it is
+                                         enabled with clk_prepare_enable()
+                                         in dai startup() and disabled with
+                                         clk_disable_unprepare() in dai
+                                         shutdown().
+
+Example
+
+sound {
+       compatible = "renesas,rsrc-card,lager";
+
+       card-name = "rsnd-ak4643";
+       format = "left_j";
+       bitclock-master = <&sndcodec>;
+       frame-master = <&sndcodec>;
+
+       sndcpu: cpu {
+               sound-dai = <&rcar_sound>;
+       };
+
+       sndcodec: codec {
+               sound-dai = <&ak4643>;
+               system-clock-frequency = <11289600>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/sound/storm.txt b/Documentation/devicetree/bindings/sound/storm.txt
new file mode 100644 (file)
index 0000000..062a4c1
--- /dev/null
@@ -0,0 +1,23 @@
+* Sound complex for Storm boards
+
+Models a soundcard for Storm boards with the Qualcomm Technologies IPQ806x SOC
+connected to a MAX98357A DAC via I2S.
+
+Required properties:
+
+- compatible   : "google,storm-audio"
+- cpu          : Phandle of the CPU DAI
+- codec                : Phandle of the codec DAI
+
+Optional properties:
+
+- qcom,model   : The user-visible name of this sound card.
+
+Example:
+
+sound {
+       compatible = "google,storm-audio";
+       qcom,model = "ipq806x-storm";
+       cpu = <&lpass_cpu>;
+       codec = <&max98357a>;
+};
index 3a3c461e0787db9935f3332054c0b8b8a6077084..080be6d19f786d1327fa2aba886c438815bd8638 100644 (file)
@@ -5271,6 +5271,13 @@ F:       drivers/char/ipmi/
 F:     include/linux/ipmi*
 F:     include/uapi/linux/ipmi*
 
+QCOM AUDIO (ASoC) DRIVERS
+M:     Patrick Lai <plai@codeaurora.org>
+M:     Banajit Goswami <bgoswami@codeaurora.org>
+L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
+S:     Supported
+F:     sound/soc/qcom/
+
 IPS SCSI RAID DRIVER
 M:     Adaptec OEM Raid Solutions <aacraid@adaptec.com>
 L:     linux-scsi@vger.kernel.org
index dcc79aa0236b548bfe5408fe56689241fc597e97..3ba52da18bc69a9bb41c84627cfc7d08f47e3bf0 100644 (file)
@@ -47,6 +47,7 @@ source "sound/soc/kirkwood/Kconfig"
 source "sound/soc/intel/Kconfig"
 source "sound/soc/mxs/Kconfig"
 source "sound/soc/pxa/Kconfig"
+source "sound/soc/qcom/Kconfig"
 source "sound/soc/rockchip/Kconfig"
 source "sound/soc/samsung/Kconfig"
 source "sound/soc/sh/Kconfig"
index 5b3c8f67c8db7a29ff7199a6103d445428978125..974ba708b4826a03077a58251434a311542d5e3c 100644 (file)
@@ -28,6 +28,7 @@ obj-$(CONFIG_SND_SOC) += nuc900/
 obj-$(CONFIG_SND_SOC)  += omap/
 obj-$(CONFIG_SND_SOC)  += kirkwood/
 obj-$(CONFIG_SND_SOC)  += pxa/
+obj-$(CONFIG_SND_SOC)  += qcom/
 obj-$(CONFIG_SND_SOC)  += rockchip/
 obj-$(CONFIG_SND_SOC)  += samsung/
 obj-$(CONFIG_SND_SOC)  += sh/
index 826037090c8385cf9a2ddaf255052051390f377d..0fcda35a3a93694fa5352d06144e934ee99b7583 100644 (file)
@@ -397,7 +397,7 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 
        if (jack) {
                /* enable IRQ */
-               if (rt286->jack->status | SND_JACK_HEADPHONE)
+               if (rt286->jack->status & SND_JACK_HEADPHONE)
                        snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1");
                regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2);
                /* Send an initial empty report */
@@ -1048,7 +1048,6 @@ static int rt286_probe(struct snd_soc_codec *codec)
        struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
 
        rt286->codec = codec;
-       codec->dapm.bias_level = SND_SOC_BIAS_OFF;
 
        if (rt286->i2c->irq) {
                regmap_update_bits(rt286->regmap,
@@ -1220,7 +1219,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
 {
        struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev);
        struct rt286_priv *rt286;
-       int i, ret;
+       int i, ret, val;
 
        rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286),
                                GFP_KERNEL);
@@ -1235,11 +1234,15 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
                return ret;
        }
 
-       regmap_read(rt286->regmap,
-               RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret);
-       if (ret != RT286_VENDOR_ID && ret != RT288_VENDOR_ID) {
+       ret = regmap_read(rt286->regmap,
+               RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val);
+       if (ret != 0) {
+               dev_err(&i2c->dev, "I2C error %d\n", ret);
+               return ret;
+       }
+       if (val != RT286_VENDOR_ID && val != RT288_VENDOR_ID) {
                dev_err(&i2c->dev,
-                       "Device with ID register %x is not rt286\n", ret);
+                       "Device with ID register %x is not rt286\n", val);
                return -ENODEV;
        }
 
@@ -1247,6 +1250,14 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
        rt286->i2c = i2c;
        i2c_set_clientdata(i2c, rt286);
 
+       /* restore codec default */
+       for (i = 0; i < INDEX_CACHE_SIZE; i++)
+               regmap_write(rt286->regmap, rt286->index_cache[i].reg,
+                               rt286->index_cache[i].def);
+       for (i = 0; i < ARRAY_SIZE(rt286_reg); i++)
+               regmap_write(rt286->regmap, rt286_reg[i].reg,
+                               rt286_reg[i].def);
+
        if (pdata)
                rt286->pdata = *pdata;
 
index e7c78b0406b59c62fef534849571e49ed7b643d0..6768e4f7d7d0e264eb3ff37881155caf8c1d0229 100644 (file)
@@ -105,7 +105,7 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040
        select SND_OMAP_SOC_MCPDM
        select SND_SOC_TWL6040
        select SND_SOC_DMIC
-       select COMMON_CLK_PALMAS if SOC_OMAP5
+       select COMMON_CLK_PALMAS if MFD_PALMAS
        help
          Say Y if you want to add support for SoC audio on OMAP boards using
          ABE and twl6040 codec. This driver currently supports:
index 5d7f9cebe04142fd9faf0974030ff4e41ccd2664..dcb5336b569815b8958e2ecb572710930ca425cb 100644 (file)
@@ -98,12 +98,11 @@ static int n810_startup(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_codec *codec = rtd->codec;
 
        snd_pcm_hw_constraint_minmax(runtime,
                                     SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
 
-       n810_ext_control(&codec->dapm);
+       n810_ext_control(&rtd->card->dapm);
        return clk_prepare_enable(sys_clkout2);
 }
 
@@ -255,24 +254,6 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = {
                     n810_get_input, n810_set_input),
 };
 
-static int n810_aic33_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       /* Not connected */
-       snd_soc_dapm_nc_pin(dapm, "MONO_LOUT");
-       snd_soc_dapm_nc_pin(dapm, "HPLCOM");
-       snd_soc_dapm_nc_pin(dapm, "HPRCOM");
-       snd_soc_dapm_nc_pin(dapm, "MIC3L");
-       snd_soc_dapm_nc_pin(dapm, "MIC3R");
-       snd_soc_dapm_nc_pin(dapm, "LINE1R");
-       snd_soc_dapm_nc_pin(dapm, "LINE2L");
-       snd_soc_dapm_nc_pin(dapm, "LINE2R");
-
-       return 0;
-}
-
 /* Digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link n810_dai = {
        .name = "TLV320AIC33",
@@ -283,7 +264,6 @@ static struct snd_soc_dai_link n810_dai = {
        .codec_dai_name = "tlv320aic3x-hifi",
        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
                   SND_SOC_DAIFMT_CBM_CFM,
-       .init = n810_aic33_init,
        .ops = &n810_ops,
 };
 
@@ -300,6 +280,7 @@ static struct snd_soc_card snd_soc_n810 = {
        .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets),
        .dapm_routes = audio_map,
        .num_dapm_routes = ARRAY_SIZE(audio_map),
+       .fully_routed = true,
 };
 
 static struct platform_device *n810_snd_device;
index f7eb42aa3f3893ca7cfd76fd2f8a96ff986d68b2..4775da4c4db56529bbc0a6bd6be0b7b2a24370f0 100644 (file)
@@ -142,8 +142,6 @@ static int hdmi_dai_hw_params(struct snd_pcm_substream *substream,
 
        iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE;
 
-       iec->status[0] |= IEC958_AES1_PRO_MODE_NOTID;
-
        iec->status[1] = IEC958_AES1_CON_GENERAL;
 
        iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC;
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
new file mode 100644 (file)
index 0000000..5f58e4f
--- /dev/null
@@ -0,0 +1,25 @@
+config SND_SOC_QCOM
+       tristate "ASoC support for QCOM platforms"
+       help
+          Say Y or M if you want to add support to use audio devices
+          in Qualcomm Technologies SOC-based platforms.
+
+config SND_SOC_LPASS_CPU
+       tristate
+       depends on SND_SOC_QCOM
+       select REGMAP_MMIO
+
+config SND_SOC_LPASS_PLATFORM
+       tristate
+       depends on SND_SOC_QCOM
+       select REGMAP_MMIO
+
+config SND_SOC_STORM
+       tristate "ASoC I2S support for Storm boards"
+       depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST
+       select SND_SOC_LPASS_CPU
+       select SND_SOC_LPASS_PLATFORM
+       select SND_SOC_MAX98357A
+       help
+          Say Y or M if you want add support for SoC audio on the
+          Qualcomm Technologies IPQ806X-based Storm board.
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
new file mode 100644 (file)
index 0000000..c5ce96c
--- /dev/null
@@ -0,0 +1,11 @@
+# Platform
+snd-soc-lpass-cpu-objs := lpass-cpu.o
+snd-soc-lpass-platform-objs := lpass-platform.o
+
+obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
+obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
+
+# Machine
+snd-soc-storm-objs := storm.o
+
+obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
new file mode 100644 (file)
index 0000000..6698d05
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS
+ */
+
+#include <linux/clk.h>
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include "lpass-lpaif-ipq806x.h"
+#include "lpass.h"
+
+static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+               unsigned int freq, int dir)
+{
+       struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+       int ret;
+
+       ret = clk_set_rate(drvdata->mi2s_osr_clk, freq);
+       if (ret)
+               dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n",
+                               __func__, freq, ret);
+
+       return ret;
+}
+
+static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+       int ret;
+
+       ret = clk_prepare_enable(drvdata->mi2s_osr_clk);
+       if (ret) {
+               dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(drvdata->mi2s_bit_clk);
+       if (ret) {
+               dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n",
+                               __func__, ret);
+               clk_disable_unprepare(drvdata->mi2s_osr_clk);
+               return ret;
+       }
+
+       return 0;
+}
+
+static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+
+       clk_disable_unprepare(drvdata->mi2s_bit_clk);
+       clk_disable_unprepare(drvdata->mi2s_osr_clk);
+}
+
+static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+       snd_pcm_format_t format = params_format(params);
+       unsigned int channels = params_channels(params);
+       unsigned int rate = params_rate(params);
+       unsigned int regval;
+       int bitwidth, ret;
+
+       bitwidth = snd_pcm_format_width(format);
+       if (bitwidth < 0) {
+               dev_err(dai->dev, "%s() invalid bit width given: %d\n",
+                               __func__, bitwidth);
+               return bitwidth;
+       }
+
+       regval = LPAIF_I2SCTL_LOOPBACK_DISABLE |
+                       LPAIF_I2SCTL_WSSRC_INTERNAL;
+
+       switch (bitwidth) {
+       case 16:
+               regval |= LPAIF_I2SCTL_BITWIDTH_16;
+               break;
+       case 24:
+               regval |= LPAIF_I2SCTL_BITWIDTH_24;
+               break;
+       case 32:
+               regval |= LPAIF_I2SCTL_BITWIDTH_32;
+               break;
+       default:
+               dev_err(dai->dev, "%s() invalid bitwidth given: %d\n",
+                               __func__, bitwidth);
+               return -EINVAL;
+       }
+
+       switch (channels) {
+       case 1:
+               regval |= LPAIF_I2SCTL_SPKMODE_SD0;
+               regval |= LPAIF_I2SCTL_SPKMONO_MONO;
+               break;
+       case 2:
+               regval |= LPAIF_I2SCTL_SPKMODE_SD0;
+               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+               break;
+       case 4:
+               regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
+               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+               break;
+       case 6:
+               regval |= LPAIF_I2SCTL_SPKMODE_6CH;
+               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+               break;
+       case 8:
+               regval |= LPAIF_I2SCTL_SPKMODE_8CH;
+               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+               break;
+       default:
+               dev_err(dai->dev, "%s() invalid channels given: %u\n",
+                               __func__, channels);
+               return -EINVAL;
+       }
+
+       ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval);
+       if (ret) {
+               dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2);
+       if (ret) {
+               dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n",
+                               __func__, rate * bitwidth * 2, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+       int ret;
+
+       ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+       if (ret)
+               dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+                               __func__, ret);
+
+       return ret;
+}
+
+static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+       int ret;
+
+       ret = regmap_update_bits(drvdata->lpaif_map,
+                       LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+                       LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
+       if (ret)
+               dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+                               __func__, ret);
+
+       return ret;
+}
+
+static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
+               int cmd, struct snd_soc_dai *dai)
+{
+       struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+       int ret;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ret = regmap_update_bits(drvdata->lpaif_map,
+                               LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+                               LPAIF_I2SCTL_SPKEN_MASK,
+                               LPAIF_I2SCTL_SPKEN_ENABLE);
+               if (ret)
+                       dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+                                       __func__, ret);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               ret = regmap_update_bits(drvdata->lpaif_map,
+                               LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S),
+                               LPAIF_I2SCTL_SPKEN_MASK,
+                               LPAIF_I2SCTL_SPKEN_DISABLE);
+               if (ret)
+                       dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+                                       __func__, ret);
+               break;
+       }
+
+       return ret;
+}
+
+static struct snd_soc_dai_ops lpass_cpu_dai_ops = {
+       .set_sysclk     = lpass_cpu_daiops_set_sysclk,
+       .startup        = lpass_cpu_daiops_startup,
+       .shutdown       = lpass_cpu_daiops_shutdown,
+       .hw_params      = lpass_cpu_daiops_hw_params,
+       .hw_free        = lpass_cpu_daiops_hw_free,
+       .prepare        = lpass_cpu_daiops_prepare,
+       .trigger        = lpass_cpu_daiops_trigger,
+};
+
+static int lpass_cpu_dai_probe(struct snd_soc_dai *dai)
+{
+       struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
+       int ret;
+
+       /* ensure audio hardware is disabled */
+       ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0);
+       if (ret)
+               dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
+                               __func__, ret);
+
+       return ret;
+}
+
+static struct snd_soc_dai_driver lpass_cpu_dai_driver = {
+       .playback = {
+               .stream_name    = "lpass-cpu-playback",
+               .formats        = SNDRV_PCM_FMTBIT_S16 |
+                                       SNDRV_PCM_FMTBIT_S24 |
+                                       SNDRV_PCM_FMTBIT_S32,
+               .rates          = SNDRV_PCM_RATE_8000 |
+                                       SNDRV_PCM_RATE_16000 |
+                                       SNDRV_PCM_RATE_32000 |
+                                       SNDRV_PCM_RATE_48000 |
+                                       SNDRV_PCM_RATE_96000,
+               .rate_min       = 8000,
+               .rate_max       = 96000,
+               .channels_min   = 1,
+               .channels_max   = 8,
+       },
+       .probe  = &lpass_cpu_dai_probe,
+       .ops    = &lpass_cpu_dai_ops,
+};
+
+static const struct snd_soc_component_driver lpass_cpu_comp_driver = {
+       .name = "lpass-cpu",
+};
+
+static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
+{
+       int i;
+
+       for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
+               if (reg == LPAIF_I2SCTL_REG(i))
+                       return true;
+
+       for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
+               if (reg == LPAIF_IRQEN_REG(i))
+                       return true;
+               if (reg == LPAIF_IRQCLEAR_REG(i))
+                       return true;
+       }
+
+       for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
+               if (reg == LPAIF_RDMACTL_REG(i))
+                       return true;
+               if (reg == LPAIF_RDMABASE_REG(i))
+                       return true;
+               if (reg == LPAIF_RDMABUFF_REG(i))
+                       return true;
+               if (reg == LPAIF_RDMAPER_REG(i))
+                       return true;
+       }
+
+       return false;
+}
+
+static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
+{
+       int i;
+
+       for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i)
+               if (reg == LPAIF_I2SCTL_REG(i))
+                       return true;
+
+       for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) {
+               if (reg == LPAIF_IRQEN_REG(i))
+                       return true;
+               if (reg == LPAIF_IRQSTAT_REG(i))
+                       return true;
+       }
+
+       for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) {
+               if (reg == LPAIF_RDMACTL_REG(i))
+                       return true;
+               if (reg == LPAIF_RDMABASE_REG(i))
+                       return true;
+               if (reg == LPAIF_RDMABUFF_REG(i))
+                       return true;
+               if (reg == LPAIF_RDMACURR_REG(i))
+                       return true;
+               if (reg == LPAIF_RDMAPER_REG(i))
+                       return true;
+       }
+
+       return false;
+}
+
+static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
+{
+       int i;
+
+       for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i)
+               if (reg == LPAIF_IRQSTAT_REG(i))
+                       return true;
+
+       for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i)
+               if (reg == LPAIF_RDMACURR_REG(i))
+                       return true;
+
+       return false;
+}
+
+static const struct regmap_config lpass_cpu_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX),
+       .writeable_reg = lpass_cpu_regmap_writeable,
+       .readable_reg = lpass_cpu_regmap_readable,
+       .volatile_reg = lpass_cpu_regmap_volatile,
+       .cache_type = REGCACHE_FLAT,
+};
+
+static int lpass_cpu_platform_probe(struct platform_device *pdev)
+{
+       struct lpass_data *drvdata;
+       struct device_node *dsp_of_node;
+       struct resource *res;
+       int ret;
+
+       dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
+       if (dsp_of_node) {
+               dev_err(&pdev->dev, "%s() DSP exists and holds audio resources\n",
+                               __func__);
+               return -EBUSY;
+       }
+
+       drvdata = devm_kzalloc(&pdev->dev, sizeof(struct lpass_data),
+                       GFP_KERNEL);
+       if (!drvdata)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, drvdata);
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif");
+       if (!res) {
+               dev_err(&pdev->dev, "%s() error getting resource\n", __func__);
+               return -ENODEV;
+       }
+
+       drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR((void const __force *)drvdata->lpaif)) {
+               dev_err(&pdev->dev, "%s() error mapping reg resource: %ld\n",
+                               __func__,
+                               PTR_ERR((void const __force *)drvdata->lpaif));
+               return PTR_ERR((void const __force *)drvdata->lpaif);
+       }
+
+       drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
+                       &lpass_cpu_regmap_config);
+       if (IS_ERR(drvdata->lpaif_map)) {
+               dev_err(&pdev->dev, "%s() error initializing regmap: %ld\n",
+                               __func__, PTR_ERR(drvdata->lpaif_map));
+               return PTR_ERR(drvdata->lpaif_map);
+       }
+
+       drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk");
+       if (IS_ERR(drvdata->mi2s_osr_clk)) {
+               dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n",
+                               __func__, PTR_ERR(drvdata->mi2s_osr_clk));
+               return PTR_ERR(drvdata->mi2s_osr_clk);
+       }
+
+       drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk");
+       if (IS_ERR(drvdata->mi2s_bit_clk)) {
+               dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n",
+                               __func__, PTR_ERR(drvdata->mi2s_bit_clk));
+               return PTR_ERR(drvdata->mi2s_bit_clk);
+       }
+
+       drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk");
+       if (IS_ERR(drvdata->ahbix_clk)) {
+               dev_err(&pdev->dev, "%s() error getting ahbix-clk: %ld\n",
+                               __func__, PTR_ERR(drvdata->ahbix_clk));
+               return PTR_ERR(drvdata->ahbix_clk);
+       }
+
+       ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY);
+       if (ret) {
+               dev_err(&pdev->dev, "%s() error setting rate on ahbix_clk: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+       dev_dbg(&pdev->dev, "%s() set ahbix_clk rate to %lu\n", __func__,
+                       clk_get_rate(drvdata->ahbix_clk));
+
+       ret = clk_prepare_enable(drvdata->ahbix_clk);
+       if (ret) {
+               dev_err(&pdev->dev, "%s() error enabling ahbix_clk: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       ret = devm_snd_soc_register_component(&pdev->dev,
+                       &lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n",
+                               __func__, ret);
+               goto err_clk;
+       }
+
+       ret = asoc_qcom_lpass_platform_register(pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "%s() error registering platform driver: %d\n",
+                               __func__, ret);
+               goto err_clk;
+       }
+
+       return 0;
+
+err_clk:
+       clk_disable_unprepare(drvdata->ahbix_clk);
+       return ret;
+}
+
+static int lpass_cpu_platform_remove(struct platform_device *pdev)
+{
+       struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+       clk_disable_unprepare(drvdata->ahbix_clk);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lpass_cpu_device_id[] = {
+       { .compatible = "qcom,lpass-cpu" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, lpass_cpu_device_id);
+#endif
+
+static struct platform_driver lpass_cpu_platform_driver = {
+       .driver = {
+               .name           = "lpass-cpu",
+               .of_match_table = of_match_ptr(lpass_cpu_device_id),
+       },
+       .probe  = lpass_cpu_platform_probe,
+       .remove = lpass_cpu_platform_remove,
+};
+module_platform_driver(lpass_cpu_platform_driver);
+
+MODULE_DESCRIPTION("QTi LPASS CPU Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/lpass-lpaif-ipq806x.h b/sound/soc/qcom/lpass-lpaif-ipq806x.h
new file mode 100644 (file)
index 0000000..dc423b8
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS
+ */
+
+#ifndef __LPASS_LPAIF_H__
+#define __LPASS_LPAIF_H__
+
+#define LPAIF_BANK_OFFSET              0x1000
+
+/* LPAIF I2S */
+
+#define LPAIF_I2SCTL_REG_BASE          0x0010
+#define LPAIF_I2SCTL_REG_STRIDE                0x4
+#define LPAIF_I2SCTL_REG_ADDR(addr, port) \
+       (LPAIF_I2SCTL_REG_BASE + (addr) + (LPAIF_I2SCTL_REG_STRIDE * (port)))
+
+enum lpaif_i2s_ports {
+       LPAIF_I2S_PORT_MIN              = 0,
+
+       LPAIF_I2S_PORT_CODEC_SPK        = 0,
+       LPAIF_I2S_PORT_CODEC_MIC        = 1,
+       LPAIF_I2S_PORT_SEC_SPK          = 2,
+       LPAIF_I2S_PORT_SEC_MIC          = 3,
+       LPAIF_I2S_PORT_MI2S             = 4,
+
+       LPAIF_I2S_PORT_MAX              = 4,
+       LPAIF_I2S_PORT_NUM              = 5,
+};
+
+#define LPAIF_I2SCTL_REG(port)         LPAIF_I2SCTL_REG_ADDR(0x0, (port))
+
+#define LPAIF_I2SCTL_LOOPBACK_MASK     0x8000
+#define LPAIF_I2SCTL_LOOPBACK_SHIFT    15
+#define LPAIF_I2SCTL_LOOPBACK_DISABLE  (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
+#define LPAIF_I2SCTL_LOOPBACK_ENABLE   (1 << LPAIF_I2SCTL_LOOPBACK_SHIFT)
+
+#define LPAIF_I2SCTL_SPKEN_MASK                0x4000
+#define LPAIF_I2SCTL_SPKEN_SHIFT       14
+#define LPAIF_I2SCTL_SPKEN_DISABLE     (0 << LPAIF_I2SCTL_SPKEN_SHIFT)
+#define LPAIF_I2SCTL_SPKEN_ENABLE      (1 << LPAIF_I2SCTL_SPKEN_SHIFT)
+
+#define LPAIF_I2SCTL_SPKMODE_MASK      0x3C00
+#define LPAIF_I2SCTL_SPKMODE_SHIFT     10
+#define LPAIF_I2SCTL_SPKMODE_NONE      (0 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_SD0       (1 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_SD1       (2 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_SD2       (3 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_SD3       (4 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_QUAD01    (5 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_QUAD23    (6 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_6CH       (7 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+#define LPAIF_I2SCTL_SPKMODE_8CH       (8 << LPAIF_I2SCTL_SPKMODE_SHIFT)
+
+#define LPAIF_I2SCTL_SPKMONO_MASK      0x0200
+#define LPAIF_I2SCTL_SPKMONO_SHIFT     9
+#define LPAIF_I2SCTL_SPKMONO_STEREO    (0 << LPAIF_I2SCTL_SPKMONO_SHIFT)
+#define LPAIF_I2SCTL_SPKMONO_MONO      (1 << LPAIF_I2SCTL_SPKMONO_SHIFT)
+
+#define LPAIF_I2SCTL_WSSRC_MASK                0x0004
+#define LPAIF_I2SCTL_WSSRC_SHIFT       2
+#define LPAIF_I2SCTL_WSSRC_INTERNAL    (0 << LPAIF_I2SCTL_WSSRC_SHIFT)
+#define LPAIF_I2SCTL_WSSRC_EXTERNAL    (1 << LPAIF_I2SCTL_WSSRC_SHIFT)
+
+#define LPAIF_I2SCTL_BITWIDTH_MASK     0x0003
+#define LPAIF_I2SCTL_BITWIDTH_SHIFT    0
+#define LPAIF_I2SCTL_BITWIDTH_16       (0 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
+#define LPAIF_I2SCTL_BITWIDTH_24       (1 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
+#define LPAIF_I2SCTL_BITWIDTH_32       (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT)
+
+/* LPAIF IRQ */
+
+#define LPAIF_IRQ_REG_BASE             0x3000
+#define LPAIF_IRQ_REG_STRIDE           0x1000
+#define LPAIF_IRQ_REG_ADDR(addr, port) \
+       (LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port)))
+
+enum lpaif_irq_ports {
+       LPAIF_IRQ_PORT_MIN              = 0,
+
+       LPAIF_IRQ_PORT_HOST             = 0,
+       LPAIF_IRQ_PORT_ADSP             = 1,
+
+       LPAIF_IRQ_PORT_MAX              = 2,
+       LPAIF_IRQ_PORT_NUM              = 3,
+};
+
+#define LPAIF_IRQEN_REG(port)          LPAIF_IRQ_REG_ADDR(0x0, (port))
+#define LPAIF_IRQSTAT_REG(port)                LPAIF_IRQ_REG_ADDR(0x4, (port))
+#define LPAIF_IRQCLEAR_REG(port)       LPAIF_IRQ_REG_ADDR(0xC, (port))
+
+#define LPAIF_IRQ_BITSTRIDE            3
+#define LPAIF_IRQ_PER(chan)            (1 << (LPAIF_IRQ_BITSTRIDE * (chan)))
+#define LPAIF_IRQ_XRUN(chan)           (2 << (LPAIF_IRQ_BITSTRIDE * (chan)))
+#define LPAIF_IRQ_ERR(chan)            (4 << (LPAIF_IRQ_BITSTRIDE * (chan)))
+#define LPAIF_IRQ_ALL(chan)            (7 << (LPAIF_IRQ_BITSTRIDE * (chan)))
+
+/* LPAIF DMA */
+
+#define LPAIF_RDMA_REG_BASE            0x6000
+#define LPAIF_RDMA_REG_STRIDE          0x1000
+#define LPAIF_RDMA_REG_ADDR(addr, chan) \
+       (LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan)))
+
+enum lpaif_dma_channels {
+       LPAIF_RDMA_CHAN_MIN             = 0,
+
+       LPAIF_RDMA_CHAN_MI2S            = 0,
+       LPAIF_RDMA_CHAN_PCM0            = 1,
+       LPAIF_RDMA_CHAN_PCM1            = 2,
+
+       LPAIF_RDMA_CHAN_MAX             = 4,
+       LPAIF_RDMA_CHAN_NUM             = 5,
+};
+
+#define LPAIF_RDMACTL_REG(chan)                LPAIF_RDMA_REG_ADDR(0x00, (chan))
+#define LPAIF_RDMABASE_REG(chan)       LPAIF_RDMA_REG_ADDR(0x04, (chan))
+#define        LPAIF_RDMABUFF_REG(chan)        LPAIF_RDMA_REG_ADDR(0x08, (chan))
+#define LPAIF_RDMACURR_REG(chan)       LPAIF_RDMA_REG_ADDR(0x0C, (chan))
+#define        LPAIF_RDMAPER_REG(chan)         LPAIF_RDMA_REG_ADDR(0x10, (chan))
+
+#define LPAIF_RDMACTL_BURSTEN_MASK     0x800
+#define LPAIF_RDMACTL_BURSTEN_SHIFT    11
+#define LPAIF_RDMACTL_BURSTEN_SINGLE   (0 << LPAIF_RDMACTL_BURSTEN_SHIFT)
+#define LPAIF_RDMACTL_BURSTEN_INCR4    (1 << LPAIF_RDMACTL_BURSTEN_SHIFT)
+
+#define LPAIF_RDMACTL_WPSCNT_MASK      0x700
+#define LPAIF_RDMACTL_WPSCNT_SHIFT     8
+#define LPAIF_RDMACTL_WPSCNT_ONE       (0 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+#define LPAIF_RDMACTL_WPSCNT_TWO       (1 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+#define LPAIF_RDMACTL_WPSCNT_THREE     (2 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+#define LPAIF_RDMACTL_WPSCNT_FOUR      (3 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+#define LPAIF_RDMACTL_WPSCNT_SIX       (5 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+#define LPAIF_RDMACTL_WPSCNT_EIGHT     (7 << LPAIF_RDMACTL_WPSCNT_SHIFT)
+
+#define LPAIF_RDMACTL_AUDINTF_MASK     0x0F0
+#define LPAIF_RDMACTL_AUDINTF_SHIFT    4
+#define LPAIF_RDMACTL_AUDINTF_NONE     (0 << LPAIF_RDMACTL_AUDINTF_SHIFT)
+#define LPAIF_RDMACTL_AUDINTF_CODEC    (1 << LPAIF_RDMACTL_AUDINTF_SHIFT)
+#define LPAIF_RDMACTL_AUDINTF_PCM      (2 << LPAIF_RDMACTL_AUDINTF_SHIFT)
+#define LPAIF_RDMACTL_AUDINTF_SEC_I2S  (3 << LPAIF_RDMACTL_AUDINTF_SHIFT)
+#define LPAIF_RDMACTL_AUDINTF_MI2S     (4 << LPAIF_RDMACTL_AUDINTF_SHIFT)
+#define LPAIF_RDMACTL_AUDINTF_HDMI     (5 << LPAIF_RDMACTL_AUDINTF_SHIFT)
+#define LPAIF_RDMACTL_AUDINTF_SEC_PCM  (7 << LPAIF_RDMACTL_AUDINTF_SHIFT)
+
+#define LPAIF_RDMACTL_FIFOWM_MASK      0x00E
+#define LPAIF_RDMACTL_FIFOWM_SHIFT     1
+#define LPAIF_RDMACTL_FIFOWM_1         (0 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_2         (1 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_3         (2 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_4         (3 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_5         (4 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_6         (5 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_7         (6 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+#define LPAIF_RDMACTL_FIFOWM_8         (7 << LPAIF_RDMACTL_FIFOWM_SHIFT)
+
+#define LPAIF_RDMACTL_ENABLE_MASK      0x1
+#define LPAIF_RDMACTL_ENABLE_SHIFT     0
+#define LPAIF_RDMACTL_ENABLE_OFF       (0 << LPAIF_RDMACTL_ENABLE_SHIFT)
+#define LPAIF_RDMACTL_ENABLE_ON                (1 << LPAIF_RDMACTL_ENABLE_SHIFT)
+
+#endif /* __LPASS_LPAIF_H__ */
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
new file mode 100644 (file)
index 0000000..2fa6280
--- /dev/null
@@ -0,0 +1,526 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
+ */
+
+#include <linux/compiler.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <sound/memalloc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "lpass-lpaif-ipq806x.h"
+#include "lpass.h"
+
+#define LPASS_PLATFORM_BUFFER_SIZE     (16 * 1024)
+#define LPASS_PLATFORM_PERIODS         2
+
+static struct snd_pcm_hardware lpass_platform_pcm_hardware = {
+       .info                   =       SNDRV_PCM_INFO_MMAP |
+                                       SNDRV_PCM_INFO_MMAP_VALID |
+                                       SNDRV_PCM_INFO_INTERLEAVED |
+                                       SNDRV_PCM_INFO_PAUSE |
+                                       SNDRV_PCM_INFO_RESUME,
+       .formats                =       SNDRV_PCM_FMTBIT_S16 |
+                                       SNDRV_PCM_FMTBIT_S24 |
+                                       SNDRV_PCM_FMTBIT_S32,
+       .rates                  =       SNDRV_PCM_RATE_8000_192000,
+       .rate_min               =       8000,
+       .rate_max               =       192000,
+       .channels_min           =       1,
+       .channels_max           =       8,
+       .buffer_bytes_max       =       LPASS_PLATFORM_BUFFER_SIZE,
+       .period_bytes_max       =       LPASS_PLATFORM_BUFFER_SIZE /
+                                               LPASS_PLATFORM_PERIODS,
+       .period_bytes_min       =       LPASS_PLATFORM_BUFFER_SIZE /
+                                               LPASS_PLATFORM_PERIODS,
+       .periods_min            =       LPASS_PLATFORM_PERIODS,
+       .periods_max            =       LPASS_PLATFORM_PERIODS,
+       .fifo_size              =       0,
+};
+
+static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       int ret;
+
+       snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
+
+       runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
+
+       ret = snd_pcm_hw_constraint_integer(runtime,
+                       SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0) {
+               dev_err(soc_runtime->dev, "%s() setting constraints failed: %d\n",
+                               __func__, ret);
+               return -EINVAL;
+       }
+
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+       return 0;
+}
+
+static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct lpass_data *drvdata =
+               snd_soc_platform_get_drvdata(soc_runtime->platform);
+       snd_pcm_format_t format = params_format(params);
+       unsigned int channels = params_channels(params);
+       unsigned int regval;
+       int bitwidth;
+       int ret;
+
+       bitwidth = snd_pcm_format_width(format);
+       if (bitwidth < 0) {
+               dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n",
+                               __func__, bitwidth);
+               return bitwidth;
+       }
+
+       regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
+                       LPAIF_RDMACTL_AUDINTF_MI2S |
+                       LPAIF_RDMACTL_FIFOWM_8;
+
+       switch (bitwidth) {
+       case 16:
+               switch (channels) {
+               case 1:
+               case 2:
+                       regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+                       break;
+               case 4:
+                       regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+                       break;
+               case 6:
+                       regval |= LPAIF_RDMACTL_WPSCNT_THREE;
+                       break;
+               case 8:
+                       regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+                       break;
+               default:
+                       dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
+                                       __func__, bitwidth, channels);
+                       return -EINVAL;
+               }
+               break;
+       case 24:
+       case 32:
+               switch (channels) {
+               case 1:
+                       regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+                       break;
+               case 2:
+                       regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+                       break;
+               case 4:
+                       regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+                       break;
+               case 6:
+                       regval |= LPAIF_RDMACTL_WPSCNT_SIX;
+                       break;
+               case 8:
+                       regval |= LPAIF_RDMACTL_WPSCNT_EIGHT;
+                       break;
+               default:
+                       dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
+                                       __func__, bitwidth, channels);
+                       return -EINVAL;
+               }
+               break;
+       default:
+               dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
+                               __func__, bitwidth, channels);
+               return -EINVAL;
+       }
+
+       ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval);
+       if (ret) {
+               dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct lpass_data *drvdata =
+               snd_soc_platform_get_drvdata(soc_runtime->platform);
+       int ret;
+
+       ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
+       if (ret)
+               dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+                               __func__, ret);
+
+       return ret;
+}
+
+static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct lpass_data *drvdata =
+               snd_soc_platform_get_drvdata(soc_runtime->platform);
+       int ret;
+
+       ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S),
+                       runtime->dma_addr);
+       if (ret) {
+               dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S),
+                       (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
+       if (ret) {
+               dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S),
+                       (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
+       if (ret) {
+               dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       ret = regmap_update_bits(drvdata->lpaif_map,
+                       LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+                       LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
+       if (ret) {
+               dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
+               int cmd)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct lpass_data *drvdata =
+               snd_soc_platform_get_drvdata(soc_runtime->platform);
+       int ret;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               /* clear status before enabling interrupts */
+               ret = regmap_write(drvdata->lpaif_map,
+                               LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
+                               LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
+               if (ret) {
+                       dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
+                                       __func__, ret);
+                       return ret;
+               }
+
+               ret = regmap_update_bits(drvdata->lpaif_map,
+                               LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
+                               LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S),
+                               LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S));
+               if (ret) {
+                       dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
+                                       __func__, ret);
+                       return ret;
+               }
+
+               ret = regmap_update_bits(drvdata->lpaif_map,
+                               LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+                               LPAIF_RDMACTL_ENABLE_MASK,
+                               LPAIF_RDMACTL_ENABLE_ON);
+               if (ret) {
+                       dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+                                       __func__, ret);
+                       return ret;
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               ret = regmap_update_bits(drvdata->lpaif_map,
+                               LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S),
+                               LPAIF_RDMACTL_ENABLE_MASK,
+                               LPAIF_RDMACTL_ENABLE_OFF);
+               if (ret) {
+                       dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+                                       __func__, ret);
+                       return ret;
+               }
+
+               ret = regmap_update_bits(drvdata->lpaif_map,
+                               LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST),
+                               LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0);
+               if (ret) {
+                       dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
+                                       __func__, ret);
+                       return ret;
+               }
+               break;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
+               struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct lpass_data *drvdata =
+                       snd_soc_platform_get_drvdata(soc_runtime->platform);
+       unsigned int base_addr, curr_addr;
+       int ret;
+
+       ret = regmap_read(drvdata->lpaif_map,
+                       LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr);
+       if (ret) {
+               dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       ret = regmap_read(drvdata->lpaif_map,
+                       LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr);
+       if (ret) {
+               dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       return bytes_to_frames(substream->runtime, curr_addr - base_addr);
+}
+
+static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
+               struct vm_area_struct *vma)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       return dma_mmap_coherent(substream->pcm->card->dev, vma,
+                       runtime->dma_area, runtime->dma_addr,
+                       runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops lpass_platform_pcm_ops = {
+       .open           = lpass_platform_pcmops_open,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = lpass_platform_pcmops_hw_params,
+       .hw_free        = lpass_platform_pcmops_hw_free,
+       .prepare        = lpass_platform_pcmops_prepare,
+       .trigger        = lpass_platform_pcmops_trigger,
+       .pointer        = lpass_platform_pcmops_pointer,
+       .mmap           = lpass_platform_pcmops_mmap,
+};
+
+static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
+{
+       struct snd_pcm_substream *substream = data;
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct lpass_data *drvdata =
+               snd_soc_platform_get_drvdata(soc_runtime->platform);
+       unsigned int interrupts;
+       irqreturn_t ret = IRQ_NONE;
+       int rv;
+
+       rv = regmap_read(drvdata->lpaif_map,
+                       LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts);
+       if (rv) {
+               dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n",
+                               __func__, rv);
+               return IRQ_NONE;
+       }
+       interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S);
+
+       if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) {
+               rv = regmap_write(drvdata->lpaif_map,
+                               LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
+                               LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S));
+               if (rv) {
+                       dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
+                                       __func__, rv);
+                       return IRQ_NONE;
+               }
+               snd_pcm_period_elapsed(substream);
+               ret = IRQ_HANDLED;
+       }
+
+       if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) {
+               rv = regmap_write(drvdata->lpaif_map,
+                               LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
+                               LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S));
+               if (rv) {
+                       dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
+                                       __func__, rv);
+                       return IRQ_NONE;
+               }
+               dev_warn(soc_runtime->dev, "%s() xrun warning\n", __func__);
+               snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+               ret = IRQ_HANDLED;
+       }
+
+       if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) {
+               rv = regmap_write(drvdata->lpaif_map,
+                               LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST),
+                               LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S));
+               if (rv) {
+                       dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n",
+                                       __func__, rv);
+                       return IRQ_NONE;
+               }
+               dev_err(soc_runtime->dev, "%s() bus access error\n", __func__);
+               snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
+               ret = IRQ_HANDLED;
+       }
+
+       return ret;
+}
+
+static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
+               struct snd_soc_pcm_runtime *soc_runtime)
+{
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
+
+       buf->dev.type = SNDRV_DMA_TYPE_DEV;
+       buf->dev.dev = soc_runtime->dev;
+       buf->private_data = NULL;
+       buf->area = dma_alloc_coherent(soc_runtime->dev, size, &buf->addr,
+                       GFP_KERNEL);
+       if (!buf->area) {
+               dev_err(soc_runtime->dev, "%s: Could not allocate DMA buffer\n",
+                               __func__);
+               return -ENOMEM;
+       }
+       buf->bytes = size;
+
+       return 0;
+}
+
+static void lpass_platform_free_buffer(struct snd_pcm_substream *substream,
+               struct snd_soc_pcm_runtime *soc_runtime)
+{
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+       if (buf->area) {
+               dma_free_coherent(soc_runtime->dev, buf->bytes, buf->area,
+                               buf->addr);
+       }
+       buf->area = NULL;
+}
+
+static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
+{
+       struct snd_pcm *pcm = soc_runtime->pcm;
+       struct snd_pcm_substream *substream =
+               pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       struct lpass_data *drvdata =
+               snd_soc_platform_get_drvdata(soc_runtime->platform);
+       int ret;
+
+       soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+       soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask;
+
+       ret = lpass_platform_alloc_buffer(substream, soc_runtime);
+       if (ret)
+               return ret;
+
+       ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq,
+                       lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
+                       "lpass-irq-lpaif", substream);
+       if (ret) {
+               dev_err(soc_runtime->dev, "%s() irq request failed: %d\n",
+                               __func__, ret);
+               goto err_buf;
+       }
+
+       /* ensure audio hardware is disabled */
+       ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0);
+       if (ret) {
+               dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+       ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0);
+       if (ret) {
+               dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       return 0;
+
+err_buf:
+       lpass_platform_free_buffer(substream, soc_runtime);
+       return ret;
+}
+
+static void lpass_platform_pcm_free(struct snd_pcm *pcm)
+{
+       struct snd_pcm_substream *substream =
+               pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+
+       lpass_platform_free_buffer(substream, soc_runtime);
+}
+
+static struct snd_soc_platform_driver lpass_platform_driver = {
+       .pcm_new        = lpass_platform_pcm_new,
+       .pcm_free       = lpass_platform_pcm_free,
+       .ops            = &lpass_platform_pcm_ops,
+};
+
+int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
+{
+       struct lpass_data *drvdata = platform_get_drvdata(pdev);
+
+       drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
+       if (drvdata->lpaif_irq < 0) {
+               dev_err(&pdev->dev, "%s() error getting irq handle: %d\n",
+                               __func__, drvdata->lpaif_irq);
+               return -ENODEV;
+       }
+
+       return devm_snd_soc_register_platform(&pdev->dev,
+                       &lpass_platform_driver);
+}
+EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
+
+MODULE_DESCRIPTION("QTi LPASS Platform Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
new file mode 100644 (file)
index 0000000..5c99b3d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * lpass.h - Definitions for the QTi LPASS
+ */
+
+#ifndef __LPASS_H__
+#define __LPASS_H__
+
+#include <linux/clk.h>
+#include <linux/compiler.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define LPASS_AHBIX_CLOCK_FREQUENCY            131072000
+
+/* Both the CPU DAI and platform drivers will access this data */
+struct lpass_data {
+
+       /* AHB-I/X bus clocks inside the low-power audio subsystem (LPASS) */
+       struct clk *ahbix_clk;
+
+       /* MI2S system clock */
+       struct clk *mi2s_osr_clk;
+
+       /* MI2S bit clock (derived from system clock by a divider */
+       struct clk *mi2s_bit_clk;
+
+       /* low-power audio interface (LPAIF) registers */
+       void __iomem *lpaif;
+
+       /* regmap backed by the low-power audio interface (LPAIF) registers */
+       struct regmap *lpaif_map;
+
+       /* interrupts from the low-power audio interface (LPAIF) */
+       int lpaif_irq;
+};
+
+/* register the platform driver from the CPU DAI driver */
+int asoc_qcom_lpass_platform_register(struct platform_device *);
+
+#endif /* __LPASS_H__ */
diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c
new file mode 100644 (file)
index 0000000..b8bd296
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * storm.c -- ALSA SoC machine driver for QTi ipq806x-based Storm board
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define STORM_SYSCLK_MULT                      4
+
+static int storm_ops_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+       struct snd_soc_card *card = soc_runtime->card;
+       snd_pcm_format_t format = params_format(params);
+       unsigned int rate = params_rate(params);
+       unsigned int sysclk_freq;
+       int bitwidth, ret;
+
+       bitwidth = snd_pcm_format_width(format);
+       if (bitwidth < 0) {
+               dev_err(card->dev, "%s() invalid bit width given: %d\n",
+                               __func__, bitwidth);
+               return bitwidth;
+       }
+
+       /*
+        * as the CPU DAI is the I2S bus master and no system clock is needed by
+        * the MAX98357a DAC, simply set the system clock to be a constant
+        * multiple of the bit clock for the clock divider
+        */
+       sysclk_freq = rate * bitwidth * 2 * STORM_SYSCLK_MULT;
+
+       ret = snd_soc_dai_set_sysclk(soc_runtime->cpu_dai, 0, sysclk_freq, 0);
+       if (ret) {
+               dev_err(card->dev, "%s() error setting sysclk to %u: %d\n",
+                               __func__, sysclk_freq, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops storm_soc_ops = {
+       .hw_params      = storm_ops_hw_params,
+};
+
+static struct snd_soc_dai_link storm_dai_link = {
+       .name           = "Primary",
+       .stream_name    = "Primary",
+       .codec_dai_name = "HiFi",
+       .ops            = &storm_soc_ops,
+};
+
+static struct snd_soc_card storm_soc_card = {
+       .name   = "ipq806x-storm",
+       .dev    = NULL,
+};
+
+static int storm_parse_of(struct snd_soc_card *card)
+{
+       struct snd_soc_dai_link *dai_link = card->dai_link;
+       struct device_node *np = card->dev->of_node;
+
+       dai_link->cpu_of_node = of_parse_phandle(np, "cpu", 0);
+       if (!dai_link->cpu_of_node) {
+               dev_err(card->dev, "%s() error getting cpu phandle\n",
+                               __func__);
+               return -EINVAL;
+       }
+       dai_link->platform_of_node = dai_link->cpu_of_node;
+
+       dai_link->codec_of_node = of_parse_phandle(np, "codec", 0);
+       if (!dai_link->codec_of_node) {
+               dev_err(card->dev, "%s() error getting codec phandle\n",
+                               __func__);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int storm_platform_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &storm_soc_card;
+       int ret;
+
+       if (card->dev) {
+               dev_err(&pdev->dev, "%s() error, existing soundcard\n",
+                               __func__);
+               return -ENODEV;
+       }
+       card->dev = &pdev->dev;
+       platform_set_drvdata(pdev, card);
+
+       ret = snd_soc_of_parse_card_name(card, "qcom,model");
+       if (ret) {
+               dev_err(&pdev->dev, "%s() error parsing card name: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       card->dai_link  = &storm_dai_link;
+       card->num_links = 1;
+
+       ret = storm_parse_of(card);
+       if (ret) {
+               dev_err(&pdev->dev, "%s() error resolving dai links: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
+       if (ret == -EPROBE_DEFER) {
+               card->dev = NULL;
+               return ret;
+       } else if (ret) {
+               dev_err(&pdev->dev, "%s() error registering soundcard: %d\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id storm_device_id[]  = {
+       { .compatible = "google,storm-audio" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, storm_device_id);
+#endif
+
+static struct platform_driver storm_platform_driver = {
+       .driver = {
+               .name = "storm-audio",
+               .of_match_table =
+                       of_match_ptr(storm_device_id),
+       },
+       .probe = storm_platform_probe,
+};
+module_platform_driver(storm_platform_driver);
+
+MODULE_DESCRIPTION("QTi IPQ806x-based Storm Machine Driver");
+MODULE_LICENSE("GPL v2");
index 80245b6eebd65389ac3ce722704af47a5499157a..07114b0b0dc12bcf23e0a25fb342ad3df23b2464 100644 (file)
@@ -36,11 +36,17 @@ config SND_SOC_SH4_SIU
 
 config SND_SOC_RCAR
        tristate "R-Car series SRU/SCU/SSIU/SSI support"
+       depends on DMA_OF
        select SND_SIMPLE_CARD
        select REGMAP_MMIO
        help
          This option enables R-Car SUR/SCU/SSIU/SSI sound support
 
+config SND_SOC_RSRC_CARD
+       tristate "Renesas Sampling Rate Convert Sound Card"
+       help
+         This option enables simple sound if you need sampling rate convert
+
 ##
 ## Boards
 ##
index 9ac536429800dc270a743b15d9598d841b8bc176..f1b445173fba7857e4dcd440ce75c5d8f21d33f3 100644 (file)
@@ -1,2 +1,5 @@
-snd-soc-rcar-objs      := core.o gen.o src.o adg.o ssi.o dvc.o
-obj-$(CONFIG_SND_SOC_RCAR)     += snd-soc-rcar.o
\ No newline at end of file
+snd-soc-rcar-objs      := core.o gen.o dma.o src.o adg.o ssi.o dvc.o
+obj-$(CONFIG_SND_SOC_RCAR)     += snd-soc-rcar.o
+
+snd-soc-rsrc-card-objs := rsrc-card.o
+obj-$(CONFIG_SND_SOC_RSRC_CARD)        += snd-soc-rsrc-card.o
index 7ac35c9d1cb8b008841a206d1eef1ed5618e4b17..fefc881dbac24acf8e190703973712d8ccc8f22c 100644 (file)
@@ -183,6 +183,8 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
 
        rsnd_mod_bset(mod, DIV_EN, en, en);
 
+       dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
+
        return 0;
 }
 
@@ -432,7 +434,5 @@ int rsnd_adg_probe(struct platform_device *pdev,
 
        priv->adg = adg;
 
-       dev_dbg(dev, "adg probed\n");
-
        return 0;
 }
index 31202e95be1ee6dc5fde83e44ce955347bc8109c..9f48d75fa992351adffef3d834620e7d2682354e 100644 (file)
  *
  */
 #include <linux/pm_runtime.h>
-#include <linux/shdma-base.h>
 #include "rsnd.h"
 
 #define RSND_RATES SNDRV_PCM_RATE_8000_96000
 #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 
-static struct rsnd_of_data rsnd_of_data_gen1 = {
+static const struct rsnd_of_data rsnd_of_data_gen1 = {
        .flags = RSND_GEN1,
 };
 
-static struct rsnd_of_data rsnd_of_data_gen2 = {
+static const struct rsnd_of_data rsnd_of_data_gen2 = {
        .flags = RSND_GEN2,
 };
 
-static struct of_device_id rsnd_of_match[] = {
+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 },
        {},
@@ -138,15 +137,12 @@ char *rsnd_mod_name(struct rsnd_mod *mod)
        return mod->ops->name;
 }
 
-char *rsnd_mod_dma_name(struct rsnd_mod *mod)
+struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod)
 {
-       if (!mod || !mod->ops)
-               return "unknown";
-
-       if (!mod->ops->dma_name)
-               return mod->ops->name;
+       if (!mod || !mod->ops || !mod->ops->dma_req)
+               return NULL;
 
-       return mod->ops->dma_name(mod);
+       return mod->ops->dma_req(mod);
 }
 
 int rsnd_mod_init(struct rsnd_mod *mod,
@@ -174,228 +170,6 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
                clk_unprepare(mod->clk);
 }
 
-/*
- *     rsnd_dma functions
- */
-void rsnd_dma_stop(struct rsnd_dma *dma)
-{
-       dmaengine_terminate_all(dma->chan);
-}
-
-static void rsnd_dma_complete(void *data)
-{
-       struct rsnd_dma *dma = (struct rsnd_dma *)data;
-       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-
-       /*
-        * Renesas sound Gen1 needs 1 DMAC,
-        * Gen2 needs 2 DMAC.
-        * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri.
-        * But, Audio-DMAC-peri-peri doesn't have interrupt,
-        * and this driver is assuming that here.
-        *
-        * If Audio-DMAC-peri-peri has interrpt,
-        * rsnd_dai_pointer_update() will be called twice,
-        * ant it will breaks io->byte_pos
-        */
-
-       rsnd_dai_pointer_update(io, io->byte_per_period);
-}
-
-void rsnd_dma_start(struct rsnd_dma *dma)
-{
-       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-       struct snd_pcm_substream *substream = io->substream;
-       struct device *dev = rsnd_priv_to_dev(priv);
-       struct dma_async_tx_descriptor *desc;
-
-       desc = dmaengine_prep_dma_cyclic(dma->chan,
-                                        (dma->addr) ? dma->addr :
-                                        substream->runtime->dma_addr,
-                                        snd_pcm_lib_buffer_bytes(substream),
-                                        snd_pcm_lib_period_bytes(substream),
-                                        dma->dir,
-                                        DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
-
-       if (!desc) {
-               dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
-               return;
-       }
-
-       desc->callback          = rsnd_dma_complete;
-       desc->callback_param    = dma;
-
-       if (dmaengine_submit(desc) < 0) {
-               dev_err(dev, "dmaengine_submit() fail\n");
-               return;
-       }
-
-       dma_async_issue_pending(dma->chan);
-}
-
-int rsnd_dma_available(struct rsnd_dma *dma)
-{
-       return !!dma->chan;
-}
-
-#define DMA_NAME_SIZE 16
-#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */
-static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod)
-{
-       if (mod)
-               return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d",
-                        rsnd_mod_dma_name(mod), rsnd_mod_id(mod));
-       else
-               return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem");
-
-}
-
-static void rsnd_dma_of_name(struct rsnd_mod *mod_from,
-                            struct rsnd_mod *mod_to,
-                            char *dma_name)
-{
-       int index = 0;
-
-       index = _rsnd_dma_of_name(dma_name + index, mod_from);
-       *(dma_name + index++) = '_';
-       index = _rsnd_dma_of_name(dma_name + index, mod_to);
-}
-
-static void rsnd_dma_of_path(struct rsnd_dma *dma,
-                            int is_play,
-                            struct rsnd_mod **mod_from,
-                            struct rsnd_mod **mod_to)
-{
-       struct rsnd_mod *this = rsnd_dma_to_mod(dma);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(this);
-       struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
-       struct rsnd_mod *src = rsnd_io_to_mod_src(io);
-       struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
-       struct rsnd_mod *mod[MOD_MAX];
-       int i, index;
-
-
-       for (i = 0; i < MOD_MAX; i++)
-               mod[i] = NULL;
-
-       /*
-        * in play case...
-        *
-        * src -> dst
-        *
-        * mem -> SSI
-        * mem -> SRC -> SSI
-        * mem -> SRC -> DVC -> SSI
-        */
-       mod[0] = NULL; /* for "mem" */
-       index = 1;
-       for (i = 1; i < MOD_MAX; i++) {
-               if (!src) {
-                       mod[i] = ssi;
-               } else if (!dvc) {
-                       mod[i] = src;
-                       src = NULL;
-               } else {
-                       if ((!is_play) && (this == src))
-                               this = dvc;
-
-                       mod[i] = (is_play) ? src : dvc;
-                       i++;
-                       mod[i] = (is_play) ? dvc : src;
-                       src = NULL;
-                       dvc = NULL;
-               }
-
-               if (mod[i] == this)
-                       index = i;
-
-               if (mod[i] == ssi)
-                       break;
-       }
-
-       if (is_play) {
-               *mod_from = mod[index - 1];
-               *mod_to   = mod[index];
-       } else {
-               *mod_from = mod[index];
-               *mod_to   = mod[index - 1];
-       }
-}
-
-int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
-                 int is_play, int id)
-{
-       struct device *dev = rsnd_priv_to_dev(priv);
-       struct dma_slave_config cfg;
-       struct rsnd_mod *mod_from;
-       struct rsnd_mod *mod_to;
-       char dma_name[DMA_NAME_SIZE];
-       dma_cap_mask_t mask;
-       int ret;
-
-       if (dma->chan) {
-               dev_err(dev, "it already has dma channel\n");
-               return -EIO;
-       }
-
-       dma_cap_zero(mask);
-       dma_cap_set(DMA_SLAVE, mask);
-
-       rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to);
-       rsnd_dma_of_name(mod_from, mod_to, dma_name);
-
-       cfg.slave_id    = id;
-       cfg.direction   = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
-       cfg.src_addr    = rsnd_gen_dma_addr(priv, mod_from, is_play, 1);
-       cfg.dst_addr    = rsnd_gen_dma_addr(priv, mod_to,   is_play, 0);
-       cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-       cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
-
-       dev_dbg(dev, "dma : %s %pad -> %pad\n",
-               dma_name, &cfg.src_addr, &cfg.dst_addr);
-
-       dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
-                                                    (void *)id, dev,
-                                                    dma_name);
-       if (!dma->chan) {
-               dev_err(dev, "can't get dma channel\n");
-               goto rsnd_dma_channel_err;
-       }
-
-       ret = dmaengine_slave_config(dma->chan, &cfg);
-       if (ret < 0)
-               goto rsnd_dma_init_err;
-
-       dma->addr = is_play ? cfg.src_addr : cfg.dst_addr;
-       dma->dir = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
-
-       return 0;
-
-rsnd_dma_init_err:
-       rsnd_dma_quit(priv, dma);
-rsnd_dma_channel_err:
-
-       /*
-        * DMA failed. try to PIO mode
-        * see
-        *      rsnd_ssi_fallback()
-        *      rsnd_rdai_continuance_probe()
-        */
-       return -EAGAIN;
-}
-
-void  rsnd_dma_quit(struct rsnd_priv *priv,
-                   struct rsnd_dma *dma)
-{
-       if (dma->chan)
-               dma_release_channel(dma->chan);
-
-       dma->chan = NULL;
-}
-
 /*
  *     settting function
  */
@@ -429,7 +203,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
 ({                                                             \
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);         \
        struct device *dev = rsnd_priv_to_dev(priv);            \
-       u32 mask = 1 << __rsnd_mod_shift_##func;                        \
+       u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31); \
        u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func;   \
        int ret = 0;                                                    \
        if ((mod->status & mask) == call) {                             \
@@ -471,7 +245,7 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
                struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
                struct device *dev = rsnd_priv_to_dev(priv);
 
-               dev_err(dev, "%s%d is not empty\n",
+               dev_err(dev, "%s[%d] is not empty\n",
                        rsnd_mod_name(mod),
                        rsnd_mod_id(mod));
                return -EIO;
@@ -887,20 +661,28 @@ static int rsnd_dai_probe(struct platform_device *pdev,
                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;
@@ -946,6 +728,15 @@ static int rsnd_pcm_open(struct snd_pcm_substream *substream)
 static int rsnd_hw_params(struct snd_pcm_substream *substream,
                         struct snd_pcm_hw_params *hw_params)
 {
+       struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
+       struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+       struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+       int ret;
+
+       ret = rsnd_dai_call(hw_params, io, substream, hw_params);
+       if (ret)
+               return ret;
+
        return snd_pcm_lib_malloc_pages(substream,
                                        params_buffer_bytes(hw_params));
 }
@@ -1210,6 +1001,7 @@ static int rsnd_probe(struct platform_device *pdev)
                            const struct rsnd_of_data *of_data,
                            struct rsnd_priv *priv) = {
                rsnd_gen_probe,
+               rsnd_dma_probe,
                rsnd_ssi_probe,
                rsnd_src_probe,
                rsnd_dvc_probe,
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c
new file mode 100644 (file)
index 0000000..ac3756f
--- /dev/null
@@ -0,0 +1,616 @@
+/*
+ * Renesas R-Car Audio DMAC support
+ *
+ * Copyright (C) 2015 Renesas Electronics Corp.
+ * 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 <linux/delay.h>
+#include <linux/of_dma.h>
+#include "rsnd.h"
+
+/*
+ * Audio DMAC peri peri register
+ */
+#define PDMASAR                0x00
+#define PDMADAR                0x04
+#define PDMACHCR       0x0c
+
+/* PDMACHCR */
+#define PDMACHCR_DE            (1 << 0)
+
+struct rsnd_dma_ctrl {
+       void __iomem *base;
+       int dmapp_num;
+};
+
+#define rsnd_priv_to_dmac(p)   ((struct rsnd_dma_ctrl *)(p)->dma)
+
+/*
+ *             Audio DMAC
+ */
+static void rsnd_dmaen_complete(void *data)
+{
+       struct rsnd_dma *dma = (struct rsnd_dma *)data;
+       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+
+       /*
+        * Renesas sound Gen1 needs 1 DMAC,
+        * Gen2 needs 2 DMAC.
+        * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri.
+        * But, Audio-DMAC-peri-peri doesn't have interrupt,
+        * and this driver is assuming that here.
+        *
+        * If Audio-DMAC-peri-peri has interrpt,
+        * rsnd_dai_pointer_update() will be called twice,
+        * ant it will breaks io->byte_pos
+        */
+
+       rsnd_dai_pointer_update(io, io->byte_per_period);
+}
+
+static void rsnd_dmaen_stop(struct rsnd_dma *dma)
+{
+       struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+
+       dmaengine_terminate_all(dmaen->chan);
+}
+
+static void rsnd_dmaen_start(struct rsnd_dma *dma)
+{
+       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 rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct snd_pcm_substream *substream = io->substream;
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct dma_async_tx_descriptor *desc;
+       int is_play = rsnd_io_is_play(io);
+
+       desc = dmaengine_prep_dma_cyclic(dmaen->chan,
+                                        substream->runtime->dma_addr,
+                                        snd_pcm_lib_buffer_bytes(substream),
+                                        snd_pcm_lib_period_bytes(substream),
+                                        is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+                                        DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+       if (!desc) {
+               dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
+               return;
+       }
+
+       desc->callback          = rsnd_dmaen_complete;
+       desc->callback_param    = dma;
+
+       if (dmaengine_submit(desc) < 0) {
+               dev_err(dev, "dmaengine_submit() fail\n");
+               return;
+       }
+
+       dma_async_issue_pending(dmaen->chan);
+}
+
+struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
+                                         struct rsnd_mod *mod, char *name)
+{
+       struct dma_chan *chan;
+       struct device_node *np;
+       int i = 0;
+
+       for_each_child_of_node(of_node, np) {
+               if (i == rsnd_mod_id(mod))
+                       break;
+               i++;
+       }
+
+       chan = of_dma_request_slave_channel(np, name);
+
+       of_node_put(np);
+       of_node_put(of_node);
+
+       return chan;
+}
+
+static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from,
+                                                  struct rsnd_mod *mod_to)
+{
+       if ((!mod_from && !mod_to) ||
+           (mod_from && mod_to))
+               return NULL;
+
+       if (mod_from)
+               return rsnd_mod_dma_req(mod_from);
+       else
+               return rsnd_mod_dma_req(mod_to);
+}
+
+static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
+                          struct rsnd_mod *mod_from, struct rsnd_mod *mod_to)
+{
+       struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct dma_slave_config cfg = {};
+       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       int is_play = rsnd_io_is_play(io);
+       int ret;
+
+       if (dmaen->chan) {
+               dev_err(dev, "it already has dma channel\n");
+               return -EIO;
+       }
+
+       if (dev->of_node) {
+               dmaen->chan = rsnd_dmaen_request_channel(mod_from, mod_to);
+       } else {
+               dma_cap_mask_t mask;
+
+               dma_cap_zero(mask);
+               dma_cap_set(DMA_SLAVE, mask);
+
+               dmaen->chan = dma_request_channel(mask, shdma_chan_filter,
+                                                 (void *)id);
+       }
+       if (IS_ERR_OR_NULL(dmaen->chan)) {
+               dev_err(dev, "can't get dma channel\n");
+               goto rsnd_dma_channel_err;
+       }
+
+       cfg.direction   = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+       cfg.src_addr    = dma->src_addr;
+       cfg.dst_addr    = dma->dst_addr;
+       cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+       dev_dbg(dev, "dma : %pad -> %pad\n",
+               &cfg.src_addr, &cfg.dst_addr);
+
+       ret = dmaengine_slave_config(dmaen->chan, &cfg);
+       if (ret < 0)
+               goto rsnd_dma_init_err;
+
+       return 0;
+
+rsnd_dma_init_err:
+       rsnd_dma_quit(dma);
+rsnd_dma_channel_err:
+
+       /*
+        * DMA failed. try to PIO mode
+        * see
+        *      rsnd_ssi_fallback()
+        *      rsnd_rdai_continuance_probe()
+        */
+       return -EAGAIN;
+}
+
+static void rsnd_dmaen_quit(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 = {
+       .start  = rsnd_dmaen_start,
+       .stop   = rsnd_dmaen_stop,
+       .init   = rsnd_dmaen_init,
+       .quit   = rsnd_dmaen_quit,
+};
+
+/*
+ *             Audio DMAC peri peri
+ */
+static const u8 gen2_id_table_ssiu[] = {
+       0x00, /* SSI00 */
+       0x04, /* SSI10 */
+       0x08, /* SSI20 */
+       0x0c, /* SSI3  */
+       0x0d, /* SSI4  */
+       0x0e, /* SSI5  */
+       0x0f, /* SSI6  */
+       0x10, /* SSI7  */
+       0x11, /* SSI8  */
+       0x12, /* SSI90 */
+};
+static const u8 gen2_id_table_scu[] = {
+       0x2d, /* SCU_SRCI0 */
+       0x2e, /* SCU_SRCI1 */
+       0x2f, /* SCU_SRCI2 */
+       0x30, /* SCU_SRCI3 */
+       0x31, /* SCU_SRCI4 */
+       0x32, /* SCU_SRCI5 */
+       0x33, /* SCU_SRCI6 */
+       0x34, /* SCU_SRCI7 */
+       0x35, /* SCU_SRCI8 */
+       0x36, /* SCU_SRCI9 */
+};
+static const u8 gen2_id_table_cmd[] = {
+       0x37, /* SCU_CMD0 */
+       0x38, /* SCU_CMD1 */
+};
+
+static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod)
+{
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+       struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+       struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
+       const u8 *entry = NULL;
+       int id = rsnd_mod_id(mod);
+       int size = 0;
+
+       if (mod == ssi) {
+               entry = gen2_id_table_ssiu;
+               size = ARRAY_SIZE(gen2_id_table_ssiu);
+       } else if (mod == src) {
+               entry = gen2_id_table_scu;
+               size = ARRAY_SIZE(gen2_id_table_scu);
+       } else if (mod == dvc) {
+               entry = gen2_id_table_cmd;
+               size = ARRAY_SIZE(gen2_id_table_cmd);
+       }
+
+       if (!entry)
+               return 0xFF;
+
+       if (size <= id)
+               return 0xFF;
+
+       return entry[id];
+}
+
+static u32 rsnd_dmapp_get_chcr(struct rsnd_mod *mod_from,
+                              struct rsnd_mod *mod_to)
+{
+       return  (rsnd_dmapp_get_id(mod_from) << 24) +
+               (rsnd_dmapp_get_id(mod_to) << 16);
+}
+
+#define rsnd_dmapp_addr(dmac, dma, reg) \
+       (dmac->base + 0x20 + reg + \
+        (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_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);
+
+       dev_dbg(dev, "w %p : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data);
+
+       iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg));
+}
+
+static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg)
+{
+       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);
+
+       return ioread32(rsnd_dmapp_addr(dmac, dma, reg));
+}
+
+static void rsnd_dmapp_stop(struct rsnd_dma *dma)
+{
+       int i;
+
+       rsnd_dmapp_write(dma, 0, PDMACHCR);
+
+       for (i = 0; i < 1024; i++) {
+               if (0 == rsnd_dmapp_read(dma, PDMACHCR))
+                       return;
+               udelay(1);
+       }
+}
+
+static void rsnd_dmapp_start(struct rsnd_dma *dma)
+{
+       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);
+}
+
+static int rsnd_dmapp_init(struct rsnd_priv *priv, 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_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+       struct device *dev = rsnd_priv_to_dev(priv);
+
+       dmapp->dmapp_id = dmac->dmapp_num;
+       dmapp->chcr = rsnd_dmapp_get_chcr(mod_from, mod_to) | PDMACHCR_DE;
+
+       dmac->dmapp_num++;
+
+       rsnd_dmapp_stop(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 = {
+       .start  = rsnd_dmapp_start,
+       .stop   = rsnd_dmapp_stop,
+       .init   = rsnd_dmapp_init,
+       .quit   = rsnd_dmapp_stop,
+};
+
+/*
+ *             Common DMAC Interface
+ */
+
+/*
+ *     DMA read/write register offset
+ *
+ *     RSND_xxx_I_N    for Audio DMAC input
+ *     RSND_xxx_O_N    for Audio DMAC output
+ *     RSND_xxx_I_P    for Audio DMAC peri peri input
+ *     RSND_xxx_O_P    for Audio DMAC peri peri output
+ *
+ *     ex) R-Car H2 case
+ *           mod        / DMAC in    / DMAC out   / DMAC PP in / DMAC pp out
+ *     SSI : 0xec541000 / 0xec241008 / 0xec24100c
+ *     SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000
+ *     SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000
+ *     CMD : 0xec500000 /            / 0xec008000                0xec308000
+ */
+#define RDMA_SSI_I_N(addr, i)  (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8)
+#define RDMA_SSI_O_N(addr, i)  (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc)
+
+#define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i))
+#define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i))
+
+#define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
+#define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
+
+#define RDMA_SRC_I_N(addr, i)  (addr ##_reg - 0x00500000 + (0x400 * i))
+#define RDMA_SRC_O_N(addr, i)  (addr ##_reg - 0x004fc000 + (0x400 * i))
+
+#define RDMA_SRC_I_P(addr, i)  (addr ##_reg - 0x00200000 + (0x400 * i))
+#define RDMA_SRC_O_P(addr, i)  (addr ##_reg - 0x001fc000 + (0x400 * i))
+
+#define RDMA_CMD_O_N(addr, i)  (addr ##_reg - 0x004f8000 + (0x400 * i))
+#define RDMA_CMD_O_P(addr, i)  (addr ##_reg - 0x001f8000 + (0x400 * i))
+
+static dma_addr_t
+rsnd_gen2_dma_addr(struct rsnd_priv *priv,
+                  struct rsnd_mod *mod,
+                  int is_play, int is_from)
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI);
+       phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU);
+       int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
+       int use_src = !!rsnd_io_to_mod_src(io);
+       int use_dvc = !!rsnd_io_to_mod_dvc(io);
+       int id = rsnd_mod_id(mod);
+       struct dma_addr {
+               dma_addr_t out_addr;
+               dma_addr_t in_addr;
+       } dma_addrs[3][2][3] = {
+               /* SRC */
+               {{{ 0,                          0 },
+                 /* Capture */
+                 { RDMA_SRC_O_N(src, id),      RDMA_SRC_I_P(src, id) },
+                 { RDMA_CMD_O_N(src, id),      RDMA_SRC_I_P(src, id) } },
+                /* Playback */
+                {{ 0,                          0, },
+                 { RDMA_SRC_O_P(src, id),      RDMA_SRC_I_N(src, id) },
+                 { RDMA_CMD_O_P(src, id),      RDMA_SRC_I_N(src, id) } }
+               },
+               /* SSI */
+               /* Capture */
+               {{{ RDMA_SSI_O_N(ssi, id),      0 },
+                 { RDMA_SSIU_O_P(ssi, id),     0 },
+                 { RDMA_SSIU_O_P(ssi, id),     0 } },
+                /* Playback */
+                {{ 0,                          RDMA_SSI_I_N(ssi, id) },
+                 { 0,                          RDMA_SSIU_I_P(ssi, id) },
+                 { 0,                          RDMA_SSIU_I_P(ssi, id) } }
+               },
+               /* SSIU */
+               /* Capture */
+               {{{ RDMA_SSIU_O_N(ssi, id),     0 },
+                 { RDMA_SSIU_O_P(ssi, id),     0 },
+                 { RDMA_SSIU_O_P(ssi, id),     0 } },
+                /* Playback */
+                {{ 0,                          RDMA_SSIU_I_N(ssi, id) },
+                 { 0,                          RDMA_SSIU_I_P(ssi, id) },
+                 { 0,                          RDMA_SSIU_I_P(ssi, id) } } },
+       };
+
+       /* it shouldn't happen */
+       if (use_dvc && !use_src)
+               dev_err(dev, "DVC is selected without SRC\n");
+
+       /* use SSIU or SSI ? */
+       if (is_ssi && rsnd_ssi_use_busif(mod))
+               is_ssi++;
+
+       return (is_from) ?
+               dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr :
+               dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr;
+}
+
+static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv,
+                               struct rsnd_mod *mod,
+                               int is_play, int is_from)
+{
+       /*
+        * gen1 uses default DMA addr
+        */
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       if (!mod)
+               return 0;
+
+       return rsnd_gen2_dma_addr(priv, mod, is_play, is_from);
+}
+
+#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */
+static void rsnd_dma_of_path(struct rsnd_dma *dma,
+                            int is_play,
+                            struct rsnd_mod **mod_from,
+                            struct rsnd_mod **mod_to)
+{
+       struct rsnd_mod *this = rsnd_dma_to_mod(dma);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(this);
+       struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io);
+       struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+       struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
+       struct rsnd_mod *mod[MOD_MAX];
+       int i, index;
+
+
+       for (i = 0; i < MOD_MAX; i++)
+               mod[i] = NULL;
+
+       /*
+        * in play case...
+        *
+        * src -> dst
+        *
+        * mem -> SSI
+        * mem -> SRC -> SSI
+        * mem -> SRC -> DVC -> SSI
+        */
+       mod[0] = NULL; /* for "mem" */
+       index = 1;
+       for (i = 1; i < MOD_MAX; i++) {
+               if (!src) {
+                       mod[i] = ssi;
+               } else if (!dvc) {
+                       mod[i] = src;
+                       src = NULL;
+               } else {
+                       if ((!is_play) && (this == src))
+                               this = dvc;
+
+                       mod[i] = (is_play) ? src : dvc;
+                       i++;
+                       mod[i] = (is_play) ? dvc : src;
+                       src = NULL;
+                       dvc = NULL;
+               }
+
+               if (mod[i] == this)
+                       index = i;
+
+               if (mod[i] == ssi)
+                       break;
+       }
+
+       if (is_play) {
+               *mod_from = mod[index - 1];
+               *mod_to   = mod[index];
+       } else {
+               *mod_from = mod[index];
+               *mod_to   = mod[index - 1];
+       }
+}
+
+void rsnd_dma_stop(struct rsnd_dma *dma)
+{
+       dma->ops->stop(dma);
+}
+
+void rsnd_dma_start(struct rsnd_dma *dma)
+{
+       dma->ops->start(dma);
+}
+
+void rsnd_dma_quit(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(dma);
+}
+
+int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id)
+{
+       struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
+       struct rsnd_mod *mod_from;
+       struct rsnd_mod *mod_to;
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
+       int is_play = rsnd_io_is_play(io);
+
+       /*
+        * DMA failed. try to PIO mode
+        * see
+        *      rsnd_ssi_fallback()
+        *      rsnd_rdai_continuance_probe()
+        */
+       if (!dmac)
+               return -EAGAIN;
+
+       rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to);
+
+       dma->src_addr = rsnd_dma_addr(priv, mod_from, is_play, 1);
+       dma->dst_addr = rsnd_dma_addr(priv, mod_to,   is_play, 0);
+
+       /* for Gen2 */
+       if (mod_from && mod_to)
+               dma->ops = &rsnd_dmapp_ops;
+       else
+               dma->ops = &rsnd_dmaen_ops;
+
+       /* for Gen1, overwrite */
+       if (rsnd_is_gen1(priv))
+               dma->ops = &rsnd_dmaen_ops;
+
+       return dma->ops->init(priv, dma, id, mod_from, mod_to);
+}
+
+int rsnd_dma_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
+                  struct rsnd_priv *priv)
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_dma_ctrl *dmac;
+       struct resource *res;
+
+       /*
+        * for Gen1
+        */
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       /*
+        * for Gen2
+        */
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp");
+       dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL);
+       if (!dmac || !res) {
+               dev_err(dev, "dma allocate failed\n");
+               return 0; /* it will be PIO mode */
+       }
+
+       dmac->dmapp_num = 0;
+       dmac->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(dmac->base))
+               return PTR_ERR(dmac->base);
+
+       priv->dma = dmac;
+
+       return 0;
+}
index 261997a3f5899d0b3ed357211181eeb00bf7310c..e5fcb062ad77239bad2ef195037f8c9969edf119 100644 (file)
@@ -24,6 +24,9 @@ struct rsnd_dvc {
        struct rsnd_kctrl_cfg_s rdown;  /* Ramp Rate Down */
 };
 
+#define rsnd_dvc_of_node(priv) \
+       of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc")
+
 #define rsnd_mod_to_dvc(_mod)  \
        container_of((_mod), struct rsnd_dvc, mod)
 
@@ -33,7 +36,7 @@ struct rsnd_dvc {
             ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);      \
             i++)
 
-static const char const *dvc_ramp_rate[] = {
+static const char * const dvc_ramp_rate[] = {
        "128 dB/1 step",         /* 00000 */
        "64 dB/1 step",          /* 00001 */
        "32 dB/1 step",          /* 00010 */
@@ -116,17 +119,6 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
        rsnd_mod_write(mod, DVC_DVUER, 1);
 }
 
-static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
-                              struct rsnd_priv *priv)
-{
-       struct device *dev = rsnd_priv_to_dev(priv);
-
-       dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-       return 0;
-}
-
 static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod,
                                struct rsnd_priv *priv)
 {
@@ -269,9 +261,17 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
        return 0;
 }
 
+static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_mod *mod)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
+       return rsnd_dma_request_channel(rsnd_dvc_of_node(priv),
+                                       mod, "tx");
+}
+
 static struct rsnd_mod_ops rsnd_dvc_ops = {
        .name           = DVC_NAME,
-       .probe          = rsnd_dvc_probe_gen2,
+       .dma_req        = rsnd_dvc_dma_req,
        .remove         = rsnd_dvc_remove_gen2,
        .init           = rsnd_dvc_init,
        .quit           = rsnd_dvc_quit,
@@ -370,8 +370,6 @@ int rsnd_dvc_probe(struct platform_device *pdev,
                              clk, RSND_MOD_DVC, i);
                if (ret)
                        return ret;
-
-               dev_dbg(dev, "CMD%d probed\n", i);
        }
 
        return 0;
index de0685f2abaee64f4d729745497b7a6a4ae51e0e..8c7dc51b1c4fd8a767a5717a651faed17e68234a 100644 (file)
@@ -28,6 +28,7 @@ struct rsnd_gen {
 
        struct regmap *regmap[RSND_BASE_MAX];
        struct regmap_field *regs[RSND_REG_MAX];
+       phys_addr_t res[RSND_REG_MAX];
 };
 
 #define rsnd_priv_to_gen(p)    ((struct rsnd_gen *)(p)->gen)
@@ -118,11 +119,19 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
                                  mask, data);
 }
 
-#define rsnd_gen_regmap_init(priv, id_size, reg_id, conf)              \
-       _rsnd_gen_regmap_init(priv, id_size, reg_id, conf, ARRAY_SIZE(conf))
+phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id)
+{
+       struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+       return  gen->res[reg_id];
+}
+
+#define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf)                \
+       _rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf))
 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,
                                 int conf_size)
 {
@@ -141,8 +150,11 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
        regc.reg_bits = 32;
        regc.val_bits = 32;
        regc.reg_stride = 4;
+       regc.name = name;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id);
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+       if (!res)
+               res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id);
        if (!res)
                return -ENODEV;
 
@@ -156,6 +168,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
 
        gen->base[reg_id] = base;
        gen->regmap[reg_id] = regmap;
+       gen->res[reg_id] = res->start;
 
        for (i = 0; i < conf_size; i++) {
 
@@ -175,126 +188,12 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
        return 0;
 }
 
-/*
- *     DMA read/write register offset
- *
- *     RSND_xxx_I_N    for Audio DMAC input
- *     RSND_xxx_O_N    for Audio DMAC output
- *     RSND_xxx_I_P    for Audio DMAC peri peri input
- *     RSND_xxx_O_P    for Audio DMAC peri peri output
- *
- *     ex) R-Car H2 case
- *           mod        / DMAC in    / DMAC out   / DMAC PP in / DMAC pp out
- *     SSI : 0xec541000 / 0xec241008 / 0xec24100c
- *     SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000
- *     SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000
- *     CMD : 0xec500000 /            / 0xec008000                0xec308000
- */
-#define RDMA_SSI_I_N(addr, i)  (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8)
-#define RDMA_SSI_O_N(addr, i)  (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc)
-
-#define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i))
-#define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i))
-
-#define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
-#define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
-
-#define RDMA_SRC_I_N(addr, i)  (addr ##_reg - 0x00500000 + (0x400 * i))
-#define RDMA_SRC_O_N(addr, i)  (addr ##_reg - 0x004fc000 + (0x400 * i))
-
-#define RDMA_SRC_I_P(addr, i)  (addr ##_reg - 0x00200000 + (0x400 * i))
-#define RDMA_SRC_O_P(addr, i)  (addr ##_reg - 0x001fc000 + (0x400 * i))
-
-#define RDMA_CMD_O_N(addr, i)  (addr ##_reg - 0x004f8000 + (0x400 * i))
-#define RDMA_CMD_O_P(addr, i)  (addr ##_reg - 0x001f8000 + (0x400 * i))
-
-static dma_addr_t
-rsnd_gen2_dma_addr(struct rsnd_priv *priv,
-                  struct rsnd_mod *mod,
-                  int is_play, int is_from)
-{
-       struct platform_device *pdev = rsnd_priv_to_pdev(priv);
-       struct device *dev = rsnd_priv_to_dev(priv);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-       dma_addr_t ssi_reg = platform_get_resource(pdev,
-                               IORESOURCE_MEM, RSND_GEN2_SSI)->start;
-       dma_addr_t src_reg = platform_get_resource(pdev,
-                               IORESOURCE_MEM, RSND_GEN2_SCU)->start;
-       int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod);
-       int use_src = !!rsnd_io_to_mod_src(io);
-       int use_dvc = !!rsnd_io_to_mod_dvc(io);
-       int id = rsnd_mod_id(mod);
-       struct dma_addr {
-               dma_addr_t out_addr;
-               dma_addr_t in_addr;
-       } dma_addrs[3][2][3] = {
-               /* SRC */
-               {{{ 0,                          0 },
-               /* Capture */
-                 { RDMA_SRC_O_N(src, id),      RDMA_SRC_I_P(src, id) },
-                 { RDMA_CMD_O_N(src, id),      RDMA_SRC_I_P(src, id) } },
-                /* Playback */
-                {{ 0,                          0, },
-                 { RDMA_SRC_O_P(src, id),      RDMA_SRC_I_N(src, id) },
-                 { RDMA_CMD_O_P(src, id),      RDMA_SRC_I_N(src, id) } }
-               },
-               /* SSI */
-               /* Capture */
-               {{{ RDMA_SSI_O_N(ssi, id),      0 },
-                 { RDMA_SSIU_O_P(ssi, id),     0 },
-                 { RDMA_SSIU_O_P(ssi, id),     0 } },
-                /* Playback */
-                {{ 0,                          RDMA_SSI_I_N(ssi, id) },
-                 { 0,                          RDMA_SSIU_I_P(ssi, id) },
-                 { 0,                          RDMA_SSIU_I_P(ssi, id) } }
-               },
-               /* SSIU */
-               /* Capture */
-               {{{ RDMA_SSIU_O_N(ssi, id),     0 },
-                 { RDMA_SSIU_O_P(ssi, id),     0 },
-                 { RDMA_SSIU_O_P(ssi, id),     0 } },
-                /* Playback */
-                {{ 0,                          RDMA_SSIU_I_N(ssi, id) },
-                 { 0,                          RDMA_SSIU_I_P(ssi, id) },
-                 { 0,                          RDMA_SSIU_I_P(ssi, id) } } },
-       };
-
-       /* it shouldn't happen */
-       if (use_dvc && !use_src)
-               dev_err(dev, "DVC is selected without SRC\n");
-
-       /* use SSIU or SSI ? */
-       if (is_ssi && (0 == strcmp(rsnd_mod_dma_name(mod), "ssiu")))
-               is_ssi++;
-
-       return (is_from) ?
-               dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr :
-               dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr;
-}
-
-dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv,
-                            struct rsnd_mod *mod,
-                            int is_play, int is_from)
-{
-       /*
-        * gen1 uses default DMA addr
-        */
-       if (rsnd_is_gen1(priv))
-               return 0;
-
-       if (!mod)
-               return 0;
-
-       return rsnd_gen2_dma_addr(priv, mod, is_play, is_from);
-}
-
 /*
  *             Gen2
  */
 static int rsnd_gen2_probe(struct platform_device *pdev,
                           struct rsnd_priv *priv)
 {
-       struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_regmap_field_conf conf_ssiu[] = {
                RSND_GEN_S_REG(SSI_MODE0,       0x800),
                RSND_GEN_S_REG(SSI_MODE1,       0x804),
@@ -368,18 +267,16 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
        int ret_adg;
        int ret_ssi;
 
-       ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, conf_ssiu);
-       ret_scu  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU,  conf_scu);
-       ret_adg  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG,  conf_adg);
-       ret_ssi  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI,  conf_ssi);
+       ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, "ssiu", conf_ssiu);
+       ret_scu  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU,  "scu",  conf_scu);
+       ret_adg  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG,  "adg",  conf_adg);
+       ret_ssi  = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI,  "ssi",  conf_ssi);
        if (ret_ssiu < 0 ||
            ret_scu  < 0 ||
            ret_adg  < 0 ||
            ret_ssi  < 0)
                return ret_ssiu | ret_scu | ret_adg | ret_ssi;
 
-       dev_dbg(dev, "Gen2 is probed\n");
-
        return 0;
 }
 
@@ -390,7 +287,6 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
 static int rsnd_gen1_probe(struct platform_device *pdev,
                           struct rsnd_priv *priv)
 {
-       struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_regmap_field_conf conf_sru[] = {
                RSND_GEN_S_REG(SRC_ROUTE_SEL,   0x00),
                RSND_GEN_S_REG(SRC_TMG_SEL0,    0x08),
@@ -440,16 +336,14 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
        int ret_adg;
        int ret_ssi;
 
-       ret_sru  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU,  conf_sru);
-       ret_adg  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG,  conf_adg);
-       ret_ssi  = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI,  conf_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 ||
            ret_ssi  < 0)
                return ret_sru | ret_adg | ret_ssi;
 
-       dev_dbg(dev, "Gen1 is probed\n");
-
        return 0;
 }
 
index 1bccc5515b5a9bbbb2e7c57255aa6a9707b11593..4e6de6804cfb17a1d70d3d1c363ee0a0c14ed019 100644 (file)
@@ -170,21 +170,47 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod);
 /*
  *     R-Car DMA
  */
-struct rsnd_dma {
-       struct sh_dmae_slave    slave;
+struct rsnd_dma;
+struct rsnd_dma_ops {
+       void (*start)(struct rsnd_dma *dma);
+       void (*stop)(struct rsnd_dma *dma);
+       int (*init)(struct rsnd_priv *priv, struct rsnd_dma *dma, int id,
+                   struct rsnd_mod *mod_from, struct rsnd_mod *mod_to);
+       void  (*quit)(struct rsnd_dma *dma);
+};
+
+struct rsnd_dmaen {
        struct dma_chan         *chan;
-       enum dma_transfer_direction dir;
-       dma_addr_t              addr;
 };
 
+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)
+
 void rsnd_dma_start(struct rsnd_dma *dma);
 void rsnd_dma_stop(struct rsnd_dma *dma);
-int rsnd_dma_available(struct rsnd_dma *dma);
-int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
-       int is_play, int id);
-void  rsnd_dma_quit(struct rsnd_priv *priv,
-                   struct rsnd_dma *dma);
+int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id);
+void  rsnd_dma_quit(struct rsnd_dma *dma);
+int rsnd_dma_probe(struct platform_device *pdev,
+                  const struct rsnd_of_data *of_data,
+                  struct rsnd_priv *priv);
+struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
+                                         struct rsnd_mod *mod, char *name);
 
+#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
 
 /*
  *     R-Car sound mod
@@ -198,7 +224,7 @@ enum rsnd_mod_type {
 
 struct rsnd_mod_ops {
        char *name;
-       char* (*dma_name)(struct rsnd_mod *mod);
+       struct dma_chan* (*dma_req)(struct rsnd_mod *mod);
        int (*probe)(struct rsnd_mod *mod,
                     struct rsnd_priv *priv);
        int (*remove)(struct rsnd_mod *mod,
@@ -213,6 +239,9 @@ struct rsnd_mod_ops {
                    struct rsnd_priv *priv);
        int (*pcm_new)(struct rsnd_mod *mod,
                       struct snd_soc_pcm_runtime *rtd);
+       int (*hw_params)(struct rsnd_mod *mod,
+                        struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *hw_params);
        int (*fallback)(struct rsnd_mod *mod,
                        struct rsnd_priv *priv);
 };
@@ -236,6 +265,9 @@ struct rsnd_mod {
  * 2   0: start        1: stop
  * 3   0: pcm_new
  * 4   0: fallback
+ *
+ * 31 bit is always called (see __rsnd_mod_call)
+ * 31  0: hw_params
  */
 #define __rsnd_mod_shift_probe         0
 #define __rsnd_mod_shift_remove                0
@@ -245,6 +277,7 @@ struct rsnd_mod {
 #define __rsnd_mod_shift_stop          2
 #define __rsnd_mod_shift_pcm_new       3
 #define __rsnd_mod_shift_fallback      4
+#define __rsnd_mod_shift_hw_params     31 /* always called */
 
 #define __rsnd_mod_call_probe          0
 #define __rsnd_mod_call_remove         1
@@ -254,10 +287,10 @@ struct rsnd_mod {
 #define __rsnd_mod_call_stop           1
 #define __rsnd_mod_call_pcm_new                0
 #define __rsnd_mod_call_fallback       0
+#define __rsnd_mod_call_hw_params      0
 
 #define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod)))
 #define rsnd_mod_to_dma(mod) (&(mod)->dma)
-#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
 #define rsnd_mod_to_io(mod) ((mod)->io)
 #define rsnd_mod_id(mod) ((mod)->id)
 #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk)
@@ -270,13 +303,14 @@ int rsnd_mod_init(struct rsnd_mod *mod,
                   int id);
 void rsnd_mod_quit(struct rsnd_mod *mod);
 char *rsnd_mod_name(struct rsnd_mod *mod);
-char *rsnd_mod_dma_name(struct rsnd_mod *mod);
+struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod);
 
 /*
  *     R-Car sound DAI
  */
 #define RSND_DAI_NAME_SIZE     16
 struct rsnd_dai_stream {
+       char name[RSND_DAI_NAME_SIZE];
        struct snd_pcm_substream *substream;
        struct rsnd_mod *mod[RSND_MOD_MAX];
        struct rsnd_dai_path_info *info; /* rcar_snd.h */
@@ -332,9 +366,7 @@ int rsnd_gen_probe(struct platform_device *pdev,
 void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
                               struct rsnd_mod *mod,
                               enum rsnd_reg reg);
-dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv,
-                      struct rsnd_mod *mod,
-                      int is_play,  int is_from);
+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)
@@ -389,6 +421,11 @@ struct rsnd_priv {
         */
        void *adg;
 
+       /*
+        * below value will be filled on rsnd_dma_probe()
+        */
+       void *dma;
+
        /*
         * below value will be filled on rsnd_ssi_probe()
         */
@@ -415,19 +452,6 @@ struct rsnd_priv {
 #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
 #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
 
-#define rsnd_info_is_playback(priv, type)                              \
-({                                                                     \
-       struct rcar_snd_info *info = rsnd_priv_to_info(priv);           \
-       int i, is_play = 0;                                             \
-       for (i = 0; i < info->dai_info_nr; i++) {                       \
-               if (info->dai_info[i].playback.type == (type)->info) {  \
-                       is_play = 1;                                    \
-                       break;                                          \
-               }                                                       \
-       }                                                               \
-       is_play;                                                        \
-})
-
 /*
  *     rsnd_kctrl
  */
@@ -506,6 +530,7 @@ void rsnd_ssi_remove(struct platform_device *pdev,
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
 int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
 int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
+int rsnd_ssi_use_busif(struct rsnd_mod *mod);
 
 /*
  *     R-Car DVC
diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c
new file mode 100644 (file)
index 0000000..a68517a
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * Renesas Sampling Rate Convert Sound Card for DPCM
+ *
+ * Copyright (C) 2015 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * based on ${LINUX}/sound/soc/generic/simple-card.c
+ *
+ * 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 <linux/clk.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+struct rsrc_card_of_data {
+       const char *prefix;
+       const struct snd_soc_dapm_route *routes;
+       int num_routes;
+};
+
+static const struct snd_soc_dapm_route routes_ssi0_ak4642[] = {
+       {"ak4642 Playback", NULL, "DAI0 Playback"},
+       {"DAI0 Capture", NULL, "ak4642 Capture"},
+};
+
+static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = {
+       .prefix         = "ak4642",
+       .routes         = routes_ssi0_ak4642,
+       .num_routes     = ARRAY_SIZE(routes_ssi0_ak4642),
+};
+
+static const struct of_device_id rsrc_card_of_match[] = {
+       { .compatible = "renesas,rsrc-card,lager",      .data = &routes_of_ssi0_ak4642 },
+       { .compatible = "renesas,rsrc-card,koelsch",    .data = &routes_of_ssi0_ak4642 },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rsrc_card_of_match);
+
+struct rsrc_card_dai {
+       const char *name;
+       unsigned int fmt;
+       unsigned int sysclk;
+       struct clk *clk;
+};
+
+#define RSRC_FB_NUM    2 /* FE/BE */
+#define IDX_CPU                0
+#define IDX_CODEC      1
+struct rsrc_card_priv {
+       struct snd_soc_card snd_card;
+       struct rsrc_card_dai_props {
+               struct rsrc_card_dai cpu_dai;
+               struct rsrc_card_dai codec_dai;
+       } dai_props[RSRC_FB_NUM];
+       struct snd_soc_codec_conf codec_conf;
+       struct snd_soc_dai_link dai_link[RSRC_FB_NUM];
+       u32 convert_rate;
+};
+
+#define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev)
+#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i)
+#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + i)
+#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data)
+
+static int rsrc_card_startup(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct rsrc_card_priv *priv =   snd_soc_card_get_drvdata(rtd->card);
+       struct rsrc_card_dai_props *dai_props =
+               &priv->dai_props[rtd - rtd->card->rtd];
+       int ret;
+
+       ret = clk_prepare_enable(dai_props->cpu_dai.clk);
+       if (ret)
+               return ret;
+
+       ret = clk_prepare_enable(dai_props->codec_dai.clk);
+       if (ret)
+               clk_disable_unprepare(dai_props->cpu_dai.clk);
+
+       return ret;
+}
+
+static void rsrc_card_shutdown(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct rsrc_card_priv *priv =   snd_soc_card_get_drvdata(rtd->card);
+       struct rsrc_card_dai_props *dai_props =
+               &priv->dai_props[rtd - rtd->card->rtd];
+
+       clk_disable_unprepare(dai_props->cpu_dai.clk);
+
+       clk_disable_unprepare(dai_props->codec_dai.clk);
+}
+
+static struct snd_soc_ops rsrc_card_ops = {
+       .startup = rsrc_card_startup,
+       .shutdown = rsrc_card_shutdown,
+};
+
+static int __rsrc_card_dai_init(struct snd_soc_dai *dai,
+                               struct rsrc_card_dai *set)
+{
+       int ret;
+
+       if (set->fmt) {
+               ret = snd_soc_dai_set_fmt(dai, set->fmt);
+               if (ret && ret != -ENOTSUPP) {
+                       dev_err(dai->dev, "set_fmt error\n");
+                       goto err;
+               }
+       }
+
+       if (set->sysclk) {
+               ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
+               if (ret && ret != -ENOTSUPP) {
+                       dev_err(dai->dev, "set_sysclk error\n");
+                       goto err;
+               }
+       }
+
+       ret = 0;
+
+err:
+       return ret;
+}
+
+static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct rsrc_card_priv *priv =   snd_soc_card_get_drvdata(rtd->card);
+       struct snd_soc_dai *codec = rtd->codec_dai;
+       struct snd_soc_dai *cpu = rtd->cpu_dai;
+       struct rsrc_card_dai_props *dai_props;
+       int num, ret;
+
+       num = rtd - rtd->card->rtd;
+       dai_props = &priv->dai_props[num];
+       ret = __rsrc_card_dai_init(codec, &dai_props->codec_dai);
+       if (ret < 0)
+               return ret;
+
+       ret = __rsrc_card_dai_init(cpu, &dai_props->cpu_dai);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+                                       struct snd_pcm_hw_params *params)
+{
+       struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+       struct snd_interval *rate = hw_param_interval(params,
+                                                     SNDRV_PCM_HW_PARAM_RATE);
+
+       if (!priv->convert_rate)
+               return 0;
+
+       rate->min = rate->max = priv->convert_rate;
+
+       return 0;
+}
+
+static int
+rsrc_card_sub_parse_of(struct rsrc_card_priv *priv,
+                      struct device_node *np,
+                      struct rsrc_card_dai *dai,
+                      struct snd_soc_dai_link *dai_link,
+                      int *args_count)
+{
+       struct device *dev = rsrc_priv_to_dev(priv);
+       const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
+       struct of_phandle_args args;
+       struct device_node **p_node;
+       struct clk *clk;
+       const char **dai_name;
+       const char **name;
+       u32 val;
+       int ret;
+
+       if (args_count) {
+               p_node          = &dai_link->cpu_of_node;
+               dai_name        = &dai_link->cpu_dai_name;
+               name            = &dai_link->cpu_name;
+       } else {
+               p_node          = &dai_link->codec_of_node;
+               dai_name        = &dai_link->codec_dai_name;
+               name            = &dai_link->codec_name;
+       }
+
+       if (!np) {
+               /* use snd-soc-dummy */
+               *p_node         = NULL;
+               *dai_name       = "snd-soc-dummy-dai";
+               *name           = "snd-soc-dummy";
+               return 0;
+       }
+
+       /*
+        * Get node via "sound-dai = <&phandle port>"
+        * it will be used as xxx_of_node on soc_bind_dai_link()
+        */
+       ret = of_parse_phandle_with_args(np, "sound-dai",
+                                        "#sound-dai-cells", 0, &args);
+       if (ret)
+               return ret;
+
+       *p_node = args.np;
+
+       /* Get dai->name */
+       ret = snd_soc_of_get_dai_name(np, dai_name);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * FIXME
+        *
+        * rsrc assumes DPCM playback/capture
+        */
+       dai_link->dpcm_playback = 1;
+       dai_link->dpcm_capture = 1;
+
+       if (args_count) {
+               *args_count = args.args_count;
+               dai_link->dynamic = 1;
+       } else {
+               dai_link->no_pcm = 1;
+               priv->codec_conf.of_node = (*p_node);
+               priv->codec_conf.name_prefix = of_data->prefix;
+       }
+
+       /*
+        * Parse dai->sysclk come from "clocks = <&xxx>"
+        * (if system has common clock)
+        *  or "system-clock-frequency = <xxx>"
+        *  or device's module clock.
+        */
+       if (of_property_read_bool(np, "clocks")) {
+               clk = of_clk_get(np, 0);
+               if (IS_ERR(clk)) {
+                       ret = PTR_ERR(clk);
+                       return ret;
+               }
+
+               dai->sysclk = clk_get_rate(clk);
+               dai->clk = clk;
+       } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
+               dai->sysclk = val;
+       } else {
+               clk = of_clk_get(args.np, 0);
+               if (!IS_ERR(clk))
+                       dai->sysclk = clk_get_rate(clk);
+       }
+
+       return 0;
+}
+
+static int rsrc_card_parse_daifmt(struct device_node *node,
+                                 struct rsrc_card_priv *priv,
+                                 struct device_node *codec,
+                                 int idx)
+{
+       struct device_node *bitclkmaster = NULL;
+       struct device_node *framemaster = NULL;
+       struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx);
+       struct rsrc_card_dai *cpu_dai = &dai_props->cpu_dai;
+       struct rsrc_card_dai *codec_dai = &dai_props->codec_dai;
+       unsigned int daifmt;
+
+       daifmt = snd_soc_of_parse_daifmt(node, NULL,
+                                        &bitclkmaster, &framemaster);
+       daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+
+       if (!bitclkmaster && !framemaster)
+               return -EINVAL;
+
+       if (codec == bitclkmaster)
+               daifmt |= (codec == framemaster) ?
+                       SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
+       else
+               daifmt |= (codec == framemaster) ?
+                       SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
+
+       cpu_dai->fmt    = daifmt;
+       codec_dai->fmt  = daifmt;
+
+       of_node_put(bitclkmaster);
+       of_node_put(framemaster);
+
+       return 0;
+}
+
+static int rsrc_card_dai_link_of(struct device_node *node,
+                                struct rsrc_card_priv *priv,
+                                int idx)
+{
+       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_props *dai_props = rsrc_priv_to_props(priv, idx);
+       struct device_node *cpu = NULL;
+       struct device_node *codec = NULL;
+       char *name;
+       char prop[128];
+       int ret, cpu_args;
+
+       cpu = of_get_child_by_name(node, "cpu");
+       codec = of_get_child_by_name(node, "codec");
+
+       if (!cpu || !codec) {
+               ret = -EINVAL;
+               dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
+               goto dai_link_of_err;
+       }
+
+       ret = rsrc_card_parse_daifmt(node, priv, codec, idx);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL,
+                                    &dai_props->cpu_dai,
+                                    dai_link,
+                                    &cpu_args);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL,
+                                    &dai_props->codec_dai,
+                                    dai_link,
+                                    NULL);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
+               ret = -EINVAL;
+               goto dai_link_of_err;
+       }
+
+       /* Simple Card assumes platform == cpu */
+       dai_link->platform_of_node = dai_link->cpu_of_node;
+
+       /* DAI link name is created from CPU/CODEC dai name */
+       name = devm_kzalloc(dev,
+                           strlen(dai_link->cpu_dai_name)   +
+                           strlen(dai_link->codec_dai_name) + 2,
+                           GFP_KERNEL);
+       if (!name) {
+               ret = -ENOMEM;
+               goto dai_link_of_err;
+       }
+
+       sprintf(name, "%s-%s", dai_link->cpu_dai_name,
+               dai_link->codec_dai_name);
+       dai_link->name = dai_link->stream_name = name;
+       dai_link->ops = &rsrc_card_ops;
+       dai_link->init = rsrc_card_dai_init;
+
+       if (idx == IDX_CODEC)
+               dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup;
+
+       dev_dbg(dev, "\tname : %s\n", dai_link->stream_name);
+       dev_dbg(dev, "\tcpu : %s / %04x / %d\n",
+               dai_link->cpu_dai_name,
+               dai_props->cpu_dai.fmt,
+               dai_props->cpu_dai.sysclk);
+       dev_dbg(dev, "\tcodec : %s / %04x / %d\n",
+               dai_link->codec_dai_name,
+               dai_props->codec_dai.fmt,
+               dai_props->codec_dai.sysclk);
+
+       /*
+        * In soc_bind_dai_link() will check cpu name after
+        * of_node matching if dai_link has cpu_dai_name.
+        * but, it will never match if name was created by
+        * fmt_single_name() remove cpu_dai_name if cpu_args
+        * was 0. See:
+        *      fmt_single_name()
+        *      fmt_multiple_name()
+        */
+       if (!cpu_args)
+               dai_link->cpu_dai_name = NULL;
+
+dai_link_of_err:
+       of_node_put(cpu);
+       of_node_put(codec);
+
+       return ret;
+}
+
+static int rsrc_card_parse_of(struct device_node *node,
+                             struct rsrc_card_priv *priv)
+{
+       struct device *dev = rsrc_priv_to_dev(priv);
+       const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
+       int ret;
+       int i;
+
+       if (!node)
+               return -EINVAL;
+
+       /* Parse the card name from DT */
+       snd_soc_of_parse_card_name(&priv->snd_card, "card-name");
+
+       /* DAPM routes */
+       priv->snd_card.of_dapm_routes           = of_data->routes;
+       priv->snd_card.num_of_dapm_routes       = of_data->num_routes;
+
+       /* sampling rate convert */
+       of_property_read_u32(node, "convert-rate", &priv->convert_rate);
+
+       dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n",
+               priv->snd_card.name ? priv->snd_card.name : "",
+               priv->convert_rate);
+
+       /* FE/BE */
+       for (i = 0; i < RSRC_FB_NUM; i++) {
+               ret = rsrc_card_dai_link_of(node, priv, i);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (!priv->snd_card.name)
+               priv->snd_card.name = priv->snd_card.dai_link->name;
+
+       return 0;
+}
+
+/* Decrease the reference count of the device nodes */
+static int rsrc_card_unref(struct snd_soc_card *card)
+{
+       struct snd_soc_dai_link *dai_link;
+       int num_links;
+
+       for (num_links = 0, dai_link = card->dai_link;
+            num_links < card->num_links;
+            num_links++, dai_link++) {
+               of_node_put(dai_link->cpu_of_node);
+               of_node_put(dai_link->codec_of_node);
+       }
+       return 0;
+}
+
+static int rsrc_card_probe(struct platform_device *pdev)
+{
+       struct rsrc_card_priv *priv;
+       struct snd_soc_dai_link *dai_link;
+       struct device_node *np = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       int ret;
+
+       /* Allocate the private data */
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       /* Init snd_soc_card */
+       priv->snd_card.owner = THIS_MODULE;
+       priv->snd_card.dev = dev;
+       dai_link = priv->dai_link;
+       priv->snd_card.dai_link = dai_link;
+       priv->snd_card.num_links = RSRC_FB_NUM;
+       priv->snd_card.codec_conf = &priv->codec_conf;
+       priv->snd_card.num_configs = 1;
+
+       ret = rsrc_card_parse_of(np, priv);
+       if (ret < 0) {
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "parse error %d\n", ret);
+               goto err;
+       }
+
+       snd_soc_card_set_drvdata(&priv->snd_card, priv);
+
+       ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
+       if (ret >= 0)
+               return ret;
+err:
+       rsrc_card_unref(&priv->snd_card);
+
+       return ret;
+}
+
+static int rsrc_card_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       return rsrc_card_unref(card);
+}
+
+static struct platform_driver rsrc_card = {
+       .driver = {
+               .name = "renesas-src-audio-card",
+               .of_match_table = rsrc_card_of_match,
+       },
+       .probe = rsrc_card_probe,
+       .remove = rsrc_card_remove,
+};
+
+module_platform_driver(rsrc_card);
+
+MODULE_ALIAS("platform:renesas-src-audio-card");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas Sampling Rate Convert Sound Card");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
index c77d059edc84ce0b63dbd2e82414864d91edf956..3beb32eb412a103db4135a20e96948f4a1c88c80 100644 (file)
 struct rsnd_src {
        struct rsnd_src_platform_info *info; /* rcar_snd.h */
        struct rsnd_mod mod;
+       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;
 };
 
 #define RSND_SRC_NAME_SIZE 16
 
-#define rsnd_src_convert_rate(p) ((p)->info->convert_rate)
+#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)
-#define rsnd_src_dma_available(src) \
-       rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod))
 
 #define for_each_rsnd_src(pos, priv, i)                                \
        for ((i) = 0;                                           \
@@ -113,6 +117,17 @@ struct rsnd_src {
 /*
  *             Gen1/Gen2 common functions
  */
+static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       int is_play = rsnd_io_is_play(io);
+
+       return rsnd_dma_request_channel(rsnd_src_of_node(priv),
+                                       mod,
+                                       is_play ? "rx" : "tx");
+}
+
 int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
                        int use_busif)
 {
@@ -220,6 +235,30 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod)
        return 0;
 }
 
+static u32 rsnd_src_convert_rate(struct rsnd_src *src)
+{
+       struct rsnd_mod *mod = &src->mod;
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       u32 convert_rate;
+
+       if (!runtime)
+               return 0;
+
+       if (!rsnd_enable_sync_convert(src))
+               return src->convert_rate;
+
+       convert_rate = src->sync.val;
+
+       if (!convert_rate)
+               convert_rate = src->convert_rate;
+
+       if (!convert_rate)
+               convert_rate = runtime->rate;
+
+       return convert_rate;
+}
+
 unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
                                   struct rsnd_dai_stream *io,
                                   struct snd_pcm_runtime *runtime)
@@ -276,7 +315,43 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod)
        return 0;
 }
 
-static int rsnd_src_init(struct rsnd_mod *mod)
+static int rsnd_src_hw_params(struct rsnd_mod *mod,
+                             struct snd_pcm_substream *substream,
+                             struct snd_pcm_hw_params *fe_params)
+{
+       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.
+        * And then, this function will be called *after* BE settings.
+        * this means, each BE already has fixuped hw_params.
+        * see
+        *      dpcm_fe_dai_hw_params()
+        *      dpcm_be_dai_hw_params()
+        */
+       if (fe->dai_link->dynamic) {
+               int stream = substream->stream;
+               struct snd_soc_dpcm *dpcm;
+               struct snd_pcm_hw_params *be_params;
+
+               list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+                       be_params = &dpcm->hw_params;
+
+                       if (params_rate(fe_params) != params_rate(be_params))
+                               src->convert_rate = params_rate(be_params);
+               }
+       }
+
+       return 0;
+}
+
+static int rsnd_src_init(struct rsnd_mod *mod,
+                        struct rsnd_priv *priv)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
 
@@ -284,6 +359,9 @@ static int rsnd_src_init(struct rsnd_mod *mod)
 
        src->err = 0;
 
+       /* reset sync convert_rate */
+       src->sync.val = 0;
+
        /*
         * Initialize the operation of the SRC internal circuits
         * see rsnd_src_start()
@@ -305,6 +383,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
                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;
 }
 
@@ -448,23 +531,12 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod)
        return 0;
 }
 
-static int rsnd_src_probe_gen1(struct rsnd_mod *mod,
-                              struct rsnd_priv *priv)
-{
-       struct device *dev = rsnd_priv_to_dev(priv);
-
-       dev_dbg(dev, "%s[%d] (Gen1) is probed\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-       return 0;
-}
-
 static int rsnd_src_init_gen1(struct rsnd_mod *mod,
                              struct rsnd_priv *priv)
 {
        int ret;
 
-       ret = rsnd_src_init(mod);
+       ret = rsnd_src_init(mod, priv);
        if (ret < 0)
                return ret;
 
@@ -505,11 +577,12 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
 
 static struct rsnd_mod_ops rsnd_src_gen1_ops = {
        .name   = SRC_NAME,
-       .probe  = rsnd_src_probe_gen1,
+       .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,
 };
 
 /*
@@ -607,13 +680,17 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data)
 
        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);
 
-               _rsnd_src_stop_gen2(mod);
-               _rsnd_src_start_gen2(mod);
-
                dev_dbg(dev, "%s[%d] restart\n",
                        rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+               _rsnd_src_stop_gen2(mod);
+               if (src->err < 1024)
+                       _rsnd_src_start_gen2(mod);
+               else
+                       dev_warn(dev, "no more SRC restart\n");
        }
 
        return IRQ_HANDLED;
@@ -627,6 +704,7 @@ static int rsnd_src_set_convert_rate_gen2(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(src);
+       u32 cr, route;
        uint ratio;
        int ret;
 
@@ -647,13 +725,21 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod)
        if (ret < 0)
                return ret;
 
-       rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
-
+       cr      = 0x00011110;
+       route   = 0x0;
        if (convert_rate) {
-               /* Gen1/Gen2 are not compatible */
-               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+               route   = 0x1;
+
+               if (rsnd_enable_sync_convert(src)) {
+                       cr |= 0x1;
+                       route |= rsnd_io_is_play(io) ?
+                               (0x1 << 24) : (0x1 << 25);
+               }
        }
 
+       rsnd_mod_write(mod, SRC_SRCCR, cr);
+       rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
+
        switch (rsnd_mod_id(mod)) {
        case 5:
        case 6:
@@ -708,24 +794,12 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
                                       IRQF_SHARED,
                                       dev_name(dev), mod);
                if (ret)
-                       goto rsnd_src_probe_gen2_fail;
+                       return ret;
        }
 
        ret = rsnd_dma_init(priv,
                            rsnd_mod_to_dma(mod),
-                           rsnd_info_is_playback(priv, src),
                            src->info->dma_id);
-       if (ret)
-               goto rsnd_src_probe_gen2_fail;
-
-       dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-       return ret;
-
-rsnd_src_probe_gen2_fail:
-       dev_err(dev, "%s[%d] (Gen2) failed\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
@@ -733,7 +807,7 @@ rsnd_src_probe_gen2_fail:
 static int rsnd_src_remove_gen2(struct rsnd_mod *mod,
                                struct rsnd_priv *priv)
 {
-       rsnd_dma_quit(priv, rsnd_mod_to_dma(mod));
+       rsnd_dma_quit(rsnd_mod_to_dma(mod));
 
        return 0;
 }
@@ -743,7 +817,7 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod,
 {
        int ret;
 
-       ret = rsnd_src_init(mod);
+       ret = rsnd_src_init(mod, priv);
        if (ret < 0)
                return ret;
 
@@ -778,14 +852,91 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
        return ret;
 }
 
+static void rsnd_src_reconvert_update(struct rsnd_mod *mod)
+{
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(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(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(struct rsnd_mod *mod,
+                           struct snd_soc_pcm_runtime *rtd)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+       struct rsnd_src *src = rsnd_mod_to_src(mod);
+       int ret;
+
+       /*
+        * enable SRC sync convert if possible
+        */
+
+       /*
+        * Gen1 is not supported
+        */
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       /*
+        * SRC sync convert needs clock master
+        */
+       if (!rsnd_rdai_is_clk_master(rdai))
+               return 0;
+
+       /*
+        * We can't use SRC sync convert
+        * if it has DVC
+        */
+       if (rsnd_io_to_mod_dvc(io))
+               return 0;
+
+       /*
+        * enable sync convert
+        */
+       ret = rsnd_kctrl_new_s(mod, rtd,
+                              rsnd_io_is_play(io) ?
+                              "SRC Out Rate Switch" :
+                              "SRC In Rate Switch",
+                              rsnd_src_reconvert_update,
+                              &src->sen, 1);
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_kctrl_new_s(mod, rtd,
+                              rsnd_io_is_play(io) ?
+                              "SRC Out Rate" :
+                              "SRC In Rate",
+                              rsnd_src_reconvert_update,
+                              &src->sync, 192000);
+
+       return ret;
+}
+
 static struct rsnd_mod_ops rsnd_src_gen2_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,
        .quit   = rsnd_src_quit,
        .start  = rsnd_src_start_gen2,
        .stop   = rsnd_src_stop_gen2,
+       .hw_params = rsnd_src_hw_params,
+       .pcm_new = rsnd_src_pcm_new,
 };
 
 struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
@@ -810,7 +961,7 @@ static void rsnd_of_parse_src(struct platform_device *pdev,
        if (!of_data)
                return;
 
-       src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src");
+       src_node = rsnd_src_of_node(priv);
        if (!src_node)
                return;
 
@@ -893,8 +1044,6 @@ int rsnd_src_probe(struct platform_device *pdev,
                ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i);
                if (ret)
                        return ret;
-
-               dev_dbg(dev, "SRC%d probed\n", i);
        }
 
        return 0;
index f7cb1fd635a0eb32e9e17f82e411356fea3532d7..7bb9c087f3dc458a0dbd4ef6fe01c7d8c44ac7f4 100644 (file)
@@ -80,13 +80,13 @@ struct rsnd_ssi {
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_dma_to_ssi(dma)  rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
 #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
-#define rsnd_ssi_dma_available(ssi) \
-       rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
 #define rsnd_ssi_clk_from_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")
 
-static int rsnd_ssi_use_busif(struct rsnd_mod *mod)
+int rsnd_ssi_use_busif(struct rsnd_mod *mod)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
@@ -416,11 +416,14 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
                /*
                 * restart SSI
                 */
-               rsnd_ssi_stop(mod, priv);
-               rsnd_ssi_start(mod, priv);
-
                dev_dbg(dev, "%s[%d] restart\n",
                        rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+               rsnd_ssi_stop(mod, priv);
+               if (ssi->err < 1024)
+                       rsnd_ssi_start(mod, priv);
+               else
+                       dev_warn(dev, "no more SSI restart\n");
        }
 
        rsnd_ssi_record_error(ssi, status);
@@ -442,12 +445,6 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
                               rsnd_ssi_interrupt,
                               IRQF_SHARED,
                               dev_name(dev), ssi);
-       if (ret)
-               dev_err(dev, "%s[%d] (PIO) request interrupt failed\n",
-                       rsnd_mod_name(mod), rsnd_mod_id(mod));
-       else
-               dev_dbg(dev, "%s[%d] (PIO) is probed\n",
-                       rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
@@ -474,23 +471,11 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
                               IRQF_SHARED,
                               dev_name(dev), ssi);
        if (ret)
-               goto rsnd_ssi_dma_probe_fail;
+               return ret;
 
        ret = rsnd_dma_init(
                priv, rsnd_mod_to_dma(mod),
-               rsnd_info_is_playback(priv, ssi),
                dma_id);
-       if (ret)
-               goto rsnd_ssi_dma_probe_fail;
-
-       dev_dbg(dev, "%s[%d] (DMA) is probed\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-       return ret;
-
-rsnd_ssi_dma_probe_fail:
-       dev_err(dev, "%s[%d] (DMA) is failed\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
@@ -502,7 +487,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
        struct device *dev = rsnd_priv_to_dev(priv);
        int irq = ssi->info->irq;
 
-       rsnd_dma_quit(priv, rsnd_mod_to_dma(mod));
+       rsnd_dma_quit(rsnd_mod_to_dma(mod));
 
        /* PIO will request IRQ again */
        devm_free_irq(dev, irq, ssi);
@@ -554,14 +539,25 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
        return 0;
 }
 
-static char *rsnd_ssi_dma_name(struct rsnd_mod *mod)
+static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_mod *mod)
 {
-       return rsnd_ssi_use_busif(mod) ? "ssiu" : SSI_NAME;
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       int is_play = rsnd_io_is_play(io);
+       char *name;
+
+       if (rsnd_ssi_use_busif(mod))
+               name = is_play ? "rxu" : "txu";
+       else
+               name = is_play ? "rx" : "tx";
+
+       return rsnd_dma_request_channel(rsnd_ssi_of_node(priv),
+                                       mod, name);
 }
 
 static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
        .name   = SSI_NAME,
-       .dma_name = rsnd_ssi_dma_name,
+       .dma_req = rsnd_ssi_dma_req,
        .probe  = rsnd_ssi_dma_probe,
        .remove = rsnd_ssi_dma_remove,
        .init   = rsnd_ssi_init,
@@ -636,7 +632,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
        if (!of_data)
                return;
 
-       node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi");
+       node = rsnd_ssi_of_node(priv);
        if (!node)
                return;