]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
Merge remote-tracking branch 'asoc/topic/max9768' into asoc-next
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Sun, 9 Dec 2012 15:22:15 +0000 (00:22 +0900)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Sun, 9 Dec 2012 15:22:15 +0000 (00:22 +0900)
102 files changed:
Documentation/devicetree/bindings/misc/atmel-ssc.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/ak4104.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/cs4271.txt
Documentation/devicetree/bindings/sound/omap-abe-twl6040.txt
arch/arm/boot/dts/at91sam9260.dtsi
arch/arm/boot/dts/at91sam9263.dtsi
arch/arm/boot/dts/at91sam9g20ek_common.dtsi
arch/arm/boot/dts/at91sam9g45.dtsi
arch/arm/boot/dts/at91sam9x5.dtsi
arch/arm/mach-at91/at91rm9200.c
arch/arm/mach-at91/at91rm9200_devices.c
arch/arm/mach-at91/at91sam9260.c
arch/arm/mach-at91/at91sam9260_devices.c
arch/arm/mach-at91/at91sam9261.c
arch/arm/mach-at91/at91sam9261_devices.c
arch/arm/mach-at91/at91sam9263.c
arch/arm/mach-at91/at91sam9263_devices.c
arch/arm/mach-at91/at91sam9g45.c
arch/arm/mach-at91/at91sam9g45_devices.c
arch/arm/mach-at91/at91sam9rl.c
arch/arm/mach-at91/at91sam9rl_devices.c
arch/arm/mach-at91/at91sam9x5.c
arch/arm/mach-at91/board-sam9g20ek.c
arch/arm/mach-davinci/board-da850-evm.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/arizona-haptics.c [new file with mode: 0644]
drivers/mfd/arizona-core.c
drivers/misc/atmel-ssc.c
include/linux/atmel-ssc.h
include/linux/mfd/arizona/core.h
include/linux/mfd/arizona/pdata.h
include/linux/platform_data/davinci_asp.h
include/sound/cs4271.h
include/sound/sh_fsi.h
sound/soc/atmel/Kconfig
sound/soc/atmel/Makefile
sound/soc/atmel/atmel-pcm-dma.c [new file with mode: 0644]
sound/soc/atmel/atmel-pcm-pdc.c [new file with mode: 0644]
sound/soc/atmel/atmel-pcm.c
sound/soc/atmel/atmel-pcm.h
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/atmel/atmel_ssc_dai.h
sound/soc/atmel/sam9g20_wm8731.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ab8500-codec.c
sound/soc/codecs/ak4104.c
sound/soc/codecs/ak4535.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h
sound/soc/codecs/cs4271.c
sound/soc/codecs/da7210.c
sound/soc/codecs/da9055.c
sound/soc/codecs/jz4740.c
sound/soc/codecs/lm49453.c
sound/soc/codecs/max98088.c
sound/soc/codecs/wm2200.c
sound/soc/codecs/wm5100.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm_adsp.c [new file with mode: 0644]
sound/soc/codecs/wm_adsp.h [new file with mode: 0644]
sound/soc/codecs/wmfw.h [new file with mode: 0644]
sound/soc/davinci/davinci-evm.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/davinci/davinci-mcasp.h
sound/soc/davinci/davinci-pcm.c
sound/soc/davinci/davinci-pcm.h
sound/soc/fsl/Kconfig
sound/soc/fsl/Makefile
sound/soc/fsl/imx-pcm.c
sound/soc/fsl/imx-sgtl5000.c
sound/soc/fsl/p1022_rdk.c [new file with mode: 0644]
sound/soc/fsl/pcm030-audio-fabric.c
sound/soc/kirkwood/kirkwood-dma.c
sound/soc/kirkwood/kirkwood-i2s.c
sound/soc/kirkwood/kirkwood.h
sound/soc/mxs/mxs-saif.c
sound/soc/omap/mcbsp.c
sound/soc/omap/mcbsp.h
sound/soc/omap/omap-abe-twl6040.c
sound/soc/omap/omap-mcbsp.c
sound/soc/omap/zoom2.c
sound/soc/sh/fsi.c
sound/soc/soc-cache.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-dmaengine-pcm.c
sound/soc/soc-jack.c
sound/soc/soc-pcm.c
sound/soc/tegra/tegra20_das.c
sound/soc/tegra/tegra20_i2s.c
sound/soc/tegra/tegra20_spdif.c
sound/soc/tegra/tegra30_ahub.c
sound/soc/tegra/tegra30_i2s.c
sound/soc/tegra/tegra_alc5632.c
sound/soc/tegra/tegra_wm8753.c
sound/soc/tegra/tegra_wm8903.c
sound/soc/tegra/trimslice.c

diff --git a/Documentation/devicetree/bindings/misc/atmel-ssc.txt b/Documentation/devicetree/bindings/misc/atmel-ssc.txt
new file mode 100644 (file)
index 0000000..38e51ad
--- /dev/null
@@ -0,0 +1,15 @@
+* Atmel SSC driver.
+
+Required properties:
+- compatible: "atmel,at91rm9200-ssc" or "atmel,at91sam9g45-ssc"
+       - atmel,at91rm9200-ssc: support pdc transfer
+       - atmel,at91sam9g45-ssc: support dma transfer
+- reg: Should contain SSC registers location and length
+- interrupts: Should contain SSC interrupt
+
+Example:
+ssc0: ssc@fffbc000 {
+       compatible = "atmel,at91rm9200-ssc";
+       reg = <0xfffbc000 0x4000>;
+       interrupts = <14 4 5>;
+};
diff --git a/Documentation/devicetree/bindings/sound/ak4104.txt b/Documentation/devicetree/bindings/sound/ak4104.txt
new file mode 100644 (file)
index 0000000..b902ee3
--- /dev/null
@@ -0,0 +1,22 @@
+AK4104 S/PDIF transmitter
+
+This device supports SPI mode only.
+
+Required properties:
+
+  - compatible : "asahi-kasei,ak4104"
+
+  - reg : The chip select number on the SPI bus
+
+Optional properties:
+
+  - reset-gpio : a GPIO spec for the reset pin. If specified, it will be
+                deasserted before communication to the device starts.
+
+Example:
+
+spdif: ak4104@0 {
+       compatible = "asahi-kasei,ak4104";
+       reg = <0>;
+       spi-max-frequency = <5000000>;
+};
diff --git a/Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt b/Documentation/devicetree/bindings/sound/atmel-at91sam9g20ek-wm8731-audio.txt
new file mode 100644 (file)
index 0000000..9c5a994
--- /dev/null
@@ -0,0 +1,26 @@
+* Atmel at91sam9g20ek wm8731 audio complex
+
+Required properties:
+  - compatible: "atmel,at91sam9g20ek-wm8731-audio"
+  - atmel,model: The user-visible name of this sound complex.
+  - atmel,audio-routing: A list of the connections between audio components.
+  - atmel,ssc-controller: The phandle of the SSC controller
+  - atmel,audio-codec: The phandle of the WM8731 audio codec
+Optional properties:
+  - pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt
+
+Example:
+sound {
+       compatible = "atmel,at91sam9g20ek-wm8731-audio";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_pck0_as_mck>;
+
+       atmel,model = "wm8731 @ AT91SAMG20EK";
+
+       atmel,audio-routing =
+               "Ext Spk", "LHPOUT",
+               "Int MIC", "MICIN";
+
+       atmel,ssc-controller = <&ssc0>;
+       atmel,audio-codec = <&wm8731>;
+};
index c81b5fd5a5bc80459a1b2d626c9f20b5a86bfbe9..a850fb9c88eab2156ec89f0c76d8ca59073540f8 100644 (file)
@@ -18,6 +18,8 @@ Optional properties:
 
  - reset-gpio:         a GPIO spec to define which pin is connected to the chip's
                !RESET pin
+ - cirrus,amuteb-eq-bmutec:    When given, the Codec's AMUTEB=BMUTEC flag
+                               is enabled.
 
 Examples:
 
index 65dec876cb2d792813abc050833950ef48b53a66..fd40c852d7c7e18c12e5fabc49f0b141614267ac 100644 (file)
@@ -12,7 +12,7 @@ Required properties:
 
 Optional properties:
 - ti,dmic: phandle for the OMAP dmic node if the machine have it connected
-- ti,jack_detection: Need to be set to <1> if the board capable to detect jack
+- ti,jack_detection: Need to be present if the board capable to detect jack
   insertion, removal.
 
 Available audio endpoints for the audio-routing table:
@@ -59,7 +59,7 @@ sound {
        compatible = "ti,abe-twl6040";
        ti,model = "SDP4430";
 
-       ti,jack-detection = <1>;
+       ti,jack-detection;
        ti,mclk-freq = <38400000>;
 
        ti,mcpdm = <&mcpdm>;
index d410581a5a859901b32a49a1f037356c9f22e294..aaa42d8d4f8895233bdaddc00b3bdf69775e0379 100644 (file)
@@ -29,6 +29,7 @@
                tcb0 = &tcb0;
                tcb1 = &tcb1;
                i2c0 = &i2c0;
+               ssc0 = &ssc0;
        };
        cpus {
                cpu@0 {
                                status = "disabled";
                        };
 
+                       ssc0: ssc@fffbc000 {
+                               compatible = "atmel,at91rm9200-ssc";
+                               reg = <0xfffbc000 0x4000>;
+                               interrupts = <14 4 5>;
+                               status = "disable";
+                       };
+
                        adc0: adc@fffe0000 {
                                compatible = "atmel,at91sam9260-adc";
                                reg = <0xfffe0000 0x100>;
index 3e6e5c1abbf37184a44237e464080a52f4312e9f..3b721ee59b109500b688bebff12e098f0aaf05ce 100644 (file)
@@ -25,6 +25,8 @@
                gpio4 = &pioE;
                tcb0 = &tcb0;
                i2c0 = &i2c0;
+               ssc0 = &ssc0;
+               ssc1 = &ssc1;
        };
        cpus {
                cpu@0 {
                                status = "disabled";
                        };
 
+                       ssc0: ssc@fff98000 {
+                               compatible = "atmel,at91rm9200-ssc";
+                               reg = <0xfff98000 0x4000>;
+                               interrupts = <16 4 5>;
+                               status = "disable";
+                       };
+
+                       ssc1: ssc@fff9c000 {
+                               compatible = "atmel,at91rm9200-ssc";
+                               reg = <0xfff9c000 0x4000>;
+                               interrupts = <17 4 5>;
+                               status = "disable";
+                       };
+
                        macb0: ethernet@fffbc000 {
                                compatible = "cdns,at32ap7000-macb", "cdns,macb";
                                reg = <0xfffbc000 0x100>;
index e6391a4e6649cc3787ba14d7ed1d190b2d932964..2dcec8de759f97c31780027614f99866bb0c8a58 100644 (file)
 
        ahb {
                apb {
+                       pinctrl@fffff400 {
+                               board {
+                                       pinctrl_pck0_as_mck: pck0_as_mck {
+                                               atmel,pins =
+                                                       <2 1 0x2 0x0>;  /* PC1 periph B */
+                                       };
+
+                               };
+                       };
+
                        dbgu: serial@fffff200 {
                                status = "okay";
                        };
                                atmel,vbus-gpio = <&pioC 5 0>;
                                status = "okay";
                        };
+
+                       ssc0: ssc@fffbc000 {
+                               status = "okay";
+                               pinctrl-0 = <&pinctrl_ssc0_tx>;
+                       };
                };
 
                nand0: nand@40000000 {
                        reg = <0x50>;
                };
 
-               wm8731@1b {
+               wm8731: wm8731@1b {
                        compatible = "wm8731";
                        reg = <0x1b>;
                };
                        gpio-key,wakeup;
                };
        };
+
+       sound {
+               compatible = "atmel,at91sam9g20ek-wm8731-audio";
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_pck0_as_mck>;
+
+               atmel,model = "wm8731 @ AT91SAMG20EK";
+
+               atmel,audio-routing =
+                       "Ext Spk", "LHPOUT",
+                       "Int Mic", "MICIN";
+
+               atmel,ssc-controller = <&ssc0>;
+               atmel,audio-codec = <&wm8731>;
+       };
 };
index 3add030d61f8651fcf5a0476b11423897de71560..acfa207162ff581eaaf063c4d3154a6f658ea177 100644 (file)
@@ -31,6 +31,8 @@
                tcb1 = &tcb1;
                i2c0 = &i2c0;
                i2c1 = &i2c1;
+               ssc0 = &ssc0;
+               ssc1 = &ssc1;
        };
        cpus {
                cpu@0 {
                                status = "disabled";
                        };
 
+                       ssc0: ssc@fff9c000 {
+                               compatible = "atmel,at91sam9g45-ssc";
+                               reg = <0xfff9c000 0x4000>;
+                               interrupts = <16 4 5>;
+                               status = "disable";
+                       };
+
+                       ssc1: ssc@fffa0000 {
+                               compatible = "atmel,at91sam9g45-ssc";
+                               reg = <0xfffa0000 0x4000>;
+                               interrupts = <17 4 5>;
+                               status = "disable";
+                       };
+
                        adc0: adc@fffb0000 {
                                compatible = "atmel,at91sam9260-adc";
                                reg = <0xfffb0000 0x100>;
index 03fc136421c5e31b01649c9caacdaf134bef4560..69667d0ac347f142a51c5acc704e731763ec2144 100644 (file)
@@ -30,6 +30,7 @@
                i2c0 = &i2c0;
                i2c1 = &i2c1;
                i2c2 = &i2c2;
+               ssc0 = &ssc0;
        };
        cpus {
                cpu@0 {
                                interrupts = <1 4 7>;
                        };
 
+                       ssc0: ssc@f0010000 {
+                               compatible = "atmel,at91sam9g45-ssc";
+                               reg = <0xf0010000 0x4000>;
+                               interrupts = <28 4 5>;
+                               status = "disable";
+                       };
+
                        tcb0: timer@f8008000 {
                                compatible = "atmel,at91sam9x5-tcb";
                                reg = <0xf8008000 0x100>;
index 5269825194a8a41c620ffa3da9f5a2cf5269bcb5..af47c75db513b58a88be84e52f2e681ec863908c 100644 (file)
@@ -184,9 +184,12 @@ static struct clk_lookup periph_clocks_lookups[] = {
        CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk),
        CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk),
        CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fffd0000.ssc", &ssc0_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fffd4000.ssc", &ssc1_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fffd8000.ssc", &ssc2_clk),
        CLKDEV_CON_DEV_ID(NULL, "i2c-at91rm9200.0", &twi_clk),
        /* fake hclk clock */
        CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
index 3cee0e6ea7c3c126ec3b319497b01cf335f3f16a..9e76427aaec29a0065963e8633196f598bc9d2bc 100644 (file)
@@ -752,7 +752,7 @@ static struct resource ssc0_resources[] = {
 };
 
 static struct platform_device at91rm9200_ssc0_device = {
-       .name   = "ssc",
+       .name   = "at91rm9200_ssc",
        .id     = 0,
        .dev    = {
                .dma_mask               = &ssc0_dmamask,
@@ -794,7 +794,7 @@ static struct resource ssc1_resources[] = {
 };
 
 static struct platform_device at91rm9200_ssc1_device = {
-       .name   = "ssc",
+       .name   = "at91rm9200_ssc",
        .id     = 1,
        .dev    = {
                .dma_mask               = &ssc1_dmamask,
@@ -836,7 +836,7 @@ static struct resource ssc2_resources[] = {
 };
 
 static struct platform_device at91rm9200_ssc2_device = {
-       .name   = "ssc",
+       .name   = "at91rm9200_ssc",
        .id     = 2,
        .dev    = {
                .dma_mask               = &ssc2_dmamask,
index f8202615f4a867f32ffe970cde12f0b6887d2375..a41eb3d23f68dc282cc51117427ea446db7b97e5 100644 (file)
@@ -210,7 +210,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
        CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk),
        CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk),
        CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc_clk),
        CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk),
        CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi_clk),
        /* more usart lookup table for DT entries */
index 414bd855fb0cb7fd1d0295012016937175d9c449..e67cfa2acbe00d05fe773630113b3331cc4661eb 100644 (file)
@@ -742,7 +742,7 @@ static struct resource ssc_resources[] = {
 };
 
 static struct platform_device at91sam9260_ssc_device = {
-       .name   = "ssc",
+       .name   = "at91rm9200_ssc",
        .id     = 0,
        .dev    = {
                .dma_mask               = &ssc_dmamask,
index 04295c04b3e03cb99d31aa2f226908ab2a975ec4..7fcbe05833426b7b648a145b796619f4e47d7d5b 100644 (file)
@@ -174,9 +174,12 @@ static struct clk_lookup periph_clocks_lookups[] = {
        CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
        CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk),
        CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc0_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc1_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc2_clk),
        CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0),
        CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9261.0", &twi_clk),
        CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi_clk),
index cd604aad8e963c217301df7fa421637d1b0315a3..a27d9dd0faa4a99b9a07e5416c7f0cc96ee18f73 100644 (file)
@@ -706,7 +706,7 @@ static struct resource ssc0_resources[] = {
 };
 
 static struct platform_device at91sam9261_ssc0_device = {
-       .name   = "ssc",
+       .name   = "at91rm9200_ssc",
        .id     = 0,
        .dev    = {
                .dma_mask               = &ssc0_dmamask,
@@ -748,7 +748,7 @@ static struct resource ssc1_resources[] = {
 };
 
 static struct platform_device at91sam9261_ssc1_device = {
-       .name   = "ssc",
+       .name   = "at91rm9200_ssc",
        .id     = 1,
        .dev    = {
                .dma_mask               = &ssc1_dmamask,
@@ -790,7 +790,7 @@ static struct resource ssc2_resources[] = {
 };
 
 static struct platform_device at91sam9261_ssc2_device = {
-       .name   = "ssc",
+       .name   = "at91rm9200_ssc",
        .id     = 2,
        .dev    = {
                .dma_mask               = &ssc2_dmamask,
index d6f9c23927c48c92202776d049a582af01b8f520..c0f4c8c1f4ed59ad31cd004a317e7f436181285c 100644 (file)
@@ -186,8 +186,10 @@ static struct clk *periph_clocks[] __initdata = {
 static struct clk_lookup periph_clocks_lookups[] = {
        /* One additional fake clock for macb_hclk */
        CLKDEV_CON_ID("hclk", &macb_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fff98000.ssc", &ssc0_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc1_clk),
        CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk),
        CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk),
        CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
index 9c61e59a2104e7f0399fc0dca4ea926d7136d4f9..8215839f2d540a46db98a83eeddf4228122c75b8 100644 (file)
@@ -1199,7 +1199,7 @@ static struct resource ssc0_resources[] = {
 };
 
 static struct platform_device at91sam9263_ssc0_device = {
-       .name   = "ssc",
+       .name   = "at91rm9200_ssc",
        .id     = 0,
        .dev    = {
                .dma_mask               = &ssc0_dmamask,
@@ -1241,7 +1241,7 @@ static struct resource ssc1_resources[] = {
 };
 
 static struct platform_device at91sam9263_ssc1_device = {
-       .name   = "ssc",
+       .name   = "at91rm9200_ssc",
        .id     = 1,
        .dev    = {
                .dma_mask               = &ssc1_dmamask,
index 84af1b506d92a0b1e9e88c885b1d57be95fbf40b..a4282d3742bf11098fed3a8101285b94eafeddbe 100644 (file)
@@ -239,8 +239,10 @@ static struct clk_lookup periph_clocks_lookups[] = {
        CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tcb0_clk),
        CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi0_clk),
        CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.1", &twi1_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.0", &ssc0_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.1", &ssc1_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc0_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fffa0000.ssc", &ssc1_clk),
        CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk),
        CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk),
        CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk),
index fcd233cb33d25d68417a992c3b09fac45e63501a..d26474a97fec884e1ef0338400cbf68b00d0ceae 100644 (file)
@@ -1459,7 +1459,7 @@ static struct resource ssc0_resources[] = {
 };
 
 static struct platform_device at91sam9g45_ssc0_device = {
-       .name   = "ssc",
+       .name   = "at91sam9g45_ssc",
        .id     = 0,
        .dev    = {
                .dma_mask               = &ssc0_dmamask,
@@ -1501,7 +1501,7 @@ static struct resource ssc1_resources[] = {
 };
 
 static struct platform_device at91sam9g45_ssc1_device = {
-       .name   = "ssc",
+       .name   = "at91sam9g45_ssc",
        .id     = 1,
        .dev    = {
                .dma_mask               = &ssc1_dmamask,
index 72e908412222615134fc0530cc339348093f76be..b683fdc699f12e1a97dc3c60fdf07f9abd7a02f1 100644 (file)
@@ -184,8 +184,10 @@ static struct clk_lookup periph_clocks_lookups[] = {
        CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
        CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk),
        CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
-       CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
+       CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc0_clk),
+       CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc1_clk),
        CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk),
        CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk),
        CLKDEV_CON_ID("pioA", &pioA_clk),
index 5047bdc92adfdb79395143d973468862219d33c4..b656110e8afe6fec0a55fa15edc3084301f54409 100644 (file)
@@ -832,7 +832,7 @@ static struct resource ssc0_resources[] = {
 };
 
 static struct platform_device at91sam9rl_ssc0_device = {
-       .name   = "ssc",
+       .name   = "at91rm9200_ssc",
        .id     = 0,
        .dev    = {
                .dma_mask               = &ssc0_dmamask,
@@ -874,7 +874,7 @@ static struct resource ssc1_resources[] = {
 };
 
 static struct platform_device at91sam9rl_ssc1_device = {
-       .name   = "ssc",
+       .name   = "at91rm9200_ssc",
        .id     = 1,
        .dev    = {
                .dma_mask               = &ssc1_dmamask,
index e5035380dcbce16e806fb460625fd73d9a3c1153..18fbbb27f97fcbb7a191867af7edaa0c2c1f4e8a 100644 (file)
@@ -231,6 +231,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
        CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk),
        CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk),
        CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk),
+       CLKDEV_CON_DEV_ID("pclk", "f0010000.ssc", &ssc_clk),
        CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk),
        CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk),
        CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk),
index 3ab2b86a3762a67da9b808386d39c51fb8cd2544..ebdbf42c02c19db171239a3d84add8da572135af 100644 (file)
@@ -353,6 +353,16 @@ static struct i2c_board_info __initdata ek_i2c_devices[] = {
         },
 };
 
+static struct platform_device sam9g20ek_audio_device = {
+       .name   = "at91sam9g20ek-audio",
+       .id     = -1,
+};
+
+static void __init ek_add_device_audio(void)
+{
+       platform_device_register(&sam9g20ek_audio_device);
+}
+
 
 static void __init ek_board_init(void)
 {
@@ -394,6 +404,7 @@ static void __init ek_board_init(void)
        at91_set_B_periph(AT91_PIN_PC1, 0);
        /* SSC (for WM8731) */
        at91_add_device_ssc(AT91SAM9260_ID_SSC, ATMEL_SSC_TX);
+       ek_add_device_audio();
 }
 
 MACHINE_START(AT91SAM9G20EK, "Atmel AT91SAM9G20-EK")
index 32ee3f89596738d988a00105c0358d4ff1a61f1b..d9bc3fa7bb228a8c9e7f2feca9bac83b88ac16f5 100644 (file)
@@ -762,16 +762,19 @@ static u8 da850_iis_serializer_direction[] = {
 };
 
 static struct snd_platform_data da850_evm_snd_data = {
-       .tx_dma_offset  = 0x2000,
-       .rx_dma_offset  = 0x2000,
-       .op_mode        = DAVINCI_MCASP_IIS_MODE,
-       .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction),
-       .tdm_slots      = 2,
-       .serial_dir     = da850_iis_serializer_direction,
-       .asp_chan_q     = EVENTQ_0,
-       .version        = MCASP_VERSION_2,
-       .txnumevt       = 1,
-       .rxnumevt       = 1,
+       .tx_dma_offset          = 0x2000,
+       .rx_dma_offset          = 0x2000,
+       .op_mode                = DAVINCI_MCASP_IIS_MODE,
+       .num_serializer         = ARRAY_SIZE(da850_iis_serializer_direction),
+       .tdm_slots              = 2,
+       .serial_dir             = da850_iis_serializer_direction,
+       .asp_chan_q             = EVENTQ_0,
+       .ram_chan_q             = EVENTQ_1,
+       .version                = MCASP_VERSION_2,
+       .txnumevt               = 1,
+       .rxnumevt               = 1,
+       .sram_size_playback     = SZ_8K,
+       .sram_size_capture      = SZ_8K,
 };
 
 static const short da850_evm_mcasp_pins[] __initconst = {
@@ -1509,6 +1512,7 @@ static __init void da850_evm_init(void)
                pr_warning("da850_evm_init: mcasp mux setup failed: %d\n",
                                ret);
 
+       da850_evm_snd_data.sram_pool = sram_get_gen_pool();
        da8xx_register_mcasp(0, &da850_evm_snd_data);
 
        ret = davinci_cfg_reg_list(da850_lcdcntl_pins);
index 7c0f1ecfdd7a3ff88fed02671e1cf0fba5847b24..104a7c3153c0c4ce5fd695403bfb8a263da992d7 100644 (file)
@@ -72,6 +72,16 @@ config INPUT_AD714X_SPI
          To compile this driver as a module, choose M here: the
          module will be called ad714x-spi.
 
+config INPUT_ARIZONA_HAPTICS
+       tristate "Arizona haptics support"
+       depends on MFD_ARIZONA && SND_SOC
+       select INPUT_FF_MEMLESS
+       help
+         Say Y to enable support for the haptics module in Arizona CODECs.
+
+         To compile this driver as a module, choose M here: the
+         module will be called arizona-haptics.
+
 config INPUT_BMA150
        tristate "BMA150/SMB380 acceleration sensor support"
        depends on I2C
index 83fe6f5b77d120e9b3194325f0fc83879a474a0a..5ea769eda999ff1fa35954bdc6bf467c22ebbf99 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_INPUT_ADXL34X)           += adxl34x.o
 obj-$(CONFIG_INPUT_ADXL34X_I2C)                += adxl34x-i2c.o
 obj-$(CONFIG_INPUT_ADXL34X_SPI)                += adxl34x-spi.o
 obj-$(CONFIG_INPUT_APANEL)             += apanel.o
+obj-$(CONFIG_INPUT_ARIZONA_HAPTICS)    += arizona-haptics.o
 obj-$(CONFIG_INPUT_ATI_REMOTE2)                += ati_remote2.o
 obj-$(CONFIG_INPUT_ATLAS_BTNS)         += atlas_btns.o
 obj-$(CONFIG_INPUT_BFIN_ROTARY)                += bfin_rotary.o
diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c
new file mode 100644 (file)
index 0000000..7a04f54
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Arizona haptics driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+
+struct arizona_haptics {
+       struct arizona *arizona;
+       struct input_dev *input_dev;
+       struct work_struct work;
+
+       struct mutex mutex;
+       u8 intensity;
+};
+
+static void arizona_haptics_work(struct work_struct *work)
+{
+       struct arizona_haptics *haptics = container_of(work,
+                                                      struct arizona_haptics,
+                                                      work);
+       struct arizona *arizona = haptics->arizona;
+       struct mutex *dapm_mutex = &arizona->dapm->card->dapm_mutex;
+       int ret;
+
+       if (!haptics->arizona->dapm) {
+               dev_err(arizona->dev, "No DAPM context\n");
+               return;
+       }
+
+       if (haptics->intensity) {
+               ret = regmap_update_bits(arizona->regmap,
+                                        ARIZONA_HAPTICS_PHASE_2_INTENSITY,
+                                        ARIZONA_PHASE2_INTENSITY_MASK,
+                                        haptics->intensity);
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Failed to set intensity: %d\n",
+                               ret);
+                       return;
+               }
+
+               /* This enable sequence will be a noop if already enabled */
+               ret = regmap_update_bits(arizona->regmap,
+                                        ARIZONA_HAPTICS_CONTROL_1,
+                                        ARIZONA_HAP_CTRL_MASK,
+                                        1 << ARIZONA_HAP_CTRL_SHIFT);
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Failed to start haptics: %d\n",
+                               ret);
+                       return;
+               }
+
+               mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+               ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS");
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Failed to start HAPTICS: %d\n",
+                               ret);
+                       mutex_unlock(dapm_mutex);
+                       return;
+               }
+
+               ret = snd_soc_dapm_sync(arizona->dapm);
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
+                               ret);
+                       mutex_unlock(dapm_mutex);
+                       return;
+               }
+
+               mutex_unlock(dapm_mutex);
+
+       } else {
+               /* This disable sequence will be a noop if already enabled */
+               mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+               ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS");
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n",
+                               ret);
+                       mutex_unlock(dapm_mutex);
+                       return;
+               }
+
+               ret = snd_soc_dapm_sync(arizona->dapm);
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
+                               ret);
+                       mutex_unlock(dapm_mutex);
+                       return;
+               }
+
+               mutex_unlock(dapm_mutex);
+
+               ret = regmap_update_bits(arizona->regmap,
+                                        ARIZONA_HAPTICS_CONTROL_1,
+                                        ARIZONA_HAP_CTRL_MASK,
+                                        1 << ARIZONA_HAP_CTRL_SHIFT);
+               if (ret != 0) {
+                       dev_err(arizona->dev, "Failed to stop haptics: %d\n",
+                               ret);
+                       return;
+               }
+       }
+}
+
+static int arizona_haptics_play(struct input_dev *input, void *data,
+                               struct ff_effect *effect)
+{
+       struct arizona_haptics *haptics = input_get_drvdata(input);
+       struct arizona *arizona = haptics->arizona;
+
+       if (!arizona->dapm) {
+               dev_err(arizona->dev, "No DAPM context\n");
+               return -EBUSY;
+       }
+
+       if (effect->u.rumble.strong_magnitude) {
+               /* Scale the magnitude into the range the device supports */
+               if (arizona->pdata.hap_act) {
+                       haptics->intensity =
+                               effect->u.rumble.strong_magnitude >> 9;
+                       if (effect->direction < 0x8000)
+                               haptics->intensity += 0x7f;
+               } else {
+                       haptics->intensity =
+                               effect->u.rumble.strong_magnitude >> 8;
+               }
+       } else {
+               haptics->intensity = 0;
+       }
+
+       schedule_work(&haptics->work);
+
+       return 0;
+}
+
+static void arizona_haptics_close(struct input_dev *input)
+{
+       struct arizona_haptics *haptics = input_get_drvdata(input);
+       struct mutex *dapm_mutex = &haptics->arizona->dapm->card->dapm_mutex;
+
+       cancel_work_sync(&haptics->work);
+
+       mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+       if (haptics->arizona->dapm)
+               snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS");
+
+       mutex_unlock(dapm_mutex);
+}
+
+static int arizona_haptics_probe(struct platform_device *pdev)
+{
+       struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+       struct arizona_haptics *haptics;
+       int ret;
+
+       haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL);
+       if (!haptics)
+               return -ENOMEM;
+
+       haptics->arizona = arizona;
+
+       ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1,
+                                ARIZONA_HAP_ACT, arizona->pdata.hap_act);
+       if (ret != 0) {
+               dev_err(arizona->dev, "Failed to set haptics actuator: %d\n",
+                       ret);
+               return ret;
+       }
+
+       INIT_WORK(&haptics->work, arizona_haptics_work);
+
+       haptics->input_dev = input_allocate_device();
+       if (haptics->input_dev == NULL) {
+               dev_err(arizona->dev, "Failed to allocate input device\n");
+               return -ENOMEM;
+       }
+
+       input_set_drvdata(haptics->input_dev, haptics);
+
+       haptics->input_dev->name = "arizona:haptics";
+       haptics->input_dev->dev.parent = pdev->dev.parent;
+       haptics->input_dev->close = arizona_haptics_close;
+       __set_bit(FF_RUMBLE, haptics->input_dev->ffbit);
+
+       ret = input_ff_create_memless(haptics->input_dev, NULL,
+                                     arizona_haptics_play);
+       if (ret < 0) {
+               dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n",
+                       ret);
+               goto err_ialloc;
+       }
+
+       ret = input_register_device(haptics->input_dev);
+       if (ret < 0) {
+               dev_err(arizona->dev, "couldn't register input device: %d\n",
+                       ret);
+               goto err_iff;
+       }
+
+       platform_set_drvdata(pdev, haptics);
+
+       return 0;
+
+err_iff:
+       if (haptics->input_dev)
+               input_ff_destroy(haptics->input_dev);
+err_ialloc:
+       input_free_device(haptics->input_dev);
+
+       return ret;
+}
+
+static int arizona_haptics_remove(struct platform_device *pdev)
+{
+       struct arizona_haptics *haptics = platform_get_drvdata(pdev);
+
+       input_unregister_device(haptics->input_dev);
+
+       return 0;
+}
+
+static struct platform_driver arizona_haptics_driver = {
+       .probe          = arizona_haptics_probe,
+       .remove         = arizona_haptics_remove,
+       .driver         = {
+               .name   = "arizona-haptics",
+               .owner  = THIS_MODULE,
+       },
+};
+module_platform_driver(arizona_haptics_driver);
+
+MODULE_ALIAS("platform:arizona-haptics");
+MODULE_DESCRIPTION("Arizona haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
index 1b48f2094806c75fa8914657978f1b7bf814ae93..75619711a9e7904000f89b30769a084b4094a27a 100644 (file)
@@ -272,6 +272,7 @@ static struct mfd_cell early_devs[] = {
 static struct mfd_cell wm5102_devs[] = {
        { .name = "arizona-extcon" },
        { .name = "arizona-gpio" },
+       { .name = "arizona-haptics" },
        { .name = "arizona-micsupp" },
        { .name = "arizona-pwm" },
        { .name = "wm5102-codec" },
@@ -280,6 +281,7 @@ static struct mfd_cell wm5102_devs[] = {
 static struct mfd_cell wm5110_devs[] = {
        { .name = "arizona-extcon" },
        { .name = "arizona-gpio" },
+       { .name = "arizona-haptics" },
        { .name = "arizona-micsupp" },
        { .name = "arizona-pwm" },
        { .name = "wm5110-codec" },
index 5bb1877810749da2581aff03c6d7d50c18b875c3..d07a9eda7fff4a29aebc43892658b339b4ebb196 100644 (file)
@@ -18,6 +18,8 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 
+#include <linux/of.h>
+
 /* Serialize access to ssc_list and user count */
 static DEFINE_SPINLOCK(user_lock);
 static LIST_HEAD(ssc_list);
@@ -29,7 +31,13 @@ struct ssc_device *ssc_request(unsigned int ssc_num)
 
        spin_lock(&user_lock);
        list_for_each_entry(ssc, &ssc_list, list) {
-               if (ssc->pdev->id == ssc_num) {
+               if (ssc->pdev->dev.of_node) {
+                       if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc")
+                               == ssc_num) {
+                               ssc_valid = 1;
+                               break;
+                       }
+               } else if (ssc->pdev->id == ssc_num) {
                        ssc_valid = 1;
                        break;
                }
@@ -68,39 +76,93 @@ void ssc_free(struct ssc_device *ssc)
 }
 EXPORT_SYMBOL(ssc_free);
 
-static int __init ssc_probe(struct platform_device *pdev)
+static struct atmel_ssc_platform_data at91rm9200_config = {
+       .use_dma = 0,
+};
+
+static struct atmel_ssc_platform_data at91sam9g45_config = {
+       .use_dma = 1,
+};
+
+static const struct platform_device_id atmel_ssc_devtypes[] = {
+       {
+               .name = "at91rm9200_ssc",
+               .driver_data = (unsigned long) &at91rm9200_config,
+       }, {
+               .name = "at91sam9g45_ssc",
+               .driver_data = (unsigned long) &at91sam9g45_config,
+       }, {
+               /* sentinel */
+       }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_ssc_dt_ids[] = {
+       {
+               .compatible = "atmel,at91rm9200-ssc",
+               .data = &at91rm9200_config,
+       }, {
+               .compatible = "atmel,at91sam9g45-ssc",
+               .data = &at91sam9g45_config,
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, atmel_ssc_dt_ids);
+#endif
+
+static inline const struct atmel_ssc_platform_data * __init
+       atmel_ssc_get_driver_data(struct platform_device *pdev)
+{
+       if (pdev->dev.of_node) {
+               const struct of_device_id *match;
+               match = of_match_node(atmel_ssc_dt_ids, pdev->dev.of_node);
+               if (match == NULL)
+                       return NULL;
+               return match->data;
+       }
+
+       return (struct atmel_ssc_platform_data *)
+               platform_get_device_id(pdev)->driver_data;
+}
+
+static int ssc_probe(struct platform_device *pdev)
 {
-       int retval = 0;
        struct resource *regs;
        struct ssc_device *ssc;
+       const struct atmel_ssc_platform_data *plat_dat;
 
-       ssc = kzalloc(sizeof(struct ssc_device), GFP_KERNEL);
+       ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL);
        if (!ssc) {
                dev_dbg(&pdev->dev, "out of memory\n");
-               retval = -ENOMEM;
-               goto out;
+               return -ENOMEM;
        }
 
+       ssc->pdev = pdev;
+
+       plat_dat = atmel_ssc_get_driver_data(pdev);
+       if (!plat_dat)
+               return -ENODEV;
+       ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat;
+
        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!regs) {
                dev_dbg(&pdev->dev, "no mmio resource defined\n");
-               retval = -ENXIO;
-               goto out_free;
+               return -ENXIO;
        }
 
-       ssc->clk = clk_get(&pdev->dev, "pclk");
-       if (IS_ERR(ssc->clk)) {
-               dev_dbg(&pdev->dev, "no pclk clock defined\n");
-               retval = -ENXIO;
-               goto out_free;
-       }
-
-       ssc->pdev = pdev;
-       ssc->regs = ioremap(regs->start, resource_size(regs));
+       ssc->regs = devm_request_and_ioremap(&pdev->dev, regs);
        if (!ssc->regs) {
                dev_dbg(&pdev->dev, "ioremap failed\n");
-               retval = -EINVAL;
-               goto out_clk;
+               return -EINVAL;
+       }
+
+       ssc->phybase = regs->start;
+
+       ssc->clk = devm_clk_get(&pdev->dev, "pclk");
+       if (IS_ERR(ssc->clk)) {
+               dev_dbg(&pdev->dev, "no pclk clock defined\n");
+               return -ENXIO;
        }
 
        /* disable all interrupts */
@@ -112,8 +174,7 @@ static int __init ssc_probe(struct platform_device *pdev)
        ssc->irq = platform_get_irq(pdev, 0);
        if (!ssc->irq) {
                dev_dbg(&pdev->dev, "could not get irq\n");
-               retval = -ENXIO;
-               goto out_unmap;
+               return -ENXIO;
        }
 
        spin_lock(&user_lock);
@@ -125,16 +186,7 @@ static int __init ssc_probe(struct platform_device *pdev)
        dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n",
                        ssc->regs, ssc->irq);
 
-       goto out;
-
-out_unmap:
-       iounmap(ssc->regs);
-out_clk:
-       clk_put(ssc->clk);
-out_free:
-       kfree(ssc);
-out:
-       return retval;
+       return 0;
 }
 
 static int __devexit ssc_remove(struct platform_device *pdev)
@@ -142,34 +194,23 @@ static int __devexit ssc_remove(struct platform_device *pdev)
        struct ssc_device *ssc = platform_get_drvdata(pdev);
 
        spin_lock(&user_lock);
-       iounmap(ssc->regs);
-       clk_put(ssc->clk);
        list_del(&ssc->list);
-       kfree(ssc);
        spin_unlock(&user_lock);
 
        return 0;
 }
 
 static struct platform_driver ssc_driver = {
-       .remove         = __devexit_p(ssc_remove),
        .driver         = {
                .name           = "ssc",
                .owner          = THIS_MODULE,
+               .of_match_table = of_match_ptr(atmel_ssc_dt_ids),
        },
+       .id_table       = atmel_ssc_devtypes,
+       .probe          = ssc_probe,
+       .remove         = __devexit_p(ssc_remove),
 };
-
-static int __init ssc_init(void)
-{
-       return platform_driver_probe(&ssc_driver, ssc_probe);
-}
-module_init(ssc_init);
-
-static void __exit ssc_exit(void)
-{
-       platform_driver_unregister(&ssc_driver);
-}
-module_exit(ssc_exit);
+module_platform_driver(ssc_driver);
 
 MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
 MODULE_DESCRIPTION("SSC driver for Atmel AVR32 and AT91");
index 4eb31752e2b77592e8a2fdbf3fde779d4851504d..deb0ae58b99bb3724bee4376b0a376b563c29cff 100644 (file)
@@ -5,10 +5,16 @@
 #include <linux/list.h>
 #include <linux/io.h>
 
+struct atmel_ssc_platform_data {
+       int                     use_dma;
+};
+
 struct ssc_device {
        struct list_head        list;
+       resource_size_t         phybase;
        void __iomem            *regs;
        struct platform_device  *pdev;
+       struct atmel_ssc_platform_data *pdata;
        struct clk              *clk;
        int                     user;
        int                     irq;
index dd231ac0bb1fd8ac678d475404bc95ebaa104cec..a580363a7d29723e6656cbed8d9d94b4b1801bea 100644 (file)
@@ -78,6 +78,8 @@ enum arizona_type {
 
 #define ARIZONA_NUM_IRQ                   50
 
+struct snd_soc_dapm_context;
+
 struct arizona {
        struct regmap *regmap;
        struct device *dev;
@@ -98,6 +100,8 @@ struct arizona {
 
        struct mutex clk_lock;
        int clk32k_ref;
+
+       struct snd_soc_dapm_context *dapm;
 };
 
 int arizona_clk32k_enable(struct arizona *arizona);
index 7ab442905a57b209b10c042cf141ac2b2ba2275e..8b1d1daaae16c27e59d037f46899a1f6c631acc4 100644 (file)
@@ -62,6 +62,9 @@
 
 #define ARIZONA_MAX_OUTPUT 6
 
+#define ARIZONA_HAP_ACT_ERM 0
+#define ARIZONA_HAP_ACT_LRA 2
+
 #define ARIZONA_MAX_PDM_SPK 2
 
 struct regulator_init_data;
@@ -114,6 +117,9 @@ struct arizona_pdata {
 
        /** PDM speaker format */
        unsigned int spk_fmt[ARIZONA_MAX_PDM_SPK];
+
+       /** Haptic actuator type */
+       unsigned int hap_act;
 };
 
 #endif
index d0c5825876f8a2d1d124a3d51fac880951a63b2f..8db5ae03b6e3f679c16988e492e8241f1567b2f5 100644 (file)
 #ifndef __DAVINCI_ASP_H
 #define __DAVINCI_ASP_H
 
+#include <linux/genalloc.h>
+
 struct snd_platform_data {
        u32 tx_dma_offset;
        u32 rx_dma_offset;
        int asp_chan_q; /* event queue number for ASP channel */
        int ram_chan_q; /* event queue number for RAM channel */
-       unsigned int codec_fmt;
        /*
         * Allowing this is more efficient and eliminates left and right swaps
         * caused by underruns, but will swap the left and right channels
@@ -30,6 +31,7 @@ struct snd_platform_data {
        unsigned enable_channel_combine:1;
        unsigned sram_size_playback;
        unsigned sram_size_capture;
+       struct gen_pool *sram_pool;
 
        /*
         * If McBSP peripheral gets the clock from an external pin,
index 50a059e7d11616d4eb836cf7f0cc738cc0eaed3f..6d9e15ed1dcf0cb2875a10ca6795b14f3d4e0c51 100644 (file)
@@ -19,6 +19,7 @@
 
 struct cs4271_platform_data {
        int gpio_nreset;        /* GPIO driving Reset pin, if any */
+       int amutec_eq_bmutec:1; /* flag to enable AMUTEC=BMUTEC */
 };
 
 #endif /* __CS4271_H */
index 906010344dd7e0a227449f674d64b00a07dc1e86..cc1c919c64365898c47f7f6871b906dce0e5e1d4 100644 (file)
@@ -26,6 +26,7 @@
  * A:  inversion
  * B:  format mode
  * C:  chip specific
+ * D:  clock selecter if master mode
  */
 
 /* A: clock inversion */
 #define SH_FSI_OPTION_MASK     0x00000F00
 #define SH_FSI_ENABLE_STREAM_MODE      (1 << 8) /* for 16bit data */
 
+/* D:  clock selecter if master mode */
+#define SH_FSI_CLK_MASK                0x0000F000
+#define SH_FSI_CLK_EXTERNAL    (0 << 12)
+#define SH_FSI_CLK_CPG         (1 << 12) /* FSIxCK + FSI-DIV */
+
 /*
  * set_rate return value
  *
index 72b09cfd3dc367d4aa28826e750e27aa2ffb1885..d1b691bf8e2d918cb3e8fd97e61cce74c464cd76 100644 (file)
@@ -6,6 +6,14 @@ config SND_ATMEL_SOC
          the ATMEL SSC interface. You will also need
          to select the audio interfaces to support below.
 
+config SND_ATMEL_SOC_PDC
+       tristate
+       depends on SND_ATMEL_SOC
+
+config SND_ATMEL_SOC_DMA
+       tristate
+       depends on SND_ATMEL_SOC
+
 config SND_ATMEL_SOC_SSC
        tristate
        depends on SND_ATMEL_SOC
@@ -16,8 +24,8 @@ config SND_ATMEL_SOC_SSC
 
 config SND_AT91_SOC_SAM9G20_WM8731
        tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
-       depends on ATMEL_SSC && ARCH_AT91SAM9G20 && SND_ATMEL_SOC && \
-                   AT91_PROGRAMMABLE_CLOCKS
+       depends on ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS
+       select SND_ATMEL_SOC_PDC
        select SND_ATMEL_SOC_SSC
        select SND_SOC_WM8731
        help
@@ -27,6 +35,7 @@ config SND_AT91_SOC_SAM9G20_WM8731
 config SND_AT91_SOC_AFEB9260
        tristate "SoC Audio support for AFEB9260 board"
        depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
+       select SND_ATMEL_SOC_PDC
        select SND_ATMEL_SOC_SSC
        select SND_SOC_TLV320AIC23
        help
index a5c0bf19da78f01e823fc614c61528c30272a67c..41967ccb6f41e3968753d4922ad03f2439ad81b9 100644 (file)
@@ -1,8 +1,12 @@
 # AT91 Platform Support
 snd-soc-atmel-pcm-objs := atmel-pcm.o
+snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
+snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
 snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
 
 obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o
+obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
+obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
 obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
 
 # AT91 Machine Support
diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c
new file mode 100644 (file)
index 0000000..30184a4
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * atmel-pcm-dma.c  --  ALSA PCM DMA support for the Atmel SoC.
+ *
+ *  Copyright (C) 2012 Atmel
+ *
+ * Author: Bo Shen <voice.shen@atmel.com>
+ *
+ * Based on atmel-pcm by:
+ * Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ * Copyright 2008 Atmel
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/atmel-ssc.h>
+#include <linux/platform_data/dma-atmel.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "atmel-pcm.h"
+
+/*--------------------------------------------------------------------------*\
+ * Hardware definition
+\*--------------------------------------------------------------------------*/
+static const struct snd_pcm_hardware atmel_pcm_dma_hardware = {
+       .info                   = SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_RESUME |
+                                 SNDRV_PCM_INFO_PAUSE,
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
+       .period_bytes_min       = 256,          /* lighting DMA overhead */
+       .period_bytes_max       = 2 * 0xffff,   /* if 2 bytes format */
+       .periods_min            = 8,
+       .periods_max            = 1024,         /* no limit */
+       .buffer_bytes_max       = ATMEL_SSC_DMABUF_SIZE,
+};
+
+/**
+ * atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC
+ *
+ * We use DMAENGINE to send/receive data to/from SSC so this ISR is only to
+ * check if any overrun occured.
+ */
+static void atmel_pcm_dma_irq(u32 ssc_sr,
+       struct snd_pcm_substream *substream)
+{
+       struct atmel_pcm_dma_params *prtd;
+
+       prtd = snd_dmaengine_pcm_get_data(substream);
+
+       if (ssc_sr & prtd->mask->ssc_error) {
+               if (snd_pcm_running(substream))
+                       pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x)\n",
+                               substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+                               ? "underrun" : "overrun", prtd->name,
+                               ssc_sr);
+
+               /* stop RX and capture: will be enabled again at restart */
+               ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_disable);
+               snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+
+               /* now drain RHR and read status to remove xrun condition */
+               ssc_readx(prtd->ssc->regs, SSC_RHR);
+               ssc_readx(prtd->ssc->regs, SSC_SR);
+       }
+}
+
+/*--------------------------------------------------------------------------*\
+ * DMAENGINE operations
+\*--------------------------------------------------------------------------*/
+static bool filter(struct dma_chan *chan, void *slave)
+{
+       struct at_dma_slave *sl = slave;
+
+       if (sl->dma_dev == chan->device->dev) {
+               chan->private = sl;
+               return true;
+       } else {
+               return false;
+       }
+}
+
+static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct atmel_pcm_dma_params *prtd;
+       struct ssc_device *ssc;
+       struct dma_chan *dma_chan;
+       struct dma_slave_config slave_config;
+       int ret;
+
+       prtd = snd_dmaengine_pcm_get_data(substream);
+       ssc = prtd->ssc;
+
+       ret = snd_hwparams_to_dma_slave_config(substream, params,
+                       &slave_config);
+       if (ret) {
+               pr_err("atmel-pcm: hwparams to dma slave configure failed\n");
+               return ret;
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               slave_config.dst_addr = (dma_addr_t)ssc->phybase + SSC_THR;
+               slave_config.dst_maxburst = 1;
+       } else {
+               slave_config.src_addr = (dma_addr_t)ssc->phybase + SSC_RHR;
+               slave_config.src_maxburst = 1;
+       }
+
+       slave_config.device_fc = false;
+
+       dma_chan = snd_dmaengine_pcm_get_chan(substream);
+       if (dmaengine_slave_config(dma_chan, &slave_config)) {
+               pr_err("atmel-pcm: failed to configure dma channel\n");
+               ret = -EBUSY;
+               return ret;
+       }
+
+       return 0;
+}
+
+static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct atmel_pcm_dma_params *prtd;
+       struct ssc_device *ssc;
+       struct at_dma_slave *sdata = NULL;
+       int ret;
+
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+       prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+       ssc = prtd->ssc;
+       if (ssc->pdev)
+               sdata = ssc->pdev->dev.platform_data;
+
+       ret = snd_dmaengine_pcm_open(substream, filter, sdata);
+       if (ret) {
+               pr_err("atmel-pcm: dmaengine pcm open failed\n");
+               return -EINVAL;
+       }
+
+       snd_dmaengine_pcm_set_data(substream, prtd);
+
+       ret = atmel_pcm_configure_dma(substream, params);
+       if (ret) {
+               pr_err("atmel-pcm: failed to configure dmai\n");
+               goto err;
+       }
+
+       prtd->dma_intr_handler = atmel_pcm_dma_irq;
+
+       return 0;
+err:
+       snd_dmaengine_pcm_close(substream);
+       return ret;
+}
+
+static int atmel_pcm_dma_prepare(struct snd_pcm_substream *substream)
+{
+       struct atmel_pcm_dma_params *prtd;
+
+       prtd = snd_dmaengine_pcm_get_data(substream);
+
+       ssc_writex(prtd->ssc->regs, SSC_IER, prtd->mask->ssc_error);
+       ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_enable);
+
+       return 0;
+}
+
+static int atmel_pcm_open(struct snd_pcm_substream *substream)
+{
+       snd_soc_set_runtime_hwparams(substream, &atmel_pcm_dma_hardware);
+
+       return 0;
+}
+
+static int atmel_pcm_close(struct snd_pcm_substream *substream)
+{
+       snd_dmaengine_pcm_close(substream);
+
+       return 0;
+}
+
+static struct snd_pcm_ops atmel_pcm_ops = {
+       .open           = atmel_pcm_open,
+       .close          = atmel_pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = atmel_pcm_hw_params,
+       .prepare        = atmel_pcm_dma_prepare,
+       .trigger        = snd_dmaengine_pcm_trigger,
+       .pointer        = snd_dmaengine_pcm_pointer_no_residue,
+       .mmap           = atmel_pcm_mmap,
+};
+
+static struct snd_soc_platform_driver atmel_soc_platform = {
+       .ops            = &atmel_pcm_ops,
+       .pcm_new        = atmel_pcm_new,
+       .pcm_free       = atmel_pcm_free,
+};
+
+int atmel_pcm_dma_platform_register(struct device *dev)
+{
+       return snd_soc_register_platform(dev, &atmel_soc_platform);
+}
+EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
+
+void atmel_pcm_dma_platform_unregister(struct device *dev)
+{
+       snd_soc_unregister_platform(dev);
+}
+EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister);
+
+MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
+MODULE_DESCRIPTION("Atmel DMA based PCM module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c
new file mode 100644 (file)
index 0000000..6a293c7
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * atmel-pcm.c  --  ALSA PCM interface for the Atmel atmel SoC.
+ *
+ *  Copyright (C) 2005 SAN People
+ *  Copyright (C) 2008 Atmel
+ *
+ * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
+ *
+ * Based on at91-pcm. by:
+ * Frank Mandarino <fmandarino@endrelia.com>
+ * Copyright 2006 Endrelia Technologies Inc.
+ *
+ * Based on pxa2xx-pcm.c by:
+ *
+ * Author:     Nicolas Pitre
+ * Created:    Nov 30, 2004
+ * Copyright:  (C) 2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/atmel_pdc.h>
+#include <linux/atmel-ssc.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "atmel-pcm.h"
+
+
+/*--------------------------------------------------------------------------*\
+ * Hardware definition
+\*--------------------------------------------------------------------------*/
+/* TODO: These values were taken from the AT91 platform driver, check
+ *      them against real values for AT32
+ */
+static const struct snd_pcm_hardware atmel_pcm_hardware = {
+       .info                   = SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_INTERLEAVED |
+                                 SNDRV_PCM_INFO_PAUSE,
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
+       .period_bytes_min       = 32,
+       .period_bytes_max       = 8192,
+       .periods_min            = 2,
+       .periods_max            = 1024,
+       .buffer_bytes_max       = ATMEL_SSC_DMABUF_SIZE,
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * Data types
+\*--------------------------------------------------------------------------*/
+struct atmel_runtime_data {
+       struct atmel_pcm_dma_params *params;
+       dma_addr_t dma_buffer;          /* physical address of dma buffer */
+       dma_addr_t dma_buffer_end;      /* first address beyond DMA buffer */
+       size_t period_size;
+
+       dma_addr_t period_ptr;          /* physical address of next period */
+
+       /* PDC register save */
+       u32 pdc_xpr_save;
+       u32 pdc_xcr_save;
+       u32 pdc_xnpr_save;
+       u32 pdc_xncr_save;
+};
+
+/*--------------------------------------------------------------------------*\
+ * ISR
+\*--------------------------------------------------------------------------*/
+static void atmel_pcm_dma_irq(u32 ssc_sr,
+       struct snd_pcm_substream *substream)
+{
+       struct atmel_runtime_data *prtd = substream->runtime->private_data;
+       struct atmel_pcm_dma_params *params = prtd->params;
+       static int count;
+
+       count++;
+
+       if (ssc_sr & params->mask->ssc_endbuf) {
+               pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
+                               substream->stream == SNDRV_PCM_STREAM_PLAYBACK
+                               ? "underrun" : "overrun",
+                               params->name, ssc_sr, count);
+
+               /* re-start the PDC */
+               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+                          params->mask->pdc_disable);
+               prtd->period_ptr += prtd->period_size;
+               if (prtd->period_ptr >= prtd->dma_buffer_end)
+                       prtd->period_ptr = prtd->dma_buffer;
+
+               ssc_writex(params->ssc->regs, params->pdc->xpr,
+                          prtd->period_ptr);
+               ssc_writex(params->ssc->regs, params->pdc->xcr,
+                          prtd->period_size / params->pdc_xfer_size);
+               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+                          params->mask->pdc_enable);
+       }
+
+       if (ssc_sr & params->mask->ssc_endx) {
+               /* Load the PDC next pointer and counter registers */
+               prtd->period_ptr += prtd->period_size;
+               if (prtd->period_ptr >= prtd->dma_buffer_end)
+                       prtd->period_ptr = prtd->dma_buffer;
+
+               ssc_writex(params->ssc->regs, params->pdc->xnpr,
+                          prtd->period_ptr);
+               ssc_writex(params->ssc->regs, params->pdc->xncr,
+                          prtd->period_size / params->pdc_xfer_size);
+       }
+
+       snd_pcm_period_elapsed(substream);
+}
+
+
+/*--------------------------------------------------------------------------*\
+ * PCM operations
+\*--------------------------------------------------------------------------*/
+static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct atmel_runtime_data *prtd = runtime->private_data;
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+       /* this may get called several times by oss emulation
+        * with different params */
+
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+       runtime->dma_bytes = params_buffer_bytes(params);
+
+       prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+       prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
+
+       prtd->dma_buffer = runtime->dma_addr;
+       prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
+       prtd->period_size = params_period_bytes(params);
+
+       pr_debug("atmel-pcm: "
+               "hw_params: DMA for %s initialized "
+               "(dma_bytes=%u, period_size=%u)\n",
+               prtd->params->name,
+               runtime->dma_bytes,
+               prtd->period_size);
+       return 0;
+}
+
+static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct atmel_runtime_data *prtd = substream->runtime->private_data;
+       struct atmel_pcm_dma_params *params = prtd->params;
+
+       if (params != NULL) {
+               ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+                          params->mask->pdc_disable);
+               prtd->params->dma_intr_handler = NULL;
+       }
+
+       return 0;
+}
+
+static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct atmel_runtime_data *prtd = substream->runtime->private_data;
+       struct atmel_pcm_dma_params *params = prtd->params;
+
+       ssc_writex(params->ssc->regs, SSC_IDR,
+                  params->mask->ssc_endx | params->mask->ssc_endbuf);
+       ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+                  params->mask->pdc_disable);
+       return 0;
+}
+
+static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
+       int cmd)
+{
+       struct snd_pcm_runtime *rtd = substream->runtime;
+       struct atmel_runtime_data *prtd = rtd->private_data;
+       struct atmel_pcm_dma_params *params = prtd->params;
+       int ret = 0;
+
+       pr_debug("atmel-pcm:buffer_size = %ld,"
+               "dma_area = %p, dma_bytes = %u\n",
+               rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               prtd->period_ptr = prtd->dma_buffer;
+
+               ssc_writex(params->ssc->regs, params->pdc->xpr,
+                          prtd->period_ptr);
+               ssc_writex(params->ssc->regs, params->pdc->xcr,
+                          prtd->period_size / params->pdc_xfer_size);
+
+               prtd->period_ptr += prtd->period_size;
+               ssc_writex(params->ssc->regs, params->pdc->xnpr,
+                          prtd->period_ptr);
+               ssc_writex(params->ssc->regs, params->pdc->xncr,
+                          prtd->period_size / params->pdc_xfer_size);
+
+               pr_debug("atmel-pcm: trigger: "
+                       "period_ptr=%lx, xpr=%u, "
+                       "xcr=%u, xnpr=%u, xncr=%u\n",
+                       (unsigned long)prtd->period_ptr,
+                       ssc_readx(params->ssc->regs, params->pdc->xpr),
+                       ssc_readx(params->ssc->regs, params->pdc->xcr),
+                       ssc_readx(params->ssc->regs, params->pdc->xnpr),
+                       ssc_readx(params->ssc->regs, params->pdc->xncr));
+
+               ssc_writex(params->ssc->regs, SSC_IER,
+                          params->mask->ssc_endx | params->mask->ssc_endbuf);
+               ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
+                          params->mask->pdc_enable);
+
+               pr_debug("sr=%u imr=%u\n",
+                       ssc_readx(params->ssc->regs, SSC_SR),
+                       ssc_readx(params->ssc->regs, SSC_IER));
+               break;          /* SNDRV_PCM_TRIGGER_START */
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+                          params->mask->pdc_disable);
+               break;
+
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
+                          params->mask->pdc_enable);
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static snd_pcm_uframes_t atmel_pcm_pointer(
+       struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct atmel_runtime_data *prtd = runtime->private_data;
+       struct atmel_pcm_dma_params *params = prtd->params;
+       dma_addr_t ptr;
+       snd_pcm_uframes_t x;
+
+       ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
+       x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
+
+       if (x == runtime->buffer_size)
+               x = 0;
+
+       return x;
+}
+
+static int atmel_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct atmel_runtime_data *prtd;
+       int ret = 0;
+
+       snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
+
+       /* ensure that buffer size is a multiple of period size */
+       ret = snd_pcm_hw_constraint_integer(runtime,
+                                               SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0)
+               goto out;
+
+       prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
+       if (prtd == NULL) {
+               ret = -ENOMEM;
+               goto out;
+       }
+       runtime->private_data = prtd;
+
+ out:
+       return ret;
+}
+
+static int atmel_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct atmel_runtime_data *prtd = substream->runtime->private_data;
+
+       kfree(prtd);
+       return 0;
+}
+
+static struct snd_pcm_ops atmel_pcm_ops = {
+       .open           = atmel_pcm_open,
+       .close          = atmel_pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = atmel_pcm_hw_params,
+       .hw_free        = atmel_pcm_hw_free,
+       .prepare        = atmel_pcm_prepare,
+       .trigger        = atmel_pcm_trigger,
+       .pointer        = atmel_pcm_pointer,
+       .mmap           = atmel_pcm_mmap,
+};
+
+
+/*--------------------------------------------------------------------------*\
+ * ASoC platform driver
+\*--------------------------------------------------------------------------*/
+#ifdef CONFIG_PM
+static int atmel_pcm_suspend(struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = dai->runtime;
+       struct atmel_runtime_data *prtd;
+       struct atmel_pcm_dma_params *params;
+
+       if (!runtime)
+               return 0;
+
+       prtd = runtime->private_data;
+       params = prtd->params;
+
+       /* disable the PDC and save the PDC registers */
+
+       ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
+
+       prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
+       prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
+       prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
+       prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
+
+       return 0;
+}
+
+static int atmel_pcm_resume(struct snd_soc_dai *dai)
+{
+       struct snd_pcm_runtime *runtime = dai->runtime;
+       struct atmel_runtime_data *prtd;
+       struct atmel_pcm_dma_params *params;
+
+       if (!runtime)
+               return 0;
+
+       prtd = runtime->private_data;
+       params = prtd->params;
+
+       /* restore the PDC registers and enable the PDC */
+       ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
+       ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
+       ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
+       ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
+
+       ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
+       return 0;
+}
+#else
+#define atmel_pcm_suspend      NULL
+#define atmel_pcm_resume       NULL
+#endif
+
+static struct snd_soc_platform_driver atmel_soc_platform = {
+       .ops            = &atmel_pcm_ops,
+       .pcm_new        = atmel_pcm_new,
+       .pcm_free       = atmel_pcm_free,
+       .suspend        = atmel_pcm_suspend,
+       .resume         = atmel_pcm_resume,
+};
+
+int atmel_pcm_pdc_platform_register(struct device *dev)
+{
+       return snd_soc_register_platform(dev, &atmel_soc_platform);
+}
+EXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
+
+void atmel_pcm_pdc_platform_unregister(struct device *dev)
+{
+       snd_soc_unregister_platform(dev);
+}
+EXPORT_SYMBOL(atmel_pcm_pdc_platform_unregister);
+
+MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
+MODULE_DESCRIPTION("Atmel PCM module");
+MODULE_LICENSE("GPL");
index 9b84f985770ee4e09ca1d7a8ab04cbe410fda037..e99f1811300aef179a5c1d5c33dc702249175681 100644 (file)
  */
 
 #include <linux/module.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
 #include <linux/dma-mapping.h>
-#include <linux/atmel_pdc.h>
-#include <linux/atmel-ssc.h>
-
-#include <sound/core.h>
 #include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
-
 #include "atmel-pcm.h"
 
-
-/*--------------------------------------------------------------------------*\
- * Hardware definition
-\*--------------------------------------------------------------------------*/
-/* TODO: These values were taken from the AT91 platform driver, check
- *      them against real values for AT32
- */
-static const struct snd_pcm_hardware atmel_pcm_hardware = {
-       .info                   = SNDRV_PCM_INFO_MMAP |
-                                 SNDRV_PCM_INFO_MMAP_VALID |
-                                 SNDRV_PCM_INFO_INTERLEAVED |
-                                 SNDRV_PCM_INFO_PAUSE,
-       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
-       .period_bytes_min       = 32,
-       .period_bytes_max       = 8192,
-       .periods_min            = 2,
-       .periods_max            = 1024,
-       .buffer_bytes_max       = 32 * 1024,
-};
-
-
-/*--------------------------------------------------------------------------*\
- * Data types
-\*--------------------------------------------------------------------------*/
-struct atmel_runtime_data {
-       struct atmel_pcm_dma_params *params;
-       dma_addr_t dma_buffer;          /* physical address of dma buffer */
-       dma_addr_t dma_buffer_end;      /* first address beyond DMA buffer */
-       size_t period_size;
-
-       dma_addr_t period_ptr;          /* physical address of next period */
-
-       /* PDC register save */
-       u32 pdc_xpr_save;
-       u32 pdc_xcr_save;
-       u32 pdc_xnpr_save;
-       u32 pdc_xncr_save;
-};
-
-
-/*--------------------------------------------------------------------------*\
- * Helper functions
-\*--------------------------------------------------------------------------*/
 static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
        int stream)
 {
        struct snd_pcm_substream *substream = pcm->streams[stream].substream;
        struct snd_dma_buffer *buf = &substream->dma_buffer;
-       size_t size = atmel_pcm_hardware.buffer_bytes_max;
+       size_t size = ATMEL_SSC_DMABUF_SIZE;
 
        buf->dev.type = SNDRV_DMA_TYPE_DEV;
        buf->dev.dev = pcm->card->dev;
        buf->private_data = NULL;
        buf->area = dma_alloc_coherent(pcm->card->dev, size,
-                                         &buf->addr, GFP_KERNEL);
-       pr_debug("atmel-pcm:"
-               "preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
-               (void *) buf->area,
-               (void *) buf->addr,
-               size);
+                       &buf->addr, GFP_KERNEL);
+       pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d\n",
+                       (void *)buf->area, (void *)buf->addr, size);
 
        if (!buf->area)
                return -ENOMEM;
@@ -113,258 +58,19 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
        buf->bytes = size;
        return 0;
 }
-/*--------------------------------------------------------------------------*\
- * ISR
-\*--------------------------------------------------------------------------*/
-static void atmel_pcm_dma_irq(u32 ssc_sr,
-       struct snd_pcm_substream *substream)
-{
-       struct atmel_runtime_data *prtd = substream->runtime->private_data;
-       struct atmel_pcm_dma_params *params = prtd->params;
-       static int count;
-
-       count++;
-
-       if (ssc_sr & params->mask->ssc_endbuf) {
-               pr_warning("atmel-pcm: buffer %s on %s"
-                               " (SSC_SR=%#x, count=%d)\n",
-                               substream->stream == SNDRV_PCM_STREAM_PLAYBACK
-                               ? "underrun" : "overrun",
-                               params->name, ssc_sr, count);
-
-               /* re-start the PDC */
-               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-                          params->mask->pdc_disable);
-               prtd->period_ptr += prtd->period_size;
-               if (prtd->period_ptr >= prtd->dma_buffer_end)
-                       prtd->period_ptr = prtd->dma_buffer;
-
-               ssc_writex(params->ssc->regs, params->pdc->xpr,
-                          prtd->period_ptr);
-               ssc_writex(params->ssc->regs, params->pdc->xcr,
-                          prtd->period_size / params->pdc_xfer_size);
-               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-                          params->mask->pdc_enable);
-       }
-
-       if (ssc_sr & params->mask->ssc_endx) {
-               /* Load the PDC next pointer and counter registers */
-               prtd->period_ptr += prtd->period_size;
-               if (prtd->period_ptr >= prtd->dma_buffer_end)
-                       prtd->period_ptr = prtd->dma_buffer;
-
-               ssc_writex(params->ssc->regs, params->pdc->xnpr,
-                          prtd->period_ptr);
-               ssc_writex(params->ssc->regs, params->pdc->xncr,
-                          prtd->period_size / params->pdc_xfer_size);
-       }
-
-       snd_pcm_period_elapsed(substream);
-}
-
-
-/*--------------------------------------------------------------------------*\
- * PCM operations
-\*--------------------------------------------------------------------------*/
-static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct atmel_runtime_data *prtd = runtime->private_data;
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-
-       /* this may get called several times by oss emulation
-        * with different params */
-
-       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-       runtime->dma_bytes = params_buffer_bytes(params);
-
-       prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-       prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
-
-       prtd->dma_buffer = runtime->dma_addr;
-       prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
-       prtd->period_size = params_period_bytes(params);
-
-       pr_debug("atmel-pcm: "
-               "hw_params: DMA for %s initialized "
-               "(dma_bytes=%u, period_size=%u)\n",
-               prtd->params->name,
-               runtime->dma_bytes,
-               prtd->period_size);
-       return 0;
-}
-
-static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-       struct atmel_runtime_data *prtd = substream->runtime->private_data;
-       struct atmel_pcm_dma_params *params = prtd->params;
-
-       if (params != NULL) {
-               ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
-                          params->mask->pdc_disable);
-               prtd->params->dma_intr_handler = NULL;
-       }
-
-       return 0;
-}
-
-static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
-{
-       struct atmel_runtime_data *prtd = substream->runtime->private_data;
-       struct atmel_pcm_dma_params *params = prtd->params;
-
-       ssc_writex(params->ssc->regs, SSC_IDR,
-                  params->mask->ssc_endx | params->mask->ssc_endbuf);
-       ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-                  params->mask->pdc_disable);
-       return 0;
-}
-
-static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
-       int cmd)
-{
-       struct snd_pcm_runtime *rtd = substream->runtime;
-       struct atmel_runtime_data *prtd = rtd->private_data;
-       struct atmel_pcm_dma_params *params = prtd->params;
-       int ret = 0;
-
-       pr_debug("atmel-pcm:buffer_size = %ld,"
-               "dma_area = %p, dma_bytes = %u\n",
-               rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               prtd->period_ptr = prtd->dma_buffer;
-
-               ssc_writex(params->ssc->regs, params->pdc->xpr,
-                          prtd->period_ptr);
-               ssc_writex(params->ssc->regs, params->pdc->xcr,
-                          prtd->period_size / params->pdc_xfer_size);
-
-               prtd->period_ptr += prtd->period_size;
-               ssc_writex(params->ssc->regs, params->pdc->xnpr,
-                          prtd->period_ptr);
-               ssc_writex(params->ssc->regs, params->pdc->xncr,
-                          prtd->period_size / params->pdc_xfer_size);
-
-               pr_debug("atmel-pcm: trigger: "
-                       "period_ptr=%lx, xpr=%u, "
-                       "xcr=%u, xnpr=%u, xncr=%u\n",
-                       (unsigned long)prtd->period_ptr,
-                       ssc_readx(params->ssc->regs, params->pdc->xpr),
-                       ssc_readx(params->ssc->regs, params->pdc->xcr),
-                       ssc_readx(params->ssc->regs, params->pdc->xnpr),
-                       ssc_readx(params->ssc->regs, params->pdc->xncr));
-
-               ssc_writex(params->ssc->regs, SSC_IER,
-                          params->mask->ssc_endx | params->mask->ssc_endbuf);
-               ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
-                          params->mask->pdc_enable);
-
-               pr_debug("sr=%u imr=%u\n",
-                       ssc_readx(params->ssc->regs, SSC_SR),
-                       ssc_readx(params->ssc->regs, SSC_IER));
-               break;          /* SNDRV_PCM_TRIGGER_START */
-
-       case SNDRV_PCM_TRIGGER_STOP:
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-                          params->mask->pdc_disable);
-               break;
-
-       case SNDRV_PCM_TRIGGER_RESUME:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
-                          params->mask->pdc_enable);
-               break;
-
-       default:
-               ret = -EINVAL;
-       }
 
-       return ret;
-}
-
-static snd_pcm_uframes_t atmel_pcm_pointer(
-       struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct atmel_runtime_data *prtd = runtime->private_data;
-       struct atmel_pcm_dma_params *params = prtd->params;
-       dma_addr_t ptr;
-       snd_pcm_uframes_t x;
-
-       ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
-       x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
-
-       if (x == runtime->buffer_size)
-               x = 0;
-
-       return x;
-}
-
-static int atmel_pcm_open(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct atmel_runtime_data *prtd;
-       int ret = 0;
-
-       snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
-
-       /* ensure that buffer size is a multiple of period size */
-       ret = snd_pcm_hw_constraint_integer(runtime,
-                                               SNDRV_PCM_HW_PARAM_PERIODS);
-       if (ret < 0)
-               goto out;
-
-       prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
-       if (prtd == NULL) {
-               ret = -ENOMEM;
-               goto out;
-       }
-       runtime->private_data = prtd;
-
- out:
-       return ret;
-}
-
-static int atmel_pcm_close(struct snd_pcm_substream *substream)
-{
-       struct atmel_runtime_data *prtd = substream->runtime->private_data;
-
-       kfree(prtd);
-       return 0;
-}
-
-static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
+int atmel_pcm_mmap(struct snd_pcm_substream *substream,
        struct vm_area_struct *vma)
 {
        return remap_pfn_range(vma, vma->vm_start,
                       substream->dma_buffer.addr >> PAGE_SHIFT,
                       vma->vm_end - vma->vm_start, vma->vm_page_prot);
 }
+EXPORT_SYMBOL_GPL(atmel_pcm_mmap);
 
-static struct snd_pcm_ops atmel_pcm_ops = {
-       .open           = atmel_pcm_open,
-       .close          = atmel_pcm_close,
-       .ioctl          = snd_pcm_lib_ioctl,
-       .hw_params      = atmel_pcm_hw_params,
-       .hw_free        = atmel_pcm_hw_free,
-       .prepare        = atmel_pcm_prepare,
-       .trigger        = atmel_pcm_trigger,
-       .pointer        = atmel_pcm_pointer,
-       .mmap           = atmel_pcm_mmap,
-};
-
-
-/*--------------------------------------------------------------------------*\
- * ASoC platform driver
-\*--------------------------------------------------------------------------*/
 static u64 atmel_pcm_dmamask = DMA_BIT_MASK(32);
 
-static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
+int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_card *card = rtd->card->snd_card;
        struct snd_pcm *pcm = rtd->pcm;
@@ -376,6 +82,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
                card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
 
        if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+               pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n");
                ret = atmel_pcm_preallocate_dma_buffer(pcm,
                        SNDRV_PCM_STREAM_PLAYBACK);
                if (ret)
@@ -383,8 +90,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
        }
 
        if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
-               pr_debug("atmel-pcm:"
-                               "Allocating PCM capture DMA buffer\n");
+               pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n");
                ret = atmel_pcm_preallocate_dma_buffer(pcm,
                        SNDRV_PCM_STREAM_CAPTURE);
                if (ret)
@@ -393,8 +99,9 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
  out:
        return ret;
 }
+EXPORT_SYMBOL_GPL(atmel_pcm_new);
 
-static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
+void atmel_pcm_free(struct snd_pcm *pcm)
 {
        struct snd_pcm_substream *substream;
        struct snd_dma_buffer *buf;
@@ -413,89 +120,5 @@ static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
                buf->area = NULL;
        }
 }
+EXPORT_SYMBOL_GPL(atmel_pcm_free);
 
-#ifdef CONFIG_PM
-static int atmel_pcm_suspend(struct snd_soc_dai *dai)
-{
-       struct snd_pcm_runtime *runtime = dai->runtime;
-       struct atmel_runtime_data *prtd;
-       struct atmel_pcm_dma_params *params;
-
-       if (!runtime)
-               return 0;
-
-       prtd = runtime->private_data;
-       params = prtd->params;
-
-       /* disable the PDC and save the PDC registers */
-
-       ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
-
-       prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
-       prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
-       prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
-       prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
-
-       return 0;
-}
-
-static int atmel_pcm_resume(struct snd_soc_dai *dai)
-{
-       struct snd_pcm_runtime *runtime = dai->runtime;
-       struct atmel_runtime_data *prtd;
-       struct atmel_pcm_dma_params *params;
-
-       if (!runtime)
-               return 0;
-
-       prtd = runtime->private_data;
-       params = prtd->params;
-
-       /* restore the PDC registers and enable the PDC */
-       ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
-       ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
-       ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
-       ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
-
-       ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
-       return 0;
-}
-#else
-#define atmel_pcm_suspend      NULL
-#define atmel_pcm_resume       NULL
-#endif
-
-static struct snd_soc_platform_driver atmel_soc_platform = {
-       .ops            = &atmel_pcm_ops,
-       .pcm_new        = atmel_pcm_new,
-       .pcm_free       = atmel_pcm_free_dma_buffers,
-       .suspend        = atmel_pcm_suspend,
-       .resume         = atmel_pcm_resume,
-};
-
-static int __devinit atmel_soc_platform_probe(struct platform_device *pdev)
-{
-       return snd_soc_register_platform(&pdev->dev, &atmel_soc_platform);
-}
-
-static int __devexit atmel_soc_platform_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_platform(&pdev->dev);
-       return 0;
-}
-
-static struct platform_driver atmel_pcm_driver = {
-       .driver = {
-                       .name = "atmel-pcm-audio",
-                       .owner = THIS_MODULE,
-       },
-
-       .probe = atmel_soc_platform_probe,
-       .remove = __devexit_p(atmel_soc_platform_remove),
-};
-
-module_platform_driver(atmel_pcm_driver);
-
-MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
-MODULE_DESCRIPTION("Atmel PCM module");
-MODULE_LICENSE("GPL");
index 5e0a95e643298b8f5210e7619139b48a02ae939f..bb45d20e7250aeace79a2595f5f9676349ba05d9 100644 (file)
@@ -36,6 +36,8 @@
 
 #include <linux/atmel-ssc.h>
 
+#define ATMEL_SSC_DMABUF_SIZE  (64 * 1024)
+
 /*
  * Registers and status bits that are required by the PCM driver.
  */
@@ -50,6 +52,7 @@ struct atmel_pdc_regs {
 struct atmel_ssc_mask {
        u32     ssc_enable;             /* SSC recv/trans enable */
        u32     ssc_disable;            /* SSC recv/trans disable */
+       u32     ssc_error;              /* SSC error conditions */
        u32     ssc_endx;               /* SSC ENDTX or ENDRX */
        u32     ssc_endbuf;             /* SSC TXBUFE or RXBUFF */
        u32     pdc_enable;             /* PDC recv/trans enable */
@@ -80,4 +83,35 @@ struct atmel_pcm_dma_params {
 #define ssc_readx(base, reg)            (__raw_readl((base) + (reg)))
 #define ssc_writex(base, reg, value)    __raw_writel((value), (base) + (reg))
 
+int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd);
+void atmel_pcm_free(struct snd_pcm *pcm);
+int atmel_pcm_mmap(struct snd_pcm_substream *substream,
+               struct vm_area_struct *vma);
+
+#ifdef CONFIG_SND_ATMEL_SOC_PDC
+int atmel_pcm_pdc_platform_register(struct device *dev);
+void atmel_pcm_pdc_platform_unregister(struct device *dev);
+#else
+static inline int atmel_pcm_pdc_platform_register(struct device *dev)
+{
+       return 0;
+}
+static inline void atmel_pcm_pdc_platform_unregister(struct device *dev)
+{
+}
+#endif
+
+#ifdef CONFIG_SND_ATMEL_SOC_DMA
+int atmel_pcm_dma_platform_register(struct device *dev);
+void atmel_pcm_dma_platform_unregister(struct device *dev);
+#else
+static inline int atmel_pcm_dma_platform_register(struct device *dev)
+{
+       return 0;
+}
+static inline void atmel_pcm_dma_platform_unregister(struct device *dev)
+{
+}
+#endif
+
 #endif /* _ATMEL_PCM_H */
index 354341ec0f42f1129adbaaf310366af46b866229..1c7663422054049265957743bad8c6f8678e9e17 100644 (file)
 #include "atmel_ssc_dai.h"
 
 
-#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
-#define NUM_SSC_DEVICES                1
-#else
 #define NUM_SSC_DEVICES                3
-#endif
 
 /*
  * SSC PDC registers required by the PCM DMA engine.
@@ -107,7 +103,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
        .pdc            = &pdc_rx_reg,
        .mask           = &ssc_rx_mask,
        } },
-#if NUM_SSC_DEVICES == 3
        {{
        .name           = "SSC1 PCM out",
        .pdc            = &pdc_tx_reg,
@@ -128,7 +123,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
        .pdc            = &pdc_rx_reg,
        .mask           = &ssc_rx_mask,
        } },
-#endif
 };
 
 
@@ -139,7 +133,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
        .dir_mask       = SSC_DIR_MASK_UNUSED,
        .initialized    = 0,
        },
-#if NUM_SSC_DEVICES == 3
        {
        .name           = "ssc1",
        .lock           = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
@@ -152,7 +145,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
        .dir_mask       = SSC_DIR_MASK_UNUSED,
        .initialized    = 0,
        },
-#endif
 };
 
 
@@ -690,27 +682,9 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
 static int atmel_ssc_probe(struct snd_soc_dai *dai)
 {
        struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
-       int ret = 0;
 
        snd_soc_dai_set_drvdata(dai, ssc_p);
 
-       /*
-        * Request SSC device
-        */
-       ssc_p->ssc = ssc_request(dai->id);
-       if (IS_ERR(ssc_p->ssc)) {
-               printk(KERN_ERR "ASoC: Failed to request SSC %d\n", dai->id);
-               ret = PTR_ERR(ssc_p->ssc);
-       }
-
-       return ret;
-}
-
-static int atmel_ssc_remove(struct snd_soc_dai *dai)
-{
-       struct atmel_ssc_info *ssc_p = snd_soc_dai_get_drvdata(dai);
-
-       ssc_free(ssc_p->ssc);
        return 0;
 }
 
@@ -728,30 +702,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
        .set_clkdiv     = atmel_ssc_set_dai_clkdiv,
 };
 
-static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
-       {
-               .name = "atmel-ssc-dai.0",
-               .probe = atmel_ssc_probe,
-               .remove = atmel_ssc_remove,
-               .suspend = atmel_ssc_suspend,
-               .resume = atmel_ssc_resume,
-               .playback = {
-                       .channels_min = 1,
-                       .channels_max = 2,
-                       .rates = ATMEL_SSC_RATES,
-                       .formats = ATMEL_SSC_FORMATS,},
-               .capture = {
-                       .channels_min = 1,
-                       .channels_max = 2,
-                       .rates = ATMEL_SSC_RATES,
-                       .formats = ATMEL_SSC_FORMATS,},
-               .ops = &atmel_ssc_dai_ops,
-       },
-#if NUM_SSC_DEVICES == 3
-       {
-               .name = "atmel-ssc-dai.1",
+static struct snd_soc_dai_driver atmel_ssc_dai = {
                .probe = atmel_ssc_probe,
-               .remove = atmel_ssc_remove,
                .suspend = atmel_ssc_suspend,
                .resume = atmel_ssc_resume,
                .playback = {
@@ -765,50 +717,50 @@ static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
                        .rates = ATMEL_SSC_RATES,
                        .formats = ATMEL_SSC_FORMATS,},
                .ops = &atmel_ssc_dai_ops,
-       },
-       {
-               .name = "atmel-ssc-dai.2",
-               .probe = atmel_ssc_probe,
-               .remove = atmel_ssc_remove,
-               .suspend = atmel_ssc_suspend,
-               .resume = atmel_ssc_resume,
-               .playback = {
-                       .channels_min = 1,
-                       .channels_max = 2,
-                       .rates = ATMEL_SSC_RATES,
-                       .formats = ATMEL_SSC_FORMATS,},
-               .capture = {
-                       .channels_min = 1,
-                       .channels_max = 2,
-                       .rates = ATMEL_SSC_RATES,
-                       .formats = ATMEL_SSC_FORMATS,},
-               .ops = &atmel_ssc_dai_ops,
-       },
-#endif
 };
 
-static __devinit int asoc_ssc_probe(struct platform_device *pdev)
+static int asoc_ssc_init(struct device *dev)
 {
-       BUG_ON(pdev->id < 0);
-       BUG_ON(pdev->id >= ARRAY_SIZE(atmel_ssc_dai));
-       return snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai[pdev->id]);
-}
+       struct platform_device *pdev = to_platform_device(dev);
+       struct ssc_device *ssc = platform_get_drvdata(pdev);
+       int ret;
+
+       ret = snd_soc_register_dai(dev, &atmel_ssc_dai);
+       if (ret) {
+               dev_err(dev, "Could not register DAI: %d\n", ret);
+               goto err;
+       }
+
+       if (ssc->pdata->use_dma)
+               ret = atmel_pcm_dma_platform_register(dev);
+       else
+               ret = atmel_pcm_pdc_platform_register(dev);
+
+       if (ret) {
+               dev_err(dev, "Could not register PCM: %d\n", ret);
+               goto err_unregister_dai;
+       };
 
-static int __devexit asoc_ssc_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_dai(&pdev->dev);
        return 0;
+
+err_unregister_dai:
+       snd_soc_unregister_dai(dev);
+err:
+       return ret;
 }
 
-static struct platform_driver asoc_ssc_driver = {
-       .driver = {
-                       .name = "atmel-ssc-dai",
-                       .owner = THIS_MODULE,
-       },
+static void asoc_ssc_exit(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct ssc_device *ssc = platform_get_drvdata(pdev);
 
-       .probe = asoc_ssc_probe,
-       .remove = __devexit_p(asoc_ssc_remove),
-};
+       if (ssc->pdata->use_dma)
+               atmel_pcm_dma_platform_unregister(dev);
+       else
+               atmel_pcm_pdc_platform_unregister(dev);
+
+       snd_soc_unregister_dai(dev);
+}
 
 /**
  * atmel_ssc_set_audio - Allocate the specified SSC for audio use.
@@ -816,50 +768,32 @@ static struct platform_driver asoc_ssc_driver = {
 int atmel_ssc_set_audio(int ssc_id)
 {
        struct ssc_device *ssc;
-       static struct platform_device *dma_pdev;
-       struct platform_device *ssc_pdev;
        int ret;
 
-       if (ssc_id < 0 || ssc_id >= ARRAY_SIZE(atmel_ssc_dai))
-               return -EINVAL;
-
-       /* Allocate a dummy device for DMA if we don't have one already */
-       if (!dma_pdev) {
-               dma_pdev = platform_device_alloc("atmel-pcm-audio", -1);
-               if (!dma_pdev)
-                       return -ENOMEM;
-
-               ret = platform_device_add(dma_pdev);
-               if (ret < 0) {
-                       platform_device_put(dma_pdev);
-                       dma_pdev = NULL;
-                       return ret;
-               }
-       }
-
-       ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id);
-       if (!ssc_pdev)
-               return -ENOMEM;
-
        /* If we can grab the SSC briefly to parent the DAI device off it */
        ssc = ssc_request(ssc_id);
-       if (IS_ERR(ssc))
-               pr_warn("Unable to parent ASoC SSC DAI on SSC: %ld\n",
+       if (IS_ERR(ssc)) {
+               pr_err("Unable to parent ASoC SSC DAI on SSC: %ld\n",
                        PTR_ERR(ssc));
-       else {
-               ssc_pdev->dev.parent = &(ssc->pdev->dev);
-               ssc_free(ssc);
+               return PTR_ERR(ssc);
+       } else {
+               ssc_info[ssc_id].ssc = ssc;
        }
 
-       ret = platform_device_add(ssc_pdev);
-       if (ret < 0)
-               platform_device_put(ssc_pdev);
+       ret = asoc_ssc_init(&ssc->pdev->dev);
 
        return ret;
 }
 EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
 
-module_platform_driver(asoc_ssc_driver);
+void atmel_ssc_put_audio(int ssc_id)
+{
+       struct ssc_device *ssc = ssc_info[ssc_id].ssc;
+
+       ssc_free(ssc);
+       asoc_ssc_exit(&ssc->pdev->dev);
+}
+EXPORT_SYMBOL_GPL(atmel_ssc_put_audio);
 
 /* Module information */
 MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com");
index 5d4f0f9b4d9a875eb935ec79488b76ff17073ab0..b1f08d51149526006021c7314aa4f3e15847de3d 100644 (file)
@@ -117,6 +117,7 @@ struct atmel_ssc_info {
        struct atmel_ssc_state ssc_state;
 };
 
-int atmel_ssc_set_audio(int ssc);
+int atmel_ssc_set_audio(int ssc_id);
+void atmel_ssc_put_audio(int ssc_id);
 
 #endif /* _AT91_SSC_DAI_H */
index c88351488f45c5c6adb0529a212ab257531a90fd..0744610e53dd97201d4a5e24099aa58e337b44fc 100644 (file)
@@ -38,6 +38,8 @@
 #include <linux/platform_device.h>
 #include <linux/i2c.h>
 
+#include <linux/pinctrl/consumer.h>
+
 #include <linux/atmel-ssc.h>
 
 #include <sound/core.h>
@@ -179,10 +181,10 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
 static struct snd_soc_dai_link at91sam9g20ek_dai = {
        .name = "WM8731",
        .stream_name = "WM8731 PCM",
-       .cpu_dai_name = "atmel-ssc-dai.0",
+       .cpu_dai_name = "at91rm9200_ssc.0",
        .codec_dai_name = "wm8731-hifi",
        .init = at91sam9g20ek_wm8731_init,
-       .platform_name = "atmel-pcm-audio",
+       .platform_name = "at91rm9200_ssc.0",
        .codec_name = "wm8731.0-001b",
        .ops = &at91sam9g20ek_ops,
 };
@@ -195,20 +197,31 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = {
        .set_bias_level = at91sam9g20ek_set_bias_level,
 };
 
-static struct platform_device *at91sam9g20ek_snd_device;
-
-static int __init at91sam9g20ek_init(void)
+static int __devinit at91sam9g20ek_audio_probe(struct platform_device *pdev)
 {
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *codec_np, *cpu_np;
        struct clk *pllb;
+       struct snd_soc_card *card = &snd_soc_at91sam9g20ek;
+       struct pinctrl *pinctrl;
        int ret;
 
-       if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc()))
-               return -ENODEV;
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl)) {
+               dev_err(&pdev->dev, "Failed to request pinctrl for mck\n");
+               return PTR_ERR(pinctrl);
+       }
+
+       if (!np) {
+               if (!(machine_is_at91sam9g20ek() ||
+                       machine_is_at91sam9g20ek_2mmc()))
+                       return -ENODEV;
+       }
 
        ret = atmel_ssc_set_audio(0);
-       if (ret != 0) {
-               pr_err("Failed to set SSC 0 for audio: %d\n", ret);
-               return ret;
+       if (ret) {
+               dev_err(&pdev->dev, "ssc channel is not valid\n");
+               return -EINVAL;
        }
 
        /*
@@ -236,45 +249,92 @@ static int __init at91sam9g20ek_init(void)
 
        clk_set_rate(mclk, MCLK_RATE);
 
-       at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!at91sam9g20ek_snd_device) {
-               printk(KERN_ERR "ASoC: Platform device allocation failed\n");
-               ret = -ENOMEM;
-               goto err_mclk;
+       card->dev = &pdev->dev;
+
+       /* Parse device node info */
+       if (np) {
+               ret = snd_soc_of_parse_card_name(card, "atmel,model");
+               if (ret)
+                       goto err;
+
+               ret = snd_soc_of_parse_audio_routing(card,
+                       "atmel,audio-routing");
+               if (ret)
+                       goto err;
+
+               /* Parse codec info */
+               at91sam9g20ek_dai.codec_name = NULL;
+               codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
+               if (!codec_np) {
+                       dev_err(&pdev->dev, "codec info missing\n");
+                       return -EINVAL;
+               }
+               at91sam9g20ek_dai.codec_of_node = codec_np;
+
+               /* Parse dai and platform info */
+               at91sam9g20ek_dai.cpu_dai_name = NULL;
+               at91sam9g20ek_dai.platform_name = NULL;
+               cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
+               if (!cpu_np) {
+                       dev_err(&pdev->dev, "dai and pcm info missing\n");
+                       return -EINVAL;
+               }
+               at91sam9g20ek_dai.cpu_of_node = cpu_np;
+               at91sam9g20ek_dai.platform_of_node = cpu_np;
+
+               of_node_put(codec_np);
+               of_node_put(cpu_np);
        }
 
-       platform_set_drvdata(at91sam9g20ek_snd_device,
-                       &snd_soc_at91sam9g20ek);
-
-       ret = platform_device_add(at91sam9g20ek_snd_device);
+       ret = snd_soc_register_card(card);
        if (ret) {
-               printk(KERN_ERR "ASoC: Platform device allocation failed\n");
-               goto err_device_add;
+               printk(KERN_ERR "ASoC: snd_soc_register_card() failed\n");
        }
 
        return ret;
 
-err_device_add:
-       platform_device_put(at91sam9g20ek_snd_device);
 err_mclk:
        clk_put(mclk);
        mclk = NULL;
 err:
+       atmel_ssc_put_audio(0);
        return ret;
 }
 
-static void __exit at91sam9g20ek_exit(void)
+static int __devexit at91sam9g20ek_audio_remove(struct platform_device *pdev)
 {
-       platform_device_unregister(at91sam9g20ek_snd_device);
-       at91sam9g20ek_snd_device = NULL;
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       atmel_ssc_put_audio(0);
+       snd_soc_unregister_card(card);
        clk_put(mclk);
        mclk = NULL;
+
+       return 0;
 }
 
-module_init(at91sam9g20ek_init);
-module_exit(at91sam9g20ek_exit);
+#ifdef CONFIG_OF
+static const struct of_device_id at91sam9g20ek_wm8731_dt_ids[] = {
+       { .compatible = "atmel,at91sam9g20ek-wm8731-audio", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, at91sam9g20ek_wm8731_dt_ids);
+#endif
+
+static struct platform_driver at91sam9g20ek_audio_driver = {
+       .driver = {
+               .name   = "at91sam9g20ek-audio",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids),
+       },
+       .probe  = at91sam9g20ek_audio_probe,
+       .remove = __devexit_p(at91sam9g20ek_audio_remove),
+};
+
+module_platform_driver(at91sam9g20ek_audio_driver);
 
 /* Module information */
 MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
 MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
+MODULE_ALIAS("platform:at91sam9g20ek-audio");
 MODULE_LICENSE("GPL");
index b92759a3936157e0884b98f752935ccbef7b8945..ef03c7ab46a67a66d1b6939403e592f9441918a1 100644 (file)
@@ -146,6 +146,13 @@ config SND_SOC_WM_HUBS
        default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y
        default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m
 
+config SND_SOC_WM_ADSP
+       tristate
+       default y if SND_SOC_WM5102=y
+       default y if SND_SOC_WM2200=y
+       default m if SND_SOC_WM5102=m
+       default m if SND_SOC_WM2200=m
+
 config SND_SOC_AB8500_CODEC
        tristate
 
@@ -229,6 +236,7 @@ config SND_SOC_CX20442
        tristate
 
 config SND_SOC_JZ4740_CODEC
+       select REGMAP_MMIO
        tristate
 
 config SND_SOC_L3
index 9bd4d95aab4ffcecb9abf5bff4e0c94d7203adee..61633d5ff3da32f6114d06513205ed1e60cdb241 100644 (file)
@@ -62,6 +62,7 @@ snd-soc-twl6040-objs := twl6040.o
 snd-soc-uda134x-objs := uda134x.o
 snd-soc-uda1380-objs := uda1380.o
 snd-soc-wl1273-objs := wl1273.o
+snd-soc-wm-adsp-objs := wm_adsp.o
 snd-soc-wm0010-objs := wm0010.o
 snd-soc-wm1250-ev1-objs := wm1250-ev1.o
 snd-soc-wm2000-objs := wm2000.o
@@ -229,6 +230,7 @@ obj-$(CONFIG_SND_SOC_WM9090)        += snd-soc-wm9090.o
 obj-$(CONFIG_SND_SOC_WM9705)   += snd-soc-wm9705.o
 obj-$(CONFIG_SND_SOC_WM9712)   += snd-soc-wm9712.o
 obj-$(CONFIG_SND_SOC_WM9713)   += snd-soc-wm9713.o
+obj-$(CONFIG_SND_SOC_WM_ADSP)  += snd-soc-wm-adsp.o
 obj-$(CONFIG_SND_SOC_WM_HUBS)  += snd-soc-wm-hubs.o
 
 # Amp
index af547490b4f7f6e0b1dbada793259c64a513a203..4d96090db66297b44b10219f21f26e768c431935 100644 (file)
@@ -2356,7 +2356,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
        return 0;
 }
 
-struct snd_soc_dai_driver ab8500_codec_dai[] = {
+static struct snd_soc_dai_driver ab8500_codec_dai[] = {
        {
                .name = "ab8500-codec-dai.0",
                .id = 0,
index 31d4483245d0db951584a0e1619b91c8ac8fa726..4b11b82b227393c5d6a60311f417c7b846976bfa 100644 (file)
@@ -15,6 +15,8 @@
 #include <sound/soc.h>
 #include <sound/initval.h>
 #include <linux/spi/spi.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
 #include <sound/asoundef.h>
 
 /* AK4104 registers addresses */
@@ -98,14 +100,32 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
        val = 0;
 
        switch (params_rate(params)) {
+       case 22050:
+               val |= IEC958_AES3_CON_FS_22050;
+               break;
+       case 24000:
+               val |= IEC958_AES3_CON_FS_24000;
+               break;
+       case 32000:
+               val |= IEC958_AES3_CON_FS_32000;
+               break;
        case 44100:
                val |= IEC958_AES3_CON_FS_44100;
                break;
        case 48000:
                val |= IEC958_AES3_CON_FS_48000;
                break;
-       case 32000:
-               val |= IEC958_AES3_CON_FS_32000;
+       case 88200:
+               val |= IEC958_AES3_CON_FS_88200;
+               break;
+       case 96000:
+               val |= IEC958_AES3_CON_FS_96000;
+               break;
+       case 176400:
+               val |= IEC958_AES3_CON_FS_176400;
+               break;
+       case 192000:
+               val |= IEC958_AES3_CON_FS_192000;
                break;
        default:
                dev_err(codec->dev, "unsupported sampling rate\n");
@@ -186,6 +206,7 @@ static const struct regmap_config ak4104_regmap = {
 
 static int ak4104_spi_probe(struct spi_device *spi)
 {
+       struct device_node *np = spi->dev.of_node;
        struct ak4104_private *ak4104;
        unsigned int val;
        int ret;
@@ -201,49 +222,59 @@ static int ak4104_spi_probe(struct spi_device *spi)
        if (ak4104 == NULL)
                return -ENOMEM;
 
-       ak4104->regmap = regmap_init_spi(spi, &ak4104_regmap);
+       ak4104->regmap = devm_regmap_init_spi(spi, &ak4104_regmap);
        if (IS_ERR(ak4104->regmap)) {
                ret = PTR_ERR(ak4104->regmap);
                return ret;
        }
 
+       if (np) {
+               enum of_gpio_flags flags;
+               int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
+
+               if (gpio_is_valid(gpio)) {
+                       ret = devm_gpio_request_one(&spi->dev, gpio,
+                                    flags & OF_GPIO_ACTIVE_LOW ?
+                                       GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
+                                    "ak4104 reset");
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
        /* read the 'reserved' register - according to the datasheet, it
         * should contain 0x5b. Not a good way to verify the presence of
         * the device, but there is no hardware ID register. */
        ret = regmap_read(ak4104->regmap, AK4104_REG_RESERVED, &val);
        if (ret != 0)
-               goto err;
-       if (val != AK4104_RESERVED_VAL) {
-               ret = -ENODEV;
-               goto err;
-       }
+               return ret;
+       if (val != AK4104_RESERVED_VAL)
+               return -ENODEV;
 
        spi_set_drvdata(spi, ak4104);
 
        ret = snd_soc_register_codec(&spi->dev,
                        &soc_codec_device_ak4104, &ak4104_dai, 1);
-       if (ret != 0)
-               goto err;
-
-       return 0;
-
-err:
-       regmap_exit(ak4104->regmap);
        return ret;
 }
 
 static int __devexit ak4104_spi_remove(struct spi_device *spi)
 {
-       struct ak4104_private *ak4101 = spi_get_drvdata(spi);
-       regmap_exit(ak4101->regmap);
        snd_soc_unregister_codec(&spi->dev);
        return 0;
 }
 
+static const struct of_device_id ak4104_of_match[] = {
+       { .compatible = "asahi-kasei,ak4104", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ak4104_of_match);
+
 static struct spi_driver ak4104_spi_driver = {
        .driver  = {
                .name   = DRV_NAME,
                .owner  = THIS_MODULE,
+               .of_match_table = ak4104_of_match,
        },
        .probe  = ak4104_spi_probe,
        .remove = __devexit_p(ak4104_spi_remove),
index 618fdc30f73eb3889f2372e4bba9033d3675270d..fc5581063b2da414c789402648508cb5a1766796 100644 (file)
@@ -447,7 +447,7 @@ static __devinit int ak4535_i2c_probe(struct i2c_client *i2c,
        if (ak4535 == NULL)
                return -ENOMEM;
 
-       ak4535->regmap = regmap_init_i2c(i2c, &ak4535_regmap);
+       ak4535->regmap = devm_regmap_init_i2c(i2c, &ak4535_regmap);
        if (IS_ERR(ak4535->regmap)) {
                ret = PTR_ERR(ak4535->regmap);
                dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
@@ -458,18 +458,13 @@ static __devinit int ak4535_i2c_probe(struct i2c_client *i2c,
 
        ret = snd_soc_register_codec(&i2c->dev,
                        &soc_codec_dev_ak4535, &ak4535_dai, 1);
-       if (ret != 0)
-               regmap_exit(ak4535->regmap);
 
        return ret;
 }
 
 static __devexit int ak4535_i2c_remove(struct i2c_client *client)
 {
-       struct ak4535_priv *ak4535 = i2c_get_clientdata(client);
-
        snd_soc_unregister_codec(&client->dev);
-       regmap_exit(ak4535->regmap);
        return 0;
 }
 
index b3e24f289421a7fda3dda11043039179c4777535..546466abb77f74b6be5cad2395bbc5049a8bfb45 100644 (file)
@@ -194,12 +194,6 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = {
        {"LINEOUT Mixer", "DACL", "DAC"},
 };
 
-/* codec private data */
-struct ak4642_priv {
-       unsigned int sysclk;
-       enum snd_soc_control_type control_type;
-};
-
 /*
  * ak4642 register cache
  */
@@ -468,10 +462,9 @@ static int ak4642_resume(struct snd_soc_codec *codec)
 
 static int ak4642_probe(struct snd_soc_codec *codec)
 {
-       struct ak4642_priv *ak4642 = snd_soc_codec_get_drvdata(codec);
        int ret;
 
-       ret = snd_soc_codec_set_cache_io(codec, 8, 8, ak4642->control_type);
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
        if (ret < 0) {
                dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
                return ret;
@@ -523,21 +516,9 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = {
 static __devinit int ak4642_i2c_probe(struct i2c_client *i2c,
                                      const struct i2c_device_id *id)
 {
-       struct ak4642_priv *ak4642;
-       int ret;
-
-       ak4642 = devm_kzalloc(&i2c->dev, sizeof(struct ak4642_priv),
-                             GFP_KERNEL);
-       if (!ak4642)
-               return -ENOMEM;
-
-       i2c_set_clientdata(i2c, ak4642);
-       ak4642->control_type = SND_SOC_I2C;
-
-       ret =  snd_soc_register_codec(&i2c->dev,
+       return snd_soc_register_codec(&i2c->dev,
                                (struct snd_soc_codec_driver *)id->driver_data,
                                &ak4642_dai, 1);
-       return ret;
 }
 
 static __devexit int ak4642_i2c_remove(struct i2c_client *client)
index 054967d8bac2f6d089214e390b1bd6ce416f64b2..adf397b9d0e650eb19768e7bc6c592352014a595 100644 (file)
@@ -226,6 +226,31 @@ EXPORT_SYMBOL_GPL(arizona_mixer_values);
 const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0);
 EXPORT_SYMBOL_GPL(arizona_mixer_tlv);
 
+static const char *arizona_vol_ramp_text[] = {
+       "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
+       "15ms/6dB", "30ms/6dB",
+};
+
+const struct soc_enum arizona_in_vd_ramp =
+       SOC_ENUM_SINGLE(ARIZONA_INPUT_VOLUME_RAMP,
+                       ARIZONA_IN_VD_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_in_vd_ramp);
+
+const struct soc_enum arizona_in_vi_ramp =
+       SOC_ENUM_SINGLE(ARIZONA_INPUT_VOLUME_RAMP,
+                       ARIZONA_IN_VI_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_in_vi_ramp);
+
+const struct soc_enum arizona_out_vd_ramp =
+       SOC_ENUM_SINGLE(ARIZONA_OUTPUT_VOLUME_RAMP,
+                       ARIZONA_OUT_VD_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_out_vd_ramp);
+
+const struct soc_enum arizona_out_vi_ramp =
+       SOC_ENUM_SINGLE(ARIZONA_OUTPUT_VOLUME_RAMP,
+                       ARIZONA_OUT_VI_RAMP_SHIFT, 7, arizona_vol_ramp_text);
+EXPORT_SYMBOL_GPL(arizona_out_vi_ramp);
+
 static const char *arizona_lhpf_mode_text[] = {
        "Low-pass", "High-pass"
 };
@@ -380,6 +405,18 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
        case 49152000:
                val |= 3 << ARIZONA_SYSCLK_FREQ_SHIFT;
                break;
+       case 67737600:
+       case 73728000:
+               val |= 4 << ARIZONA_SYSCLK_FREQ_SHIFT;
+               break;
+       case 90316800:
+       case 98304000:
+               val |= 5 << ARIZONA_SYSCLK_FREQ_SHIFT;
+               break;
+       case 135475200:
+       case 147456000:
+               val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT;
+               break;
        default:
                return -EINVAL;
        }
@@ -737,6 +774,9 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
                return -EBUSY;
        }
 
+       dev_dbg(codec->dev, "Setting AIF%d to %s\n", dai->id + 1,
+               arizona_dai_clk_str(clk_id));
+
        memset(&routes, 0, sizeof(routes));
        routes[0].sink = dai->driver->capture.stream_name;
        routes[1].sink = dai->driver->playback.stream_name;
@@ -749,6 +789,8 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
        routes[1].source = arizona_dai_clk_str(clk_id);
        snd_soc_dapm_add_routes(&codec->dapm, routes, ARRAY_SIZE(routes));
 
+       dai_priv->clk = clk_id;
+
        return snd_soc_dapm_sync(&codec->dapm);
 }
 
@@ -925,6 +967,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
        bool ena;
        int ret;
 
+       if (fll->fref == Fref && fll->fout == Fout)
+               return 0;
+
        ret = regmap_read(arizona->regmap, fll->base + 1, &reg);
        if (ret != 0) {
                arizona_fll_err(fll, "Failed to read current state: %d\n",
@@ -970,6 +1015,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
                if (ena)
                        pm_runtime_put_autosuspend(arizona->dev);
 
+               fll->fref = Fref;
+               fll->fout = Fout;
+
                return 0;
        }
 
@@ -998,10 +1046,13 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
                                   ARIZONA_FLL1_SYNC_ENA);
 
        ret = wait_for_completion_timeout(&fll->ok,
-                                         msecs_to_jiffies(25));
+                                         msecs_to_jiffies(250));
        if (ret == 0)
                arizona_fll_warn(fll, "Timed out waiting for lock\n");
 
+       fll->fref = Fref;
+       fll->fout = Fout;
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(arizona_set_fll);
index 36ec64946120077455ab218c85aef34eaa29ef9a..41dae1ed3b714a4734448b1d3c99938af1fe9d9a 100644 (file)
@@ -17,6 +17,8 @@
 
 #include <sound/soc.h>
 
+#include "wm_adsp.h"
+
 #define ARIZONA_CLK_SYSCLK         1
 #define ARIZONA_CLK_ASYNCCLK       2
 #define ARIZONA_CLK_OPCLK          3
 #define ARIZONA_MIXER_VOL_SHIFT                 1
 #define ARIZONA_MIXER_VOL_WIDTH                 7
 
-#define ARIZONA_MAX_DAI 3
+#define ARIZONA_MAX_DAI  4
+#define ARIZONA_MAX_ADSP 4
 
 struct arizona;
+struct wm_adsp;
 
 struct arizona_dai_priv {
        int clk;
 };
 
 struct arizona_priv {
+       struct wm_adsp adsp[ARIZONA_MAX_ADSP];
        struct arizona *arizona;
        int sysclk;
        int asyncclk;
@@ -89,19 +94,30 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
        const struct snd_kcontrol_new name##_mux =      \
                SOC_DAPM_VALUE_ENUM("Route", name##_enum)
 
+#define ARIZONA_MUX_ENUMS(name, base_reg) \
+       static ARIZONA_MUX_ENUM_DECL(name##_enum, base_reg);      \
+       static ARIZONA_MUX_CTL_DECL(name)
+
 #define ARIZONA_MIXER_ENUMS(name, base_reg) \
-       static ARIZONA_MUX_ENUM_DECL(name##_in1_enum, base_reg);      \
-       static ARIZONA_MUX_ENUM_DECL(name##_in2_enum, base_reg + 2);  \
-       static ARIZONA_MUX_ENUM_DECL(name##_in3_enum, base_reg + 4);  \
-       static ARIZONA_MUX_ENUM_DECL(name##_in4_enum, base_reg + 6);  \
-       static ARIZONA_MUX_CTL_DECL(name##_in1); \
-       static ARIZONA_MUX_CTL_DECL(name##_in2); \
-       static ARIZONA_MUX_CTL_DECL(name##_in3); \
-       static ARIZONA_MUX_CTL_DECL(name##_in4)
+       ARIZONA_MUX_ENUMS(name##_in1, base_reg);     \
+       ARIZONA_MUX_ENUMS(name##_in2, base_reg + 2); \
+       ARIZONA_MUX_ENUMS(name##_in3, base_reg + 4); \
+       ARIZONA_MUX_ENUMS(name##_in4, base_reg + 6)
+
+#define ARIZONA_DSP_AUX_ENUMS(name, base_reg) \
+       ARIZONA_MUX_ENUMS(name##_aux1, base_reg);       \
+       ARIZONA_MUX_ENUMS(name##_aux2, base_reg + 8);   \
+       ARIZONA_MUX_ENUMS(name##_aux3, base_reg + 16);  \
+       ARIZONA_MUX_ENUMS(name##_aux4, base_reg + 24);  \
+       ARIZONA_MUX_ENUMS(name##_aux5, base_reg + 32);  \
+       ARIZONA_MUX_ENUMS(name##_aux6, base_reg + 40)
 
 #define ARIZONA_MUX(name, ctrl) \
        SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
 
+#define ARIZONA_MUX_WIDGETS(name, name_str) \
+       ARIZONA_MUX(name_str " Input", &name##_mux)
+
 #define ARIZONA_MIXER_WIDGETS(name, name_str)  \
        ARIZONA_MUX(name_str " Input 1", &name##_in1_mux), \
        ARIZONA_MUX(name_str " Input 2", &name##_in2_mux), \
@@ -109,6 +125,19 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
        ARIZONA_MUX(name_str " Input 4", &name##_in4_mux), \
        SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
 
+#define ARIZONA_DSP_WIDGETS(name, name_str) \
+       ARIZONA_MIXER_WIDGETS(name##L, name_str "L"), \
+       ARIZONA_MIXER_WIDGETS(name##R, name_str "R"), \
+       ARIZONA_MUX(name_str " Aux 1", &name##_aux1_mux), \
+       ARIZONA_MUX(name_str " Aux 2", &name##_aux2_mux), \
+       ARIZONA_MUX(name_str " Aux 3", &name##_aux3_mux), \
+       ARIZONA_MUX(name_str " Aux 4", &name##_aux4_mux), \
+       ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \
+       ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux)
+
+#define ARIZONA_MUX_ROUTES(name) \
+       ARIZONA_MIXER_INPUT_ROUTES(name " Input")
+
 #define ARIZONA_MIXER_ROUTES(widget, name) \
        { widget, NULL, name " Mixer" },         \
        { name " Mixer", NULL, name " Input 1" }, \
@@ -120,6 +149,28 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
        ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \
        ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
 
+#define ARIZONA_DSP_ROUTES(name) \
+       { name, NULL, name " Aux 1" }, \
+       { name, NULL, name " Aux 2" }, \
+       { name, NULL, name " Aux 3" }, \
+       { name, NULL, name " Aux 4" }, \
+       { name, NULL, name " Aux 5" }, \
+       { name, NULL, name " Aux 6" }, \
+       ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \
+       ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \
+       ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \
+       ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \
+       ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \
+       ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \
+       ARIZONA_MIXER_ROUTES(name, name "L"), \
+       ARIZONA_MIXER_ROUTES(name, name "R")
+
+extern const struct soc_enum arizona_in_vi_ramp;
+extern const struct soc_enum arizona_in_vd_ramp;
+
+extern const struct soc_enum arizona_out_vi_ramp;
+extern const struct soc_enum arizona_out_vd_ramp;
+
 extern const struct soc_enum arizona_lhpf1_mode;
 extern const struct soc_enum arizona_lhpf2_mode;
 extern const struct soc_enum arizona_lhpf3_mode;
@@ -146,6 +197,8 @@ struct arizona_fll {
        unsigned int vco_mult;
        struct completion lock;
        struct completion ok;
+       unsigned int fref;
+       unsigned int fout;
 
        char lock_name[ARIZONA_FLL_NAME_LEN];
        char clock_ok_name[ARIZONA_FLL_NAME_LEN];
index e3f0a7f3131e14b7895aadf2daf8bf81f6058d8b..6ad3878db8fcf4663cf948812f6b063a5993acfb 100644 (file)
@@ -474,15 +474,25 @@ static int cs4271_probe(struct snd_soc_codec *codec)
        struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
        int ret;
        int gpio_nreset = -EINVAL;
+       int amutec_eq_bmutec = 0;
 
 #ifdef CONFIG_OF
-       if (of_match_device(cs4271_dt_ids, codec->dev))
+       if (of_match_device(cs4271_dt_ids, codec->dev)) {
                gpio_nreset = of_get_named_gpio(codec->dev->of_node,
                                                "reset-gpio", 0);
+
+               if (!of_get_property(codec->dev->of_node,
+                                    "cirrus,amutec-eq-bmutec", NULL))
+                       amutec_eq_bmutec = 1;
+       }
 #endif
 
-       if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset))
-               gpio_nreset = cs4271plat->gpio_nreset;
+       if (cs4271plat) {
+               if (gpio_is_valid(cs4271plat->gpio_nreset))
+                       gpio_nreset = cs4271plat->gpio_nreset;
+
+               amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
+       }
 
        if (gpio_nreset >= 0)
                if (devm_gpio_request(codec->dev, gpio_nreset, "CS4271 Reset"))
@@ -528,6 +538,11 @@ static int cs4271_probe(struct snd_soc_codec *codec)
        /* Power-up sequence requires 85 uS */
        udelay(85);
 
+       if (amutec_eq_bmutec)
+               snd_soc_update_bits(codec, CS4271_MODE2,
+                                   CS4271_MODE2_MUTECAEQUB,
+                                   CS4271_MODE2_MUTECAEQUB);
+
        return snd_soc_add_codec_controls(codec, cs4271_snd_controls,
                ARRAY_SIZE(cs4271_snd_controls));
 }
index af5db70805199e81b06e7d48a8e844ae504489b2..843c1eb72faf54aca00be107a74a5a47b2cbf991 100644 (file)
@@ -1231,7 +1231,7 @@ static int __devinit da7210_i2c_probe(struct i2c_client *i2c,
 
        i2c_set_clientdata(i2c, da7210);
 
-       da7210->regmap = regmap_init_i2c(i2c, &da7210_regmap_config_i2c);
+       da7210->regmap = devm_regmap_init_i2c(i2c, &da7210_regmap_config_i2c);
        if (IS_ERR(da7210->regmap)) {
                ret = PTR_ERR(da7210->regmap);
                dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
@@ -1245,24 +1245,15 @@ static int __devinit da7210_i2c_probe(struct i2c_client *i2c,
 
        ret =  snd_soc_register_codec(&i2c->dev,
                        &soc_codec_dev_da7210, &da7210_dai, 1);
-       if (ret < 0) {
+       if (ret < 0)
                dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
-               goto err_regmap;
-       }
-       return ret;
-
-err_regmap:
-       regmap_exit(da7210->regmap);
 
        return ret;
 }
 
 static int __devexit da7210_i2c_remove(struct i2c_client *client)
 {
-       struct da7210_priv *da7210 = i2c_get_clientdata(client);
-
        snd_soc_unregister_codec(&client->dev);
-       regmap_exit(da7210->regmap);
        return 0;
 }
 
@@ -1346,24 +1337,15 @@ static int __devinit da7210_spi_probe(struct spi_device *spi)
        if (ret != 0)
                dev_warn(&spi->dev, "Failed to apply regmap patch: %d\n", ret);
 
-       ret =  snd_soc_register_codec(&spi->dev,
+       ret = snd_soc_register_codec(&spi->dev,
                        &soc_codec_dev_da7210, &da7210_dai, 1);
-       if (ret < 0)
-               goto err_regmap;
-
-       return ret;
-
-err_regmap:
-       regmap_exit(da7210->regmap);
 
        return ret;
 }
 
 static int __devexit da7210_spi_remove(struct spi_device *spi)
 {
-       struct da7210_priv *da7210 = spi_get_drvdata(spi);
        snd_soc_unregister_codec(&spi->dev);
-       regmap_exit(da7210->regmap);
        return 0;
 }
 
index f379b085c39204dfeb2a6fc34d7c07d9f15c9f0b..d3a6de2e757b9f8d0b96fd2ac0f3444f2d017dc5 100644 (file)
 #define DA9055_AIF_FORMAT_I2S_MODE     (0 << 0)
 #define DA9055_AIF_FORMAT_LEFT_J       (1 << 0)
 #define DA9055_AIF_FORMAT_RIGHT_J      (2 << 0)
+#define DA9055_AIF_FORMAT_DSP          (3 << 0)
 #define DA9055_AIF_WORD_S16_LE         (0 << 2)
 #define DA9055_AIF_WORD_S20_3LE                (1 << 2)
 #define DA9055_AIF_WORD_S24_LE         (2 << 2)
@@ -752,6 +753,17 @@ static const struct snd_kcontrol_new da9055_dapm_mixoutr_controls[] = {
                        6, 1, 0),
 };
 
+/* Headphone Output Enable */
+static const struct snd_kcontrol_new da9055_dapm_hp_l_control =
+SOC_DAPM_SINGLE("Switch", DA9055_HP_L_CTRL, 3, 1, 0);
+
+static const struct snd_kcontrol_new da9055_dapm_hp_r_control =
+SOC_DAPM_SINGLE("Switch", DA9055_HP_R_CTRL, 3, 1, 0);
+
+/* Lineout Output Enable */
+static const struct snd_kcontrol_new da9055_dapm_lineout_control =
+SOC_DAPM_SINGLE("Switch", DA9055_LINE_CTRL, 3, 1, 0);
+
 /* DAPM widgets */
 static const struct snd_soc_dapm_widget da9055_dapm_widgets[] = {
        /* Input Side */
@@ -816,6 +828,14 @@ static const struct snd_soc_dapm_widget da9055_dapm_widgets[] = {
                           &da9055_dapm_mixoutr_controls[0],
                           ARRAY_SIZE(da9055_dapm_mixoutr_controls)),
 
+       /* Output Enable Switches */
+       SND_SOC_DAPM_SWITCH("Headphone Left Enable", SND_SOC_NOPM, 0, 0,
+                           &da9055_dapm_hp_l_control),
+       SND_SOC_DAPM_SWITCH("Headphone Right Enable", SND_SOC_NOPM, 0, 0,
+                           &da9055_dapm_hp_r_control),
+       SND_SOC_DAPM_SWITCH("Lineout Enable", SND_SOC_NOPM, 0, 0,
+                           &da9055_dapm_lineout_control),
+
        /* Output PGAs */
        SND_SOC_DAPM_PGA("MIXOUT Left", DA9055_MIXOUT_L_CTRL, 7, 0, NULL, 0),
        SND_SOC_DAPM_PGA("MIXOUT Right", DA9055_MIXOUT_R_CTRL, 7, 0, NULL, 0),
@@ -901,17 +921,20 @@ static const struct snd_soc_dapm_route da9055_audio_map[] = {
        {"Out Mixer Right", "DAC Right Switch", "DAC Right"},
 
        {"MIXOUT Left", NULL, "Out Mixer Left"},
-       {"Headphone Left", NULL, "MIXOUT Left"},
+       {"Headphone Left Enable", "Switch", "MIXOUT Left"},
+       {"Headphone Left", NULL, "Headphone Left Enable"},
        {"Headphone Left", NULL, "Charge Pump"},
        {"HPL", NULL, "Headphone Left"},
 
        {"MIXOUT Right", NULL, "Out Mixer Right"},
-       {"Headphone Right", NULL, "MIXOUT Right"},
+       {"Headphone Right Enable", "Switch", "MIXOUT Right"},
+       {"Headphone Right", NULL, "Headphone Right Enable"},
        {"Headphone Right", NULL, "Charge Pump"},
        {"HPR", NULL, "Headphone Right"},
 
        {"MIXOUT Right", NULL, "Out Mixer Right"},
-       {"Lineout", NULL, "MIXOUT Right"},
+       {"Lineout Enable", "Switch", "MIXOUT Right"},
+       {"Lineout", NULL, "Lineout Enable"},
        {"LINE", NULL, "Lineout"},
 };
 
@@ -1175,6 +1198,9 @@ static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
        case SND_SOC_DAIFMT_RIGHT_J:
                aif_ctrl = DA9055_AIF_FORMAT_RIGHT_J;
                break;
+       case SND_SOC_DAIFMT_DSP_A:
+               aif_ctrl = DA9055_AIF_FORMAT_DSP;
+               break;
        default:
                return -EINVAL;
        }
@@ -1390,8 +1416,7 @@ static int da9055_probe(struct snd_soc_codec *codec)
                            DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
 
        /*
-        * There are two separate control bits for input and output mixers as
-        * well as headphone and line outs.
+        * There are two separate control bits for input and output mixers.
         * One to enable corresponding amplifier and other to enable its
         * output. As amplifier bits are related to power control, they are
         * being managed by DAPM while other (non power related) bits are
@@ -1407,14 +1432,6 @@ static int da9055_probe(struct snd_soc_codec *codec)
        snd_soc_update_bits(codec, DA9055_MIXOUT_R_CTRL,
                            DA9055_MIXOUT_R_MIX_EN, DA9055_MIXOUT_R_MIX_EN);
 
-       snd_soc_update_bits(codec, DA9055_HP_L_CTRL,
-                           DA9055_HP_L_AMP_OE, DA9055_HP_L_AMP_OE);
-       snd_soc_update_bits(codec, DA9055_HP_R_CTRL,
-                           DA9055_HP_R_AMP_OE, DA9055_HP_R_AMP_OE);
-
-       snd_soc_update_bits(codec, DA9055_LINE_CTRL,
-                           DA9055_LINE_AMP_OE, DA9055_LINE_AMP_OE);
-
        /* Set this as per your system configuration */
        snd_soc_write(codec, DA9055_PLL_CTRL, DA9055_PLL_INDIV_10_20_MHZ);
 
index 85d9cabe6d555d9e28e3812f1d97f631434ff461..9ad1da36887e54316ed3568d556625f743c40363 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/io.h>
+#include <linux/regmap.h>
 
 #include <linux/delay.h>
 
 #include <sound/pcm_params.h>
 #include <sound/initval.h>
 #include <sound/soc.h>
+#include <sound/tlv.h>
 
 #define JZ4740_REG_CODEC_1 0x0
-#define JZ4740_REG_CODEC_2 0x1
+#define JZ4740_REG_CODEC_2 0x4
 
 #define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
 #define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
 #define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET    4
 #define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET  0
 
-static const uint32_t jz4740_codec_regs[] = {
-       0x021b2302, 0x00170803,
+static const struct reg_default jz4740_codec_reg_defaults[] = {
+       { JZ4740_REG_CODEC_1, 0x021b2302 },
+       { JZ4740_REG_CODEC_2, 0x00170803 },
 };
 
 struct jz4740_codec {
-       void __iomem *base;
-       struct resource *mem;
+       struct regmap *regmap;
 };
 
-static unsigned int jz4740_codec_read(struct snd_soc_codec *codec,
-       unsigned int reg)
-{
-       struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
-       return readl(jz4740_codec->base + (reg << 2));
-}
-
-static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
-       unsigned int val)
-{
-       struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
-       u32 *cache = codec->reg_cache;
-
-       cache[reg] = val;
-       writel(val, jz4740_codec->base + (reg << 2));
+static const unsigned int jz4740_mic_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 2, TLV_DB_SCALE_ITEM(0, 600, 0),
+       3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0),
+};
 
-       return 0;
-}
+static const DECLARE_TLV_DB_SCALE(jz4740_out_tlv, 0, 200, 0);
+static const DECLARE_TLV_DB_SCALE(jz4740_in_tlv, -3450, 150, 0);
 
 static const struct snd_kcontrol_new jz4740_codec_controls[] = {
-       SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
-                       JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
-       SOC_SINGLE("Master Capture Volume", JZ4740_REG_CODEC_2,
-                       JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
+       SOC_SINGLE_TLV("Master Playback Volume", JZ4740_REG_CODEC_2,
+                       JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0,
+                       jz4740_out_tlv),
+       SOC_SINGLE_TLV("Master Capture Volume", JZ4740_REG_CODEC_2,
+                       JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0,
+                       jz4740_in_tlv),
        SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
                        JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
-       SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
-                       JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
+       SOC_SINGLE_TLV("Mic Capture Volume", JZ4740_REG_CODEC_2,
+                       JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0,
+                       jz4740_mic_tlv),
 };
 
 static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
@@ -163,8 +158,8 @@ static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
 static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
+       struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(dai->codec);
        uint32_t val;
-       struct snd_soc_codec *codec = dai->codec;
 
        switch (params_rate(params)) {
        case 8000:
@@ -200,7 +195,7 @@ static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
 
        val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
 
-       snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
+       regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_2,
                                JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
 
        return 0;
@@ -230,25 +225,23 @@ static struct snd_soc_dai_driver jz4740_codec_dai = {
        .symmetric_rates = 1,
 };
 
-static void jz4740_codec_wakeup(struct snd_soc_codec *codec)
+static void jz4740_codec_wakeup(struct regmap *regmap)
 {
-       int i;
-       uint32_t *cache = codec->reg_cache;
-
-       snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+       regmap_update_bits(regmap, JZ4740_REG_CODEC_1,
                JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
        udelay(2);
 
-       snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+       regmap_update_bits(regmap, JZ4740_REG_CODEC_1,
                JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
 
-       for (i = 0; i < ARRAY_SIZE(jz4740_codec_regs); ++i)
-               jz4740_codec_write(codec, i, cache[i]);
+       regcache_sync(regmap);
 }
 
 static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
        enum snd_soc_bias_level level)
 {
+       struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
+       struct regmap *regmap = jz4740_codec->regmap;
        unsigned int mask;
        unsigned int value;
 
@@ -261,12 +254,12 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
                                JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
                value = 0;
 
-               snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+               regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
                break;
        case SND_SOC_BIAS_STANDBY:
                /* The only way to clear the suspend flag is to reset the codec */
                if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
-                       jz4740_codec_wakeup(codec);
+                       jz4740_codec_wakeup(regmap);
 
                mask = JZ4740_CODEC_1_VREF_DISABLE |
                        JZ4740_CODEC_1_VREF_AMP_DISABLE |
@@ -275,13 +268,14 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
                        JZ4740_CODEC_1_VREF_AMP_DISABLE |
                        JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
 
-               snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+               regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
                break;
        case SND_SOC_BIAS_OFF:
                mask = JZ4740_CODEC_1_SUSPEND;
                value = JZ4740_CODEC_1_SUSPEND;
 
-               snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+               regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
+               regcache_mark_dirty(regmap);
                break;
        default:
                break;
@@ -294,7 +288,9 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
 
 static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
 {
-       snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+       struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
+
+       regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1,
                        JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
 
        jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -331,12 +327,7 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
        .remove = jz4740_codec_dev_remove,
        .suspend = jz4740_codec_suspend,
        .resume = jz4740_codec_resume,
-       .read = jz4740_codec_read,
-       .write = jz4740_codec_write,
        .set_bias_level = jz4740_codec_set_bias_level,
-       .reg_cache_default      = jz4740_codec_regs,
-       .reg_word_size = sizeof(u32),
-       .reg_cache_size = 2,
 
        .controls = jz4740_codec_controls,
        .num_controls = ARRAY_SIZE(jz4740_codec_controls),
@@ -346,11 +337,23 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
        .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes),
 };
 
+static const struct regmap_config jz4740_codec_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = JZ4740_REG_CODEC_2,
+
+       .reg_defaults = jz4740_codec_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults),
+       .cache_type = REGCACHE_RBTREE,
+};
+
 static int __devinit jz4740_codec_probe(struct platform_device *pdev)
 {
        int ret;
        struct jz4740_codec *jz4740_codec;
        struct resource *mem;
+       void __iomem *base;
 
        jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec),
                                    GFP_KERNEL);
@@ -358,56 +361,29 @@ static int __devinit jz4740_codec_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!mem) {
-               dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
-               ret = -ENOENT;
-               goto err_out;
-       }
-
-       mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
-       if (!mem) {
-               dev_err(&pdev->dev, "Failed to request mmio memory region\n");
-               ret = -EBUSY;
-               goto err_out;
-       }
+       base = devm_request_and_ioremap(&pdev->dev, mem);
+       if (!base)
+               return -EBUSY;
 
-       jz4740_codec->base = ioremap(mem->start, resource_size(mem));
-       if (!jz4740_codec->base) {
-               dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
-               ret = -EBUSY;
-               goto err_release_mem_region;
-       }
-       jz4740_codec->mem = mem;
+       jz4740_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+                                           &jz4740_codec_regmap_config);
+       if (IS_ERR(jz4740_codec->regmap))
+               return PTR_ERR(jz4740_codec->regmap);
 
        platform_set_drvdata(pdev, jz4740_codec);
 
        ret = snd_soc_register_codec(&pdev->dev,
                        &soc_codec_dev_jz4740_codec, &jz4740_codec_dai, 1);
-       if (ret) {
+       if (ret)
                dev_err(&pdev->dev, "Failed to register codec\n");
-               goto err_iounmap;
-       }
 
-       return 0;
-
-err_iounmap:
-       iounmap(jz4740_codec->base);
-err_release_mem_region:
-       release_mem_region(mem->start, resource_size(mem));
-err_out:
        return ret;
 }
 
 static int __devexit jz4740_codec_remove(struct platform_device *pdev)
 {
-       struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
-       struct resource *mem = jz4740_codec->mem;
-
        snd_soc_unregister_codec(&pdev->dev);
 
-       iounmap(jz4740_codec->base);
-       release_mem_region(mem->start, resource_size(mem));
-
        platform_set_drvdata(pdev, NULL);
 
        return 0;
index 99b0a9dcff346ac68b3915ae320b60645cb6a01f..096b6aa87f0f7fd508bb6c508cc8d821f7b1db27 100644 (file)
@@ -1497,7 +1497,7 @@ static __devinit int lm49453_i2c_probe(struct i2c_client *i2c,
 
        i2c_set_clientdata(i2c, lm49453);
 
-       lm49453->regmap = regmap_init_i2c(i2c, &lm49453_regmap_config);
+       lm49453->regmap = devm_regmap_init_i2c(i2c, &lm49453_regmap_config);
        if (IS_ERR(lm49453->regmap)) {
                ret = PTR_ERR(lm49453->regmap);
                dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -1508,21 +1508,15 @@ static __devinit int lm49453_i2c_probe(struct i2c_client *i2c,
        ret =  snd_soc_register_codec(&i2c->dev,
                                      &soc_codec_dev_lm49453,
                                      lm49453_dai, ARRAY_SIZE(lm49453_dai));
-       if (ret < 0) {
+       if (ret < 0)
                dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
-               regmap_exit(lm49453->regmap);
-               return ret;
-       }
 
        return ret;
 }
 
 static int __devexit lm49453_i2c_remove(struct i2c_client *client)
 {
-       struct lm49453_priv *lm49453 = i2c_get_clientdata(client);
-
        snd_soc_unregister_codec(&client->dev);
-       regmap_exit(lm49453->regmap);
        return 0;
 }
 
index 3264a5169306fc3f8d769912173f803fae3d17a4..b858264235c44e8f77f0adaef778b9f01d828a88 100644 (file)
@@ -2098,13 +2098,13 @@ static const struct i2c_device_id max98088_i2c_id[] = {
 MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
 
 static struct i2c_driver max98088_i2c_driver = {
-       .driver = {
-               .name = "max98088",
-               .owner = THIS_MODULE,
-       },
-       .probe  = max98088_i2c_probe,
-       .remove = __devexit_p(max98088_i2c_remove),
-       .id_table = max98088_i2c_id,
+       .driver = {
+               .name = "max98088",
+               .owner = THIS_MODULE,
+       },
+       .probe  = max98088_i2c_probe,
+       .remove = max98088_i2c_remove,
+       .id_table = max98088_i2c_id,
 };
 
 module_i2c_driver(max98088_i2c_driver);
index eab64a193989ac2917773a4df7613cbf644dd58f..06d4e612a164e45505c664b1b6be455a85d9aaa3 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
+#include <linux/firmware.h>
 #include <linux/gcd.h>
 #include <linux/gpio.h>
 #include <linux/i2c.h>
 #include <sound/wm2200.h>
 
 #include "wm2200.h"
+#include "wmfw.h"
+
+#define WM2200_DSP_CONTROL_1                   0x00
+#define WM2200_DSP_CONTROL_2                   0x02
+#define WM2200_DSP_CONTROL_3                   0x03
+#define WM2200_DSP_CONTROL_4                   0x04
+#define WM2200_DSP_CONTROL_5                   0x06
+#define WM2200_DSP_CONTROL_6                   0x07
+#define WM2200_DSP_CONTROL_7                   0x08
+#define WM2200_DSP_CONTROL_8                   0x09
+#define WM2200_DSP_CONTROL_9                   0x0A
+#define WM2200_DSP_CONTROL_10                  0x0B
+#define WM2200_DSP_CONTROL_11                  0x0C
+#define WM2200_DSP_CONTROL_12                  0x0D
+#define WM2200_DSP_CONTROL_13                  0x0F
+#define WM2200_DSP_CONTROL_14                  0x10
+#define WM2200_DSP_CONTROL_15                  0x11
+#define WM2200_DSP_CONTROL_16                  0x12
+#define WM2200_DSP_CONTROL_17                  0x13
+#define WM2200_DSP_CONTROL_18                  0x14
+#define WM2200_DSP_CONTROL_19                  0x16
+#define WM2200_DSP_CONTROL_20                  0x17
+#define WM2200_DSP_CONTROL_21                  0x18
+#define WM2200_DSP_CONTROL_22                  0x1A
+#define WM2200_DSP_CONTROL_23                  0x1B
+#define WM2200_DSP_CONTROL_24                  0x1C
+#define WM2200_DSP_CONTROL_25                  0x1E
+#define WM2200_DSP_CONTROL_26                  0x20
+#define WM2200_DSP_CONTROL_27                  0x21
+#define WM2200_DSP_CONTROL_28                  0x22
+#define WM2200_DSP_CONTROL_29                  0x23
+#define WM2200_DSP_CONTROL_30                  0x24
+#define WM2200_DSP_CONTROL_31                  0x26
 
 /* The code assumes DCVDD is generated internally */
 #define WM2200_NUM_CORE_SUPPLIES 2
@@ -64,6 +98,66 @@ struct wm2200_priv {
        int sysclk;
 };
 
+#define WM2200_DSP_RANGE_BASE (WM2200_MAX_REGISTER + 1)
+#define WM2200_DSP_SPACING 12288
+
+#define WM2200_DSP1_DM_BASE (WM2200_DSP_RANGE_BASE + (0 * WM2200_DSP_SPACING))
+#define WM2200_DSP1_PM_BASE (WM2200_DSP_RANGE_BASE + (1 * WM2200_DSP_SPACING))
+#define WM2200_DSP1_ZM_BASE (WM2200_DSP_RANGE_BASE + (2 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_DM_BASE (WM2200_DSP_RANGE_BASE + (3 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_PM_BASE (WM2200_DSP_RANGE_BASE + (4 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_ZM_BASE (WM2200_DSP_RANGE_BASE + (5 * WM2200_DSP_SPACING))
+
+static const struct regmap_range_cfg wm2200_ranges[] = {
+       /* DSP1 DM */
+       { .range_min = WM2200_DSP1_DM_BASE,
+         .range_max = WM2200_DSP1_DM_BASE + 12287,
+         .selector_reg = WM2200_DSP1_CONTROL_3,
+         .selector_mask = WM2200_DSP1_PAGE_BASE_DM_0_MASK,
+         .selector_shift = WM2200_DSP1_PAGE_BASE_DM_0_SHIFT,
+         .window_start = WM2200_DSP1_DM_0, .window_len = 2048, },
+
+       /* DSP1 PM */
+       { .range_min = WM2200_DSP1_PM_BASE,
+         .range_max = WM2200_DSP1_PM_BASE + 12287,
+         .selector_reg = WM2200_DSP1_CONTROL_2,
+         .selector_mask = WM2200_DSP1_PAGE_BASE_PM_0_MASK,
+         .selector_shift = WM2200_DSP1_PAGE_BASE_PM_0_SHIFT,
+         .window_start = WM2200_DSP1_PM_0, .window_len = 768, },
+
+       /* DSP1 ZM */
+       { .range_min = WM2200_DSP1_ZM_BASE,
+         .range_max = WM2200_DSP1_ZM_BASE + 2047,
+         .selector_reg = WM2200_DSP1_CONTROL_4,
+         .selector_mask = WM2200_DSP1_PAGE_BASE_ZM_0_MASK,
+         .selector_shift = WM2200_DSP1_PAGE_BASE_ZM_0_SHIFT,
+         .window_start = WM2200_DSP1_ZM_0, .window_len = 1024, },
+
+       /* DSP2 DM */
+       { .range_min = WM2200_DSP2_DM_BASE,
+         .range_max = WM2200_DSP2_DM_BASE + 4095,
+         .selector_reg = WM2200_DSP2_CONTROL_3,
+         .selector_mask = WM2200_DSP2_PAGE_BASE_DM_0_MASK,
+         .selector_shift = WM2200_DSP2_PAGE_BASE_DM_0_SHIFT,
+         .window_start = WM2200_DSP2_DM_0, .window_len = 2048, },
+
+       /* DSP2 PM */
+       { .range_min = WM2200_DSP2_PM_BASE,
+         .range_max = WM2200_DSP2_PM_BASE + 11287,
+         .selector_reg = WM2200_DSP2_CONTROL_2,
+         .selector_mask = WM2200_DSP2_PAGE_BASE_PM_0_MASK,
+         .selector_shift = WM2200_DSP2_PAGE_BASE_PM_0_SHIFT,
+         .window_start = WM2200_DSP2_PM_0, .window_len = 768, },
+
+       /* DSP2 ZM */
+       { .range_min = WM2200_DSP2_ZM_BASE,
+         .range_max = WM2200_DSP2_ZM_BASE + 2047,
+         .selector_reg = WM2200_DSP2_CONTROL_4,
+         .selector_mask = WM2200_DSP2_PAGE_BASE_ZM_0_MASK,
+         .selector_shift = WM2200_DSP2_PAGE_BASE_ZM_0_SHIFT,
+         .window_start = WM2200_DSP2_ZM_0, .window_len = 1024, },
+};
+
 static struct reg_default wm2200_reg_defaults[] = {
        { 0x000B, 0x0000 },   /* R11    - Tone Generator 1 */
        { 0x0102, 0x0000 },   /* R258   - Clocking 3 */
@@ -407,6 +501,16 @@ static struct reg_default wm2200_reg_defaults[] = {
 
 static bool wm2200_volatile_register(struct device *dev, unsigned int reg)
 {
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(wm2200_ranges); i++)
+               if ((reg >= wm2200_ranges[i].window_start &&
+                    reg <= wm2200_ranges[i].window_start +
+                    wm2200_ranges[i].window_len) ||
+                   (reg >= wm2200_ranges[i].range_min &&
+                    reg <= wm2200_ranges[i].range_max))
+                       return true;
+
        switch (reg) {
        case WM2200_SOFTWARE_RESET:
        case WM2200_DEVICE_REVISION:
@@ -423,6 +527,16 @@ static bool wm2200_volatile_register(struct device *dev, unsigned int reg)
 
 static bool wm2200_readable_register(struct device *dev, unsigned int reg)
 {
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(wm2200_ranges); i++)
+               if ((reg >= wm2200_ranges[i].window_start &&
+                    reg <= wm2200_ranges[i].window_start +
+                    wm2200_ranges[i].window_len) ||
+                   (reg >= wm2200_ranges[i].range_min &&
+                    reg <= wm2200_ranges[i].range_max))
+                       return true;
+
        switch (reg) {
        case WM2200_SOFTWARE_RESET:
        case WM2200_DEVICE_REVISION:
@@ -873,6 +987,400 @@ static int wm2200_reset(struct wm2200_priv *wm2200)
        }
 }
 
+static int wm2200_dsp_load(struct snd_soc_codec *codec, int base)
+{
+       const struct firmware *firmware;
+       struct regmap *regmap = codec->control_data;
+       unsigned int pos = 0;
+       const struct wmfw_header *header;
+       const struct wmfw_adsp1_sizes *adsp1_sizes;
+       const struct wmfw_footer *footer;
+       const struct wmfw_region *region;
+       const char *file, *region_name;
+       char *text;
+       unsigned int dm, pm, zm, reg;
+       int regions = 0;
+       int ret, offset, type;
+
+       switch (base) {
+       case WM2200_DSP1_CONTROL_1:
+               file = "wm2200-dsp1.wmfw";
+               dm = WM2200_DSP1_DM_BASE;
+               pm = WM2200_DSP1_PM_BASE;
+               zm = WM2200_DSP1_ZM_BASE;
+               break;
+       case WM2200_DSP2_CONTROL_1:
+               file = "wm2200-dsp2.wmfw";
+               dm = WM2200_DSP2_DM_BASE;
+               pm = WM2200_DSP2_PM_BASE;
+               zm = WM2200_DSP2_ZM_BASE;
+               break;
+       default:
+               dev_err(codec->dev, "BASE %x\n", base);
+               BUG_ON(1);
+               return -EINVAL;
+       }
+
+       ret = request_firmware(&firmware, file, codec->dev);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request '%s'\n", file);
+               return ret;
+       }
+
+       pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+       if (pos >= firmware->size) {
+               dev_err(codec->dev, "%s: file too short, %d bytes\n",
+                       file, firmware->size);
+               return -EINVAL;
+       }
+
+       header = (void*)&firmware->data[0];
+
+       if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
+               dev_err(codec->dev, "%s: invalid magic\n", file);
+               return -EINVAL;
+       }
+
+       if (header->ver != 0) {
+               dev_err(codec->dev, "%s: unknown file format %d\n",
+                       file, header->ver);
+               return -EINVAL;
+       }
+
+       if (le32_to_cpu(header->len) != sizeof(*header) +
+           sizeof(*adsp1_sizes) + sizeof(*footer)) {
+               dev_err(codec->dev, "%s: unexpected header length %d\n",
+                       file, le32_to_cpu(header->len));
+               return -EINVAL;
+       }
+
+       if (header->core != WMFW_ADSP1) {
+               dev_err(codec->dev, "%s: invalid core %d\n",
+                       file, header->core);
+               return -EINVAL;
+       }
+
+       adsp1_sizes = (void *)&(header[1]);
+       footer = (void *)&(adsp1_sizes[1]);
+
+       dev_dbg(codec->dev, "%s: %d DM, %d PM, %d ZM\n",
+               file, le32_to_cpu(adsp1_sizes->dm),
+               le32_to_cpu(adsp1_sizes->pm), le32_to_cpu(adsp1_sizes->zm));
+
+       dev_dbg(codec->dev, "%s: timestamp %llu\n", file,
+               le64_to_cpu(footer->timestamp));
+
+       while (pos < firmware->size &&
+              pos - firmware->size > sizeof(*region)) {
+               region = (void *)&(firmware->data[pos]);
+               region_name = "Unknown";
+               reg = 0;
+               text = NULL;
+               offset = le32_to_cpu(region->offset) & 0xffffff;
+               type = be32_to_cpu(region->type) & 0xff;
+               
+               switch (type) {
+               case WMFW_NAME_TEXT:
+                       region_name = "Firmware name";
+                       text = kzalloc(le32_to_cpu(region->len) + 1,
+                                      GFP_KERNEL);
+                       break;
+               case WMFW_INFO_TEXT:
+                       region_name = "Information";
+                       text = kzalloc(le32_to_cpu(region->len) + 1,
+                                      GFP_KERNEL);
+                       break;
+               case WMFW_ABSOLUTE:
+                       region_name = "Absolute";
+                       reg = offset;
+                       break;
+               case WMFW_ADSP1_PM:
+                       region_name = "PM";
+                       reg = pm + (offset * 3);
+                       break;
+               case WMFW_ADSP1_DM:
+                       region_name = "DM";
+                       reg = dm + (offset * 2);
+                       break;
+               case WMFW_ADSP1_ZM:
+                       region_name = "ZM";
+                       reg = zm + (offset * 2);
+                       break;
+               default:
+                       dev_warn(codec->dev,
+                                "%s.%d: Unknown region type %x at %d(%x)\n",
+                                file, regions, type, pos, pos);
+                       break;
+               }
+
+               dev_dbg(codec->dev, "%s.%d: %d bytes at %d in %s\n", file,
+                       regions, le32_to_cpu(region->len), offset,
+                       region_name);
+
+               if (text) {
+                       memcpy(text, region->data, le32_to_cpu(region->len));
+                       dev_info(codec->dev, "%s: %s\n", file, text);
+                       kfree(text);
+               }
+
+               if (reg) {
+                       ret = regmap_raw_write(regmap, reg, region->data,
+                                              le32_to_cpu(region->len));
+                       if (ret != 0) {
+                               dev_err(codec->dev,
+                                       "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+                                       file, regions,
+                                       le32_to_cpu(region->len), offset,
+                                       region_name, ret);
+                               goto out;
+                       }
+               }
+
+               pos += le32_to_cpu(region->len) + sizeof(*region);
+               regions++;
+       }
+
+       if (pos > firmware->size)
+               dev_warn(codec->dev, "%s.%d: %d bytes at end of file\n",
+                        file, regions, pos - firmware->size);
+
+out:
+       release_firmware(firmware);
+       
+       return ret;
+}
+
+static int wm2200_setup_algs(struct snd_soc_codec *codec, int base)
+{
+       struct regmap *regmap = codec->control_data;
+       struct wmfw_adsp1_id_hdr id;
+       struct wmfw_adsp1_alg_hdr *alg;
+       size_t algs;
+       int zm, dm, pm, ret, i;
+       __be32 val;
+
+       switch (base) {
+       case WM2200_DSP1_CONTROL_1:
+               dm = WM2200_DSP1_DM_BASE;
+               pm = WM2200_DSP1_PM_BASE;
+               zm = WM2200_DSP1_ZM_BASE;
+               break;
+       case WM2200_DSP2_CONTROL_1:
+               dm = WM2200_DSP2_DM_BASE;
+               pm = WM2200_DSP2_PM_BASE;
+               zm = WM2200_DSP2_ZM_BASE;
+               break;
+       default:
+               dev_err(codec->dev, "BASE %x\n", base);
+               BUG_ON(1);
+               return -EINVAL;
+       }
+
+       ret = regmap_raw_read(regmap, dm, &id, sizeof(id));
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to read algorithm info: %d\n",
+                       ret);
+               return ret;
+       }
+
+       algs = be32_to_cpu(id.algs);
+       dev_info(codec->dev, "Firmware: %x v%d.%d.%d, %d algorithms\n",
+                be32_to_cpu(id.fw.id),
+                (be32_to_cpu(id.fw.ver) & 0xff000) >> 16,
+                (be32_to_cpu(id.fw.ver) & 0xff00) >> 8,
+                be32_to_cpu(id.fw.ver) & 0xff,
+                algs);
+
+       /* Read the terminator first to validate the length */
+       ret = regmap_raw_read(regmap, dm +
+                             (sizeof(id) + (algs * sizeof(*alg))) / 2,
+                             &val, sizeof(val));
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to read algorithm list end: %d\n",
+                       ret);
+               return ret;
+       }
+
+       if (be32_to_cpu(val) != 0xbedead)
+               dev_warn(codec->dev, "Algorithm list end %x 0x%x != 0xbeadead\n",
+                        (sizeof(id) + (algs * sizeof(*alg))) / 2,
+                        be32_to_cpu(val));
+
+       alg = kzalloc(sizeof(*alg) * algs, GFP_KERNEL);
+       if (!alg)
+               return -ENOMEM;
+
+       ret = regmap_raw_read(regmap, dm + (sizeof(id) / 2),
+                             alg, algs * sizeof(*alg));
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to read algorithm list: %d\n",
+                       ret);
+               goto out;
+       }
+
+       for (i = 0; i < algs; i++) {
+               dev_info(codec->dev, "%d: ID %x v%d.%d.%d\n",
+                        i, be32_to_cpu(alg[i].alg.id),
+                        (be32_to_cpu(alg[i].alg.ver) & 0xff000) >> 16,
+                        (be32_to_cpu(alg[i].alg.ver) & 0xff00) >> 8,
+                        be32_to_cpu(alg[i].alg.ver) & 0xff);
+       }
+
+out:
+       kfree(alg);
+       return ret;
+}
+
+static int wm2200_load_coeff(struct snd_soc_codec *codec, int base)
+{
+       struct regmap *regmap = codec->control_data;
+       struct wmfw_coeff_hdr *hdr;
+       struct wmfw_coeff_item *blk;
+       const struct firmware *firmware;
+       const char *file, *region_name;
+       int ret, dm, pm, zm, pos, blocks, type, offset, reg;
+
+       switch (base) {
+       case WM2200_DSP1_CONTROL_1:
+               file = "wm2200-dsp1.bin";
+               dm = WM2200_DSP1_DM_BASE;
+               pm = WM2200_DSP1_PM_BASE;
+               zm = WM2200_DSP1_ZM_BASE;
+               break;
+       case WM2200_DSP2_CONTROL_1:
+               file = "wm2200-dsp2.bin";
+               dm = WM2200_DSP2_DM_BASE;
+               pm = WM2200_DSP2_PM_BASE;
+               zm = WM2200_DSP2_ZM_BASE;
+               break;
+       default:
+               dev_err(codec->dev, "BASE %x\n", base);
+               BUG_ON(1);
+               return -EINVAL;
+       }
+
+       ret = request_firmware(&firmware, file, codec->dev);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request '%s'\n", file);
+               return ret;
+       }
+
+       if (sizeof(*hdr) >= firmware->size) {
+               dev_err(codec->dev, "%s: file too short, %d bytes\n",
+                       file, firmware->size);
+               return -EINVAL;
+       }
+
+       hdr = (void*)&firmware->data[0];
+       if (memcmp(hdr->magic, "WMDR", 4) != 0) {
+               dev_err(codec->dev, "%s: invalid magic\n", file);
+               return -EINVAL;
+       }
+
+       dev_dbg(codec->dev, "%s: v%d.%d.%d\n", file,
+               (le32_to_cpu(hdr->ver) >> 16) & 0xff,
+               (le32_to_cpu(hdr->ver) >>  8) & 0xff,
+               le32_to_cpu(hdr->ver) & 0xff);
+
+       pos = le32_to_cpu(hdr->len);
+
+       blocks = 0;
+       while (pos < firmware->size &&
+              pos - firmware->size > sizeof(*blk)) {
+               blk = (void*)(&firmware->data[pos]);
+
+               type = be32_to_cpu(blk->type) & 0xff;
+               offset = le32_to_cpu(blk->offset) & 0xffffff;
+
+               dev_dbg(codec->dev, "%s.%d: %x v%d.%d.%d\n",
+                       file, blocks, le32_to_cpu(blk->id),
+                       (le32_to_cpu(blk->ver) >> 16) & 0xff,
+                       (le32_to_cpu(blk->ver) >>  8) & 0xff,
+                       le32_to_cpu(blk->ver) & 0xff);
+               dev_dbg(codec->dev, "%s.%d: %d bytes at 0x%x in %x\n",
+                       file, blocks, le32_to_cpu(blk->len), offset, type);
+
+               reg = 0;
+               region_name = "Unknown";
+               switch (type) {
+               case WMFW_NAME_TEXT:
+               case WMFW_INFO_TEXT:
+                       break;
+               case WMFW_ABSOLUTE:
+                       region_name = "register";
+                       reg = offset;
+                       break;
+               default:
+                       dev_err(codec->dev, "Unknown region type %x\n", type);
+                       break;
+               }
+
+               if (reg) {
+                       ret = regmap_raw_write(regmap, reg, blk->data,
+                                              le32_to_cpu(blk->len));
+                       if (ret != 0) {
+                               dev_err(codec->dev,
+                                       "%s.%d: Failed to write to %x in %s\n",
+                                       file, blocks, reg, region_name);
+                       }
+               }
+
+               pos += le32_to_cpu(blk->len) + sizeof(*blk);
+               blocks++;
+       }
+
+       if (pos > firmware->size)
+               dev_warn(codec->dev, "%s.%d: %d bytes at end of file\n",
+                        file, blocks, pos - firmware->size);
+
+       return 0;
+}
+
+static int wm2200_dsp_ev(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *kcontrol,
+                        int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       int base = w->reg - WM2200_DSP_CONTROL_30;
+       int ret;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               ret = wm2200_dsp_load(codec, base);
+               if (ret != 0)
+                       return ret;
+
+               ret = wm2200_setup_algs(codec, base);
+               if (ret != 0)
+                       return ret;
+
+               ret = wm2200_load_coeff(codec, base);
+               if (ret != 0)
+                       return ret;
+
+               /* Start the core running */
+               snd_soc_update_bits(codec, w->reg,
+                                   WM2200_DSP1_CORE_ENA | WM2200_DSP1_START,
+                                   WM2200_DSP1_CORE_ENA | WM2200_DSP1_START);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               /* Halt the core */
+               snd_soc_update_bits(codec, w->reg,
+                                   WM2200_DSP1_CORE_ENA | WM2200_DSP1_START,
+                                   0);
+
+               snd_soc_update_bits(codec, base + WM2200_DSP_CONTROL_19,
+                                   WM2200_DSP1_WDMA_BUFFER_LENGTH_MASK, 0);
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
 static DECLARE_TLV_DB_SCALE(in_tlv, -6300, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
 static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0);
@@ -880,7 +1388,7 @@ static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0);
 static const char *wm2200_mixer_texts[] = {
        "None",
        "Tone Generator",
-       "AEC loopback",
+       "AEC Loopback",
        "IN1L",
        "IN1R",
        "IN2L",
@@ -976,6 +1484,20 @@ static int wm2200_mixer_values[] = {
        static WM2200_MUX_CTL_DECL(name##_in3); \
        static WM2200_MUX_CTL_DECL(name##_in4)
 
+#define WM2200_DSP_ENUMS(name, base_reg) \
+       static WM2200_MUX_ENUM_DECL(name##_aux1_enum, base_reg);     \
+       static WM2200_MUX_ENUM_DECL(name##_aux2_enum, base_reg + 1); \
+       static WM2200_MUX_ENUM_DECL(name##_aux3_enum, base_reg + 2); \
+       static WM2200_MUX_ENUM_DECL(name##_aux4_enum, base_reg + 3); \
+       static WM2200_MUX_ENUM_DECL(name##_aux5_enum, base_reg + 4); \
+       static WM2200_MUX_ENUM_DECL(name##_aux6_enum, base_reg + 5); \
+       static WM2200_MUX_CTL_DECL(name##_aux1); \
+       static WM2200_MUX_CTL_DECL(name##_aux2); \
+       static WM2200_MUX_CTL_DECL(name##_aux3); \
+       static WM2200_MUX_CTL_DECL(name##_aux4); \
+       static WM2200_MUX_CTL_DECL(name##_aux5); \
+       static WM2200_MUX_CTL_DECL(name##_aux6);
+
 static const struct snd_kcontrol_new wm2200_snd_controls[] = {
 SOC_SINGLE("IN1 High Performance Switch", WM2200_IN1L_CONTROL,
           WM2200_IN1_OSR_SHIFT, 1, 0),
@@ -1051,6 +1573,9 @@ WM2200_MIXER_ENUMS(DSP1R, WM2200_DSP1RMIX_INPUT_1_SOURCE);
 WM2200_MIXER_ENUMS(DSP2L, WM2200_DSP2LMIX_INPUT_1_SOURCE);
 WM2200_MIXER_ENUMS(DSP2R, WM2200_DSP2RMIX_INPUT_1_SOURCE);
 
+WM2200_DSP_ENUMS(DSP1, WM2200_DSP1AUX1MIX_INPUT_1_SOURCE);
+WM2200_DSP_ENUMS(DSP2, WM2200_DSP2AUX1MIX_INPUT_1_SOURCE);
+
 WM2200_MIXER_ENUMS(LHPF1, WM2200_LHPF1MIX_INPUT_1_SOURCE);
 WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE);
 
@@ -1064,8 +1589,19 @@ WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE);
        WM2200_MUX(name_str " Input 4", &name##_in4_mux), \
        SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
 
+#define WM2200_DSP_WIDGETS(name, name_str) \
+       WM2200_MIXER_WIDGETS(name##L, name_str "L"), \
+       WM2200_MIXER_WIDGETS(name##R, name_str "R"), \
+       WM2200_MUX(name_str " Aux 1", &name##_aux1_mux), \
+       WM2200_MUX(name_str " Aux 2", &name##_aux2_mux), \
+       WM2200_MUX(name_str " Aux 3", &name##_aux3_mux), \
+       WM2200_MUX(name_str " Aux 4", &name##_aux4_mux), \
+       WM2200_MUX(name_str " Aux 5", &name##_aux5_mux), \
+       WM2200_MUX(name_str " Aux 6", &name##_aux6_mux)
+
 #define WM2200_MIXER_INPUT_ROUTES(name)        \
        { name, "Tone Generator", "Tone Generator" }, \
+       { name, "AEC Loopback", "AEC Loopback" }, \
         { name, "IN1L", "IN1L PGA" }, \
         { name, "IN1R", "IN1R PGA" }, \
         { name, "IN2L", "IN2L PGA" }, \
@@ -1106,6 +1642,33 @@ WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE);
        WM2200_MIXER_INPUT_ROUTES(name " Input 3"), \
        WM2200_MIXER_INPUT_ROUTES(name " Input 4")
 
+#define WM2200_DSP_AUX_ROUTES(name) \
+       { name, NULL, name " Aux 1" }, \
+       { name, NULL, name " Aux 2" }, \
+       { name, NULL, name " Aux 3" }, \
+       { name, NULL, name " Aux 4" }, \
+       { name, NULL, name " Aux 5" }, \
+       { name, NULL, name " Aux 6" }, \
+       WM2200_MIXER_INPUT_ROUTES(name " Aux 1"), \
+       WM2200_MIXER_INPUT_ROUTES(name " Aux 2"), \
+       WM2200_MIXER_INPUT_ROUTES(name " Aux 3"), \
+       WM2200_MIXER_INPUT_ROUTES(name " Aux 4"), \
+       WM2200_MIXER_INPUT_ROUTES(name " Aux 5"), \
+       WM2200_MIXER_INPUT_ROUTES(name " Aux 6")
+
+static const char *wm2200_aec_loopback_texts[] = {
+       "OUT1L", "OUT1R", "OUT2L", "OUT2R",
+};
+
+static const struct soc_enum wm2200_aec_loopback =
+       SOC_ENUM_SINGLE(WM2200_DAC_AEC_CONTROL_1,
+                       WM2200_AEC_LOOPBACK_SRC_SHIFT,
+                       ARRAY_SIZE(wm2200_aec_loopback_texts),
+                       wm2200_aec_loopback_texts);
+
+static const struct snd_kcontrol_new wm2200_aec_loopback_mux =
+       SOC_DAPM_ENUM("AEC Loopback", wm2200_aec_loopback);
+
 static const struct snd_soc_dapm_widget wm2200_dapm_widgets[] = {
 SND_SOC_DAPM_SUPPLY("SYSCLK", WM2200_CLOCKING_3, WM2200_SYSCLK_ENA_SHIFT, 0,
                    NULL, 0),
@@ -1165,8 +1728,12 @@ SND_SOC_DAPM_PGA("LHPF1", WM2200_HPLPF1_1, WM2200_LHPF1_ENA_SHIFT, 0,
 SND_SOC_DAPM_PGA("LHPF2", WM2200_HPLPF2_1, WM2200_LHPF2_ENA_SHIFT, 0,
                 NULL, 0),
 
-SND_SOC_DAPM_PGA_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
-SND_SOC_DAPM_PGA_E("DSP2", SND_SOC_NOPM, 1, 0, NULL, 0, NULL, 0),
+SND_SOC_DAPM_PGA_E("DSP1", WM2200_DSP1_CONTROL_30, WM2200_DSP1_SYS_ENA_SHIFT,
+                  0, NULL, 0, wm2200_dsp_ev,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("DSP2", WM2200_DSP2_CONTROL_30, WM2200_DSP2_SYS_ENA_SHIFT,
+                  0, NULL, 0, wm2200_dsp_ev,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 
 SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0,
                    WM2200_AUDIO_IF_1_22, WM2200_AIF1TX1_ENA_SHIFT, 0),
@@ -1181,6 +1748,9 @@ SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 4,
 SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 5,
                    WM2200_AUDIO_IF_1_22, WM2200_AIF1TX6_ENA_SHIFT, 0),
 
+SND_SOC_DAPM_MUX("AEC Loopback", WM2200_DAC_AEC_CONTROL_1,
+                WM2200_AEC_LOOPBACK_ENA_SHIFT, 0, &wm2200_aec_loopback_mux),
+
 SND_SOC_DAPM_PGA_S("OUT1L", 0, WM2200_OUTPUT_ENABLES,
                   WM2200_OUT1L_ENA_SHIFT, 0, NULL, 0),
 SND_SOC_DAPM_PGA_S("OUT1R", 0, WM2200_OUTPUT_ENABLES,
@@ -1231,10 +1801,8 @@ WM2200_MIXER_WIDGETS(EQR, "EQR"),
 WM2200_MIXER_WIDGETS(LHPF1, "LHPF1"),
 WM2200_MIXER_WIDGETS(LHPF2, "LHPF2"),
 
-WM2200_MIXER_WIDGETS(DSP1L, "DSP1L"),
-WM2200_MIXER_WIDGETS(DSP1R, "DSP1R"),
-WM2200_MIXER_WIDGETS(DSP2L, "DSP2L"),
-WM2200_MIXER_WIDGETS(DSP2R, "DSP2R"),
+WM2200_DSP_WIDGETS(DSP1, "DSP1"),
+WM2200_DSP_WIDGETS(DSP2, "DSP2"),
 
 WM2200_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
 WM2200_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
@@ -1326,11 +1894,19 @@ static const struct snd_soc_dapm_route wm2200_dapm_routes[] = {
        { "SPK", NULL, "OUT2L" },
        { "SPK", NULL, "OUT2R" },
 
+       { "AEC Loopback", "OUT1L", "OUT1L" },
+       { "AEC Loopback", "OUT1R", "OUT1R" },
+       { "AEC Loopback", "OUT2L", "OUT2L" },
+       { "AEC Loopback", "OUT2R", "OUT2R" },
+
        WM2200_MIXER_ROUTES("DSP1", "DSP1L"),
        WM2200_MIXER_ROUTES("DSP1", "DSP1R"),
        WM2200_MIXER_ROUTES("DSP2", "DSP2L"),
        WM2200_MIXER_ROUTES("DSP2", "DSP2R"),
 
+       WM2200_DSP_AUX_ROUTES("DSP1"),
+       WM2200_DSP_AUX_ROUTES("DSP2"),
+
        WM2200_MIXER_ROUTES("OUT1L", "OUT1L"),
        WM2200_MIXER_ROUTES("OUT1R", "OUT1R"),
        WM2200_MIXER_ROUTES("OUT2L", "OUT2L"),
@@ -1968,12 +2544,15 @@ static const struct regmap_config wm2200_regmap = {
        .reg_bits = 16,
        .val_bits = 16,
 
-       .max_register = WM2200_MAX_REGISTER,
+       .max_register = WM2200_MAX_REGISTER + (ARRAY_SIZE(wm2200_ranges) *
+                                              WM2200_DSP_SPACING),
        .reg_defaults = wm2200_reg_defaults,
        .num_reg_defaults = ARRAY_SIZE(wm2200_reg_defaults),
        .volatile_reg = wm2200_volatile_register,
        .readable_reg = wm2200_readable_register,
        .cache_type = REGCACHE_RBTREE,
+       .ranges = wm2200_ranges,
+       .num_ranges = ARRAY_SIZE(wm2200_ranges),
 };
 
 static const unsigned int wm2200_dig_vu[] = {
@@ -2011,7 +2590,7 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
        wm2200->dev = &i2c->dev;
        init_completion(&wm2200->fll_lock);
 
-       wm2200->regmap = regmap_init_i2c(i2c, &wm2200_regmap);
+       wm2200->regmap = devm_regmap_init_i2c(i2c, &wm2200_regmap);
        if (IS_ERR(wm2200->regmap)) {
                ret = PTR_ERR(wm2200->regmap);
                dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -2027,8 +2606,9 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
        for (i = 0; i < ARRAY_SIZE(wm2200->core_supplies); i++)
                wm2200->core_supplies[i].supply = wm2200_core_supply_names[i];
 
-       ret = regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm2200->core_supplies),
-                                wm2200->core_supplies);
+       ret = devm_regulator_bulk_get(&i2c->dev,
+                                     ARRAY_SIZE(wm2200->core_supplies),
+                                     wm2200->core_supplies);
        if (ret != 0) {
                dev_err(&i2c->dev, "Failed to request core supplies: %d\n",
                        ret);
@@ -2044,8 +2624,9 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
        }
 
        if (wm2200->pdata.ldo_ena) {
-               ret = gpio_request_one(wm2200->pdata.ldo_ena,
-                                      GPIOF_OUT_INIT_HIGH, "WM2200 LDOENA");
+               ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.ldo_ena,
+                                           GPIOF_OUT_INIT_HIGH,
+                                           "WM2200 LDOENA");
                if (ret < 0) {
                        dev_err(&i2c->dev, "Failed to request LDOENA %d: %d\n",
                                wm2200->pdata.ldo_ena, ret);
@@ -2055,8 +2636,9 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
        }
 
        if (wm2200->pdata.reset) {
-               ret = gpio_request_one(wm2200->pdata.reset,
-                                      GPIOF_OUT_INIT_HIGH, "WM2200 /RESET");
+               ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.reset,
+                                           GPIOF_OUT_INIT_HIGH,
+                                           "WM2200 /RESET");
                if (ret < 0) {
                        dev_err(&i2c->dev, "Failed to request /RESET %d: %d\n",
                                wm2200->pdata.reset, ret);
@@ -2166,23 +2748,16 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
 err_pm_runtime:
        pm_runtime_disable(&i2c->dev);
 err_reset:
-       if (wm2200->pdata.reset) {
+       if (wm2200->pdata.reset)
                gpio_set_value_cansleep(wm2200->pdata.reset, 0);
-               gpio_free(wm2200->pdata.reset);
-       }
 err_ldo:
-       if (wm2200->pdata.ldo_ena) {
+       if (wm2200->pdata.ldo_ena)
                gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
-               gpio_free(wm2200->pdata.ldo_ena);
-       }
 err_enable:
        regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
                               wm2200->core_supplies);
 err_core:
-       regulator_bulk_free(ARRAY_SIZE(wm2200->core_supplies),
-                           wm2200->core_supplies);
 err_regmap:
-       regmap_exit(wm2200->regmap);
 err:
        return ret;
 }
@@ -2194,17 +2769,10 @@ static __devexit int wm2200_i2c_remove(struct i2c_client *i2c)
        snd_soc_unregister_codec(&i2c->dev);
        if (i2c->irq)
                free_irq(i2c->irq, wm2200);
-       if (wm2200->pdata.reset) {
+       if (wm2200->pdata.reset)
                gpio_set_value_cansleep(wm2200->pdata.reset, 0);
-               gpio_free(wm2200->pdata.reset);
-       }
-       if (wm2200->pdata.ldo_ena) {
+       if (wm2200->pdata.ldo_ena)
                gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
-               gpio_free(wm2200->pdata.ldo_ena);
-       }
-       regulator_bulk_free(ARRAY_SIZE(wm2200->core_supplies),
-                           wm2200->core_supplies);
-       regmap_exit(wm2200->regmap);
 
        return 0;
 }
index 7f567585832eae40c9ff03b33a49f0612427c877..9f57996ff2725ddc4570d35bc77a22838b8d69ac 100644 (file)
@@ -1233,7 +1233,7 @@ static const struct snd_soc_dapm_route wm5100_dapm_routes[] = {
        { "PWM2", NULL, "PWM2 Driver" },
 };
 
-static const __devinitconst struct reg_default wm5100_reva_patches[] = {
+static const struct reg_default wm5100_reva_patches[] = {
        { WM5100_AUDIO_IF_1_10, 0 },
        { WM5100_AUDIO_IF_1_11, 1 },
        { WM5100_AUDIO_IF_1_12, 2 },
index 7394e73fa43c0e526e7ab4e83480fb89bbf9891d..27f7e38e7ecac3984a2a454ff7f6bf5a4593a622 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "arizona.h"
 #include "wm5102.h"
+#include "wm_adsp.h"
 
 struct wm5102_priv {
        struct arizona_priv core;
@@ -42,6 +43,13 @@ static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
 static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
 
+static const struct wm_adsp_region wm5102_dsp1_regions[] = {
+       { .type = WMFW_ADSP2_PM, .base = 0x100000 },
+       { .type = WMFW_ADSP2_ZM, .base = 0x180000 },
+       { .type = WMFW_ADSP2_XM, .base = 0x190000 },
+       { .type = WMFW_ADSP2_YM, .base = 0x1a8000 },
+};
+
 static const struct reg_default wm5102_sysclk_reva_patch[] = {
        { 0x3000, 0x2225 },
        { 0x3001, 0x3a03 },
@@ -627,11 +635,23 @@ SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
                 ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT,
                 0xbf, 0, digital_tlv),
 
+SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+
 ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
 
+SND_SOC_BYTES_MASK("EQ1 Coefficeints", ARIZONA_EQ1_1, 21,
+                  ARIZONA_EQ1_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ2 Coefficeints", ARIZONA_EQ2_1, 21,
+                  ARIZONA_EQ2_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ3 Coefficeints", ARIZONA_EQ3_1, 21,
+                  ARIZONA_EQ3_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ4 Coefficeints", ARIZONA_EQ4_1, 21,
+                  ARIZONA_EQ4_ENA_MASK),
+
 SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
               24, 0, eq_tlv),
 SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
@@ -687,6 +707,14 @@ ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
 
+SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+
+ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
+
 SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
 SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
 SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
@@ -708,14 +736,6 @@ ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
 
-SOC_SINGLE("HPOUT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_1L,
-          ARIZONA_OUT1_OSR_SHIFT, 1, 0),
-SOC_SINGLE("OUT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_2L,
-          ARIZONA_OUT2_OSR_SHIFT, 1, 0),
-SOC_SINGLE("EPOUT High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L,
-          ARIZONA_OUT3_OSR_SHIFT, 1, 0),
-SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
-          ARIZONA_OUT4_OSR_SHIFT, 1, 0),
 SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
           ARIZONA_OUT5_OSR_SHIFT, 1, 0),
 
@@ -745,16 +765,8 @@ SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
                 ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
                 0xbf, 0, digital_tlv),
 
-SOC_DOUBLE_R_RANGE_TLV("HPOUT1 Volume", ARIZONA_OUTPUT_PATH_CONFIG_1L,
-                      ARIZONA_OUTPUT_PATH_CONFIG_1R,
-                      ARIZONA_OUT1L_PGA_VOL_SHIFT,
-                      0x34, 0x40, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("OUT2 Volume", ARIZONA_OUTPUT_PATH_CONFIG_2L,
-                      ARIZONA_OUTPUT_PATH_CONFIG_2R,
-                      ARIZONA_OUT2L_PGA_VOL_SHIFT,
-                      0x34, 0x40, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("EPOUT Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L,
-                    ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv),
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
 
 SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
           ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
@@ -819,11 +831,15 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
 ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
 ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
 
-ARIZONA_MIXER_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE);
 
+ARIZONA_DSP_AUX_ENUMS(DSP1, ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE);
 
 static const char *wm5102_aec_loopback_texts[] = {
        "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "EPOUT",
@@ -864,6 +880,7 @@ SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
 
 SND_SOC_DAPM_SIGGEN("TONE"),
 SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
 
 SND_SOC_DAPM_INPUT("IN1L"),
 SND_SOC_DAPM_INPUT("IN1R"),
@@ -894,9 +911,9 @@ SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
 SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
                    ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
-                   ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+                   ARIZONA_MICB2_ENA_SHIFT, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
-                   ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+                   ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0),
 
 SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
                 ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
@@ -996,6 +1013,8 @@ SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
 SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
                    ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
 
+ARIZONA_DSP_WIDGETS(DSP1, "DSP1"),
+
 SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
                       ARIZONA_AEC_LOOPBACK_ENA, 0, &wm5102_aec_loopback_mux),
 
@@ -1071,10 +1090,12 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
 ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
 ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
 
-ARIZONA_MIXER_WIDGETS(ASRC1L, "ASRC1L"),
-ARIZONA_MIXER_WIDGETS(ASRC1R, "ASRC1R"),
-ARIZONA_MIXER_WIDGETS(ASRC2L, "ASRC2L"),
-ARIZONA_MIXER_WIDGETS(ASRC2R, "ASRC2R"),
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+
+WM_ADSP2("DSP1", 0),
 
 SND_SOC_DAPM_OUTPUT("HPOUT1L"),
 SND_SOC_DAPM_OUTPUT("HPOUT1R"),
@@ -1094,6 +1115,7 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
        { name, "Noise Generator", "Noise Generator" }, \
        { name, "Tone Generator 1", "Tone Generator 1" }, \
        { name, "Tone Generator 2", "Tone Generator 2" }, \
+       { name, "Haptics", "HAPTICS" }, \
        { name, "AEC", "AEC Loopback" }, \
        { name, "IN1L", "IN1L PGA" }, \
        { name, "IN1R", "IN1R PGA" }, \
@@ -1127,7 +1149,13 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
        { name, "ASRC1L", "ASRC1L" }, \
        { name, "ASRC1R", "ASRC1R" }, \
        { name, "ASRC2L", "ASRC2L" }, \
-       { name, "ASRC2R", "ASRC2R" }
+       { name, "ASRC2R", "ASRC2R" }, \
+       { name, "DSP1.1", "DSP1" }, \
+       { name, "DSP1.2", "DSP1" }, \
+       { name, "DSP1.3", "DSP1" }, \
+       { name, "DSP1.4", "DSP1" }, \
+       { name, "DSP1.5", "DSP1" }, \
+       { name, "DSP1.6", "DSP1" }
 
 static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        { "AIF2 Capture", NULL, "DBVDD2" },
@@ -1213,6 +1241,11 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        { "IN3L PGA", NULL, "IN3L" },
        { "IN3R PGA", NULL, "IN3R" },
 
+       { "ASRC1L", NULL, "ASRC1L Input" },
+       { "ASRC1R", NULL, "ASRC1R Input" },
+       { "ASRC2L", NULL, "ASRC2L Input" },
+       { "ASRC2R", NULL, "ASRC2R Input" },
+
        ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
        ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
        ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
@@ -1255,10 +1288,12 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
        ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
 
-       ARIZONA_MIXER_ROUTES("ASRC1L", "ASRC1L"),
-       ARIZONA_MIXER_ROUTES("ASRC1R", "ASRC1R"),
-       ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"),
-       ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"),
+       ARIZONA_MUX_ROUTES("ASRC1L"),
+       ARIZONA_MUX_ROUTES("ASRC1R"),
+       ARIZONA_MUX_ROUTES("ASRC2L"),
+       ARIZONA_MUX_ROUTES("ASRC2R"),
+
+       ARIZONA_DSP_ROUTES("DSP1"),
 
        { "AEC Loopback", "HPOUT1L", "OUT1L" },
        { "AEC Loopback", "HPOUT1R", "OUT1R" },
@@ -1377,9 +1412,28 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
 static int wm5102_codec_probe(struct snd_soc_codec *codec)
 {
        struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+       int ret;
 
        codec->control_data = priv->core.arizona->regmap;
-       return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+
+       ret = snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+       if (ret != 0)
+               return ret;
+
+       snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+       priv->core.arizona->dapm = &codec->dapm;
+
+       return 0;
+}
+
+static int wm5102_codec_remove(struct snd_soc_codec *codec)
+{
+       struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       priv->core.arizona->dapm = NULL;
+
+       return 0;
 }
 
 #define WM5102_DIG_VU 0x0200
@@ -1406,6 +1460,7 @@ static unsigned int wm5102_digital_vu[] = {
 
 static struct snd_soc_codec_driver soc_codec_dev_wm5102 = {
        .probe = wm5102_codec_probe,
+       .remove = wm5102_codec_remove,
 
        .idle_bias_off = true,
 
@@ -1424,7 +1479,7 @@ static int __devinit wm5102_probe(struct platform_device *pdev)
 {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
        struct wm5102_priv *wm5102;
-       int i;
+       int i, ret;
 
        wm5102 = devm_kzalloc(&pdev->dev, sizeof(struct wm5102_priv),
                              GFP_KERNEL);
@@ -1434,6 +1489,19 @@ static int __devinit wm5102_probe(struct platform_device *pdev)
 
        wm5102->core.arizona = arizona;
 
+       wm5102->core.adsp[0].part = "wm5102";
+       wm5102->core.adsp[0].num = 1;
+       wm5102->core.adsp[0].type = WMFW_ADSP2;
+       wm5102->core.adsp[0].base = ARIZONA_DSP1_CONTROL_1;
+       wm5102->core.adsp[0].dev = arizona->dev;
+       wm5102->core.adsp[0].regmap = arizona->regmap;
+       wm5102->core.adsp[0].mem = wm5102_dsp1_regions;
+       wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions);
+
+       ret = wm_adsp2_init(&wm5102->core.adsp[0], true);
+       if (ret != 0)
+               return ret;
+
        for (i = 0; i < ARRAY_SIZE(wm5102->fll); i++)
                wm5102->fll[i].vco_mult = 1;
 
index 9211e4192f710b305ca46420e0be64037a0f6967..c57dc7468300b7d8082dae3fcf266a12d20922a8 100644 (file)
@@ -84,11 +84,23 @@ SOC_DOUBLE_R_TLV("IN4 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L,
                 ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_DIG_VOL_SHIFT,
                 0xbf, 0, digital_tlv),
 
+SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+
 ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
 
+SND_SOC_BYTES_MASK("EQ1 Coefficeints", ARIZONA_EQ1_1, 21,
+                  ARIZONA_EQ1_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ2 Coefficeints", ARIZONA_EQ2_1, 21,
+                  ARIZONA_EQ2_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ3 Coefficeints", ARIZONA_EQ3_1, 21,
+                  ARIZONA_EQ3_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ4 Coefficeints", ARIZONA_EQ4_1, 21,
+                  ARIZONA_EQ4_ENA_MASK),
+
 SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
               24, 0, eq_tlv),
 SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
@@ -148,6 +160,11 @@ ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
 
+SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+
 SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
 SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
 SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
@@ -243,6 +260,9 @@ SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
 SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT,
           ARIZONA_SPK2R_MUTE_SHIFT, 1, 1),
 
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
 ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
 ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
@@ -308,10 +328,10 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
 ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
 ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
 
-ARIZONA_MIXER_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
 
 static const char *wm5110_aec_loopback_texts[] = {
        "HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
@@ -352,6 +372,7 @@ SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
 
 SND_SOC_DAPM_SIGGEN("TONE"),
 SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
 
 SND_SOC_DAPM_INPUT("IN1L"),
 SND_SOC_DAPM_INPUT("IN1R"),
@@ -585,10 +606,10 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
 ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
 ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
 
-ARIZONA_MIXER_WIDGETS(ASRC1L, "ASRC1L"),
-ARIZONA_MIXER_WIDGETS(ASRC1R, "ASRC1R"),
-ARIZONA_MIXER_WIDGETS(ASRC2L, "ASRC2L"),
-ARIZONA_MIXER_WIDGETS(ASRC2R, "ASRC2R"),
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
 
 SND_SOC_DAPM_OUTPUT("HPOUT1L"),
 SND_SOC_DAPM_OUTPUT("HPOUT1R"),
@@ -610,6 +631,7 @@ SND_SOC_DAPM_OUTPUT("SPKDAT2R"),
        { name, "Noise Generator", "Noise Generator" }, \
        { name, "Tone Generator 1", "Tone Generator 1" }, \
        { name, "Tone Generator 2", "Tone Generator 2" }, \
+       { name, "Haptics", "HAPTICS" }, \
        { name, "AEC", "AEC Loopback" }, \
        { name, "IN1L", "IN1L PGA" }, \
        { name, "IN1R", "IN1R PGA" }, \
@@ -786,10 +808,10 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
        ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
        ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
 
-       ARIZONA_MIXER_ROUTES("ASRC1L", "ASRC1L"),
-       ARIZONA_MIXER_ROUTES("ASRC1R", "ASRC1R"),
-       ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"),
-       ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"),
+       ARIZONA_MUX_ROUTES("ASRC1L"),
+       ARIZONA_MUX_ROUTES("ASRC1R"),
+       ARIZONA_MUX_ROUTES("ASRC2L"),
+       ARIZONA_MUX_ROUTES("ASRC2R"),
 
        { "HPOUT1L", NULL, "OUT1L" },
        { "HPOUT1R", NULL, "OUT1R" },
@@ -902,9 +924,29 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
 static int wm5110_codec_probe(struct snd_soc_codec *codec)
 {
        struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+       int ret;
 
        codec->control_data = priv->core.arizona->regmap;
-       return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+       priv->core.arizona->dapm = &codec->dapm;
+
+       ret = snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+       if (ret != 0)
+               return ret;
+
+       snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+       priv->core.arizona->dapm = &codec->dapm;
+
+       return 0;
+}
+
+static int wm5110_codec_remove(struct snd_soc_codec *codec)
+{
+       struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       priv->core.arizona->dapm = NULL;
+
+       return 0;
 }
 
 #define WM5110_DIG_VU 0x0200
@@ -935,6 +977,7 @@ static unsigned int wm5110_digital_vu[] = {
 
 static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
        .probe = wm5110_codec_probe,
+       .remove = wm5110_codec_remove,
 
        .idle_bias_off = true,
 
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
new file mode 100644 (file)
index 0000000..ffc89fa
--- /dev/null
@@ -0,0 +1,699 @@
+/*
+ * wm_adsp.c  --  Wolfson ADSP support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/registers.h>
+
+#include "wm_adsp.h"
+
+#define adsp_crit(_dsp, fmt, ...) \
+       dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_err(_dsp, fmt, ...) \
+       dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_warn(_dsp, fmt, ...) \
+       dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_info(_dsp, fmt, ...) \
+       dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_dbg(_dsp, fmt, ...) \
+       dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+
+#define ADSP1_CONTROL_1                   0x00
+#define ADSP1_CONTROL_2                   0x02
+#define ADSP1_CONTROL_3                   0x03
+#define ADSP1_CONTROL_4                   0x04
+#define ADSP1_CONTROL_5                   0x06
+#define ADSP1_CONTROL_6                   0x07
+#define ADSP1_CONTROL_7                   0x08
+#define ADSP1_CONTROL_8                   0x09
+#define ADSP1_CONTROL_9                   0x0A
+#define ADSP1_CONTROL_10                  0x0B
+#define ADSP1_CONTROL_11                  0x0C
+#define ADSP1_CONTROL_12                  0x0D
+#define ADSP1_CONTROL_13                  0x0F
+#define ADSP1_CONTROL_14                  0x10
+#define ADSP1_CONTROL_15                  0x11
+#define ADSP1_CONTROL_16                  0x12
+#define ADSP1_CONTROL_17                  0x13
+#define ADSP1_CONTROL_18                  0x14
+#define ADSP1_CONTROL_19                  0x16
+#define ADSP1_CONTROL_20                  0x17
+#define ADSP1_CONTROL_21                  0x18
+#define ADSP1_CONTROL_22                  0x1A
+#define ADSP1_CONTROL_23                  0x1B
+#define ADSP1_CONTROL_24                  0x1C
+#define ADSP1_CONTROL_25                  0x1E
+#define ADSP1_CONTROL_26                  0x20
+#define ADSP1_CONTROL_27                  0x21
+#define ADSP1_CONTROL_28                  0x22
+#define ADSP1_CONTROL_29                  0x23
+#define ADSP1_CONTROL_30                  0x24
+#define ADSP1_CONTROL_31                  0x26
+
+/*
+ * ADSP1 Control 19
+ */
+#define ADSP1_WDMA_BUFFER_LENGTH_MASK     0x00FF  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT         0  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH         8  /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+
+
+/*
+ * ADSP1 Control 30
+ */
+#define ADSP1_DBG_CLK_ENA                 0x0008  /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_MASK            0x0008  /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_SHIFT                3  /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_WIDTH                1  /* DSP1_DBG_CLK_ENA */
+#define ADSP1_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
+#define ADSP1_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
+#define ADSP1_START                       0x0001  /* DSP1_START */
+#define ADSP1_START_MASK                  0x0001  /* DSP1_START */
+#define ADSP1_START_SHIFT                      0  /* DSP1_START */
+#define ADSP1_START_WIDTH                      1  /* DSP1_START */
+
+#define ADSP2_CONTROL  0
+#define ADSP2_CLOCKING 1
+#define ADSP2_STATUS1  4
+
+/*
+ * ADSP2 Control
+ */
+
+#define ADSP2_MEM_ENA                     0x0010  /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_MASK                0x0010  /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_SHIFT                    4  /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_WIDTH                    1  /* DSP1_MEM_ENA */
+#define ADSP2_SYS_ENA                     0x0004  /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_MASK                0x0004  /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_SHIFT                    2  /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_WIDTH                    1  /* DSP1_SYS_ENA */
+#define ADSP2_CORE_ENA                    0x0002  /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_MASK               0x0002  /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_SHIFT                   1  /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_WIDTH                   1  /* DSP1_CORE_ENA */
+#define ADSP2_START                       0x0001  /* DSP1_START */
+#define ADSP2_START_MASK                  0x0001  /* DSP1_START */
+#define ADSP2_START_SHIFT                      0  /* DSP1_START */
+#define ADSP2_START_WIDTH                      1  /* DSP1_START */
+
+/*
+ * ADSP2 clocking
+ */
+#define ADSP2_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
+#define ADSP2_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
+#define ADSP2_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
+
+/*
+ * ADSP2 Status 1
+ */
+#define ADSP2_RAM_RDY                     0x0001
+#define ADSP2_RAM_RDY_MASK                0x0001
+#define ADSP2_RAM_RDY_SHIFT                    0
+#define ADSP2_RAM_RDY_WIDTH                    1
+
+
+static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
+                                                       int type)
+{
+       int i;
+
+       for (i = 0; i < dsp->num_mems; i++)
+               if (dsp->mem[i].type == type)
+                       return &dsp->mem[i];
+
+       return NULL;
+}
+
+static int wm_adsp_load(struct wm_adsp *dsp)
+{
+       const struct firmware *firmware;
+       struct regmap *regmap = dsp->regmap;
+       unsigned int pos = 0;
+       const struct wmfw_header *header;
+       const struct wmfw_adsp1_sizes *adsp1_sizes;
+       const struct wmfw_adsp2_sizes *adsp2_sizes;
+       const struct wmfw_footer *footer;
+       const struct wmfw_region *region;
+       const struct wm_adsp_region *mem;
+       const char *region_name;
+       char *file, *text;
+       unsigned int reg;
+       int regions = 0;
+       int ret, offset, type, sizes;
+
+       file = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (file == NULL)
+               return -ENOMEM;
+
+       snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num);
+       file[PAGE_SIZE - 1] = '\0';
+
+       ret = request_firmware(&firmware, file, dsp->dev);
+       if (ret != 0) {
+               adsp_err(dsp, "Failed to request '%s'\n", file);
+               goto out;
+       }
+       ret = -EINVAL;
+
+       pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+       if (pos >= firmware->size) {
+               adsp_err(dsp, "%s: file too short, %zu bytes\n",
+                        file, firmware->size);
+               goto out_fw;
+       }
+
+       header = (void*)&firmware->data[0];
+
+       if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
+               adsp_err(dsp, "%s: invalid magic\n", file);
+               goto out_fw;
+       }
+
+       if (header->ver != 0) {
+               adsp_err(dsp, "%s: unknown file format %d\n",
+                        file, header->ver);
+               goto out_fw;
+       }
+
+       if (header->core != dsp->type) {
+               adsp_err(dsp, "%s: invalid core %d != %d\n",
+                        file, header->core, dsp->type);
+               goto out_fw;
+       }
+
+       switch (dsp->type) {
+       case WMFW_ADSP1:
+               pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+               adsp1_sizes = (void *)&(header[1]);
+               footer = (void *)&(adsp1_sizes[1]);
+               sizes = sizeof(*adsp1_sizes);
+
+               adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
+                        file, le32_to_cpu(adsp1_sizes->dm),
+                        le32_to_cpu(adsp1_sizes->pm),
+                        le32_to_cpu(adsp1_sizes->zm));
+               break;
+
+       case WMFW_ADSP2:
+               pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
+               adsp2_sizes = (void *)&(header[1]);
+               footer = (void *)&(adsp2_sizes[1]);
+               sizes = sizeof(*adsp2_sizes);
+
+               adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
+                        file, le32_to_cpu(adsp2_sizes->xm),
+                        le32_to_cpu(adsp2_sizes->ym),
+                        le32_to_cpu(adsp2_sizes->pm),
+                        le32_to_cpu(adsp2_sizes->zm));
+               break;
+
+       default:
+               BUG_ON(NULL == "Unknown DSP type");
+               goto out_fw;
+       }
+
+       if (le32_to_cpu(header->len) != sizeof(*header) +
+           sizes + sizeof(*footer)) {
+               adsp_err(dsp, "%s: unexpected header length %d\n",
+                        file, le32_to_cpu(header->len));
+               goto out_fw;
+       }
+
+       adsp_dbg(dsp, "%s: timestamp %llu\n", file,
+                le64_to_cpu(footer->timestamp));
+
+       while (pos < firmware->size &&
+              pos - firmware->size > sizeof(*region)) {
+               region = (void *)&(firmware->data[pos]);
+               region_name = "Unknown";
+               reg = 0;
+               text = NULL;
+               offset = le32_to_cpu(region->offset) & 0xffffff;
+               type = be32_to_cpu(region->type) & 0xff;
+               mem = wm_adsp_find_region(dsp, type);
+               
+               switch (type) {
+               case WMFW_NAME_TEXT:
+                       region_name = "Firmware name";
+                       text = kzalloc(le32_to_cpu(region->len) + 1,
+                                      GFP_KERNEL);
+                       break;
+               case WMFW_INFO_TEXT:
+                       region_name = "Information";
+                       text = kzalloc(le32_to_cpu(region->len) + 1,
+                                      GFP_KERNEL);
+                       break;
+               case WMFW_ABSOLUTE:
+                       region_name = "Absolute";
+                       reg = offset;
+                       break;
+               case WMFW_ADSP1_PM:
+                       BUG_ON(!mem);
+                       region_name = "PM";
+                       reg = mem->base + (offset * 3);
+                       break;
+               case WMFW_ADSP1_DM:
+                       BUG_ON(!mem);
+                       region_name = "DM";
+                       reg = mem->base + (offset * 2);
+                       break;
+               case WMFW_ADSP2_XM:
+                       BUG_ON(!mem);
+                       region_name = "XM";
+                       reg = mem->base + (offset * 2);
+                       break;
+               case WMFW_ADSP2_YM:
+                       BUG_ON(!mem);
+                       region_name = "YM";
+                       reg = mem->base + (offset * 2);
+                       break;
+               case WMFW_ADSP1_ZM:
+                       BUG_ON(!mem);
+                       region_name = "ZM";
+                       reg = mem->base + (offset * 2);
+                       break;
+               default:
+                       adsp_warn(dsp,
+                                 "%s.%d: Unknown region type %x at %d(%x)\n",
+                                 file, regions, type, pos, pos);
+                       break;
+               }
+
+               adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
+                        regions, le32_to_cpu(region->len), offset,
+                        region_name);
+
+               if (text) {
+                       memcpy(text, region->data, le32_to_cpu(region->len));
+                       adsp_info(dsp, "%s: %s\n", file, text);
+                       kfree(text);
+               }
+
+               if (reg) {
+                       ret = regmap_raw_write(regmap, reg, region->data,
+                                              le32_to_cpu(region->len));
+                       if (ret != 0) {
+                               adsp_err(dsp,
+                                       "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+                                       file, regions,
+                                       le32_to_cpu(region->len), offset,
+                                       region_name, ret);
+                               goto out_fw;
+                       }
+               }
+
+               pos += le32_to_cpu(region->len) + sizeof(*region);
+               regions++;
+       }
+       
+       if (pos > firmware->size)
+               adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
+                         file, regions, pos - firmware->size);
+
+out_fw:
+       release_firmware(firmware);
+out:
+       kfree(file);
+
+       return ret;
+}
+
+static int wm_adsp_load_coeff(struct wm_adsp *dsp)
+{
+       struct regmap *regmap = dsp->regmap;
+       struct wmfw_coeff_hdr *hdr;
+       struct wmfw_coeff_item *blk;
+       const struct firmware *firmware;
+       const char *region_name;
+       int ret, pos, blocks, type, offset, reg;
+       char *file;
+
+       file = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       if (file == NULL)
+               return -ENOMEM;
+
+       snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num);
+       file[PAGE_SIZE - 1] = '\0';
+
+       ret = request_firmware(&firmware, file, dsp->dev);
+       if (ret != 0) {
+               adsp_warn(dsp, "Failed to request '%s'\n", file);
+               ret = 0;
+               goto out;
+       }
+       ret = -EINVAL;
+
+       if (sizeof(*hdr) >= firmware->size) {
+               adsp_err(dsp, "%s: file too short, %zu bytes\n",
+                       file, firmware->size);
+               goto out_fw;
+       }
+
+       hdr = (void*)&firmware->data[0];
+       if (memcmp(hdr->magic, "WMDR", 4) != 0) {
+               adsp_err(dsp, "%s: invalid magic\n", file);
+               return -EINVAL;
+       }
+
+       adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
+               (le32_to_cpu(hdr->ver) >> 16) & 0xff,
+               (le32_to_cpu(hdr->ver) >>  8) & 0xff,
+               le32_to_cpu(hdr->ver) & 0xff);
+
+       pos = le32_to_cpu(hdr->len);
+
+       blocks = 0;
+       while (pos < firmware->size &&
+              pos - firmware->size > sizeof(*blk)) {
+               blk = (void*)(&firmware->data[pos]);
+
+               type = be32_to_cpu(blk->type) & 0xff;
+               offset = le32_to_cpu(blk->offset) & 0xffffff;
+
+               adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
+                        file, blocks, le32_to_cpu(blk->id),
+                        (le32_to_cpu(blk->ver) >> 16) & 0xff,
+                        (le32_to_cpu(blk->ver) >>  8) & 0xff,
+                        le32_to_cpu(blk->ver) & 0xff);
+               adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
+                        file, blocks, le32_to_cpu(blk->len), offset, type);
+
+               reg = 0;
+               region_name = "Unknown";
+               switch (type) {
+               case WMFW_NAME_TEXT:
+               case WMFW_INFO_TEXT:
+                       break;
+               case WMFW_ABSOLUTE:
+                       region_name = "register";
+                       reg = offset;
+                       break;
+               default:
+                       adsp_err(dsp, "Unknown region type %x\n", type);
+                       break;
+               }
+
+               if (reg) {
+                       ret = regmap_raw_write(regmap, reg, blk->data,
+                                              le32_to_cpu(blk->len));
+                       if (ret != 0) {
+                               adsp_err(dsp,
+                                       "%s.%d: Failed to write to %x in %s\n",
+                                       file, blocks, reg, region_name);
+                       }
+               }
+
+               pos += le32_to_cpu(blk->len) + sizeof(*blk);
+               blocks++;
+       }
+
+       if (pos > firmware->size)
+               adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
+                         file, blocks, pos - firmware->size);
+
+out_fw:
+       release_firmware(firmware);
+out:
+       kfree(file);
+       return 0;
+}
+
+int wm_adsp1_event(struct snd_soc_dapm_widget *w,
+                  struct snd_kcontrol *kcontrol,
+                  int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+       struct wm_adsp *dsp = &dsps[w->shift];
+       int ret;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+                                  ADSP1_SYS_ENA, ADSP1_SYS_ENA);
+
+               ret = wm_adsp_load(dsp);
+               if (ret != 0)
+                       goto err;
+
+               ret = wm_adsp_load_coeff(dsp);
+               if (ret != 0)
+                       goto err;
+
+               /* Start the core running */
+               regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+                                  ADSP1_CORE_ENA | ADSP1_START,
+                                  ADSP1_CORE_ENA | ADSP1_START);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               /* Halt the core */
+               regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+                                  ADSP1_CORE_ENA | ADSP1_START, 0);
+
+               regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
+                                  ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
+
+               regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+                                  ADSP1_SYS_ENA, 0);
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+
+err:
+       regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+                          ADSP1_SYS_ENA, 0);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp1_event);
+
+static int wm_adsp2_ena(struct wm_adsp *dsp)
+{
+       unsigned int val;
+       int ret, count;
+
+       ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+                                ADSP2_SYS_ENA, ADSP2_SYS_ENA);
+       if (ret != 0)
+               return ret;
+
+       /* Wait for the RAM to start, should be near instantaneous */
+       count = 0;
+       do {
+               ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
+                                 &val);
+               if (ret != 0)
+                       return ret;
+       } while (!(val & ADSP2_RAM_RDY) && ++count < 10);
+
+       if (!(val & ADSP2_RAM_RDY)) {
+               adsp_err(dsp, "Failed to start DSP RAM\n");
+               return -EBUSY;
+       }
+
+       adsp_dbg(dsp, "RAM ready after %d polls\n", count);
+       adsp_info(dsp, "RAM ready after %d polls\n", count);
+
+       return 0;
+}
+
+int wm_adsp2_event(struct snd_soc_dapm_widget *w,
+                  struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+       struct wm_adsp *dsp = &dsps[w->shift];
+       unsigned int val;
+       int ret;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /*
+                * For simplicity set the DSP clock rate to be the
+                * SYSCLK rate rather than making it configurable.
+                */
+               ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
+               if (ret != 0) {
+                       adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
+                                ret);
+                       return ret;
+               }
+               val = (val & ARIZONA_SYSCLK_FREQ_MASK)
+                       >> ARIZONA_SYSCLK_FREQ_SHIFT;
+
+               ret = regmap_update_bits(dsp->regmap,
+                                        dsp->base + ADSP2_CLOCKING,
+                                        ADSP2_CLK_SEL_MASK, val);
+               if (ret != 0) {
+                       adsp_err(dsp, "Failed to set clock rate: %d\n",
+                                ret);
+                       return ret;
+               }
+
+               if (dsp->dvfs) {
+                       ret = regmap_read(dsp->regmap,
+                                         dsp->base + ADSP2_CLOCKING, &val);
+                       if (ret != 0) {
+                               dev_err(dsp->dev,
+                                       "Failed to read clocking: %d\n", ret);
+                               return ret;
+                       }
+
+                       if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
+                               ret = regulator_enable(dsp->dvfs);
+                               if (ret != 0) {
+                                       dev_err(dsp->dev,
+                                               "Failed to enable supply: %d\n",
+                                               ret);
+                                       return ret;
+                               }
+
+                               ret = regulator_set_voltage(dsp->dvfs,
+                                                           1800000,
+                                                           1800000);
+                               if (ret != 0) {
+                                       dev_err(dsp->dev,
+                                               "Failed to raise supply: %d\n",
+                                               ret);
+                                       return ret;
+                               }
+                       }
+               }
+
+               ret = wm_adsp2_ena(dsp);
+               if (ret != 0)
+                       return ret;
+
+               ret = wm_adsp_load(dsp);
+               if (ret != 0)
+                       goto err;
+
+               ret = wm_adsp_load_coeff(dsp);
+               if (ret != 0)
+                       goto err;
+
+               ret = regmap_update_bits(dsp->regmap,
+                                        dsp->base + ADSP2_CONTROL,
+                                        ADSP2_CORE_ENA | ADSP2_START,
+                                        ADSP2_CORE_ENA | ADSP2_START);
+               if (ret != 0)
+                       goto err;
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+                                  ADSP2_SYS_ENA | ADSP2_CORE_ENA |
+                                  ADSP2_START, 0);
+
+               if (dsp->dvfs) {
+                       ret = regulator_set_voltage(dsp->dvfs, 1200000,
+                                                   1800000);
+                       if (ret != 0)
+                               dev_warn(dsp->dev,
+                                        "Failed to lower supply: %d\n",
+                                        ret);
+
+                       ret = regulator_disable(dsp->dvfs);
+                       if (ret != 0)
+                               dev_err(dsp->dev,
+                                       "Failed to enable supply: %d\n",
+                                       ret);
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+err:
+       regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+                          ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_event);
+
+int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
+{
+       int ret;
+
+       /*
+        * Disable the DSP memory by default when in reset for a small
+        * power saving.
+        */
+       ret = regmap_update_bits(adsp->regmap, adsp->base + ADSP2_CONTROL,
+                                ADSP2_MEM_ENA, 0);
+       if (ret != 0) {
+               adsp_err(adsp, "Failed to clear memory retention: %d\n", ret);
+               return ret;
+       }
+
+       if (dvfs) {
+               adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
+               if (IS_ERR(adsp->dvfs)) {
+                       ret = PTR_ERR(adsp->dvfs);
+                       dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
+                       return ret;
+               }
+
+               ret = regulator_enable(adsp->dvfs);
+               if (ret != 0) {
+                       dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
+               if (ret != 0) {
+                       dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               ret = regulator_disable(adsp->dvfs);
+               if (ret != 0) {
+                       dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_init);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
new file mode 100644 (file)
index 0000000..ffd29a4
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * wm_adsp.h  --  Wolfson ADSP support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __WM_ADSP_H
+#define __WM_ADSP_H
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "wmfw.h"
+
+struct regulator;
+
+struct wm_adsp_region {
+       int type;
+       unsigned int base;
+};
+
+struct wm_adsp {
+       const char *part;
+       int num;
+       int type;
+       struct device *dev;
+       struct regmap *regmap;
+
+       int base;
+
+       const struct wm_adsp_region *mem;
+       int num_mems;
+
+       struct regulator *dvfs;
+};
+
+#define WM_ADSP1(wname, num) \
+       { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
+       .shift = num, .event = wm_adsp1_event, \
+       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+
+#define WM_ADSP2(wname, num) \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
+       .shift = num, .event = wm_adsp2_event, \
+       .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+
+int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs);
+int wm_adsp1_event(struct snd_soc_dapm_widget *w,
+                  struct snd_kcontrol *kcontrol, int event);
+int wm_adsp2_event(struct snd_soc_dapm_widget *w,
+                  struct snd_kcontrol *kcontrol, int event);
+
+#endif
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
new file mode 100644 (file)
index 0000000..5632ded
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * wmfw.h - Wolfson firmware format information
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __WMFW_H
+#define __WMFW_H
+
+#include <linux/types.h>
+
+struct wmfw_header {
+       char magic[4];
+       __le32 len;
+       __le16 rev;
+       u8 core;
+       u8 ver;
+} __packed;
+
+struct wmfw_footer {
+       __le64 timestamp;
+       __le32 checksum;
+} __packed;
+
+struct wmfw_adsp1_sizes {
+       __le32 dm;
+       __le32 pm;
+       __le32 zm;
+} __packed;
+
+struct wmfw_adsp2_sizes {
+       __le32 xm;
+       __le32 ym;
+       __le32 pm;
+       __le32 zm;
+} __packed;
+
+struct wmfw_region {
+       union {
+               __be32 type;
+               __le32 offset;
+       };
+       __le32 len;
+       u8 data[];
+} __packed;
+
+struct wmfw_id_hdr {
+       __be32 core_id;
+       __be32 core_rev;
+       __be32 id;
+       __be32 ver;
+} __packed;
+
+struct wmfw_adsp1_id_hdr {
+       struct wmfw_id_hdr fw;
+       __be32 zm;
+       __be32 dm;
+       __be32 algs;
+} __packed;
+
+struct wmfw_adsp2_id_hdr {
+       struct wmfw_id_hdr fw;
+       __be32 zm;
+       __be32 xm;
+       __be32 ym;
+       __be32 algs;
+} __packed;
+
+struct wmfw_alg_hdr {
+       __be32 id;
+       __be32 ver;
+} __packed;
+
+struct wmfw_adsp1_alg_hdr {
+       struct wmfw_alg_hdr alg;
+       __be32 zm;
+       __be32 dm;
+} __packed;
+
+struct wmfw_adsp2_alg_hdr {
+       struct wmfw_alg_hdr alg;
+       __be32 zm;
+       __be32 xm;
+       __be32 ym;
+} __packed;
+
+struct wmfw_coeff_hdr {
+       u8 magic[4];
+       __le32 len;
+       __le32 ver;
+       u8 data[];
+} __packed;
+
+struct wmfw_coeff_item {
+       union {
+               __be32 type;
+               __le32 offset;
+       };
+       __le32 id;
+       __le32 ver;
+       __le32 sr;
+       __le32 len;
+       u8 data[];
+} __packed;
+
+#define WMFW_ADSP1 1
+#define WMFW_ADSP2 2
+
+#define WMFW_ABSOLUTE  0xf0
+#define WMFW_NAME_TEXT 0xfe
+#define WMFW_INFO_TEXT 0xff
+
+#define WMFW_ADSP1_PM 2
+#define WMFW_ADSP1_DM 3
+#define WMFW_ADSP1_ZM 4
+
+#define WMFW_ADSP2_PM 2
+#define WMFW_ADSP2_ZM 4
+#define WMFW_ADSP2_XM 5
+#define WMFW_ADSP2_YM 6
+
+#endif
index 6fac5af13298ffa2c64ebf5bad1b5fde6a0ac6f6..d55e6477bff0c9a855379d26db50538ff7b5e178 100644 (file)
@@ -71,6 +71,11 @@ static int evm_hw_params(struct snd_pcm_substream *substream,
        if (ret < 0)
                return ret;
 
+       /* set the CPU system clock */
+       ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_OUT);
+       if (ret < 0)
+               return ret;
+
        return 0;
 }
 
index 714e51e5be5bd2c7038f1965a74545a01c5a268c..55e2bf652beff53d6694e3cf20d2d2e4d6f17393 100644 (file)
 #define ACLKXE         BIT(5)
 #define TX_ASYNC       BIT(6)
 #define ACLKXPOL       BIT(7)
+#define ACLKXDIV_MASK  0x1f
 
 /*
  * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits
 #define ACLKRE         BIT(5)
 #define RX_ASYNC       BIT(6)
 #define ACLKRPOL       BIT(7)
+#define ACLKRDIV_MASK  0x1f
 
 /*
  * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
 #define AHCLKXDIV(val) (val)
 #define AHCLKXPOL      BIT(14)
 #define AHCLKXE                BIT(15)
+#define AHCLKXDIV_MASK 0xfff
 
 /*
  * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control
 #define AHCLKRDIV(val) (val)
 #define AHCLKRPOL      BIT(14)
 #define AHCLKRE                BIT(15)
+#define AHCLKRDIV_MASK 0xfff
 
 /*
  * DAVINCI_MCASP_XRSRCTL_BASE_REG -  Serializer Control Register Bits
@@ -473,6 +477,23 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
        void __iomem *base = dev->base;
 
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_B:
+       case SND_SOC_DAIFMT_AC97:
+               mcasp_clr_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+               mcasp_clr_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+               break;
+       default:
+               /* configure a full-word SYNC pulse (LRCLK) */
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+
+               /* make 1st data bit occur one ACLK cycle after the frame sync */
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, FSXDLY(1));
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, FSRDLY(1));
+               break;
+       }
+
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBS_CFS:
                /* codec is clock and frame slave */
@@ -482,8 +503,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
                mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
 
-               mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG,
-                               ACLKX | AHCLKX | AFSX);
+               mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, ACLKX | AFSX);
                break;
        case SND_SOC_DAIFMT_CBM_CFS:
                /* codec is clock master and frame slave */
@@ -554,59 +574,75 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        return 0;
 }
 
-static int davinci_config_channel_size(struct davinci_audio_dev *dev,
-                                      int channel_size)
+static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
 {
-       u32 fmt = 0;
-       u32 mask, rotate;
-
-       switch (channel_size) {
-       case DAVINCI_AUDIO_WORD_8:
-               fmt = 0x03;
-               rotate = 6;
-               mask = 0x000000ff;
-               break;
+       struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(dai);
 
-       case DAVINCI_AUDIO_WORD_12:
-               fmt = 0x05;
-               rotate = 5;
-               mask = 0x00000fff;
+       switch (div_id) {
+       case 0:         /* MCLK divider */
+               mcasp_mod_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG,
+                              AHCLKXDIV(div - 1), AHCLKXDIV_MASK);
+               mcasp_mod_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG,
+                              AHCLKRDIV(div - 1), AHCLKRDIV_MASK);
                break;
 
-       case DAVINCI_AUDIO_WORD_16:
-               fmt = 0x07;
-               rotate = 4;
-               mask = 0x0000ffff;
+       case 1:         /* BCLK divider */
+               mcasp_mod_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG,
+                              ACLKXDIV(div - 1), ACLKXDIV_MASK);
+               mcasp_mod_bits(dev->base + DAVINCI_MCASP_ACLKRCTL_REG,
+                              ACLKRDIV(div - 1), ACLKRDIV_MASK);
                break;
 
-       case DAVINCI_AUDIO_WORD_20:
-               fmt = 0x09;
-               rotate = 3;
-               mask = 0x000fffff;
+       case 2:         /* BCLK/LRCLK ratio */
+               dev->bclk_lrclk_ratio = div;
                break;
 
-       case DAVINCI_AUDIO_WORD_24:
-               fmt = 0x0B;
-               rotate = 2;
-               mask = 0x00ffffff;
-               break;
+       default:
+               return -EINVAL;
+       }
 
-       case DAVINCI_AUDIO_WORD_28:
-               fmt = 0x0D;
-               rotate = 1;
-               mask = 0x0fffffff;
-               break;
+       return 0;
+}
 
-       case DAVINCI_AUDIO_WORD_32:
-               fmt = 0x0F;
-               rotate = 0;
-               mask = 0xffffffff;
-               break;
+static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+                                   unsigned int freq, int dir)
+{
+       struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(dai);
 
-       default:
-               return -EINVAL;
+       if (dir == SND_SOC_CLOCK_OUT) {
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
+               mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AHCLKX);
+       } else {
+               mcasp_clr_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE);
+               mcasp_clr_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE);
+               mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AHCLKX);
        }
 
+       return 0;
+}
+
+static int davinci_config_channel_size(struct davinci_audio_dev *dev,
+                                      int word_length)
+{
+       u32 fmt;
+       u32 rotate = (32 - word_length) / 4;
+       u32 mask = (1ULL << word_length) - 1;
+
+       /*
+        * if s BCLK-to-LRCLK ratio has been configured via the set_clkdiv()
+        * callback, take it into account here. That allows us to for example
+        * send 32 bits per channel to the codec, while only 16 of them carry
+        * audio payload.
+        * The clock ratio is given for a full period of data (both left and
+        * right channels), so it has to be divided by 2.
+        */
+       if (dev->bclk_lrclk_ratio)
+               word_length = dev->bclk_lrclk_ratio / 2;
+
+       /* mapping of the XSSZ bit-field as described in the datasheet */
+       fmt = (word_length >> 1) - 1;
+
        mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG,
                                        RXSSZ(fmt), RXSSZ(0x0F));
        mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
@@ -709,8 +745,6 @@ static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
        if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
                /* bit stream is MSB first  with no delay */
                /* DSP_B mode */
-               mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG,
-                               AHCLKXE);
                mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask);
                mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD);
 
@@ -720,14 +754,10 @@ static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
                else
                        printk(KERN_ERR "playback tdm slot %d not supported\n",
                                dev->tdm_slots);
-
-               mcasp_clr_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
        } else {
                /* bit stream is MSB first with no delay */
                /* DSP_B mode */
                mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXORD);
-               mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG,
-                               AHCLKRE);
                mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask);
 
                if ((dev->tdm_slots >= 2) && (dev->tdm_slots <= 32))
@@ -736,8 +766,6 @@ static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
                else
                        printk(KERN_ERR "capture tdm slot %d not supported\n",
                                dev->tdm_slots);
-
-               mcasp_clr_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
        }
 }
 
@@ -800,19 +828,27 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
        case SNDRV_PCM_FORMAT_U8:
        case SNDRV_PCM_FORMAT_S8:
                dma_params->data_type = 1;
-               word_length = DAVINCI_AUDIO_WORD_8;
+               word_length = 8;
                break;
 
        case SNDRV_PCM_FORMAT_U16_LE:
        case SNDRV_PCM_FORMAT_S16_LE:
                dma_params->data_type = 2;
-               word_length = DAVINCI_AUDIO_WORD_16;
+               word_length = 16;
+               break;
+
+       case SNDRV_PCM_FORMAT_U24_3LE:
+       case SNDRV_PCM_FORMAT_S24_3LE:
+               dma_params->data_type = 3;
+               word_length = 24;
                break;
 
+       case SNDRV_PCM_FORMAT_U24_LE:
+       case SNDRV_PCM_FORMAT_S24_LE:
        case SNDRV_PCM_FORMAT_U32_LE:
        case SNDRV_PCM_FORMAT_S32_LE:
                dma_params->data_type = 4;
-               word_length = DAVINCI_AUDIO_WORD_32;
+               word_length = 32;
                break;
 
        default:
@@ -880,13 +916,18 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
        .trigger        = davinci_mcasp_trigger,
        .hw_params      = davinci_mcasp_hw_params,
        .set_fmt        = davinci_mcasp_set_dai_fmt,
-
+       .set_clkdiv     = davinci_mcasp_set_clkdiv,
+       .set_sysclk     = davinci_mcasp_set_sysclk,
 };
 
 #define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
                                SNDRV_PCM_FMTBIT_U8 | \
                                SNDRV_PCM_FMTBIT_S16_LE | \
                                SNDRV_PCM_FMTBIT_U16_LE | \
+                               SNDRV_PCM_FMTBIT_S24_LE | \
+                               SNDRV_PCM_FMTBIT_U24_LE | \
+                               SNDRV_PCM_FMTBIT_S24_3LE | \
+                               SNDRV_PCM_FMTBIT_U24_3LE | \
                                SNDRV_PCM_FMTBIT_S32_LE | \
                                SNDRV_PCM_FMTBIT_U32_LE)
 
@@ -1089,7 +1130,6 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        dev->tdm_slots = pdata->tdm_slots;
        dev->num_serializer = pdata->num_serializer;
        dev->serial_dir = pdata->serial_dir;
-       dev->codec_fmt = pdata->codec_fmt;
        dev->version = pdata->version;
        dev->txnumevt = pdata->txnumevt;
        dev->rxnumevt = pdata->rxnumevt;
@@ -1098,6 +1138,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        dma_data = &dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
        dma_data->asp_chan_q = pdata->asp_chan_q;
        dma_data->ram_chan_q = pdata->ram_chan_q;
+       dma_data->sram_pool = pdata->sram_pool;
        dma_data->sram_size = pdata->sram_size_playback;
        dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
                                                        mem->start);
@@ -1115,6 +1156,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        dma_data = &dev->dma_params[SNDRV_PCM_STREAM_CAPTURE];
        dma_data->asp_chan_q = pdata->asp_chan_q;
        dma_data->ram_chan_q = pdata->ram_chan_q;
+       dma_data->sram_pool = pdata->sram_pool;
        dma_data->sram_size = pdata->sram_size_capture;
        dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
                                                        mem->start);
index 0de9ed6ce038a8472aeb762d403fbf26d579bc24..0edd3b5a37fd86a5c04a54b66b8735d900e33a06 100644 (file)
 
 #include "davinci-pcm.h"
 
-#define DAVINCI_MCASP_RATES    SNDRV_PCM_RATE_8000_96000
+#define DAVINCI_MCASP_RATES    SNDRV_PCM_RATE_8000_192000
 #define DAVINCI_MCASP_I2S_DAI  0
 #define DAVINCI_MCASP_DIT_DAI  1
 
-enum {
-       DAVINCI_AUDIO_WORD_8 = 0,
-       DAVINCI_AUDIO_WORD_12,
-       DAVINCI_AUDIO_WORD_16,
-       DAVINCI_AUDIO_WORD_20,
-       DAVINCI_AUDIO_WORD_24,
-       DAVINCI_AUDIO_WORD_32,
-       DAVINCI_AUDIO_WORD_28,  /* This is only valid for McASP */
-};
-
 struct davinci_audio_dev {
        struct davinci_pcm_dma_params dma_params[2];
        void __iomem *base;
-       int sample_rate;
        struct device *dev;
-       unsigned int codec_fmt;
 
        /* McASP specific data */
        int     tdm_slots;
@@ -50,6 +38,7 @@ struct davinci_audio_dev {
        u8      num_serializer;
        u8      *serial_dir;
        u8      version;
+       u8      bclk_lrclk_ratio;
 
        /* McASP FIFO related */
        u8      txnumevt;
index 93ea3bf567e1fd1835ff643ff4e73d67fecdce12..afab81f844ae82c32418d6d1f83301c2d29d1f4e 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
 #include <linux/kernel.h>
+#include <linux/genalloc.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -23,7 +24,6 @@
 #include <sound/soc.h>
 
 #include <asm/dma.h>
-#include <mach/sram.h>
 
 #include "davinci-pcm.h"
 
@@ -67,13 +67,9 @@ static struct snd_pcm_hardware pcm_hardware_playback = {
                 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME|
                 SNDRV_PCM_INFO_BATCH),
        .formats = DAVINCI_PCM_FMTBITS,
-       .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
-                 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
-                 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
-                 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
-                 SNDRV_PCM_RATE_KNOT),
+       .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT,
        .rate_min = 8000,
-       .rate_max = 96000,
+       .rate_max = 192000,
        .channels_min = 2,
        .channels_max = 384,
        .buffer_bytes_max = 128 * 1024,
@@ -90,13 +86,9 @@ static struct snd_pcm_hardware pcm_hardware_capture = {
                 SNDRV_PCM_INFO_PAUSE |
                 SNDRV_PCM_INFO_BATCH),
        .formats = DAVINCI_PCM_FMTBITS,
-       .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
-                 SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
-                 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
-                 SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
-                 SNDRV_PCM_RATE_KNOT),
+       .rates = SNDRV_PCM_RATE_8000_192000 | SNDRV_PCM_RATE_KNOT,
        .rate_min = 8000,
-       .rate_max = 96000,
+       .rate_max = 192000,
        .channels_min = 2,
        .channels_max = 384,
        .buffer_bytes_max = 128 * 1024,
@@ -259,7 +251,9 @@ static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
        }
 }
 
-static int allocate_sram(struct snd_pcm_substream *substream, unsigned size,
+#ifdef CONFIG_GENERIC_ALLOCATOR
+static int allocate_sram(struct snd_pcm_substream *substream,
+               struct gen_pool *sram_pool, unsigned size,
                struct snd_pcm_hardware *ppcm)
 {
        struct snd_dma_buffer *buf = &substream->dma_buffer;
@@ -271,9 +265,10 @@ static int allocate_sram(struct snd_pcm_substream *substream, unsigned size,
                return 0;
 
        ppcm->period_bytes_max = size;
-       iram_virt = sram_alloc(size, &iram_phys);
+       iram_virt = (void *)gen_pool_alloc(sram_pool, size);
        if (!iram_virt)
                goto exit1;
+       iram_phys = gen_pool_virt_to_phys(sram_pool, (unsigned)iram_virt);
        iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL);
        if (!iram_dma)
                goto exit2;
@@ -285,11 +280,33 @@ static int allocate_sram(struct snd_pcm_substream *substream, unsigned size,
        return 0;
 exit2:
        if (iram_virt)
-               sram_free(iram_virt, size);
+               gen_pool_free(sram_pool, (unsigned)iram_virt, size);
 exit1:
        return -ENOMEM;
 }
 
+static void davinci_free_sram(struct snd_pcm_substream *substream,
+                             struct snd_dma_buffer *iram_dma)
+{
+       struct davinci_runtime_data *prtd = substream->runtime->private_data;
+       struct gen_pool *sram_pool = prtd->params->sram_pool;
+
+       gen_pool_free(sram_pool, (unsigned) iram_dma->area, iram_dma->bytes);
+}
+#else
+static int allocate_sram(struct snd_pcm_substream *substream,
+               struct gen_pool *sram_pool, unsigned size,
+               struct snd_pcm_hardware *ppcm)
+{
+       return 0;
+}
+
+static void davinci_free_sram(struct snd_pcm_substream *substream,
+                             struct snd_dma_buffer *iram_dma)
+{
+}
+#endif
+
 /*
  * Only used with ping/pong.
  * This is called after runtime->dma_addr, period_bytes and data_type are valid
@@ -676,7 +693,7 @@ static int davinci_pcm_open(struct snd_pcm_substream *substream)
 
        ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
                        &pcm_hardware_playback : &pcm_hardware_capture;
-       allocate_sram(substream, params->sram_size, ppcm);
+       allocate_sram(substream, params->sram_pool, params->sram_size, ppcm);
        snd_soc_set_runtime_hwparams(substream, ppcm);
        /* ensure that buffer size is a multiple of period size */
        ret = snd_pcm_hw_constraint_integer(runtime,
@@ -819,7 +836,7 @@ static void davinci_pcm_free(struct snd_pcm *pcm)
                buf->area = NULL;
                iram_dma = buf->private_data;
                if (iram_dma) {
-                       sram_free(iram_dma->area, iram_dma->bytes);
+                       davinci_free_sram(substream, iram_dma);
                        kfree(iram_dma);
                }
        }
index fc4d01cdd8c95c92b11694faece56d77fc648bd9..b6ef7039dd097b4ac99c6e9b14a0c35ed59fd203 100644 (file)
@@ -12,6 +12,7 @@
 #ifndef _DAVINCI_PCM_H
 #define _DAVINCI_PCM_H
 
+#include <linux/genalloc.h>
 #include <linux/platform_data/davinci_asp.h>
 #include <mach/edma.h>
 
@@ -20,6 +21,7 @@ struct davinci_pcm_dma_params {
        unsigned short acnt;
        dma_addr_t dma_addr;            /* device physical address for DMA */
        unsigned sram_size;
+       struct gen_pool *sram_pool;     /* SRAM gen_pool for ping pong */
        enum dma_event_q asp_chan_q;    /* event queue number for ASP channel */
        enum dma_event_q ram_chan_q;    /* event queue number for RAM channel */
        unsigned char data_type;        /* xfer data type */
index 4563b28bd6254ddbeda6f0cd8229f22b10159ef7..3b98159d9645a8ecf917fb8c86c41de6a401c929 100644 (file)
@@ -46,6 +46,20 @@ config SND_SOC_P1022_DS
          This will also include the Wolfson Microelectronics WM8776 codec
          driver.
 
+config SND_SOC_P1022_RDK
+       tristate "ALSA SoC support for the Freescale / iVeia P1022 RDK board"
+       # I2C is necessary for the WM8960 driver
+       depends on P1022_RDK && I2C
+       select SND_SOC_FSL_SSI
+       select SND_SOC_FSL_UTILS
+       select SND_SOC_POWERPC_DMA
+       select SND_SOC_WM8960
+       default y if P1022_RDK
+       help
+         Say Y if you want to enable audio on the Freescale / iVeia
+         P1022 RDK board.  This will also include the Wolfson
+         Microelectronics WM8960 codec driver.
+
 config SND_SOC_MPC5200_I2S
        tristate "Freescale MPC5200 PSC in I2S mode driver"
        depends on PPC_MPC52xx && PPC_BESTCOMM
@@ -98,12 +112,12 @@ config SND_SOC_IMX_PCM
        tristate
 
 config SND_SOC_IMX_PCM_FIQ
-       tristate
+       bool
        select FIQ
        select SND_SOC_IMX_PCM
 
 config SND_SOC_IMX_PCM_DMA
-       tristate
+       bool
        select SND_SOC_DMAENGINE_PCM
        select SND_SOC_IMX_PCM
 
@@ -112,7 +126,7 @@ config SND_SOC_IMX_AUDMUX
 
 config SND_MXC_SOC_WM1133_EV1
        tristate "Audio on the i.MX31ADS with WM1133-EV1 fitted"
-       depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL
+       depends on MACH_MX31ADS_WM1133_EV1
        select SND_SOC_WM8350
        select SND_SOC_IMX_PCM_FIQ
        select SND_SOC_IMX_AUDMUX
index 5f3cf3f52ea03e16a1a374ca530f6daec7a2ed35..afd34794db539a0f5de4154a5a635a69f40913e1 100644 (file)
@@ -6,6 +6,10 @@ obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o
 snd-soc-p1022-ds-objs := p1022_ds.o
 obj-$(CONFIG_SND_SOC_P1022_DS) += snd-soc-p1022-ds.o
 
+# P1022 RDK Machine Support
+snd-soc-p1022-rdk-objs := p1022_rdk.o
+obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
+
 # Freescale PowerPC SSI/DMA Platform Support
 snd-soc-fsl-ssi-objs := fsl_ssi.o
 snd-soc-fsl-utils-objs := fsl_utils.o
@@ -26,14 +30,18 @@ obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o
 # i.MX Platform Support
 snd-soc-imx-ssi-objs := imx-ssi.o
 snd-soc-imx-audmux-objs := imx-audmux.o
+snd-soc-imx-pcm-objs := imx-pcm.o
+ifneq ($(CONFIG_SND_SOC_IMX_PCM_FIQ),)
+       snd-soc-imx-pcm-objs += imx-pcm-fiq.o
+endif
+ifneq ($(CONFIG_SND_SOC_IMX_PCM_DMA),)
+       snd-soc-imx-pcm-objs += imx-pcm-dma.o
+endif
 
 obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o
 obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o
 
 obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o
-snd-soc-imx-pcm-y := imx-pcm.o
-snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += imx-pcm-fiq.o
-snd-soc-imx-pcm-$(CONFIG_SND_SOC_IMX_PCM_DMA) += imx-pcm-dma.o
 
 # i.MX Machine Support
 snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
index 93dc360b17770cbb2833afdc6fc7eb5c70c5b19a..d5cd9eff3b48e92ca77349b930fe84056fcd7017 100644 (file)
@@ -103,3 +103,7 @@ void imx_pcm_free(struct snd_pcm *pcm)
        }
 }
 EXPORT_SYMBOL_GPL(imx_pcm_free);
+
+MODULE_DESCRIPTION("Freescale i.MX PCM driver");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL");
index 199408ec42612dfe57f63e3933457e901cf7bcfa..3d9b1c427ce912056afdf876f09388d05b1c0e5d 100644 (file)
@@ -162,6 +162,7 @@ static int __devinit imx_sgtl5000_probe(struct platform_device *pdev)
        if (ret)
                goto clk_fail;
        data->card.num_links = 1;
+       data->card.owner = THIS_MODULE;
        data->card.dai_link = &data->dai;
        data->card.dapm_widgets = imx_sgtl5000_dapm_widgets;
        data->card.num_dapm_widgets = ARRAY_SIZE(imx_sgtl5000_dapm_widgets);
diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c
new file mode 100644 (file)
index 0000000..897e32f
--- /dev/null
@@ -0,0 +1,392 @@
+/**
+ * Freescale P1022RDK ALSA SoC Machine driver
+ *
+ * Author: Timur Tabi <timur@freescale.com>
+ *
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ *
+ * Note: in order for audio to work correctly, the output controls need
+ * to be enabled, because they control the clock.  So for playback, for
+ * example:
+ *
+ *      amixer sset 'Left Output Mixer PCM' on
+ *      amixer sset 'Right Output Mixer PCM' on
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <asm/fsl_guts.h>
+
+#include "fsl_dma.h"
+#include "fsl_ssi.h"
+#include "fsl_utils.h"
+
+/* P1022-specific PMUXCR and DMUXCR bit definitions */
+
+#define CCSR_GUTS_PMUXCR_UART0_I2C1_MASK       0x0001c000
+#define CCSR_GUTS_PMUXCR_UART0_I2C1_UART0_SSI  0x00010000
+#define CCSR_GUTS_PMUXCR_UART0_I2C1_SSI                0x00018000
+
+#define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_MASK      0x00000c00
+#define CCSR_GUTS_PMUXCR_SSI_DMA_TDM_SSI       0x00000000
+
+#define CCSR_GUTS_DMUXCR_PAD   1       /* DMA controller/channel set to pad */
+#define CCSR_GUTS_DMUXCR_SSI   2       /* DMA controller/channel set to SSI */
+
+/*
+ * Set the DMACR register in the GUTS
+ *
+ * The DMACR register determines the source of initiated transfers for each
+ * channel on each DMA controller.  Rather than have a bunch of repetitive
+ * macros for the bit patterns, we just have a function that calculates
+ * them.
+ *
+ * guts: Pointer to GUTS structure
+ * co: The DMA controller (0 or 1)
+ * ch: The channel on the DMA controller (0, 1, 2, or 3)
+ * device: The device to set as the target (CCSR_GUTS_DMUXCR_xxx)
+ */
+static inline void guts_set_dmuxcr(struct ccsr_guts __iomem *guts,
+       unsigned int co, unsigned int ch, unsigned int device)
+{
+       unsigned int shift = 16 + (8 * (1 - co) + 2 * (3 - ch));
+
+       clrsetbits_be32(&guts->dmuxcr, 3 << shift, device << shift);
+}
+
+/* There's only one global utilities register */
+static phys_addr_t guts_phys;
+
+/**
+ * machine_data: machine-specific ASoC device data
+ *
+ * This structure contains data for a single sound platform device on an
+ * P1022 RDK.  Some of the data is taken from the device tree.
+ */
+struct machine_data {
+       struct snd_soc_dai_link dai[2];
+       struct snd_soc_card card;
+       unsigned int dai_format;
+       unsigned int codec_clk_direction;
+       unsigned int cpu_clk_direction;
+       unsigned int clk_frequency;
+       unsigned int dma_id[2];         /* 0 = DMA1, 1 = DMA2, etc */
+       unsigned int dma_channel_id[2]; /* 0 = ch 0, 1 = ch 1, etc*/
+       char platform_name[2][DAI_NAME_SIZE]; /* One for each DMA channel */
+};
+
+/**
+ * p1022_rdk_machine_probe: initialize the board
+ *
+ * This function is used to initialize the board-specific hardware.
+ *
+ * Here we program the DMACR and PMUXCR registers.
+ */
+static int p1022_rdk_machine_probe(struct snd_soc_card *card)
+{
+