Merge remote-tracking branch 'asoc/topic/fsl' into asoc-next
authorMark Brown <broonie@opensource.wolfsonmicro.com>
Tue, 12 Feb 2013 22:18:45 +0000 (22:18 +0000)
committerMark Brown <broonie@opensource.wolfsonmicro.com>
Tue, 12 Feb 2013 22:18:45 +0000 (22:18 +0000)
114 files changed:
Documentation/devicetree/bindings/sound/ak4642.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/cs4271.txt
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/omap-twl4030.txt
Documentation/devicetree/bindings/sound/renesas,fsi.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/samsung-i2s.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/tlv320aic3x.txt
Documentation/devicetree/bindings/sound/wm8962.txt [new file with mode: 0644]
arch/arm/boot/dts/exynos5250-smdk5250.dts
arch/arm/boot/dts/exynos5250.dtsi
arch/arm/mach-exynos/mach-exynos5-dt.c
arch/arm/mach-pxa/pxa27x.c
arch/arm/mach-shmobile/board-ap4evb.c
arch/arm/mach-shmobile/board-armadillo800eva.c
arch/arm/mach-shmobile/board-kzm9g.c
arch/arm/mach-shmobile/board-mackerel.c
arch/arm/plat-samsung/dma-ops.c
arch/arm/plat-samsung/include/plat/dma-ops.h
arch/arm/plat-samsung/s3c-dma-ops.c
arch/sh/boards/mach-ecovec24/setup.c
arch/sh/boards/mach-se/7724/setup.c
drivers/base/regmap/internal.h
drivers/base/regmap/regmap-spi.c
drivers/base/regmap/regmap.c
drivers/misc/Kconfig
drivers/misc/atmel-ssc.c
drivers/spi/spi-s3c64xx.c
include/linux/mfd/arizona/pdata.h
include/linux/regmap.h
include/sound/cs4271.h
include/sound/max98090.h [new file with mode: 0755]
include/sound/saif.h [deleted file]
include/sound/sh_fsi.h
include/sound/simple_card.h
include/sound/soc-dai.h
include/sound/soc.h
include/sound/tlv320aic3x.h
include/sound/wm2000.h
include/sound/wm2200.h
sound/arm/pxa2xx-ac97-lib.c
sound/soc/atmel/Kconfig
sound/soc/atmel/atmel-pcm-pdc.c
sound/soc/atmel/atmel-pcm.c
sound/soc/atmel/atmel-pcm.h
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/atmel/sam9g20_wm8731.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h
sound/soc/codecs/cs4271.c
sound/soc/codecs/cs42l52.c
sound/soc/codecs/max98090.c [changed mode: 0644->0755]
sound/soc/codecs/max98090.h [new file with mode: 0755]
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/tlv320aic3x.h
sound/soc/codecs/tlv320dac33.c
sound/soc/codecs/twl4030.c
sound/soc/codecs/twl6040.c
sound/soc/codecs/wm2000.c
sound/soc/codecs/wm2000.h
sound/soc/codecs/wm2200.c
sound/soc/codecs/wm5100.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8804.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8983.c
sound/soc/codecs/wm8985.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/codecs/wmfw.h
sound/soc/davinci/davinci-evm.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/dwc/designware_i2s.c
sound/soc/generic/simple-card.c
sound/soc/mxs/mxs-saif.c
sound/soc/omap/Kconfig
sound/soc/omap/Makefile
sound/soc/omap/n810.c
sound/soc/omap/omap-hdmi.c
sound/soc/omap/omap-pcm.c
sound/soc/omap/omap-twl4030.c
sound/soc/omap/omap3pandora.c
sound/soc/omap/rx51.c
sound/soc/omap/sdp3430.c [deleted file]
sound/soc/omap/zoom2.c [deleted file]
sound/soc/pxa/palm27x.c
sound/soc/samsung/Kconfig
sound/soc/samsung/dma.c
sound/soc/samsung/dma.h
sound/soc/samsung/i2s.c
sound/soc/samsung/i2s.h
sound/soc/samsung/s3c24xx-i2s.c
sound/soc/samsung/smdk_wm8580.c
sound/soc/samsung/smdk_wm8994.c
sound/soc/sh/fsi.c
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-pcm.c
sound/soc/tegra/Kconfig
sound/soc/tegra/Makefile
sound/soc/tegra/tegra20_ac97.c [new file with mode: 0644]
sound/soc/tegra/tegra20_ac97.h [new file with mode: 0644]
sound/soc/tegra/tegra20_das.c
sound/soc/tegra/tegra30_ahub.c
sound/soc/tegra/tegra30_i2s.c
sound/soc/tegra/tegra_asoc_utils.c
sound/soc/tegra/tegra_asoc_utils.h
sound/soc/tegra/tegra_wm9712.c [new file with mode: 0644]
sound/soc/ux500/mop500.c

diff --git a/Documentation/devicetree/bindings/sound/ak4642.txt b/Documentation/devicetree/bindings/sound/ak4642.txt
new file mode 100644 (file)
index 0000000..623d4e7
--- /dev/null
@@ -0,0 +1,17 @@
+AK4642 I2C transmitter
+
+This device supports I2C mode only.
+
+Required properties:
+
+  - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648"
+  - reg : The chip select number on the I2C bus
+
+Example:
+
+&i2c {
+       ak4648: ak4648@0x12 {
+               compatible = "asahi-kasei,ak4642";
+               reg = <0x12>;
+       };
+};
index a850fb9c88eab2156ec89f0c76d8ca59073540f8..e2cd1d7539e527897cc34566508ed56f070a60cb 100644 (file)
@@ -20,6 +20,18 @@ Optional properties:
                !RESET pin
  - cirrus,amuteb-eq-bmutec:    When given, the Codec's AMUTEB=BMUTEC flag
                                is enabled.
+ - cirrus,enable-soft-reset:
+       The CS4271 requires its LRCLK and MCLK to be stable before its RESET
+       line is de-asserted. That also means that clocks cannot be changed
+       without putting the chip back into hardware reset, which also requires
+       a complete re-initialization of all registers.
+
+       One (undocumented) workaround is to assert and de-assert the PDN bit
+       in the MODE2 register. This workaround can be enabled with this DT
+       property.
+
+       Note that this is not needed in case the clocks are stable
+       throughout the entire runtime of the codec.
 
 Examples:
 
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt
new file mode 100644 (file)
index 0000000..be35d34
--- /dev/null
@@ -0,0 +1,51 @@
+NVIDIA Tegra audio complex
+
+Required properties:
+- compatible : "nvidia,tegra-audio-wm9712"
+- nvidia,model : The user-visible name of this sound complex.
+- nvidia,audio-routing : A list of the connections between audio components.
+  Each entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source. Valid names for sources and
+  sinks are the WM9712's pins, and the jacks on the board:
+
+  WM9712 pins:
+
+  * MONOOUT
+  * HPOUTL
+  * HPOUTR
+  * LOUT2
+  * ROUT2
+  * OUT3
+  * LINEINL
+  * LINEINR
+  * PHONE
+  * PCBEEP
+  * MIC1
+  * MIC2
+  * Mic Bias
+
+  Board connectors:
+
+  * Headphone
+  * LineIn
+  * Mic
+
+- nvidia,ac97-controller : The phandle of the Tegra AC97 controller
+
+
+Example:
+
+sound {
+       compatible = "nvidia,tegra-audio-wm9712-colibri_t20",
+                        "nvidia,tegra-audio-wm9712";
+       nvidia,model = "Toradex Colibri T20";
+
+       nvidia,audio-routing =
+               "Headphone", "HPOUTL",
+               "Headphone", "HPOUTR",
+               "LineIn", "LINEINL",
+               "LineIn", "LINEINR",
+               "Mic", "MIC1";
+
+       nvidia,ac97-controller = <&ac97>;
+};
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt
new file mode 100644 (file)
index 0000000..c145497
--- /dev/null
@@ -0,0 +1,22 @@
+NVIDIA Tegra 20 AC97 controller
+
+Required properties:
+- compatible : "nvidia,tegra20-ac97"
+- reg : Should contain AC97 controller registers location and length
+- interrupts : Should contain AC97 interrupt
+- nvidia,dma-request-selector : The Tegra DMA controller's phandle and
+  request selector for the AC97 controller
+- nvidia,codec-reset-gpio : The Tegra GPIO controller's phandle and the number
+  of the GPIO used to reset the external AC97 codec
+- nvidia,codec-sync-gpio : The Tegra GPIO controller's phandle and the number
+  of the GPIO corresponding with the AC97 DAP _FS line
+Example:
+
+ac97@70002000 {
+       compatible = "nvidia,tegra20-ac97";
+       reg = <0x70002000 0x200>;
+       interrupts = <0 81 0x04>;
+       nvidia,dma-request-selector = <&apbdma 12>;
+       nvidia,codec-reset-gpio = <&gpio 170 0>;
+       nvidia,codec-sync-gpio = <&gpio 120 0>;
+};
index 6fae51c7f766fe995ba96627cde3f1bfeb661426..1ab6bc8404d5bc850dea1fc1533f451b03787e5f 100644 (file)
@@ -6,6 +6,52 @@ Required properties:
 - ti,mcbsp: phandle for the McBSP node
 - ti,codec: phandle for the twl4030 audio node
 
+Optional properties:
+- ti,mcbsp-voice: phandle for the McBSP node connected to the voice port of twl
+- ti, jack-det-gpio: Jack detect GPIO
+- ti,audio-routing: List of connections between audio components.
+  Each entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source.
+  If the routing is not provided all possible connection will be available
+
+Available audio endpoints for the audio-routing table:
+
+Board connectors:
+ * Headset Stereophone
+ * Earpiece Spk
+ * Handsfree Spk
+ * Ext Spk
+ * Main Mic
+ * Sub Mic
+ * Headset Mic
+ * Carkit Mic
+ * Digital0 Mic
+ * Digital1 Mic
+ * Line In
+
+twl4030 pins:
+ * HSOL
+ * HSOR
+ * EARPIECE
+ * HFL
+ * HFR
+ * PREDRIVEL
+ * PREDRIVER
+ * CARKITL
+ * CARKITR
+ * MAINMIC
+ * SUBMIC
+ * HSMIC
+ * DIGIMIC0
+ * DIGIMIC1
+ * CARKITMIC
+ * AUXL
+ * AUXR
+
+ * Headset Mic Bias
+ * Mic Bias 1 /* Used for Main Mic or Digimic0 */
+ * Mic Bias 2 /* Used for Sub Mic or Digimic1 */
+
 Example:
 
 sound {
diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.txt b/Documentation/devicetree/bindings/sound/renesas,fsi.txt
new file mode 100644 (file)
index 0000000..c5be003
--- /dev/null
@@ -0,0 +1,26 @@
+Renesas FSI
+
+Required properties:
+- compatible                   : "renesas,sh_fsi2" or "renesas,sh_fsi"
+- reg                          : Should contain the register physical address and length
+- interrupts                   : Should contain FSI interrupt
+
+- fsia,spdif-connection                : FSI is connected by S/PDFI
+- fsia,stream-mode-support     : FSI supports 16bit stream mode.
+- fsia,use-internal-clock      : FSI uses internal clock when master mode.
+
+- fsib,spdif-connection                : same as fsia
+- fsib,stream-mode-support     : same as fsia
+- fsib,use-internal-clock      : same as fsia
+
+Example:
+
+sh_fsi2: sh_fsi2@0xec230000 {
+       compatible = "renesas,sh_fsi2";
+       reg = <0xec230000 0x400>;
+       interrupts = <0 146 0x4>;
+
+       fsia,spdif-connection;
+       fsia,stream-mode-support;
+       fsia,use-internal-clock;
+};
diff --git a/Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt b/Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt
new file mode 100644 (file)
index 0000000..4686646
--- /dev/null
@@ -0,0 +1,14 @@
+Samsung SMDK audio complex
+
+Required properties:
+- compatible : "samsung,smdk-wm8994"
+- samsung,i2s-controller: The phandle of the Samsung I2S0 controller
+- samsung,audio-codec: The phandle of the WM8994 audio codec
+Example:
+
+sound {
+               compatible = "samsung,smdk-wm8994";
+
+               samsung,i2s-controller = <&i2s0>;
+               samsung,audio-codec = <&wm8994>;
+};
diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt
new file mode 100644 (file)
index 0000000..3070046
--- /dev/null
@@ -0,0 +1,63 @@
+* Samsung I2S controller
+
+Required SoC Specific Properties:
+
+- compatible : "samsung,i2s-v5"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- dmas: list of DMA controller phandle and DMA request line ordered pairs.
+- dma-names: identifier string for each DMA request line in the dmas property.
+  These strings correspond 1:1 with the ordered pairs in dmas.
+
+Optional SoC Specific Properties:
+
+- samsung,supports-6ch: If the I2S Primary sound source has 5.1 Channel
+  support, this flag is enabled.
+- samsung,supports-rstclr: This flag should be set if I2S software reset bit
+  control is required. When this flag is set I2S software reset bit will be
+  enabled or disabled based on need.
+- samsung,supports-secdai:If I2S block has a secondary FIFO and internal DMA,
+  then this flag is enabled.
+- samsung,idma-addr: Internal DMA register base address of the audio
+  sub system(used in secondary sound source).
+
+Required Board Specific Properties:
+
+- gpios: The gpio specifier for data out,data in, LRCLK, CDCLK and SCLK
+  interface lines. The format of the gpio specifier depends on the gpio
+  controller.
+  The syntax of samsung gpio specifier is
+       <[phandle of the gpio controller node]
+        [pin number within the gpio controller]
+        [mux function]
+        [flags and pull up/down]
+        [drive strength]>
+
+Example:
+
+- SoC Specific Portion:
+
+i2s@03830000 {
+       compatible = "samsung,i2s-v5";
+       reg = <0x03830000 0x100>;
+       dmas = <&pdma0 10
+               &pdma0 9
+               &pdma0 8>;
+       dma-names = "tx", "rx", "tx-sec";
+       samsung,supports-6ch;
+       samsung,supports-rstclr;
+       samsung,supports-secdai;
+       samsung,idma-addr = <0x03000000>;
+};
+
+- Board Specific Portion:
+
+i2s@03830000 {
+       gpios = <&gpz 0 2 0 0>, /* I2S_0_SCLK */
+               <&gpz 1 2 0 0>, /* I2S_0_CDCLK */
+               <&gpz 2 2 0 0>, /* I2S_0_LRCK */
+               <&gpz 3 2 0 0>, /* I2S_0_SDI */
+               <&gpz 4 2 0 0>, /* I2S_0_SDO[1] */
+               <&gpz 5 2 0 0>, /* I2S_0_SDO[2] */
+               <&gpz 6 2 0 0>; /* I2S_0_SDO[3] */
+};
index e7b98f41fa5fd14eba634184848529f1b519a68c..f47c3f589fd03a8deb2485292116e33d17a0f7da 100644 (file)
@@ -11,6 +11,12 @@ Optional properties:
 
 - gpio-reset - gpio pin number used for codec reset
 - ai3x-gpio-func - <array of 2 int> - AIC3X_GPIO1 & AIC3X_GPIO2 Functionality
+- ai3x-micbias-vg - MicBias Voltage required.
+       1 - MICBIAS output is powered to 2.0V,
+       2 - MICBIAS output is powered to 2.5V,
+       3 - MICBIAS output is connected to AVDD,
+       If this node is not mentioned or if the value is incorrect, then MicBias
+       is powered down.
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/sound/wm8962.txt b/Documentation/devicetree/bindings/sound/wm8962.txt
new file mode 100644 (file)
index 0000000..dceb3b1
--- /dev/null
@@ -0,0 +1,16 @@
+WM8962 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+  - compatible : "wlf,wm8962"
+
+  - reg : the I2C address of the device.
+
+Example:
+
+codec: wm8962@1a {
+       compatible = "wlf,wm8962";
+       reg = <0x1a>;
+};
index e05b18f3c33d67d6e5202083bf60f8aa65b4344f..4db9db0a844333fb6db6da2bd0de80d4811949f6 100644 (file)
                        compatible = "samsung,s524ad0xd1";
                        reg = <0x51>;
                };
+
+               wm8994: wm8994@1a {
+                        compatible = "wlf,wm8994";
+                        reg = <0x1a>;
+               };
        };
 
        i2c@121D0000 {
                samsung,mfc-r = <0x43000000 0x800000>;
                samsung,mfc-l = <0x51000000 0x800000>;
        };
+
+       i2s0: i2s@03830000 {
+               gpios = <&gpz 0 2 0 0>, <&gpz 1 2 0 0>, <&gpz 2 2 0 0>,
+                       <&gpz 3 2 0 0>, <&gpz 4 2 0 0>, <&gpz 5 2 0 0>,
+                       <&gpz 6 2 0 0>;
+       };
+
+       i2s1: i2s@12D60000 {
+               status = "disabled";
+       };
+
+       i2s2: i2s@12D70000 {
+               status = "disabled";
+       };
+
+       sound {
+               compatible = "samsung,smdk-wm8994";
+
+               samsung,i2s-controller = <&i2s0>;
+               samsung,audio-codec = <&wm8994>;
+       };
 };
index 3acf594ea60bda97bc5e53187011455bf86b0c5b..f50b4e854355342bf0fe18d69bd4cf41ffc0c06f 100644 (file)
                compatible = "samsung,exynos4210-spi";
                reg = <0x12d20000 0x100>;
                interrupts = <0 66 0>;
-               tx-dma-channel = <&pdma0 5>; /* preliminary */
-               rx-dma-channel = <&pdma0 4>; /* preliminary */
+               dmas = <&pdma0 5
+                       &pdma0 4>;
+               dma-names = "tx", "rx";
                #address-cells = <1>;
                #size-cells = <0>;
        };
                compatible = "samsung,exynos4210-spi";
                reg = <0x12d30000 0x100>;
                interrupts = <0 67 0>;
-               tx-dma-channel = <&pdma1 5>; /* preliminary */
-               rx-dma-channel = <&pdma1 4>; /* preliminary */
+               dmas = <&pdma1 5
+                       &pdma1 4>;
+               dma-names = "tx", "rx";
                #address-cells = <1>;
                #size-cells = <0>;
        };
                compatible = "samsung,exynos4210-spi";
                reg = <0x12d40000 0x100>;
                interrupts = <0 68 0>;
-               tx-dma-channel = <&pdma0 7>; /* preliminary */
-               rx-dma-channel = <&pdma0 6>; /* preliminary */
+               dmas = <&pdma0 7
+                       &pdma0 6>;
+               dma-names = "tx", "rx";
                #address-cells = <1>;
                #size-cells = <0>;
        };
                #size-cells = <0>;
        };
 
+       i2s0: i2s@03830000 {
+               compatible = "samsung,i2s-v5";
+               reg = <0x03830000 0x100>;
+               dmas = <&pdma0 10
+                       &pdma0 9
+                       &pdma0 8>;
+               dma-names = "tx", "rx", "tx-sec";
+               samsung,supports-6ch;
+               samsung,supports-rstclr;
+               samsung,supports-secdai;
+               samsung,idma-addr = <0x03000000>;
+       };
+
+       i2s1: i2s@12D60000 {
+               compatible = "samsung,i2s-v5";
+               reg = <0x12D60000 0x100>;
+               dmas = <&pdma1 12
+                       &pdma1 11>;
+               dma-names = "tx", "rx";
+       };
+
+       i2s2: i2s@12D70000 {
+               compatible = "samsung,i2s-v5";
+               reg = <0x12D70000 0x100>;
+               dmas = <&pdma0 12
+                       &pdma0 11>;
+               dma-names = "tx", "rx";
+       };
+
        amba {
                #address-cells = <1>;
                #size-cells = <1>;
index e99d3d8f2bcf436ab52a07f5c8d1d7ad519d8467..ea9e3020972d2b2a18ac7b26d559035d36e58f46 100644 (file)
@@ -104,6 +104,12 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = {
        OF_DEV_AUXDATA("samsung,mfc-v6", 0x11000000, "s5p-mfc-v6", NULL),
        OF_DEV_AUXDATA("samsung,exynos5250-tmu", 0x10060000,
                                "exynos-tmu", NULL),
+       OF_DEV_AUXDATA("samsung,i2s-v5", 0x03830000,
+                               "samsung-i2s.0", NULL),
+       OF_DEV_AUXDATA("samsung,i2s-v5", 0x12D60000,
+                               "samsung-i2s.1", NULL),
+       OF_DEV_AUXDATA("samsung,i2s-v5", 0x12D70000,
+                               "samsung-i2s.2", NULL),
        {},
 };
 
index 616cb87b61792544f470110cd6322b2d3ea9d914..69985b06c0dae83e5864bf76691599c6499e4e73 100644 (file)
@@ -53,17 +53,25 @@ static unsigned long ac97_reset_config[] = {
        GPIO95_AC97_nRESET,
 };
 
-void pxa27x_assert_ac97reset(int reset_gpio, int on)
+void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio)
 {
+       /*
+        * This helper function is used to work around a bug in the pxa27x's
+        * ac97 controller during a warm reset.  The configuration of the
+        * reset_gpio is changed as follows:
+        * to_gpio == true: configured to generic output gpio and driven high
+        * to_gpio == false: configured to ac97 controller alt fn AC97_nRESET
+        */
+
        if (reset_gpio == 113)
-               pxa2xx_mfp_config(on ? &ac97_reset_config[0] :
-                                      &ac97_reset_config[1], 1);
+               pxa2xx_mfp_config(to_gpio ? &ac97_reset_config[0] :
+                                 &ac97_reset_config[1], 1);
 
        if (reset_gpio == 95)
-               pxa2xx_mfp_config(on ? &ac97_reset_config[2] :
-                                      &ac97_reset_config[3], 1);
+               pxa2xx_mfp_config(to_gpio ? &ac97_reset_config[2] :
+                                 &ac97_reset_config[3], 1);
 }
-EXPORT_SYMBOL_GPL(pxa27x_assert_ac97reset);
+EXPORT_SYMBOL_GPL(pxa27x_configure_ac97reset);
 
 /* Crystal clock: 13MHz */
 #define BASE_CLK       13000000
index 99ef190d0909d5ee5bf633efd0b660061244e71b..08294fa9e0d47a84b4f478298dba40b865dcf5ba 100644 (file)
@@ -657,14 +657,8 @@ static struct platform_device lcdc_device = {
 /* FSI */
 #define IRQ_FSI                evt2irq(0x1840)
 static struct sh_fsi_platform_info fsi_info = {
-       .port_a = {
-               .flags          = SH_FSI_BRS_INV,
-       },
        .port_b = {
-               .flags          = SH_FSI_BRS_INV |
-                                 SH_FSI_BRM_INV |
-                                 SH_FSI_LRS_INV |
-                                 SH_FSI_CLK_CPG |
+               .flags          = SH_FSI_CLK_CPG |
                                  SH_FSI_FMT_SPDIF,
        },
 };
@@ -692,21 +686,21 @@ static struct platform_device fsi_device = {
        },
 };
 
-static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = {
-       .fmt            = SND_SOC_DAIFMT_LEFT_J,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
-       .sysclk         = 11289600,
-};
-
 static struct asoc_simple_card_info fsi2_ak4643_info = {
        .name           = "AK4643",
        .card           = "FSI2A-AK4643",
-       .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0013",
        .platform       = "sh_fsi2",
-       .codec_dai      = "ak4642-hifi",
-       .init           = &fsi2_ak4643_init_info,
+       .daifmt         = SND_SOC_DAIFMT_LEFT_J,
+       .cpu_dai = {
+               .name   = "fsia-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS,
+       },
+       .codec_dai = {
+               .name   = "ak4642-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+               .sysclk = 11289600,
+       },
 };
 
 static struct platform_device fsi_ak4643_device = {
@@ -815,18 +809,18 @@ static struct platform_device lcdc1_device = {
        },
 };
 
-static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = {
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBM_CFM,
-};
-
 static struct asoc_simple_card_info fsi2_hdmi_info = {
        .name           = "HDMI",
        .card           = "FSI2B-HDMI",
-       .cpu_dai        = "fsib-dai",
        .codec          = "sh-mobile-hdmi",
        .platform       = "sh_fsi2",
-       .codec_dai      = "sh_mobile_hdmi-hifi",
-       .init           = &fsi2_hdmi_init_info,
+       .cpu_dai = {
+               .name   = "fsib-dai",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF,
+       },
+       .codec_dai = {
+               .name   = "sh_mobile_hdmi-hifi",
+       },
 };
 
 static struct platform_device fsi_hdmi_device = {
index 5353adf6b828a55446933e070968c9b4bec20a94..0679ca6bf1f67bfaf64c0d1d118a6c908b044655 100644 (file)
@@ -806,21 +806,21 @@ static struct platform_device fsi_device = {
 };
 
 /* FSI-WM8978 */
-static struct asoc_simple_dai_init_info fsi_wm8978_init_info = {
-       .fmt            = SND_SOC_DAIFMT_I2S,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_NB_NF,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
-       .sysclk         = 12288000,
-};
-
 static struct asoc_simple_card_info fsi_wm8978_info = {
        .name           = "wm8978",
        .card           = "FSI2A-WM8978",
-       .cpu_dai        = "fsia-dai",
        .codec          = "wm8978.0-001a",
        .platform       = "sh_fsi2",
-       .codec_dai      = "wm8978-hifi",
-       .init           = &fsi_wm8978_init_info,
+       .daifmt         = SND_SOC_DAIFMT_I2S,
+       .cpu_dai = {
+               .name   = "fsia-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF,
+       },
+       .codec_dai = {
+               .name   = "wm8978-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_NB_NF,
+               .sysclk = 12288000,
+       },
 };
 
 static struct platform_device fsi_wm8978_device = {
@@ -832,18 +832,18 @@ static struct platform_device fsi_wm8978_device = {
 };
 
 /* FSI-HDMI */
-static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = {
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBM_CFM,
-};
-
 static struct asoc_simple_card_info fsi2_hdmi_info = {
        .name           = "HDMI",
        .card           = "FSI2B-HDMI",
-       .cpu_dai        = "fsib-dai",
        .codec          = "sh-mobile-hdmi",
        .platform       = "sh_fsi2",
-       .codec_dai      = "sh_mobile_hdmi-hifi",
-       .init           = &fsi2_hdmi_init_info,
+       .cpu_dai = {
+               .name   = "fsib-dai",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+       },
+       .codec_dai = {
+               .name = "sh_mobile_hdmi-hifi",
+       },
 };
 
 static struct platform_device fsi_hdmi_device = {
index c02448d6847f40aba1d2906e9957e376aecd0df4..f41b71e8df3eb331eedd4816ea876cd3b10313c5 100644 (file)
@@ -525,21 +525,21 @@ static struct platform_device fsi_device = {
        },
 };
 
-static struct asoc_simple_dai_init_info fsi2_ak4648_init_info = {
-       .fmt            = SND_SOC_DAIFMT_LEFT_J,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
-       .sysclk         = 11289600,
-};
-
 static struct asoc_simple_card_info fsi2_ak4648_info = {
        .name           = "AK4648",
        .card           = "FSI2A-AK4648",
-       .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0012",
        .platform       = "sh_fsi2",
-       .codec_dai      = "ak4642-hifi",
-       .init           = &fsi2_ak4648_init_info,
+       .daifmt         = SND_SOC_DAIFMT_LEFT_J,
+       .cpu_dai = {
+               .name   = "fsia-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS,
+       },
+       .codec_dai = {
+               .name   = "ak4642-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+               .sysclk = 11289600,
+       },
 };
 
 static struct platform_device fsi_ak4648_device = {
index 2fed62f660459ccae4b08f8548543309587eb0fc..3fd716dae40544d33f11fe1f45556304446fe165 100644 (file)
@@ -502,18 +502,18 @@ static struct platform_device hdmi_lcdc_device = {
        },
 };
 
-static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = {
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBM_CFM,
-};
-
 static struct asoc_simple_card_info fsi2_hdmi_info = {
        .name           = "HDMI",
        .card           = "FSI2B-HDMI",
-       .cpu_dai        = "fsib-dai",
        .codec          = "sh-mobile-hdmi",
        .platform       = "sh_fsi2",
-       .codec_dai      = "sh_mobile_hdmi-hifi",
-       .init           = &fsi2_hdmi_init_info,
+       .cpu_dai = {
+               .name   = "fsib-dai",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF,
+       },
+       .codec_dai = {
+               .name   = "sh_mobile_hdmi-hifi",
+       },
 };
 
 static struct platform_device fsi_hdmi_device = {
@@ -858,16 +858,12 @@ static struct platform_device leds_device = {
 #define IRQ_FSI evt2irq(0x1840)
 static struct sh_fsi_platform_info fsi_info = {
        .port_a = {
-               .flags = SH_FSI_BRS_INV,
                .tx_id = SHDMA_SLAVE_FSIA_TX,
                .rx_id = SHDMA_SLAVE_FSIA_RX,
        },
        .port_b = {
-               .flags = SH_FSI_BRS_INV |
-                       SH_FSI_BRM_INV  |
-                       SH_FSI_LRS_INV  |
-                       SH_FSI_CLK_CPG  |
-                       SH_FSI_FMT_SPDIF,
+               .flags = SH_FSI_CLK_CPG |
+                        SH_FSI_FMT_SPDIF,
        }
 };
 
@@ -896,21 +892,21 @@ static struct platform_device fsi_device = {
        },
 };
 
-static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = {
-       .fmt            = SND_SOC_DAIFMT_LEFT_J,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
-       .sysclk         = 11289600,
-};
-
 static struct asoc_simple_card_info fsi2_ak4643_info = {
        .name           = "AK4643",
        .card           = "FSI2A-AK4643",
-       .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0013",
        .platform       = "sh_fsi2",
-       .codec_dai      = "ak4642-hifi",
-       .init           = &fsi2_ak4643_init_info,
+       .daifmt         = SND_SOC_DAIFMT_LEFT_J,
+       .cpu_dai = {
+               .name   = "fsia-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS,
+       },
+       .codec_dai = {
+               .name   = "ak4642-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+               .sysclk = 11289600,
+       },
 };
 
 static struct platform_device fsi_ak4643_device = {
index d088afa034e87bf2d4df760a690e1bdb33bc0281..71d58ddea9c1a375efb0366c8fab3d1f1850cd72 100644 (file)
@@ -19,7 +19,8 @@
 #include <mach/dma.h>
 
 static unsigned samsung_dmadev_request(enum dma_ch dma_ch,
-                               struct samsung_dma_req *param)
+                               struct samsung_dma_req *param,
+                               struct device *dev, char *ch_name)
 {
        dma_cap_mask_t mask;
        void *filter_param;
@@ -33,7 +34,12 @@ static unsigned samsung_dmadev_request(enum dma_ch dma_ch,
         */
        filter_param = (dma_ch == DMACH_DT_PROP) ?
                (void *)param->dt_dmach_prop : (void *)dma_ch;
-       return (unsigned)dma_request_channel(mask, pl330_filter, filter_param);
+
+       if (dev->of_node)
+               return (unsigned)dma_request_slave_channel(dev, ch_name);
+       else
+               return (unsigned)dma_request_channel(mask, pl330_filter,
+                                                       filter_param);
 }
 
 static int samsung_dmadev_release(unsigned ch, void *param)
index f5144cdd300106b5afb936015a35251eae1d2de9..114178268b75e70d8e25e5035d893d81c8d8f60b 100644 (file)
@@ -39,7 +39,8 @@ struct samsung_dma_config {
 };
 
 struct samsung_dma_ops {
-       unsigned (*request)(enum dma_ch ch, struct samsung_dma_req *param);
+       unsigned (*request)(enum dma_ch ch, struct samsung_dma_req *param,
+                               struct device *dev, char *ch_name);
        int (*release)(unsigned ch, void *param);
        int (*config)(unsigned ch, struct samsung_dma_config *param);
        int (*prepare)(unsigned ch, struct samsung_dma_prep *param);
index f99448c48d30dc93d1dcd7a3f9658b523a9e10dd..0cc40aea3f5adbd9a78ff8557f246f476d1b97e2 100644 (file)
@@ -36,7 +36,8 @@ static void s3c_dma_cb(struct s3c2410_dma_chan *channel, void *param,
 }
 
 static unsigned s3c_dma_request(enum dma_ch dma_ch,
-                                       struct samsung_dma_req *param)
+                               struct samsung_dma_req *param,
+                               struct device *dev, char *ch_name)
 {
        struct cb_data *data;
 
index a0fa5791cd44abe9b29784e26142338c11768c1d..aaff7671101b301cedc1e30edf2055fc6aa008c8 100644 (file)
@@ -887,12 +887,6 @@ static struct platform_device camera_devices[] = {
 };
 
 /* FSI */
-static struct sh_fsi_platform_info fsi_info = {
-       .port_b = {
-               .flags = SH_FSI_BRS_INV,
-       },
-};
-
 static struct resource fsi_resources[] = {
        [0] = {
                .name   = "FSI",
@@ -911,25 +905,22 @@ static struct platform_device fsi_device = {
        .id             = 0,
        .num_resources  = ARRAY_SIZE(fsi_resources),
        .resource       = fsi_resources,
-       .dev    = {
-               .platform_data  = &fsi_info,
-       },
-};
-
-static struct asoc_simple_dai_init_info fsi_da7210_init_info = {
-       .fmt            = SND_SOC_DAIFMT_I2S,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
 };
 
 static struct asoc_simple_card_info fsi_da7210_info = {
        .name           = "DA7210",
        .card           = "FSIB-DA7210",
-       .cpu_dai        = "fsib-dai",
        .codec          = "da7210.0-001a",
        .platform       = "sh_fsi.0",
-       .codec_dai      = "da7210-hifi",
-       .init           = &fsi_da7210_init_info,
+       .daifmt         = SND_SOC_DAIFMT_I2S,
+       .cpu_dai = {
+               .name   = "fsib-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF,
+       },
+       .codec_dai = {
+               .name   = "da7210-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+       },
 };
 
 static struct platform_device fsi_da7210_device = {
index 35f6efa3ac0e60c8f1842ef36629e3356622d0c9..4010e63e82d8491422457d32459a8d216455d4ef 100644 (file)
@@ -279,12 +279,6 @@ static struct platform_device ceu1_device = {
 
 /* FSI */
 /* change J20, J21, J22 pin to 1-2 connection to use slave mode */
-static struct sh_fsi_platform_info fsi_info = {
-       .port_a = {
-               .flags = SH_FSI_BRS_INV,
-       },
-};
-
 static struct resource fsi_resources[] = {
        [0] = {
                .name   = "FSI",
@@ -303,26 +297,23 @@ static struct platform_device fsi_device = {
        .id             = 0,
        .num_resources  = ARRAY_SIZE(fsi_resources),
        .resource       = fsi_resources,
-       .dev    = {
-               .platform_data  = &fsi_info,
-       },
-};
-
-static struct asoc_simple_dai_init_info fsi2_ak4642_init_info = {
-       .fmt            = SND_SOC_DAIFMT_LEFT_J,
-       .codec_daifmt   = SND_SOC_DAIFMT_CBM_CFM,
-       .cpu_daifmt     = SND_SOC_DAIFMT_CBS_CFS,
-       .sysclk         = 11289600,
 };
 
 static struct asoc_simple_card_info fsi_ak4642_info = {
        .name           = "AK4642",
        .card           = "FSIA-AK4642",
-       .cpu_dai        = "fsia-dai",
        .codec          = "ak4642-codec.0-0012",
        .platform       = "sh_fsi.0",
-       .codec_dai      = "ak4642-hifi",
-       .init           = &fsi2_ak4642_init_info,
+       .daifmt         = SND_SOC_DAIFMT_LEFT_J,
+       .cpu_dai = {
+               .name   = "fsia-dai",
+               .fmt    = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF,
+       },
+       .codec_dai = {
+               .name   = "ak4642-hifi",
+               .fmt    = SND_SOC_DAIFMT_CBM_CFM,
+               .sysclk = 11289600,
+       },
 };
 
 static struct platform_device fsi_ak4642_device = {
index 401d1919635a5fcdf9bac6b84d99ffdc485d0e14..2025186417792e8649ab24dd40a23b99f59bab60 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/regmap.h>
 #include <linux/fs.h>
 #include <linux/list.h>
+#include <linux/wait.h>
 
 struct regmap;
 struct regcache_ops;
@@ -39,6 +40,13 @@ struct regmap_format {
        unsigned int (*parse_val)(void *buf);
 };
 
+struct regmap_async {
+       struct list_head list;
+       struct work_struct cleanup;
+       struct regmap *map;
+       void *work_buf;
+};
+
 struct regmap {
        struct mutex mutex;
        spinlock_t spinlock;
@@ -53,6 +61,11 @@ struct regmap {
        void *bus_context;
        const char *name;
 
+       spinlock_t async_lock;
+       wait_queue_head_t async_waitq;
+       struct list_head async_list;
+       int async_ret;
+
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs;
        const char *debugfs_name;
@@ -74,6 +87,9 @@ struct regmap {
        const struct regmap_access_table *volatile_table;
        const struct regmap_access_table *precious_table;
 
+       int (*reg_read)(void *context, unsigned int reg, unsigned int *val);
+       int (*reg_write)(void *context, unsigned int reg, unsigned int val);
+
        u8 read_flag_mask;
        u8 write_flag_mask;
 
@@ -175,6 +191,8 @@ bool regcache_set_val(void *base, unsigned int idx,
                      unsigned int val, unsigned int word_size);
 int regcache_lookup_reg(struct regmap *map, unsigned int reg);
 
+void regmap_async_complete_cb(struct regmap_async *async, int ret);
+
 extern struct regcache_ops regcache_rbtree_ops;
 extern struct regcache_ops regcache_lzo_ops;
 
index ffa46a92ad3383dcc8609cd891514f0b5c3af967..913274b5f00a0dc11893bf40ef79416765712f8c 100644 (file)
 #include <linux/init.h>
 #include <linux/module.h>
 
+#include "internal.h"
+
+struct regmap_async_spi {
+       struct regmap_async core;
+       struct spi_message m;
+       struct spi_transfer t[2];
+};
+
+static void regmap_spi_complete(void *data)
+{
+       struct regmap_async_spi *async = data;
+
+       regmap_async_complete_cb(&async->core, async->m.status);
+}
+
 static int regmap_spi_write(void *context, const void *data, size_t count)
 {
        struct device *dev = context;
@@ -40,6 +55,41 @@ static int regmap_spi_gather_write(void *context,
        return spi_sync(spi, &m);
 }
 
+static int regmap_spi_async_write(void *context,
+                                 const void *reg, size_t reg_len,
+                                 const void *val, size_t val_len,
+                                 struct regmap_async *a)
+{
+       struct regmap_async_spi *async = container_of(a,
+                                                     struct regmap_async_spi,
+                                                     core);
+       struct device *dev = context;
+       struct spi_device *spi = to_spi_device(dev);
+
+       async->t[0].tx_buf = reg;
+       async->t[0].len = reg_len;
+       async->t[1].tx_buf = val;
+       async->t[1].len = val_len;
+
+       spi_message_init(&async->m);
+       spi_message_add_tail(&async->t[0], &async->m);
+       spi_message_add_tail(&async->t[1], &async->m);
+
+       async->m.complete = regmap_spi_complete;
+       async->m.context = async;
+
+       return spi_async(spi, &async->m);
+}
+
+static struct regmap_async *regmap_spi_async_alloc(void)
+{
+       struct regmap_async_spi *async_spi;
+
+       async_spi = kzalloc(sizeof(*async_spi), GFP_KERNEL);
+
+       return &async_spi->core;
+}
+
 static int regmap_spi_read(void *context,
                           const void *reg, size_t reg_size,
                           void *val, size_t val_size)
@@ -53,6 +103,8 @@ static int regmap_spi_read(void *context,
 static struct regmap_bus regmap_spi = {
        .write = regmap_spi_write,
        .gather_write = regmap_spi_gather_write,
+       .async_write = regmap_spi_async_write,
+       .async_alloc = regmap_spi_async_alloc,
        .read = regmap_spi_read,
        .read_flag_mask = 0x80,
 };
index f00b059c057ad01e4c8b9f05e95676c3c53c0e79..47825095ebfdcde6a679c8d54cab1d21c1feb8dd 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/mutex.h>
 #include <linux/err.h>
 #include <linux/rbtree.h>
+#include <linux/sched.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/regmap.h>
@@ -34,6 +35,22 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
                               unsigned int mask, unsigned int val,
                               bool *change);
 
+static int _regmap_bus_read(void *context, unsigned int reg,
+                           unsigned int *val);
+static int _regmap_bus_formatted_write(void *context, unsigned int reg,
+                                      unsigned int val);
+static int _regmap_bus_raw_write(void *context, unsigned int reg,
+                                unsigned int val);
+
+static void async_cleanup(struct work_struct *work)
+{
+       struct regmap_async *async = container_of(work, struct regmap_async,
+                                                 cleanup);
+
+       kfree(async->work_buf);
+       kfree(async);
+}
+
 bool regmap_reg_in_ranges(unsigned int reg,
                          const struct regmap_range *ranges,
                          unsigned int nranges)
@@ -423,6 +440,10 @@ struct regmap *regmap_init(struct device *dev,
        map->cache_type = config->cache_type;
        map->name = config->name;
 
+       spin_lock_init(&map->async_lock);
+       INIT_LIST_HEAD(&map->async_list);
+       init_waitqueue_head(&map->async_waitq);
+
        if (config->read_flag_mask || config->write_flag_mask) {
                map->read_flag_mask = config->read_flag_mask;
                map->write_flag_mask = config->write_flag_mask;
@@ -430,6 +451,8 @@ struct regmap *regmap_init(struct device *dev,
                map->read_flag_mask = bus->read_flag_mask;
        }
 
+       map->reg_read = _regmap_bus_read;
+
        reg_endian = config->reg_format_endian;
        if (reg_endian == REGMAP_ENDIAN_DEFAULT)
                reg_endian = bus->reg_format_endian_default;
@@ -575,6 +598,11 @@ struct regmap *regmap_init(struct device *dev,
                goto err_map;
        }
 
+       if (map->format.format_write)
+               map->reg_write = _regmap_bus_formatted_write;
+       else if (map->format.format_val)
+               map->reg_write = _regmap_bus_raw_write;
+
        map->range_tree = RB_ROOT;
        for (i = 0; i < config->num_ranges; i++) {
                const struct regmap_range_cfg *range_cfg = &config->ranges[i];
@@ -870,10 +898,13 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg,
 }
 
 static int _regmap_raw_write(struct regmap *map, unsigned int reg,
-                            const void *val, size_t val_len)
+                            const void *val, size_t val_len, bool async)
 {
        struct regmap_range_node *range;
+       unsigned long flags;
        u8 *u8 = map->work_buf;
+       void *work_val = map->work_buf + map->format.reg_bytes +
+               map->format.pad_bytes;
        void *buf;
        int ret = -ENOTSUPP;
        size_t len;
@@ -918,7 +949,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
                        dev_dbg(map->dev, "Writing window %d/%zu\n",
                                win_residue, val_len / map->format.val_bytes);
                        ret = _regmap_raw_write(map, reg, val, win_residue *
-                                               map->format.val_bytes);
+                                               map->format.val_bytes, async);
                        if (ret != 0)
                                return ret;
 
@@ -941,6 +972,50 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
 
        u8[0] |= map->write_flag_mask;
 
+       if (async && map->bus->async_write) {
+               struct regmap_async *async = map->bus->async_alloc();
+               if (!async)
+                       return -ENOMEM;
+
+               async->work_buf = kzalloc(map->format.buf_size,
+                                         GFP_KERNEL | GFP_DMA);
+               if (!async->work_buf) {
+                       kfree(async);
+                       return -ENOMEM;
+               }
+
+               INIT_WORK(&async->cleanup, async_cleanup);
+               async->map = map;
+
+               /* If the caller supplied the value we can use it safely. */
+               memcpy(async->work_buf, map->work_buf, map->format.pad_bytes +
+                      map->format.reg_bytes + map->format.val_bytes);
+               if (val == work_val)
+                       val = async->work_buf + map->format.pad_bytes +
+                               map->format.reg_bytes;
+
+               spin_lock_irqsave(&map->async_lock, flags);
+               list_add_tail(&async->list, &map->async_list);
+               spin_unlock_irqrestore(&map->async_lock, flags);
+
+               ret = map->bus->async_write(map->bus_context, async->work_buf,
+                                           map->format.reg_bytes +
+                                           map->format.pad_bytes,
+                                           val, val_len, async);
+
+               if (ret != 0) {
+                       dev_err(map->dev, "Failed to schedule write: %d\n",
+                               ret);
+
+                       spin_lock_irqsave(&map->async_lock, flags);
+                       list_del(&async->list);
+                       spin_unlock_irqrestore(&map->async_lock, flags);
+
+                       kfree(async->work_buf);
+                       kfree(async);
+               }
+       }
+
        trace_regmap_hw_write_start(map->dev, reg,
                                    val_len / map->format.val_bytes);
 
@@ -948,8 +1023,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
         * send the work_buf directly, otherwise try to do a gather
         * write.
         */
-       if (val == (map->work_buf + map->format.pad_bytes +
-                   map->format.reg_bytes))
+       if (val == work_val)
                ret = map->bus->write(map->bus_context, map->work_buf,
                                      map->format.reg_bytes +
                                      map->format.pad_bytes +
@@ -981,12 +1055,54 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
        return ret;
 }
 
+static int _regmap_bus_formatted_write(void *context, unsigned int reg,
+                                      unsigned int val)
+{
+       int ret;
+       struct regmap_range_node *range;
+       struct regmap *map = context;
+
+       BUG_ON(!map->format.format_write);
+
+       range = _regmap_range_lookup(map, reg);
+       if (range) {
+               ret = _regmap_select_page(map, &reg, range, 1);
+               if (ret != 0)
+                       return ret;
+       }
+
+       map->format.format_write(map, reg, val);
+
+       trace_regmap_hw_write_start(map->dev, reg, 1);
+
+       ret = map->bus->write(map->bus_context, map->work_buf,
+                             map->format.buf_size);
+
+       trace_regmap_hw_write_done(map->dev, reg, 1);
+
+       return ret;
+}
+
+static int _regmap_bus_raw_write(void *context, unsigned int reg,
+                                unsigned int val)
+{
+       struct regmap *map = context;
+
+       BUG_ON(!map->format.format_val);
+
+       map->format.format_val(map->work_buf + map->format.reg_bytes
+                              + map->format.pad_bytes, val, 0);
+       return _regmap_raw_write(map, reg,
+                                map->work_buf +
+                                map->format.reg_bytes +
+                                map->format.pad_bytes,
+                                map->format.val_bytes, false);
+}
+
 int _regmap_write(struct regmap *map, unsigned int reg,
                  unsigned int val)
 {
-       struct regmap_range_node *range;
        int ret;
-       BUG_ON(!map->format.format_write && !map->format.format_val);
 
        if (!map->cache_bypass && map->format.format_write) {
                ret = regcache_write(map, reg, val);
@@ -1005,33 +1121,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
 
        trace_regmap_reg_write(map->dev, reg, val);
 
-       if (map->format.format_write) {
-               range = _regmap_range_lookup(map, reg);
-               if (range) {
-                       ret = _regmap_select_page(map, &reg, range, 1);
-                       if (ret != 0)
-                               return ret;
-               }
-
-               map->format.format_write(map, reg, val);
-
-               trace_regmap_hw_write_start(map->dev, reg, 1);
-
-               ret = map->bus->write(map->bus_context, map->work_buf,
-                                     map->format.buf_size);
-
-               trace_regmap_hw_write_done(map->dev, reg, 1);
-
-               return ret;
-       } else {
-               map->format.format_val(map->work_buf + map->format.reg_bytes
-                                      + map->format.pad_bytes, val, 0);
-               return _regmap_raw_write(map, reg,
-                                        map->work_buf +
-                                        map->format.reg_bytes +
-                                        map->format.pad_bytes,
-                                        map->format.val_bytes);
-       }
+       return map->reg_write(map, reg, val);
 }
 
 /**
@@ -1089,7 +1179,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
 
        map->lock(map->lock_arg);
 
-       ret = _regmap_raw_write(map, reg, val, val_len);
+       ret = _regmap_raw_write(map, reg, val, val_len, false);
 
        map->unlock(map->lock_arg);
 
@@ -1145,14 +1235,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
        if (map->use_single_rw) {
                for (i = 0; i < val_count; i++) {
                        ret = regmap_raw_write(map,
-                                               reg + (i * map->reg_stride),
-                                               val + (i * val_bytes),
-                                               val_bytes);
+                                              reg + (i * map->reg_stride),
+                                              val + (i * val_bytes),
+                                              val_bytes);
                        if (ret != 0)
                                return ret;
                }
        } else {
-               ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
+               ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count,
+                                       false);
        }
 
        if (val_bytes != 1)
@@ -1164,6 +1255,48 @@ out:
 }
 EXPORT_SYMBOL_GPL(regmap_bulk_write);
 
+/**
+ * regmap_raw_write_async(): Write raw values to one or more registers
+ *                           asynchronously
+ *
+ * @map: Register map to write to
+ * @reg: Initial register to write to
+ * @val: Block of data to be written, laid out for direct transmission to the
+ *       device.  Must be valid until regmap_async_complete() is called.
+ * @val_len: Length of data pointed to by val.
+ *
+ * This function is intended to be used for things like firmware
+ * download where a large block of data needs to be transferred to the
+ * device.  No formatting will be done on the data provided.
+ *
+ * If supported by the underlying bus the write will be scheduled
+ * asynchronously, helping maximise I/O speed on higher speed buses
+ * like SPI.  regmap_async_complete() can be called to ensure that all
+ * asynchrnous writes have been completed.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_raw_write_async(struct regmap *map, unsigned int reg,
+                          const void *val, size_t val_len)
+{
+       int ret;
+
+       if (val_len % map->format.val_bytes)
+               return -EINVAL;
+       if (reg % map->reg_stride)
+               return -EINVAL;
+
+       map->lock(map->lock_arg);
+
+       ret = _regmap_raw_write(map, reg, val, val_len, true);
+
+       map->unlock(map->lock_arg);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_raw_write_async);
+
 static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
                            unsigned int val_len)
 {
@@ -1202,10 +1335,27 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
        return ret;
 }
 
+static int _regmap_bus_read(void *context, unsigned int reg,
+                           unsigned int *val)
+{
+       int ret;
+       struct regmap *map = context;
+
+       if (!map->format.parse_val)
+               return -EINVAL;
+
+       ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
+       if (ret == 0)
+               *val = map->format.parse_val(map->work_buf);
+
+       return ret;
+}
+
 static int _regmap_read(struct regmap *map, unsigned int reg,
                        unsigned int *val)
 {
        int ret;
+       BUG_ON(!map->reg_read);
 
        if (!map->cache_bypass) {
                ret = regcache_read(map, reg, val);
@@ -1213,26 +1363,21 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
                        return 0;
        }
 
-       if (!map->format.parse_val)
-               return -EINVAL;
-
        if (map->cache_only)
                return -EBUSY;
 
-       ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
+       ret = map->reg_read(map, reg, val);
        if (ret == 0) {
-               *val = map->format.parse_val(map->work_buf);
-
 #ifdef LOG_DEVICE
                if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
                        dev_info(map->dev, "%x => %x\n", reg, *val);
 #endif
 
                trace_regmap_reg_read(map->dev, reg, *val);
-       }
 
-       if (ret == 0 && !map->cache_bypass)
-               regcache_write(map, reg, *val);
+               if (!map->cache_bypass)
+                       regcache_write(map, reg, *val);
+       }
 
        return ret;
 }
@@ -1450,6 +1595,68 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
 }
 EXPORT_SYMBOL_GPL(regmap_update_bits_check);
 
+void regmap_async_complete_cb(struct regmap_async *async, int ret)
+{
+       struct regmap *map = async->map;
+       bool wake;
+
+       spin_lock(&map->async_lock);
+
+       list_del(&async->list);
+       wake = list_empty(&map->async_list);
+
+       if (ret != 0)
+               map->async_ret = ret;
+
+       spin_unlock(&map->async_lock);
+
+       schedule_work(&async->cleanup);
+
+       if (wake)
+               wake_up(&map->async_waitq);
+}
+EXPORT_SYMBOL_GPL(regmap_async_complete_cb);
+
+static int regmap_async_is_done(struct regmap *map)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&map->async_lock, flags);
+       ret = list_empty(&map->async_list);
+       spin_unlock_irqrestore(&map->async_lock, flags);
+
+       return ret;
+}
+
+/**
+ * regmap_async_complete: Ensure all asynchronous I/O has completed.
+ *
+ * @map: Map to operate on.
+ *
+ * Blocks until any pending asynchronous I/O has completed.  Returns
+ * an error code for any failed I/O operations.
+ */
+int regmap_async_complete(struct regmap *map)
+{
+       unsigned long flags;
+       int ret;
+
+       /* Nothing to do with no async support */
+       if (!map->bus->async_write)
+               return 0;
+
+       wait_event(map->async_waitq, regmap_async_is_done(map));
+
+       spin_lock_irqsave(&map->async_lock, flags);
+       ret = map->async_ret;
+       map->async_ret = 0;
+       spin_unlock_irqrestore(&map->async_lock, flags);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_async_complete);
+
 /**
  * regmap_register_patch: Register and apply register updates to be applied
  *                        on device initialistion
index b151b7c1bd59d9fe0f7cad745802572a6f7156ea..8f59d88897e7287354354b1c2f7cf807e97b18b3 100644 (file)
@@ -192,7 +192,7 @@ config ICS932S401
 
 config ATMEL_SSC
        tristate "Device driver for Atmel SSC peripheral"
-       depends on AVR32 || ARCH_AT91
+       depends on HAS_IOMEM
        ---help---
          This option enables device driver support for Atmel Synchronized
          Serial Communication peripheral (SSC).
index 3c09cbb70b1dc01e45f1d6f4fdb93d7e351ad880..bd90dd23242e882e6e11fa1481a002e7689450d8 100644 (file)
@@ -175,7 +175,7 @@ static int ssc_probe(struct platform_device *pdev)
 
        /* disable all interrupts */
        clk_enable(ssc->clk);
-       ssc_writel(ssc->regs, IDR, ~0UL);
+       ssc_writel(ssc->regs, IDR, -1);
        ssc_readl(ssc->regs, SR);
        clk_disable(ssc->clk);
 
index ad93231a8038dd9ae9adf3f5ea611ebd9c785758..51a8c4216ebbc82136c59f3e4cedd7e87213a3a4 100644 (file)
@@ -134,7 +134,6 @@ struct s3c64xx_spi_dma_data {
        unsigned                ch;
        enum dma_transfer_direction direction;
        enum dma_ch     dmach;
-       struct property         *dma_prop;
 };
 
 /**
@@ -319,16 +318,15 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
 static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
 {
        struct samsung_dma_req req;
+       struct device *dev = &sdd->pdev->dev;
 
        sdd->ops = samsung_dma_get_ops();
 
        req.cap = DMA_SLAVE;
        req.client = &s3c64xx_spi_dma_client;
 
-       req.dt_dmach_prop = sdd->rx_dma.dma_prop;
-       sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req);
-       req.dt_dmach_prop = sdd->tx_dma.dma_prop;
-       sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req);
+       sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req, dev, "rx");
+       sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req, dev, "tx");
 
        return 1;
 }
@@ -1054,49 +1052,6 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
        flush_fifo(sdd);
 }
 
-static int s3c64xx_spi_get_dmares(
-                       struct s3c64xx_spi_driver_data *sdd, bool tx)
-{
-       struct platform_device *pdev = sdd->pdev;
-       struct s3c64xx_spi_dma_data *dma_data;
-       struct property *prop;
-       struct resource *res;
-       char prop_name[15], *chan_str;
-
-       if (tx) {
-               dma_data = &sdd->tx_dma;
-               dma_data->direction = DMA_MEM_TO_DEV;
-               chan_str = "tx";
-       } else {
-               dma_data = &sdd->rx_dma;
-               dma_data->direction = DMA_DEV_TO_MEM;
-               chan_str = "rx";
-       }
-
-       if (!sdd->pdev->dev.of_node) {
-               res = platform_get_resource(pdev, IORESOURCE_DMA, tx ? 0 : 1);
-               if (!res) {
-                       dev_err(&pdev->dev, "Unable to get SPI-%s dma "
-                                       "resource\n", chan_str);
-                       return -ENXIO;
-               }
-               dma_data->dmach = res->start;
-               return 0;
-       }
-
-       sprintf(prop_name, "%s-dma-channel", chan_str);
-       prop = of_find_property(pdev->dev.of_node, prop_name, NULL);
-       if (!prop) {
-               dev_err(&pdev->dev, "%s dma channel property not specified\n",
-                                       chan_str);
-               return -ENXIO;
-       }
-
-       dma_data->dmach = DMACH_DT_PROP;
-       dma_data->dma_prop = prop;
-       return 0;
-}
-
 #ifdef CONFIG_OF
 static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd)
 {
@@ -1198,6 +1153,7 @@ static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config(
 static int __init s3c64xx_spi_probe(struct platform_device *pdev)
 {
        struct resource *mem_res;
+       struct resource *res;
        struct s3c64xx_spi_driver_data *sdd;
        struct s3c64xx_spi_info *sci = pdev->dev.platform_data;
        struct spi_master *master;
@@ -1256,13 +1212,26 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
 
        sdd->cur_bpw = 8;
 
-       ret = s3c64xx_spi_get_dmares(sdd, true);
-       if (ret)
-               goto err0;
+       if (!sdd->pdev->dev.of_node) {
+               res = platform_get_resource(pdev, IORESOURCE_DMA,  0);
+               if (!res) {
+                       dev_err(&pdev->dev, "Unable to get SPI tx dma "
+                                       "resource\n");
+                       return -ENXIO;
+               }
+               sdd->tx_dma.dmach = res->start;
+
+               res = platform_get_resource(pdev, IORESOURCE_DMA,  1);
+               if (!res) {
+                       dev_err(&pdev->dev, "Unable to get SPI rx dma "
+                                       "resource\n");
+                       return -ENXIO;
+               }
+               sdd->rx_dma.dmach = res->start;
+       }
 
-       ret = s3c64xx_spi_get_dmares(sdd, false);
-       if (ret)
-               goto err0;
+       sdd->tx_dma.direction = DMA_MEM_TO_DEV;
+       sdd->rx_dma.direction = DMA_DEV_TO_MEM;
 
        master->dev.of_node = pdev->dev.of_node;
        master->bus_num = sdd->port_id;
index 8b1d1daaae16c27e59d037f46899a1f6c631acc4..ec3e2a2a6d77a9a16c8916f63783dc2e0822036e 100644 (file)
@@ -62,6 +62,8 @@
 
 #define ARIZONA_MAX_OUTPUT 6
 
+#define ARIZONA_MAX_AIF 3
+
 #define ARIZONA_HAP_ACT_ERM 0
 #define ARIZONA_HAP_ACT_LRA 2
 
@@ -96,6 +98,13 @@ struct arizona_pdata {
        /** Pin state for GPIO pins */
        int gpio_defaults[ARIZONA_MAX_GPIO];
 
+       /**
+        * Maximum number of channels clocks will be generated for,
+        * useful for systems where and I2S bus with multiple data
+        * lines is mastered.
+        */
+       int max_channels_clocked[ARIZONA_MAX_AIF];
+
        /** GPIO for mic detection polarity */
        int micd_pol_gpio;
 
index b7e95bf942c9ce8936f61c61efb0261f9c09ad37..f9b7fbe35ab1ba0382b6e5e1791316f937aa28c9 100644 (file)
@@ -235,14 +235,21 @@ struct regmap_range_cfg {
        unsigned int window_len;
 };
 
+struct regmap_async;
+
 typedef int (*regmap_hw_write)(void *context, const void *data,
                               size_t count);
 typedef int (*regmap_hw_gather_write)(void *context,
                                      const void *reg, size_t reg_len,
                                      const void *val, size_t val_len);
+typedef int (*regmap_hw_async_write)(void *context,
+                                    const void *reg, size_t reg_len,
+                                    const void *val, size_t val_len,
+                                    struct regmap_async *async);
 typedef int (*regmap_hw_read)(void *context,
                              const void *reg_buf, size_t reg_size,
                              void *val_buf, size_t val_size);
+typedef struct regmap_async *(*regmap_hw_async_alloc)(void);
 typedef void (*regmap_hw_free_context)(void *context);
 
 /**
@@ -255,8 +262,11 @@ typedef void (*regmap_hw_free_context)(void *context);
  * @write: Write operation.
  * @gather_write: Write operation with split register/value, return -ENOTSUPP
  *                if not implemented  on a given device.
+ * @async_write: Write operation which completes asynchronously, optional and
+ *               must serialise with respect to non-async I/O.
  * @read: Read operation.  Data is returned in the buffer used to transmit
  *         data.
+ * @async_alloc: Allocate a regmap_async() structure.
  * @read_flag_mask: Mask to be set in the top byte of the register when doing
  *                  a read.
  * @reg_format_endian_default: Default endianness for formatted register
@@ -265,13 +275,16 @@ typedef void (*regmap_hw_free_context)(void *context);
  * @val_format_endian_default: Default endianness for formatted register
  *     values. Used when the regmap_config specifies DEFAULT. If this is
  *     DEFAULT, BIG is assumed.
+ * @async_size: Size of struct used for async work.
  */
 struct regmap_bus {
        bool fast_io;
        regmap_hw_write write;
        regmap_hw_gather_write gather_write;
+       regmap_hw_async_write async_write;
        regmap_hw_read read;
        regmap_hw_free_context free_context;
+       regmap_hw_async_alloc async_alloc;
        u8 read_flag_mask;
        enum regmap_endian reg_format_endian_default;
        enum regmap_endian val_format_endian_default;
@@ -310,6 +323,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
                     const void *val, size_t val_len);
 int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
                        size_t val_count);
+int regmap_raw_write_async(struct regmap *map, unsigned int reg,
+                          const void *val, size_t val_len);
 int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
 int regmap_raw_read(struct regmap *map, unsigned int reg,
                    void *val, size_t val_len);
@@ -321,6 +336,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
                             unsigned int mask, unsigned int val,
                             bool *change);
 int regmap_get_val_bytes(struct regmap *map);
+int regmap_async_complete(struct regmap *map);
 
 int regcache_sync(struct regmap *map);
 int regcache_sync_region(struct regmap *map, unsigned int min,
@@ -422,6 +438,13 @@ static inline int regmap_raw_write(struct regmap *map, unsigned int reg,
        return -EINVAL;
 }
 
+static inline int regmap_raw_write_async(struct regmap *map, unsigned int reg,
+                                        const void *val, size_t val_len)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+       return -EINVAL;
+}
+
 static inline int regmap_bulk_write(struct regmap *map, unsigned int reg,
                                    const void *val, size_t val_count)
 {
@@ -500,6 +523,11 @@ static inline void regcache_mark_dirty(struct regmap *map)
        WARN_ONCE(1, "regmap API is disabled");
 }
 
+static inline void regmap_async_complete(struct regmap *map)
+{
+       WARN_ONCE(1, "regmap API is disabled");
+}
+
 static inline int regmap_register_patch(struct regmap *map,
                                        const struct reg_default *regs,
                                        int num_regs)
index dd8c48d14ed9637b3d1264ac5b88811e9be2dae6..70f45355acaa299eed01ecdc2ecc1f967a77e114 100644 (file)
 struct cs4271_platform_data {
        int gpio_nreset;        /* GPIO driving Reset pin, if any */
        bool amutec_eq_bmutec;  /* flag to enable AMUTEC=BMUTEC */
+
+       /*
+        * The CS4271 requires its LRCLK and MCLK to be stable before its RESET
+        * line is de-asserted. That also means that clocks cannot be changed
+        * without putting the chip back into hardware reset, which also requires
+        * a complete re-initialization of all registers.
+        *
+        * One (undocumented) workaround is to assert and de-assert the PDN bit
+        * in the MODE2 register. This workaround can be enabled with the
+        * following flag.
+        *
+        * Note that this is not needed in case the clocks are stable
+        * throughout the entire runtime of the codec.
+        */
+       bool enable_soft_reset;
 };
 
 #endif /* __CS4271_H */
diff --git a/include/sound/max98090.h b/include/sound/max98090.h
new file mode 100755 (executable)
index 0000000..95efb13
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Platform data for MAX98090
+ *
+ * Copyright 2011-2012 Maxim Integrated Products
+ *
+ *  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.
+ *
+ */
+
+#ifndef __SOUND_MAX98090_PDATA_H__
+#define __SOUND_MAX98090_PDATA_H__
+
+/* codec platform data */
+struct max98090_pdata {
+
+       /* Analog/digital microphone configuration:
+        * 0 = analog microphone input (normal setting)
+        * 1 = digital microphone input
+        */
+       unsigned int digmic_left_mode:1;
+       unsigned int digmic_right_mode:1;
+       unsigned int digmic_3_mode:1;
+       unsigned int digmic_4_mode:1;
+};
+
+#endif
diff --git a/include/sound/saif.h b/include/sound/saif.h
deleted file mode 100644 (file)
index f22f3e1..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __SOUND_SAIF_H__
-#define __SOUND_SAIF_H__
-
-struct mxs_saif_platform_data {
-       bool master_mode;       /* if true use master mode */
-       int master_id;          /* id of the master if in slave mode */
-};
-#endif
index cc1c919c64365898c47f7f6871b906dce0e5e1d4..7a9710b4b799cd057ec2319e16a9237586d1411a 100644 (file)
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
-
-#define FSI_PORT_A     0
-#define FSI_PORT_B     1
-
 #include <linux/clk.h>
 #include <sound/soc.h>
 
 /*
- * flags format
- *
- * 0x00000CBA
- *
- * A:  inversion
- * B:  format mode
- * C:  chip specific
- * D:  clock selecter if master mode
+ * flags
  */
-
-/* A: clock inversion */
-#define SH_FSI_INVERSION_MASK  0x0000000F
-#define SH_FSI_LRM_INV         (1 << 0)
-#define SH_FSI_BRM_INV         (1 << 1)
-#define SH_FSI_LRS_INV         (1 << 2)
-#define SH_FSI_BRS_INV         (1 << 3)
-
-/* B: format mode */
-#define SH_FSI_FMT_MASK                0x000000F0
-#define SH_FSI_FMT_DAI         (0 << 4)
-#define SH_FSI_FMT_SPDIF       (1 << 4)
-
-/* C: chip specific */
-#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
- *
- * see ACKMD/BPFMD on
- *     ACK_MD (FSI2)
- *     CKG1   (FSI)
- *
- * err         : return value <  0
- * no change   : return value == 0
- * change xMD  : return value >  0
- *
- * 0x-00000AB
- *
- * A:  ACKMD value
- * B:  BPFMD value
- */
-
-#define SH_FSI_ACKMD_MASK      (0xF << 0)
-#define SH_FSI_ACKMD_512       (1 << 0)
-#define SH_FSI_ACKMD_256       (2 << 0)
-#define SH_FSI_ACKMD_128       (3 << 0)
-#define SH_FSI_ACKMD_64                (4 << 0)
-#define SH_FSI_ACKMD_32                (5 << 0)
-
-#define SH_FSI_BPFMD_MASK      (0xF << 4)
-#define SH_FSI_BPFMD_512       (1 << 4)
-#define SH_FSI_BPFMD_256       (2 << 4)
-#define SH_FSI_BPFMD_128       (3 << 4)
-#define SH_FSI_BPFMD_64                (4 << 4)
-#define SH_FSI_BPFMD_32                (5 << 4)
-#define SH_FSI_BPFMD_16                (6 << 4)
+#define SH_FSI_FMT_SPDIF               (1 << 0) /* spdif for HDMI */
+#define SH_FSI_ENABLE_STREAM_MODE      (1 << 1) /* for 16bit data */
+#define SH_FSI_CLK_CPG                 (1 << 2) /* FSIxCK + FSI-DIV */
 
 struct sh_fsi_port_info {
        unsigned long flags;
        int tx_id;
        int rx_id;
-       int (*set_rate)(struct device *dev, int rate, int enable);
 };
 
 struct sh_fsi_platform_info {
index 4b62b8dc6a4fd2baddef3e1c7f60b24c9ed2a1ae..6c74527d4926b3e476428fb6327652d9ca22dfbb 100644 (file)
 
 #include <sound/soc.h>
 
-struct asoc_simple_dai_init_info {
+struct asoc_simple_dai {
+       const char *name;
        unsigned int fmt;
-       unsigned int cpu_daifmt;
-       unsigned int codec_daifmt;
        unsigned int sysclk;
 };
 
 struct asoc_simple_card_info {
        const char *name;
        const char *card;
-       const char *cpu_dai;
        const char *codec;
        const char *platform;
-       const char *codec_dai;
-       struct asoc_simple_dai_init_info *init; /* for snd_link.init */
+
+       unsigned int daifmt;
+       struct asoc_simple_dai cpu_dai;
+       struct asoc_simple_dai codec_dai;
 
        /* used in simple-card.c */
        struct snd_soc_dai_link snd_link;
index 3953cea0ecfbf4255525bc00e821d5b031564070..3d84808952b9146ad9e6b479ddb44eb228fa3f2f 100644 (file)
@@ -45,7 +45,7 @@ struct snd_compr_stream;
  * sending or receiving PCM data in a frame. This can be used to save power.
  */
 #define SND_SOC_DAIFMT_CONT            (1 << 4) /* continuous clock */
-#define SND_SOC_DAIFMT_GATED           (2 << 4) /* clock is gated */
+#define SND_SOC_DAIFMT_GATED           (0 << 4) /* clock is gated */
 
 /*
  * DAI hardware signal inversions.
@@ -53,7 +53,7 @@ struct snd_compr_stream;
  * Specifies whether the DAI can also support inverted clocks for the specified
  * format.
  */
-#define SND_SOC_DAIFMT_NB_NF           (1 << 8) /* normal bit clock + frame */
+#define SND_SOC_DAIFMT_NB_NF           (0 << 8) /* normal bit clock + frame */
 #define SND_SOC_DAIFMT_NB_IF           (2 << 8) /* normal BCLK + inv FRM */
 #define SND_SOC_DAIFMT_IB_NF           (3 << 8) /* invert BCLK + nor FRM */
 #define SND_SOC_DAIFMT_IB_IF           (4 << 8) /* invert BCLK + FRM */
@@ -126,7 +126,8 @@ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
 
 /* Digital Audio Interface mute */
-int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute);
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
+                            int direction);
 
 struct snd_soc_dai_ops {
        /*
@@ -157,6 +158,7 @@ struct snd_soc_dai_ops {
         * Called by soc-core to minimise any pops.
         */
        int (*digital_mute)(struct snd_soc_dai *dai, int mute);
+       int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream);
 
        /*
         * ALSA PCM audio operations - all optional.
index bc56738cb1091b8319b2413fe7e6b2f105ba0ac8..a6a059ca387471083de2dc2e21e1584f6796925f 100644 (file)
@@ -906,8 +906,8 @@ struct snd_soc_dai_link {
                        struct snd_pcm_hw_params *params);
 
        /* machine stream operations */
-       struct snd_soc_ops *ops;
-       struct snd_soc_compr_ops *compr_ops;
+       const struct snd_soc_ops *ops;
+       const struct snd_soc_compr_ops *compr_ops;
 };
 
 struct snd_soc_codec_conf {
@@ -1171,6 +1171,8 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card,
                               const char *propname);
 int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                                   const char *propname);
+unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
+                                    const char *prefix);
 
 #include <sound/soc-dai.h>
 
index ffd9bc7931053f0713c88df9b9ee6def2e938cae..9407fd00363be527b3ce27e9f5aabca8fff5a2b0 100644 (file)
@@ -46,6 +46,13 @@ enum {
        AIC3X_GPIO2_FUNC_BUTTON_PRESS_IRQ       = 15
 };
 
+enum aic3x_micbias_voltage {
+       AIC3X_MICBIAS_OFF = 0,
+       AIC3X_MICBIAS_2_0V = 1,
+       AIC3X_MICBIAS_2_5V = 2,
+       AIC3X_MICBIAS_AVDDV = 3,
+};
+
 struct aic3x_setup_data {
        unsigned int gpio_func[2];
 };
@@ -53,6 +60,9 @@ struct aic3x_setup_data {
 struct aic3x_pdata {
        int gpio_reset; /* < 0 if not used */
        struct aic3x_setup_data *setup;
+
+       /* Selects the micbias voltage */
+       enum aic3x_micbias_voltage micbias_vg;
 };
 
 #endif
index aa388ca9ec64739f0c8d717be02c164f331d621b..4de81f41c90ffb40f9ce6a6b61d8b83591398d9f 100644 (file)
@@ -15,9 +15,6 @@ struct wm2000_platform_data {
        /** Filename for system-specific image to download to device. */
        const char *download_file;
 
-       /** Divide MCLK by 2 for system clock? */
-       unsigned int mclkdiv2:1;
-
        /** Disable speech clarity enhancement, for use when an
         * external algorithm is used. */
        unsigned int speech_enh_disable:1;
index 79bf55be7ffa844dbea9bf971e1f9832c00c1fcf..bc7ab1a4b48000adf5dbe96f3ab7fe355cfaed75 100644 (file)
@@ -12,6 +12,7 @@
 #define __LINUX_SND_WM2200_H
 
 #define WM2200_GPIO_SET 0x10000
+#define WM2200_MAX_MICBIAS 2
 
 enum wm2200_in_mode {
        WM2200_IN_SE = 0,
@@ -25,6 +26,24 @@ enum wm2200_dmic_sup {
        WM2200_DMIC_SUP_MICBIAS2 = 2,
 };
 
+enum wm2200_mbias_lvl {
+       WM2200_MBIAS_LVL_1V5 = 1,
+       WM2200_MBIAS_LVL_1V8 = 2,
+       WM2200_MBIAS_LVL_1V9 = 3,
+       WM2200_MBIAS_LVL_2V0 = 4,
+       WM2200_MBIAS_LVL_2V2 = 5,
+       WM2200_MBIAS_LVL_2V4 = 6,
+       WM2200_MBIAS_LVL_2V5 = 7,
+       WM2200_MBIAS_LVL_2V6 = 8,
+};
+
+struct wm2200_micbias {
+       enum wm2200_mbias_lvl mb_lvl;      /** Regulated voltage */
+       unsigned int discharge:1;          /** Actively discharge */
+       unsigned int fast_start:1;         /** Enable aggressive startup ramp rate */
+       unsigned int bypass:1;             /** Use bypass mode */
+};
+
 struct wm2200_pdata {
        int reset;      /** GPIO controlling /RESET, if any */
        int ldo_ena;    /** GPIO controlling LODENA, if any */
@@ -35,7 +54,8 @@ struct wm2200_pdata {
        enum wm2200_in_mode in_mode[3];
        enum wm2200_dmic_sup dmic_sup[3];
 
-       int micbias_cfg[2];  /** Register value to configure MICBIAS */
+       /** MICBIAS configurations */
+       struct wm2200_micbias micbias[WM2200_MAX_MICBIAS];
 };
 
 #endif
index fff7753e35c15ba6f82612594fbda19e7203b66e..e6f4633b8dd5b6b9cdfebc6409fc6db279393aed 100644 (file)
@@ -34,7 +34,7 @@ static struct clk *ac97_clk;
 static struct clk *ac97conf_clk;
 static int reset_gpio;
 
-extern void pxa27x_assert_ac97reset(int reset_gpio, int on);
+extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio);
 
 /*
  * Beware PXA27x bugs:
@@ -140,10 +140,10 @@ static inline void pxa_ac97_warm_pxa27x(void)
        gsr_bits = 0;
 
        /* warm reset broken on Bulverde, so manually keep AC97 reset high */
-       pxa27x_assert_ac97reset(reset_gpio, 1);
+       pxa27x_configure_ac97reset(reset_gpio, true);
        udelay(10);
        GCR |= GCR_WARM_RST;
-       pxa27x_assert_ac97reset(reset_gpio, 0);
+       pxa27x_configure_ac97reset(reset_gpio, false);
        udelay(500);
 }
 
@@ -358,7 +358,7 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev)
                               __func__, ret);
                        goto err_conf;
                }
-               pxa27x_assert_ac97reset(reset_gpio, 0);
+               pxa27x_configure_ac97reset(reset_gpio, false);
 
                ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
                if (IS_ERR(ac97conf_clk)) {
index d1b691bf8e2d918cb3e8fd97e61cce74c464cd76..3fdd87fa18a970db151fdadcc578ca61c87d3241 100644 (file)
@@ -1,6 +1,6 @@
 config SND_ATMEL_SOC
        tristate "SoC Audio for the Atmel System-on-Chip"
-       depends on ARCH_AT91
+       depends on HAS_IOMEM
        help
          Say Y or M if you want to add support for codecs attached to
          the ATMEL SSC interface. You will also need
@@ -24,7 +24,7 @@ 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 && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS
+       depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS
        select SND_ATMEL_SOC_PDC
        select SND_ATMEL_SOC_SSC
        select SND_SOC_WM8731
@@ -34,7 +34,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
+       depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
        select SND_ATMEL_SOC_PDC
        select SND_ATMEL_SOC_SSC
        select SND_SOC_TLV320AIC23
index 6a293c713a38889a80fae114553b5d38263c3a0b..054ea4d9326a2e34f8f9bc6f6ae51f18fb1d98dd 100644 (file)
@@ -159,7 +159,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
 
        pr_debug("atmel-pcm: "
                "hw_params: DMA for %s initialized "
-               "(dma_bytes=%u, period_size=%u)\n",
+               "(dma_bytes=%zu, period_size=%zu)\n",
                prtd->params->name,
                runtime->dma_bytes,
                prtd->period_size);
@@ -201,7 +201,7 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
        int ret = 0;
 
        pr_debug("atmel-pcm:buffer_size = %ld,"
-               "dma_area = %p, dma_bytes = %u\n",
+               "dma_area = %p, dma_bytes = %zu\n",
                rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
 
        switch (cmd) {
index e99f1811300aef179a5c1d5c33dc702249175681..3109db7b9017cc08dd23ef6f9c70f9c605bcdf3e 100644 (file)
@@ -49,7 +49,7 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
        buf->private_data = NULL;
        buf->area = dma_alloc_coherent(pcm->card->dev, size,
                        &buf->addr, GFP_KERNEL);
-       pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d\n",
+       pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n",
                        (void *)buf->area, (void *)buf->addr, size);
 
        if (!buf->area)
index bb45d20e7250aeace79a2595f5f9676349ba05d9..12ae814eff214fc2977fe9c0bdde17e5025e2cdb 100644 (file)
@@ -88,7 +88,8 @@ 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
+#if defined(CONFIG_SND_ATMEL_SOC_PDC) || \
+       defined(CONFIG_SND_ATMEL_SOC_PDC_MODULE)
 int atmel_pcm_pdc_platform_register(struct device *dev);
 void atmel_pcm_pdc_platform_unregister(struct device *dev);
 #else
@@ -101,7 +102,8 @@ static inline void atmel_pcm_pdc_platform_unregister(struct device *dev)
 }
 #endif
 
-#ifdef CONFIG_SND_ATMEL_SOC_DMA
+#if defined(CONFIG_SND_ATMEL_SOC_DMA) || \
+       defined(CONFIG_SND_ATMEL_SOC_DMA_MODULE)
 int atmel_pcm_dma_platform_register(struct device *dev);
 void atmel_pcm_dma_platform_unregister(struct device *dev);
 #else
index 1c7663422054049265957743bad8c6f8678e9e17..e13580d6c4761a9e57d7b187409ba2429ca20722 100644 (file)
@@ -42,8 +42,6 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 
-#include <mach/hardware.h>
-
 #include "atmel-pcm.h"
 #include "atmel_ssc_dai.h"
 
@@ -679,15 +677,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
 #  define atmel_ssc_resume     NULL
 #endif /* CONFIG_PM */
 
-static int atmel_ssc_probe(struct snd_soc_dai *dai)
-{
-       struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
-
-       snd_soc_dai_set_drvdata(dai, ssc_p);
-
-       return 0;
-}
-
 #define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
 
 #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
@@ -703,7 +692,6 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
 };
 
 static struct snd_soc_dai_driver atmel_ssc_dai = {
-               .probe = atmel_ssc_probe,
                .suspend = atmel_ssc_suspend,
                .resume = atmel_ssc_resume,
                .playback = {
@@ -790,8 +778,8 @@ 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);
+       ssc_free(ssc);
 }
 EXPORT_SYMBOL_GPL(atmel_ssc_put_audio);
 
index da976291da9e28c46c55f45ff9ba70ea52544181..2d6fbd0125b9a6b6411a256c6d3e950d6be34878 100644 (file)
@@ -305,10 +305,10 @@ static int at91sam9g20ek_audio_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
 
-       atmel_ssc_put_audio(0);
-       snd_soc_unregister_card(card);
-       clk_put(mclk);
+       clk_disable(mclk);
        mclk = NULL;
+       snd_soc_unregister_card(card);
+       atmel_ssc_put_audio(0);
 
        return 0;
 }
index 1f0cdab03294725b4ba02f6a28e4b6ce68eb6e89..2d037870970221b2c2a87f4f96cac4886d606cb9 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
+#include <linux/of_device.h>
 #include <linux/module.h>
 #include <sound/soc.h>
 #include <sound/initval.h>
@@ -513,12 +514,31 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = {
 };
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static struct of_device_id ak4642_of_match[];
 static int ak4642_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
+       struct device_node *np = i2c->dev.of_node;
+       const struct snd_soc_codec_driver *driver;
+
+       driver = NULL;
+       if (np) {
+               const struct of_device_id *of_id;
+
+               of_id = of_match_device(ak4642_of_match, &i2c->dev);
+               if (of_id)
+                       driver = of_id->data;
+       } else {
+               driver = (struct snd_soc_codec_driver *)id->driver_data;
+       }
+
+       if (!driver) {
+               dev_err(&i2c->dev, "no driver\n");
+               return -EINVAL;
+       }
+
        return snd_soc_register_codec(&i2c->dev,
-                               (struct snd_soc_codec_driver *)id->driver_data,
-                               &ak4642_dai, 1);
+                                     driver, &ak4642_dai, 1);
 }
 
 static int ak4642_i2c_remove(struct i2c_client *client)
@@ -527,6 +547,14 @@ static int ak4642_i2c_remove(struct i2c_client *client)
        return 0;
 }
 
+static struct of_device_id ak4642_of_match[] = {
+       { .compatible = "asahi-kasei,ak4642",   .data = &soc_codec_dev_ak4642},
+       { .compatible = "asahi-kasei,ak4643",   .data = &soc_codec_dev_ak4642},
+       { .compatible = "asahi-kasei,ak4648",   .data = &soc_codec_dev_ak4648},
+       {},
+};
+MODULE_DEVICE_TABLE(of, ak4642_of_match);
+
 static const struct i2c_device_id ak4642_i2c_id[] = {
        { "ak4642", (kernel_ulong_t)&soc_codec_dev_ak4642 },
        { "ak4643", (kernel_ulong_t)&soc_codec_dev_ak4642 },
@@ -539,6 +567,7 @@ static struct i2c_driver ak4642_i2c_driver = {
        .driver = {
                .name = "ak4642-codec",
                .owner = THIS_MODULE,
+               .of_match_table = ak4642_of_match,
        },
        .probe          = ak4642_i2c_probe,
        .remove         = ak4642_i2c_remove,
index ef62c435848eb776421776d7e80e90527e3f2b72..ac948a671ea677898b280a3113eb4b3aeb6ef8ed 100644 (file)
 #define arizona_fll_warn(_fll, fmt, ...) \
        dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
 #define arizona_fll_dbg(_fll, fmt, ...) \
-       dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+       dev_dbg(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
 
 #define arizona_aif_err(_dai, fmt, ...) \
        dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
 #define arizona_aif_warn(_dai, fmt, ...) \
        dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
 #define arizona_aif_dbg(_dai, fmt, ...) \
-       dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+       dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
 
 const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
        "None",
@@ -141,6 +141,30 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
        "ASRC1R",
        "ASRC2L",
        "ASRC2R",
+       "ISRC1INT1",
+       "ISRC1INT2",
+       "ISRC1INT3",
+       "ISRC1INT4",
+       "ISRC1DEC1",
+       "ISRC1DEC2",
+       "ISRC1DEC3",
+       "ISRC1DEC4",
+       "ISRC2INT1",
+       "ISRC2INT2",
+       "ISRC2INT3",
+       "ISRC2INT4",
+       "ISRC2DEC1",
+       "ISRC2DEC2",
+       "ISRC2DEC3",
+       "ISRC2DEC4",
+       "ISRC3INT1",
+       "ISRC3INT2",
+       "ISRC3INT3",
+       "ISRC3INT4",
+       "ISRC3DEC1",
+       "ISRC3DEC2",
+       "ISRC3DEC3",
+       "ISRC3DEC4",
 };
 EXPORT_SYMBOL_GPL(arizona_mixer_texts);
 
@@ -220,6 +244,30 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
        0x91,
        0x92,
        0x93,
+       0xa0,  /* ISRC1INT1 */
+       0xa1,
+       0xa2,
+       0xa3,
+       0xa4,  /* ISRC1DEC1 */
+       0xa5,
+       0xa6,
+       0xa7,
+       0xa8,  /* ISRC2DEC1 */
+       0xa9,
+       0xaa,
+       0xab,
+       0xac,  /* ISRC2INT1 */
+       0xad,
+       0xae,
+       0xaf,
+       0xb0,  /* ISRC3DEC1 */
+       0xb1,
+       0xb2,
+       0xb3,
+       0xb4,  /* ISRC3INT1 */
+       0xb5,
+       0xb6,
+       0xb7,
 };
 EXPORT_SYMBOL_GPL(arizona_mixer_values);
 
@@ -275,9 +323,35 @@ const struct soc_enum arizona_lhpf4_mode =
                        arizona_lhpf_mode_text);
 EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
 
+static const char *arizona_ng_hold_text[] = {
+       "30ms", "120ms", "250ms", "500ms",
+};
+
+const struct soc_enum arizona_ng_hold =
+       SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT,
+                       4, arizona_ng_hold_text);
+EXPORT_SYMBOL_GPL(arizona_ng_hold);
+
 int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
                  int event)
 {
+       unsigned int reg;
+
+       if (w->shift % 2)
+               reg = ARIZONA_ADC_DIGITAL_VOLUME_1L + ((w->shift / 2) * 8);
+       else
+               reg = ARIZONA_ADC_DIGITAL_VOLUME_1R + ((w->shift / 2) * 8);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(w->codec, reg, ARIZONA_IN1L_MUTE, 0);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(w->codec, reg, ARIZONA_IN1L_MUTE,
+                                   ARIZONA_IN1L_MUTE);
+               break;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(arizona_in_ev);
@@ -417,6 +491,10 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
        case 147456000:
                val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT;
                break;
+       case 0:
+               dev_dbg(arizona->dev, "%s cleared\n", name);
+               *clk = freq;
+               return 0;
        default:
                return -EINVAL;
        }
@@ -635,6 +713,9 @@ static int arizona_startup(struct snd_pcm_substream *substream,
                return 0;
        }
 
+       if (base_rate == 0)
+               return 0;
+
        if (base_rate % 8000)
                constraint = &arizona_44k1_constraint;
        else
@@ -645,25 +726,81 @@ static int arizona_startup(struct snd_pcm_substream *substream,
                                          constraint);
 }
 
+static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *params,
+                                 struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+       int base = dai->driver->base;
+       int i, sr_val;
+
+       /*
+        * We will need to be more flexible than this in future,
+        * currently we use a single sample rate for SYSCLK.
+        */
+       for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++)
+               if (arizona_sr_vals[i] == params_rate(params))
+                       break;
+       if (i == ARRAY_SIZE(arizona_sr_vals)) {
+               arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
+                               params_rate(params));
+               return -EINVAL;
+       }
+       sr_val = i;
+
+       switch (dai_priv->clk) {
+       case ARIZONA_CLK_SYSCLK:
+               snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
+                                   ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
+               if (base)
+                       snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
+                                           ARIZONA_AIF1_RATE_MASK, 0);
+               break;
+       case ARIZONA_CLK_ASYNCCLK:
+               snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
+                                   ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val);
+               if (base)
+                       snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
+                                           ARIZONA_AIF1_RATE_MASK,
+                                           8 << ARIZONA_AIF1_RATE_SHIFT);
+               break;
+       default:
+               arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int arizona_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *params,
                             struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
        struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
-       struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+       struct arizona *arizona = priv->arizona;
        int base = dai->driver->base;
        const int *rates;
-       int i;
-       int bclk, lrclk, wl, frame, sr_val;
+       int i, ret;
+       int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1];
+       int bclk, lrclk, wl, frame, bclk_target;
 
        if (params_rate(params) % 8000)
                rates = &arizona_44k1_bclk_rates[0];
        else
                rates = &arizona_48k_bclk_rates[0];
 
+       bclk_target = snd_soc_params_to_bclk(params);
+       if (chan_limit && chan_limit < params_channels(params)) {
+               arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit);
+               bclk_target /= params_channels(params);
+               bclk_target *= chan_limit;
+       }
+
        for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) {
-               if (rates[i] >= snd_soc_params_to_bclk(params) &&
+               if (rates[i] >= bclk_target &&
                    rates[i] % params_rate(params) == 0) {
                        bclk = i;
                        break;
@@ -675,16 +812,6 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++)
-               if (arizona_sr_vals[i] == params_rate(params))
-                       break;
-       if (i == ARRAY_SIZE(arizona_sr_vals)) {
-               arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
-                               params_rate(params));
-               return -EINVAL;
-       }
-       sr_val = i;
-
        lrclk = rates[bclk] / params_rate(params);
 
        arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n",
@@ -693,28 +820,9 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
        wl = snd_pcm_format_width(params_format(params));
        frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl;
 
-       /*
-        * We will need to be more flexible than this in future,
-        * currently we use a single sample rate for SYSCLK.
-        */
-       switch (dai_priv->clk) {
-       case ARIZONA_CLK_SYSCLK:
-               snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
-                                   ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
-               snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
-                                   ARIZONA_AIF1_RATE_MASK, 0);
-               break;
-       case ARIZONA_CLK_ASYNCCLK:
-               snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
-                                   ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val);
-               snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
-                                   ARIZONA_AIF1_RATE_MASK,
-                                   8 << ARIZONA_AIF1_RATE_SHIFT);
-               break;
-       default:
-               arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
-               return -EINVAL;
-       }
+       ret = arizona_hw_params_rate(substream, params, dai);
+       if (ret != 0)
+               return ret;
 
        snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL,
                            ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
@@ -789,11 +897,27 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
        return snd_soc_dapm_sync(&codec->dapm);
 }
 
+static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       int base = dai->driver->base;
+       unsigned int reg;
+
+       if (tristate)
+               reg = ARIZONA_AIF1_TRI;
+       else
+               reg = 0;
+
+       return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
+                                  ARIZONA_AIF1_TRI, reg);
+}
+
 const struct snd_soc_dai_ops arizona_dai_ops = {
        .startup = arizona_startup,
        .set_fmt = arizona_set_fmt,
        .hw_params = arizona_hw_params,
        .set_sysclk = arizona_dai_set_sysclk,
+       .set_tristate = arizona_set_tristate,
 };
 EXPORT_SYMBOL_GPL(arizona_dai_ops);
 
@@ -807,17 +931,6 @@ int arizona_init_dai(struct arizona_priv *priv, int id)
 }
 EXPORT_SYMBOL_GPL(arizona_init_dai);
 
-static irqreturn_t arizona_fll_lock(int irq, void *data)
-{
-       struct arizona_fll *fll = data;
-
-       arizona_fll_dbg(fll, "Lock status changed\n");
-
-       complete(&fll->lock);
-
-       return IRQ_HANDLED;
-}
-
 static irqreturn_t arizona_fll_clock_ok(int irq, void *data)
 {
        struct arizona_fll *fll = data;
@@ -910,7 +1023,7 @@ static int arizona_calc_fll(struct arizona_fll *fll,
 
        cfg->n = target / (ratio * Fref);
 
-       if (target % Fref) {
+       if (target % (ratio * Fref)) {
                gcd_fll = gcd(target, ratio * Fref);
                arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll);
 
@@ -922,6 +1035,15 @@ static int arizona_calc_fll(struct arizona_fll *fll,
                cfg->lambda = 0;
        }
 
+       /* Round down to 16bit range with cost of accuracy lost.
+        * Denominator must be bigger than numerator so we only
+        * take care of it.
+        */
+       while (cfg->lambda >= (1 << 16)) {
+               cfg->theta >>= 1;
+               cfg->lambda >>= 1;
+       }
+
        arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
                        cfg->n, cfg->theta, cfg->lambda);
        arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
@@ -1057,7 +1179,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
 {
        int ret;
 
-       init_completion(&fll->lock);
        init_completion(&fll->ok);
 
        fll->id = id;
@@ -1068,13 +1189,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
        snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name),
                 "FLL%d clock OK", id);
 
-       ret = arizona_request_irq(arizona, lock_irq, fll->lock_name,
-                                 arizona_fll_lock, fll);
-       if (ret != 0) {
-               dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n",
-                       id, ret);
-       }
-
        ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name,
                                  arizona_fll_clock_ok, fll);
        if (ret != 0) {
@@ -1089,6 +1203,40 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
 }
 EXPORT_SYMBOL_GPL(arizona_init_fll);
 
+/**
+ * arizona_set_output_mode - Set the mode of the specified output
+ *
+ * @codec: Device to configure
+ * @output: Output number
+ * @diff: True to set the output to differential mode
+ *
+ * Some systems use external analogue switches to connect more
+ * analogue devices to the CODEC than are supported by the device.  In
+ * some systems this requires changing the switched output from single
+ * ended to differential mode dynamically at runtime, an operation
+ * supported using this function.
+ *
+ * Most systems have a single static configuration and should use
+ * platform data instead.
+ */
+int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
+{
+       unsigned int reg, val;
+
+       if (output < 1 || output > 6)
+               return -EINVAL;
+
+       reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8;
+
+       if (diff)
+               val = ARIZONA_OUT1_MONO;
+       else
+               val = 0;
+
+       return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val);
+}
+EXPORT_SYMBOL_GPL(arizona_set_output_mode);
+
 MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");
index 4deebeb0717754085edceb38e7c00b141f4502fb..116372c91f5de3f60eafd9a738cd4a31e1ab0bcb 100644 (file)
@@ -66,7 +66,7 @@ struct arizona_priv {
        struct arizona_dai_priv dai[ARIZONA_MAX_DAI];
 };
 
-#define ARIZONA_NUM_MIXER_INPUTS 75
+#define ARIZONA_NUM_MIXER_INPUTS 99
 
 extern const unsigned int arizona_mixer_tlv[];
 extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
@@ -176,6 +176,8 @@ extern const struct soc_enum arizona_lhpf2_mode;
 extern const struct soc_enum arizona_lhpf3_mode;
 extern const struct soc_enum arizona_lhpf4_mode;
 
+extern const struct soc_enum arizona_ng_hold;
+
 extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
                         struct snd_kcontrol *kcontrol,
                         int event);
@@ -195,7 +197,6 @@ struct arizona_fll {
        int id;
        unsigned int base;
        unsigned int vco_mult;
-       struct completion lock;
        struct completion ok;
        unsigned int fref;
        unsigned int fout;
@@ -211,4 +212,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source,
 
 extern int arizona_init_dai(struct arizona_priv *priv, int dai);
 
+int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
+                           bool diff);
+
 #endif
index ac8742a1f25ab7c69fcca2b22458ae02acf3d67e..2415a4118dbd84ad8dee6a74692edff2be6dc743 100644 (file)
@@ -167,6 +167,8 @@ struct cs4271_private {
        int                             gpio_nreset;
        /* GPIO that disable serial bus, if any */
        int                             gpio_disable;
+       /* enable soft reset workaround */
+       bool                            enable_soft_reset;
 };
 
 /*
@@ -325,6 +327,33 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
        int i, ret;
        unsigned int ratio, val;
 
+       if (cs4271->enable_soft_reset) {
+               /*
+                * Put the codec in soft reset and back again in case it's not
+                * currently streaming data. This way of bringing the codec in
+                * sync to the current clocks is not explicitly documented in
+                * the data sheet, but it seems to work fine, and in contrast
+                * to a read hardware reset, we don't have to sync back all
+                * registers every time.
+                */
+
+               if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+                    !dai->capture_active) ||
+                   (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+                    !dai->playback_active)) {
+                       ret = snd_soc_update_bits(codec, CS4271_MODE2,
+                                                 CS4271_MODE2_PDN,
+                                                 CS4271_MODE2_PDN);
+                       if (ret < 0)
+                               return ret;
+
+                       ret = snd_soc_update_bits(codec, CS4271_MODE2,
+                                                 CS4271_MODE2_PDN, 0);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
        cs4271->rate = params_rate(params);
 
        /* Configure DAC */
@@ -484,6 +513,10 @@ static int cs4271_probe(struct snd_soc_codec *codec)
                if (of_get_property(codec->dev->of_node,
                                     "cirrus,amutec-eq-bmutec", NULL))
                        amutec_eq_bmutec = true;
+
+               if (of_get_property(codec->dev->of_node,
+                                    "cirrus,enable-soft-reset", NULL))
+                       cs4271->enable_soft_reset = true;
        }
 #endif
 
@@ -492,6 +525,7 @@ static int cs4271_probe(struct snd_soc_codec *codec)
                        gpio_nreset = cs4271plat->gpio_nreset;
 
                amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
+               cs4271->enable_soft_reset = cs4271plat->enable_soft_reset;
        }
 
        if (gpio_nreset >= 0)
index 9811a5478c87649da6c42806a3a43bfcc5a5767e..0f6f481cec09cd941688ed790ecd31e10e207496 100644 (file)
@@ -1038,7 +1038,7 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec)
        struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
        int ret;
 
-       cs42l52->beep = input_allocate_device();
+       cs42l52->beep = devm_input_allocate_device(codec->dev);
        if (!cs42l52->beep) {
                dev_err(codec->dev, "Failed to allocate beep device\n");
                return;
@@ -1059,7 +1059,6 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec)
 
        ret = input_register_device(cs42l52->beep);
        if (ret != 0) {
-               input_free_device(cs42l52->beep);
                cs42l52->beep = NULL;
                dev_err(codec->dev, "Failed to register beep device\n");
        }
@@ -1076,7 +1075,6 @@ static void cs42l52_free_beep(struct snd_soc_codec *codec)
        struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
 
        device_remove_file(codec->dev, &dev_attr_beep);
-       input_unregister_device(cs42l52->beep);
        cancel_work_sync(&cs42l52->beep_work);
        cs42l52->beep = NULL;
 
old mode 100644 (file)
new mode 100755 (executable)
index c9772ca..fc17604
 /*
  * max98090.c -- MAX98090 ALSA SoC Audio driver
- * based on Rev0p8 datasheet
  *
- * Copyright (C) 2012 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
- *
- * Based on
- *
- * max98095.c
- * Copyright 2011 Maxim Integrated Products
- *
- * https://github.com/hardkernel/linux/commit/\
- *     3417d7166b17113b3b33b0a337c74d1c7cc313df#sound/soc/codecs/max98090.c
- * Copyright 2011 Maxim Integrated Products
+ * Copyright 2011-2012 Maxim Integrated Products
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
 
+#include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/tlv.h>
+#include <sound/max98090.h>
+#include "max98090.h"
+
+#include <linux/version.h>
+
+#define DEBUG
+#define EXTMIC_METHOD
+#define EXTMIC_METHOD_TEST
+
+/* Allows for sparsely populated register maps */
+static struct reg_default max98090_reg[] = {
+       { 0x00, 0x00 }, /* 00 Software Reset */
+       { 0x03, 0x04 }, /* 03 Interrupt Masks */
+       { 0x04, 0x00 }, /* 04 System Clock Quick */
+       { 0x05, 0x00 }, /* 05 Sample Rate Quick */
+       { 0x06, 0x00 }, /* 06 DAI Interface Quick */
+       { 0x07, 0x00 }, /* 07 DAC Path Quick */
+       { 0x08, 0x00 }, /* 08 Mic/Direct to ADC Quick */
+       { 0x09, 0x00 }, /* 09 Line to ADC Quick */
+       { 0x0A, 0x00 }, /* 0A Analog Mic Loop Quick */
+       { 0x0B, 0x00 }, /* 0B Analog Line Loop Quick */
+       { 0x0C, 0x00 }, /* 0C Reserved */
+       { 0x0D, 0x00 }, /* 0D Input Config */
+       { 0x0E, 0x1B }, /* 0E Line Input Level */
+       { 0x0F, 0x00 }, /* 0F Line Config */
+
+       { 0x10, 0x14 }, /* 10 Mic1 Input Level */
+       { 0x11, 0x14 }, /* 11 Mic2 Input Level */
+       { 0x12, 0x00 }, /* 12 Mic Bias Voltage */
+       { 0x13, 0x00 }, /* 13 Digital Mic Config */
+       { 0x14, 0x00 }, /* 14 Digital Mic Mode */
+       { 0x15, 0x00 }, /* 15 Left ADC Mixer */
+       { 0x16, 0x00 }, /* 16 Right ADC Mixer */
+       { 0x17, 0x03 }, /* 17 Left ADC Level */
+       { 0x18, 0x03 }, /* 18 Right ADC Level */
+       { 0x19, 0x00 }, /* 19 ADC Biquad Level */
+       { 0x1A, 0x00 }, /* 1A ADC Sidetone */
+       { 0x1B, 0x00 }, /* 1B System Clock */
+       { 0x1C, 0x00 }, /* 1C Clock Mode */
+       { 0x1D, 0x00 }, /* 1D Any Clock 1 */
+       { 0x1E, 0x00 }, /* 1E Any Clock 2 */
+       { 0x1F, 0x00 }, /* 1F Any Clock 3 */
+
+       { 0x20, 0x00 }, /* 20 Any Clock 4 */
+       { 0x21, 0x00 }, /* 21 Master Mode */
+       { 0x22, 0x00 }, /* 22 Interface Format */
+       { 0x23, 0x00 }, /* 23 TDM Format 1*/
+       { 0x24, 0x00 }, /* 24 TDM Format 2*/
+       { 0x25, 0x00 }, /* 25 I/O Configuration */
+       { 0x26, 0x80 }, /* 26 Filter Config */
+       { 0x27, 0x00 }, /* 27 DAI Playback Level */
+       { 0x28, 0x00 }, /* 28 EQ Playback Level */
+       { 0x29, 0x00 }, /* 29 Left HP Mixer */
+       { 0x2A, 0x00 }, /* 2A Right HP Mixer */
+       { 0x2B, 0x00 }, /* 2B HP Control */
+       { 0x2C, 0x1A }, /* 2C Left HP Volume */
+       { 0x2D, 0x1A }, /* 2D Right HP Volume */
+       { 0x2E, 0x00 }, /* 2E Left Spk Mixer */
+       { 0x2F, 0x00 }, /* 2F Right Spk Mixer */
+
+       { 0x30, 0x00 }, /* 30 Spk Control */
+       { 0x31, 0x2C }, /* 31 Left Spk Volume */
+       { 0x32, 0x2C }, /* 32 Right Spk Volume */
+       { 0x33, 0x00 }, /* 33 ALC Timing */
+       { 0x34, 0x00 }, /* 34 ALC Compressor */
+       { 0x35, 0x00 }, /* 35 ALC Expander */
+       { 0x36, 0x00 }, /* 36 ALC Gain */
+       { 0x37, 0x00 }, /* 37 Rcv/Line OutL Mixer */
+       { 0x38, 0x00 }, /* 38 Rcv/Line OutL Control */
+       { 0x39, 0x15 }, /* 39 Rcv/Line OutL Volume */
+       { 0x3A, 0x00 }, /* 3A Line OutR Mixer */
+       { 0x3B, 0x00 }, /* 3B Line OutR Control */
+       { 0x3C, 0x15 }, /* 3C Line OutR Volume */
+       { 0x3D, 0x00 }, /* 3D Jack Detect */
+       { 0x3E, 0x00 }, /* 3E Input Enable */
+       { 0x3F, 0x00 }, /* 3F Output Enable */
+
+       { 0x40, 0x00 }, /* 40 Level Control */
+       { 0x41, 0x00 }, /* 41 DSP Filter Enable */
+       { 0x42, 0x00 }, /* 42 Bias Control */
+       { 0x43, 0x00 }, /* 43 DAC Control */
+       { 0x44, 0x06 }, /* 44 ADC Control */
+       { 0x45, 0x00 }, /* 45 Device Shutdown */
+       { 0x46, 0x00 }, /* 46 Equalizer Band 1 Coefficient B0 */
+       { 0x47, 0x00 }, /* 47 Equalizer Band 1 Coefficient B0 */
+       { 0x48, 0x00 }, /* 48 Equalizer Band 1 Coefficient B0 */
+       { 0x49, 0x00 }, /* 49 Equalizer Band 1 Coefficient B1 */
+       { 0x4A, 0x00 }, /* 4A Equalizer Band 1 Coefficient B1 */
+       { 0x4B, 0x00 }, /* 4B Equalizer Band 1 Coefficient B1 */
+       { 0x4C, 0x00 }, /* 4C Equalizer Band 1 Coefficient B2 */
+       { 0x4D, 0x00 }, /* 4D Equalizer Band 1 Coefficient B2 */
+       { 0x4E, 0x00 }, /* 4E Equalizer Band 1 Coefficient B2 */
+       { 0x4F, 0x00 }, /* 4F Equalizer Band 1 Coefficient A1 */
+
+       { 0x50, 0x00 }, /* 50 Equalizer Band 1 Coefficient A1 */
+       { 0x51, 0x00 }, /* 51 Equalizer Band 1 Coefficient A1 */
+       { 0x52, 0x00 }, /* 52 Equalizer Band 1 Coefficient A2 */
+       { 0x53, 0x00 }, /* 53 Equalizer Band 1 Coefficient A2 */
+       { 0x54, 0x00 }, /* 54 Equalizer Band 1 Coefficient A2 */
+       { 0x55, 0x00 }, /* 55 Equalizer Band 2 Coefficient B0 */
+       { 0x56, 0x00 }, /* 56 Equalizer Band 2 Coefficient B0 */
+       { 0x57, 0x00 }, /* 57 Equalizer Band 2 Coefficient B0 */
+       { 0x58, 0x00 }, /* 58 Equalizer Band 2 Coefficient B1 */
+       { 0x59, 0x00 }, /* 59 Equalizer Band 2 Coefficient B1 */
+       { 0x5A, 0x00 }, /* 5A Equalizer Band 2 Coefficient B1 */
+       { 0x5B, 0x00 }, /* 5B Equalizer Band 2 Coefficient B2 */
+       { 0x5C, 0x00 }, /* 5C Equalizer Band 2 Coefficient B2 */
+       { 0x5D, 0x00 }, /* 5D Equalizer Band 2 Coefficient B2 */
+       { 0x5E, 0x00 }, /* 5E Equalizer Band 2 Coefficient A1 */
+       { 0x5F, 0x00 }, /* 5F Equalizer Band 2 Coefficient A1 */
+
+       { 0x60, 0x00 }, /* 60 Equalizer Band 2 Coefficient A1 */
+       { 0x61, 0x00 }, /* 61 Equalizer Band 2 Coefficient A2 */
+       { 0x62, 0x00 }, /* 62 Equalizer Band 2 Coefficient A2 */
+       { 0x63, 0x00 }, /* 63 Equalizer Band 2 Coefficient A2 */
+       { 0x64, 0x00 }, /* 64 Equalizer Band 3 Coefficient B0 */
+       { 0x65, 0x00 }, /* 65 Equalizer Band 3 Coefficient B0 */
+       { 0x66, 0x00 }, /* 66 Equalizer Band 3 Coefficient B0 */
+       { 0x67, 0x00 }, /* 67 Equalizer Band 3 Coefficient B1 */
+       { 0x68, 0x00 }, /* 68 Equalizer Band 3 Coefficient B1 */
+       { 0x69, 0x00 }, /* 69 Equalizer Band 3 Coefficient B1 */
+       { 0x6A, 0x00 }, /* 6A Equalizer Band 3 Coefficient B2 */
+       { 0x6B, 0x00 }, /* 6B Equalizer Band 3 Coefficient B2 */
+       { 0x6C, 0x00 }, /* 6C Equalizer Band 3 Coefficient B2 */
+       { 0x6D, 0x00 }, /* 6D Equalizer Band 3 Coefficient A1 */
+       { 0x6E, 0x00 }, /* 6E Equalizer Band 3 Coefficient A1 */
+       { 0x6F, 0x00 }, /* 6F Equalizer Band 3 Coefficient A1 */
+
+       { 0x70, 0x00 }, /* 70 Equalizer Band 3 Coefficient A2 */
+       { 0x71, 0x00 }, /* 71 Equalizer Band 3 Coefficient A2 */
+       { 0x72, 0x00 }, /* 72 Equalizer Band 3 Coefficient A2 */
+       { 0x73, 0x00 }, /* 73 Equalizer Band 4 Coefficient B0 */
+       { 0x74, 0x00 }, /* 74 Equalizer Band 4 Coefficient B0 */
+       { 0x75, 0x00 }, /* 75 Equalizer Band 4 Coefficient B0 */
+       { 0x76, 0x00 }, /* 76 Equalizer Band 4 Coefficient B1 */
+       { 0x77, 0x00 }, /* 77 Equalizer Band 4 Coefficient B1 */
+       { 0x78, 0x00 }, /* 78 Equalizer Band 4 Coefficient B1 */
+       { 0x79, 0x00 }, /* 79 Equalizer Band 4 Coefficient B2 */
+       { 0x7A, 0x00 }, /* 7A Equalizer Band 4 Coefficient B2 */
+       { 0x7B, 0x00 }, /* 7B Equalizer Band 4 Coefficient B2 */
+       { 0x7C, 0x00 }, /* 7C Equalizer Band 4 Coefficient A1 */
+       { 0x7D, 0x00 }, /* 7D Equalizer Band 4 Coefficient A1 */
+       { 0x7E, 0x00 }, /* 7E Equalizer Band 4 Coefficient A1 */
+       { 0x7F, 0x00 }, /* 7F Equalizer Band 4 Coefficient A2 */
+
+       { 0x80, 0x00 }, /* 80 Equalizer Band 4 Coefficient A2 */
+       { 0x81, 0x00 }, /* 81 Equalizer Band 4 Coefficient A2 */
+       { 0x82, 0x00 }, /* 82 Equalizer Band 5 Coefficient B0 */
+       { 0x83, 0x00 }, /* 83 Equalizer Band 5 Coefficient B0 */
+       { 0x84, 0x00 }, /* 84 Equalizer Band 5 Coefficient B0 */
+       { 0x85, 0x00 }, /* 85 Equalizer Band 5 Coefficient B1 */
+       { 0x86, 0x00 }, /* 86 Equalizer Band 5 Coefficient B1 */
+       { 0x87, 0x00 }, /* 87 Equalizer Band 5 Coefficient B1 */
+       { 0x88, 0x00 }, /* 88 Equalizer Band 5 Coefficient B2 */
+       { 0x89, 0x00 }, /* 89 Equalizer Band 5 Coefficient B2 */
+       { 0x8A, 0x00 }, /* 8A Equalizer Band 5 Coefficient B2 */
+       { 0x8B, 0x00 }, /* 8B Equalizer Band 5 Coefficient A1 */
+       { 0x8C, 0x00 }, /* 8C Equalizer Band 5 Coefficient A1 */
+       { 0x8D, 0x00 }, /* 8D Equalizer Band 5 Coefficient A1 */
+       { 0x8E, 0x00 }, /* 8E Equalizer Band 5 Coefficient A2 */
+       { 0x8F, 0x00 }, /* 8F Equalizer Band 5 Coefficient A2 */
+
+       { 0x90, 0x00 }, /* 90 Equalizer Band 5 Coefficient A2 */
+       { 0x91, 0x00 }, /* 91 Equalizer Band 6 Coefficient B0 */
+       { 0x92, 0x00 }, /* 92 Equalizer Band 6 Coefficient B0 */
+       { 0x93, 0x00 }, /* 93 Equalizer Band 6 Coefficient B0 */
+       { 0x94, 0x00 }, /* 94 Equalizer Band 6 Coefficient B1 */
+       { 0x95, 0x00 }, /* 95 Equalizer Band 6 Coefficient B1 */
+       { 0x96, 0x00 }, /* 96 Equalizer Band 6 Coefficient B1 */
+       { 0x97, 0x00 }, /* 97 Equalizer Band 6 Coefficient B2 */
+       { 0x98, 0x00 }, /* 98 Equalizer Band 6 Coefficient B2 */
+       { 0x99, 0x00 }, /* 99 Equalizer Band 6 Coefficient B2 */
+       { 0x9A, 0x00 }, /* 9A Equalizer Band 6 Coefficient A1 */
+       { 0x9B, 0x00 }, /* 9B Equalizer Band 6 Coefficient A1 */
+       { 0x9C, 0x00 }, /* 9C Equalizer Band 6 Coefficient A1 */
+       { 0x9D, 0x00 }, /* 9D Equalizer Band 6 Coefficient A2 */
+       { 0x9E, 0x00 }, /* 9E Equalizer Band 6 Coefficient A2 */
+       { 0x9F, 0x00 }, /* 9F Equalizer Band 6 Coefficient A2 */
+
+       { 0xA0, 0x00 }, /* A0 Equalizer Band 7 Coefficient B0 */
+       { 0xA1, 0x00 }, /* A1 Equalizer Band 7 Coefficient B0 */
+       { 0xA2, 0x00 }, /* A2 Equalizer Band 7 Coefficient B0 */
+       { 0xA3, 0x00 }, /* A3 Equalizer Band 7 Coefficient B1 */
+       { 0xA4, 0x00 }, /* A4 Equalizer Band 7 Coefficient B1 */
+       { 0xA5, 0x00 }, /* A5 Equalizer Band 7 Coefficient B1 */
+       { 0xA6, 0x00 }, /* A6 Equalizer Band 7 Coefficient B2 */
+       { 0xA7, 0x00 }, /* A7 Equalizer Band 7 Coefficient B2 */
+       { 0xA8, 0x00 }, /* A8 Equalizer Band 7 Coefficient B2 */
+       { 0xA9, 0x00 }, /* A9 Equalizer Band 7 Coefficient A1 */
+       { 0xAA, 0x00 }, /* AA Equalizer Band 7 Coefficient A1 */
+       { 0xAB, 0x00 }, /* AB Equalizer Band 7 Coefficient A1 */
+       { 0xAC, 0x00 }, /* AC Equalizer Band 7 Coefficient A2 */
+       { 0xAD, 0x00 }, /* AD Equalizer Band 7 Coefficient A2 */
+       { 0xAE, 0x00 }, /* AE Equalizer Band 7 Coefficient A2 */
+       { 0xAF, 0x00 }, /* AF ADC Biquad Coefficient B0 */
+
+       { 0xB0, 0x00 }, /* B0 ADC Biquad Coefficient B0 */
+       { 0xB1, 0x00 }, /* B1 ADC Biquad Coefficient B0 */
+       { 0xB2, 0x00 }, /* B2 ADC Biquad Coefficient B1 */
+       { 0xB3, 0x00 }, /* B3 ADC Biquad Coefficient B1 */
+       { 0xB4, 0x00 }, /* B4 ADC Biquad Coefficient B1 */
+       { 0xB5, 0x00 }, /* B5 ADC Biquad Coefficient B2 */
+       { 0xB6, 0x00 }, /* B6 ADC Biquad Coefficient B2 */
+       { 0xB7, 0x00 }, /* B7 ADC Biquad Coefficient B2 */
+       { 0xB8, 0x00 }, /* B8 ADC Biquad Coefficient A1 */
+       { 0xB9, 0x00 }, /* B9 ADC Biquad Coefficient A1 */
+       { 0xBA, 0x00 }, /* BA ADC Biquad Coefficient A1 */
+       { 0xBB, 0x00 }, /* BB ADC Biquad Coefficient A2 */
+       { 0xBC, 0x00 }, /* BC ADC Biquad Coefficient A2 */
+       { 0xBD, 0x00 }, /* BD ADC Biquad Coefficient A2 */
+       { 0xBE, 0x00 }, /* BE Digital Mic 3 Volume */
+       { 0xBF, 0x00 }, /* BF Digital Mic 4 Volume */
+
+       { 0xC0, 0x00 }, /* C0 Digital Mic 34 Biquad Pre Atten */
+       { 0xC1, 0x00 }, /* C1 Record TDM Slot */
+       { 0xC2, 0x00 }, /* C2 Sample Rate */
+       { 0xC3, 0x00 }, /* C3 Digital Mic 34 Biquad Coefficient C3 */
+       { 0xC4, 0x00 }, /* C4 Digital Mic 34 Biquad Coefficient C4 */
+       { 0xC5, 0x00 }, /* C5 Digital Mic 34 Biquad Coefficient C5 */
+       { 0xC6, 0x00 }, /* C6 Digital Mic 34 Biquad Coefficient C6 */
+       { 0xC7, 0x00 }, /* C7 Digital Mic 34 Biquad Coefficient C7 */
+       { 0xC8, 0x00 }, /* C8 Digital Mic 34 Biquad Coefficient C8 */
+       { 0xC9, 0x00 }, /* C9 Digital Mic 34 Biquad Coefficient C9 */
+       { 0xCA, 0x00 }, /* CA Digital Mic 34 Biquad Coefficient CA */
+       { 0xCB, 0x00 }, /* CB Digital Mic 34 Biquad Coefficient CB */
+       { 0xCC, 0x00 }, /* CC Digital Mic 34 Biquad Coefficient CC */
+       { 0xCD, 0x00 }, /* CD Digital Mic 34 Biquad Coefficient CD */
+       { 0xCE, 0x00 }, /* CE Digital Mic 34 Biquad Coefficient CE */
+       { 0xCF, 0x00 }, /* CF Digital Mic 34 Biquad Coefficient CF */
+
+       { 0xD0, 0x00 }, /* D0 Digital Mic 34 Biquad Coefficient D0 */
+       { 0xD1, 0x00 }, /* D1 Digital Mic 34 Biquad Coefficient D1 */
+};
 
-/*
- *
- * MAX98090 Registers Definition
- *
- */
+static bool max98090_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case M98090_REG_DEVICE_STATUS:
+       case M98090_REG_JACK_STATUS:
+       case M98090_REG_REVISION_ID:
+               return true;
+       default:
+               return false;
+       }
+}
 
-/* RESET / STATUS / INTERRUPT REGISTERS */
-#define MAX98090_0x00_SW_RESET         0x00
-#define MAX98090_0x01_INT_STS          0x01
-#define MAX98090_0x02_JACK_STS         0x02
-#define MAX98090_0x03_INT_MASK         0x03
-
-/* QUICK SETUP REGISTERS */
-#define MAX98090_0x04_SYS_CLK          0x04
-#define MAX98090_0x05_SAMPLE_RATE      0x05
-#define MAX98090_0x06_DAI_IF           0x06
-#define MAX98090_0x07_DAC_PATH         0x07
-#define MAX98090_0x08_MIC_TO_ADC       0x08
-#define MAX98090_0x09_LINE_TO_ADC      0x09
-#define MAX98090_0x0A_ANALOG_MIC_LOOP  0x0A
-#define MAX98090_0x0B_ANALOG_LINE_LOOP 0x0B
-
-/* ANALOG INPUT CONFIGURATION REGISTERS */
-#define MAX98090_0x0D_INPUT_CONFIG     0x0D
-#define MAX98090_0x0E_LINE_IN_LVL      0x0E
-#define MAX98090_0x0F_LINI_IN_CFG      0x0F
-#define MAX98090_0x10_MIC1_IN_LVL      0x10
-#define MAX98090_0x11_MIC2_IN_LVL      0x11
-
-/* MICROPHONE CONFIGURATION REGISTERS  */
-#define MAX98090_0x12_MIC_BIAS_VOL     0x12
-#define MAX98090_0x13_DIGITAL_MIC_CFG  0x13
-#define MAX98090_0x14_DIGITAL_MIC_MODE 0x14
-
-/* ADC PATH AND CONFIGURATION REGISTERS */
-#define MAX98090_0x15_L_ADC_MIX                0x15
-#define MAX98090_0x16_R_ADC_MIX                0x16
-#define MAX98090_0x17_L_ADC_LVL                0x17
-#define MAX98090_0x18_R_ADC_LVL                0x18
-#define MAX98090_0x19_ADC_BIQUAD_LVL   0x19
-#define MAX98090_0x1A_ADC_SIDETONE     0x1A
-
-/* CLOCK CONFIGURATION REGISTERS */
-#define MAX98090_0x1B_SYS_CLK          0x1B
-#define MAX98090_0x1C_CLK_MODE         0x1C
-#define MAX98090_0x1D_ANY_CLK1         0x1D
-#define MAX98090_0x1E_ANY_CLK2         0x1E
-#define MAX98090_0x1F_ANY_CLK3         0x1F
-#define MAX98090_0x20_ANY_CLK4         0x20
-#define MAX98090_0x21_MASTER_MODE      0x21
-
-/* INTERFACE CONTROL REGISTERS */
-#define MAX98090_0x22_DAI_IF_FMT       0x22
-#define MAX98090_0x23_DAI_TDM_FMT1     0x23
-#define MAX98090_0x24_DAI_TDM_FMT2     0x24
-#define MAX98090_0x25_DAI_IO_CFG       0x25
-#define MAX98090_0x26_FILTER_CFG       0x26
-#define MAX98090_0x27_DAI_PLAYBACK_LVL 0x27
-#define MAX98090_0x28_EQ_PLAYBACK_LVL  0x28
-
-/* HEADPHONE CONTROL REGISTERS */
-#define MAX98090_0x29_L_HP_MIX         0x29
-#define MAX98090_0x2A_R_HP_MIX         0x2A
-#define MAX98090_0x2B_HP_CTR           0x2B
-#define MAX98090_0x2C_L_HP_VOL         0x2C
-#define MAX98090_0x2D_R_HP_VOL         0x2D
-
-/* SPEAKER CONFIGURATION REGISTERS */
-#define MAX98090_0x2E_L_SPK_MIX                0x2E
-#define MAX98090_0x2F_R_SPK_MIX                0x2F
-#define MAX98090_0x30_SPK_CTR          0x30
-#define MAX98090_0x31_L_SPK_VOL                0x31
-#define MAX98090_0x32_R_SPK_VOL                0x32
-
-/* ALC CONFIGURATION REGISTERS */
-#define MAX98090_0x33_ALC_TIMING       0x33
-#define MAX98090_0x34_ALC_COMPRESSOR   0x34
-#define MAX98090_0x35_ALC_EXPANDER     0x35
-#define MAX98090_0x36_ALC_GAIN         0x36
-
-/* RECEIVER AND LINE_OUTPUT REGISTERS */
-#define MAX98090_0x37_RCV_LOUT_L_MIX   0x37
-#define MAX98090_0x38_RCV_LOUT_L_CNTL  0x38
-#define MAX98090_0x39_RCV_LOUT_L_VOL   0x39
-#define MAX98090_0x3A_LOUT_R_MIX       0x3A
-#define MAX98090_0x3B_LOUT_R_CNTL      0x3B
-#define MAX98090_0x3C_LOUT_R_VOL       0x3C
-
-/* JACK DETECT AND ENABLE REGISTERS */
-#define MAX98090_0x3D_JACK_DETECT      0x3D
-#define MAX98090_0x3E_IN_ENABLE                0x3E
-#define MAX98090_0x3F_OUT_ENABLE       0x3F
-#define MAX98090_0x40_LVL_CTR          0x40
-#define MAX98090_0x41_DSP_FILTER_ENABLE        0x41
-
-/* BIAS AND POWER MODE CONFIGURATION REGISTERS */
-#define MAX98090_0x42_BIAS_CTR         0x42
-#define MAX98090_0x43_DAC_CTR          0x43
-#define MAX98090_0x44_ADC_CTR          0x44
-#define MAX98090_0x45_DEV_SHUTDOWN     0x45
-
-/* REVISION ID REGISTER */
-#define MAX98090_0xFF_REV_ID           0xFF
-
-#define MAX98090_REG_MAX_CACHED                0x45
-#define MAX98090_REG_END               0xFF
+static bool max98090_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case M98090_REG_DEVICE_STATUS:
+       case M98090_REG_JACK_STATUS:
+       case M98090_REG_INTERRUPT_S:
+       case M98090_REG_RESERVED:
+       case M98090_REG_LINE_INPUT_CONFIG:
+       case M98090_REG_LINE_INPUT_LEVEL:
+       case M98090_REG_INPUT_MODE:
+       case M98090_REG_MIC1_INPUT_LEVEL:
+       case M98090_REG_MIC2_INPUT_LEVEL:
+       case M98090_REG_MIC_BIAS_VOLTAGE:
+       case M98090_REG_DIGITAL_MIC_ENABLE:
+       case M98090_REG_DIGITAL_MIC_CONFIG:
+       case M98090_REG_LEFT_ADC_MIXER:
+       case M98090_REG_RIGHT_ADC_MIXER:
+       case M98090_REG_LEFT_ADC_LEVEL:
+       case M98090_REG_RIGHT_ADC_LEVEL:
+       case M98090_REG_ADC_BIQUAD_LEVEL:
+       case M98090_REG_ADC_SIDETONE:
+       case M98090_REG_SYSTEM_CLOCK:
+       case M98090_REG_CLOCK_MODE:
+       case M98090_REG_CLOCK_RATIO_NI_MSB:
+       case M98090_REG_CLOCK_RATIO_NI_LSB:
+       case M98090_REG_CLOCK_RATIO_MI_MSB:
+       case M98090_REG_CLOCK_RATIO_MI_LSB:
+       case M98090_REG_MASTER_MODE:
+       case M98090_REG_INTERFACE_FORMAT:
+       case M98090_REG_TDM_CONTROL:
+       case M98090_REG_TDM_FORMAT:
+       case M98090_REG_IO_CONFIGURATION:
+       case M98090_REG_FILTER_CONFIG:
+       case M98090_REG_DAI_PLAYBACK_LEVEL:
+       case M98090_REG_DAI_PLAYBACK_LEVEL_EQ:
+       case M98090_REG_LEFT_HP_MIXER:
+       case M98090_REG_RIGHT_HP_MIXER:
+       case M98090_REG_HP_CONTROL:
+       case M98090_REG_LEFT_HP_VOLUME:
+       case M98090_REG_RIGHT_HP_VOLUME:
+       case M98090_REG_LEFT_SPK_MIXER:
+       case M98090_REG_RIGHT_SPK_MIXER:
+       case M98090_REG_SPK_CONTROL:
+       case M98090_REG_LEFT_SPK_VOLUME:
+       case M98090_REG_RIGHT_SPK_VOLUME:
+       case M98090_REG_DRC_TIMING:
+       case M98090_REG_DRC_COMPRESSOR:
+       case M98090_REG_DRC_EXPANDER:
+       case M98090_REG_DRC_GAIN:
+       case M98090_REG_RCV_LOUTL_MIXER:
+       case M98090_REG_RCV_LOUTL_CONTROL:
+       case M98090_REG_RCV_LOUTL_VOLUME:
+       case M98090_REG_LOUTR_MIXER:
+       case M98090_REG_LOUTR_CONTROL:
+       case M98090_REG_LOUTR_VOLUME:
+       case M98090_REG_JACK_DETECT:
+       case M98090_REG_INPUT_ENABLE:
+       case M98090_REG_OUTPUT_ENABLE:
+       case M98090_REG_LEVEL_CONTROL:
+       case M98090_REG_DSP_FILTER_ENABLE:
+       case M98090_REG_BIAS_CONTROL:
+       case M98090_REG_DAC_CONTROL:
+       case M98090_REG_ADC_CONTROL:
+       case M98090_REG_DEVICE_SHUTDOWN:
+       case M98090_REG_EQUALIZER_BASE ... M98090_REG_EQUALIZER_BASE + 0x68:
+       case M98090_REG_RECORD_BIQUAD_BASE ... M98090_REG_RECORD_BIQUAD_BASE + 0x0E:
+       case M98090_REG_DMIC3_VOLUME:
+       case M98090_REG_DMIC4_VOLUME:
+       case M98090_REG_DMIC34_BQ_PREATTEN:
+       case M98090_REG_RECORD_TDM_SLOT:
+       case M98090_REG_SAMPLE_RATE:
+       case M98090_REG_DMIC34_BIQUAD_BASE ... M98090_REG_DMIC34_BIQUAD_BASE + 0x0E:
+               return true;
+       default:
+               return false;
+       }
+}
 
-/*
- *
- * MAX98090 Registers Bit Fields
- *
- */
+static int max98090_reset(struct max98090_priv *max98090)
+{
+       int ret;
 
-/* MAX98090_0x06_DAI_IF */
-#define MAX98090_DAI_IF_MASK           0x3F
-#define MAX98090_RJ_M                  (1 << 5)
-#define MAX98090_RJ_S                  (1 << 4)
-#define MAX98090_LJ_M                  (1 << 3)
-#define MAX98090_LJ_S                  (1 << 2)
-#define MAX98090_I2S_M                 (1 << 1)
-#define MAX98090_I2S_S                 (1 << 0)
-
-/* MAX98090_0x45_DEV_SHUTDOWN */
-#define MAX98090_SHDNRUN               (1 << 7)
-
-/* codec private data */
-struct max98090_priv {
-       struct regmap *regmap;
-};
-
-static const struct reg_default max98090_reg_defaults[] = {
-       /* RESET / STATUS / INTERRUPT REGISTERS */
-       {MAX98090_0x00_SW_RESET,                0x00},
-       {MAX98090_0x01_INT_STS,                 0x00},
-       {MAX98090_0x02_JACK_STS,                0x00},
-       {MAX98090_0x03_INT_MASK,                0x04},
-
-       /* QUICK SETUP REGISTERS */
-       {MAX98090_0x04_SYS_CLK,                 0x00},
-       {MAX98090_0x05_SAMPLE_RATE,             0x00},
-       {MAX98090_0x06_DAI_IF,                  0x00},
-       {MAX98090_0x07_DAC_PATH,                0x00},
-       {MAX98090_0x08_MIC_TO_ADC,              0x00},
-       {MAX98090_0x09_LINE_TO_ADC,             0x00},
-       {MAX98090_0x0A_ANALOG_MIC_LOOP,         0x00},
-       {MAX98090_0x0B_ANALOG_LINE_LOOP,        0x00},
-
-       /* ANALOG INPUT CONFIGURATION REGISTERS */
-       {MAX98090_0x0D_INPUT_CONFIG,            0x00},
-       {MAX98090_0x0E_LINE_IN_LVL,             0x1B},
-       {MAX98090_0x0F_LINI_IN_CFG,             0x00},
-       {MAX98090_0x10_MIC1_IN_LVL,             0x11},
-       {MAX98090_0x11_MIC2_IN_LVL,             0x11},
-
-       /* MICROPHONE CONFIGURATION REGISTERS  */
-       {MAX98090_0x12_MIC_BIAS_VOL,            0x00},
-       {MAX98090_0x13_DIGITAL_MIC_CFG,         0x00},
-       {MAX98090_0x14_DIGITAL_MIC_MODE,        0x00},
-
-       /* ADC PATH AND CONFIGURATION REGISTERS */
-       {MAX98090_0x15_L_ADC_MIX,               0x00},
-       {MAX98090_0x16_R_ADC_MIX,               0x00},
-       {MAX98090_0x17_L_ADC_LVL,               0x03},
-       {MAX98090_0x18_R_ADC_LVL,               0x03},
-       {MAX98090_0x19_ADC_BIQUAD_LVL,          0x00},
-       {MAX98090_0x1A_ADC_SIDETONE,            0x00},
-
-       /* CLOCK CONFIGURATION REGISTERS */
-       {MAX98090_0x1B_SYS_CLK,                 0x00},
-       {MAX98090_0x1C_CLK_MODE,                0x00},
-       {MAX98090_0x1D_ANY_CLK1,                0x00},
-       {MAX98090_0x1E_ANY_CLK2,                0x00},
-       {MAX98090_0x1F_ANY_CLK3,                0x00},
-       {MAX98090_0x20_ANY_CLK4,                0x00},
-       {MAX98090_0x21_MASTER_MODE,             0x00},
-
-       /* INTERFACE CONTROL REGISTERS */
-       {MAX98090_0x22_DAI_IF_FMT,              0x00},
-       {MAX98090_0x23_DAI_TDM_FMT1,            0x00},
-       {MAX98090_0x24_DAI_TDM_FMT2,            0x00},
-       {MAX98090_0x25_DAI_IO_CFG,              0x00},
-       {MAX98090_0x26_FILTER_CFG,              0x80},
-       {MAX98090_0x27_DAI_PLAYBACK_LVL,        0x00},
-       {MAX98090_0x28_EQ_PLAYBACK_LVL,         0x00},
-
-       /* HEADPHONE CONTROL REGISTERS */
-       {MAX98090_0x29_L_HP_MIX,                0x00},
-       {MAX98090_0x2A_R_HP_MIX,                0x00},
-       {MAX98090_0x2B_HP_CTR,                  0x00},
-       {MAX98090_0x2C_L_HP_VOL,                0x1A},
-       {MAX98090_0x2D_R_HP_VOL,                0x1A},
-
-       /* SPEAKER CONFIGURATION REGISTERS */
-       {MAX98090_0x2E_L_SPK_MIX,               0x00},
-       {MAX98090_0x2F_R_SPK_MIX,               0x00},
-       {MAX98090_0x30_SPK_CTR,                 0x00},
-       {MAX98090_0x31_L_SPK_VOL,               0x2C},
-       {MAX98090_0x32_R_SPK_VOL,               0x2C},
-
-       /* ALC CONFIGURATION REGISTERS */
-       {MAX98090_0x33_ALC_TIMING,              0x00},
-       {MAX98090_0x34_ALC_COMPRESSOR,          0x00},
-       {MAX98090_0x35_ALC_EXPANDER,            0x00},
-       {MAX98090_0x36_ALC_GAIN,                0x00},
-
-       /* RECEIVER AND LINE_OUTPUT REGISTERS */
-       {MAX98090_0x37_RCV_LOUT_L_MIX,          0x00},
-       {MAX98090_0x38_RCV_LOUT_L_CNTL,         0x00},
-       {MAX98090_0x39_RCV_LOUT_L_VOL,          0x15},
-       {MAX98090_0x3A_LOUT_R_MIX,              0x00},
-       {MAX98090_0x3B_LOUT_R_CNTL,             0x00},
-       {MAX98090_0x3C_LOUT_R_VOL,              0x15},
-
-       /* JACK DETECT AND ENABLE REGISTERS */
-       {MAX98090_0x3D_JACK_DETECT,             0x00},
-       {MAX98090_0x3E_IN_ENABLE,               0x00},
-       {MAX98090_0x3F_OUT_ENABLE,              0x00},
-       {MAX98090_0x40_LVL_CTR,                 0x00},
-       {MAX98090_0x41_DSP_FILTER_ENABLE,       0x00},
-
-       /* BIAS AND POWER MODE CONFIGURATION REGISTERS */
-       {MAX98090_0x42_BIAS_CTR,                0x00},
-       {MAX98090_0x43_DAC_CTR,                 0x00},
-       {MAX98090_0x44_ADC_CTR,                 0x06},
-       {MAX98090_0x45_DEV_SHUTDOWN,            0x00},
+       /* Reset the codec by writing to this write-only reset register */
+       ret = regmap_write(max98090->regmap, M98090_REG_SOFTWARE_RESET,
+               M98090_SWRESET_MASK);
+       if (ret < 0) {
+               dev_err(max98090->codec->dev,
+                       "Failed to reset codec: %d\n", ret);
+               return ret;
+       }
+
+       msleep(20);
+       return ret;
+}
+
+static const unsigned int max98090_micboost_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+       2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(max98090_mic_tlv, 0, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(max98090_line_single_ended_tlv,
+       -600, 600, 0);
+
+static const unsigned int max98090_line_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 3, TLV_DB_SCALE_ITEM(-600, 300, 0),
+       4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0),
+};
+
+static const DECLARE_TLV_DB_SCALE(max98090_avg_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_av_tlv, -1200, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(max98090_dvg_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_dv_tlv, -1500, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(max98090_sidetone_tlv, -6050, 200, 0);
+
+static const DECLARE_TLV_DB_SCALE(max98090_alc_tlv, -1500, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_alcmakeup_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_alccomp_tlv, -3100, 100, 0);
+static const DECLARE_TLV_DB_SCALE(max98090_drcexp_tlv, -6600, 100, 0);
+
+static const unsigned int max98090_mixout_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 1, TLV_DB_SCALE_ITEM(-1200, 250, 0),
+       2, 3, TLV_DB_SCALE_ITEM(-600, 600, 0),
 };
 
 static const unsigned int max98090_hp_tlv[] = {
        TLV_DB_RANGE_HEAD(5),
-       0x0,    0x6,    TLV_DB_SCALE_ITEM(-6700, 400, 0),
-       0x7,    0xE,    TLV_DB_SCALE_ITEM(-4000, 300, 0),
-       0xF,    0x15,   TLV_DB_SCALE_ITEM(-1700, 200, 0),
-       0x16,   0x1B,   TLV_DB_SCALE_ITEM(-400, 100, 0),
-       0x1C,   0x1F,   TLV_DB_SCALE_ITEM(150, 50, 0),
+       0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0),
+       7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0),
+       15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0),
+       22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0),
+       28, 31, TLV_DB_SCALE_ITEM(150, 50, 0),
 };
 
-static struct snd_kcontrol_new max98090_snd_controls[] = {
-       SOC_DOUBLE_R_TLV("Headphone Volume", MAX98090_0x2C_L_HP_VOL,
-                        MAX98090_0x2D_R_HP_VOL, 0, 31, 0, max98090_hp_tlv),
+static const unsigned int max98090_spk_tlv[] = {
+       TLV_DB_RANGE_HEAD(5),
+       0, 4, TLV_DB_SCALE_ITEM(-4800, 400, 0),
+       5, 10, TLV_DB_SCALE_ITEM(-2900, 300, 0),
+       11, 14, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+       15, 29, TLV_DB_SCALE_ITEM(-500, 100, 0),
+       30, 39, TLV_DB_SCALE_ITEM(950, 50, 0),
 };
 
-/* Left HeadPhone Mixer Switch */
-static struct snd_kcontrol_new max98090_left_hp_mixer_controls[] = {
-       SOC_DAPM_SINGLE("DACR Switch", MAX98090_0x29_L_HP_MIX, 1, 1, 0),
-       SOC_DAPM_SINGLE("DACL Switch", MAX98090_0x29_L_HP_MIX, 0, 1, 0),
+static const unsigned int max98090_rcv_lout_tlv[] = {
+       TLV_DB_RANGE_HEAD(5),
+       0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0),
+       7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0),
+       15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0),
+       22, 27, TLV_DB_SCALE_ITEM(100, 100, 0),
+       28, 31, TLV_DB_SCALE_ITEM(650, 50, 0),
 };
 
-/* Right HeadPhone Mixer Switch */
-static struct snd_kcontrol_new max98090_right_hp_mixer_controls[] = {
-       SOC_DAPM_SINGLE("DACR Switch", MAX98090_0x2A_R_HP_MIX, 1, 1, 0),
-       SOC_DAPM_SINGLE("DACL Switch", MAX98090_0x2A_R_HP_MIX, 0, 1, 0),
+static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int mask = (1 << fls(mc->max)) - 1;
+       unsigned int val = snd_soc_read(codec, mc->reg);
+       unsigned int *select;
+
+       switch (mc->reg) {
+       case M98090_REG_MIC1_INPUT_LEVEL:
+               select = &(max98090->pa1en);
+               break;
+       case M98090_REG_MIC2_INPUT_LEVEL:
+               select = &(max98090->pa2en);
+               break;
+       case M98090_REG_ADC_SIDETONE:
+               select = &(max98090->sidetone);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       val = (val >> mc->shift) & mask;
+
+       if (val >= 1) {
+               /* If on, return the volume */
+               val = val - 1;
+               *select = val;
+       } else {
+               /* If off, return last stored value */
+               val = *select;
+       }
+
+       ucontrol->value.integer.value[0] = val;
+       return 0;
+}
+
+static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int mask = (1 << fls(mc->max)) - 1;
+       unsigned int sel = ucontrol->value.integer.value[0];
+       unsigned int val = snd_soc_read(codec, mc->reg);
+       unsigned int *select;
+
+       switch (mc->reg) {
+       case M98090_REG_MIC1_INPUT_LEVEL:
+               select = &(max98090->pa1en);
+               break;
+       case M98090_REG_MIC2_INPUT_LEVEL:
+               select = &(max98090->pa2en);
+               break;
+       case M98090_REG_ADC_SIDETONE:
+               select = &(max98090->sidetone);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       val = (val >> mc->shift) & mask;
+
+       *select = sel;
+
+       /* Setting a volume is only valid if it is already On */
+       if (val >= 1) {
+               sel = sel + 1;
+       } else {
+               /* Write what was already there */
+               sel = val;
+       }
+
+       snd_soc_update_bits(codec, mc->reg,
+               mask << mc->shift,
+               sel << mc->shift);
+
+       return 0;
+}
+
+static const char * max98090_perf_pwr_text[] =
+       { "High Performance", "Low Power" };
+static const char * max98090_pwr_perf_text[] =
+       { "Low Power", "High Performance" };
+
+static const struct soc_enum max98090_vcmbandgap_enum =
+       SOC_ENUM_SINGLE(M98090_REG_BIAS_CONTROL, M98090_VCM_MODE_SHIFT,
+               ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text);
+
+static const char * max98090_osr128_text[] = { "64*fs", "128*fs" };
+
+static const struct soc_enum max98090_osr128_enum =
+       SOC_ENUM_SINGLE(M98090_REG_ADC_CONTROL, M98090_OSR128_SHIFT,
+               ARRAY_SIZE(max98090_osr128_text), max98090_osr128_text);
+
+static const char *max98090_mode_text[] = { "Voice", "Music" };
+
+static const struct soc_enum max98090_mode_enum =
+       SOC_ENUM_SINGLE(M98090_REG_FILTER_CONFIG, M98090_MODE_SHIFT,
+               ARRAY_SIZE(max98090_mode_text), max98090_mode_text);
+
+static const struct soc_enum max98090_filter_dmic34mode_enum =
+       SOC_ENUM_SINGLE(M98090_REG_FILTER_CONFIG,
+               M98090_FLT_DMIC34MODE_SHIFT,
+               ARRAY_SIZE(max98090_mode_text), max98090_mode_text);
+
+static const char * max98090_drcatk_text[] =
+       { "0.5ms", "1ms", "5ms", "10ms", "25ms", "50ms", "100ms", "200ms" };
+
+static const struct soc_enum max98090_drcatk_enum =
+       SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCATK_SHIFT,
+               ARRAY_SIZE(max98090_drcatk_text), max98090_drcatk_text);
+
+static const char * max98090_drcrls_text[] =
+       { "8s", "4s", "2s", "1s", "0.5s", "0.25s", "0.125s", "0.0625s" };
+
+static const struct soc_enum max98090_drcrls_enum =
+       SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCRLS_SHIFT,
+               ARRAY_SIZE(max98090_drcrls_text), max98090_drcrls_text);
+
+static const char * max98090_alccmp_text[] =
+       { "1:1", "1:1.5", "1:2", "1:4", "1:INF" };
+
+static const struct soc_enum max98090_alccmp_enum =
+       SOC_ENUM_SINGLE(M98090_REG_DRC_COMPRESSOR, M98090_DRCCMP_SHIFT,
+               ARRAY_SIZE(max98090_alccmp_text), max98090_alccmp_text);
+
+static const char * max98090_drcexp_text[] = { "1:1", "2:1", "3:1" };
+
+static const struct soc_enum max98090_drcexp_enum =
+       SOC_ENUM_SINGLE(M98090_REG_DRC_EXPANDER, M98090_DRCEXP_SHIFT,
+               ARRAY_SIZE(max98090_drcexp_text), max98090_drcexp_text);
+
+static const struct soc_enum max98090_dac_perfmode_enum =
+       SOC_ENUM_SINGLE(M98090_REG_DAC_CONTROL, M98090_PERFMODE_SHIFT,
+               ARRAY_SIZE(max98090_perf_pwr_text), max98090_perf_pwr_text);
+
+static const struct soc_enum max98090_dachp_enum =
+       SOC_ENUM_SINGLE(M98090_REG_DAC_CONTROL, M98090_DACHP_SHIFT,
+               ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text);
+
+static const struct soc_enum max98090_adchp_enum =
+       SOC_ENUM_SINGLE(M98090_REG_ADC_CONTROL, M98090_ADCHP_SHIFT,
+               ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text);
+
+static const struct snd_kcontrol_new max98090_snd_controls[] = {
+       SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum),
+
+       SOC_SINGLE("DMIC MIC Comp Filter Config", M98090_REG_DIGITAL_MIC_CONFIG,
+               M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0),
+
+       SOC_SINGLE_EXT_TLV("MIC1 Boost Volume",
+               M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT,
+               M98090_MIC_PA1EN_NUM - 1, 0, max98090_get_enab_tlv,
+               max98090_put_enab_tlv, max98090_micboost_tlv),
+
+       SOC_SINGLE_EXT_TLV("MIC2 Boost Volume",
+               M98090_REG_MIC2_INPUT_LEVEL, M98090_MIC_PA2EN_SHIFT,
+               M98090_MIC_PA2EN_NUM - 1, 0, max98090_get_enab_tlv,
+               max98090_put_enab_tlv, max98090_micboost_tlv),
+
+       SOC_SINGLE_TLV("MIC1 Volume", M98090_REG_MIC1_INPUT_LEVEL,
+               M98090_MIC_PGAM1_SHIFT, M98090_MIC_PGAM1_NUM - 1, 1,
+               max98090_mic_tlv),
+
+       SOC_SINGLE_TLV("MIC2 Volume", M98090_REG_MIC2_INPUT_LEVEL,
+               M98090_MIC_PGAM2_SHIFT, M98090_MIC_PGAM2_NUM - 1, 1,
+               max98090_mic_tlv),
+
+       SOC_SINGLE_RANGE_TLV("LINEA Single Ended Volume",
+               M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG135_SHIFT, 0,
+               M98090_MIXG135_NUM - 1, 1, max98090_line_single_ended_tlv),
+
+       SOC_SINGLE_RANGE_TLV("LINEB Single Ended Volume",
+               M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG246_SHIFT, 0,
+               M98090_MIXG246_NUM - 1, 1, max98090_line_single_ended_tlv),
+
+       SOC_SINGLE_RANGE_TLV("LINEA Volume", M98090_REG_LINE_INPUT_LEVEL,
+               M98090_LINAPGA_SHIFT, 0, M98090_LINAPGA_NUM - 1, 1,
+               max98090_line_tlv),
+
+       SOC_SINGLE_RANGE_TLV("LINEB Volume", M98090_REG_LINE_INPUT_LEVEL,
+               M98090_LINBPGA_SHIFT, 0, M98090_LINBPGA_NUM - 1, 1,
+               max98090_line_tlv),
+
+       SOC_SINGLE("LINEA Ext Resistor Gain Mode", M98090_REG_INPUT_MODE,
+               M98090_EXTBUFA_SHIFT, M98090_EXTBUFA_NUM - 1, 0),
+       SOC_SINGLE("LINEB Ext Resistor Gain Mode", M98090_REG_INPUT_MODE,
+               M98090_EXTBUFB_SHIFT, M98090_EXTBUFB_NUM - 1, 0),
+
+       SOC_SINGLE_TLV("ADCL Boost Volume", M98090_REG_LEFT_ADC_LEVEL,
+               M98090_AVLG_SHIFT, M98090_AVLG_NUM - 1, 0,
+               max98090_avg_tlv),
+       SOC_SINGLE_TLV("ADCR Boost Volume", M98090_REG_RIGHT_ADC_LEVEL,
+               M98090_AVRG_SHIFT, M98090_AVLG_NUM - 1, 0,
+               max98090_avg_tlv),
+
+       SOC_SINGLE_TLV("ADCL Volume", M98090_REG_LEFT_ADC_LEVEL,
+               M98090_AVL_SHIFT, M98090_AVL_NUM - 1, 1,
+               max98090_av_tlv),
+       SOC_SINGLE_TLV("ADCR Volume", M98090_REG_RIGHT_ADC_LEVEL,
+               M98090_AVR_SHIFT, M98090_AVR_NUM - 1, 1,
+               max98090_av_tlv),
+
+       SOC_ENUM("ADC Oversampling Rate", max98090_osr128_enum),
+       SOC_SINGLE("ADC Quantizer Dither", M98090_REG_ADC_CONTROL,
+               M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0),
+       SOC_ENUM("ADC High Performance Mode", max98090_adchp_enum),
+
+       SOC_SINGLE("DAC Mono Mode", M98090_REG_IO_CONFIGURATION,
+               M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0),
+       SOC_SINGLE("SDIN Mode", M98090_REG_IO_CONFIGURATION,
+               M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0),
+       SOC_SINGLE("SDOUT Mode", M98090_REG_IO_CONFIGURATION,
+               M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0),
+       SOC_SINGLE("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION,
+               M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1),
+       SOC_ENUM("Filter Mode", max98090_mode_enum),
+       SOC_SINGLE("Record Path DC Blocking", M98090_REG_FILTER_CONFIG,
+               M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0),
+       SOC_SINGLE("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG,
+               M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0),
+       SOC_SINGLE_TLV("Digital BQ Volume", M98090_REG_ADC_BIQUAD_LEVEL,
+               M98090_AVBQ_SHIFT, M98090_AVBQ_NUM - 1, 1, max98090_dv_tlv),
+       SOC_SINGLE_EXT_TLV("Digital Sidetone Volume",
+               M98090_REG_ADC_SIDETONE, M98090_DVST_SHIFT,
+               M98090_DVST_NUM - 1, 1, max98090_get_enab_tlv,
+               max98090_put_enab_tlv, max98090_micboost_tlv),
+       SOC_SINGLE_TLV("Digital Coarse Volume", M98090_REG_DAI_PLAYBACK_LEVEL,
+               M98090_DVG_SHIFT, M98090_DVG_NUM - 1, 0,
+               max98090_dvg_tlv),
+       SOC_SINGLE_TLV("Digital Volume", M98090_REG_DAI_PLAYBACK_LEVEL,
+               M98090_DV_SHIFT, M98090_DV_NUM - 1, 1,
+               max98090_dv_tlv),
+       SND_SOC_BYTES("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105),
+       SOC_SINGLE("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+               M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0),
+       SOC_SINGLE("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+               M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0),
+       SOC_SINGLE("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE,
+               M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0),
+       SOC_SINGLE("Digital EQ Clipping Detection", M98090_REG_DAI_PLAYBACK_LEVEL_EQ,
+               M98090_EQCLPN_SHIFT, M98090_EQCLPN_NUM - 1,
+               1),
+       SOC_SINGLE_TLV("Digital EQ Volume", M98090_REG_DAI_PLAYBACK_LEVEL_EQ,
+               M98090_DVEQ_SHIFT, M98090_DVEQ_NUM - 1, 1,
+               max98090_dv_tlv),
+
+       SOC_SINGLE("ALC Enable", M98090_REG_DRC_TIMING,
+               M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0),
+       SOC_ENUM("ALC Attack Time", max98090_drcatk_enum),
+       SOC_ENUM("ALC Release Time", max98090_drcrls_enum),
+       SOC_SINGLE_TLV("ALC Make Up Volume", M98090_REG_DRC_GAIN,
+               M98090_DRCG_SHIFT, M98090_DRCG_NUM - 1, 0,
+               max98090_alcmakeup_tlv),
+       SOC_ENUM("ALC Compression Ratio", max98090_alccmp_enum),
+       SOC_ENUM("ALC Expansion Ratio", max98090_drcexp_enum),
+       SOC_SINGLE_TLV("ALC Compression Threshold Volume",
+               M98090_REG_DRC_COMPRESSOR, M98090_DRCTHC_SHIFT,
+               M98090_DRCTHC_NUM - 1, 1, max98090_alccomp_tlv),
+       SOC_SINGLE_TLV("ALC Expansion Threshold Volume",
+               M98090_REG_DRC_EXPANDER, M98090_DRCTHE_SHIFT,
+               M98090_DRCTHE_NUM - 1, 1, max98090_drcexp_tlv),
+
+       SOC_ENUM("DAC HP Playback Performance Mode",
+               max98090_dac_perfmode_enum),
+       SOC_ENUM("DAC High Performance Mode", max98090_dachp_enum),
+
+       SOC_SINGLE_TLV("Headphone Left Mixer Volume",
+               M98090_REG_HP_CONTROL, M98090_MIXHPLG_SHIFT,
+               M98090_MIXHPLG_NUM - 1, 1, max98090_mixout_tlv),
+       SOC_SINGLE_TLV("Headphone Right Mixer Volume",
+               M98090_REG_HP_CONTROL, M98090_MIXHPRG_SHIFT,
+               M98090_MIXHPRG_NUM - 1, 1, max98090_mixout_tlv),
+
+       SOC_SINGLE_TLV("Speaker Left Mixer Volume",
+               M98090_REG_SPK_CONTROL, M98090_MIXSPLG_SHIFT,
+               M98090_MIXSPLG_NUM - 1, 1, max98090_mixout_tlv),
+       SOC_SINGLE_TLV("Speaker Right Mixer Volume",
+               M98090_REG_SPK_CONTROL, M98090_MIXSPRG_SHIFT,
+               M98090_MIXSPRG_NUM - 1, 1, max98090_mixout_tlv),
+
+       SOC_SINGLE_TLV("Receiver Left Mixer Volume",
+               M98090_REG_RCV_LOUTL_CONTROL, M98090_MIXRCVLG_SHIFT,
+               M98090_MIXRCVLG_NUM - 1, 1, max98090_mixout_tlv),
+       SOC_SINGLE_TLV("Receiver Right Mixer Volume",
+               M98090_REG_LOUTR_CONTROL, M98090_MIXRCVRG_SHIFT,
+               M98090_MIXRCVRG_NUM - 1, 1, max98090_mixout_tlv),
+
+       SOC_DOUBLE_R_TLV("Headphone Volume", M98090_REG_LEFT_HP_VOLUME,
+               M98090_REG_RIGHT_HP_VOLUME, M98090_HPVOLL_SHIFT,
+               M98090_HPVOLL_NUM - 1, 0, max98090_hp_tlv),
+
+       SOC_DOUBLE_R_RANGE_TLV("Speaker Volume",
+               M98090_REG_LEFT_SPK_VOLUME, M98090_REG_RIGHT_SPK_VOLUME,
+               M98090_SPVOLL_SHIFT, 24, M98090_SPVOLL_NUM - 1 + 24,
+               0, max98090_spk_tlv),
+
+       SOC_DOUBLE_R_TLV("Receiver Volume", M98090_REG_RCV_LOUTL_VOLUME,
+               M98090_REG_LOUTR_VOLUME, M98090_RCVLVOL_SHIFT,
+               M98090_RCVLVOL_NUM - 1, 0, max98090_rcv_lout_tlv),
+
+       SOC_SINGLE("Headphone Left Switch", M98090_REG_LEFT_HP_VOLUME,
+               M98090_HPLM_SHIFT, 1, 1),
+       SOC_SINGLE("Headphone Right Switch", M98090_REG_RIGHT_HP_VOLUME,
+               M98090_HPRM_SHIFT, 1, 1),
+
+       SOC_SINGLE("Speaker Left Switch", M98090_REG_LEFT_SPK_VOLUME,
+               M98090_SPLM_SHIFT, 1, 1),
+       SOC_SINGLE("Speaker Right Switch", M98090_REG_RIGHT_SPK_VOLUME,
+               M98090_SPRM_SHIFT, 1, 1),
+
+       SOC_SINGLE("Receiver Left Switch", M98090_REG_RCV_LOUTL_VOLUME,
+               M98090_RCVLM_SHIFT, 1, 1),
+       SOC_SINGLE("Receiver Right Switch", M98090_REG_LOUTR_VOLUME,
+               M98090_RCVRM_SHIFT, 1, 1),
+
+       SOC_SINGLE("Zero-Crossing Detection", M98090_REG_LEVEL_CONTROL,
+               M98090_ZDENN_SHIFT, M98090_ZDENN_NUM - 1, 1),
+       SOC_SINGLE("Enhanced Vol Smoothing", M98090_REG_LEVEL_CONTROL,
+               M98090_VS2ENN_SHIFT, M98090_VS2ENN_NUM - 1, 1),
+       SOC_SINGLE("Volume Adjustment Smoothing", M98090_REG_LEVEL_CONTROL,
+               M98090_VSENN_SHIFT, M98090_VSENN_NUM - 1, 1),
+
+       SND_SOC_BYTES("Biquad Coefficients", M98090_REG_RECORD_BIQUAD_BASE, 15),
+       SOC_SINGLE("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
+               M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0),
 };
 
-static struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
-       /* Output */
+static const struct snd_kcontrol_new max98091_snd_controls[] = {
+
+       SOC_SINGLE("DMIC34 Zeropad", M98090_REG_SAMPLE_RATE,
+               M98090_DMIC34_ZEROPAD_SHIFT,
+               M98090_DMIC34_ZEROPAD_NUM - 1, 0),
+
+       SOC_ENUM("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum),
+       SOC_SINGLE("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG,
+               M98090_FLT_DMIC34HPF_SHIFT,
+               M98090_FLT_DMIC34HPF_NUM - 1, 0),
+
+       SOC_SINGLE_TLV("DMIC3 Boost Volume", M98090_REG_DMIC3_VOLUME,
+               M98090_DMIC_AV3G_SHIFT, M98090_DMIC_AV3G_NUM - 1, 0,
+               max98090_avg_tlv),
+       SOC_SINGLE_TLV("DMIC4 Boost Volume", M98090_REG_DMIC4_VOLUME,
+               M98090_DMIC_AV4G_SHIFT, M98090_DMIC_AV4G_NUM - 1, 0,
+               max98090_avg_tlv),
+
+       SOC_SINGLE_TLV("DMIC3 Volume", M98090_REG_DMIC3_VOLUME,
+               M98090_DMIC_AV3_SHIFT, M98090_DMIC_AV3_NUM - 1, 1,
+               max98090_av_tlv),
+       SOC_SINGLE_TLV("DMIC4 Volume", M98090_REG_DMIC4_VOLUME,
+               M98090_DMIC_AV4_SHIFT, M98090_DMIC_AV4_NUM - 1, 1,
+               max98090_av_tlv),
+
+       SND_SOC_BYTES("DMIC34 Biquad Coefficients",
+               M98090_REG_DMIC34_BIQUAD_BASE, 15),
+       SOC_SINGLE("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE,
+               M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0),
+
+       SOC_SINGLE_TLV("DMIC34 BQ PreAttenuation Volume",
+               M98090_REG_DMIC34_BQ_PREATTEN, M98090_AV34BQ_SHIFT,
+               M98090_AV34BQ_NUM - 1, 1, max98090_dv_tlv),
+};
+
+static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
+                                struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+       unsigned int val = snd_soc_read(codec, w->reg);
+
+       if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
+               val = (val & M98090_MIC_PA1EN_MASK) >> M98090_MIC_PA1EN_SHIFT;
+       else
+               val = (val & M98090_MIC_PA2EN_MASK) >> M98090_MIC_PA2EN_SHIFT;
+
+
+       if (val >= 1) {
+               if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) {
+                       max98090->pa1en = val - 1; /* Update for volatile */
+               } else {
+                       max98090->pa2en = val - 1; /* Update for volatile */
+               }
+       }
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /* If turning on, set to most recently selected volume */
+               if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
+                       val = max98090->pa1en + 1;
+               else
+                       val = max98090->pa2en + 1;
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               /* If turning off, turn off */
+               val = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (w->reg == M98090_REG_MIC1_INPUT_LEVEL)
+               snd_soc_update_bits(codec, w->reg, M98090_MIC_PA1EN_MASK,
+                       val << M98090_MIC_PA1EN_SHIFT);
+       else
+               snd_soc_update_bits(codec, w->reg, M98090_MIC_PA2EN_MASK,
+                       val << M98090_MIC_PA2EN_SHIFT);
+
+       return 0;
+}
+
+static const char *mic1_mux_text[] = { "IN12", "IN56" };
+
+static const struct soc_enum mic1_mux_enum =
+       SOC_ENUM_SINGLE(M98090_REG_INPUT_MODE, M98090_EXTMIC1_SHIFT,
+               ARRAY_SIZE(mic1_mux_text), mic1_mux_text);
+
+static const struct snd_kcontrol_new max98090_mic1_mux =
+       SOC_DAPM_ENUM("MIC1 Mux", mic1_mux_enum);
+
+static const char *mic2_mux_text[] = { "IN34", "IN56" };
+
+static const struct soc_enum mic2_mux_enum =
+       SOC_ENUM_SINGLE(M98090_REG_INPUT_MODE, M98090_EXTMIC2_SHIFT,
+               ARRAY_SIZE(mic2_mux_text), mic2_mux_text);
+
+static const struct snd_kcontrol_new max98090_mic2_mux =
+       SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum);
+
+static const char * max98090_micpre_text[] = { "Off", "On" };
+
+static const struct soc_enum max98090_pa1en_enum =
+       SOC_ENUM_SINGLE(M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT,
+               ARRAY_SIZE(max98090_micpre_text), max98090_micpre_text);
+
+static const struct soc_enum max98090_pa2en_enum =
+       SOC_ENUM_SINGLE(M98090_REG_MIC2_INPUT_LEVEL, M98090_MIC_PA2EN_SHIFT,
+               ARRAY_SIZE(max98090_micpre_text), max98090_micpre_text);
+
+/* LINEA mixer switch */
+static const struct snd_kcontrol_new max98090_linea_mixer_controls[] = {
+       SOC_DAPM_SINGLE("IN1 Switch", M98090_REG_LINE_INPUT_CONFIG,
+               M98090_IN1SEEN_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("IN3 Switch", M98090_REG_LINE_INPUT_CONFIG,
+               M98090_IN3SEEN_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("IN5 Switch", M98090_REG_LINE_INPUT_CONFIG,
+               M98090_IN5SEEN_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LINE_INPUT_CONFIG,
+               M98090_IN34DIFF_SHIFT, 1, 0),
+};
+
+/* LINEB mixer switch */
+static const struct snd_kcontrol_new max98090_lineb_mixer_controls[] = {
+       SOC_DAPM_SINGLE("IN2 Switch", M98090_REG_LINE_INPUT_CONFIG,
+               M98090_IN2SEEN_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("IN4 Switch", M98090_REG_LINE_INPUT_CONFIG,
+               M98090_IN4SEEN_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("IN6 Switch", M98090_REG_LINE_INPUT_CONFIG,
+               M98090_IN6SEEN_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LINE_INPUT_CONFIG,
+               M98090_IN56DIFF_SHIFT, 1, 0),
+};
+
+/* Left ADC mixer switch */
+static const struct snd_kcontrol_new max98090_left_adc_mixer_controls[] = {
+       SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_LEFT_ADC_MIXER,
+               M98090_MIXADL_IN12DIFF_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LEFT_ADC_MIXER,
+               M98090_MIXADL_IN34DIFF_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LEFT_ADC_MIXER,
+               M98090_MIXADL_IN65DIFF_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_ADC_MIXER,
+               M98090_MIXADL_LINEA_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_ADC_MIXER,
+               M98090_MIXADL_LINEB_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_ADC_MIXER,
+               M98090_MIXADL_MIC1_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_ADC_MIXER,
+               M98090_MIXADL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right ADC mixer switch */
+static const struct snd_kcontrol_new max98090_right_adc_mixer_controls[] = {
+       SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_RIGHT_ADC_MIXER,
+               M98090_MIXADR_IN12DIFF_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_RIGHT_ADC_MIXER,
+               M98090_MIXADR_IN34DIFF_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_RIGHT_ADC_MIXER,
+               M98090_MIXADR_IN65DIFF_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_ADC_MIXER,
+               M98090_MIXADR_LINEA_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_ADC_MIXER,
+               M98090_MIXADR_LINEB_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_ADC_MIXER,
+               M98090_MIXADR_MIC1_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_ADC_MIXER,
+               M98090_MIXADR_MIC2_SHIFT, 1, 0),
+};
+
+static const char *lten_mux_text[] = { "Normal", "Loopthrough" };
+
+static const struct soc_enum ltenl_mux_enum =
+       SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LTEN_SHIFT,
+               ARRAY_SIZE(lten_mux_text), lten_mux_text);
+
+static const struct soc_enum ltenr_mux_enum =
+       SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LTEN_SHIFT,
+               ARRAY_SIZE(lten_mux_text), lten_mux_text);
+
+static const struct snd_kcontrol_new max98090_ltenl_mux =
+       SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum);
+
+static const struct snd_kcontrol_new max98090_ltenr_mux =
+       SOC_DAPM_ENUM("LTENR Mux", ltenr_mux_enum);
+
+static const char *lben_mux_text[] = { "Normal", "Loopback" };
+
+static const struct soc_enum lbenl_mux_enum =
+       SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LBEN_SHIFT,
+               ARRAY_SIZE(lben_mux_text), lben_mux_text);
+
+static const struct soc_enum lbenr_mux_enum =
+       SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LBEN_SHIFT,
+               ARRAY_SIZE(lben_mux_text), lben_mux_text);
+
+static const struct snd_kcontrol_new max98090_lbenl_mux =
+       SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum);
+
+static const struct snd_kcontrol_new max98090_lbenr_mux =
+       SOC_DAPM_ENUM("LBENR Mux", lbenr_mux_enum);
+
+static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" };
+
+static const char *stenr_mux_text[] = { "Normal", "Sidetone Right" };
+
+static const struct soc_enum stenl_mux_enum =
+       SOC_ENUM_SINGLE(M98090_REG_ADC_SIDETONE, M98090_DSTSL_SHIFT,
+               ARRAY_SIZE(stenl_mux_text), stenl_mux_text);
+
+static const struct soc_enum stenr_mux_enum =
+       SOC_ENUM_SINGLE(M98090_REG_ADC_SIDETONE, M98090_DSTSR_SHIFT,
+               ARRAY_SIZE(stenr_mux_text), stenr_mux_text);
+
+static const struct snd_kcontrol_new max98090_stenl_mux =
+       SOC_DAPM_ENUM("STENL Mux", stenl_mux_enum);
+
+static const struct snd_kcontrol_new max98090_stenr_mux =
+       SOC_DAPM_ENUM("STENR Mux", stenr_mux_enum);
+
+/* Left speaker mixer switch */
+static const struct
+       snd_kcontrol_new max98090_left_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_SPK_MIXER,
+               M98090_MIXSPL_DACL_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_SPK_MIXER,
+               M98090_MIXSPL_DACR_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_SPK_MIXER,
+               M98090_MIXSPL_LINEA_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_SPK_MIXER,
+               M98090_MIXSPL_LINEB_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_SPK_MIXER,
+               M98090_MIXSPL_MIC1_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_SPK_MIXER,
+               M98090_MIXSPL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right speaker mixer switch */
+static const struct
+       snd_kcontrol_new max98090_right_speaker_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_SPK_MIXER,
+               M98090_MIXSPR_DACL_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_SPK_MIXER,
+               M98090_MIXSPR_DACR_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_SPK_MIXER,
+               M98090_MIXSPR_LINEA_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_SPK_MIXER,
+               M98090_MIXSPR_LINEB_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_SPK_MIXER,
+               M98090_MIXSPR_MIC1_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_SPK_MIXER,
+               M98090_MIXSPR_MIC2_SHIFT, 1, 0),
+};
+
+/* Left headphone mixer switch */
+static const struct snd_kcontrol_new max98090_left_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_HP_MIXER,
+               M98090_MIXHPL_DACL_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_HP_MIXER,
+               M98090_MIXHPL_DACR_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_HP_MIXER,
+               M98090_MIXHPL_LINEA_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_HP_MIXER,
+               M98090_MIXHPL_LINEB_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_HP_MIXER,
+               M98090_MIXHPL_MIC1_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_HP_MIXER,
+               M98090_MIXHPL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right headphone mixer switch */
+static const struct snd_kcontrol_new max98090_right_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_HP_MIXER,
+               M98090_MIXHPR_DACL_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_HP_MIXER,
+               M98090_MIXHPR_DACR_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_HP_MIXER,
+               M98090_MIXHPR_LINEA_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_HP_MIXER,
+               M98090_MIXHPR_LINEB_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_HP_MIXER,
+               M98090_MIXHPR_MIC1_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_HP_MIXER,
+               M98090_MIXHPR_MIC2_SHIFT, 1, 0),
+};
+
+/* Left receiver mixer switch */
+static const struct snd_kcontrol_new max98090_left_rcv_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RCV_LOUTL_MIXER,
+               M98090_MIXRCVL_DACL_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RCV_LOUTL_MIXER,
+               M98090_MIXRCVL_DACR_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RCV_LOUTL_MIXER,
+               M98090_MIXRCVL_LINEA_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RCV_LOUTL_MIXER,
+               M98090_MIXRCVL_LINEB_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RCV_LOUTL_MIXER,
+               M98090_MIXRCVL_MIC1_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RCV_LOUTL_MIXER,
+               M98090_MIXRCVL_MIC2_SHIFT, 1, 0),
+};
+
+/* Right receiver mixer switch */
+static const struct snd_kcontrol_new max98090_right_rcv_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LOUTR_MIXER,
+               M98090_MIXRCVR_DACL_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LOUTR_MIXER,
+               M98090_MIXRCVR_DACR_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LOUTR_MIXER,
+               M98090_MIXRCVR_LINEA_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LOUTR_MIXER,
+               M98090_MIXRCVR_LINEB_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LOUTR_MIXER,
+               M98090_MIXRCVR_MIC1_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LOUTR_MIXER,
+               M98090_MIXRCVR_MIC2_SHIFT, 1, 0),
+};
+
+static const char *linmod_mux_text[] = { "Left Only", "Left and Right" };
+
+static const struct soc_enum linmod_mux_enum =
+       SOC_ENUM_SINGLE(M98090_REG_LOUTR_MIXER, M98090_LINMOD_SHIFT,
+               ARRAY_SIZE(linmod_mux_text), linmod_mux_text);
+
+static const struct snd_kcontrol_new max98090_linmod_mux =
+       SOC_DAPM_ENUM("LINMOD Mux", linmod_mux_enum);
+
+static const char *mixhpsel_mux_text[] = { "DAC Only", "HP Mixer" };
+
+/*
+ * This is a mux as it selects the HP output, but to DAPM it is a Mixer enable
+ */
+static const struct soc_enum mixhplsel_mux_enum =
+       SOC_ENUM_SINGLE(M98090_REG_HP_CONTROL, M98090_MIXHPLSEL_SHIFT,
+               ARRAY_SIZE(mixhpsel_mux_text), mixhpsel_mux_text);
+
+static const struct snd_kcontrol_new max98090_mixhplsel_mux =
+       SOC_DAPM_ENUM("MIXHPLSEL Mux", mixhplsel_mux_enum);
+
+static const struct soc_enum mixhprsel_mux_enum =
+       SOC_ENUM_SINGLE(M98090_REG_HP_CONTROL, M98090_MIXHPRSEL_SHIFT,
+               ARRAY_SIZE(mixhpsel_mux_text), mixhpsel_mux_text);
+
+static const struct snd_kcontrol_new max98090_mixhprsel_mux =
+       SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum);
+
+static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
+
+       SND_SOC_DAPM_INPUT("MIC1"),
+       SND_SOC_DAPM_INPUT("MIC2"),
+       SND_SOC_DAPM_INPUT("DMICL"),
+       SND_SOC_DAPM_INPUT("DMICR"),
+       SND_SOC_DAPM_INPUT("IN1"),
+       SND_SOC_DAPM_INPUT("IN2"),
+       SND_SOC_DAPM_INPUT("IN3"),
+       SND_SOC_DAPM_INPUT("IN4"),
+       SND_SOC_DAPM_INPUT("IN5"),
+       SND_SOC_DAPM_INPUT("IN6"),
+       SND_SOC_DAPM_INPUT("IN12"),
+       SND_SOC_DAPM_INPUT("IN34"),
+       SND_SOC_DAPM_INPUT("IN56"),
+
+       SND_SOC_DAPM_SUPPLY("MICBIAS", M98090_REG_INPUT_ENABLE,
+               M98090_MBEN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("SHDN", M98090_REG_DEVICE_SHUTDOWN,
+               M98090_SHDNN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("SDIEN", M98090_REG_IO_CONFIGURATION,
+               M98090_SDIEN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION,
+               M98090_SDOEN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+                M98090_DIGMICL_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+                M98090_DIGMICR_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG,
+               M98090_AHPF_SHIFT, 0, NULL, 0),
+
+/*
+ * Note: Sysclk and misc power supplies are taken care of by SHDN
+ */
+
+       SND_SOC_DAPM_MUX("MIC1 Mux", SND_SOC_NOPM,
+               0, 0, &max98090_mic1_mux),
+
+       SND_SOC_DAPM_MUX("MIC2 Mux", SND_SOC_NOPM,
+               0, 0, &max98090_mic2_mux),
+
+       SND_SOC_DAPM_PGA_E("MIC1 Input", M98090_REG_MIC1_INPUT_LEVEL,
+               M98090_MIC_PA1EN_SHIFT, 0, NULL, 0, max98090_micinput_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_PGA_E("MIC2 Input", M98090_REG_MIC2_INPUT_LEVEL,
+               M98090_MIC_PA2EN_SHIFT, 0, NULL, 0, max98090_micinput_event,
+               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_MIXER("LINEA Mixer", SND_SOC_NOPM, 0, 0,
+               &max98090_linea_mixer_controls[0],
+               ARRAY_SIZE(max98090_linea_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("LINEB Mixer", SND_SOC_NOPM, 0, 0,
+               &max98090_lineb_mixer_controls[0],
+               ARRAY_SIZE(max98090_lineb_mixer_controls)),
+
+       SND_SOC_DAPM_PGA("LINEA Input", M98090_REG_INPUT_ENABLE,
+               M98090_LINEAEN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("LINEB Input", M98090_REG_INPUT_ENABLE,
+               M98090_LINEBEN_SHIFT, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98090_left_adc_mixer_controls[0],
+               ARRAY_SIZE(max98090_left_adc_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+               &max98090_right_adc_mixer_controls[0],
+               ARRAY_SIZE(max98090_right_adc_mixer_controls)),
+
+       SND_SOC_DAPM_ADC("ADCL", NULL, M98090_REG_INPUT_ENABLE,
+               M98090_ADLEN_SHIFT, 0),
+       SND_SOC_DAPM_ADC("ADCR", NULL, M98090_REG_INPUT_ENABLE,
+               M98090_ADREN_SHIFT, 0),
+
+       SND_SOC_DAPM_AIF_OUT("AIFOUTL", "HiFi Capture", 0,
+               SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIFOUTR", "HiFi Capture", 1,
+               SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_MUX("LBENL Mux", SND_SOC_NOPM,
+               0, 0, &max98090_lbenl_mux),
+
+       SND_SOC_DAPM_MUX("LBENR Mux", SND_SOC_NOPM,
+               0, 0, &max98090_lbenr_mux),
+
+       SND_SOC_DAPM_MUX("LTENL Mux", SND_SOC_NOPM,
+               0, 0, &max98090_ltenl_mux),
+
+       SND_SOC_DAPM_MUX("LTENR Mux", SND_SOC_NOPM,
+               0, 0, &max98090_ltenr_mux),
+
+       SND_SOC_DAPM_MUX("STENL Mux", SND_SOC_NOPM,
+               0, 0, &max98090_stenl_mux),
+
+       SND_SOC_DAPM_MUX("STENR Mux", SND_SOC_NOPM,
+               0, 0, &max98090_stenr_mux),
+
+       SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_DAC("DACL", NULL, M98090_REG_OUTPUT_ENABLE,
+               M98090_DALEN_SHIFT, 0),
+       SND_SOC_DAPM_DAC("DACR", NULL, M98090_REG_OUTPUT_ENABLE,
+               M98090_DAREN_SHIFT, 0),
+
+       SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+               &max98090_left_hp_mixer_controls[0],
+               ARRAY_SIZE(max98090_left_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+               &max98090_right_hp_mixer_controls[0],
+               ARRAY_SIZE(max98090_right_hp_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0,
+               &max98090_left_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98090_left_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0,
+               &max98090_right_speaker_mixer_controls[0],
+               ARRAY_SIZE(max98090_right_speaker_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Left Receiver Mixer", SND_SOC_NOPM, 0, 0,
+               &max98090_left_rcv_mixer_controls[0],
+               ARRAY_SIZE(max98090_left_rcv_mixer_controls)),
+
+       SND_SOC_DAPM_MIXER("Right Receiver Mixer", SND_SOC_NOPM, 0, 0,
+               &max98090_right_rcv_mixer_controls[0],
+               ARRAY_SIZE(max98090_right_rcv_mixer_controls)),
+
+       SND_SOC_DAPM_MUX("LINMOD Mux", M98090_REG_LOUTR_MIXER,
+               M98090_LINMOD_SHIFT, 0, &max98090_linmod_mux),
+
+       SND_SOC_DAPM_MUX("MIXHPLSEL Mux", M98090_REG_HP_CONTROL,
+               M98090_MIXHPLSEL_SHIFT, 0, &max98090_mixhplsel_mux),
+
+       SND_SOC_DAPM_MUX("MIXHPRSEL Mux", M98090_REG_HP_CONTROL,
+               M98090_MIXHPRSEL_SHIFT, 0, &max98090_mixhprsel_mux),
+
+       SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE,
+               M98090_HPLEN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("HP Right Out", M98090_REG_OUTPUT_ENABLE,
+               M98090_HPREN_SHIFT, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("SPK Left Out", M98090_REG_OUTPUT_ENABLE,
+               M98090_SPLEN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("SPK Right Out", M98090_REG_OUTPUT_ENABLE,
+               M98090_SPREN_SHIFT, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("RCV Left Out", M98090_REG_OUTPUT_ENABLE,
+               M98090_RCVLEN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("RCV Right Out", M98090_REG_OUTPUT_ENABLE,
+               M98090_RCVREN_SHIFT, 0, NULL, 0),
+
        SND_SOC_DAPM_OUTPUT("HPL"),
        SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("SPKL"),
+       SND_SOC_DAPM_OUTPUT("SPKR"),
+       SND_SOC_DAPM_OUTPUT("RCVL"),
+       SND_SOC_DAPM_OUTPUT("RCVR"),
+};
+
+static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
 
-       /* PGA */
-       SND_SOC_DAPM_PGA("HPL Out", MAX98090_0x3F_OUT_ENABLE, 7, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("HPR Out", MAX98090_0x3F_OUT_ENABLE, 6, 0, NULL, 0),
+       SND_SOC_DAPM_INPUT("DMIC3"),
+       SND_SOC_DAPM_INPUT("DMIC4"),
 
-       /* Mixer */
-       SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0,
-                          max98090_left_hp_mixer_controls,
-                          ARRAY_SIZE(max98090_left_hp_mixer_controls)),
+       SND_SOC_DAPM_SUPPLY("DMIC3_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+                M98090_DIGMIC3_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DMIC4_ENA", M98090_REG_DIGITAL_MIC_ENABLE,
+                M98090_DIGMIC4_SHIFT, 0, NULL, 0),
+};
 
-       SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0,
-                          max98090_right_hp_mixer_controls,
-                          ARRAY_SIZE(max98090_right_hp_mixer_controls)),
+static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
+
+       {"MIC1 Input", NULL, "MIC1"},
+       {"MIC2 Input", NULL, "MIC2"},
+
+       {"DMICL", NULL, "DMICL_ENA"},
+       {"DMICR", NULL, "DMICR_ENA"},
+       {"DMICL", NULL, "AHPF"},
+       {"DMICR", NULL, "AHPF"},
+
+       /* MIC1 input mux */
+       {"MIC1 Mux", "IN12", "IN12"},
+       {"MIC1 Mux", "IN56", "IN56"},
+
+       /* MIC2 input mux */
+       {"MIC2 Mux", "IN34", "IN34"},
+       {"MIC2 Mux", "IN56", "IN56"},
+
+       {"MIC1 Input", NULL, "MIC1 Mux"},
+       {"MIC2 Input", NULL, "MIC2 Mux"},
+
+       /* Left ADC input mixer */
+       {"Left ADC Mixer", "IN12 Switch", "IN12"},
+       {"Left ADC Mixer", "IN34 Switch", "IN34"},
+       {"Left ADC Mixer", "IN56 Switch", "IN56"},
+       {"Left ADC Mixer", "LINEA Switch", "LINEA Input"},
+       {"Left ADC Mixer", "LINEB Switch", "LINEB Input"},
+       {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+
+       /* Right ADC input mixer */
+       {"Right ADC Mixer", "IN12 Switch", "IN12"},
+       {"Right ADC Mixer", "IN34 Switch", "IN34"},
+       {"Right ADC Mixer", "IN56 Switch", "IN56"},
+       {"Right ADC Mixer", "LINEA Switch", "LINEA Input"},
+       {"Right ADC Mixer", "LINEB Switch", "LINEB Input"},
+       {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"},
+
+       /* Line A input mixer */
+       {"LINEA Mixer", "IN1 Switch", "IN1"},
+       {"LINEA Mixer", "IN3 Switch", "IN3"},
+       {"LINEA Mixer", "IN5 Switch", "IN5"},
+       {"LINEA Mixer", "IN34 Switch", "IN34"},
+
+       /* Line B input mixer */
+       {"LINEB Mixer", "IN2 Switch", "IN2"},
+       {"LINEB Mixer", "IN4 Switch", "IN4"},
+       {"LINEB Mixer", "IN6 Switch", "IN6"},
+       {"LINEB Mixer", "IN56 Switch", "IN56"},
+
+       {"LINEA Input", NULL, "LINEA Mixer"},
+       {"LINEB Input", NULL, "LINEB Mixer"},
+
+       /* Inputs */
+       {"ADCL", NULL, "Left ADC Mixer"},
+       {"ADCR", NULL, "Right ADC Mixer"},
+       {"ADCL", NULL, "SHDN"},
+       {"ADCR", NULL, "SHDN"},
+
+       {"LBENL Mux", "Normal", "ADCL"},
+       {"LBENL Mux", "Normal", "DMICL"},
+       {"LBENL Mux", "Loopback", "LTENL Mux"},
+       {"LBENR Mux", "Normal", "ADCR"},
+       {"LBENR Mux", "Normal", "DMICR"},
+       {"LBENR Mux", "Loopback", "LTENR Mux"},
+
+       {"AIFOUTL", NULL, "LBENL Mux"},
+       {"AIFOUTR", NULL, "LBENR Mux"},
+       {"AIFOUTL", NULL, "SHDN"},
+       {"AIFOUTR", NULL, "SHDN"},
+       {"AIFOUTL", NULL, "SDOEN"},
+       {"AIFOUTR", NULL, "SDOEN"},
+
+       {"LTENL Mux", "Normal", "AIFINL"},
+       {"LTENL Mux", "Loopthrough", "LBENL Mux"},
+       {"LTENR Mux", "Normal", "AIFINR"},
+       {"LTENR Mux", "Loopthrough", "LBENR Mux"},
+
+       {"DACL", NULL, "LTENL Mux"},
+       {"DACR", NULL, "LTENR Mux"},
+
+       {"STENL Mux", "Sidetone Left", "ADCL"},
+       {"STENL Mux", "Sidetone Left", "DMICL"},
+       {"STENR Mux", "Sidetone Right", "ADCR"},
+       {"STENR Mux", "Sidetone Right", "DMICR"},
+       {"DACL", "NULL", "STENL Mux"},
+       {"DACR", "NULL", "STENL Mux"},
+
+       {"AIFINL", NULL, "SHDN"},
+       {"AIFINR", NULL, "SHDN"},
+       {"AIFINL", NULL, "SDIEN"},
+       {"AIFINR", NULL, "SDIEN"},
+       {"DACL", NULL, "SHDN"},
+       {"DACR", NULL, "SHDN"},
+
+       /* Left headphone output mixer */
+       {"Left Headphone Mixer", "Left DAC Switch", "DACL"},
+       {"Left Headphone Mixer", "Right DAC Switch", "DACR"},
+       {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left Headphone Mixer", "LINEA Switch", "LINEA Input"},
+       {"Left Headphone Mixer", "LINEB Switch", "LINEB Input"},
+
+       /* Right headphone output mixer */
+       {"Right Headphone Mixer", "Left DAC Switch", "DACL"},
+       {"Right Headphone Mixer", "Right DAC Switch", "DACR"},
+       {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right Headphone Mixer", "LINEA Switch", "LINEA Input"},
+       {"Right Headphone Mixer", "LINEB Switch", "LINEB Input"},
+
+       /* Left speaker output mixer */
+       {"Left Speaker Mixer", "Left DAC Switch", "DACL"},
+       {"Left Speaker Mixer", "Right DAC Switch", "DACR"},
+       {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left Speaker Mixer", "LINEA Switch", "LINEA Input"},
+       {"Left Speaker Mixer", "LINEB Switch", "LINEB Input"},
+
+       /* Right speaker output mixer */
+       {"Right Speaker Mixer", "Left DAC Switch", "DACL"},
+       {"Right Speaker Mixer", "Right DAC Switch", "DACR"},
+       {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right Speaker Mixer", "LINEA Switch", "LINEA Input"},
+       {"Right Speaker Mixer", "LINEB Switch", "LINEB Input"},
+
+       /* Left Receiver output mixer */
+       {"Left Receiver Mixer", "Left DAC Switch", "DACL"},
+       {"Left Receiver Mixer", "Right DAC Switch", "DACR"},
+       {"Left Receiver Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Left Receiver Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Left Receiver Mixer", "LINEA Switch", "LINEA Input"},
+       {"Left Receiver Mixer", "LINEB Switch", "LINEB Input"},
+
+       /* Right Receiver output mixer */
+       {"Right Receiver Mixer", "Left DAC Switch", "DACL"},
+       {"Right Receiver Mixer", "Right DAC Switch", "DACR"},
+       {"Right Receiver Mixer", "MIC1 Switch", "MIC1 Input"},
+       {"Right Receiver Mixer", "MIC2 Switch", "MIC2 Input"},
+       {"Right Receiver Mixer", "LINEA Switch", "LINEA Input"},
+       {"Right Receiver Mixer", "LINEB Switch", "LINEB Input"},
+
+       {"MIXHPLSEL Mux", "HP Mixer", "Left Headphone Mixer"},
+
+       /*
+        * Disable this for lowest power if bypassing
+        * the DAC with an analog signal
+        */
+       {"HP Left Out", NULL, "DACL"},
+       {"HP Left Out", NULL, "MIXHPLSEL Mux"},
+
+       {"MIXHPRSEL Mux", "HP Mixer", "Right Headphone Mixer"},
+
+       /*
+        * Disable this for lowest power if bypassing
+        * the DAC with an analog signal
+        */
+       {"HP Right Out", NULL, "DACR"},
+       {"HP Right Out", NULL, "MIXHPRSEL Mux"},
+
+       {"SPK Left Out", NULL, "Left Speaker Mixer"},
+       {"SPK Right Out", NULL, "Right Speaker Mixer"},
+       {"RCV Left Out", NULL, "Left Receiver Mixer"},
+
+       {"LINMOD Mux", "Left and Right", "Right Receiver Mixer"},
+       {"LINMOD Mux", "Left Only",  "Left Receiver Mixer"},
+       {"RCV Right Out", NULL, "LINMOD Mux"},
+
+       {"HPL", NULL, "HP Left Out"},
+       {"HPR", NULL, "HP Right Out"},
+       {"SPKL", NULL, "SPK Left Out"},
+       {"SPKR", NULL, "SPK Right Out"},
+       {"RCVL", NULL, "RCV Left Out"},
+       {"RCVR", NULL, "RCV Right Out"},
 
-       /* DAC */
-       SND_SOC_DAPM_DAC("DACL", "Hifi Playback", MAX98090_0x3F_OUT_ENABLE, 0, 0),
-       SND_SOC_DAPM_DAC("DACR", "Hifi Playback", MAX98090_0x3F_OUT_ENABLE, 1, 0),
 };
 
-static struct snd_soc_dapm_route max98090_audio_map[] = {
-       /* Output */
-       {"HPL", NULL, "HPL Out"},
-       {"HPR", NULL, "HPR Out"},
+static const struct snd_soc_dapm_route max98091_dapm_routes[] = {
+
+       /* DMIC inputs */
+       {"DMIC3", NULL, "DMIC3_ENA"},
+       {"DMIC4", NULL, "DMIC4_ENA"},
+       {"DMIC3", NULL, "AHPF"},
+       {"DMIC4", NULL, "AHPF"},
+
+};
+
+static int max98090_add_widgets(struct snd_soc_codec *codec)
+{
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+       snd_soc_add_codec_controls(codec, max98090_snd_controls,
+               ARRAY_SIZE(max98090_snd_controls));
 
-       /* PGA */
-       {"HPL Out", NULL, "HPL Mixer"},
-       {"HPR Out", NULL, "HPR Mixer"},
+       if (max98090->devtype == MAX98091) {
+               snd_soc_add_codec_controls(codec, max98091_snd_controls,
+                       ARRAY_SIZE(max98091_snd_controls));
+       }
+
+       snd_soc_dapm_new_controls(dapm, max98090_dapm_widgets,
+               ARRAY_SIZE(max98090_dapm_widgets));
+
+       snd_soc_dapm_add_routes(dapm, max98090_dapm_routes,
+               ARRAY_SIZE(max98090_dapm_routes));
+
+       if (max98090->devtype == MAX98091) {
+               snd_soc_dapm_new_controls(dapm, max98091_dapm_widgets,
+                       ARRAY_SIZE(max98091_dapm_widgets));
+
+               snd_soc_dapm_add_routes(dapm, max98091_dapm_routes,
+                       ARRAY_SIZE(max98091_dapm_routes));
+
+       }
 
-       /* Mixer*/
-       {"HPL Mixer", "DACR Switch", "DACR"},
-       {"HPL Mixer", "DACL Switch", "DACL"},
+       return 0;
+}
+
+static const int pclk_rates[] = {
+       12000000, 12000000, 13000000, 13000000,
+       16000000, 16000000, 19200000, 19200000
+};
+
+static const int lrclk_rates[] = {
+       8000, 16000, 8000, 16000,
+       8000, 16000, 8000, 16000
+};
 
-       {"HPR Mixer", "DACR Switch", "DACR"},
-       {"HPR Mixer", "DACL Switch", "DACL"},
+static const int user_pclk_rates[] = {
+       13000000, 13000000
 };
 
-static bool max98090_volatile(struct device *dev, unsigned int reg)
+static const int user_lrclk_rates[] = {
+       44100, 48000
+};
+
+static const unsigned long long ni_value[] = {
+       3528, 768
+};
+
+static const unsigned long long mi_value[] = {
+       8125, 1625
+};
+
+static void max98090_configure_bclk(struct snd_soc_codec *codec)
 {
-       if ((reg == MAX98090_0x01_INT_STS)      ||
-           (reg == MAX98090_0x02_JACK_STS)     ||
-           (reg >  MAX98090_REG_MAX_CACHED))
-               return true;
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       unsigned long long ni;
+       int i;
+
+       if (!max98090->sysclk) {
+               dev_err(codec->dev, "No SYSCLK configured\n");
+               return;
+       }
+
+       if (!max98090->bclk || !max98090->lrclk) {
+               dev_err(codec->dev, "No audio clocks configured\n");
+               return;
+       }
+
+       /* Skip configuration when operating as slave */
+       if (!(snd_soc_read(codec, M98090_REG_MASTER_MODE) &
+               M98090_MAS_MASK)) {
+               return;
+       }
 
-       return false;
+       /* Check for supported PCLK to LRCLK ratios */
+       for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) {
+               if ((pclk_rates[i] == max98090->sysclk) &&
+                       (lrclk_rates[i] == max98090->lrclk)) {
+                       dev_dbg(codec->dev,
+                               "Found supported PCLK to LRCLK rates 0x%x\n",
+                               i + 0x8);
+
+                       snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+                               M98090_FREQ_MASK,
+                               (i + 0x8) << M98090_FREQ_SHIFT);
+                       snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+                               M98090_USE_M1_MASK, 0);
+                       return;
+               }
+       }
+
+       /* Check for user calculated MI and NI ratios */
+       for (i = 0; i < ARRAY_SIZE(user_pclk_rates); i++) {
+               if ((user_pclk_rates[i] == max98090->sysclk) &&
+                       (user_lrclk_rates[i] == max98090->lrclk)) {
+                       dev_dbg(codec->dev,
+                               "Found user supported PCLK to LRCLK rates\n");
+                       dev_dbg(codec->dev, "i %d ni %lld mi %lld\n",
+                               i, ni_value[i], mi_value[i]);
+
+                       snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+                               M98090_FREQ_MASK, 0);
+                       snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+                               M98090_USE_M1_MASK,
+                                       1 << M98090_USE_M1_SHIFT);
+
+                       snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB,
+                               (ni_value[i] >> 8) & 0x7F);
+                       snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB,
+                               ni_value[i] & 0xFF);
+                       snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_MSB,
+                               (mi_value[i] >> 8) & 0x7F);
+                       snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_LSB,
+                               mi_value[i] & 0xFF);
+
+                       return;
+               }
+       }
+
+       /*
+        * Calculate based on MI = 65536 (not as good as either method above)
+        */
+       snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+               M98090_FREQ_MASK, 0);
+       snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+               M98090_USE_M1_MASK, 0);
+
+       /*
+        * Configure NI when operating as master
+        * Note: There is a small, but significant audio quality improvement
+        * by calculating ni and mi.
+        */
+       ni = 65536ULL * (max98090->lrclk < 50000 ? 96ULL : 48ULL)
+                       * (unsigned long long int)max98090->lrclk;
+       do_div(ni, (unsigned long long int)max98090->sysclk);
+       dev_info(codec->dev, "No better method found\n");
+       dev_info(codec->dev, "Calculating ni %lld with mi 65536\n", ni);
+       snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB,
+               (ni >> 8) & 0x7F);
+       snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, ni & 0xFF);
 }
 
-static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
-               struct snd_pcm_hw_params *params,
-               struct snd_soc_dai *dai)
+static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
+                                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = dai->codec;
-       unsigned int val;
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       struct max98090_cdata *cdata;
+       u8 regval;
+
+       max98090->dai_fmt = fmt;
+       cdata = &max98090->dai[0];
+
+       if (fmt != cdata->fmt) {
+               cdata->fmt = fmt;
+
+               regval = 0;
+               switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+               case SND_SOC_DAIFMT_CBS_CFS:
+                       /* Set to slave mode PLL - MAS mode off */
+                       snd_soc_write(codec,
+                               M98090_REG_CLOCK_RATIO_NI_MSB, 0x00);
+                       snd_soc_write(codec,
+                               M98090_REG_CLOCK_RATIO_NI_LSB, 0x00);
+                       snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
+                               M98090_USE_M1_MASK, 0);
+                       break;
+               case SND_SOC_DAIFMT_CBM_CFM:
+                       /* Set to master mode */
+                       if (max98090->tdm_slots == 4) {
+                               /* TDM */
+                               regval |= M98090_MAS_MASK |
+                                       M98090_BSEL_64;
+                       } else if (max98090->tdm_slots == 3) {
+                               /* TDM */
+                               regval |= M98090_MAS_MASK |
+                                       M98090_BSEL_48;
+                       } else {
+                               /* Few TDM slots, or No TDM */
+                               regval |= M98090_MAS_MASK |
+                                       M98090_BSEL_32;
+                       }
+                       break;
+               case SND_SOC_DAIFMT_CBS_CFM:
+               case SND_SOC_DAIFMT_CBM_CFS:
+               default:
+                       dev_err(codec->dev, "DAI clock mode unsupported");
+                       return -EINVAL;
+               }
+               snd_soc_write(codec, M98090_REG_MASTER_MODE, regval);
+
+               regval = 0;
+               switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+               case SND_SOC_DAIFMT_I2S:
+                       regval |= M98090_DLY_MASK;
+                       break;
+               case SND_SOC_DAIFMT_LEFT_J:
+                       break;
+               case SND_SOC_DAIFMT_RIGHT_J:
+                       regval |= M98090_RJ_MASK;
+                       break;
+               case SND_SOC_DAIFMT_DSP_A:
+                       /* Not supported mode */
+               default:
+                       dev_err(codec->dev, "DAI format unsupported");
+                       return -EINVAL;
+               }
+
+               switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+               case SND_SOC_DAIFMT_NB_NF:
+                       break;
+               case SND_SOC_DAIFMT_NB_IF:
+                       regval |= M98090_WCI_MASK;
+                       break;
+               case SND_SOC_DAIFMT_IB_NF:
+                       regval |= M98090_BCI_MASK;
+                       break;
+               case SND_SOC_DAIFMT_IB_IF:
+                       regval |= M98090_BCI_MASK|M98090_WCI_MASK;
+                       break;
+               default:
+                       dev_err(codec->dev, "DAI invert mode unsupported");
+                       return -EINVAL;
+               }
+
+               /*
+                * This accommodates an inverted logic in the MAX98090 chip
+                * for Bit Clock Invert (BCI). The inverted logic is only
+                * seen for the case of TDM mode. The remaining cases have
+                * normal logic.
+                */
+               if (max98090->tdm_slots > 1) {
+                       regval ^= M98090_BCI_MASK;
+               }
+
+               snd_soc_write(codec,
+                       M98090_REG_INTERFACE_FORMAT, regval);
+       }
 
-       switch (params_rate(params)) {
-       case 96000:
-               val = 1 << 5;
-               break;
-       case 32000:
-               val = 1 << 4;
-               break;
-       case 48000:
-               val = 1 << 3;
+       return 0;
+}
+
+static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai,
+       unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       struct max98090_cdata *cdata;
+       cdata = &max98090->dai[0];
+
+       if (slots < 0 || slots > 4)
+               return -EINVAL;
+
+       max98090->tdm_slots = slots;
+       max98090->tdm_width = slot_width;
+
+       if (max98090->tdm_slots > 1) {
+               /* SLOTL SLOTR SLOTDLY */
+               snd_soc_write(codec, M98090_REG_TDM_FORMAT,
+                       0 << M98090_TDM_SLOTL_SHIFT |
+                       1 << M98090_TDM_SLOTR_SHIFT |
+                       0 << M98090_TDM_SLOTDLY_SHIFT);
+
+               /* FSW TDM */
+               snd_soc_update_bits(codec, M98090_REG_TDM_CONTROL,
+                       M98090_TDM_MASK,
+                       M98090_TDM_MASK);
+       }
+
+       /*
+        * Normally advisable to set TDM first, but this permits either order
+        */
+       cdata->fmt = 0;
+       max98090_dai_set_fmt(codec_dai, max98090->dai_fmt);
+
+       return 0;
+}
+
+static int max98090_set_bias_level(struct snd_soc_codec *codec,
+                                  enum snd_soc_bias_level level)
+{
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       ret = regcache_sync(max98090->regmap);
+
+                       if (ret != 0) {
+                               dev_err(codec->dev,
+                                       "Failed to sync cache: %d\n", ret);
+                               return ret;
+                       }
+               }
+
+               if (max98090->jack_state == M98090_JACK_STATE_HEADSET) {
+                       /*
+                        * Set to normal bias level.
+                        */
+                       snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
+                               M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
+               }
                break;
-       case 44100:
-               val = 1 << 2;
+
+       case SND_SOC_BIAS_PREPARE:
                break;
-       case 16000:
-               val = 1 << 1;
+
+       case SND_SOC_BIAS_STANDBY:
+       case SND_SOC_BIAS_OFF:
+               /* Set internal pull-up to lowest power mode */
+               snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+                       M98090_JDWK_MASK, M98090_JDWK_MASK);
+               regcache_mark_dirty(max98090->regmap);
                break;
-       case 8000:
-               val = 1 << 0;
+       }
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+static const int comp_pclk_rates[] = {
+       11289600, 12288000, 12000000, 13000000, 19200000
+};
+
+static const int dmic_micclk[] = {
+       2, 2, 2, 2, 4, 2
+};
+
+static const int comp_lrclk_rates[] = {
+       8000, 16000, 32000, 44100, 48000, 96000
+};
+
+static const int dmic_comp[6][6] = {
+       {7, 8, 3, 3, 3, 3},
+       {7, 8, 3, 3, 3, 3},
+       {7, 8, 3, 3, 3, 3},
+       {7, 8, 3, 1, 1, 1},
+       {7, 8, 3, 1, 2, 2},
+       {7, 8, 3, 3, 3, 3}
+};
+
+static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params,
+                                  struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       struct max98090_cdata *cdata;
+       int i, j;
+
+       cdata = &max98090->dai[0];
+       max98090->bclk = snd_soc_params_to_bclk(params);
+       if (params_channels(params) == 1)
+               max98090->bclk *= 2;
+
+       max98090->lrclk = params_rate(params);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               snd_soc_update_bits(codec, M98090_REG_INTERFACE_FORMAT,
+                       M98090_WS_MASK, 0);
                break;
        default:
-               dev_err(codec->dev, "unsupported rate\n");
                return -EINVAL;
        }
-       snd_soc_update_bits(codec, MAX98090_0x05_SAMPLE_RATE, 0x03F, val);
+
+       max98090_configure_bclk(codec);
+
+       cdata->rate = max98090->lrclk;
+
+       /* Update filter mode */
+       if (max98090->lrclk < 24000)
+               snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+                       M98090_MODE_MASK, 0);
+       else
+               snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+                       M98090_MODE_MASK, M98090_MODE_MASK);
+
+       /* Update sample rate mode */
+       if (max98090->lrclk < 50000)
+               snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+                       M98090_DHF_MASK, 0);
+       else
+               snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
+                       M98090_DHF_MASK, M98090_DHF_MASK);
+
+       /* Check for supported PCLK to LRCLK ratios */
+       for (j = 0; j < ARRAY_SIZE(comp_pclk_rates); j++) {
+               if (comp_pclk_rates[j] == max98090->sysclk) {
+                       break;
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) {
+               if (max98090->lrclk <= (comp_lrclk_rates[i] +
+                       comp_lrclk_rates[i + 1]) / 2) {
+                       break;
+               }
+       }
+
+       snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_ENABLE,
+                       M98090_MICCLK_MASK,
+                       dmic_micclk[j] << M98090_MICCLK_SHIFT);
+
+       snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_CONFIG,
+                       M98090_DMIC_COMP_MASK,
+                       dmic_comp[j][i] << M98090_DMIC_COMP_SHIFT);
 
        return 0;
 }
 
+/*
+ * PLL / Sysclk
+ */
 static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
-               int clk_id, unsigned int freq, int dir)
+                                  int clk_id, unsigned int freq, int dir)
 {
        struct snd_soc_codec *codec = dai->codec;
-       unsigned int val;
-
-       snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN,
-                           MAX98090_SHDNRUN, 0);
-
-       switch (freq) {
-       case 26000000:
-               val = 1 << 7;
-               break;
-       case 19200000:
-               val = 1 << 6;
-               break;
-       case 13000000:
-               val = 1 << 5;
-               break;
-       case 12288000:
-               val = 1 << 4;
-               break;
-       case 12000000:
-               val = 1 << 3;
-               break;
-       case 11289600:
-               val = 1 << 2;
-               break;
-       default:
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+       /* Requested clock frequency is already setup */
+       if (freq == max98090->sysclk)
+               return 0;
+
+       /* Setup clocks for slave mode, and using the PLL
+        * PSCLK = 0x01 (when master clk is 10MHz to 20MHz)
+        *               0x02 (when master clk is 20MHz to 40MHz)..
+        *               0x03 (when master clk is 40MHz to 60MHz)..
+        */
+       if ((freq >= 10000000) && (freq < 20000000)) {
+               snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+                       M98090_PSCLK_DIV1);
+       } else if ((freq >= 20000000) && (freq < 40000000)) {
+               snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+                       M98090_PSCLK_DIV2);
+       } else if ((freq >= 40000000) && (freq < 60000000)) {
+               snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
+                       M98090_PSCLK_DIV4);
+       } else {
                dev_err(codec->dev, "Invalid master clock frequency\n");
                return -EINVAL;
        }
-       snd_soc_update_bits(codec, MAX98090_0x04_SYS_CLK, 0xFD, val);
 
-       snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN,
-                           MAX98090_SHDNRUN, MAX98090_SHDNRUN);
+       max98090->sysclk = freq;
 
-       dev_dbg(dai->dev, "sysclk is %uHz\n", freq);
+       max98090_configure_bclk(codec);
 
        return 0;
 }
 
-static int max98090_dai_set_fmt(struct snd_soc_dai *dai,
-                               unsigned int fmt)
+static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute)
 {
-       struct snd_soc_codec *codec = dai->codec;
-       int is_master;
-       u8 val;
+       struct snd_soc_codec *codec = codec_dai->codec;
+       int regval;
 
-       /* master/slave mode */
-       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-       case SND_SOC_DAIFMT_CBM_CFM:
-               is_master = 1;
-               break;
-       case SND_SOC_DAIFMT_CBS_CFS:
-               is_master = 0;
-               break;
-       default:
-               dev_err(codec->dev, "unsupported clock\n");
-               return -EINVAL;
+       regval = mute ? M98090_DVM_MASK : 0;
+       snd_soc_update_bits(codec, M98090_REG_DAI_PLAYBACK_LEVEL,
+               M98090_DVM_MASK, regval);
+
+       return 0;
+}
+
+static void max98090_jack_work(struct work_struct *work)
+{
+       struct max98090_priv *max98090 = container_of(work,
+               struct max98090_priv,
+               jack_work.work);
+       struct snd_soc_codec *codec = max98090->codec;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       int status = 0;
+       int reg;
+
+       /* Read a second time */
+       if (max98090->jack_state == M98090_JACK_STATE_NO_HEADSET) {
+
+               /* Strong pull up allows mic detection */
+               snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+                       M98090_JDWK_MASK, 0);
+
+               msleep(50);
+
+               reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
+
+               /* Weak pull up allows only insertion detection */
+               snd_soc_update_bits(codec, M98090_REG_JACK_DETECT,
+                       M98090_JDWK_MASK, M98090_JDWK_MASK);
+       } else {
+               reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
        }
 
-       /* format */
-       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-       case SND_SOC_DAIFMT_I2S:
-               val = (is_master) ? MAX98090_I2S_M : MAX98090_I2S_S;
-               break;
-       case SND_SOC_DAIFMT_RIGHT_J:
-               val = (is_master) ? MAX98090_RJ_M : MAX98090_RJ_S;
-               break;
-       case SND_SOC_DAIFMT_LEFT_J:
-               val = (is_master) ? MAX98090_LJ_M : MAX98090_LJ_S;
-               break;
-       default:
-               dev_err(codec->dev, "unsupported format\n");
-               return -EINVAL;
+       reg = snd_soc_read(codec, M98090_REG_JACK_STATUS);
+
+       switch (reg & (M98090_LSNS_MASK | M98090_JKSNS_MASK)) {
+               case M98090_LSNS_MASK | M98090_JKSNS_MASK:
+                       dev_dbg(codec->dev, "No Headset Detected\n");
+
+                       max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
+
+                       status |= 0;
+
+                       break;
+
+               case 0:
+                       if (max98090->jack_state ==
+                               M98090_JACK_STATE_HEADSET) {
+
+                               dev_dbg(codec->dev,
+                                       "Headset Button Down Detected\n");
+
+                               /*
+                                * max98090_headset_button_event(codec)
+                                * could be defined, then called here.
+                                */
+
+                               status |= SND_JACK_HEADSET;
+                               status |= SND_JACK_BTN_0;
+
+                               break;
+                       }
+
+                       /* Line is reported as Headphone */
+                       /* Nokia Headset is reported as Headphone */
+                       /* Mono Headphone is reported as Headphone */
+                       dev_dbg(codec->dev, "Headphone Detected\n");
+
+                       max98090->jack_state = M98090_JACK_STATE_HEADPHONE;
+
+                       status |= SND_JACK_HEADPHONE;
+
+                       break;
+
+               case M98090_JKSNS_MASK:
+                       dev_dbg(codec->dev, "Headset Detected\n");
+
+                       max98090->jack_state = M98090_JACK_STATE_HEADSET;
+
+                       status |= SND_JACK_HEADSET;
+
+                       break;
+
+               default:
+                       dev_dbg(codec->dev, "Unrecognized Jack Status\n");
+                       break;
+       }
+
+       snd_soc_jack_report(max98090->jack, status,
+                           SND_JACK_HEADSET | SND_JACK_BTN_0);
+
+       snd_soc_dapm_sync(dapm);
+}
+
+static irqreturn_t max98090_interrupt(int irq, void *data)
+{
+       struct snd_soc_codec *codec = data;
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+       unsigned int mask;
+       unsigned int active;
+
+       dev_dbg(codec->dev, "***** max98090_interrupt *****\n");
+
+       ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask);
+
+       if (ret != 0) {
+               dev_err(codec->dev,
+                       "failed to read M98090_REG_INTERRUPT_S: %d\n",
+                       ret);
+               return IRQ_NONE;
+       }
+
+       ret = regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &active);
+
+       if (ret != 0) {
+               dev_err(codec->dev,
+                       "failed to read M98090_REG_DEVICE_STATUS: %d\n",
+                       ret);
+               return IRQ_NONE;
+       }
+
+       dev_dbg(codec->dev, "active=0x%02x mask=0x%02x -> active=0x%02x\n",
+               active, mask, active & mask);
+
+       active &= mask;
+
+       if (!active)
+               return IRQ_NONE;
+
+       if (active & M98090_CLD_MASK) {
+               dev_err(codec->dev, "M98090_CLD_MASK\n");
+       }
+
+       if (active & M98090_SLD_MASK) {
+               dev_dbg(codec->dev, "M98090_SLD_MASK\n");
        }
-       snd_soc_update_bits(codec, MAX98090_0x06_DAI_IF,
-                           MAX98090_DAI_IF_MASK, val);
+
+       if (active & M98090_ULK_MASK) {
+               dev_err(codec->dev, "M98090_ULK_MASK\n");
+       }
+
+       if (active & M98090_JDET_MASK) {
+               dev_dbg(codec->dev, "M98090_JDET_MASK\n");
+
+               pm_wakeup_event(codec->dev, 100);
+
+               schedule_delayed_work(&max98090->jack_work,
+                       msecs_to_jiffies(100));
+       }
+
+       if (active & M98090_DRCACT_MASK) {
+               dev_dbg(codec->dev, "M98090_DRCACT_MASK\n");
+       }
+
+       if (active & M98090_DRCCLP_MASK) {
+               dev_err(codec->dev, "M98090_DRCCLP_MASK\n");
+       }
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * max98090_mic_detect - Enable microphone detection via the MAX98090 IRQ
+ *
+ * @codec:  MAX98090 codec
+ * @jack:   jack to report detection events on
+ *
+ * Enable microphone detection via IRQ on the MAX98090.  If GPIOs are
+ * being used to bring out signals to the processor then only platform
+ * data configuration is needed for MAX98090 and processor GPIOs should
+ * be configured using snd_soc_jack_add_gpios() instead.
+ *
+ * If no jack is supplied detection will be disabled.
+ */
+int max98090_mic_detect(struct snd_soc_codec *codec,
+       struct snd_soc_jack *jack)
+{
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+       dev_dbg(codec->dev, "max98090_mic_detect\n");
+
+       max98090->jack = jack;
+       if (jack) {
+               snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+                       M98090_IJDET_MASK,
+                       1 << M98090_IJDET_SHIFT);
+       } else {
+               snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S,
+                       M98090_IJDET_MASK,
+                       0);
+       }
+
+       /* Send an initial empty report */
+       snd_soc_jack_report(max98090->jack, 0,
+                           SND_JACK_HEADSET | SND_JACK_BTN_0);
+
+       schedule_delayed_work(&max98090->jack_work,
+               msecs_to_jiffies(100));
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(max98090_mic_detect);
 
 #define MAX98090_RATES SNDRV_PCM_RATE_8000_96000
 #define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
 
 static struct snd_soc_dai_ops max98090_dai_ops = {
-       .set_sysclk     = max98090_dai_set_sysclk,
-       .set_fmt        = max98090_dai_set_fmt,
-       .hw_params      = max98090_dai_hw_params,
+       .set_sysclk = max98090_dai_set_sysclk,
+       .set_fmt = max98090_dai_set_fmt,
+       .set_tdm_slot = max98090_set_tdm_slot,
+       .hw_params = max98090_dai_hw_params,
+       .digital_mute = max98090_dai_digital_mute,
 };
 
-static struct snd_soc_dai_driver max98090_dai = {
-       .name = "max98090-Hifi",
+static struct snd_soc_dai_driver max98090_dai[] = {
+{
+       .name = "HiFi",
        .playback = {
-               .stream_name    = "Playback",
-               .channels_min   = 1,
-               .channels_max   = 2,
-               .rates          = MAX98090_RATES,
-               .formats        = MAX98090_FORMATS,
+               .stream_name = "HiFi Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = MAX98090_RATES,
+               .formats = MAX98090_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX98090_RATES,
+               .formats = MAX98090_FORMATS,
        },
-       .ops = &max98090_dai_ops,
+        .ops = &max98090_dai_ops,
+}
 };
 
+static void max98090_handle_pdata(struct snd_soc_codec *codec)
+{
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       struct max98090_pdata *pdata = max98090->pdata;
+
+       if (!pdata) {
+               dev_err(codec->dev, "No platform data\n");
+               return;
+       }
+
+}
+
 static int max98090_probe(struct snd_soc_codec *codec)
 {
-       struct max98090_priv *priv = snd_soc_codec_get_drvdata(codec);
-       struct device *dev = codec->dev;
-       int ret;
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+       struct max98090_cdata *cdata;
+       int ret = 0;
+
+       dev_dbg(codec->dev, "max98090_probe\n");
+
+       max98090->codec = codec;
+
+       codec->control_data = max98090->regmap;
 
-       codec->control_data = priv->regmap;
        ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
-       if (ret < 0) {
-               dev_err(dev, "Failed to set cache I/O: %d\n", ret);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
                return ret;
        }
 
-       /* Device active */
-       snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN,
-                           MAX98090_SHDNRUN, MAX98090_SHDNRUN);
+       /* Reset the codec, the DSP core, and disable all interrupts */
+       max98090_reset(max98090);
 
-       return 0;
+       /* Initialize private data */
+
+       max98090->sysclk = (unsigned)-1;
+
+       cdata = &max98090->dai[0];
+       cdata->rate = (unsigned)-1;
+       cdata->fmt  = (unsigned)-1;
+
+       max98090->lin_state = 0;
+       max98090->pa1en = 0;
+       max98090->pa2en = 0;
+       max98090->extmic_mux = 0;
+
+       ret = snd_soc_read(codec, M98090_REG_REVISION_ID);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to read device revision: %d\n",
+                       ret);
+               goto err_access;
+       }
+
+       if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) {
+               max98090->devtype = MAX98090;
+               dev_info(codec->dev, "MAX98090 REVID=0x%02x\n", ret);
+       } else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) {
+               max98090->devtype = MAX98091;
+               dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret);
+       } else {
+               max98090->devtype = MAX98090;
+               dev_err(codec->dev, "Unrecognized revision 0x%02x\n", ret);
+       }
+
+       max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
+
+       INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work);
+
+       /* Enable jack detection */
+       snd_soc_write(codec, M98090_REG_JACK_DETECT,
+               M98090_JDETEN_MASK | M98090_JDEB_25MS);
+
+       /* Register for interrupts */
+       dev_dbg(codec->dev, "irq = %d\n", max98090->irq);
+
+       ret = request_threaded_irq(max98090->irq, NULL,
+               max98090_interrupt, IRQF_TRIGGER_FALLING,
+               "max98090_interrupt", codec);
+       if (ret < 0) {
+               dev_err(codec->dev, "request_irq failed: %d\n",
+                       ret);
+       }
+
+       /*
+        * Clear any old interrupts.
+        * An old interrupt ocurring prior to installing the ISR
+        * can keep a new interrupt from generating a trigger.
+        */
+       snd_soc_read(codec, M98090_REG_DEVICE_STATUS);
+
+       /* High Performance is default */
+       snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL,
+               M98090_DACHP_MASK,
+               1 << M98090_DACHP_SHIFT);
+       snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL,
+               M98090_PERFMODE_MASK,
+               0 << M98090_PERFMODE_SHIFT);
+       snd_soc_update_bits(codec, M98090_REG_ADC_CONTROL,
+               M98090_ADCHP_MASK,
+               1 << M98090_ADCHP_SHIFT);
+
+       /* Turn on VCM bandgap reference */
+       snd_soc_write(codec, M98090_REG_BIAS_CONTROL,
+               M98090_VCM_MODE_MASK);
+
+       max98090_handle_pdata(codec);
+
+       max98090_add_widgets(codec);
+
+err_access:
+       return ret;
 }
 
 static int max98090_remove(struct snd_soc_codec *codec)
 {
+       struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
+
+       cancel_delayed_work_sync(&max98090->jack_work);
+
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_max98090 = {
-       .probe                  = max98090_probe,
-       .remove                 = max98090_remove,
-       .controls               = max98090_snd_controls,
-       .num_controls           = ARRAY_SIZE(max98090_snd_controls),
-       .dapm_widgets           = max98090_dapm_widgets,
-       .num_dapm_widgets       = ARRAY_SIZE(max98090_dapm_widgets),
-       .dapm_routes            = max98090_audio_map,
-       .num_dapm_routes        = ARRAY_SIZE(max98090_audio_map),
+       .probe   = max98090_probe,
+       .remove  = max98090_remove,
+       .set_bias_level = max98090_set_bias_level,
 };
 
 static const struct regmap_config max98090_regmap = {
-       .reg_bits               = 8,
-       .val_bits               = 8,
-       .max_register           = MAX98090_REG_END,
-       .volatile_reg           = max98090_volatile,
-       .cache_type             = REGCACHE_RBTREE,
-       .reg_defaults           = max98090_reg_defaults,
-       .num_reg_defaults       = ARRAY_SIZE(max98090_reg_defaults),
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = MAX98090_MAX_REGISTER,
+       .reg_defaults = max98090_reg,
+       .num_reg_defaults = ARRAY_SIZE(max98090_reg),
+       .volatile_reg = max98090_volatile_register,
+       .readable_reg = max98090_readable_register,
+       .cache_type = REGCACHE_RBTREE,
 };
 
 static int max98090_i2c_probe(struct i2c_client *i2c,
-                             const struct i2c_device_id *id)
+                                const struct i2c_device_id *id)
 {
-       struct max98090_priv *priv;
-       struct device *dev = &i2c->dev;
-       unsigned int val;
+       struct max98090_priv *max98090;
        int ret;
 
-       priv = devm_kzalloc(dev, sizeof(struct max98090_priv),
-                           GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       priv->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap);
-       if (IS_ERR(priv->regmap)) {
-               ret = PTR_ERR(priv->regmap);
-               dev_err(dev, "Failed to init regmap: %d\n", ret);
-               return ret;
-       }
+       pr_debug("max98090_i2c_probe\n");
 
-       i2c_set_clientdata(i2c, priv);
+       max98090 = devm_kzalloc(&i2c->dev, sizeof(struct max98090_priv),
+               GFP_KERNEL);
+       if (max98090 == NULL)
+               return -ENOMEM;
 
-       ret = regmap_read(priv->regmap, MAX98090_0xFF_REV_ID, &val);
-       if (ret < 0) {
-               dev_err(dev, "Failed to read device revision: %d\n", ret);
-               return ret;
+       max98090->devtype = id->driver_data;
+       i2c_set_clientdata(i2c, max98090);
+       max98090->control_data = i2c;
+       max98090->pdata = i2c->dev.platform_data;
+       max98090->irq = i2c->irq;
+
+       max98090->regmap = regmap_init_i2c(i2c, &max98090_regmap);
+       if (IS_ERR(max98090->regmap)) {
+               ret = PTR_ERR(max98090->regmap);
+               dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+               goto err_enable;
        }
-       dev_info(dev, "revision 0x%02x\n", val);
 
-       ret = snd_soc_register_codec(dev,
-                                    &soc_codec_dev_max98090,
-                                    &max98090_dai, 1);
+       ret = snd_soc_register_codec(&i2c->dev,
+                       &soc_codec_dev_max98090, max98090_dai,
+                       ARRAY_SIZE(max98090_dai));
+       if (ret < 0)
+               regmap_exit(max98090->regmap);
 
+err_enable:
        return ret;
 }
 
 static int max98090_i2c_remove(struct i2c_client *client)
 {
+       struct max98090_priv *max98090 = dev_get_drvdata(&client->dev);
        snd_soc_unregister_codec(&client->dev);
+       regmap_exit(max98090->regmap);
+       return 0;
+}
+
+static int max98090_runtime_resume(struct device *dev)
+{
+       struct max98090_priv *max98090 = dev_get_drvdata(dev);
+
+       regcache_cache_only(max98090->regmap, false);
+
+       regcache_sync(max98090->regmap);
+
        return 0;
 }
 
+static int max98090_runtime_suspend(struct device *dev)
+{
+       struct max98090_priv *max98090 = dev_get_drvdata(dev);
+
+       regcache_cache_only(max98090->regmap, true);
+
+       return 0;
+}
+
+static struct dev_pm_ops max98090_pm = {
+       SET_RUNTIME_PM_OPS(max98090_runtime_suspend,
+               max98090_runtime_resume, NULL)
+};
+
 static const struct i2c_device_id max98090_i2c_id[] = {
-       { "max98090", 0 },
+       { "max98090", MAX98090 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
@@ -565,13 +2384,15 @@ static struct i2c_driver max98090_i2c_driver = {
        .driver = {
                .name = "max98090",
                .owner = THIS_MODULE,
+               .pm = &max98090_pm,
        },
-       .probe          = max98090_i2c_probe,
-       .remove         = max98090_i2c_remove,
-       .id_table       = max98090_i2c_id,
+       .probe  = max98090_i2c_probe,
+       .remove = max98090_i2c_remove,
+       .id_table = max98090_i2c_id,
 };
+
 module_i2c_driver(max98090_i2c_driver);
 
 MODULE_DESCRIPTION("ALSA SoC MAX98090 driver");
-MODULE_AUTHOR("Peter Hsiang, Kuninori Morimoto");
+MODULE_AUTHOR("Peter Hsiang, Jesse Marroqin, Jerry Wong");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h
new file mode 100755 (executable)
index 0000000..7e103f2
--- /dev/null
@@ -0,0 +1,1549 @@
+/*
+ * max98090.h -- MAX98090 ALSA SoC Audio driver
+ *
+ * Copyright 2011-2012 Maxim Integrated Products
+ *
+ * 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 _MAX98090_H
+#define _MAX98090_H
+
+#include <linux/version.h>
+
+/* One can override the Linux version here with an explicit version number */
+#define M98090_LINUX_VERSION LINUX_VERSION_CODE
+
+/*
+ * MAX98090 Register Definitions
+ */
+
+#define M98090_REG_SOFTWARE_RESET              0x00
+#define M98090_REG_DEVICE_STATUS               0x01
+#define M98090_REG_JACK_STATUS                 0x02
+#define M98090_REG_INTERRUPT_S                 0x03
+#define M98090_REG_QUICK_SYSTEM_CLOCK          0x04
+#define M98090_REG_QUICK_SAMPLE_RATE           0x05
+#define M98090_REG_DAI_INTERFACE               0x06
+#define M98090_REG_DAC_PATH                    0x07
+#define M98090_REG_MIC_DIRECT_TO_ADC           0x08
+#define M98090_REG_LINE_TO_ADC                 0x09
+#define M98090_REG_ANALOG_MIC_LOOP             0x0A
+#define M98090_REG_ANALOG_LINE_LOOP            0x0B
+#define M98090_REG_RESERVED                    0x0C
+#define M98090_REG_LINE_INPUT_CONFIG           0x0D
+#define M98090_REG_LINE_INPUT_LEVEL            0x0E
+#define M98090_REG_INPUT_MODE                  0x0F
+#define M98090_REG_MIC1_INPUT_LEVEL            0x10
+#define M98090_REG_MIC2_INPUT_LEVEL            0x11
+#define M98090_REG_MIC_BIAS_VOLTAGE            0x12
+#define M98090_REG_DIGITAL_MIC_ENABLE          0x13
+#define M98090_REG_DIGITAL_MIC_CONFIG          0x14
+#define M98090_REG_LEFT_ADC_MIXER              0x15
+#define M98090_REG_RIGHT_ADC_MIXER             0x16
+#define M98090_REG_LEFT_ADC_LEVEL              0x17
+#define M98090_REG_RIGHT_ADC_LEVEL             0x18
+#define M98090_REG_ADC_BIQUAD_LEVEL            0x19
+#define M98090_REG_ADC_SIDETONE                        0x1A
+#define M98090_REG_SYSTEM_CLOCK                        0x1B
+#define M98090_REG_CLOCK_MODE                  0x1C
+#define M98090_REG_CLOCK_RATIO_NI_MSB          0x1D
+#define M98090_REG_CLOCK_RATIO_NI_LSB          0x1E
+#define M98090_REG_CLOCK_RATIO_MI_MSB          0x1F
+#define M98090_REG_CLOCK_RATIO_MI_LSB          0x20
+#define M98090_REG_MASTER_MODE                 0x21
+#define M98090_REG_INTERFACE_FORMAT            0x22
+#define M98090_REG_TDM_CONTROL                 0x23
+#define M98090_REG_TDM_FORMAT                  0x24
+#define M98090_REG_IO_CONFIGURATION            0x25
+#define M98090_REG_FILTER_CONFIG               0x26
+#define M98090_REG_DAI_PLAYBACK_LEVEL          0x27
+#define M98090_REG_DAI_PLAYBACK_LEVEL_EQ       0x28
+#define M98090_REG_LEFT_HP_MIXER               0x29
+#define M98090_REG_RIGHT_HP_MIXER              0x2A
+#define M98090_REG_HP_CONTROL                  0x2B
+#define M98090_REG_LEFT_HP_VOLUME              0x2C
+#define M98090_REG_RIGHT_HP_VOLUME             0x2D
+#define M98090_REG_LEFT_SPK_MIXER              0x2E
+#define M98090_REG_RIGHT_SPK_MIXER             0x2F
+#define M98090_REG_SPK_CONTROL                 0x30
+#define M98090_REG_LEFT_SPK_VOLUME             0x31
+#define M98090_REG_RIGHT_SPK_VOLUME            0x32
+#define M98090_REG_DRC_TIMING                  0x33
+#define M98090_REG_DRC_COMPRESSOR              0x34
+#define M98090_REG_DRC_EXPANDER                        0x35
+#define M98090_REG_DRC_GAIN                    0x36
+#define M98090_REG_RCV_LOUTL_MIXER             0x37
+#define M98090_REG_RCV_LOUTL_CONTROL           0x38
+#define M98090_REG_RCV_LOUTL_VOLUME            0x39
+#define M98090_REG_LOUTR_MIXER                 0x3A
+#define M98090_REG_LOUTR_CONTROL               0x3B
+#define M98090_REG_LOUTR_VOLUME                        0x3C
+#define M98090_REG_JACK_DETECT                 0x3D
+#define M98090_REG_INPUT_ENABLE                        0x3E
+#define M98090_REG_OUTPUT_ENABLE               0x3F
+#define M98090_REG_LEVEL_CONTROL               0x40
+#define M98090_REG_DSP_FILTER_ENABLE           0x41
+#define M98090_REG_BIAS_CONTROL                        0x42
+#define M98090_REG_DAC_CONTROL                 0x43
+#define M98090_REG_ADC_CONTROL                 0x44
+#define M98090_REG_DEVICE_SHUTDOWN             0x45
+#define M98090_REG_EQUALIZER_BASE              0x46
+#define M98090_REG_RECORD_BIQUAD_BASE          0xAF
+#define M98090_REG_DMIC3_VOLUME                        0xBE
+#define M98090_REG_DMIC4_VOLUME                        0xBF
+#define M98090_REG_DMIC34_BQ_PREATTEN          0xC0
+#define M98090_REG_RECORD_TDM_SLOT             0xC1
+#define M98090_REG_SAMPLE_RATE                 0xC2
+#define M98090_REG_DMIC34_BIQUAD_BASE          0xC3
+#define M98090_REG_REVISION_ID                 0xFF
+
+#define M98090_REG_CNT                         (0xFF+1)
+#define MAX98090_MAX_REGISTER                  0xFF
+
+/* MAX98090 Register Bit Fields */
+
+/*
+ * M98090_REG_SOFTWARE_RESET
+ */
+#define M98090_SWRESET_MASK            (1<<7)
+#define M98090_SWRESET_SHIFT           7
+#define M98090_SWRESET_WIDTH           1
+
+/*
+ * M98090_REG_DEVICE_STATUS
+ */
+#define M98090_CLD_MASK                        (1<<7)
+#define M98090_CLD_SHIFT               7
+#define M98090_CLD_WIDTH               1
+#define M98090_SLD_MASK                        (1<<6)
+#define M98090_SLD_SHIFT               6
+#define M98090_SLD_WIDTH               1
+#define M98090_ULK_MASK                        (1<<5)
+#define M98090_ULK_SHIFT               5
+#define M98090_ULK_WIDTH               1
+#define M98090_JDET_MASK               (1<<2)
+#define M98090_JDET_SHIFT              2
+#define M98090_JDET_WIDTH              1
+#define M98090_DRCACT_MASK             (1<<1)
+#define M98090_DRCACT_SHIFT            1
+#define M98090_DRCACT_WIDTH            1
+#define M98090_DRCCLP_MASK             (1<<0)
+#define M98090_DRCCLP_SHIFT            0
+#define M98090_DRCCLP_WIDTH            1
+
+/*
+ * M98090_REG_JACK_STATUS
+ */
+#define M98090_LSNS_MASK               (1<<2)
+#define M98090_LSNS_SHIFT              2
+#define M98090_LSNS_WIDTH              1
+#define M98090_JKSNS_MASK              (1<<1)
+#define M98090_JKSNS_SHIFT             1
+#define M98090_JKSNS_WIDTH             1
+
+/*
+ * M98090_REG_INTERRUPT_S
+ */
+#define M98090_ICLD_MASK               (1<<7)
+#define M98090_ICLD_SHIFT              7
+#define M98090_ICLD_WIDTH              1
+#define M98090_ISLD_MASK               (1<<6)
+#define M98090_ISLD_SHIFT              6
+#define M98090_ISLD_WIDTH              1
+#define M98090_IULK_MASK               (1<<5)
+#define M98090_IULK_SHIFT              5
+#define M98090_IULK_WIDTH              1
+#define M98090_IJDET_MASK              (1<<2)
+#define M98090_IJDET_SHIFT             2
+#define M98090_IJDET_WIDTH             1
+#define M98090_IDRCACT_MASK            (1<<1)
+#define M98090_IDRCACT_SHIFT           1
+#define M98090_IDRCACT_WIDTH           1
+#define M98090_IDRCCLP_MASK            (1<<0)
+#define M98090_IDRCCLP_SHIFT           0
+#define M98090_IDRCCLP_WIDTH           1
+
+/*
+ * M98090_REG_QUICK_SYSTEM_CLOCK
+ */
+#define M98090_26M_MASK                        (1<<7)
+#define M98090_26M_SHIFT               7
+#define M98090_26M_WIDTH               1
+#define M98090_19P2M_MASK              (1<<6)
+#define M98090_19P2M_SHIFT             6
+#define M98090_19P2M_WIDTH             1
+#define M98090_13M_MASK                        (1<<5)
+#define M98090_13M_SHIFT               5
+#define M98090_13M_WIDTH               1
+#define M98090_12P288M_MASK            (1<<4)
+#define M98090_12P288M_SHIFT           4
+#define M98090_12P288M_WIDTH           1
+#define M98090_12M_MASK                        (1<<3)
+#define M98090_12M_SHIFT               3
+#define M98090_12M_WIDTH               1
+#define M98090_11P2896M_MASK           (1<<2)
+#define M98090_11P2896M_SHIFT          2
+#define M98090_11P2896M_WIDTH          1
+#define M98090_256FS_MASK              (1<<0)
+#define M98090_256FS_SHIFT             0
+#define M98090_256FS_WIDTH             1
+#define M98090_CLK_ALL_SHIFT           0
+#define M98090_CLK_ALL_WIDTH           8
+#define M98090_CLK_ALL_NUM             (1<<M98090_CLK_ALL_WIDTH)
+
+/*
+ * M98090_REG_QUICK_SAMPLE_RATE
+ */
+#define M98090_SR_96K_MASK             (1<<5)
+#define M98090_SR_96K_SHIFT            5
+#define M98090_SR_96K_WIDTH            1
+#define M98090_SR_32K_MASK             (1<<4)
+#define M98090_SR_32K_SHIFT            4
+#define M98090_SR_32K_WIDTH            1
+#define M98090_SR_48K_MASK             (1<<3)
+#define M98090_SR_48K_SHIFT            3
+#define M98090_SR_48K_WIDTH            1
+#define M98090_SR_44K1_MASK            (1<<2)
+#define M98090_SR_44K1_SHIFT           2
+#define M98090_SR_44K1_WIDTH           1
+#define M98090_SR_16K_MASK             (1<<1)
+#define M98090_SR_16K_SHIFT            1
+#define M98090_SR_16K_WIDTH            1
+#define M98090_SR_8K_MASK              (1<<0)
+#define M98090_SR_8K_SHIFT             0
+#define M98090_SR_8K_WIDTH             1
+#define M98090_SR_MASK                 0x3F
+#define M98090_SR_ALL_SHIFT            0
+#define M98090_SR_ALL_WIDTH            8
+#define M98090_SR_ALL_NUM              (1<<M98090_SR_ALL_WIDTH)
+
+/*
+ * M98090_REG_DAI_INTERFACE
+ */
+#define M98090_RJ_M_MASK               (1<<5)
+#define M98090_RJ_M_SHIFT              5
+#define M98090_RJ_M_WIDTH              1
+#define M98090_RJ_S_MASK               (1<<4)
+#define M98090_RJ_S_SHIFT              4
+#define M98090_RJ_S_WIDTH              1
+#define M98090_LJ_M_MASK               (1<<3)
+#define M98090_LJ_M_SHIFT              3
+#define M98090_LJ_M_WIDTH              1
+#define M98090_LJ_S_MASK               (1<<2)
+#define M98090_LJ_S_SHIFT              2
+#define M98090_LJ_S_WIDTH              1
+#define M98090_I2S_M_MASK              (1<<1)
+#define M98090_I2S_M_SHIFT             1
+#define M98090_I2S_M_WIDTH             1
+#define M98090_I2S_S_MASK              (1<<0)
+#define M98090_I2S_S_SHIFT             0
+#define M98090_I2S_S_WIDTH             1
+#define M98090_DAI_ALL_SHIFT           0
+#define M98090_DAI_ALL_WIDTH           8
+#define M98090_DAI_ALL_NUM             (1<<M98090_DAI_ALL_WIDTH)
+
+/*
+ * M98090_REG_DAC_PATH
+ */
+#define M98090_DIG2_HP_MASK            (1<<7)
+#define M98090_DIG2_HP_SHIFT           7
+#define M98090_DIG2_HP_WIDTH           1
+#define M98090_DIG2_EAR_MASK           (1<<6)
+#define M98090_DIG2_EAR_SHIFT          6
+#define M98090_DIG2_EAR_WIDTH          1
+#define M98090_DIG2_SPK_MASK           (1<<5)
+#define M98090_DIG2_SPK_SHIFT          5
+#define M98090_DIG2_SPK_WIDTH          1
+#define M98090_DIG2_LOUT_MASK          (1<<4)
+#define M98090_DIG2_LOUT_SHIFT         4
+#define M98090_DIG2_LOUT_WIDTH         1
+#define M98090_DIG2_ALL_SHIFT          0
+#define M98090_DIG2_ALL_WIDTH          8
+#define M98090_DIG2_ALL_NUM            (1<<M98090_DIG2_ALL_WIDTH)
+
+/*
+ * M98090_REG_MIC_DIRECT_TO_ADC
+ */
+#define M98090_IN12_MIC1_MASK          (1<<7)
+#define M98090_IN12_MIC1_SHIFT         7
+#define M98090_IN12_MIC1_WIDTH         1
+#define M98090_IN34_MIC2_MASK          (1<<6)
+#define M98090_IN34_MIC2_SHIFT         6
+#define M98090_IN34_MIC2_WIDTH         1
+#define M98090_IN56_MIC1_MASK          (1<<5)
+#define M98090_IN56_MIC1_SHIFT         5
+#define M98090_IN56_MIC1_WIDTH         1
+#define M98090_IN56_MIC2_MASK          (1<<4)
+#define M98090_IN56_MIC2_SHIFT         4
+#define M98090_IN56_MIC2_WIDTH         1
+#define M98090_IN12_DADC_MASK          (1<<3)
+#define M98090_IN12_DADC_SHIFT         3
+#define M98090_IN12_DADC_WIDTH         1
+#define M98090_IN34_DADC_MASK          (1<<2)
+#define M98090_IN34_DADC_SHIFT         2
+#define M98090_IN34_DADC_WIDTH         1
+#define M98090_IN56_DADC_MASK          (1<<1)
+#define M98090_IN56_DADC_SHIFT         1
+#define M98090_IN56_DADC_WIDTH         1
+#define M98090_MIC_ALL_SHIFT           0
+#define M98090_MIC_ALL_WIDTH           8
+#define M98090_MIC_ALL_NUM             (1<<M98090_MIC_ALL_WIDTH)
+
+/*
+ * M98090_REG_LINE_TO_ADC
+ */
+#define M98090_IN12S_AB_MASK           (1<<7)
+#define M98090_IN12S_AB_SHIFT          7
+#define M98090_IN12S_AB_WIDTH          1
+#define M98090_IN34S_AB_MASK           (1<<6)
+#define M98090_IN34S_AB_SHIFT          6
+#define M98090_IN34S_AB_WIDTH          1
+#define M98090_IN56S_AB_MASK           (1<<5)
+#define M98090_IN56S_AB_SHIFT          5
+#define M98090_IN56S_AB_WIDTH          1
+#define M98090_IN34D_A_MASK            (1<<4)
+#define M98090_IN34D_A_SHIFT           4
+#define M98090_IN34D_A_WIDTH           1
+#define M98090_IN56D_B_MASK            (1<<3)
+#define M98090_IN56D_B_SHIFT           3
+#define M98090_IN56D_B_WIDTH           1
+#define M98090_LINE_ALL_SHIFT          0
+#define M98090_LINE_ALL_WIDTH          8
+#define M98090_LINE_ALL_NUM            (1<<M98090_LINE_ALL_WIDTH)
+
+/*
+ * M98090_REG_ANALOG_MIC_LOOP
+ */
+#define M98090_IN12_M1HPL_MASK         (1<<7)
+#define M98090_IN12_M1HPL_SHIFT                7
+#define M98090_IN12_M1HPL_WIDTH                1
+#define M98090_IN12_M1SPKL_MASK                (1<<6)
+#define M98090_IN12_M1SPKL_SHIFT       6
+#define M98090_IN12_M1SPKL_WIDTH       1
+#define M98090_IN12_M1EAR_MASK         (1<<5)
+#define M98090_IN12_M1EAR_SHIFT                5
+#define M98090_IN12_M1EAR_WIDTH                1
+#define M98090_IN12_M1LOUTL_MASK       (1<<4)
+#define M98090_IN12_M1LOUTL_SHIFT      4
+#define M98090_IN12_M1LOUTL_WIDTH      1
+#define M98090_IN34_M2HPR_MASK         (1<<3)
+#define M98090_IN34_M2HPR_SHIFT                3
+#define M98090_IN34_M2HPR_WIDTH                1
+#define M98090_IN34_M2SPKR_MASK                (1<<2)
+#define M98090_IN34_M2SPKR_SHIFT       2
+#define M98090_IN34_M2SPKR_WIDTH       1
+#define M98090_IN34_M2EAR_MASK         (1<<1)
+#define M98090_IN34_M2EAR_SHIFT                1
+#define M98090_IN34_M2EAR_WIDTH                1
+#define M98090_IN34_M2LOUTR_MASK       (1<<0)
+#define M98090_IN34_M2LOUTR_SHIFT      0
+#define M98090_IN34_M2LOUTR_WIDTH      1
+#define M98090_AMIC_ALL_SHIFT          0
+#define M98090_AMIC_ALL_WIDTH          8
+#define M98090_AMIC_ALL_NUM            (1<<M98090_AMIC_ALL_WIDTH)
+
+/*
+ * M98090_REG_ANALOG_LINE_LOOP
+ */
+#define M98090_IN12S_ABHP_MASK         (1<<7)
+#define M98090_IN12S_ABHP_SHIFT                7
+#define M98090_IN12S_ABHP_WIDTH                1
+#define M98090_IN34D_ASPKL_MASK                (1<<6)
+#define M98090_IN34D_ASPKL_SHIFT       6
+#define M98090_IN34D_ASPKL_WIDTH       1
+#define M98090_IN34D_AEAR_MASK         (1<<5)
+#define M98090_IN34D_AEAR_SHIFT                5
+#define M98090_IN34D_AEAR_WIDTH                1
+#define M98090_IN12S_ABLOUT_MASK       (1<<4)
+#define M98090_IN12S_ABLOUT_SHIFT      4
+#define M98090_IN12S_ABLOUT_WIDTH      1
+#define M98090_IN34S_ABHP_MASK         (1<<3)
+#define M98090_IN34S_ABHP_SHIFT                3
+#define M98090_IN34S_ABHP_WIDTH                1
+#define M98090_IN56D_BSPKR_MASK                (1<<2)
+#define M98090_IN56D_BSPKR_SHIFT       2
+#define M98090_IN56D_BSPKR_WIDTH       1
+#define M98090_IN56D_BEAR_MASK         (1<<1)
+#define M98090_IN56D_BEAR_SHIFT                1
+#define M98090_IN56D_BEAR_WIDTH                1
+#define M98090_IN34S_ABLOUT_MASK       (1<<0)
+#define M98090_IN34S_ABLOUT_SHIFT      0
+#define M98090_IN34S_ABLOUT_WIDTH      1
+#define M98090_ALIN_ALL_SHIFT          0
+#define M98090_ALIN_ALL_WIDTH          8
+#define M98090_ALIN_ALL_NUM            (1<<M98090_ALIN_ALL_WIDTH)
+
+/*
+ * M98090_REG_RESERVED
+ */
+
+/*
+ * M98090_REG_LINE_INPUT_CONFIG
+ */
+#define M98090_IN34DIFF_MASK           (1<<7)
+#define M98090_IN34DIFF_SHIFT          7
+#define M98090_IN34DIFF_WIDTH          1
+#define M98090_IN56DIFF_MASK           (1<<6)
+#define M98090_IN56DIFF_SHIFT          6
+#define M98090_IN56DIFF_WIDTH          1
+#define M98090_IN1SEEN_MASK            (1<<5)
+#define M98090_IN1SEEN_SHIFT           5
+#define M98090_IN1SEEN_WIDTH           1
+#define M98090_IN2SEEN_MASK            (1<<4)
+#define M98090_IN2SEEN_SHIFT           4
+#define M98090_IN2SEEN_WIDTH           1
+#define M98090_IN3SEEN_MASK            (1<<3)
+#define M98090_IN3SEEN_SHIFT           3
+#define M98090_IN3SEEN_WIDTH           1
+#define M98090_IN4SEEN_MASK            (1<<2)
+#define M98090_IN4SEEN_SHIFT           2
+#define M98090_IN4SEEN_WIDTH           1
+#define M98090_IN5SEEN_MASK            (1<<1)
+#define M98090_IN5SEEN_SHIFT           1
+#define M98090_IN5SEEN_WIDTH           1
+#define M98090_IN6SEEN_MASK            (1<<0)
+#define M98090_IN6SEEN_SHIFT           0
+#define M98090_IN6SEEN_WIDTH           1
+
+/*
+ * M98090_REG_LINE_INPUT_LEVEL
+ */
+#define M98090_MIXG135_MASK            (1<<7)
+#define M98090_MIXG135_SHIFT           7
+#define M98090_MIXG135_WIDTH           1
+#define M98090_MIXG135_NUM             (1<<M98090_MIXG135_WIDTH)
+#define M98090_MIXG246_MASK            (1<<6)
+#define M98090_MIXG246_SHIFT           6
+#define M98090_MIXG246_WIDTH           1
+#define M98090_MIXG246_NUM             (1<<M98090_MIXG246_WIDTH)
+#define M98090_LINAPGA_MASK            (7<<3)
+#define M98090_LINAPGA_SHIFT           3
+#define M98090_LINAPGA_WIDTH           3
+#define M98090_LINAPGA_NUM             6
+#define M98090_LINBPGA_MASK            (7<<0)
+#define M98090_LINBPGA_SHIFT           0
+#define M98090_LINBPGA_WIDTH           3
+#define M98090_LINBPGA_NUM             6
+
+/*
+ * M98090_REG_INPUT_MODE
+ */
+#define M98090_EXTBUFA_MASK            (1<<7)
+#define M98090_EXTBUFA_SHIFT           7
+#define M98090_EXTBUFA_WIDTH           1
+#define M98090_EXTBUFA_NUM             (1<<M98090_EXTBUFA_WIDTH)
+#define M98090_EXTBUFB_MASK            (1<<6)
+#define M98090_EXTBUFB_SHIFT           6
+#define M98090_EXTBUFB_WIDTH           1
+#define M98090_EXTBUFB_NUM             (1<<M98090_EXTBUFB_WIDTH)
+#define M98090_EXTMIC_MASK             (3<<0)
+#define M98090_EXTMIC_SHIFT            0
+#define M98090_EXTMIC1_SHIFT           0
+#define M98090_EXTMIC2_SHIFT           1
+#define M98090_EXTMIC_WIDTH            2
+#define M98090_EXTMIC_NONE             (0<<0)
+#define M98090_EXTMIC_MIC1             (1<<0)
+#define M98090_EXTMIC_MIC2             (2<<0)
+
+/*
+ * M98090_REG_MIC1_INPUT_LEVEL
+ */
+#define M98090_MIC_PA1EN_MASK          (3<<5)
+#define M98090_MIC_PA1EN_SHIFT         5
+#define M98090_MIC_PA1EN_WIDTH         2
+#define M98090_MIC_PA1EN_NUM           3
+#define M98090_MIC_PGAM1_MASK          (31<<0)
+#define M98090_MIC_PGAM1_SHIFT         0
+#define M98090_MIC_PGAM1_WIDTH         5
+#define M98090_MIC_PGAM1_NUM           21
+
+/*
+ * M98090_REG_MIC2_INPUT_LEVEL
+ */
+#define M98090_MIC_PA2EN_MASK          (3<<5)
+#define M98090_MIC_PA2EN_SHIFT         5
+#define M98090_MIC_PA2EN_WIDTH         2
+#define M98090_MIC_PA2EN_NUM           3
+#define M98090_MIC_PGAM2_MASK          (31<<0)
+#define M98090_MIC_PGAM2_SHIFT         0
+#define M98090_MIC_PGAM2_WIDTH         5
+#define M98090_MIC_PGAM2_NUM           21
+
+/*
+ * M98090_REG_MIC_BIAS_VOLTAGE
+ */
+#define M98090_MBVSEL_MASK             (3<<0)
+#define M98090_MBVSEL_SHIFT            0
+#define M98090_MBVSEL_WIDTH            2
+#define M98090_MBVSEL_2V8              (3<<0)
+#define M98090_MBVSEL_2V55             (2<<0)
+#define M98090_MBVSEL_2V4              (1<<0)
+#define M98090_MBVSEL_2V2              (0<<0)
+
+/*
+ * M98090_REG_DIGITAL_MIC_ENABLE
+ */
+#define M98090_MICCLK_MASK             (7<<4)
+#define M98090_MICCLK_SHIFT            4
+#define M98090_MICCLK_WIDTH            3
+#define M98090_DIGMIC4_MASK            (1<<3)
+#define M98090_DIGMIC4_SHIFT           3
+#define M98090_DIGMIC4_WIDTH           1
+#define M98090_DIGMIC4_NUM             (1<<M98090_DIGMIC4_WIDTH)
+#define M98090_DIGMIC3_MASK            (1<<2)
+#define M98090_DIGMIC3_SHIFT           2
+#define M98090_DIGMIC3_WIDTH           1
+#define M98090_DIGMIC3_NUM             (1<<M98090_DIGMIC3_WIDTH)
+#define M98090_DIGMICR_MASK            (1<<1)
+#define M98090_DIGMICR_SHIFT           1
+#define M98090_DIGMICR_WIDTH           1
+#define M98090_DIGMICR_NUM             (1<<M98090_DIGMICR_WIDTH)
+#define M98090_DIGMICL_MASK            (1<<0)
+#define M98090_DIGMICL_SHIFT           0
+#define M98090_DIGMICL_WIDTH           1
+#define M98090_DIGMICL_NUM             (1<<M98090_DIGMICL_WIDTH)
+
+/*
+ * M98090_REG_DIGITAL_MIC_CONFIG
+ */
+#define M98090_DMIC_COMP_MASK          (15<<4)
+#define M98090_DMIC_COMP_SHIFT         4
+#define M98090_DMIC_COMP_WIDTH         4
+#define M98090_DMIC_COMP_NUM           (1<<M98090_DMIC_COMP_WIDTH)
+#define M98090_DMIC_FREQ_MASK          (3<<0)
+#define M98090_DMIC_FREQ_SHIFT         0
+#define M98090_DMIC_FREQ_WIDTH         2
+
+/*
+ * M98090_REG_LEFT_ADC_MIXER
+ */
+#define M98090_MIXADL_MIC2_MASK                (1<<6)
+#define M98090_MIXADL_MIC2_SHIFT       6
+#define M98090_MIXADL_MIC2_WIDTH       1
+#define M98090_MIXADL_MIC1_MASK                (1<<5)
+#define M98090_MIXADL_MIC1_SHIFT       5
+#define M98090_MIXADL_MIC1_WIDTH       1
+#define M98090_MIXADL_LINEB_MASK       (1<<4)
+#define M98090_MIXADL_LINEB_SHIFT      4
+#define M98090_MIXADL_LINEB_WIDTH      1
+#define M98090_MIXADL_LINEA_MASK       (1<<3)
+#define M98090_MIXADL_LINEA_SHIFT      3
+#define M98090_MIXADL_LINEA_WIDTH      1
+#define M98090_MIXADL_IN65DIFF_MASK    (1<<2)
+#define M98090_MIXADL_IN65DIFF_SHIFT   2
+#define M98090_MIXADL_IN65DIFF_WIDTH   1
+#define M98090_MIXADL_IN34DIFF_MASK    (1<<1)
+#define M98090_MIXADL_IN34DIFF_SHIFT   1
+#define M98090_MIXADL_IN34DIFF_WIDTH   1
+#define M98090_MIXADL_IN12DIFF_MASK    (1<<0)
+#define M98090_MIXADL_IN12DIFF_SHIFT   0
+#define M98090_MIXADL_IN12DIFF_WIDTH   1
+#define M98090_MIXADL_MASK             (255<<0)
+#define M98090_MIXADL_SHIFT            0
+#define M98090_MIXADL_WIDTH            8
+
+/*
+ * M98090_REG_RIGHT_ADC_MIXER
+ */
+#define M98090_MIXADR_MIC2_MASK                (1<<6)
+#define M98090_MIXADR_MIC2_SHIFT       6
+#define M98090_MIXADR_MIC2_WIDTH       1
+#define M98090_MIXADR_MIC1_MASK                (1<<5)
+#define M98090_MIXADR_MIC1_SHIFT       5
+#define M98090_MIXADR_MIC1_WIDTH       1
+#define M98090_MIXADR_LINEB_MASK       (1<<4)
+#define M98090_MIXADR_LINEB_SHIFT      4
+#define M98090_MIXADR_LINEB_WIDTH      1
+#define M98090_MIXADR_LINEA_MASK       (1<<3)
+#define M98090_MIXADR_LINEA_SHIFT      3
+#define M98090_MIXADR_LINEA_WIDTH      1
+#define M98090_MIXADR_IN65DIFF_MASK    (1<<2)
+#define M98090_MIXADR_IN65DIFF_SHIFT   2
+#define M98090_MIXADR_IN65DIFF_WIDTH   1
+#define M98090_MIXADR_IN34DIFF_MASK    (1<<1)
+#define M98090_MIXADR_IN34DIFF_SHIFT   1
+#define M98090_MIXADR_IN34DIFF_WIDTH   1
+#define M98090_MIXADR_IN12DIFF_MASK    (1<<0)
+#define M98090_MIXADR_IN12DIFF_SHIFT   0
+#define M98090_MIXADR_IN12DIFF_WIDTH   1
+#define M98090_MIXADR_MASK             (255<<0)
+#define M98090_MIXADR_SHIFT            0
+#define M98090_MIXADR_WIDTH            8
+
+/*
+ * M98090_REG_LEFT_ADC_LEVEL
+ */
+#define M98090_AVLG_MASK               (7<<4)
+#define M98090_AVLG_SHIFT              4
+#define M98090_AVLG_WIDTH              3
+#define M98090_AVLG_NUM                        (1<<M98090_AVLG_WIDTH)
+#define M98090_AVL_MASK                        (15<<0)
+#define M98090_AVL_SHIFT               0
+#define M98090_AVL_WIDTH               4
+#define M98090_AVL_NUM                 (1<<M98090_AVL_WIDTH)
+
+/*
+ * M98090_REG_RIGHT_ADC_LEVEL
+ */
+#define M98090_AVRG_MASK               (7<<4)
+#define M98090_AVRG_SHIFT              4
+#define M98090_AVRG_WIDTH              3
+#define M98090_AVRG_NUM                        (1<<M98090_AVRG_WIDTH)
+#define M98090_AVR_MASK                        (15<<0)
+#define M98090_AVR_SHIFT               0
+#define M98090_AVR_WIDTH               4
+#define M98090_AVR_NUM                 (1<<M98090_AVR_WIDTH)
+
+/*
+ * M98090_REG_ADC_BIQUAD_LEVEL
+ */
+#define M98090_AVBQ_MASK               (15<<0)
+#define M98090_AVBQ_SHIFT              0
+#define M98090_AVBQ_WIDTH              4
+#define M98090_AVBQ_NUM                        (1<<M98090_AVBQ_WIDTH)
+
+/*
+ * M98090_REG_ADC_SIDETONE
+ */
+#define M98090_DSTSR_MASK              (1<<7)
+#define M98090_DSTSR_SHIFT             7
+#define M98090_DSTSR_WIDTH             1
+#define M98090_DSTSL_MASK              (1<<6)
+#define M98090_DSTSL_SHIFT             6
+#define M98090_DSTSL_WIDTH             1
+#define M98090_DVST_MASK               (31<<0)
+#define M98090_DVST_SHIFT              0
+#define M98090_DVST_WIDTH              5
+#define M98090_DVST_NUM                        31
+
+/*
+ * M98090_REG_SYSTEM_CLOCK
+ */
+#define M98090_PSCLK_MASK              (3<<4)
+#define M98090_PSCLK_SHIFT             4
+#define M98090_PSCLK_WIDTH             2
+#define M98090_PSCLK_DISABLED          (0<<4)
+#define M98090_PSCLK_DIV1              (1<<4)
+#define M98090_PSCLK_DIV2              (2<<4)
+#define M98090_PSCLK_DIV4              (3<<4)
+
+/*
+ * M98090_REG_CLOCK_MODE
+ */
+#define M98090_FREQ_MASK               (15<<4)
+#define M98090_FREQ_SHIFT              4
+#define M98090_FREQ_WIDTH              4
+#define M98090_USE_M1_MASK             (1<<0)
+#define M98090_USE_M1_SHIFT            0
+#define M98090_USE_M1_WIDTH            1
+#define M98090_USE_M1_NUM              (1<<M98090_USE_M1_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_NI_MSB
+ */
+#define M98090_NI_HI_MASK              (127<<0)
+#define M98090_NI_HI_SHIFT             0
+#define M98090_NI_HI_WIDTH             7
+#define M98090_NI_HI_NUM               (1<<M98090_NI_HI_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_NI_LSB
+ */
+#define M98090_NI_LO_MASK              (255<<0)
+#define M98090_NI_LO_SHIFT             0
+#define M98090_NI_LO_WIDTH             8
+#define M98090_NI_LO_NUM               (1<<M98090_NI_LO_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_MI_MSB
+ */
+#define M98090_MI_HI_MASK              (255<<0)
+#define M98090_MI_HI_SHIFT             0
+#define M98090_MI_HI_WIDTH             8
+#define M98090_MI_HI_NUM               (1<<M98090_MI_HI_WIDTH)
+
+/*
+ * M98090_REG_CLOCK_RATIO_MI_LSB
+ */
+#define M98090_MI_LO_MASK              (255<<0)
+#define M98090_MI_LO_SHIFT             0
+#define M98090_MI_LO_WIDTH             8
+#define M98090_MI_LO_NUM               (1<<M98090_MI_LO_WIDTH)
+
+/*
+ * M98090_REG_MASTER_MODE
+ */
+#define M98090_MAS_MASK                        (1<<7)
+#define M98090_MAS_SHIFT               7
+#define M98090_MAS_WIDTH               1
+#define M98090_BSEL_MASK               (1<<0)
+#define M98090_BSEL_SHIFT              0
+#define M98090_BSEL_WIDTH              1
+#define M98090_BSEL_32                 (1<<0)
+#define M98090_BSEL_48                 (2<<0)
+#define M98090_BSEL_64                 (3<<0)
+
+/*
+ * M98090_REG_INTERFACE_FORMAT
+ */
+#define M98090_RJ_MASK                 (1<<5)
+#define M98090_RJ_SHIFT                        5
+#define M98090_RJ_WIDTH                        1
+#define M98090_WCI_MASK                        (1<<4)
+#define M98090_WCI_SHIFT               4
+#define M98090_WCI_WIDTH               1
+#define M98090_BCI_MASK                        (1<<3)
+#define M98090_BCI_SHIFT               3
+#define M98090_BCI_WIDTH               1
+#define M98090_DLY_MASK                        (1<<2)
+#define M98090_DLY_SHIFT               2
+#define M98090_DLY_WIDTH               1
+#define M98090_WS_MASK                 (3<<0)
+#define M98090_WS_SHIFT                        0
+#define M98090_WS_WIDTH                        2
+#define M98090_WS_NUM                  (1<<M98090_WS_WIDTH)
+
+/*
+ * M98090_REG_TDM_CONTROL
+ */
+#define M98090_FSW_MASK                        (1<<1)
+#define M98090_FSW_SHIFT               1
+#define M98090_FSW_WIDTH               1
+#define M98090_TDM_MASK                        (1<<0)
+#define M98090_TDM_SHIFT               0
+#define M98090_TDM_WIDTH               1
+#define M98090_TDM_NUM                 (1<<M98090_TDM_WIDTH)
+
+/*
+ * M98090_REG_TDM_FORMAT
+ */
+#define M98090_TDM_SLOTL_MASK          (3<<6)
+#define M98090_TDM_SLOTL_SHIFT         6
+#define M98090_TDM_SLOTL_WIDTH         2
+#define M98090_TDM_SLOTL_NUM           (1<<M98090_TDM_SLOTL_WIDTH)
+#define M98090_TDM_SLOTR_MASK          (3<<4)
+#define M98090_TDM_SLOTR_SHIFT         4
+#define M98090_TDM_SLOTR_WIDTH         2
+#define M98090_TDM_SLOTR_NUM           (1<<M98090_TDM_SLOTR_WIDTH)
+#define M98090_TDM_SLOTDLY_MASK                (15<<0)
+#define M98090_TDM_SLOTDLY_SHIFT       0
+#define M98090_TDM_SLOTDLY_WIDTH       4
+#define M98090_TDM_SLOTDLY_NUM         (1<<M98090_TDM_SLOTDLY_WIDTH)
+
+/*
+ * M98090_REG_IO_CONFIGURATION
+ */
+#define M98090_LTEN_MASK               (1<<5)
+#define M98090_LTEN_SHIFT              5
+#define M98090_LTEN_WIDTH              1
+#define M98090_LTEN_NUM                        (1<<M98090_LTEN_WIDTH)
+#define M98090_LBEN_MASK               (1<<4)
+#define M98090_LBEN_SHIFT              4
+#define M98090_LBEN_WIDTH              1
+#define M98090_LBEN_NUM                        (1<<M98090_LBEN_WIDTH)
+#define M98090_DMONO_MASK              (1<<3)
+#define M98090_DMONO_SHIFT             3
+#define M98090_DMONO_WIDTH             1
+#define M98090_DMONO_NUM               (1<<M98090_DMONO_WIDTH)
+#define M98090_HIZOFF_MASK             (1<<2)
+#define M98090_HIZOFF_SHIFT            2
+#define M98090_HIZOFF_WIDTH            1
+#define M98090_HIZOFF_NUM              (1<<M98090_HIZOFF_WIDTH)
+#define M98090_SDOEN_MASK              (1<<1)
+#define M98090_SDOEN_SHIFT             1
+#define M98090_SDOEN_WIDTH             1
+#define M98090_SDOEN_NUM               (1<<M98090_SDOEN_WIDTH)
+#define M98090_SDIEN_MASK              (1<<0)
+#define M98090_SDIEN_SHIFT             0
+#define M98090_SDIEN_WIDTH             1
+#define M98090_SDIEN_NUM               (1<<M98090_SDIEN_WIDTH)
+
+/*
+ * M98090_REG_FILTER_CONFIG
+ */
+#define M98090_MODE_MASK               (1<<7)
+#define M98090_MODE_SHIFT              7
+#define M98090_MODE_WIDTH              1
+#define M98090_AHPF_MASK               (1<<6)
+#define M98090_AHPF_SHIFT              6
+#define M98090_AHPF_WIDTH              1
+#define M98090_AHPF_NUM                        (1<<M98090_AHPF_WIDTH)
+#define M98090_DHPF_MASK               (1<<5)
+#define M98090_DHPF_SHIFT              5
+#define M98090_DHPF_WIDTH              1
+#define M98090_DHPF_NUM                        (1<<M98090_DHPF_WIDTH)
+#define M98090_DHF_MASK                        (1<<4)
+#define M98090_DHF_SHIFT               4
+#define M98090_DHF_WIDTH               1
+#define M98090_FLT_DMIC34MODE_MASK     (1<<3)
+#define M98090_FLT_DMIC34MODE_SHIFT    3
+#define M98090_FLT_DMIC34MODE_WIDTH    1
+#define M98090_FLT_DMIC34HPF_MASK      (1<<2)
+#define M98090_FLT_DMIC34HPF_SHIFT     2
+#define M98090_FLT_DMIC34HPF_WIDTH     1
+#define M98090_FLT_DMIC34HPF_NUM       (1<<M98090_FLT_DMIC34HPF_WIDTH)
+
+/*
+ * M98090_REG_DAI_PLAYBACK_LEVEL
+ */
+#define M98090_DVM_MASK                        (1<<7)
+#define M98090_DVM_SHIFT               7
+#define M98090_DVM_WIDTH               1
+#define M98090_DVG_MASK                        (3<<4)
+#define M98090_DVG_SHIFT               4
+#define M98090_DVG_WIDTH               2
+#define M98090_DVG_NUM                 (1<<M98090_DVG_WIDTH)
+#define M98090_DV_MASK                 (15<<0)
+#define M98090_DV_SHIFT                        0
+#define M98090_DV_WIDTH                        4
+#define M98090_DV_NUM                  (1<<M98090_DV_WIDTH)
+
+/*
+ * M98090_REG_DAI_PLAYBACK_LEVEL_EQ
+ */
+#define M98090_EQCLPN_MASK             (1<<4)
+#define M98090_EQCLPN_SHIFT            4
+#define M98090_EQCLPN_WIDTH            1
+#define M98090_EQCLPN_NUM              (1<<M98090_EQCLPN_WIDTH)
+#define M98090_DVEQ_MASK               (15<<0)
+#define M98090_DVEQ_SHIFT              0
+#define M98090_DVEQ_WIDTH              4
+#define M98090_DVEQ_NUM                        (1<<M98090_DVEQ_WIDTH)
+
+/*
+ * M98090_REG_LEFT_HP_MIXER
+ */
+#define M98090_MIXHPL_MIC2_MASK                (1<<5)
+#define M98090_MIXHPL_MIC2_SHIFT       5
+#define M98090_MIXHPL_MIC2_WIDTH       1
+#define M98090_MIXHPL_MIC1_MASK                (1<<4)
+#define M98090_MIXHPL_MIC1_SHIFT       4
+#define M98090_MIXHPL_MIC1_WIDTH       1
+#define M98090_MIXHPL_LINEB_MASK       (1<<3)
+#define M98090_MIXHPL_LINEB_SHIFT      3
+#define M98090_MIXHPL_LINEB_WIDTH      1
+#define M98090_MIXHPL_LINEA_MASK       (1<<2)
+#define M98090_MIXHPL_LINEA_SHIFT      2
+#define M98090_MIXHPL_LINEA_WIDTH      1
+#define M98090_MIXHPL_DACR_MASK                (1<<1)
+#define M98090_MIXHPL_DACR_SHIFT       1
+#define M98090_MIXHPL_DACR_WIDTH       1
+#define M98090_MIXHPL_DACL_MASK                (1<<0)
+#define M98090_MIXHPL_DACL_SHIFT       0
+#define M98090_MIXHPL_DACL_WIDTH       1
+#define M98090_MIXHPL_MASK             (63<<0)
+#define M98090_MIXHPL_SHIFT            0
+#define M98090_MIXHPL_WIDTH            6
+
+/*
+ * M98090_REG_RIGHT_HP_MIXER
+ */
+#define M98090_MIXHPR_MIC2_MASK                (1<<5)
+#define M98090_MIXHPR_MIC2_SHIFT       5
+#define M98090_MIXHPR_MIC2_WIDTH       1
+#define M98090_MIXHPR_MIC1_MASK                (1<<4)
+#define M98090_MIXHPR_MIC1_SHIFT       4
+#define M98090_MIXHPR_MIC1_WIDTH       1
+#define M98090_MIXHPR_LINEB_MASK       (1<<3)
+#define M98090_MIXHPR_LINEB_SHIFT      3
+#define M98090_MIXHPR_LINEB_WIDTH      1
+#define M98090_MIXHPR_LINEA_MASK       (1<<2)
+#define M98090_MIXHPR_LINEA_SHIFT      2
+#define M98090_MIXHPR_LINEA_WIDTH      1
+#define M98090_MIXHPR_DACR_MASK                (1<<1)
+#define M98090_MIXHPR_DACR_SHIFT       1
+#define M98090_MIXHPR_DACR_WIDTH       1
+#define M98090_MIXHPR_DACL_MASK                (1<<0)
+#define M98090_MIXHPR_DACL_SHIFT       0
+#define M98090_MIXHPR_DACL_WIDTH       1
+#define M98090_MIXHPR_MASK             (63<<0)
+#define M98090_MIXHPR_SHIFT            0
+#define M98090_MIXHPR_WIDTH            6
+
+/*
+ * M98090_REG_HP_CONTROL
+ */
+#define M98090_MIXHPRSEL_MASK          (1<<5)
+#define M98090_MIXHPRSEL_SHIFT         5
+#define M98090_MIXHPRSEL_WIDTH         1
+#define M98090_MIXHPLSEL_MASK          (1<<4)
+#define M98090_MIXHPLSEL_SHIFT         4
+#define M98090_MIXHPLSEL_WIDTH         1
+#define M98090_MIXHPRG_MASK            (3<<2)
+#define M98090_MIXHPRG_SHIFT           2
+#define M98090_MIXHPRG_WIDTH           2
+#define M98090_MIXHPRG_NUM             (1<<M98090_MIXHPRG_WIDTH)
+#define M98090_MIXHPLG_MASK            (3<<0)
+#define M98090_MIXHPLG_SHIFT           0
+#define M98090_MIXHPLG_WIDTH           2
+#define M98090_MIXHPLG_NUM             (1<<M98090_MIXHPLG_WIDTH)
+
+/*
+ * M98090_REG_LEFT_HP_VOLUME
+ */
+#define M98090_HPLM_MASK               (1<<7)
+#define M98090_HPLM_SHIFT              7
+#define M98090_HPLM_WIDTH              1
+#define M98090_HPVOLL_MASK             (31<<0)
+#define M98090_HPVOLL_SHIFT            0
+#define M98090_HPVOLL_WIDTH            5
+#define M98090_HPVOLL_NUM              (1<<M98090_HPVOLL_WIDTH)
+
+/*
+ * M98090_REG_RIGHT_HP_VOLUME
+ */
+#define M98090_HPRM_MASK               (1<<7)
+#define M98090_HPRM_SHIFT              7
+#define M98090_HPRM_WIDTH              1
+#define M98090_HPVOLR_MASK             (31<<0)
+#define M98090_HPVOLR_SHIFT            0
+#define M98090_HPVOLR_WIDTH            5
+#define M98090_HPVOLR_NUM              (1<<M98090_HPVOLR_WIDTH)
+
+/*
+ * M98090_REG_LEFT_SPK_MIXER
+ */
+#define M98090_MIXSPL_MIC2_MASK                (1<<5)
+#define M98090_MIXSPL_MIC2_SHIFT       5
+#define M98090_MIXSPL_MIC2_WIDTH       1
+#define M98090_MIXSPL_MIC1_MASK                (1<<4)
+#define M98090_MIXSPL_MIC1_SHIFT       4
+#define M98090_MIXSPL_MIC1_WIDTH       1
+#define M98090_MIXSPL_LINEB_MASK       (1<<3)
+#define M98090_MIXSPL_LINEB_SHIFT      3
+#define M98090_MIXSPL_LINEB_WIDTH      1
+#define M98090_MIXSPL_LINEA_MASK       (1<<2)
+#define M98090_MIXSPL_LINEA_SHIFT      2
+#define M98090_MIXSPL_LINEA_WIDTH      1
+#define M98090_MIXSPL_DACR_MASK                (1<<1)
+#define M98090_MIXSPL_DACR_SHIFT       1
+#define M98090_MIXSPL_DACR_WIDTH       1
+#define M98090_MIXSPL_DACL_MASK                (1<<0)
+#define M98090_MIXSPL_DACL_SHIFT       0
+#define M98090_MIXSPL_DACL_WIDTH       1
+#define M98090_MIXSPL_MASK             (63<<0)
+#define M98090_MIXSPL_SHIFT            0
+#define M98090_MIXSPL_WIDTH            6
+#define M98090_MIXSPR_DACR_MASK                (1<<1)
+#define M98090_MIXSPR_DACR_SHIFT       1
+#define M98090_MIXSPR_DACR_WIDTH       1
+
+
+/*
+ * M98090_REG_RIGHT_SPK_MIXER
+ */
+#define M98090_SPK_SLAVE_MASK          (1<<6)
+#define M98090_SPK_SLAVE_SHIFT         6
+#define M98090_SPK_SLAVE_WIDTH         1
+#define M98090_MIXSPR_MIC2_MASK                (1<<5)
+#define M98090_MIXSPR_MIC2_SHIFT       5
+#define M98090_MIXSPR_MIC2_WIDTH       1
+#define M98090_MIXSPR_MIC1_MASK                (1<<4)
+#define M98090_MIXSPR_MIC1_SHIFT       4
+#define M98090_MIXSPR_MIC1_WIDTH       1
+#define M98090_MIXSPR_LINEB_MASK       (1<<3)
+#define M98090_MIXSPR_LINEB_SHIFT      3
+#define M98090_MIXSPR_LINEB_WIDTH      1
+#define M98090_MIXSPR_LINEA_MASK       (1<<2)
+#define M98090_MIXSPR_LINEA_SHIFT      2
+#define M98090_MIXSPR_LINEA_WIDTH      1
+#define M98090_MIXSPR_DACR_MASK                (1<<1)
+#define M98090_MIXSPR_DACR_SHIFT       1
+#define M98090_MIXSPR_DACR_WIDTH       1
+#define M98090_MIXSPR_DACL_MASK                (1<<0)
+#define M98090_MIXSPR_DACL_SHIFT       0
+#define M98090_MIXSPR_DACL_WIDTH       1
+#define M98090_MIXSPR_MASK             (63<<0)
+#define M98090_MIXSPR_SHIFT            0
+#define M98090_MIXSPR_WIDTH            6
+
+/*
+ * M98090_REG_SPK_CONTROL
+ */
+#define M98090_MIXSPRG_MASK            (3<<2)
+#define M98090_MIXSPRG_SHIFT           2
+#define M98090_MIXSPRG_WIDTH           2
+#define M98090_MIXSPRG_NUM             (1<<M98090_MIXSPRG_WIDTH)
+#define M98090_MIXSPLG_MASK            (3<<0)
+#define M98090_MIXSPLG_SHIFT           0
+#define M98090_MIXSPLG_WIDTH           2
+#define M98090_MIXSPLG_NUM             (1<<M98090_MIXSPLG_WIDTH)
+
+/*
+ * M98090_REG_LEFT_SPK_VOLUME
+ */
+#define M98090_SPLM_MASK               (1<<7)
+#define M98090_SPLM_SHIFT              7
+#define M98090_SPLM_WIDTH              1
+#define M98090_SPVOLL_MASK             (63<<0)
+#define M98090_SPVOLL_SHIFT            0
+#define M98090_SPVOLL_WIDTH            6
+#define M98090_SPVOLL_NUM              40
+
+/*
+ * M98090_REG_RIGHT_SPK_VOLUME
+ */
+#define M98090_SPRM_MASK               (1<<7)
+#define M98090_SPRM_SHIFT              7
+#define M98090_SPRM_WIDTH              1
+#define M98090_SPVOLR_MASK             (63<<0)
+#define M98090_SPVOLR_SHIFT            0
+#define M98090_SPVOLR_WIDTH            6
+#define M98090_SPVOLR_NUM              40
+
+/*
+ * M98090_REG_DRC_TIMING
+ */
+#define M98090_DRCEN_MASK              (1<<7)
+#define M98090_DRCEN_SHIFT             7
+#define M98090_DRCEN_WIDTH             1
+#define M98090_DRCEN_NUM               (1<<M98090_DRCEN_WIDTH)
+#define M98090_DRCRLS_MASK             (7<<4)
+#define M98090_DRCRLS_SHIFT            4
+#define M98090_DRCRLS_WIDTH            3
+#define M98090_DRCATK_MASK             (7<<0)
+#define M98090_DRCATK_SHIFT            0
+#define M98090_DRCATK_WIDTH            3
+
+/*
+ * M98090_REG_DRC_COMPRESSOR
+ */
+#define M98090_DRCCMP_MASK             (7<<5)
+#define M98090_DRCCMP_SHIFT            5
+#define M98090_DRCCMP_WIDTH            3
+#define M98090_DRCTHC_MASK             (31<<0)
+#define M98090_DRCTHC_SHIFT            0
+#define M98090_DRCTHC_WIDTH            5
+#define M98090_DRCTHC_NUM              (1<<M98090_DRCTHC_WIDTH)
+
+/*
+ * M98090_REG_DRC_EXPANDER
+ */
+#define M98090_DRCEXP_MASK             (7<<5)
+#define M98090_DRCEXP_SHIFT            5
+#define M98090_DRCEXP_WIDTH            3
+#define M98090_DRCTHE_MASK             (31<<0)
+#define M98090_DRCTHE_SHIFT            0
+#define M98090_DRCTHE_WIDTH            5
+#define M98090_DRCTHE_NUM              (1<<M98090_DRCTHE_WIDTH)
+
+/*
+ * M98090_REG_DRC_GAIN
+ */
+#define M98090_DRCG_MASK               (31<<0)
+#define M98090_DRCG_SHIFT              0
+#define M98090_DRCG_WIDTH              5
+#define M98090_DRCG_NUM                        13
+
+/*
+ * M98090_REG_RCV_LOUTL_MIXER
+ */
+#define M98090_MIXRCVL_MIC2_MASK       (1<<5)
+#define M98090_MIXRCVL_MIC2_SHIFT      5
+#define M98090_MIXRCVL_MIC2_WIDTH      1
+#define M98090_MIXRCVL_MIC1_MASK       (1<<4)
+#define M98090_MIXRCVL_MIC1_SHIFT      4
+#define M98090_MIXRCVL_MIC1_WIDTH      1
+#define M98090_MIXRCVL_LINEB_MASK      (1<<3)
+#define M98090_MIXRCVL_LINEB_SHIFT     3
+#define M98090_MIXRCVL_LINEB_WIDTH     1
+#define M98090_MIXRCVL_LINEA_MASK      (1<<2)
+#define M98090_MIXRCVL_LINEA_SHIFT     2
+#define M98090_MIXRCVL_LINEA_WIDTH     1
+#define M98090_MIXRCVL_DACR_MASK       (1<<1)
+#define M98090_MIXRCVL_DACR_SHIFT      1
+#define M98090_MIXRCVL_DACR_WIDTH      1
+#define M98090_MIXRCVL_DACL_MASK       (1<<0)
+#define M98090_MIXRCVL_DACL_SHIFT      0
+#define M98090_MIXRCVL_DACL_WIDTH      1
+#define M98090_MIXRCVL_MASK            (63<<0)
+#define M98090_MIXRCVL_SHIFT           0
+#define M98090_MIXRCVL_WIDTH           6
+
+/*
+ * M98090_REG_RCV_LOUTL_CONTROL
+ */
+#define M98090_MIXRCVLG_MASK           (3<<0)
+#define M98090_MIXRCVLG_SHIFT          0
+#define M98090_MIXRCVLG_WIDTH          2
+#define M98090_MIXRCVLG_NUM            (1<<M98090_MIXRCVLG_WIDTH)
+
+/*
+ * M98090_REG_RCV_LOUTL_VOLUME
+ */
+#define M98090_RCVLM_MASK              (1<<7)
+#define M98090_RCVLM_SHIFT             7
+#define M98090_RCVLM_WIDTH             1
+#define M98090_RCVLVOL_MASK            (31<<0)
+#define M98090_RCVLVOL_SHIFT           0
+#define M98090_RCVLVOL_WIDTH           5
+#define M98090_RCVLVOL_NUM             (1<<M98090_RCVLVOL_WIDTH)
+
+/*
+ * M98090_REG_LOUTR_MIXER
+ */
+#define M98090_LINMOD_MASK             (1<<7)
+#define M98090_LINMOD_SHIFT            7
+#define M98090_LINMOD_WIDTH            1
+#define M98090_MIXRCVR_MIC2_MASK       (1<<5)
+#define M98090_MIXRCVR_MIC2_SHIFT      5
+#define M98090_MIXRCVR_MIC2_WIDTH      1
+#define M98090_MIXRCVR_MIC1_MASK       (1<<4)
+#define M98090_MIXRCVR_MIC1_SHIFT      4
+#define M98090_MIXRCVR_MIC1_WIDTH      1
+#define M98090_MIXRCVR_LINEB_MASK      (1<<3)
+#define M98090_MIXRCVR_LINEB_SHIFT     3
+#define M98090_MIXRCVR_LINEB_WIDTH     1
+#define M98090_MIXRCVR_LINEA_MASK      (1<<2)
+#define M98090_MIXRCVR_LINEA_SHIFT     2
+#define M98090_MIXRCVR_LINEA_WIDTH     1
+#define M98090_MIXRCVR_DACR_MASK       (1<<1)
+#define M98090_MIXRCVR_DACR_SHIFT      1
+#define M98090_MIXRCVR_DACR_WIDTH      1
+#define M98090_MIXRCVR_DACL_MASK       (1<<0)
+#define M98090_MIXRCVR_DACL_SHIFT      0
+#define M98090_MIXRCVR_DACL_WIDTH      1
+#define M98090_MIXRCVR_MASK            (63<<0)
+#define M98090_MIXRCVR_SHIFT           0
+#define M98090_MIXRCVR_WIDTH           6
+
+/*
+ * M98090_REG_LOUTR_CONTROL
+ */
+#define M98090_MIXRCVRG_MASK           (3<<0)
+#define M98090_MIXRCVRG_SHIFT          0
+#define M98090_MIXRCVRG_WIDTH          2
+#define M98090_MIXRCVRG_NUM            (1<<M98090_MIXRCVRG_WIDTH)
+
+/*
+ * M98090_REG_LOUTR_VOLUME
+ */
+#define M98090_RCVRM_MASK              (1<<7)
+#define M98090_RCVRM_SHIFT             7
+#define M98090_RCVRM_WIDTH             1
+#define M98090_RCVRVOL_MASK            (31<<0)
+#define M98090_RCVRVOL_SHIFT           0
+#define M98090_RCVRVOL_WIDTH           5
+#define M98090_RCVRVOL_NUM             (1<<M98090_RCVRVOL_WIDTH)
+
+/*
+ * M98090_REG_JACK_DETECT
+ */
+#define M98090_JDETEN_MASK             (1<<7)
+#define M98090_JDETEN_SHIFT            7
+#define M98090_JDETEN_WIDTH            1
+#define M98090_JDWK_MASK               (1<<6)
+#define M98090_JDWK_SHIFT              6
+#define M98090_JDWK_WIDTH              1
+#define M98090_JDEB_MASK               (3<<0)
+#define M98090_JDEB_SHIFT              0
+#define M98090_JDEB_WIDTH              2
+#define M98090_JDEB_25MS               (0<<0)
+#define M98090_JDEB_50MS               (1<<0)
+#define M98090_JDEB_100MS              (2<<0)
+#define M98090_JDEB_200MS              (3<<0)
+
+/*
+ * M98090_REG_INPUT_ENABLE
+ */
+#define M98090_MBEN_MASK               (1<<4)
+#define M98090_MBEN_SHIFT              4
+#define M98090_MBEN_WIDTH              1
+#define M98090_LINEAEN_MASK            (1<<3)
+#define M98090_LINEAEN_SHIFT           3
+#define M98090_LINEAEN_WIDTH           1
+#define M98090_LINEBEN_MASK            (1<<2)
+#define M98090_LINEBEN_SHIFT           2
+#define M98090_LINEBEN_WIDTH           1
+#define M98090_ADREN_MASK              (1<<1)
+#define M98090_ADREN_SHIFT             1
+#define M98090_ADREN_WIDTH             1
+#define M98090_ADLEN_MASK              (1<<0)
+#define M98090_ADLEN_SHIFT             0
+#define M98090_ADLEN_WIDTH             1
+
+/*
+ * M98090_REG_OUTPUT_ENABLE
+ */
+#define M98090_HPREN_MASK              (1<<7)
+#define M98090_HPREN_SHIFT             7
+#define M98090_HPREN_WIDTH             1
+#define M98090_HPLEN_MASK              (1<<6)
+#define M98090_HPLEN_SHIFT             6
+#define M98090_HPLEN_WIDTH             1
+#define M98090_SPREN_MASK              (1<<5)
+#define M98090_SPREN_SHIFT             5
+#define M98090_SPREN_WIDTH             1
+#define M98090_SPLEN_MASK              (1<<4)
+#define M98090_SPLEN_SHIFT             4
+#define M98090_SPLEN_WIDTH             1
+#define M98090_RCVLEN_MASK             (1<<3)
+#define M98090_RCVLEN_SHIFT            3
+#define M98090_RCVLEN_WIDTH            1
+#define M98090_RCVREN_MASK             (1<<2)
+#define M98090_RCVREN_SHIFT            2
+#define M98090_RCVREN_WIDTH            1
+#define M98090_DAREN_MASK              (1<<1)
+#define M98090_DAREN_SHIFT             1
+#define M98090_DAREN_WIDTH             1
+#define M98090_DALEN_MASK              (1<<0)
+#define M98090_DALEN_SHIFT             0
+#define M98090_DALEN_WIDTH             1
+
+/*
+ * M98090_REG_LEVEL_CONTROL
+ */
+#define M98090_ZDENN_MASK              (1<<2)
+#define M98090_ZDENN_SHIFT             2
+#define M98090_ZDENN_WIDTH             1
+#define M98090_ZDENN_NUM               (1<<M98090_ZDENN_WIDTH)
+#define M98090_VS2ENN_MASK             (1<<1)
+#define M98090_VS2ENN_SHIFT            1
+#define M98090_VS2ENN_WIDTH            1
+#define M98090_VS2ENN_NUM              (1<<M98090_VS2ENN_WIDTH)
+#define M98090_VSENN_MASK              (1<<0)
+#define M98090_VSENN_SHIFT             0
+#define M98090_VSENN_WIDTH             1
+#define M98090_VSENN_NUM               (1<<M98090_VSENN_WIDTH)
+
+/*
+ * M98090_REG_DSP_FILTER_ENABLE
+ */
+#define M98090_DMIC34BQEN_MASK         (1<<4)
+#define M98090_DMIC34BQEN_SHIFT                4
+#define M98090_DMIC34BQEN_WIDTH                1
+#define M98090_DMIC34BQEN_NUM          (1<<M98090_DMIC34BQEN_WIDTH)
+#define M98090_ADCBQEN_MASK            (1<<3)
+#define M98090_ADCBQEN_SHIFT           3
+#define M98090_ADCBQEN_WIDTH           1
+#define M98090_ADCBQEN_NUM             (1<<M98090_ADCBQEN_WIDTH)
+#define M98090_EQ3BANDEN_MASK          (1<<2)
+#define M98090_EQ3BANDEN_SHIFT         2
+#define M98090_EQ3BANDEN_WIDTH         1
+#define M98090_EQ3BANDEN_NUM           (1<<M98090_EQ3BANDEN_WIDTH)
+#define M98090_EQ5BANDEN_MASK          (1<<1)
+#define M98090_EQ5BANDEN_SHIFT         1
+#define M98090_EQ5BANDEN_WIDTH         1
+#define M98090_EQ5BANDEN_NUM           (1<<M98090_EQ5BANDEN_WIDTH)
+#define M98090_EQ7BANDEN_MASK          (1<<0)
+#define M98090_EQ7BANDEN_SHIFT         0
+#define M98090_EQ7BANDEN_WIDTH         1
+#define M98090_EQ7BANDEN_NUM           (1<<M98090_EQ7BANDEN_WIDTH)
+
+/*
+ * M98090_REG_BIAS_CONTROL
+ */
+#define M98090_VCM_MODE_MASK           (1<<0)
+#define M98090_VCM_MODE_SHIFT          0
+#define M98090_VCM_MODE_WIDTH          1
+#define M98090_VCM_MODE_NUM            (1<<M98090_VCM_MODE_WIDTH)
+
+/*
+ * M98090_REG_DAC_CONTROL
+ */
+#define M98090_PERFMODE_MASK           (1<<1)
+#define M98090_PERFMODE_SHIFT          1
+#define M98090_PERFMODE_WIDTH          1
+#define M98090_PERFMODE_NUM            (1<<M98090_PERFMODE_WIDTH)
+#define M98090_DACHP_MASK              (1<<0)
+#define M98090_DACHP_SHIFT             0
+#define M98090_DACHP_WIDTH             1
+#define M98090_DACHP_NUM               (1<<M98090_DACHP_WIDTH)
+
+/*
+ * M98090_REG_ADC_CONTROL
+ */
+#define M98090_OSR128_MASK             (1<<2)
+#define M98090_OSR128_SHIFT            2
+#define M98090_OSR128_WIDTH            1
+#define M98090_ADCDITHER_MASK          (1<<1)
+#define M98090_ADCDITHER_SHIFT         1
+#define M98090_ADCDITHER_WIDTH         1
+#define M98090_ADCDITHER_NUM           (1<<M98090_ADCDITHER_WIDTH)
+#define M98090_ADCHP_MASK              (1<<0)
+#define M98090_ADCHP_SHIFT             0
+#define M98090_ADCHP_WIDTH             1
+#define M98090_ADCHP_NUM               (1<<M98090_ADCHP_WIDTH)
+
+/*
+ * M98090_REG_DEVICE_SHUTDOWN
+ */
+#define M98090_SHDNN_MASK              (1<<7)
+#define M98090_SHDNN_SHIFT             7
+#define M98090_SHDNN_WIDTH             1
+
+/*
+ * M98090_REG_EQUALIZER_BASE
+ */
+#define M98090_B0_1_HI_MASK            (255<<0)
+#define M98090_B0_1_HI_SHIFT           0
+#define M98090_B0_1_HI_WIDTH           8
+#define M98090_B0_1_MID_MASK           (255<<0)
+#define M98090_B0_1_MID_SHIFT          0
+#define M98090_B0_1_MID_WIDTH          8
+#define M98090_B0_1_LO_MASK            (255<<0)
+#define M98090_B0_1_LO_SHIFT           0
+#define M98090_B0_1_LO_WIDTH           8
+#define M98090_B1_1_HI_MASK            (255<<0)
+#define M98090_B1_1_HI_SHIFT           0
+#define M98090_B1_1_HI_WIDTH           8
+#define M98090_B1_1_MID_MASK           (255<<0)
+#define M98090_B1_1_MID_SHIFT          0
+#define M98090_B1_1_MID_WIDTH          8
+#define M98090_B1_1_LO_MASK            (255<<0)
+#define M98090_B1_1_LO_SHIFT           0
+#define M98090_B1_1_LO_WIDTH           8
+#define M98090_B2_1_HI_MASK            (255<<0)
+#define M98090_B2_1_HI_SHIFT           0
+#define M98090_B2_1_HI_WIDTH           8
+#define M98090_B2_1_MID_MASK           (255<<0)
+#define M98090_B2_1_MID_SHIFT          0
+#define M98090_B2_1_MID_WIDTH          8
+#define M98090_B2_1_LO_MASK            (255<<0)
+#define M98090_B2_1_LO_SHIFT           0
+#define M98090_B2_1_LO_WIDTH           8
+#define M98090_A1_1_HI_MASK            (255<<0)
+#define M98090_A1_1_HI_SHIFT           0
+#define M98090_A1_1_HI_WIDTH           8
+#define M98090_A1_1_MID_MASK           (255<<0)
+#define M98090_A1_1_MID_SHIFT          0
+#define M98090_A1_1_MID_WIDTH          8
+#define M98090_A1_1_LO_MASK            (255<<0)
+#define M98090_A1_1_LO_SHIFT           0
+#define M98090_A1_1_LO_WIDTH           8
+#define M98090_A2_1_HI_MASK            (255<<0)
+#define M98090_A2_1_HI_SHIFT           0
+#define M98090_A2_1_HI_WIDTH           8
+#define M98090_A2_1_MID_MASK           (255<<0)
+#define M98090_A2_1_MID_SHIFT          0
+#define M98090_A2_1_MID_WIDTH          8
+#define M98090_A2_1_LO_MASK            (255<<0)
+#define M98090_A2_1_LO_SHIFT           0
+#define M98090_A2_1_LO_WIDTH           8
+
+#define M98090_COEFS_PER_BAND          5
+#define M98090_COEFS_BLK_SZ            (M98090_COEFS_PER_BAND * 3)
+#define M98090_COEFS_MAX_SZ            (M98090_COEFS_BLK_SZ * 7)
+
+/*
+ * M98090_REG_RECORD_BIQUAD_BASE
+ */
+#define M98090_REC_B0_HI_MASK          (255<<0)
+#define M98090_REC_B0_HI_SHIFT         0
+#define M98090_REC_B0_HI_WIDTH         8
+#define M98090_REC_B0_MID_MASK         (255<<0)
+#define M98090_REC_B0_MID_SHIFT                0
+#define M98090_REC_B0_MID_WIDTH                8
+#define M98090_REC_B0_LO_MASK          (255<<0)
+#define M98090_REC_B0_LO_SHIFT         0
+#define M98090_REC_B0_LO_WIDTH         8
+#define M98090_REC_B1_HI_MASK          (255<<0)
+#define M98090_REC_B1_HI_SHIFT         0
+#define M98090_REC_B1_HI_WIDTH         8
+#define M98090_REC_B1_MID_MASK         (255<<0)
+#define M98090_REC_B1_MID_SHIFT                0
+#define M98090_REC_B1_MID_WIDTH                8
+#define M98090_REC_B1_LO_MASK          (255<<0)
+#define M98090_REC_B1_LO_SHIFT         0
+#define M98090_REC_B1_LO_WIDTH         8
+#define M98090_REC_B2_HI_MASK          (255<<0)
+#define M98090_REC_B2_HI_SHIFT         0
+#define M98090_REC_B2_HI_WIDTH         8
+#define M98090_REC_B2_MID_MASK         (255<<0)
+#define M98090_REC_B2_MID_SHIFT                0
+#define M98090_REC_B2_MID_WIDTH                8
+#define M98090_REC_B2_LO_MASK          (255<<0)
+#define M98090_REC_B2_LO_SHIFT         0
+#define M98090_REC_B2_LO_WIDTH         8
+#define M98090_REC_A1_HI_MASK          (255<<0)
+#define M98090_REC_A1_HI_SHIFT         0
+#define M98090_REC_A1_HI_WIDTH         8
+#define M98090_REC_A1_MID_MASK         (255<<0)
+#define M98090_REC_A1_MID_SHIFT                0
+#define M98090_REC_A1_MID_WIDTH                8
+#define M98090_REC_A1_LO_MASK          (255<<0)
+#define M98090_REC_A1_LO_SHIFT         0
+#define M98090_REC_A1_LO_WIDTH         8
+#define M98090_REC_A2_HI_MASK          (255<<0)
+#define M98090_REC_A2_HI_SHIFT         0
+#define M98090_REC_A2_HI_WIDTH         8
+#define M98090_REC_A2_MID_MASK         (255<<0)
+#define M98090_REC_A2_MID_SHIFT                0
+#define M98090_REC_A2_MID_WIDTH                8
+#define M98090_REC_A2_LO_MASK          (255<<0)
+#define M98090_REC_A2_LO_SHIFT         0
+#define M98090_REC_A2_LO_WIDTH         8
+
+/*
+ * M98090_REG_DMIC3_VOLUME
+ */
+#define M98090_DMIC_AV3G_MASK          (7<<4)
+#define M98090_DMIC_AV3G_SHIFT         4
+#define M98090_DMIC_AV3G_WIDTH         3
+#define M98090_DMIC_AV3G_NUM           (1<<M98090_DMIC_AV3G_WIDTH)
+#define M98090_DMIC_AV3_MASK           (15<<0)
+#define M98090_DMIC_AV3_SHIFT          0
+#define M98090_DMIC_AV3_WIDTH          4
+#define M98090_DMIC_AV3_NUM            (1<<M98090_DMIC_AV3_WIDTH)
+
+/*
+ * M98090_REG_DMIC4_VOLUME
+ */
+#define M98090_DMIC_AV4G_MASK          (7<<4)
+#define M98090_DMIC_AV4G_SHIFT         4
+#define M98090_DMIC_AV4G_WIDTH         3
+#define M98090_DMIC_AV4G_NUM           (1<<M98090_DMIC_AV4G_WIDTH)
+#define M98090_DMIC_AV4_MASK           (15<<0)
+#define M98090_DMIC_AV4_SHIFT          0
+#define M98090_DMIC_AV4_WIDTH          4
+#define M98090_DMIC_AV4_NUM            (1<<M98090_DMIC_AV4_WIDTH)
+
+/*
+ * M98090_REG_DMIC34_BQ_PREATTEN
+ */
+#define M98090_AV34BQ_MASK             (15<<0)
+#define M98090_AV34BQ_SHIFT            0
+#define M98090_AV34BQ_WIDTH            4
+#define M98090_AV34BQ_NUM              (1<<M98090_AV34BQ_WIDTH)
+
+/*
+ * M98090_REG_RECORD_TDM_SLOT
+ */
+#define M98090_TDM_SLOTADCL_MASK       (3<<6)
+#define M98090_TDM_SLOTADCL_SHIFT      6
+#define M98090_TDM_SLOTADCL_WIDTH      2
+#define M98090_TDM_SLOTADCL_NUM                (1<<M98090_TDM_SLOTADCL_WIDTH)
+#define M98090_TDM_SLOTADCR_MASK       (3<<4)
+#define M98090_TDM_SLOTADCR_SHIFT      4
+#define M98090_TDM_SLOTADCR_WIDTH      2
+#define M98090_TDM_SLOTADCR_NUM                (1<<M98090_TDM_SLOTADCR_WIDTH)
+#define M98090_TDM_SLOTDMIC3_MASK      (3<<2)
+#define M98090_TDM_SLOTDMIC3_SHIFT     2
+#define M98090_TDM_SLOTDMIC3_WIDTH     2
+#define M98090_TDM_SLOTDMIC3_NUM       (1<<M98090_TDM_SLOTDMIC3_WIDTH)
+#define M98090_TDM_SLOTDMIC4_MASK      (3<<0)
+#define M98090_TDM_SLOTDMIC4_SHIFT     0
+#define M98090_TDM_SLOTDMIC4_WIDTH     2
+#define M98090_TDM_SLOTDMIC4_NUM       (1<<M98090_TDM_SLOTDMIC4_WIDTH)
+
+/*
+ * M98090_REG_SAMPLE_RATE
+ */
+#define M98090_DMIC34_ZEROPAD_MASK     (1<<4)
+#define M98090_DMIC34_ZEROPAD_SHIFT    4
+#define M98090_DMIC34_ZEROPAD_WIDTH    1
+#define M98090_DMIC34_ZEROPAD_NUM      (1<<M98090_DIGMIC4_WIDTH)
+#define M98090_DMIC34_SRDIV_MASK       (7<<0)
+#define M98090_DMIC34_SRDIV_SHIFT      0
+#define M98090_DMIC34_SRDIV_WIDTH      3
+
+/*
+ * M98090_REG_DMIC34_BIQUAD_BASE
+ */
+#define M98090_DMIC34_B0_HI_MASK       (255<<0)
+#define M98090_DMIC34_B0_HI_SHIFT      0
+#define M98090_DMIC34_B0_HI_WIDTH      8
+#define M98090_DMIC34_B0_MID_MASK      (255<<0)
+#define M98090_DMIC34_B0_MID_SHIFT     0
+#define M98090_DMIC34_B0_MID_WIDTH     8
+#define M98090_DMIC34_B0_LO_MASK       (255<<0)
+#define M98090_DMIC34_B0_LO_SHIFT      0
+#define M98090_DMIC34_B0_LO_WIDTH      8
+#define M98090_DMIC34_B1_HI_MASK       (255<<0)
+#define M98090_DMIC34_B1_HI_SHIFT      0
+#define M98090_DMIC34_B1_HI_WIDTH      8
+#define M98090_DMIC34_B1_MID_MASK      (255<<0)
+#define M98090_DMIC34_B1_MID_SHIFT     0
+#define M98090_DMIC34_B1_MID_WIDTH     8
+#define M98090_DMIC34_B1_LO_MASK       (255<<0)
+#define M98090_DMIC34_B1_LO_SHIFT      0
+#define M98090_DMIC34_B1_LO_WIDTH      8
+#define M98090_DMIC34_B2_HI_MASK       (255<<0)
+#define M98090_DMIC34_B2_HI_SHIFT      0
+#define M98090_DMIC34_B2_HI_WIDTH      8
+#define M98090_DMIC34_B2_MID_MASK      (255<<0)
+#define M98090_DMIC34_B2_MID_SHIFT     0
+#define M98090_DMIC34_B2_MID_WIDTH     8
+#define M98090_DMIC34_B2_LO_MASK       (255<<0)
+#define M98090_DMIC34_B2_LO_SHIFT      0
+#define M98090_DMIC34_B2_LO_WIDTH      8
+#define M98090_DMIC34_A1_HI_MASK       (255<<0)
+#define M98090_DMIC34_A1_HI_SHIFT      0
+#define M98090_DMIC34_A1_HI_WIDTH      8
+#define M98090_DMIC34_A1_MID_MASK      (255<<0)
+#define M98090_DMIC34_A1_MID_SHIFT     0
+#define M98090_DMIC34_A1_MID_WIDTH     8
+#define M98090_DMIC34_A1_LO_MASK       (255<<0)
+#define M98090_DMIC34_A1_LO_SHIFT      0
+#define M98090_DMIC34_A1_LO_WIDTH      8
+#define M98090_DMIC34_A2_HI_MASK       (255<<0)
+#define M98090_DMIC34_A2_HI_SHIFT      0
+#define M98090_DMIC34_A2_HI_WIDTH      8
+#define M98090_DMIC34_A2_MID_MASK      (255<<0)
+#define M98090_DMIC34_A2_MID_SHIFT     0
+#define M98090_DMIC34_A2_MID_WIDTH     8
+#define M98090_DMIC34_A2_LO_MASK       (255<<0)
+#define M98090_DMIC34_A2_LO_SHIFT      0
+#define M98090_DMIC34_A2_LO_WIDTH      8
+
+#define M98090_JACK_STATE_NO_HEADSET   0
+#define M98090_JACK_STATE_NO_HEADSET_2 1
+#define M98090_JACK_STATE_HEADPHONE    2
+#define M98090_JACK_STATE_HEADSET      3
+
+/*
+ * M98090_REG_REVISION_ID
+ */
+#define M98090_REVID_MASK              (255<<0)
+#define M98090_REVID_SHIFT             0
+#define M98090_REVID_WIDTH             8
+#define M98090_REVID_NUM               (1<<M98090_REVID_WIDTH)
+
+#define M98090_BYTE1(w) ((w >> 8) & 0xff)
+#define M98090_BYTE0(w) (w & 0xff)
+
+/* Silicon revision number */
+#define M98090_REVA                    0x40
+#define M98091_REVA                    0x50
+
+enum max98090_type {
+       MAX98090,
+       MAX98091,
+};
+
+struct max98090_cdata {
+       unsigned int rate;
+       unsigned int fmt;
+};
+
+struct max98090_priv {
+       struct regmap *regmap;
+       struct snd_soc_codec *codec;
+       enum max98090_type devtype;
+       void *control_data;
+       struct max98090_pdata *pdata;
+       unsigned int sysclk;
+       unsigned int bclk;
+       unsigned int lrclk;
+       struct max98090_cdata dai[1];
+       int irq;
+       int jack_state;
+       struct delayed_work jack_work;
+       struct snd_soc_jack *jack;
+       unsigned int dai_fmt;
+       int tdm_slots;
+       int tdm_width;
+       u8 lin_state;
+       unsigned int pa1en;
+       unsigned int pa2en;
+       unsigned int extmic_mux;
+       unsigned int sidetone;
+};
+
+int max98090_mic_detect(struct snd_soc_codec *codec,
+       struct snd_soc_jack *jack);
+
+#endif
index 5708a973a77627a21c71143ff7e7622ab47314ee..65d09d60b7c634c55284eca35f3dc128db10307e 100644 (file)
@@ -85,6 +85,9 @@ struct aic3x_priv {
 #define AIC3X_MODEL_33 1
 #define AIC3X_MODEL_3007 2
        u16 model;
+
+       /* Selects the micbias voltage */
+       enum aic3x_micbias_voltage micbias_vg;
 };
 
 /*
@@ -195,6 +198,37 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
        return ret;
 }
 
+/*
+ * mic bias power on/off share the same register bits with
+ * output voltage of mic bias. when power on mic bias, we
+ * need reclaim it to voltage value.
+ * 0x0 = Powered off
+ * 0x1 = MICBIAS output is powered to 2.0V,
+ * 0x2 = MICBIAS output is powered to 2.5V
+ * 0x3 = MICBIAS output is connected to AVDD
+ */
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /* change mic bias voltage to user defined */
+               snd_soc_update_bits(codec, MICBIAS_CTRL,
+                               MICBIAS_LEVEL_MASK,
+                               aic3x->micbias_vg << MICBIAS_LEVEL_SHIFT);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, MICBIAS_CTRL,
+                               MICBIAS_LEVEL_MASK, 0);
+               break;
+       }
+       return 0;
+}
+
 static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" };
 static const char *aic3x_right_dac_mux[] = { "DAC_R1", "DAC_R3", "DAC_R2" };
 static const char *aic3x_left_hpcom_mux[] =
@@ -596,12 +630,9 @@ static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
                         AIC3X_ASD_INTF_CTRLA, 0, 3, 3, 0),
 
        /* Mic Bias */
-       SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2V",
-                        MICBIAS_CTRL, 6, 3, 1, 0),
-       SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias 2.5V",
-                        MICBIAS_CTRL, 6, 3, 2, 0),
-       SND_SOC_DAPM_REG(snd_soc_dapm_micbias, "Mic Bias AVDD",
-                        MICBIAS_CTRL, 6, 3, 3, 0),
+       SND_SOC_DAPM_SUPPLY("Mic Bias", MICBIAS_CTRL, 6, 0,
+                        mic_bias_event,
+                        SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 
        /* Output mixers */
        SND_SOC_DAPM_MIXER("Left Line Mixer", SND_SOC_NOPM, 0, 0,
@@ -1210,13 +1241,13 @@ static struct snd_soc_dai_driver aic3x_dai = {
        .name = "tlv320aic3x-hifi",
        .playback = {
                .stream_name = "Playback",
-               .channels_min = 1,
+               .channels_min = 2,
                .channels_max = 2,
                .rates = AIC3X_RATES,
                .formats = AIC3X_FORMATS,},
        .capture = {
                .stream_name = "Capture",
-               .channels_min = 1,
+               .channels_min = 2,
                .channels_max = 2,
                .rates = AIC3X_RATES,
                .formats = AIC3X_FORMATS,},
@@ -1386,6 +1417,24 @@ static int aic3x_probe(struct snd_soc_codec *codec)
        if (aic3x->model == AIC3X_MODEL_3007)
                snd_soc_add_codec_controls(codec, &aic3x_classd_amp_gain_ctrl, 1);
 
+       /* set mic bias voltage */
+       switch (aic3x->micbias_vg) {
+       case AIC3X_MICBIAS_2_0V:
+       case AIC3X_MICBIAS_2_5V:
+       case AIC3X_MICBIAS_AVDDV:
+               snd_soc_update_bits(codec, MICBIAS_CTRL,
+                                   MICBIAS_LEVEL_MASK,
+                                   (aic3x->micbias_vg) << MICBIAS_LEVEL_SHIFT);
+               break;
+       case AIC3X_MICBIAS_OFF:
+               /*
+                * noting to do. target won't enter here. This is just to avoid
+                * compile time warning "warning: enumeration value
+                * 'AIC3X_MICBIAS_OFF' not handled in switch"
+                */
+               break;
+       }
+
        aic3x_add_widgets(codec);
        list_add(&aic3x->list, &reset_list);
 
@@ -1461,6 +1510,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
        struct aic3x_setup_data *ai3x_setup;
        struct device_node *np = i2c->dev.of_node;
        int ret;
+       u32 value;
 
        aic3x = devm_kzalloc(&i2c->dev, sizeof(struct aic3x_priv), GFP_KERNEL);
        if (aic3x == NULL) {
@@ -1474,6 +1524,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
        if (pdata) {
                aic3x->gpio_reset = pdata->gpio_reset;
                aic3x->setup = pdata->setup;
+               aic3x->micbias_vg = pdata->micbias_vg;
        } else if (np) {
                ai3x_setup = devm_kzalloc(&i2c->dev, sizeof(*ai3x_setup),
                                                                GFP_KERNEL);
@@ -1493,6 +1544,26 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
                        aic3x->setup = ai3x_setup;
                }
 
+               if (!of_property_read_u32(np, "ai3x-micbias-vg", &value)) {
+                       switch (value) {
+                       case 1 :
+                               aic3x->micbias_vg = AIC3X_MICBIAS_2_0V;
+                               break;
+                       case 2 :
+                               aic3x->micbias_vg = AIC3X_MICBIAS_2_5V;
+                               break;
+                       case 3 :
+                               aic3x->micbias_vg = AIC3X_MICBIAS_AVDDV;
+                               break;
+                       default :
+                               aic3x->micbias_vg = AIC3X_MICBIAS_OFF;
+                               dev_err(&i2c->dev, "Unsuitable MicBias voltage "
+                                                       "found in DT\n");
+                       }
+               } else {
+                       aic3x->micbias_vg = AIC3X_MICBIAS_OFF;
+               }
+
        } else {
                aic3x->gpio_reset = -1;
        }
index 6db3c41b0163823fc49cb280f088366fe2cd21fb..e521ac3ddde82b1de6adbc6ed0c9491c60d7fcef 100644 (file)
 /* Default input volume */
 #define DEFAULT_GAIN    0x20
 
+/* MICBIAS Control Register */
+#define MICBIAS_LEVEL_SHIFT    (6)
+#define MICBIAS_LEVEL_MASK     (3 << 6)
+
 /* headset detection / button API */
 
 /* The AIC3x supports detection of stereo headsets (GND + left + right signal)
index 782b0cded2e69350046d942cfa67c836e521ce1a..4f358393d6d63b721495ccc8330da431a9c38a20 100644 (file)
@@ -1452,20 +1452,6 @@ static int dac33_soc_remove(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int dac33_soc_suspend(struct snd_soc_codec *codec)
-{
-       dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int dac33_soc_resume(struct snd_soc_codec *codec)
-{
-       dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_tlv320dac33 = {
        .read = dac33_read_reg_cache,
        .write = dac33_write_locked,
@@ -1476,8 +1462,6 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320dac33 = {
        .reg_cache_default = dac33_reg,
        .probe = dac33_soc_probe,
        .remove = dac33_soc_remove,
-       .suspend = dac33_soc_suspend,
-       .resume = dac33_soc_resume,
 
        .controls = dac33_snd_controls,
        .num_controls = ARRAY_SIZE(dac33_snd_controls),
index 63b280b060359f12ffda4f018f4f9e012f32eac2..8e6e5b0160219ef140748a3dbf31bc7561b9a8d1 100644 (file)
 /* Register descriptions are here */
 #include <linux/mfd/twl4030-audio.h>
 
+/* TWL4030 PMBR1 Register */
+#define TWL4030_PMBR1_REG              0x0D
+/* TWL4030 PMBR1 Register GPIO6 mux bits */
+#define TWL4030_GPIO6_PWM0_MUTE(value) ((value & 0x03) << 2)
+
 /* Shadow register used by the audio driver */
 #define TWL4030_REG_SW_SHADOW          0x4A
 #define TWL4030_CACHEREGNUM    (TWL4030_REG_SW_SHADOW + 1)
@@ -348,19 +353,32 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
 
        pdata = twl4030_get_pdata(codec);
 
-       if (pdata && pdata->hs_extmute &&
-           gpio_is_valid(pdata->hs_extmute_gpio)) {
-               int ret;
-
-               if (!pdata->hs_extmute_gpio)
-                       dev_warn(codec->dev,
-                                "Extmute GPIO is 0 is this correct?\n");
-
-               ret = gpio_request_one(pdata->hs_extmute_gpio,
-                                      GPIOF_OUT_INIT_LOW, "hs_extmute");
-               if (ret) {
-                       dev_err(codec->dev, "Failed to get hs_extmute GPIO\n");
-                       pdata->hs_extmute_gpio = -1;
+       if (pdata && pdata->hs_extmute) {
+               if (gpio_is_valid(pdata->hs_extmute_gpio)) {
+                       int ret;
+
+                       if (!pdata->hs_extmute_gpio)
+                               dev_warn(codec->dev,
+                                       "Extmute GPIO is 0 is this correct?\n");
+
+                       ret = gpio_request_one(pdata->hs_extmute_gpio,
+                                              GPIOF_OUT_INIT_LOW,
+                                              "hs_extmute");
+                       if (ret) {
+                               dev_err(codec->dev,
+                                       "Failed to get hs_extmute GPIO\n");
+                               pdata->hs_extmute_gpio = -1;
+                       }
+               } else {
+                       u8 pin_mux;
+
+                       /* Set TWL4030 GPIO6 as EXTMUTE signal */
+                       twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux,
+                                       TWL4030_PMBR1_REG);
+                       pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03);
+                       pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02);
+                       twl_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux,
+                                        TWL4030_PMBR1_REG);
                }
        }
 
@@ -1306,6 +1324,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        SND_SOC_DAPM_DAC("DAC Left2", NULL, SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_DAC("DAC Voice", NULL, SND_SOC_NOPM, 0, 0),
 
+       SND_SOC_DAPM_AIF_IN("VAIFIN", "Voice Playback", 0,
+                           TWL4030_REG_VOICE_IF, 6, 0),
+
        /* Analog bypasses */
        SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
                        &twl4030_dapm_abypassr1_control),
@@ -1438,6 +1459,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        SND_SOC_DAPM_ADC("ADC Virtual Left2", NULL, SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_ADC("ADC Virtual Right2", NULL, SND_SOC_NOPM, 0, 0),
 
+       SND_SOC_DAPM_AIF_OUT("VAIFOUT", "Voice Capture", 0,
+                            TWL4030_REG_VOICE_IF, 5, 0),
+
        /* Analog/Digital mic path selection.
           TX1 Left/Right: either analog Left/Right or Digimic0
           TX2 Left/Right: either analog Left/Right or Digimic1 */
@@ -1473,10 +1497,15 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
        SND_SOC_DAPM_SUPPLY("micbias2 select", TWL4030_REG_MICBIAS_CTL, 6, 0,
                            NULL, 0),
 
-       SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0),
-       SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0),
-       SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0),
+       /* Microphone bias */
+       SND_SOC_DAPM_SUPPLY("Mic Bias 1",
+                           TWL4030_REG_MICBIAS_CTL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Mic Bias 2",
+                           TWL4030_REG_MICBIAS_CTL, 1, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Headset Mic Bias",
+                           TWL4030_REG_MICBIAS_CTL, 2, 0, NULL, 0),
 
+       SND_SOC_DAPM_SUPPLY("VIF Enable", TWL4030_REG_VOICE_IF, 0, 0, NULL, 0),
 };
 
 static const struct snd_soc_dapm_route intercon[] = {
@@ -1485,17 +1514,16 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"DAC Left1", NULL, "HiFi Playback"},
        {"DAC Right2", NULL, "HiFi Playback"},
        {"DAC Left2", NULL, "HiFi Playback"},
-       {"DAC Voice", NULL, "Voice Playback"},
+       {"DAC Voice", NULL, "VAIFIN"},
 
        /* ADC -> Stream mapping */
        {"HiFi Capture", NULL, "ADC Virtual Left1"},
        {"HiFi Capture", NULL, "ADC Virtual Right1"},
        {"HiFi Capture", NULL, "ADC Virtual Left2"},
        {"HiFi Capture", NULL, "ADC Virtual Right2"},
-       {"Voice Capture", NULL, "ADC Virtual Left1"},
-       {"Voice Capture", NULL, "ADC Virtual Right1"},
-       {"Voice Capture", NULL, "ADC Virtual Left2"},
-       {"Voice Capture", NULL, "ADC Virtual Right2"},
+       {"VAIFOUT", NULL, "ADC Virtual Left2"},
+       {"VAIFOUT", NULL, "ADC Virtual Right2"},
+       {"VAIFOUT", NULL, "VIF Enable"},
 
        {"Digital L1 Playback Mixer", NULL, "DAC Left1"},
        {"Digital R1 Playback Mixer", NULL, "DAC Right1"},
@@ -1510,6 +1538,7 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"DAC Right1", NULL, "AIF Enable"},
        {"DAC Left2", NULL, "AIF Enable"},
        {"DAC Right1", NULL, "AIF Enable"},
+       {"DAC Voice", NULL, "VIF Enable"},
 
        {"Digital R2 Playback Mixer", NULL, "AIF Enable"},
        {"Digital L2 Playback Mixer", NULL, "AIF Enable"},
@@ -2267,18 +2296,6 @@ static struct snd_soc_dai_driver twl4030_dai[] = {
 },
 };
 
-static int twl4030_soc_suspend(struct snd_soc_codec *codec)
-{
-       twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int twl4030_soc_resume(struct snd_soc_codec *codec)
-{
-       twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int twl4030_soc_probe(struct snd_soc_codec *codec)
 {
        struct twl4030_priv *twl4030;
@@ -2316,8 +2333,6 @@ static int twl4030_soc_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_twl4030 = {
        .probe = twl4030_soc_probe,
        .remove = twl4030_soc_remove,
-       .suspend = twl4030_soc_suspend,
-       .resume = twl4030_soc_resume,
        .read = twl4030_read_reg_cache,
        .write = twl4030_write,
        .set_bias_level = twl4030_set_bias_level,
index 3fc3fc64dd8b47a85ca142d3ef95dfe637908754..9b9a6e587610540c310c39387791f588592e1a4c 100644 (file)
@@ -69,13 +69,8 @@ struct twl6040_data {
        int hs_power_mode_locked;
        unsigned int clk_in;
        unsigned int sysclk;
-       u16 hs_left_step;
-       u16 hs_right_step;
-       u16 hf_left_step;
-       u16 hf_right_step;
        struct twl6040_jack_data hs_jack;
        struct snd_soc_codec *codec;
-       struct workqueue_struct *workqueue;
        struct mutex mutex;
 };
 
@@ -404,8 +399,7 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data)
        struct snd_soc_codec *codec = data;
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 
-       queue_delayed_work(priv->workqueue, &priv->hs_jack.work,
-                          msecs_to_jiffies(200));
+       schedule_delayed_work(&priv->hs_jack.work, msecs_to_jiffies(200));
 
        return IRQ_HANDLED;
 }
@@ -1115,7 +1109,6 @@ static int twl6040_suspend(struct snd_soc_codec *codec)
 static int twl6040_resume(struct snd_soc_codec *codec)
 {
        twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       twl6040_set_bias_level(codec, codec->dapm.suspend_bias_level);
 
        return 0;
 }
@@ -1127,83 +1120,46 @@ static int twl6040_resume(struct snd_soc_codec *codec)
 static int twl6040_probe(struct snd_soc_codec *codec)
 {
        struct twl6040_data *priv;
-       struct twl6040_codec_data *pdata = dev_get_platdata(codec->dev);
        struct platform_device *pdev = container_of(codec->dev,
                                                   struct platform_device, dev);
        int ret = 0;
 
-       priv = kzalloc(sizeof(struct twl6040_data), GFP_KERNEL);
+       priv = devm_kzalloc(codec->dev, sizeof(*priv), GFP_KERNEL);
        if (priv == NULL)
                return -ENOMEM;
+
        snd_soc_codec_set_drvdata(codec, priv);
 
        priv->codec = codec;
        codec->control_data = dev_get_drvdata(codec->dev->parent);
 
-       if (pdata && pdata->hs_left_step && pdata->hs_right_step) {
-               priv->hs_left_step = pdata->hs_left_step;
-               priv->hs_right_step = pdata->hs_right_step;
-       } else {
-               priv->hs_left_step = 1;
-               priv->hs_right_step = 1;
-       }
-
-       if (pdata && pdata->hf_left_step && pdata->hf_right_step) {
-               priv->hf_left_step = pdata->hf_left_step;
-               priv->hf_right_step = pdata->hf_right_step;
-       } else {
-               priv->hf_left_step = 1;
-               priv->hf_right_step = 1;
-       }
-
        priv->plug_irq = platform_get_irq(pdev, 0);
        if (priv->plug_irq < 0) {
                dev_err(codec->dev, "invalid irq\n");
-               ret = -EINVAL;
-               goto work_err;
-       }
-
-       priv->workqueue = alloc_workqueue("twl6040-codec", 0, 0);
-       if (!priv->workqueue) {
-               ret = -ENOMEM;
-               goto work_err;
+               return -EINVAL;
        }
 
        INIT_DELAYED_WORK(&priv->hs_jack.work, twl6040_accessory_work);
 
        mutex_init(&priv->mutex);
 
-       ret = request_threaded_irq(priv->plug_irq, NULL, twl6040_audio_handler,
-                                  0, "twl6040_irq_plug", codec);
+       ret = devm_request_threaded_irq(codec->dev, priv->plug_irq, NULL,
+                                       twl6040_audio_handler, IRQF_NO_SUSPEND,
+                                       "twl6040_irq_plug", codec);
        if (ret) {
                dev_err(codec->dev, "PLUG IRQ request failed: %d\n", ret);
-               goto plugirq_err;
+               return ret;
        }
 
        twl6040_init_chip(codec);
 
        /* power on device */
-       ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       if (!ret)
-               return 0;
-
-       /* Error path */
-       free_irq(priv->plug_irq, codec);
-plugirq_err:
-       destroy_workqueue(priv->workqueue);
-work_err:
-       kfree(priv);
-       return ret;
+       return twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 }
 
 static int twl6040_remove(struct snd_soc_codec *codec)
 {
-       struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
-
        twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       free_irq(priv->plug_irq, codec);
-       destroy_workqueue(priv->workqueue);
-       kfree(priv);
 
        return 0;
 }
index 12bcae63a7f020f2dab718b535ad8b3d41d49ccd..f2ac38b61a1bc15b8b583915069c6a09fe870108 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/firmware.h>
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
@@ -62,6 +63,7 @@ enum wm2000_anc_mode {
 struct wm2000_priv {
        struct i2c_client *i2c;
        struct regmap *regmap;
+       struct clk *mclk;
 
        struct regulator_bulk_data supplies[WM2000_NUM_SUPPLIES];
 
@@ -71,11 +73,12 @@ struct wm2000_priv {
        unsigned int anc_eng_ena:1;
        unsigned int spk_ena:1;
 
-       unsigned int mclk_div:1;
        unsigned int speech_clarity:1;
 
        int anc_download_size;
        char *anc_download;
+
+       struct mutex lock;
 };
 
 static int wm2000_write(struct i2c_client *i2c, unsigned int reg,
@@ -131,6 +134,7 @@ static int wm2000_poll_bit(struct i2c_client *i2c,
 static int wm2000_power_up(struct i2c_client *i2c, int analogue)
 {
        struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+       unsigned long rate;
        int ret;
 
        BUG_ON(wm2000->anc_mode != ANC_OFF);
@@ -143,7 +147,8 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)
                return ret;
        }
 
-       if (!wm2000->mclk_div) {
+       rate = clk_get_rate(wm2000->mclk);
+       if (rate <= 13500000) {
                dev_dbg(&i2c->dev, "Disabling MCLK divider\n");
                wm2000_write(i2c, WM2000_REG_SYS_CTL2,
                             WM2000_MCLK_DIV2_ENA_CLR);
@@ -550,6 +555,15 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000,
                return -EINVAL;
        }
 
+       /* Maintain clock while active */
+       if (anc_transitions[i].source == ANC_OFF) {
+               ret = clk_prepare_enable(wm2000->mclk);
+               if (ret != 0) {
+                       dev_err(&i2c->dev, "Failed to enable MCLK: %d\n", ret);
+                       return ret;
+               }
+       }
+
        for (j = 0; j < ARRAY_SIZE(anc_transitions[j].step); j++) {
                if (!anc_transitions[i].step[j])
                        break;
@@ -559,7 +573,10 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000,
                        return ret;
        }
 
-       return 0;
+       if (anc_transitions[i].dest == ANC_OFF)
+               clk_disable_unprepare(wm2000->mclk);
+
+       return ret;
 }
 
 static int wm2000_anc_set_mode(struct wm2000_priv *wm2000)
@@ -599,13 +616,20 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
        int anc_active = ucontrol->value.enumerated.item[0];
+       int ret;
 
        if (anc_active > 1)
                return -EINVAL;
 
+       mutex_lock(&wm2000->lock);
+
        wm2000->anc_active = anc_active;
 
-       return wm2000_anc_set_mode(wm2000);
+       ret = wm2000_anc_set_mode(wm2000);
+
+       mutex_unlock(&wm2000->lock);
+
+       return ret;
 }
 
 static int wm2000_speaker_get(struct snd_kcontrol *kcontrol,
@@ -625,16 +649,24 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
        int val = ucontrol->value.enumerated.item[0];
+       int ret;
 
        if (val > 1)
                return -EINVAL;
 
+       mutex_lock(&wm2000->lock);
+
        wm2000->spk_ena = val;
 
-       return wm2000_anc_set_mode(wm2000);
+       ret = wm2000_anc_set_mode(wm2000);
+
+       mutex_unlock(&wm2000->lock);
+
+       return ret;
 }
 
 static const struct snd_kcontrol_new wm2000_controls[] = {
+       SOC_SINGLE("ANC Volume", WM2000_REG_ANC_GAIN_CTRL, 0, 255, 0),
        SOC_SINGLE_BOOL_EXT("WM2000 ANC Switch", 0,
                            wm2000_anc_mode_get,
                            wm2000_anc_mode_put),
@@ -648,6 +680,9 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w,
 {
        struct snd_soc_codec *codec = w->codec;
        struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+       int ret;
+
+       mutex_lock(&wm2000->lock);
 
        if (SND_SOC_DAPM_EVENT_ON(event))
                wm2000->anc_eng_ena = 1;
@@ -655,7 +690,11 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w,
        if (SND_SOC_DAPM_EVENT_OFF(event))
                wm2000->anc_eng_ena = 0;
 
-       return wm2000_anc_set_mode(wm2000);
+       ret = wm2000_anc_set_mode(wm2000);
+
+       mutex_unlock(&wm2000->lock);
+
+       return ret;
 }
 
 static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = {
@@ -702,6 +741,9 @@ static bool wm2000_readable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
        case WM2000_REG_SYS_START:
+       case WM2000_REG_ANC_GAIN_CTRL:
+       case WM2000_REG_MSE_TH1:
+       case WM2000_REG_MSE_TH2:
        case WM2000_REG_SPEECH_CLARITY:
        case WM2000_REG_SYS_WATCHDOG:
        case WM2000_REG_ANA_VMID_PD_TIME:
@@ -737,6 +779,8 @@ static int wm2000_probe(struct snd_soc_codec *codec)
 {
        struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
 
+       snd_soc_codec_set_cache_io(codec, 16, 8, SND_SOC_REGMAP);
+
        /* This will trigger a transition to standby mode by default */
        wm2000_anc_set_mode(wm2000);
 
@@ -782,6 +826,8 @@ static int wm2000_i2c_probe(struct i2c_client *i2c,
                return -ENOMEM;
        }
 
+       mutex_init(&wm2000->lock);
+
        dev_set_drvdata(&i2c->dev, wm2000);
 
        wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap);
@@ -823,10 +869,16 @@ static int wm2000_i2c_probe(struct i2c_client *i2c,
        reg = wm2000_read(i2c, WM2000_REG_REVISON);
        dev_info(&i2c->dev, "revision %c\n", reg + 'A');
 
+       wm2000->mclk = devm_clk_get(&i2c->dev, "MCLK");
+       if (IS_ERR(wm2000->mclk)) {
+               ret = PTR_ERR(wm2000->mclk);
+               dev_err(&i2c->dev, "Failed to get MCLK: %d\n", ret);
+               goto err_supplies;
+       }
+
        filename = "wm2000_anc.bin";
        pdata = dev_get_platdata(&i2c->dev);
        if (pdata) {
-               wm2000->mclk_div = pdata->mclkdiv2;
                wm2000->speech_clarity = !pdata->speech_enh_disable;
 
                if (pdata->download_file)
index abcd82a93995b357ba8052b0559178912990923b..fb812cd9e77dd7ea8e44cd7bc48bfd328fc0eadd 100644 (file)
@@ -10,6 +10,9 @@
 #define _WM2000_H
 
 #define WM2000_REG_SYS_START       0x8000
+#define WM2000_REG_ANC_GAIN_CTRL    0x8fa2
+#define WM2000_REG_MSE_TH2          0x8fdf
+#define WM2000_REG_MSE_TH1          0x8fe0
 #define WM2000_REG_SPEECH_CLARITY   0x8fef
 #define WM2000_REG_SYS_WATCHDOG     0x8ff6
 #define WM2000_REG_ANA_VMID_PD_TIME 0x8ff7
index d8c65f5746583474b4cdfb286d339c512f3562d6..ddc98f02ecbd0ea9cf4b61de47490a21bfd3fdb9 100644 (file)
@@ -1109,6 +1109,16 @@ static int wm2200_mixer_values[] = {
        static WM2200_MUX_CTL_DECL(name##_aux5); \
        static WM2200_MUX_CTL_DECL(name##_aux6);
 
+static const char *wm2200_rxanc_input_sel_texts[] = {
+       "None", "IN1", "IN2", "IN3",
+};
+
+static const struct soc_enum wm2200_rxanc_input_sel =
+       SOC_ENUM_SINGLE(WM2200_RXANC_SRC,
+                       WM2200_IN_RXANC_SEL_SHIFT,
+                       ARRAY_SIZE(wm2200_rxanc_input_sel_texts),
+                       wm2200_rxanc_input_sel_texts);
+
 static const struct snd_kcontrol_new wm2200_snd_controls[] = {
 SOC_SINGLE("IN1 High Performance Switch", WM2200_IN1L_CONTROL,
           WM2200_IN1_OSR_SHIFT, 1, 0),
@@ -1126,9 +1136,9 @@ SOC_DOUBLE_R_TLV("IN3 Volume", WM2200_IN3L_CONTROL, WM2200_IN3R_CONTROL,
 
 SOC_DOUBLE_R("IN1 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L,
             WM2200_ADC_DIGITAL_VOLUME_1R, WM2200_IN1L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L,
+SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_2L,
             WM2200_ADC_DIGITAL_VOLUME_2R, WM2200_IN2L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L,
+SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_3L,
             WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_MUTE_SHIFT, 1, 1),
 
 SOC_DOUBLE_R_TLV("IN1 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_1L,
@@ -1141,6 +1151,12 @@ SOC_DOUBLE_R_TLV("IN3 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_3L,
                 WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_DIG_VOL_SHIFT,
                 0xbf, 0, digital_tlv),
 
+SND_SOC_BYTES_MASK("EQL Coefficients", WM2200_EQL_1, 20, WM2200_EQL_ENA),
+SND_SOC_BYTES_MASK("EQR Coefficients", WM2200_EQR_1, 20, WM2200_EQR_ENA),
+
+SND_SOC_BYTES("LHPF1 Coefficeints", WM2200_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficeints", WM2200_HPLPF2_2, 1),
+
 SOC_SINGLE("OUT1 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_1L,
           WM2200_OUT1_OSR_SHIFT, 1, 0),
 SOC_SINGLE("OUT2 High Performance Switch", WM2200_DAC_DIGITAL_VOLUME_2L,
@@ -1162,6 +1178,7 @@ SOC_DOUBLE_R_TLV("OUT2 Digital Volume", WM2200_DAC_DIGITAL_VOLUME_2L,
                 digital_tlv),
 SOC_DOUBLE("OUT2 Switch", WM2200_PDM_1, WM2200_SPK1L_MUTE_SHIFT,
           WM2200_SPK1R_MUTE_SHIFT, 1, 1),
+SOC_ENUM("RxANC Src", wm2200_rxanc_input_sel),
 };
 
 WM2200_MIXER_ENUMS(OUT1L, WM2200_OUT1LMIX_INPUT_1_SOURCE);
@@ -1548,6 +1565,10 @@ static int wm2200_probe(struct snd_soc_codec *codec)
                return ret;
        }
 
+       ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 2);
+       if (ret != 0)
+               return ret;
+
        return ret;
 }
 
@@ -2182,6 +2203,7 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
        struct wm2200_priv *wm2200;
        unsigned int reg;
        int ret, i;
+       int val;
 
        wm2200 = devm_kzalloc(&i2c->dev, sizeof(struct wm2200_priv),
                              GFP_KERNEL);
@@ -2205,6 +2227,9 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
                wm2200->dsp[i].num = i + 1;
                wm2200->dsp[i].dev = &i2c->dev;
                wm2200->dsp[i].regmap = wm2200->regmap;
+               wm2200->dsp[i].sysclk_reg = WM2200_CLOCKING_3;
+               wm2200->dsp[i].sysclk_mask = WM2200_SYSCLK_FREQ_MASK;
+               wm2200->dsp[i].sysclk_shift =  WM2200_SYSCLK_FREQ_SHIFT;
        }
 
        wm2200->dsp[0].base = WM2200_DSP1_CONTROL_1;
@@ -2215,6 +2240,9 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
        wm2200->dsp[1].mem = wm2200_dsp2_regions;
        wm2200->dsp[1].num_mems = ARRAY_SIZE(wm2200_dsp2_regions);
 
+       for (i = 0; i < ARRAY_SIZE(wm2200->dsp); i++)
+               wm_adsp1_init(&wm2200->dsp[i]);
+
        if (pdata)
                wm2200->pdata = *pdata;
 
@@ -2326,6 +2354,36 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
                regmap_write(wm2200->regmap, WM2200_AUDIO_IF_1_16 + i, i);
        }
 
+       for (i = 0; i < WM2200_MAX_MICBIAS; i++) {
+               if (!wm2200->pdata.micbias[i].mb_lvl &&
+                   !wm2200->pdata.micbias[i].bypass)
+                       continue;
+
+               /* Apply default for bypass mode */
+               if (!wm2200->pdata.micbias[i].mb_lvl)
+                       wm2200->pdata.micbias[i].mb_lvl
+                                       = WM2200_MBIAS_LVL_1V5;
+
+               val = (wm2200->pdata.micbias[i].mb_lvl -1)
+                                       << WM2200_MICB1_LVL_SHIFT;
+
+               if (wm2200->pdata.micbias[i].discharge)
+                       val |= WM2200_MICB1_DISCH;
+
+               if (wm2200->pdata.micbias[i].fast_start)
+                       val |= WM2200_MICB1_RATE;
+
+               if (wm2200->pdata.micbias[i].bypass)
+                       val |= WM2200_MICB1_MODE;
+
+               regmap_update_bits(wm2200->regmap,
+                                  WM2200_MIC_BIAS_CTRL_1 + i,
+                                  WM2200_MICB1_LVL_MASK |
+                                  WM2200_MICB1_DISCH |
+                                  WM2200_MICB1_MODE |
+                                  WM2200_MICB1_RATE, val);
+       }
+
        for (i = 0; i < ARRAY_SIZE(wm2200->pdata.in_mode); i++) {
                regmap_update_bits(wm2200->regmap, wm2200_mic_ctrl_reg[i],
                                   WM2200_IN1_MODE_MASK |
index 54397a50807379bf2e625b4f980773ad72515164..ac1745d030d6256525f8d05aebe14cd542de89b3 100644 (file)
@@ -563,6 +563,19 @@ SOC_DOUBLE_R("IN3 Switch", WM5100_ADC_DIGITAL_VOLUME_3L,
 SOC_DOUBLE_R("IN4 Switch", WM5100_ADC_DIGITAL_VOLUME_4L,
             WM5100_ADC_DIGITAL_VOLUME_4R, WM5100_IN4L_MUTE_SHIFT, 1, 1),
 
+SND_SOC_BYTES_MASK("EQ1 Coefficients", WM5100_EQ1_1, 20, WM5100_EQ1_ENA),
+SND_SOC_BYTES_MASK("EQ2 Coefficients", WM5100_EQ2_1, 20, WM5100_EQ2_ENA),
+SND_SOC_BYTES_MASK("EQ3 Coefficients", WM5100_EQ3_1, 20, WM5100_EQ3_ENA),
+SND_SOC_BYTES_MASK("EQ4 Coefficients", WM5100_EQ4_1, 20, WM5100_EQ4_ENA),
+
+SND_SOC_BYTES_MASK("DRC Coefficients", WM5100_DRC1_CTRL1, 5,
+                  WM5100_DRCL_ENA | WM5100_DRCR_ENA),
+
+SND_SOC_BYTES("LHPF1 Coefficeints", WM5100_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficeints", WM5100_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficeints", WM5100_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficeints", WM5100_HPLPF4_2, 1),
+
 SOC_SINGLE("HPOUT1 High Performance Switch", WM5100_OUT_VOLUME_1L,
           WM5100_OUT1_OSR_SHIFT, 1, 0),
 SOC_SINGLE("HPOUT2 High Performance Switch", WM5100_OUT_VOLUME_2L,
index 1440b3f9b7bbe1e04afdfd3d6857808f52972fa9..ab69c83626cd23d529bc95b3ae03a5b314fd2735 100644 (file)
@@ -45,6 +45,7 @@ static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
 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 DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
 
 static const struct wm_adsp_region wm5102_dsp1_regions[] = {
        { .type = WMFW_ADSP2_PM, .base = 0x100000 },
@@ -603,6 +604,17 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+#define WM5102_NG_SRC(name, base) \
+       SOC_SINGLE(name " NG HPOUT1L Switch",  base, 0, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT1R Switch",  base, 1, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT2L Switch",  base, 2, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT2R Switch",  base, 3, 1, 0), \
+       SOC_SINGLE(name " NG EPOUT Switch",    base, 4, 1, 0), \
+       SOC_SINGLE(name " NG SPKOUTL Switch",  base, 6, 1, 0), \
+       SOC_SINGLE(name " NG SPKOUTR Switch",  base, 7, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
 static const struct snd_kcontrol_new wm5102_snd_controls[] = {
 SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
           ARIZONA_IN1_OSR_SHIFT, 1, 0),
@@ -611,32 +623,31 @@ SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
 SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
           ARIZONA_IN3_OSR_SHIFT, 1, 0),
 
-SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL,
-                      ARIZONA_IN1R_CONTROL,
-                      ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL,
-                      ARIZONA_IN2R_CONTROL,
-                      ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL,
-                      ARIZONA_IN3R_CONTROL,
-                      ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-
-SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
-            ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
-            ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
-            ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
-
-SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
-                ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT,
-                0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
-                ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT,
-                0xbf, 0, digital_tlv),
-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_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+                    ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+                    ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+                    ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+                    ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+                    ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+                    ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+              ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+              ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+              ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+              ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+              ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+              ARIZONA_IN3R_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),
@@ -774,6 +785,22 @@ 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),
 
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+          ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+              ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L),
+WM5102_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R),
+WM5102_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L),
+WM5102_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM5102_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM5102_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM5102_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R),
+
 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),
@@ -880,6 +907,18 @@ 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_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
 ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE);
 ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE);
 
@@ -1002,6 +1041,26 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
 SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
                 NULL, 0),
 
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+
 SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
                     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
 SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
@@ -1138,6 +1197,18 @@ ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
 ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
 ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
 
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
 WM_ADSP2("DSP1", 0),
 
 SND_SOC_DAPM_OUTPUT("HPOUT1L"),
@@ -1193,6 +1264,14 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
        { name, "ASRC1R", "ASRC1R" }, \
        { name, "ASRC2L", "ASRC2L" }, \
        { name, "ASRC2R", "ASRC2R" }, \
+       { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+       { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+       { name, "ISRC1INT1", "ISRC1INT1" }, \
+       { name, "ISRC1INT2", "ISRC1INT2" }, \
+       { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+       { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+       { name, "ISRC2INT1", "ISRC2INT1" }, \
+       { name, "ISRC2INT2", "ISRC2INT2" }, \
        { name, "DSP1.1", "DSP1" }, \
        { name, "DSP1.2", "DSP1" }, \
        { name, "DSP1.3", "DSP1" }, \
@@ -1289,6 +1368,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        { "ASRC2L", NULL, "ASRC2L Input" },
        { "ASRC2R", NULL, "ASRC2R Input" },
 
+       { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" },
+       { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" },
+
+       { "ISRC1INT1", NULL, "ISRC1INT1 Input" },
+       { "ISRC1INT2", NULL, "ISRC1INT2 Input" },
+
+       { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" },
+       { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" },
+
+       { "ISRC2INT1", NULL, "ISRC2INT1 Input" },
+       { "ISRC2INT2", NULL, "ISRC2INT2 Input" },
+
        ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
        ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
        ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
@@ -1336,6 +1427,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        ARIZONA_MUX_ROUTES("ASRC2L"),
        ARIZONA_MUX_ROUTES("ASRC2R"),
 
+       ARIZONA_MUX_ROUTES("ISRC1INT1"),
+       ARIZONA_MUX_ROUTES("ISRC1INT2"),
+
+       ARIZONA_MUX_ROUTES("ISRC1DEC1"),
+       ARIZONA_MUX_ROUTES("ISRC1DEC2"),
+
+       ARIZONA_MUX_ROUTES("ISRC2INT1"),
+       ARIZONA_MUX_ROUTES("ISRC2INT2"),
+
+       ARIZONA_MUX_ROUTES("ISRC2DEC1"),
+       ARIZONA_MUX_ROUTES("ISRC2DEC2"),
+
        ARIZONA_DSP_ROUTES("DSP1"),
 
        { "AEC Loopback", "HPOUT1L", "OUT1L" },
@@ -1463,6 +1566,10 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
        if (ret != 0)
                return ret;
 
+       ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 1);
+       if (ret != 0)
+               return ret;
+
        snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
 
        priv->core.arizona->dapm = &codec->dapm;
index 7a090968c4f737bd360f05c2c7a66dc362350d21..a1631320b448669942ae4a23f64aa622798d2373 100644 (file)
@@ -41,6 +41,21 @@ static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
 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 DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+#define WM5110_NG_SRC(name, base) \
+       SOC_SINGLE(name " NG HPOUT1L Switch",  base,  0, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT1R Switch",  base,  1, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT2L Switch",  base,  2, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT2R Switch",  base,  3, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT3L Switch",  base,  4, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT3R Switch",  base,  5, 1, 0), \
+       SOC_SINGLE(name " NG SPKOUTL Switch",  base,  6, 1, 0), \
+       SOC_SINGLE(name " NG SPKOUTR Switch",  base,  7, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT1L Switch", base,  8, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT1R Switch", base,  9, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
 
 static const struct snd_kcontrol_new wm5110_snd_controls[] = {
 SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
@@ -52,37 +67,35 @@ SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
 SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL,
           ARIZONA_IN4_OSR_SHIFT, 1, 0),
 
-SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL,
-                      ARIZONA_IN1R_CONTROL,
-                      ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL,
-                      ARIZONA_IN2R_CONTROL,
-                      ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL,
-                      ARIZONA_IN3R_CONTROL,
-                      ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-
-SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
-            ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
-            ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
-            ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN4 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L,
-            ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_MUTE_SHIFT, 1, 1),
-
-SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
-                ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT,
-                0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
-                ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT,
-                0xbf, 0, digital_tlv),
-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_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_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+                    ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+                    ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+                    ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+                    ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+                    ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+                    ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+              ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+              ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+              ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+              ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+              ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+              ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN4L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L,
+              ARIZONA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R,
+              ARIZONA_IN4R_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),
@@ -263,6 +276,25 @@ SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT,
 SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
 SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
 
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+          ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+              ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM5110_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM5110_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM5110_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L),
+WM5110_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R),
+WM5110_NG_SRC("HPOUT3L", ARIZONA_NOISE_GATE_SELECT_3L),
+WM5110_NG_SRC("HPOUT3R", ARIZONA_NOISE_GATE_SELECT_3R),
+WM5110_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM5110_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM5110_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM5110_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R),
+WM5110_NG_SRC("SPKDAT2L", ARIZONA_NOISE_GATE_SELECT_6L),
+WM5110_NG_SRC("SPKDAT2R", ARIZONA_NOISE_GATE_SELECT_6R),
+
 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),
index fb92fb47d636c1a92d04d1633b9aacf2d42b31d7..ec0efc1443ba770a6c40a2e18c90b117931823bd 100644 (file)
@@ -283,18 +283,16 @@ static int pga_event(struct snd_soc_dapm_widget *w,
                out->ramp = WM8350_RAMP_UP;
                out->active = 1;
 
-               if (!delayed_work_pending(&codec->dapm.delayed_work))
-                       schedule_delayed_work(&codec->dapm.delayed_work,
-                                             msecs_to_jiffies(1));
+               schedule_delayed_work(&codec->dapm.delayed_work,
+                                     msecs_to_jiffies(1));
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
                out->ramp = WM8350_RAMP_DOWN;
                out->active = 0;
 
-               if (!delayed_work_pending(&codec->dapm.delayed_work))
-                       schedule_delayed_work(&codec->dapm.delayed_work,
-                                             msecs_to_jiffies(1));
+               schedule_delayed_work(&codec->dapm.delayed_work,
+                                     msecs_to_jiffies(1));
                break;
        }
 
index d321a875b029dc693fb2b7483dd73ceea1ce4ee8..1704b1e119cb021db8fced2be8482c5da40810d9 100644 (file)
@@ -395,9 +395,6 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id,
                /* power down the PLL before reprogramming it */
                snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1);
 
-               if (!freq_in || !freq_out)
-                       return 0;
-
                /* set PLLN and PRESCALE */
                snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10,
                                    pll_div.n | (pll_div.prescale << 4));
index bd4b0db4cdaadb11fe12caed12b8e8e2f5bab7b1..e9710280e5e18b882242f8d5b4cb61a2492e8eae 100644 (file)
@@ -2873,22 +2873,20 @@ static int wm8962_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
 
        ret = 0;
 
-       if (fll1 & WM8962_FLL_ENA) {
-               /* This should be a massive overestimate but go even
-                * higher if we'll error out
-                */
-               if (wm8962->irq)
-                       timeout = msecs_to_jiffies(5);
-               else
-                       timeout = msecs_to_jiffies(1);
+       /* This should be a massive overestimate but go even
+        * higher if we'll error out
+        */
+       if (wm8962->irq)
+               timeout = msecs_to_jiffies(5);
+       else
+               timeout = msecs_to_jiffies(1);
 
-               timeout = wait_for_completion_timeout(&wm8962->fll_lock,
-                                                     timeout);
+       timeout = wait_for_completion_timeout(&wm8962->fll_lock,
+                                             timeout);
 
-               if (timeout == 0 && wm8962->irq) {
-                       dev_err(codec->dev, "FLL lock timed out");
-                       ret = -ETIMEDOUT;
-               }
+       if (timeout == 0 && wm8962->irq) {
+               dev_err(codec->dev, "FLL lock timed out");
+               ret = -ETIMEDOUT;
        }
 
        wm8962->fll_fref = Fref;
@@ -3189,7 +3187,7 @@ static void wm8962_init_beep(struct snd_soc_codec *codec)
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
        int ret;
 
-       wm8962->beep = input_allocate_device();
+       wm8962->beep = devm_input_allocate_device(codec->dev);
        if (!wm8962->beep) {
                dev_err(codec->dev, "Failed to allocate beep device\n");
                return;
@@ -3210,7 +3208,6 @@ static void wm8962_init_beep(struct snd_soc_codec *codec)
 
        ret = input_register_device(wm8962->beep);
        if (ret != 0) {
-               input_free_device(wm8962->beep);
                wm8962->beep = NULL;
                dev_err(codec->dev, "Failed to register beep device\n");
        }
@@ -3227,7 +3224,6 @@ static void wm8962_free_beep(struct snd_soc_codec *codec)
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
 
        device_remove_file(codec->dev, &dev_attr_beep);
-       input_unregister_device(wm8962->beep);
        cancel_work_sync(&wm8962->beep_work);
        wm8962->beep = NULL;
 
@@ -3758,10 +3754,17 @@ static const struct i2c_device_id wm8962_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, wm8962_i2c_id);
 
+static const struct of_device_id wm8962_of_match[] = {
+       { .compatible = "wlf,wm8962", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8962_of_match);
+
 static struct i2c_driver wm8962_i2c_driver = {
        .driver = {
                .name = "wm8962",
                .owner = THIS_MODULE,
+               .of_match_table = wm8962_of_match,
                .pm = &wm8962_pm,
        },
        .probe =    wm8962_i2c_probe,
index 9fe1e041da498cec9bc335959c92d569ca0102b9..c9c707b8698f36f160e63f94e9a91e5bad989103 100644 (file)
@@ -851,30 +851,33 @@ static int wm8983_set_pll(struct snd_soc_dai *dai, int pll_id,
        struct pll_div pll_div;
 
        codec = dai->codec;
-       if (freq_in && freq_out) {
+       if (!freq_in || !freq_out) {
+               /* disable the PLL */
+               snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+                                   WM8983_PLLEN_MASK, 0);
+               return 0;
+       } else {
                ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in);
                if (ret)
                        return ret;
-       }
-
-       /* disable the PLL before re-programming it */
-       snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
-                           WM8983_PLLEN_MASK, 0);
 
-       if (!freq_in || !freq_out)
-               return 0;
+               /* disable the PLL before re-programming it */
+               snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+                                   WM8983_PLLEN_MASK, 0);
+
+               /* set PLLN and PRESCALE */
+               snd_soc_write(codec, WM8983_PLL_N,
+                       (pll_div.div2 << WM8983_PLL_PRESCALE_SHIFT)
+                       | pll_div.n);
+               /* set PLLK */
+               snd_soc_write(codec, WM8983_PLL_K_3, pll_div.k & 0x1ff);
+               snd_soc_write(codec, WM8983_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
+               snd_soc_write(codec, WM8983_PLL_K_1, (pll_div.k >> 18));
+               /* enable the PLL */
+               snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
+                                       WM8983_PLLEN_MASK, WM8983_PLLEN);
+       }
 
-       /* set PLLN and PRESCALE */
-       snd_soc_write(codec, WM8983_PLL_N,
-                     (pll_div.div2 << WM8983_PLL_PRESCALE_SHIFT)
-                     | pll_div.n);
-       /* set PLLK */
-       snd_soc_write(codec, WM8983_PLL_K_3, pll_div.k & 0x1ff);
-       snd_soc_write(codec, WM8983_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
-       snd_soc_write(codec, WM8983_PLL_K_1, (pll_div.k >> 18));
-       /* enable the PLL */
-       snd_soc_update_bits(codec, WM8983_POWER_MANAGEMENT_1,
-                           WM8983_PLLEN_MASK, WM8983_PLLEN);
        return 0;
 }
 
index ab3782657ac81e7ae11afa0142e1d2d5a1e4d483..dd6ce3bc01cf2828279b2fc13f39b3953506d391 100644 (file)
@@ -830,33 +830,30 @@ static int wm8985_set_pll(struct snd_soc_dai *dai, int pll_id,
        struct pll_div pll_div;
 
        codec = dai->codec;
-       if (freq_in && freq_out) {
+       if (!freq_in || !freq_out) {
+               /* disable the PLL */
+               snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+                                   WM8985_PLLEN_MASK, 0);
+       } else {
                ret = pll_factors(&pll_div, freq_out * 4 * 2, freq_in);
                if (ret)
                        return ret;
-       }
 
-       /* disable the PLL before reprogramming it */
-       snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
-                           WM8985_PLLEN_MASK, 0);
-       
-       if (!freq_in || !freq_out)
-               return 0;
-
-       /* set PLLN and PRESCALE */
-       snd_soc_write(codec, WM8985_PLL_N,
-                     (pll_div.div2 << WM8985_PLL_PRESCALE_SHIFT)
-                     | pll_div.n);
-       /* set PLLK */
-       snd_soc_write(codec, WM8985_PLL_K_3, pll_div.k & 0x1ff);
-       snd_soc_write(codec, WM8985_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
-       snd_soc_write(codec, WM8985_PLL_K_1, (pll_div.k >> 18));
-       /* set the source of the clock to be the PLL */
-       snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
-                           WM8985_CLKSEL_MASK, WM8985_CLKSEL);
-       /* enable the PLL */
-       snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
-                           WM8985_PLLEN_MASK, WM8985_PLLEN);
+               /* set PLLN and PRESCALE */
+               snd_soc_write(codec, WM8985_PLL_N,
+                             (pll_div.div2 << WM8985_PLL_PRESCALE_SHIFT)
+                             | pll_div.n);
+               /* set PLLK */
+               snd_soc_write(codec, WM8985_PLL_K_3, pll_div.k & 0x1ff);
+               snd_soc_write(codec, WM8985_PLL_K_2, (pll_div.k >> 9) & 0x1ff);
+               snd_soc_write(codec, WM8985_PLL_K_1, (pll_div.k >> 18));
+               /* set the source of the clock to be the PLL */
+               snd_soc_update_bits(codec, WM8985_CLOCK_GEN_CONTROL,
+                                   WM8985_CLKSEL_MASK, WM8985_CLKSEL);
+               /* enable the PLL */
+               snd_soc_update_bits(codec, WM8985_POWER_MANAGEMENT_1,
+                                   WM8985_PLLEN_MASK, WM8985_PLLEN);
+       }
        return 0;
 }
 
index b6b65483758518f8b8cab7672af882ef923d2768..f3f7e75f86280ef5160facc2a7f3cfa7972c6b91 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/firmware.h>
+#include <linux/list.h>
 #include <linux/pm.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #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
+/*
+ * ADSP1 Control 31
+ */
+#define ADSP1_CLK_SEL_MASK                0x0007  /* CLK_SEL_ENA */
+#define ADSP1_CLK_SEL_SHIFT                    0  /* CLK_SEL_ENA */
+#define ADSP1_CLK_SEL_WIDTH                    3  /* CLK_SEL_ENA */
+
+#define ADSP2_CONTROL        0x0
+#define ADSP2_CLOCKING       0x1
+#define ADSP2_STATUS1        0x4
+#define ADSP2_WDMA_CONFIG_1 0x30
+#define ADSP2_WDMA_CONFIG_2 0x31
+#define ADSP2_RDMA_CONFIG_1 0x34
 
 /*
  * ADSP2 Control
 #define ADSP2_RAM_RDY_SHIFT                    0
 #define ADSP2_RAM_RDY_WIDTH                    1
 
+struct wm_adsp_buf {
+       struct list_head list;
+       void *buf;
+};
+
+static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
+                                            struct list_head *list)
+{
+       struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+
+       if (buf == NULL)
+               return NULL;
+
+       buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA);
+       if (!buf->buf) {
+               kfree(buf);
+               return NULL;
+       }
+
+       if (list)
+               list_add_tail(&buf->list, list);
+
+       return buf;
+}
+
+static void wm_adsp_buf_free(struct list_head *list)
+{
+       while (!list_empty(list)) {
+               struct wm_adsp_buf *buf = list_first_entry(list,
+                                                          struct wm_adsp_buf,
+                                                          list);
+               list_del(&buf->list);
+               kfree(buf->buf);
+               kfree(buf);
+       }
+}
+
+#define WM_ADSP_NUM_FW 4
+
+static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
+       "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC"
+};
+
+static struct {
+       const char *file;
+} wm_adsp_fw[WM_ADSP_NUM_FW] = {
+       { .file = "mbc-vss" },
+       { .file = "tx" },
+       { .file = "tx-spk" },
+       { .file = "rx-anc" },
+};
+
+static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
+
+       return 0;
+}
+
+static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+
+       if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
+               return 0;
+
+       if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
+               return -EINVAL;
+
+       if (adsp[e->shift_l].running)
+               return -EBUSY;
+
+       adsp[e->shift_l].fw = ucontrol->value.integer.value[0];
+
+       return 0;
+}
+
+static const struct soc_enum wm_adsp_fw_enum[] = {
+       SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+       SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+       SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+       SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+};
+
+const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
+       SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
+                    wm_adsp_fw_get, wm_adsp_fw_put),
+       SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
+                    wm_adsp_fw_get, wm_adsp_fw_put),
+       SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
+                    wm_adsp_fw_get, wm_adsp_fw_put),
+       SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
+                    wm_adsp_fw_get, wm_adsp_fw_put),
+};
+EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
 
 static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
                                                        int type)
@@ -156,8 +270,29 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
        return NULL;
 }
 
+static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
+                                         unsigned int offset)
+{
+       switch (region->type) {
+       case WMFW_ADSP1_PM:
+               return region->base + (offset * 3);
+       case WMFW_ADSP1_DM:
+               return region->base + (offset * 2);
+       case WMFW_ADSP2_XM:
+               return region->base + (offset * 2);
+       case WMFW_ADSP2_YM:
+               return region->base + (offset * 2);
+       case WMFW_ADSP1_ZM:
+               return region->base + (offset * 2);
+       default:
+               WARN_ON(NULL != "Unknown memory region type");
+               return offset;
+       }
+}
+
 static int wm_adsp_load(struct wm_adsp *dsp)
 {
+       LIST_HEAD(buf_list);
        const struct firmware *firmware;
        struct regmap *regmap = dsp->regmap;
        unsigned int pos = 0;
@@ -169,7 +304,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)
        const struct wm_adsp_region *mem;
        const char *region_name;
        char *file, *text;
-       void *buf;
+       struct wm_adsp_buf *buf;
        unsigned int reg;
        int regions = 0;
        int ret, offset, type, sizes;
@@ -178,7 +313,8 @@ static int wm_adsp_load(struct wm_adsp *dsp)
        if (file == NULL)
                return -ENOMEM;
 
-       snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num);
+       snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
+                wm_adsp_fw[dsp->fw].file);
        file[PAGE_SIZE - 1] = '\0';
 
        ret = request_firmware(&firmware, file, dsp->dev);
@@ -283,27 +419,27 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                case WMFW_ADSP1_PM:
                        BUG_ON(!mem);
                        region_name = "PM";
-                       reg = mem->base + (offset * 3);
+                       reg = wm_adsp_region_to_reg(mem, offset);
                        break;
                case WMFW_ADSP1_DM:
                        BUG_ON(!mem);
                        region_name = "DM";
-                       reg = mem->base + (offset * 2);
+                       reg = wm_adsp_region_to_reg(mem, offset);
                        break;
                case WMFW_ADSP2_XM:
                        BUG_ON(!mem);
                        region_name = "XM";
-                       reg = mem->base + (offset * 2);
+                       reg = wm_adsp_region_to_reg(mem, offset);
                        break;
                case WMFW_ADSP2_YM:
                        BUG_ON(!mem);
                        region_name = "YM";
-                       reg = mem->base + (offset * 2);
+                       reg = wm_adsp_region_to_reg(mem, offset);
                        break;
                case WMFW_ADSP1_ZM:
                        BUG_ON(!mem);
                        region_name = "ZM";
-                       reg = mem->base + (offset * 2);
+                       reg = wm_adsp_region_to_reg(mem, offset);
                        break;
                default:
                        adsp_warn(dsp,
@@ -323,18 +459,16 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                }
 
                if (reg) {
-                       buf = kmemdup(region->data, le32_to_cpu(region->len),
-                                     GFP_KERNEL | GFP_DMA);
+                       buf = wm_adsp_buf_alloc(region->data,
+                                               le32_to_cpu(region->len),
+                                               &buf_list);
                        if (!buf) {
                                adsp_err(dsp, "Out of memory\n");
                                return -ENOMEM;
                        }
 
-                       ret = regmap_raw_write(regmap, reg, buf,
-                                              le32_to_cpu(region->len));
-
-                       kfree(buf);
-
+                       ret = regmap_raw_write_async(regmap, reg, buf->buf,
+                                                    le32_to_cpu(region->len));
                        if (ret != 0) {
                                adsp_err(dsp,
                                        "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
@@ -348,12 +482,20 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                pos += le32_to_cpu(region->len) + sizeof(*region);
                regions++;
        }
-       
+
+       ret = regmap_async_complete(regmap);
+       if (ret != 0) {
+               adsp_err(dsp, "Failed to complete async write: %d\n", ret);
+               goto out_fw;
+       }
+
        if (pos > firmware->size)
                adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
                          file, regions, pos - firmware->size);
 
 out_fw:
+       regmap_async_complete(regmap);
+       wm_adsp_buf_free(&buf_list);
        release_firmware(firmware);
 out:
        kfree(file);
@@ -361,22 +503,222 @@ out:
        return ret;
 }
 
+static int wm_adsp_setup_algs(struct wm_adsp *dsp)
+{
+       struct regmap *regmap = dsp->regmap;
+       struct wmfw_adsp1_id_hdr adsp1_id;
+       struct wmfw_adsp2_id_hdr adsp2_id;
+       struct wmfw_adsp1_alg_hdr *adsp1_alg;
+       struct wmfw_adsp2_alg_hdr *adsp2_alg;
+       void *alg, *buf;
+       struct wm_adsp_alg_region *region;
+       const struct wm_adsp_region *mem;
+       unsigned int pos, term;
+       size_t algs, buf_size;
+       __be32 val;
+       int i, ret;
+
+       switch (dsp->type) {
+       case WMFW_ADSP1:
+               mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
+               break;
+       case WMFW_ADSP2:
+               mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
+               break;
+       default:
+               mem = NULL;
+               break;
+       }
+
+       if (mem == NULL) {
+               BUG_ON(mem != NULL);
+               return -EINVAL;
+       }
+
+       switch (dsp->type) {
+       case WMFW_ADSP1:
+               ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
+                                     sizeof(adsp1_id));
+               if (ret != 0) {
+                       adsp_err(dsp, "Failed to read algorithm info: %d\n",
+                                ret);
+                       return ret;
+               }
+
+               buf = &adsp1_id;
+               buf_size = sizeof(adsp1_id);
+
+               algs = be32_to_cpu(adsp1_id.algs);
+               adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+                         be32_to_cpu(adsp1_id.fw.id),
+                         (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
+                         (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
+                         be32_to_cpu(adsp1_id.fw.ver) & 0xff,
+                         algs);
+
+               pos = sizeof(adsp1_id) / 2;
+               term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
+               break;
+
+       case WMFW_ADSP2:
+               ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
+                                     sizeof(adsp2_id));
+               if (ret != 0) {
+                       adsp_err(dsp, "Failed to read algorithm info: %d\n",
+                                ret);
+                       return ret;
+               }
+
+               buf = &adsp2_id;
+               buf_size = sizeof(adsp2_id);
+
+               algs = be32_to_cpu(adsp2_id.algs);
+               adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+                         be32_to_cpu(adsp2_id.fw.id),
+                         (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
+                         (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
+                         be32_to_cpu(adsp2_id.fw.ver) & 0xff,
+                         algs);
+
+               pos = sizeof(adsp2_id) / 2;
+               term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
+               break;
+
+       default:
+               BUG_ON(NULL == "Unknown DSP type");
+               return -EINVAL;
+       }
+
+       if (algs == 0) {
+               adsp_err(dsp, "No algorithms\n");
+               return -EINVAL;
+       }
+
+       if (algs > 1024) {
+               adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
+               print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
+                                    buf, buf_size);
+               return -EINVAL;
+       }
+
+       /* Read the terminator first to validate the length */
+       ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
+       if (ret != 0) {
+               adsp_err(dsp, "Failed to read algorithm list end: %d\n",
+                       ret);
+               return ret;
+       }
+
+       if (be32_to_cpu(val) != 0xbedead)
+               adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
+                         term, be32_to_cpu(val));
+
+       alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
+       if (!alg)
+               return -ENOMEM;
+
+       ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
+       if (ret != 0) {
+               adsp_err(dsp, "Failed to read algorithm list: %d\n",
+                       ret);
+               goto out;
+       }
+
+       adsp1_alg = alg;
+       adsp2_alg = alg;
+
+       for (i = 0; i < algs; i++) {
+               switch (dsp->type) {
+               case WMFW_ADSP1:
+                       adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
+                                 i, be32_to_cpu(adsp1_alg[i].alg.id),
+                                 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
+                                 (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
+                                 be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
+                                 be32_to_cpu(adsp1_alg[i].dm),
+                                 be32_to_cpu(adsp1_alg[i].zm));
+
+                       region = kzalloc(sizeof(*region), GFP_KERNEL);
+                       if (!region)
+                               return -ENOMEM;
+                       region->type = WMFW_ADSP1_DM;
+                       region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
+                       region->base = be32_to_cpu(adsp1_alg[i].dm);
+                       list_add_tail(&region->list, &dsp->alg_regions);
+
+                       region = kzalloc(sizeof(*region), GFP_KERNEL);
+                       if (!region)
+                               return -ENOMEM;
+                       region->type = WMFW_ADSP1_ZM;
+                       region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
+                       region->base = be32_to_cpu(adsp1_alg[i].zm);
+                       list_add_tail(&region->list, &dsp->alg_regions);
+                       break;
+
+               case WMFW_ADSP2:
+                       adsp_info(dsp,
+                                 "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
+                                 i, be32_to_cpu(adsp2_alg[i].alg.id),
+                                 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
+                                 (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
+                                 be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
+                                 be32_to_cpu(adsp2_alg[i].xm),
+                                 be32_to_cpu(adsp2_alg[i].ym),
+                                 be32_to_cpu(adsp2_alg[i].zm));
+
+                       region = kzalloc(sizeof(*region), GFP_KERNEL);
+                       if (!region)
+                               return -ENOMEM;
+                       region->type = WMFW_ADSP2_XM;
+                       region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+                       region->base = be32_to_cpu(adsp2_alg[i].xm);
+                       list_add_tail(&region->list, &dsp->alg_regions);
+
+                       region = kzalloc(sizeof(*region), GFP_KERNEL);
+                       if (!region)
+                               return -ENOMEM;
+                       region->type = WMFW_ADSP2_YM;
+                       region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+                       region->base = be32_to_cpu(adsp2_alg[i].ym);
+                       list_add_tail(&region->list, &dsp->alg_regions);
+
+                       region = kzalloc(sizeof(*region), GFP_KERNEL);
+                       if (!region)
+                               return -ENOMEM;
+                       region->type = WMFW_ADSP2_ZM;
+                       region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+                       region->base = be32_to_cpu(adsp2_alg[i].zm);
+                       list_add_tail(&region->list, &dsp->alg_regions);
+                       break;
+               }
+       }
+
+out:
+       kfree(alg);
+       return ret;
+}
+
 static int wm_adsp_load_coeff(struct wm_adsp *dsp)
 {
+       LIST_HEAD(buf_list);
        struct regmap *regmap = dsp->regmap;
        struct wmfw_coeff_hdr *hdr;
        struct wmfw_coeff_item *blk;
        const struct firmware *firmware;
+       const struct wm_adsp_region *mem;
+       struct wm_adsp_alg_region *alg_region;
        const char *region_name;
        int ret, pos, blocks, type, offset, reg;
        char *file;
-       void *buf;
+       struct wm_adsp_buf *buf;
+       int tmp;
 
        file = kzalloc(PAGE_SIZE, GFP_KERNEL);
        if (file == NULL)
                return -ENOMEM;
 
-       snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num);
+       snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
+                wm_adsp_fw[dsp->fw].file);
        file[PAGE_SIZE - 1] = '\0';
 
        ret = request_firmware(&firmware, file, dsp->dev);
@@ -399,6 +741,16 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                goto out_fw;
        }
 
+       switch (be32_to_cpu(hdr->rev) & 0xff) {
+       case 1:
+               break;
+       default:
+               adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
+                        file, be32_to_cpu(hdr->rev) & 0xff);
+               ret = -EINVAL;
+               goto out_fw;
+       }
+
        adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
                (le32_to_cpu(hdr->ver) >> 16) & 0xff,
                (le32_to_cpu(hdr->ver) >>  8) & 0xff,
@@ -411,8 +763,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
               pos - firmware->size > sizeof(*blk)) {
                blk = (void*)(&firmware->data[pos]);
 
-               type = be32_to_cpu(blk->type) & 0xff;
-               offset = le32_to_cpu(blk->offset) & 0xffffff;
+               type = le16_to_cpu(blk->type);
+               offset = le16_to_cpu(blk->offset);
 
                adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
                         file, blocks, le32_to_cpu(blk->id),
@@ -425,52 +777,105 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
                reg = 0;
                region_name = "Unknown";
                switch (type) {
-               case WMFW_NAME_TEXT:
-               case WMFW_INFO_TEXT:
+               case (WMFW_NAME_TEXT << 8):
+               case (WMFW_INFO_TEXT << 8):
                        break;
-               case WMFW_ABSOLUTE:
+               case (WMFW_ABSOLUTE << 8):
                        region_name = "register";
                        reg = offset;
                        break;
+
+               case WMFW_ADSP1_DM:
+               case WMFW_ADSP1_ZM:
+               case WMFW_ADSP2_XM:
+               case WMFW_ADSP2_YM:
+                       adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
+                                file, blocks, le32_to_cpu(blk->len),
+                                type, le32_to_cpu(blk->id));
+
+                       mem = wm_adsp_find_region(dsp, type);
+                       if (!mem) {
+                               adsp_err(dsp, "No base for region %x\n", type);
+                               break;
+                       }
+
+                       reg = 0;
+                       list_for_each_entry(alg_region,
+                                           &dsp->alg_regions, list) {
+                               if (le32_to_cpu(blk->id) == alg_region->alg &&
+                                   type == alg_region->type) {
+                                       reg = alg_region->base;
+                                       reg = wm_adsp_region_to_reg(mem,
+                                                                   reg);
+                                       reg += offset;
+                               }
+                       }
+
+                       if (reg == 0)
+                               adsp_err(dsp, "No %x for algorithm %x\n",
+                                        type, le32_to_cpu(blk->id));
+                       break;
+
                default:
-                       adsp_err(dsp, "Unknown region type %x\n", type);
+                       adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
+                                file, blocks, type, pos);
                        break;
                }
 
                if (reg) {
-                       buf = kmemdup(blk->data, le32_to_cpu(blk->len),
-                                     GFP_KERNEL | GFP_DMA);
+                       buf = wm_adsp_buf_alloc(blk->data,
+                                               le32_to_cpu(blk->len),
+                                               &buf_list);
                        if (!buf) {
                                adsp_err(dsp, "Out of memory\n");
                                return -ENOMEM;
                        }
 
-                       ret = regmap_raw_write(regmap, reg, blk->data,
-                                              le32_to_cpu(blk->len));
+                       adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
+                                file, blocks, le32_to_cpu(blk->len),
+                                reg);
+                       ret = regmap_raw_write_async(regmap, reg, buf->buf,
+                                                    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);
                        }
-
-                       kfree(buf);
                }
 
-               pos += le32_to_cpu(blk->len) + sizeof(*blk);
+               tmp = le32_to_cpu(blk->len) % 4;
+               if (tmp)
+                       pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk);
+               else
+                       pos += le32_to_cpu(blk->len) + sizeof(*blk);
+
                blocks++;
        }
 
+       ret = regmap_async_complete(regmap);
+       if (ret != 0)
+               adsp_err(dsp, "Failed to complete async write: %d\n", ret);
+
        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);
+       wm_adsp_buf_free(&buf_list);
 out:
        kfree(file);
        return 0;
 }
 
+int wm_adsp1_init(struct wm_adsp *adsp)
+{
+       INIT_LIST_HEAD(&adsp->alg_regions);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp1_init);
+
 int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                   struct snd_kcontrol *kcontrol,
                   int event)
@@ -479,16 +884,46 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
        struct wm_adsp *dsp = &dsps[w->shift];
        int ret;
+       int val;
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
                regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
                                   ADSP1_SYS_ENA, ADSP1_SYS_ENA);
 
+               /*
+                * For simplicity set the DSP clock rate to be the
+                * SYSCLK rate rather than making it configurable.
+                */
+               if(dsp->sysclk_reg) {
+                       ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
+                       if (ret != 0) {
+                               adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
+                               ret);
+                               return ret;
+                       }
+
+                       val = (val & dsp->sysclk_mask)
+                               >> dsp->sysclk_shift;
+
+                       ret = regmap_update_bits(dsp->regmap,
+                                                dsp->base + ADSP1_CONTROL_31,
+                                                ADSP1_CLK_SEL_MASK, val);
+                       if (ret != 0) {
+                               adsp_err(dsp, "Failed to set clock rate: %d\n",
+                                        ret);
+                               return ret;
+                       }
+               }
+
                ret = wm_adsp_load(dsp);
                if (ret != 0)
                        goto err;
 
+               ret = wm_adsp_setup_algs(dsp);
+               if (ret != 0)
+                       goto err;
+
                ret = wm_adsp_load_coeff(dsp);
                if (ret != 0)
                        goto err;
@@ -560,6 +995,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
        struct snd_soc_codec *codec = w->codec;
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
        struct wm_adsp *dsp = &dsps[w->shift];
+       struct wm_adsp_alg_region *alg_region;
        unsigned int val;
        int ret;
 
@@ -625,6 +1061,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                if (ret != 0)
                        goto err;
 
+               ret = wm_adsp_setup_algs(dsp);
+               if (ret != 0)
+                       goto err;
+
                ret = wm_adsp_load_coeff(dsp);
                if (ret != 0)
                        goto err;
@@ -635,13 +1075,22 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                                         ADSP2_CORE_ENA | ADSP2_START);
                if (ret != 0)
                        goto err;
+
+               dsp->running = true;
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
+               dsp->running = false;
+
                regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
                                   ADSP2_SYS_ENA | ADSP2_CORE_ENA |
                                   ADSP2_START, 0);
 
+               /* Make sure DMAs are quiesced */
+               regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
+               regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
+               regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+
                if (dsp->dvfs) {
                        ret = regulator_set_voltage(dsp->dvfs, 1200000,
                                                    1800000);
@@ -656,6 +1105,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                                        "Failed to enable supply: %d\n",
                                        ret);
                }
+
+               while (!list_empty(&dsp->alg_regions)) {
+                       alg_region = list_first_entry(&dsp->alg_regions,
+                                                     struct wm_adsp_alg_region,
+                                                     list);
+                       list_del(&alg_region->list);
+                       kfree(alg_region);
+               }
                break;
 
        default:
@@ -685,6 +1142,8 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
                return ret;
        }
 
+       INIT_LIST_HEAD(&adsp->alg_regions);
+
        if (dvfs) {
                adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
                if (IS_ERR(adsp->dvfs)) {
index ffd29a4609e2a50b1214fc5389a73906973d9538..cb8871a3ec0015854c6d13bebb1055661b7cf4be 100644 (file)
@@ -25,6 +25,13 @@ struct wm_adsp_region {
        unsigned int base;
 };
 
+struct wm_adsp_alg_region {
+       struct list_head list;
+       unsigned int alg;
+       int type;
+       unsigned int base;
+};
+
 struct wm_adsp {
        const char *part;
        int num;
@@ -33,10 +40,18 @@ struct wm_adsp {
        struct regmap *regmap;
 
        int base;
+       int sysclk_reg;
+       int sysclk_mask;
+       int sysclk_shift;
+
+       struct list_head alg_regions;
 
        const struct wm_adsp_region *mem;
        int num_mems;
 
+       int fw;
+       bool running;
+
        struct regulator *dvfs;
 };
 
@@ -50,6 +65,9 @@ struct wm_adsp {
        .shift = num, .event = wm_adsp2_event, \
        .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
 
+extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
+
+int wm_adsp1_init(struct wm_adsp *adsp);
 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);
index 5632ded67fdd1bee57cb66ebbbff3c2de3289991..ef163360a7459ebd7a8a70af00118cbcdb95717e 100644 (file)
@@ -93,15 +93,20 @@ struct wmfw_adsp2_alg_hdr {
 struct wmfw_coeff_hdr {
        u8 magic[4];
        __le32 len;
-       __le32 ver;
+       union {
+               __be32 rev;
+               __le32 ver;
+       };
+       union {
+               __be32 core;
+               __le32 core_ver;
+       };
        u8 data[];
 } __packed;
 
 struct wmfw_coeff_item {
-       union {
-               __be32 type;
-               __le32 offset;
-       };
+       __le16 offset;
+       __le16 type;
        __le32 id;
        __le32 ver;
        __le32 sr;
index d55e6477bff0c9a855379d26db50538ff7b5e178..484b22c5df5df80a5a69d3a8745bb37f91eb49b8 100644 (file)
@@ -116,9 +116,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
        {"Line Out", NULL, "RLOUT"},
 
        /* Mic connected to (MIC3L | MIC3R) */
-       {"MIC3L", NULL, "Mic Bias 2V"},
-       {"MIC3R", NULL, "Mic Bias 2V"},
-       {"Mic Bias 2V", NULL, "Mic Jack"},
+       {"MIC3L", NULL, "Mic Bias"},
+       {"MIC3R", NULL, "Mic Bias"},
+       {"Mic Bias", NULL, "Mic Jack"},
 
        /* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */
        {"LINE1L", NULL, "Line In"},
index 55e2bf652beff53d6694e3cf20d2d2e4d6f17393..9321e5c9d8c125b3d8894dda33df44d94b7068dc 100644 (file)
@@ -626,7 +626,7 @@ static int davinci_config_channel_size(struct davinci_audio_dev *dev,
                                       int word_length)
 {
        u32 fmt;
-       u32 rotate = (32 - word_length) / 4;
+       u32 rotate = (word_length / 4) & 0x7;
        u32 mask = (1ULL << word_length) - 1;
 
        /*
index 1aa51300c564b3d7b6e3c4a55eb61268d5e1f9b0..deb30d59965e770a9c8096f5c92b1767f51f96cf 100644 (file)
@@ -210,15 +210,19 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
        switch (config->chan_nr) {
        case EIGHT_CHANNEL_SUPPORT:
                ch_reg = 3;
+               break;
        case SIX_CHANNEL_SUPPORT:
                ch_reg = 2;
+               break;
        case FOUR_CHANNEL_SUPPORT:
                ch_reg = 1;
+               break;
        case TWO_CHANNEL_SUPPORT:
                ch_reg = 0;
                break;
        default:
                dev_err(dev->dev, "channel not supported\n");
+               return -EINVAL;
        }
 
        i2s_disable_channels(dev, substream->stream);
index b4b4cab30232ad2a6c712cf35c84521872d7ebfa..6cf8355a8542656c89dda5f1564c721d3f2cb4aa 100644 (file)
 #define asoc_simple_get_card_info(p) \
        container_of(p->dai_link, struct asoc_simple_card_info, snd_link)
 
+static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
+                                      struct asoc_simple_dai *set,
+                                      unsigned int daifmt)
+{
+       int ret = 0;
+
+       daifmt |= set->fmt;
+
+       if (!ret && daifmt)
+               ret = snd_soc_dai_set_fmt(dai, daifmt);
+
+       if (!ret && set->sysclk)
+               ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0);
+
+       return ret;
+}
+
 static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
-       struct asoc_simple_card_info *cinfo = asoc_simple_get_card_info(rtd);
-       struct asoc_simple_dai_init_info *iinfo = cinfo->init;
+       struct asoc_simple_card_info *info = asoc_simple_get_card_info(rtd);
        struct snd_soc_dai *codec = rtd->codec_dai;
        struct snd_soc_dai *cpu = rtd->cpu_dai;
-       unsigned int cpu_daifmt = iinfo->fmt | iinfo->cpu_daifmt;
-       unsigned int codec_daifmt = iinfo->fmt | iinfo->codec_daifmt;
+       unsigned int daifmt = info->daifmt;
        int ret;
 
-       if (codec_daifmt) {
-               ret = snd_soc_dai_set_fmt(codec, codec_daifmt);
-               if (ret < 0)
-                       return ret;
-       }
-
-       if (iinfo->sysclk) {
-               ret = snd_soc_dai_set_sysclk(codec, 0, iinfo->sysclk, 0);
-               if (ret < 0)
-                       return ret;
-       }
+       ret = __asoc_simple_card_dai_init(codec, &info->codec_dai, daifmt);
+       if (ret < 0)
+               return ret;
 
-       if (cpu_daifmt) {
-               ret = snd_soc_dai_set_fmt(cpu, cpu_daifmt);
-               if (ret < 0)
-                       return ret;
-       }
+       ret = __asoc_simple_card_dai_init(cpu, &info->cpu_dai, daifmt);
+       if (ret < 0)
+               return ret;
 
        return 0;
 }
@@ -50,19 +55,20 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
 static int asoc_simple_card_probe(struct platform_device *pdev)
 {
        struct asoc_simple_card_info *cinfo = pdev->dev.platform_data;
+       struct device *dev = &pdev->dev;
 
        if (!cinfo) {
-               dev_err(&pdev->dev, "no info for asoc-simple-card\n");
+               dev_err(dev, "no info for asoc-simple-card\n");
                return -EINVAL;
        }
 
        if (!cinfo->name        ||
            !cinfo->card        ||
-           !cinfo->cpu_dai     ||
            !cinfo->codec       ||
            !cinfo->platform    ||
-           !cinfo->codec_dai) {
-               dev_err(&pdev->dev, "insufficient asoc_simple_card_info settings\n");
+           !cinfo->cpu_dai.name ||
+           !cinfo->codec_dai.name) {
+               dev_err(dev, "insufficient asoc_simple_card_info settings\n");
                return -EINVAL;
        }
 
@@ -71,14 +77,11 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
         */
        cinfo->snd_link.name            = cinfo->name;
        cinfo->snd_link.stream_name     = cinfo->name;
-       cinfo->snd_link.cpu_dai_name    = cinfo->cpu_dai;
+       cinfo->snd_link.cpu_dai_name    = cinfo->cpu_dai.name;
        cinfo->snd_link.platform_name   = cinfo->platform;
        cinfo->snd_link.codec_name      = cinfo->codec;
-       cinfo->snd_link.codec_dai_name  = cinfo->codec_dai;
-
-       /* enable snd_link.init if cinfo has settings */
-       if (cinfo->init)
-               cinfo->snd_link.init    = asoc_simple_card_dai_init;
+       cinfo->snd_link.codec_dai_name  = cinfo->codec_dai.name;
+       cinfo->snd_link.init            = asoc_simple_card_dai_init;
 
        /*
         * init snd_soc_card
index 365d9d27a3216b2c8969451c83b0e49e0bbc9520..e70e6c844f96b29ca631e69841aea53b51886d00 100644 (file)
@@ -32,7 +32,6 @@
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
-#include <sound/saif.h>
 #include <asm/mach-types.h>
 #include <mach/hardware.h>
 #include <mach/mxs.h>
@@ -662,46 +661,40 @@ static int mxs_saif_probe(struct platform_device *pdev)
        struct device_node *np = pdev->dev.of_node;
        struct resource *iores, *dmares;
        struct mxs_saif *saif;
-       struct mxs_saif_platform_data *pdata;
        struct pinctrl *pinctrl;
        int ret = 0;
+       struct device_node *master;
 
-
-       if (!np && pdev->id >= ARRAY_SIZE(mxs_saif))
+       if (!np)
                return -EINVAL;
 
        saif = devm_kzalloc(&pdev->dev, sizeof(*saif), GFP_KERNEL);
        if (!saif)
                return -ENOMEM;
 
-       if (np) {
-               struct device_node *master;
-               saif->id = of_alias_get_id(np, "saif");
-               if (saif->id < 0)
-                       return saif->id;
-               /*
-                * If there is no "fsl,saif-master" phandle, it's a saif
-                * master.  Otherwise, it's a slave and its phandle points
-                * to the master.
-                */
-               master = of_parse_phandle(np, "fsl,saif-master", 0);
-               if (!master) {
-                       saif->master_id = saif->id;
-               } else {
-                       saif->master_id = of_alias_get_id(master, "saif");
-                       if (saif->master_id < 0)
-                               return saif->master_id;
-               }
+       ret = of_alias_get_id(np, "saif");
+       if (ret < 0)
+               return ret;
+       else
+               saif->id = ret;
+
+       /*
+        * If there is no "fsl,saif-master" phandle, it's a saif
+        * master.  Otherwise, it's a slave and its phandle points
+        * to the master.
+        */
+       master = of_parse_phandle(np, "fsl,saif-master", 0);
+       if (!master) {
+               saif->master_id = saif->id;
        } else {
-               saif->id = pdev->id;
-               pdata = pdev->dev.platform_data;
-               if (pdata && !pdata->master_mode)
-                       saif->master_id = pdata->master_id;
+               ret = of_alias_get_id(master, "saif");
+               if (ret < 0)
+                       return ret;
                else
-                       saif->master_id = saif->id;
+                       saif->master_id = ret;
        }
 
-       if (saif->master_id < 0 || saif->master_id >= ARRAY_SIZE(mxs_saif)) {
+       if (saif->master_id >= ARRAY_SIZE(mxs_saif)) {
                dev_err(&pdev->dev, "get wrong master id\n");
                return -EINVAL;
        }
index 7048137f9a337340e0e0a78a7a55859220718538..60259f2f3f2cd7f65b1b702d43d88bba4f59b5c1 100644 (file)
@@ -70,15 +70,6 @@ config SND_OMAP_SOC_AM3517EVM
          Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517
          EVM.
 
-config SND_OMAP_SOC_SDP3430
-       tristate "SoC Audio support for Texas Instruments SDP3430"
-       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
-       select SND_OMAP_SOC_MCBSP
-       select SND_SOC_TWL4030
-       help
-         Say Y if you want to add support for SoC audio on Texas Instruments
-         SDP3430.
-
 config SND_OMAP_SOC_OMAP_TWL4030
        tristate "SoC Audio support for TI SoC based boards with twl4030 codec"
        depends on TWL4030_CORE && SND_OMAP_SOC
@@ -91,6 +82,8 @@ config SND_OMAP_SOC_OMAP_TWL4030
          - Gumstix Overo or CompuLab CM-T35/CM-T3730
          - IGEP v2
          - OMAP3EVM
+         - SDP3430
+         - Zoom2
 
 config SND_OMAP_SOC_OMAP_ABE_TWL6040
        tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
@@ -123,11 +116,3 @@ config SND_OMAP_SOC_OMAP3_PANDORA
        select SND_SOC_TWL4030
        help
          Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
-
-config SND_OMAP_SOC_ZOOM2
-       tristate "SoC Audio support for Zoom2"
-       depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_ZOOM2
-       select SND_OMAP_SOC_MCBSP
-       select SND_SOC_TWL4030
-       help
-         Say Y if you want to add support for Soc audio on Zoom2 board.
index 19637e55ea48c7ac78e5fa90cef1916b0f1f7d3d..2b225945359beee9c2e4dd745979f2a40b443f50 100644 (file)
@@ -17,11 +17,9 @@ snd-soc-rx51-objs := rx51.o
 snd-soc-ams-delta-objs := ams-delta.o
 snd-soc-osk5912-objs := osk5912.o
 snd-soc-am3517evm-objs := am3517evm.o
-snd-soc-sdp3430-objs := sdp3430.o
 snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o
 snd-soc-omap-twl4030-objs := omap-twl4030.o
 snd-soc-omap3pandora-objs := omap3pandora.o
-snd-soc-zoom2-objs := zoom2.o
 snd-soc-omap-hdmi-card-objs := omap-hdmi-card.o
 
 obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
@@ -30,9 +28,7 @@ obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
 obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP2EVM) += snd-soc-omap2evm.o
 obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o
-obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
-obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP_HDMI) += snd-soc-omap-hdmi-card.o
index 230b8c14484842ac961dd651320ebe072a9cc8bd..ee7cd53aa3ee7db8b430b84f170b9be3bdc2852c 100644 (file)
@@ -230,8 +230,8 @@ static const struct snd_soc_dapm_route audio_map[] = {
        {"Ext Spk", NULL, "LLOUT"},
        {"Ext Spk", NULL, "RLOUT"},
 
-       {"DMic Rate 64", NULL, "Mic Bias 2V"},
-       {"Mic Bias 2V", NULL, "DMic"},
+       {"DMic Rate 64", NULL, "Mic Bias"},
+       {"Mic Bias", NULL, "DMic"},
 };
 
 static const char *spk_function[] = {"Off", "On"};
index 7ea24819d570ec912f60fc978497578bddeb753b..32fa840c493e9f31a6860d0eb48a7392f57d0f91 100644 (file)
@@ -110,6 +110,8 @@ static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
        /*
         * fill the IEC-60958 channel status word
         */
+       /* initialize the word bytes */
+       memset(iec->status, 0, sizeof(iec->status));
 
        /* specify IEC-60958-3 (commercial use) */
        iec->status[0] &= ~IEC958_AES0_PROFESSIONAL;
index 47bdbd415ad87af59686327ad3be8f25c48bf2a4..c722c2ef9665c9d6b0e3082077a77755bd4d95c9 100644 (file)
@@ -174,23 +174,15 @@ static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream)
 
 static int omap_pcm_open(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct omap_pcm_dma_data *dma_data;
-       int ret;
 
        snd_soc_set_runtime_hwparams(substream, &omap_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)
-               return ret;
-
        dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-       ret = snd_dmaengine_pcm_open(substream, omap_dma_filter_fn,
-                                    &dma_data->dma_req);
-       return ret;
+
+       return snd_dmaengine_pcm_open(substream, omap_dma_filter_fn,
+                                     &dma_data->dma_req);
 }
 
 static int omap_pcm_close(struct snd_pcm_substream *substream)
index 4541d28b531495d7484f3a2dfcf4fe8472af451f..fd98509d0f49deea2bf474dc246edd04f251d6dd 100644 (file)
@@ -11,6 +11,8 @@
  * omap3evm (Author: Anuj Aggarwal <anuj.aggarwal@ti.com>)
  * overo (Author: Steve Sakoman <steve@sakoman.com>)
  * igep0020 (Author: Enric Balletbo i Serra <eballetbo@iseebcn.com>)
+ * zoom2 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
+ * sdp3430 (Author: Misael Lopez Cruz <misael.lopez@ti.com>)
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
 #include <linux/platform_data/omap-twl4030.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
+#include <sound/jack.h>
 
 #include "omap-mcbsp.h"
 #include "omap-pcm.h"
 
+struct omap_twl4030 {
+       int jack_detect;        /* board can detect jack events */
+       struct snd_soc_jack hs_jack;
+};
+
 static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params)
 {
@@ -87,17 +97,164 @@ static struct snd_soc_ops omap_twl4030_ops = {
        .hw_params = omap_twl4030_hw_params,
 };
 
+static const struct snd_soc_dapm_widget dapm_widgets[] = {
+       SND_SOC_DAPM_SPK("Earpiece Spk", NULL),
+       SND_SOC_DAPM_SPK("Handsfree Spk", NULL),
+       SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+       SND_SOC_DAPM_SPK("Carkit Spk", NULL),
+
+       SND_SOC_DAPM_MIC("Main Mic", NULL),
+       SND_SOC_DAPM_MIC("Sub Mic", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Carkit Mic", NULL),
+       SND_SOC_DAPM_MIC("Digital0 Mic", NULL),
+       SND_SOC_DAPM_MIC("Digital1 Mic", NULL),
+       SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Headset Stereophone:  HSOL, HSOR */
+       {"Headset Stereophone", NULL, "HSOL"},
+       {"Headset Stereophone", NULL, "HSOR"},
+       /* External Speakers: HFL, HFR */
+       {"Handsfree Spk", NULL, "HFL"},
+       {"Handsfree Spk", NULL, "HFR"},
+       /* External Speakers: PredrivL, PredrivR */
+       {"Ext Spk", NULL, "PREDRIVEL"},
+       {"Ext Spk", NULL, "PREDRIVER"},
+       /* Carkit speakers:  CARKITL, CARKITR */
+       {"Carkit Spk", NULL, "CARKITL"},
+       {"Carkit Spk", NULL, "CARKITR"},
+       /* Earpiece */
+       {"Earpiece Spk", NULL, "EARPIECE"},
+
+       /* External Mics: MAINMIC, SUBMIC with bias */
+       {"MAINMIC", NULL, "Main Mic"},
+       {"Main Mic", NULL, "Mic Bias 1"},
+       {"SUBMIC", NULL, "Sub Mic"},
+       {"Sub Mic", NULL, "Mic Bias 2"},
+       /* Headset Mic: HSMIC with bias */
+       {"HSMIC", NULL, "Headset Mic"},
+       {"Headset Mic", NULL, "Headset Mic Bias"},
+       /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */
+       {"DIGIMIC0", NULL, "Digital0 Mic"},
+       {"Digital0 Mic", NULL, "Mic Bias 1"},
+       {"DIGIMIC1", NULL, "Digital1 Mic"},
+       {"Digital1 Mic", NULL, "Mic Bias 2"},
+       /* Carkit In: CARKITMIC */
+       {"CARKITMIC", NULL, "Carkit Mic"},
+       /* Aux In: AUXL, AUXR */
+       {"AUXL", NULL, "Line In"},
+       {"AUXR", NULL, "Line In"},
+};
+
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+       {
+               .pin = "Headset Mic",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headset Stereophone",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
+/* Headset jack detection gpios */
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+       {
+               .name = "hsdet-gpio",
+               .report = SND_JACK_HEADSET,
+               .debounce_time = 200,
+       },
+};
+
+static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm,
+                                         int connected, char *pin)
+{
+       if (!connected)
+               snd_soc_dapm_disable_pin(dapm, pin);
+}
+
+static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_card *card = codec->card;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev);
+       struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
+       int ret = 0;
+
+       /* Headset jack detection only if it is supported */
+       if (priv->jack_detect > 0) {
+               hs_jack_gpios[0].gpio = priv->jack_detect;
+
+               ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
+                                      &priv->hs_jack);
+               if (ret)
+                       return ret;
+
+               ret = snd_soc_jack_add_pins(&priv->hs_jack,
+                                           ARRAY_SIZE(hs_jack_pins),
+                                           hs_jack_pins);
+               if (ret)
+                       return ret;
+
+               ret = snd_soc_jack_add_gpios(&priv->hs_jack,
+                                            ARRAY_SIZE(hs_jack_gpios),
+                                            hs_jack_gpios);
+               if (ret)
+                       return ret;
+       }
+
+       /*
+        * NULL pdata means we booted with DT. In this case the routing is
+        * provided and the card is fully routed, no need to mark pins.
+        */
+       if (!pdata || !pdata->custom_routing)
+               return ret;
+
+       /* Disable not connected paths if not used */
+       twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk");
+       twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk");
+       twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
+       twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk");
+       twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk");
+
+       twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic");
+       twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In");
+
+       return ret;
+}
+
 /* Digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link omap_twl4030_dai_links[] = {
        {
-               .name = "TWL4030",
-               .stream_name = "TWL4030",
+               .name = "TWL4030 HiFi",
+               .stream_name = "TWL4030 HiFi",
                .cpu_dai_name = "omap-mcbsp.2",
                .codec_dai_name = "twl4030-hifi",
                .platform_name = "omap-pcm-audio",
                .codec_name = "twl4030-codec",
+               .init = omap_twl4030_init,
                .ops = &omap_twl4030_ops,
        },
+       {
+               .name = "TWL4030 Voice",
+               .stream_name = "TWL4030 Voice",
+               .cpu_dai_name = "omap-mcbsp.3",
+               .codec_dai_name = "twl4030-voice",
+               .platform_name = "omap-pcm-audio",
+               .codec_name = "twl4030-codec",
+               .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
+                          SND_SOC_DAIFMT_CBM_CFM,
+       },
 };
 
 /* Audio machine driver */
@@ -105,6 +262,11 @@ static struct snd_soc_card omap_twl4030_card = {
        .owner = THIS_MODULE,
        .dai_link = omap_twl4030_dai_links,
        .num_links = ARRAY_SIZE(omap_twl4030_dai_links),
+
+       .dapm_widgets = dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
+       .dapm_routes = audio_map,
+       .num_dapm_routes = ARRAY_SIZE(audio_map),
 };
 
 static int omap_twl4030_probe(struct platform_device *pdev)
@@ -112,12 +274,18 @@ static int omap_twl4030_probe(struct platform_device *pdev)
        struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev);
        struct device_node *node = pdev->dev.of_node;
        struct snd_soc_card *card = &omap_twl4030_card;
+       struct omap_twl4030 *priv;
        int ret = 0;
 
        card->dev = &pdev->dev;
 
+       priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL);
+       if (priv == NULL)
+               return -ENOMEM;
+
        if (node) {
                struct device_node *dai_node;
+               struct property *prop;
 
                if (snd_soc_of_parse_card_name(card, "ti,model")) {
                        dev_err(&pdev->dev, "Card name is not provided\n");
@@ -132,6 +300,27 @@ static int omap_twl4030_probe(struct platform_device *pdev)
                omap_twl4030_dai_links[0].cpu_dai_name  = NULL;
                omap_twl4030_dai_links[0].cpu_of_node = dai_node;
 
+               dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0);
+               if (!dai_node) {
+                       card->num_links = 1;
+               } else {
+                       omap_twl4030_dai_links[1].cpu_dai_name  = NULL;
+                       omap_twl4030_dai_links[1].cpu_of_node = dai_node;
+               }
+
+               priv->jack_detect = of_get_named_gpio(node,
+                                                     "ti,jack-det-gpio", 0);
+
+               /* Optional: audio routing can be provided */
+               prop = of_find_property(node, "ti,audio-routing", NULL);
+               if (prop) {
+                       ret = snd_soc_of_parse_audio_routing(card,
+                                                           "ti,audio-routing");
+                       if (ret)
+                               return ret;
+
+                       card->fully_routed = 1;
+               }
        } else if (pdata) {
                if (pdata->card_name) {
                        card->name = pdata->card_name;
@@ -139,11 +328,17 @@ static int omap_twl4030_probe(struct platform_device *pdev)
                        dev_err(&pdev->dev, "Card name is not provided\n");
                        return -ENODEV;
                }
+
+               if (!pdata->voice_connected)
+                       card->num_links = 1;
+
+               priv->jack_detect = pdata->jack_detect;
        } else {
                dev_err(&pdev->dev, "Missing pdata\n");
                return -ENODEV;
        }
 
+       snd_soc_card_set_drvdata(card, priv);
        ret = snd_soc_register_card(card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
@@ -157,7 +352,12 @@ static int omap_twl4030_probe(struct platform_device *pdev)
 static int omap_twl4030_remove(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card);
 
+       if (priv->jack_detect > 0)
+               snd_soc_jack_free_gpios(&priv->hs_jack,
+                                       ARRAY_SIZE(hs_jack_gpios),
+                                       hs_jack_gpios);
        snd_soc_unregister_card(card);
 
        return 0;
index 43d950a79ff9faf0f6496bd3e0bbba7f7326502e..805512f2555a0b95571a4608f6d4e35a71312c5f 100644 (file)
@@ -144,11 +144,11 @@ static const struct snd_soc_dapm_route omap3pandora_in_map[] = {
        {"AUXL", NULL, "Line In"},
        {"AUXR", NULL, "Line In"},
 
-       {"MAINMIC", NULL, "Mic Bias 1"},
-       {"Mic Bias 1", NULL, "Mic (internal)"},
+       {"MAINMIC", NULL, "Mic (internal)"},
+       {"Mic (internal)", NULL, "Mic Bias 1"},
 
-       {"SUBMIC", NULL, "Mic Bias 2"},
-       {"Mic Bias 2", NULL, "Mic (external)"},
+       {"SUBMIC", NULL, "Mic (external)"},
+       {"Mic (external)", NULL, "Mic Bias 2"},
 };
 
 static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd)
index d921ddbe3ecbd2fdaa11ca1f14b1e8a7e20c4286..3cd5257489759b41a053aca0e10955f7df1ae8b5 100644 (file)
@@ -248,16 +248,16 @@ static const struct snd_soc_dapm_route audio_map[] = {
        {"FM Transmitter", NULL, "LLOUT"},
        {"FM Transmitter", NULL, "RLOUT"},
 
-       {"DMic Rate 64", NULL, "Mic Bias 2V"},
-       {"Mic Bias 2V", NULL, "DMic"},
+       {"DMic Rate 64", NULL, "Mic Bias"},
+       {"Mic Bias", NULL, "DMic"},
 };
 
 static const struct snd_soc_dapm_route audio_mapb[] = {
        {"b LINE2R", NULL, "MONO_LOUT"},
        {"Earphone", NULL, "b HPLOUT"},
 
-       {"LINE1L", NULL, "b Mic Bias 2.5V"},
-       {"b Mic Bias 2.5V", NULL, "HS Mic"}
+       {"LINE1L", NULL, "b Mic Bias"},
+       {"b Mic Bias", NULL, "HS Mic"}
 };
 
 static const char *spk_function[] = {"Off", "On"};
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c
deleted file mode 100644 (file)
index b462a2c..0000000
+++ /dev/null
@@ -1,278 +0,0 @@
-/*
- * sdp3430.c  --  SoC audio for TI OMAP3430 SDP
- *
- * Author: Misael Lopez Cruz <x0052729@ti.com>
- *
- * Based on:
- * Author: Steve Sakoman <steve@sakoman.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.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/i2c/twl.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/jack.h>
-
-#include <asm/mach-types.h>
-#include <linux/platform_data/gpio-omap.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-
-/* Register descriptions for twl4030 codec part */
-#include <linux/mfd/twl4030-audio.h>
-#include <linux/module.h>
-
-#include "omap-mcbsp.h"
-#include "omap-pcm.h"
-
-/* TWL4030 PMBR1 Register */
-#define TWL4030_INTBR_PMBR1            0x0D
-/* TWL4030 PMBR1 Register GPIO6 mux bit */
-#define TWL4030_GPIO6_PWM0_MUTE(value) (value << 2)
-
-static struct snd_soc_card snd_soc_sdp3430;
-
-static int sdp3430_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret;
-
-       /* Set the codec system clock for DAC and ADC */
-       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
-                                           SND_SOC_CLOCK_IN);
-       if (ret < 0) {
-               printk(KERN_ERR "can't set codec system clock\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static struct snd_soc_ops sdp3430_ops = {
-       .hw_params = sdp3430_hw_params,
-};
-
-/* Headset jack */
-static struct snd_soc_jack hs_jack;
-
-/* Headset jack detection DAPM pins */
-static struct snd_soc_jack_pin hs_jack_pins[] = {
-       {
-               .pin = "Headset Mic",
-               .mask = SND_JACK_MICROPHONE,
-       },
-       {
-               .pin = "Headset Stereophone",
-               .mask = SND_JACK_HEADPHONE,
-       },
-};
-
-/* Headset jack detection gpios */
-static struct snd_soc_jack_gpio hs_jack_gpios[] = {
-       {
-               .gpio = (OMAP_MAX_GPIO_LINES + 2),
-               .name = "hsdet-gpio",
-               .report = SND_JACK_HEADSET,
-               .debounce_time = 200,
-       },
-};
-
-/* SDP3430 machine DAPM */
-static const struct snd_soc_dapm_widget sdp3430_twl4030_dapm_widgets[] = {
-       SND_SOC_DAPM_MIC("Ext Mic", NULL),
-       SND_SOC_DAPM_SPK("Ext Spk", NULL),
-       SND_SOC_DAPM_MIC("Headset Mic", NULL),
-       SND_SOC_DAPM_HP("Headset Stereophone", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
-       /* External Mics: MAINMIC, SUBMIC with bias*/
-       {"MAINMIC", NULL, "Mic Bias 1"},
-       {"SUBMIC", NULL, "Mic Bias 2"},
-       {"Mic Bias 1", NULL, "Ext Mic"},
-       {"Mic Bias 2", NULL, "Ext Mic"},
-
-       /* External Speakers: HFL, HFR */
-       {"Ext Spk", NULL, "HFL"},
-       {"Ext Spk", NULL, "HFR"},
-
-       /* Headset Mic: HSMIC with bias */
-       {"HSMIC", NULL, "Headset Mic Bias"},
-       {"Headset Mic Bias", NULL, "Headset Mic"},
-
-       /* Headset Stereophone (Headphone): HSOL, HSOR */
-       {"Headset Stereophone", NULL, "HSOL"},
-       {"Headset Stereophone", NULL, "HSOR"},
-};
-
-static int sdp3430_twl4030_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-       int ret;
-
-       /* SDP3430 connected pins */
-       snd_soc_dapm_enable_pin(dapm, "Ext Mic");
-       snd_soc_dapm_enable_pin(dapm, "Ext Spk");
-       snd_soc_dapm_disable_pin(dapm, "Headset Mic");
-       snd_soc_dapm_disable_pin(dapm, "Headset Stereophone");
-
-       /* TWL4030 not connected pins */
-       snd_soc_dapm_nc_pin(dapm, "AUXL");
-       snd_soc_dapm_nc_pin(dapm, "AUXR");
-       snd_soc_dapm_nc_pin(dapm, "CARKITMIC");
-       snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");
-       snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");
-
-       snd_soc_dapm_nc_pin(dapm, "OUTL");
-       snd_soc_dapm_nc_pin(dapm, "OUTR");
-       snd_soc_dapm_nc_pin(dapm, "EARPIECE");
-       snd_soc_dapm_nc_pin(dapm, "PREDRIVEL");
-       snd_soc_dapm_nc_pin(dapm, "PREDRIVER");
-       snd_soc_dapm_nc_pin(dapm, "CARKITL");
-       snd_soc_dapm_nc_pin(dapm, "CARKITR");
-
-       /* Headset jack detection */
-       ret = snd_soc_jack_new(codec, "Headset Jack",
-                               SND_JACK_HEADSET, &hs_jack);
-       if (ret)
-               return ret;
-
-       ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
-                               hs_jack_pins);
-       if (ret)
-               return ret;
-
-       ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
-                               hs_jack_gpios);
-
-       return ret;
-}
-
-static int sdp3430_twl4030_voice_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       unsigned short reg;
-
-       /* Enable voice interface */
-       reg = codec->driver->read(codec, TWL4030_REG_VOICE_IF);
-       reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN;
-       codec->driver->write(codec, TWL4030_REG_VOICE_IF, reg);
-
-       return 0;
-}
-
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link sdp3430_dai[] = {
-       {
-               .name = "TWL4030 I2S",
-               .stream_name = "TWL4030 Audio",
-               .cpu_dai_name = "omap-mcbsp.2",
-               .codec_dai_name = "twl4030-hifi",
-               .platform_name = "omap-pcm-audio",
-               .codec_name = "twl4030-codec",
-               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
-                          SND_SOC_DAIFMT_CBM_CFM,
-               .init = sdp3430_twl4030_init,
-               .ops = &sdp3430_ops,
-       },
-       {
-               .name = "TWL4030 PCM",
-               .stream_name = "TWL4030 Voice",
-               .cpu_dai_name = "omap-mcbsp.3",
-               .codec_dai_name = "twl4030-voice",
-               .platform_name = "omap-pcm-audio",
-               .codec_name = "twl4030-codec",
-               .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
-                          SND_SOC_DAIFMT_CBM_CFM,
-               .init = sdp3430_twl4030_voice_init,
-               .ops = &sdp3430_ops,
-       },
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_sdp3430 = {
-       .name = "SDP3430",
-       .owner = THIS_MODULE,
-       .dai_link = sdp3430_dai,
-       .num_links = ARRAY_SIZE(sdp3430_dai),
-
-       .dapm_widgets = sdp3430_twl4030_dapm_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(sdp3430_twl4030_dapm_widgets),
-       .dapm_routes = audio_map,
-       .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static struct platform_device *sdp3430_snd_device;
-
-static int __init sdp3430_soc_init(void)
-{
-       int ret;
-       u8 pin_mux;
-
-       if (!machine_is_omap_3430sdp())
-               return -ENODEV;
-       printk(KERN_INFO "SDP3430 SoC init\n");
-
-       sdp3430_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!sdp3430_snd_device) {
-               printk(KERN_ERR "Platform device allocation failed\n");
-               return -ENOMEM;
-       }
-
-       platform_set_drvdata(sdp3430_snd_device, &snd_soc_sdp3430);
-
-       /* Set TWL4030 GPIO6 as EXTMUTE signal */
-       twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux,
-                                               TWL4030_INTBR_PMBR1);
-       pin_mux &= ~TWL4030_GPIO6_PWM0_MUTE(0x03);
-       pin_mux |= TWL4030_GPIO6_PWM0_MUTE(0x02);
-       twl_i2c_write_u8(TWL4030_MODULE_INTBR, pin_mux,
-                                               TWL4030_INTBR_PMBR1);
-
-       ret = platform_device_add(sdp3430_snd_device);
-       if (ret)
-               goto err1;
-
-       return 0;
-
-err1:
-       printk(KERN_ERR "Unable to add platform device\n");
-       platform_device_put(sdp3430_snd_device);
-
-       return ret;
-}
-module_init(sdp3430_soc_init);
-
-static void __exit sdp3430_soc_exit(void)
-{
-       snd_soc_jack_free_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
-                               hs_jack_gpios);
-
-       platform_device_unregister(sdp3430_snd_device);
-}
-module_exit(sdp3430_soc_exit);
-
-MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
-MODULE_DESCRIPTION("ALSA SoC SDP3430");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c
deleted file mode 100644 (file)
index 771bff2..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * zoom2.c  --  SoC audio for Zoom2
- *
- * Author: Misael Lopez Cruz <x0052729@ti.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.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <linux/platform_data/asoc-ti-mcbsp.h>
-#include <linux/platform_data/gpio-omap.h>
-
-/* Register descriptions for twl4030 codec part */
-#include <linux/mfd/twl4030-audio.h>
-#include <linux/module.h>
-
-#include "omap-mcbsp.h"
-#include "omap-pcm.h"
-
-static int zoom2_hw_params(struct snd_pcm_substream *substream,
-                               struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int ret;
-
-       /* Set the codec system clock for DAC and ADC */
-       ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
-                                       SND_SOC_CLOCK_IN);
-       if (ret < 0) {
-               printk(KERN_ERR "can't set codec system clock\n");
-               return ret;
-       }
-
-       return 0;
-}
-
-static struct snd_soc_ops zoom2_ops = {
-       .hw_params = zoom2_hw_params,
-};
-
-/* Zoom2 machine DAPM */
-static const struct snd_soc_dapm_widget zoom2_twl4030_dapm_widgets[] = {
-       SND_SOC_DAPM_MIC("Ext Mic", NULL),
-       SND_SOC_DAPM_SPK("Ext Spk", NULL),
-       SND_SOC_DAPM_MIC("Headset Mic", NULL),
-       SND_SOC_DAPM_HP("Headset Stereophone", NULL),
-       SND_SOC_DAPM_LINE("Aux In", NULL),
-};
-
-static const struct snd_soc_dapm_route audio_map[] = {
-       /* External Mics: MAINMIC, SUBMIC with bias*/
-       {"MAINMIC", NULL, "Mic Bias 1"},
-       {"SUBMIC", NULL, "Mic Bias 2"},
-       {"Mic Bias 1", NULL, "Ext Mic"},
-       {"Mic Bias 2", NULL, "Ext Mic"},
-
-       /* External Speakers: HFL, HFR */
-       {"Ext Spk", NULL, "HFL"},
-       {"Ext Spk", NULL, "HFR"},
-
-       /* Headset Stereophone:  HSOL, HSOR */
-       {"Headset Stereophone", NULL, "HSOL"},
-       {"Headset Stereophone", NULL, "HSOR"},
-
-       /* Headset Mic: HSMIC with bias */
-       {"HSMIC", NULL, "Headset Mic Bias"},
-       {"Headset Mic Bias", NULL, "Headset Mic"},
-
-       /* Aux In: AUXL, AUXR */
-       {"Aux In", NULL, "AUXL"},
-       {"Aux In", NULL, "AUXR"},
-};
-
-static int zoom2_twl4030_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       /* TWL4030 not connected pins */
-       snd_soc_dapm_nc_pin(dapm, "CARKITMIC");
-       snd_soc_dapm_nc_pin(dapm, "DIGIMIC0");
-       snd_soc_dapm_nc_pin(dapm, "DIGIMIC1");
-       snd_soc_dapm_nc_pin(dapm, "EARPIECE");
-       snd_soc_dapm_nc_pin(dapm, "PREDRIVEL");
-       snd_soc_dapm_nc_pin(dapm, "PREDRIVER");
-       snd_soc_dapm_nc_pin(dapm, "CARKITL");
-       snd_soc_dapm_nc_pin(dapm, "CARKITR");
-
-       return 0;
-}
-
-static int zoom2_twl4030_voice_init(struct snd_soc_pcm_runtime *rtd)
-{
-       struct snd_soc_codec *codec = rtd->codec;
-       unsigned short reg;
-
-       /* Enable voice interface */
-       reg = codec->driver->read(codec, TWL4030_REG_VOICE_IF);
-       reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN;
-       codec->driver->write(codec, TWL4030_REG_VOICE_IF, reg);
-
-       return 0;
-}
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link zoom2_dai[] = {
-       {
-               .name = "TWL4030 I2S",
-               .stream_name = "TWL4030 Audio",
-               .cpu_dai_name = "omap-mcbsp.2",
-               .codec_dai_name = "twl4030-hifi",
-               .platform_name = "omap-pcm-audio",
-               .codec_name = "twl4030-codec",
-               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
-                          SND_SOC_DAIFMT_CBM_CFM,
-               .init = zoom2_twl4030_init,
-               .ops = &zoom2_ops,
-       },
-       {
-               .name = "TWL4030 PCM",
-               .stream_name = "TWL4030 Voice",
-               .cpu_dai_name = "omap-mcbsp.3",
-               .codec_dai_name = "twl4030-voice",
-               .platform_name = "omap-pcm-audio",
-               .codec_name = "twl4030-codec",
-               .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF |
-                          SND_SOC_DAIFMT_CBM_CFM,
-               .init = zoom2_twl4030_voice_init,
-               .ops = &zoom2_ops,
-       },
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_zoom2 = {
-       .name = "Zoom2",
-       .owner = THIS_MODULE,
-       .dai_link = zoom2_dai,
-       .num_links = ARRAY_SIZE(zoom2_dai),
-
-       .dapm_widgets = zoom2_twl4030_dapm_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(zoom2_twl4030_dapm_widgets),
-       .dapm_routes = audio_map,
-       .num_dapm_routes = ARRAY_SIZE(audio_map),
-};
-
-static struct platform_device *zoom2_snd_device;
-
-static int __init zoom2_soc_init(void)
-{
-       int ret;
-
-       if (!machine_is_omap_zoom2())
-               return -ENODEV;
-       printk(KERN_INFO "Zoom2 SoC init\n");
-
-       zoom2_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!zoom2_snd_device) {
-               printk(KERN_ERR "Platform device allocation failed\n");
-               return -ENOMEM;
-       }
-
-       platform_set_drvdata(zoom2_snd_device, &snd_soc_zoom2);
-       ret = platform_device_add(zoom2_snd_device);
-       if (ret)
-               goto err1;
-
-       return 0;
-
-err1:
-       printk(KERN_ERR "Unable to add platform device\n");
-       platform_device_put(zoom2_snd_device);
-
-       return ret;
-}
-module_init(zoom2_soc_init);
-
-static void __exit zoom2_soc_exit(void)
-{
-       platform_device_unregister(zoom2_snd_device);
-}
-module_exit(zoom2_soc_exit);
-
-MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
-MODULE_DESCRIPTION("ALSA SoC Zoom2");
-MODULE_LICENSE("GPL");
-
index 2074e2daf9c6a341e002ff26771e05de9ce2f9a6..e1ffcdd9a6492678c604ab045a7a981cae562971 100644 (file)
@@ -79,17 +79,6 @@ static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd)
        struct snd_soc_dapm_context *dapm = &codec->dapm;
        int err;
 
-       /* add palm27x specific widgets */
-       err = snd_soc_dapm_new_controls(dapm, palm27x_dapm_widgets,
-                               ARRAY_SIZE(palm27x_dapm_widgets));
-       if (err)
-               return err;
-
-       /* set up palm27x specific audio path audio_map */
-       err = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
-       if (err)
-               return err;
-
        /* connected pins */
        if (machine_is_palmld())
                snd_soc_dapm_enable_pin(dapm, "MIC1");
@@ -149,10 +138,12 @@ static struct snd_soc_card palm27x_asoc = {
        .owner = THIS_MODULE,
        .dai_link = palm27x_dai,
        .num_links = ARRAY_SIZE(palm27x_dai),
+       .dapm_widgets = palm27x_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(palm27x_dapm_widgets),
+       .dapm_routes = audio_map,
+       .num_dapm_routes = ARRAY_SIZE(audio_map)
 };
 
-static struct platform_device *palm27x_snd_device;
-
 static int palm27x_asoc_probe(struct platform_device *pdev)
 {
        int ret;
@@ -169,27 +160,18 @@ static int palm27x_asoc_probe(struct platform_device *pdev)
        hs_jack_gpios[0].gpio = ((struct palm27x_asoc_info *)
                        (pdev->dev.platform_data))->jack_gpio;
 
-       palm27x_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!palm27x_snd_device)
-               return -ENOMEM;
-
-       platform_set_drvdata(palm27x_snd_device, &palm27x_asoc);
-       ret = platform_device_add(palm27x_snd_device);
-
-       if (ret != 0)
-               goto put_device;
-
-       return 0;
-
-put_device:
-       platform_device_put(palm27x_snd_device);
+       palm27x_asoc.dev = &pdev->dev;
 
+       ret = snd_soc_register_card(&palm27x_asoc);
+       if (ret)
+               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+                       ret);
        return ret;
 }
 
 static int palm27x_asoc_remove(struct platform_device *pdev)
 {
-       platform_device_unregister(palm27x_snd_device);
+       snd_soc_unregister_card(&palm27x_asoc);
        return 0;
 }
 
index 3c7c3a59ed3987b9017e89c07cf130a2a54c4a39..90e7e6653233d920e626f9ffe497e2fdffeb67a2 100644 (file)
@@ -63,7 +63,7 @@ config SND_SOC_SAMSUNG_SMDK_WM8580
 
 config SND_SOC_SAMSUNG_SMDK_WM8994
        tristate "SoC I2S Audio support for WM8994 on SMDK"
-       depends on SND_SOC_SAMSUNG && (MACH_SMDKV310 || MACH_SMDKC210 || MACH_SMDK4212)
+       depends on SND_SOC_SAMSUNG
        depends on I2C=y && GENERIC_HARDIRQS
        select MFD_WM8994
        select SND_SOC_WM8994
@@ -162,7 +162,7 @@ config SND_SOC_GONI_AQUILA_WM8994
 
 config SND_SOC_SAMSUNG_SMDK_SPDIF
        tristate "SoC S/PDIF Audio support for SMDK"
-       depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210 || MACH_SMDKV310 || MACH_SMDK4212)
+       depends on SND_SOC_SAMSUNG
        select SND_SAMSUNG_SPDIF
        help
          Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
@@ -177,7 +177,7 @@ config SND_SOC_SMDK_WM8580_PCM
 
 config SND_SOC_SMDK_WM8994_PCM
        tristate "SoC PCM Audio support for WM8994 on SMDK"
-       depends on SND_SOC_SAMSUNG && (MACH_SMDKC210 || MACH_SMDKV310 || MACH_SMDK4212)
+       depends on SND_SOC_SAMSUNG
        depends on I2C=y && GENERIC_HARDIRQS
        select MFD_WM8994
        select SND_SOC_WM8994
index db87628d7630cd0effc77588deb6f1fc7d337140..21b79262010ecb3774d8a5ad5ad7f8f87707549c 100644 (file)
@@ -174,7 +174,8 @@ static int dma_hw_params(struct snd_pcm_substream *substream,
                config.width = prtd->params->dma_size;
                config.fifo = prtd->params->dma_addr;
                prtd->params->ch = prtd->params->ops->request(
-                               prtd->params->channel, &req);
+                               prtd->params->channel, &req, rtd->cpu_dai->dev,
+                               prtd->params->ch_name);
                prtd->params->ops->config(prtd->params->ch, &config);
        }
 
index 73d8c7c8a1e8458119d9c2b5afecece3b64bfe1a..189a7a6d5020c637d1523ba8261ac9a421882c14 100644 (file)
@@ -19,6 +19,7 @@ struct s3c_dma_params {
        int dma_size;                   /* Size of the DMA transfer */
        unsigned ch;
        struct samsung_dma_ops *ops;
+       char *ch_name;
 };
 
 int asoc_dma_platform_register(struct device *dev);
index d2d124f1dd1b2a3c83cdd9c51c104be78b4f18b9..d7231e336a7c678e4b8b5f86ef0efa4a2eb04e8a 100644 (file)
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
 #include <linux/pm_runtime.h>
 
 #include <sound/soc.h>
 #include <sound/pcm_params.h>
 
+#include <mach/dma.h>
+
 #include <linux/platform_data/asoc-s3c.h>
 
 #include "dma.h"
 
 #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
 
+enum samsung_dai_type {
+       TYPE_PRI,
+       TYPE_SEC,
+};
+
+struct samsung_i2s_dai_data {
+       int dai_type;
+};
+
 struct i2s_dai {
        /* Platform device for this DAI */
        struct platform_device *pdev;
@@ -66,6 +79,7 @@ struct i2s_dai {
        u32     suspend_i2smod;
        u32     suspend_i2scon;
        u32     suspend_i2spsr;
+       unsigned long gpios[7]; /* i2s gpio line numbers */
 };
 
 /* Lock for cross i/f checks */
@@ -651,6 +665,9 @@ static int i2s_startup(struct snd_pcm_substream *substream,
        /* Enforce set_sysclk in Master mode */
        i2s->rclk_srcrate = 0;
 
+       if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR))
+               writel(CON_RSTCLR, i2s->addr + I2SCON);
+
        spin_unlock_irqrestore(&lock, flags);
 
        return 0;
@@ -981,8 +998,7 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
                i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
        } else {        /* Create a new platform_device for Secondary */
                i2s->pdev = platform_device_register_resndata(NULL,
-                               pdev->name, pdev->id + SAMSUNG_I2S_SECOFF,
-                               NULL, 0, NULL, 0);
+                               "samsung-i2s-sec", -1, NULL, 0, NULL, 0);
                if (IS_ERR(i2s->pdev))
                        return NULL;
        }
@@ -993,18 +1009,103 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
        return i2s;
 }
 
+#ifdef CONFIG_OF
+static int samsung_i2s_parse_dt_gpio(struct i2s_dai *i2s)
+{
+       struct device *dev = &i2s->pdev->dev;
+       int index, gpio, ret;
+
+       for (index = 0; index < 7; index++) {
+               gpio = of_get_gpio(dev->of_node, index);
+               if (!gpio_is_valid(gpio)) {
+                       dev_err(dev, "invalid gpio[%d]: %d\n", index, gpio);
+                       goto free_gpio;
+               }
+
+               ret = gpio_request(gpio, dev_name(dev));
+               if (ret) {
+                       dev_err(dev, "gpio [%d] request failed\n", gpio);
+                       goto free_gpio;
+               }
+               i2s->gpios[index] = gpio;
+       }
+       return 0;
+
+free_gpio:
+       while (--index >= 0)
+               gpio_free(i2s->gpios[index]);
+       return -EINVAL;
+}
+
+static void samsung_i2s_dt_gpio_free(struct i2s_dai *i2s)
+{
+       unsigned int index;
+       for (index = 0; index < 7; index++)
+               gpio_free(i2s->gpios[index]);
+}
+#else
+static int samsung_i2s_parse_dt_gpio(struct i2s_dai *dai)
+{
+       return -EINVAL;
+}
+
+static void samsung_i2s_dt_gpio_free(struct i2s_dai *dai)
+{
+}
+
+#endif
+
+static const struct of_device_id exynos_i2s_match[];
+
+static inline int samsung_i2s_get_driver_data(struct platform_device *pdev)
+{
+#ifdef CONFIG_OF
+       struct samsung_i2s_dai_data *data;
+       if (pdev->dev.of_node) {
+               const struct of_device_id *match;
+               match = of_match_node(exynos_i2s_match, pdev->dev.of_node);
+               data = (struct samsung_i2s_dai_data *) match->data;
+               return data->dai_type;
+       } else
+#endif
+               return platform_get_device_id(pdev)->driver_data;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int i2s_runtime_suspend(struct device *dev)
+{
+       struct i2s_dai *i2s = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(i2s->clk);
+
+       return 0;
+}
+
+static int i2s_runtime_resume(struct device *dev)
+{
+       struct i2s_dai *i2s = dev_get_drvdata(dev);
+
+       clk_prepare_enable(i2s->clk);
+
+       return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
 static int samsung_i2s_probe(struct platform_device *pdev)
 {
-       u32 dma_pl_chan, dma_cp_chan, dma_pl_sec_chan;
        struct i2s_dai *pri_dai, *sec_dai = NULL;
-       struct s3c_audio_pdata *i2s_pdata;
-       struct samsung_i2s *i2s_cfg;
+       struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
+       struct samsung_i2s *i2s_cfg = NULL;
        struct resource *res;
-       u32 regs_base, quirks;
+       u32 regs_base, quirks = 0, idma_addr = 0;
+       struct device_node *np = pdev->dev.of_node;
+       enum samsung_dai_type samsung_dai_type;
        int ret = 0;
 
        /* Call during Seconday interface registration */
-       if (pdev->id >= SAMSUNG_I2S_SECOFF) {
+       samsung_dai_type = samsung_i2s_get_driver_data(pdev);
+
+       if (samsung_dai_type == TYPE_SEC) {
                sec_dai = dev_get_drvdata(&pdev->dev);
                snd_soc_register_dai(&sec_dai->pdev->dev,
                        &sec_dai->i2s_dai_drv);
@@ -1012,31 +1113,60 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                return 0;
        }
 
-       i2s_pdata = pdev->dev.platform_data;
-       if (i2s_pdata == NULL) {
-               dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n");
-               return -EINVAL;
+       pri_dai = i2s_alloc_dai(pdev, false);
+       if (!pri_dai) {
+               dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
+               return -ENOMEM;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-       if (!res) {
-               dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n");
-               return -ENXIO;
-       }
-       dma_pl_chan = res->start;
+       if (!np) {
+               res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+               if (!res) {
+                       dev_err(&pdev->dev,
+                               "Unable to get I2S-TX dma resource\n");
+                       return -ENXIO;
+               }
+               pri_dai->dma_playback.channel = res->start;
 
-       res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
-       if (!res) {
-               dev_err(&pdev->dev, "Unable to get I2S-RX dma resource\n");
-               return -ENXIO;
-       }
-       dma_cp_chan = res->start;
+               res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+               if (!res) {
+                       dev_err(&pdev->dev,
+                               "Unable to get I2S-RX dma resource\n");
+                       return -ENXIO;
+               }
+               pri_dai->dma_capture.channel = res->start;
 
-       res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
-       if (res)
-               dma_pl_sec_chan = res->start;
-       else
-               dma_pl_sec_chan = 0;
+               if (i2s_pdata == NULL) {
+                       dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n");
+                       return -EINVAL;
+               }
+
+               if (&i2s_pdata->type)
+                       i2s_cfg = &i2s_pdata->type.i2s;
+
+               if (i2s_cfg) {
+                       quirks = i2s_cfg->quirks;
+                       idma_addr = i2s_cfg->idma_addr;
+               }
+       } else {
+               if (of_find_property(np, "samsung,supports-6ch", NULL))
+                       quirks |= QUIRK_PRI_6CHAN;
+
+               if (of_find_property(np, "samsung,supports-secdai", NULL))
+                       quirks |= QUIRK_SEC_DAI;
+
+               if (of_find_property(np, "samsung,supports-rstclr", NULL))
+                       quirks |= QUIRK_NEED_RSTCLR;
+
+               if (of_property_read_u32(np, "samsung,idma-addr",
+                                        &idma_addr)) {
+                       if (quirks & QUIRK_SEC_DAI) {
+                               dev_err(&pdev->dev, "idma address is not"\
+                                               "specified");
+                               return -EINVAL;
+                       }
+               }
+       }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
@@ -1051,24 +1181,14 @@ static int samsung_i2s_probe(struct platform_device *pdev)
        }
        regs_base = res->start;
 
-       i2s_cfg = &i2s_pdata->type.i2s;
-       quirks = i2s_cfg->quirks;
-
-       pri_dai = i2s_alloc_dai(pdev, false);
-       if (!pri_dai) {
-               dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
-               ret = -ENOMEM;
-               goto err;
-       }
-
        pri_dai->dma_playback.dma_addr = regs_base + I2STXD;
        pri_dai->dma_capture.dma_addr = regs_base + I2SRXD;
        pri_dai->dma_playback.client =
                (struct s3c2410_dma_client *)&pri_dai->dma_playback;
+       pri_dai->dma_playback.ch_name = "tx";
        pri_dai->dma_capture.client =
                (struct s3c2410_dma_client *)&pri_dai->dma_capture;
-       pri_dai->dma_playback.channel = dma_pl_chan;
-       pri_dai->dma_capture.channel = dma_cp_chan;
+       pri_dai->dma_capture.ch_name = "rx";
        pri_dai->dma_playback.dma_size = 4;
        pri_dai->dma_capture.dma_size = 4;
        pri_dai->base = regs_base;
@@ -1087,20 +1207,34 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                sec_dai->dma_playback.dma_addr = regs_base + I2STXDS;
                sec_dai->dma_playback.client =
                        (struct s3c2410_dma_client *)&sec_dai->dma_playback;
-               /* Use iDMA always if SysDMA not provided */
-               sec_dai->dma_playback.channel = dma_pl_sec_chan ? : -1;
+               sec_dai->dma_playback.ch_name = "tx-sec";
+
+               if (!np) {
+                       res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
+                       if (res)
+                               sec_dai->dma_playback.channel = res->start;
+               }
+
                sec_dai->dma_playback.dma_size = 4;
                sec_dai->base = regs_base;
                sec_dai->quirks = quirks;
-               sec_dai->idma_playback.dma_addr = i2s_cfg->idma_addr;
+               sec_dai->idma_playback.dma_addr = idma_addr;
                sec_dai->pri_dai = pri_dai;
                pri_dai->sec_dai = sec_dai;
        }
 
-       if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
-               dev_err(&pdev->dev, "Unable to configure gpio\n");
-               ret = -EINVAL;
-               goto err;
+       if (np) {
+               if (samsung_i2s_parse_dt_gpio(pri_dai)) {
+                       dev_err(&pdev->dev, "Unable to configure gpio\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
+       } else {
+               if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
+                       dev_err(&pdev->dev, "Unable to configure gpio\n");
+                       ret = -EINVAL;
+                       goto err;
+               }
        }
 
        snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv);
@@ -1120,10 +1254,14 @@ static int samsung_i2s_remove(struct platform_device *pdev)
 {
        struct i2s_dai *i2s, *other;
        struct resource *res;
+       struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
 
        i2s = dev_get_drvdata(&pdev->dev);
        other = i2s->pri_dai ? : i2s->sec_dai;
 
+       if (!i2s_pdata->cfg_gpio && pdev->dev.of_node)
+               samsung_i2s_dt_gpio_free(i2s->pri_dai);
+
        if (other) {
                other->pri_dai = NULL;
                other->sec_dai = NULL;
@@ -1143,12 +1281,47 @@ static int samsung_i2s_remove(struct platform_device *pdev)
        return 0;
 }
 
+static struct platform_device_id samsung_i2s_driver_ids[] = {
+       {
+               .name           = "samsung-i2s",
+               .driver_data    = TYPE_PRI,
+       }, {
+               .name           = "samsung-i2s-sec",
+               .driver_data    = TYPE_SEC,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(platform, samsung-i2s-driver-ids);
+
+#ifdef CONFIG_OF
+static struct samsung_i2s_dai_data samsung_i2s_dai_data_array[] = {
+       [TYPE_PRI] = { TYPE_PRI },
+       [TYPE_SEC] = { TYPE_SEC },
+};
+
+static const struct of_device_id exynos_i2s_match[] = {
+       { .compatible = "samsung,i2s-v5",
+         .data = &samsung_i2s_dai_data_array[TYPE_PRI],
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, exynos_i2s_match);
+#endif
+
+static const struct dev_pm_ops samsung_i2s_pm = {
+       SET_RUNTIME_PM_OPS(i2s_runtime_suspend,
+                               i2s_runtime_resume, NULL)
+};
+
 static struct platform_driver samsung_i2s_driver = {
        .probe  = samsung_i2s_probe,
        .remove = samsung_i2s_remove,
+       .id_table = samsung_i2s_driver_ids,
        .driver = {
                .name = "samsung-i2s",
                .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(exynos_i2s_match),
+               .pm = &samsung_i2s_pm,
        },
 };
 
index d420a7ca56cada50ac1db744a9df05fffa143168..7966afc934db3905d8c629cc3d83d82f7fd8f329 100644 (file)
 #ifndef __SND_SOC_SAMSUNG_I2S_H
 #define __SND_SOC_SAMSUNG_I2S_H
 
-/*
- * Maximum number of I2S blocks that any SoC can have.
- * The secondary interface of a CPU dai(if there exists any),
- * is indexed at [cpu-dai's ID + SAMSUNG_I2S_SECOFF]
- */
-#define SAMSUNG_I2S_SECOFF     4
-
 #define SAMSUNG_I2S_DIV_BCLK   1
 
 #define SAMSUNG_I2S_RCLKSRC_0  0
index ee10e8704e976f61f7ea6332416946af5471f59a..13f6dd1ceb00ae2d48febed247630af3e56ecc38 100644 (file)
@@ -469,7 +469,7 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
 {
        int ret = 0;
 
-       ret = s3c_i2sv2_register_dai(&pdev->dev, -1, &s3c2412_i2s_dai);
+       ret = snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai);
        if (ret) {
                pr_err("failed to register the dai\n");
                return ret;
index 7e2b710763be05c96537214189612fae3c538aa9..7a16b32ed673372de134c3b87ba457b1f5e853d2 100644 (file)
@@ -193,9 +193,9 @@ static struct snd_soc_dai_link smdk_dai[] = {
        [SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */
                .name = "Sec_FIFO TX",
                .stream_name = "Playback",
-               .cpu_dai_name = "samsung-i2s.x",
+               .cpu_dai_name = "samsung-i2s-sec",
                .codec_dai_name = "wm8580-hifi-playback",
-               .platform_name = "samsung-i2s.x",
+               .platform_name = "samsung-i2s-sec",
                .codec_name = "wm8580.0-001b",
                .ops = &smdk_ops,
        },
@@ -223,9 +223,6 @@ static int __init smdk_audio_init(void)
        if (machine_is_smdkc100()
                        || machine_is_smdkv210() || machine_is_smdkc110()) {
                smdk.num_links = 3;
-               /* Secondary is at offset SAMSUNG_I2S_SECOFF from Primary */
-               str = (char *)smdk_dai[SEC_PLAYBACK].cpu_dai_name;
-               str[strlen(str) - 1] = '0' + SAMSUNG_I2S_SECOFF;
        } else if (machine_is_smdk6410()) {
                str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
                str[strlen(str) - 1] = '2';
index b0d0ab8bff5ae8299628cd1aec8d229af4a73af6..581ea4a06fc684d1e3f677477326426d7334b427 100644 (file)
@@ -10,6 +10,7 @@
 #include "../codecs/wm8994.h"
 #include <sound/pcm_params.h>
 #include <linux/module.h>
+#include <linux/of.h>
 
  /*
   * Default CFG switch settings to use this driver:
@@ -134,9 +135,9 @@ static struct snd_soc_dai_link smdk_dai[] = {
        }, { /* Sec_Fifo Playback i/f */
                .name = "Sec_FIFO TX",
                .stream_name = "Sec_Dai",
-               .cpu_dai_name = "samsung-i2s.4",
+               .cpu_dai_name = "samsung-i2s-sec",
                .codec_dai_name = "wm8994-aif1",
-               .platform_name = "samsung-i2s.4",
+               .platform_name = "samsung-i2s-sec",
                .codec_name = "wm8994-codec",
                .ops = &smdk_ops,
        },
@@ -153,9 +154,25 @@ static struct snd_soc_card smdk = {
 static int smdk_audio_probe(struct platform_device *pdev)
 {
        int ret;
+       struct device_node *np = pdev->dev.of_node;
        struct snd_soc_card *card = &smdk;
 
        card->dev = &pdev->dev;
+
+       if (np) {
+               smdk_dai[0].cpu_dai_name = NULL;
+               smdk_dai[0].cpu_of_node = of_parse_phandle(np,
+                               "samsung,i2s-controller", 0);
+               if (!smdk_dai[0].cpu_of_node) {
+                       dev_err(&pdev->dev,
+                          "Property 'samsung,i2s-controller' missing or invalid\n");
+                       ret = -EINVAL;
+               }
+
+               smdk_dai[0].platform_name = NULL;
+               smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node;
+       }
+
        ret = snd_soc_register_card(card);
 
        if (ret)
@@ -173,10 +190,19 @@ static int smdk_audio_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id samsung_wm8994_of_match[] = {
+       { .compatible = "samsung,smdk-wm8994", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
+#endif /* CONFIG_OF */
+
 static struct platform_driver smdk_audio_driver = {
        .driver         = {
                .name   = "smdk-audio",
                .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(samsung_wm8994_of_match),
        },
        .probe          = smdk_audio_probe,
        .remove         = smdk_audio_remove,
index a606d0f93d1cea4db2e88f47e8ba0d94b36aa59f..c724026a246f6ec90f80b11dfed4397fee6d7d37 100644 (file)
@@ -16,6 +16,8 @@
 #include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/scatterlist.h>
 #include <linux/sh_dma.h>
 #include <linux/slab.h>
 
 #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 
-typedef int (*set_rate_func)(struct device *dev, int rate, int enable);
-
 /*
  * bus options
  *
@@ -244,8 +244,7 @@ struct fsi_clk {
        struct clk *ick;
        struct clk *div;
        int (*set_rate)(struct device *dev,
-                       struct fsi_priv *fsi,
-                       unsigned long rate);
+                       struct fsi_priv *fsi);
 
        unsigned long rate;
        unsigned int count;
@@ -254,7 +253,6 @@ struct fsi_clk {
 struct fsi_priv {
        void __iomem *base;
        struct fsi_master *master;
-       struct sh_fsi_port_info *info;
 
        struct fsi_stream playback;
        struct fsi_stream capture;
@@ -270,8 +268,6 @@ struct fsi_priv {
        int enable_stream:1;
        int bit_clk_inv:1;
        int lr_clk_inv:1;
-
-       long rate;
 };
 
 struct fsi_stream_handler {
@@ -303,7 +299,7 @@ struct fsi_master {
        int irq;
        struct fsi_priv fsia;
        struct fsi_priv fsib;
-       struct fsi_core *core;
+       const struct fsi_core *core;
        spinlock_t lock;
 };
 
@@ -431,22 +427,6 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
        return fsi_get_priv_frm_dai(fsi_get_dai(substream));
 }
 
-static set_rate_func fsi_get_info_set_rate(struct fsi_priv *fsi)
-{
-       if (!fsi->info)
-               return NULL;
-
-       return fsi->info->set_rate;
-}
-
-static u32 fsi_get_info_flags(struct fsi_priv *fsi)
-{
-       if (!fsi->info)
-               return 0;
-
-       return fsi->info->flags;
-}
-
 static u32 fsi_get_port_shift(struct fsi_priv *fsi, struct fsi_stream *io)
 {
        int is_play = fsi_stream_is_play(fsi, io);
@@ -757,8 +737,7 @@ static int fsi_clk_init(struct device *dev,
                        int ick,
                        int div,
                        int (*set_rate)(struct device *dev,
-                                       struct fsi_priv *fsi,
-                                       unsigned long rate))
+                                       struct fsi_priv *fsi))
 {
        struct fsi_clk *clock = &fsi->clock;
        int is_porta = fsi_is_port_a(fsi);
@@ -829,8 +808,7 @@ static int fsi_clk_is_valid(struct fsi_priv *fsi)
 }
 
 static int fsi_clk_enable(struct device *dev,
-                         struct fsi_priv *fsi,
-                         unsigned long rate)
+                         struct fsi_priv *fsi)
 {
        struct fsi_clk *clock = &fsi->clock;
        int ret = -EINVAL;
@@ -839,7 +817,7 @@ static int fsi_clk_enable(struct device *dev,
                return ret;
 
        if (0 == clock->count) {
-               ret = clock->set_rate(dev, fsi, rate);
+               ret = clock->set_rate(dev, fsi);
                if (ret < 0) {
                        fsi_clk_invalid(fsi);
                        return ret;
@@ -946,11 +924,11 @@ static int fsi_clk_set_ackbpf(struct device *dev,
 }
 
 static int fsi_clk_set_rate_external(struct device *dev,
-                                    struct fsi_priv *fsi,
-                                    unsigned long rate)
+                                    struct fsi_priv *fsi)
 {
        struct clk *xck = fsi->clock.xck;
        struct clk *ick = fsi->clock.ick;
+       unsigned long rate = fsi->clock.rate;
        unsigned long xrate;
        int ackmd, bpfmd;
        int ret = 0;
@@ -978,11 +956,11 @@ static int fsi_clk_set_rate_external(struct device *dev,
 }
 
 static int fsi_clk_set_rate_cpg(struct device *dev,
-                               struct fsi_priv *fsi,
-                               unsigned long rate)
+                               struct fsi_priv *fsi)
 {
        struct clk *ick = fsi->clock.ick;
        struct clk *div = fsi->clock.div;
+       unsigned long rate = fsi->clock.rate;
        unsigned long target = 0; /* 12288000 or 11289600 */
        unsigned long actual, cout;
        unsigned long diff, min;
@@ -1063,85 +1041,6 @@ static int fsi_clk_set_rate_cpg(struct device *dev,
        return ret;
 }
 
-static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi,
-                             long rate, int enable)
-{
-       set_rate_func set_rate = fsi_get_info_set_rate(fsi);
-       int ret;
-
-       /*
-        * CAUTION
-        *
-        * set_rate will be deleted
-        */
-       if (!set_rate) {
-               if (enable)
-                       return fsi_clk_enable(dev, fsi, rate);
-               else
-                       return fsi_clk_disable(dev, fsi);
-       }
-
-       ret = set_rate(dev, rate, enable);
-       if (ret < 0) /* error */
-               return ret;
-
-       if (!enable)
-               return 0;
-
-       if (ret > 0) {
-               u32 data = 0;
-
-               switch (ret & SH_FSI_ACKMD_MASK) {
-               default:
-                       /* FALL THROUGH */
-               case SH_FSI_ACKMD_512:
-                       data |= (0x0 << 12);
-                       break;
-               case SH_FSI_ACKMD_256:
-                       data |= (0x1 << 12);
-                       break;
-               case SH_FSI_ACKMD_128:
-                       data |= (0x2 << 12);
-                       break;
-               case SH_FSI_ACKMD_64:
-                       data |= (0x3 << 12);
-                       break;
-               case SH_FSI_ACKMD_32:
-                       data |= (0x4 << 12);
-                       break;
-               }
-
-               switch (ret & SH_FSI_BPFMD_MASK) {
-               default:
-                       /* FALL THROUGH */
-               case SH_FSI_BPFMD_32:
-                       data |= (0x0 << 8);
-                       break;
-               case SH_FSI_BPFMD_64:
-                       data |= (0x1 << 8);
-                       break;
-               case SH_FSI_BPFMD_128:
-                       data |= (0x2 << 8);
-                       break;
-               case SH_FSI_BPFMD_256:
-                       data |= (0x3 << 8);
-                       break;
-               case SH_FSI_BPFMD_512:
-                       data |= (0x4 << 8);
-                       break;
-               case SH_FSI_BPFMD_16:
-                       data |= (0x7 << 8);
-                       break;
-               }
-
-               fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data);
-               udelay(10);
-               ret = 0;
-       }
-
-       return ret;
-}
-
 /*
  *             pio data transfer handler
  */
@@ -1637,7 +1536,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
                          struct fsi_stream *io,
                          struct device *dev)
 {
-       u32 flags = fsi_get_info_flags(fsi);
        u32 data = 0;
 
        /* clock setting */
@@ -1654,19 +1552,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
                data |= (1 << 4);
        if (fsi_is_clk_master(fsi))
                data <<= 8;
-       /* FIXME
-        *
-        * SH_FSI_xxx_INV style will be removed
-        */
-       if (SH_FSI_LRM_INV & flags)
-               data |= 1 << 12;
-       if (SH_FSI_BRM_INV & flags)
-               data |= 1 << 8;
-       if (SH_FSI_LRS_INV & flags)
-               data |= 1 << 4;
-       if (SH_FSI_BRS_INV & flags)
-               data |= 1 << 0;
-
        fsi_reg_write(fsi, CKG2, data);
 
        /* spdif ? */
@@ -1698,7 +1583,7 @@ static int fsi_hw_startup(struct fsi_priv *fsi,
 
        /* start master clock */
        if (fsi_is_clk_master(fsi))
-               return fsi_set_master_clk(dev, fsi, fsi->rate, 1);
+               return fsi_clk_enable(dev, fsi);
 
        return 0;
 }
@@ -1708,7 +1593,7 @@ static int fsi_hw_shutdown(struct fsi_priv *fsi,
 {
        /* stop master clock */
        if (fsi_is_clk_master(fsi))
-               return fsi_set_master_clk(dev, fsi, fsi->rate, 0);
+               return fsi_clk_disable(dev, fsi);
 
        return 0;
 }
@@ -1719,7 +1604,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream,
        struct fsi_priv *fsi = fsi_get_priv(substream);
 
        fsi_clk_invalid(fsi);
-       fsi->rate = 0;
 
        return 0;
 }
@@ -1730,7 +1614,6 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
        struct fsi_priv *fsi = fsi_get_priv(substream);
 
        fsi_clk_invalid(fsi);
-       fsi->rate = 0;
 }
 
 static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -1795,7 +1678,6 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
 static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
        struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai);
-       set_rate_func set_rate = fsi_get_info_set_rate(fsi);
        int ret;
 
        /* set master/slave audio interface */
@@ -1831,14 +1713,6 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        }
 
        if (fsi_is_clk_master(fsi)) {
-               /*
-                * CAUTION
-                *
-                * set_rate will be deleted
-                */
-               if (set_rate)
-                       dev_warn(dai->dev, "set_rate will be removed soon\n");
-
                if (fsi->clk_cpg)
                        fsi_clk_init(dai->dev, fsi, 0, 1, 1,
                                     fsi_clk_set_rate_cpg);
@@ -1862,10 +1736,8 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
 {
        struct fsi_priv *fsi = fsi_get_priv(substream);
 
-       if (fsi_is_clk_master(fsi)) {
-               fsi->rate = params_rate(params);
-               fsi_clk_valid(fsi, fsi->rate);
-       }
+       if (fsi_is_clk_master(fsi))
+               fsi_clk_valid(fsi, params_rate(params));
 
        return 0;
 }
@@ -2017,6 +1889,33 @@ static struct snd_soc_platform_driver fsi_soc_platform = {
 /*
  *             platform function
  */
+static void fsi_of_parse(char *name,
+                        struct device_node *np,
+                        struct sh_fsi_port_info *info,
+                        struct device *dev)
+{
+       int i;
+       char prop[128];
+       unsigned long flags = 0;
+       struct {
+               char *name;
+               unsigned int val;
+       } of_parse_property[] = {
+               { "spdif-connection",           SH_FSI_FMT_SPDIF },
+               { "stream-mode-support",        SH_FSI_ENABLE_STREAM_MODE },
+               { "use-internal-clock",         SH_FSI_CLK_CPG },
+       };
+
+       for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) {
+               sprintf(prop, "%s,%s", name, of_parse_property[i].name);
+               if (of_get_property(np, prop, NULL))
+                       flags |= of_parse_property[i].val;
+       }
+       info->flags = flags;
+
+       dev_dbg(dev, "%s flags : %lx\n", name, info->flags);
+}
+
 static void fsi_port_info_init(struct fsi_priv *fsi,
                               struct sh_fsi_port_info *info)
 {
@@ -2044,23 +1943,40 @@ static void fsi_handler_init(struct fsi_priv *fsi,
        }
 }
 
+static struct of_device_id fsi_of_match[];
 static int fsi_probe(struct platform_device *pdev)
 {
        struct fsi_master *master;
-       const struct platform_device_id *id_entry;
-       struct sh_fsi_platform_info *info = pdev->dev.platform_data;
-       struct sh_fsi_port_info nul_info, *pinfo;
+       struct device_node *np = pdev->dev.of_node;
+       struct sh_fsi_platform_info info;
+       const struct fsi_core *core;
        struct fsi_priv *fsi;
        struct resource *res;
        unsigned int irq;
        int ret;
 
-       nul_info.flags  = 0;
-       nul_info.tx_id  = 0;
-       nul_info.rx_id  = 0;
+       memset(&info, 0, sizeof(info));
+
+       core = NULL;
+       if (np) {
+               const struct of_device_id *of_id;
 
-       id_entry = pdev->id_entry;
-       if (!id_entry) {
+               of_id = of_match_device(fsi_of_match, &pdev->dev);
+               if (of_id) {
+                       core = of_id->data;
+                       fsi_of_parse("fsia", np, &info.port_a, &pdev->dev);
+                       fsi_of_parse("fsib", np, &info.port_b, &pdev->dev);
+               }
+       } else {
+               const struct platform_device_id *id_entry = pdev->id_entry;
+               if (id_entry)
+                       core = (struct fsi_core *)id_entry->driver_data;
+
+               if (pdev->dev.platform_data)
+                       memcpy(&info, pdev->dev.platform_data, sizeof(info));
+       }
+
+       if (!core) {
                dev_err(&pdev->dev, "unknown fsi device\n");
                return -ENODEV;
        }
@@ -2087,17 +2003,15 @@ static int fsi_probe(struct platform_device *pdev)
 
        /* master setting */
        master->irq             = irq;
-       master->core            = (struct fsi_core *)id_entry->driver_data;
+       master->core            = core;
        spin_lock_init(&master->lock);
 
        /* FSI A setting */
-       pinfo           = (info) ? &info->port_a : &nul_info;
        fsi             = &master->fsia;
        fsi->base       = master->base;
        fsi->master     = master;
-       fsi->info       = pinfo;
-       fsi_port_info_init(fsi, pinfo);
-       fsi_handler_init(fsi, pinfo);
+       fsi_port_info_init(fsi, &info.port_a);
+       fsi_handler_init(fsi, &info.port_a);
        ret = fsi_stream_probe(fsi, &pdev->dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "FSIA stream probe failed\n");
@@ -2105,13 +2019,11 @@ static int fsi_probe(struct platform_device *pdev)
        }
 
        /* FSI B setting */
-       pinfo           = (info) ? &info->port_b : &nul_info;
        fsi             = &master->fsib;
        fsi->base       = master->base + 0x40;
        fsi->master     = master;
-       fsi->info       = pinfo;
-       fsi_port_info_init(fsi, pinfo);
-       fsi_handler_init(fsi, pinfo);
+       fsi_port_info_init(fsi, &info.port_b);
+       fsi_handler_init(fsi, &info.port_b);
        ret = fsi_stream_probe(fsi, &pdev->dev);
        if (ret < 0) {
                dev_err(&pdev->dev, "FSIB stream probe failed\n");
@@ -2122,7 +2034,7 @@ static int fsi_probe(struct platform_device *pdev)
        dev_set_drvdata(&pdev->dev, master);
 
        ret = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0,
-                         id_entry->name, master);
+                              dev_name(&pdev->dev), master);
        if (ret) {
                dev_err(&pdev->dev, "irq request err\n");
                goto exit_fsib;
@@ -2248,6 +2160,13 @@ static struct fsi_core fsi2_core = {
        .b_mclk = B_MST_CTLR,
 };
 
+static struct of_device_id fsi_of_match[] = {
+       { .compatible = "renesas,sh_fsi",       .data = &fsi1_core},
+       { .compatible = "renesas,sh_fsi2",      .data = &fsi2_core},
+       {},
+};
+MODULE_DEVICE_TABLE(of, fsi_of_match);
+
 static struct platform_device_id fsi_id_table[] = {
        { "sh_fsi",     (kernel_ulong_t)&fsi1_core },
        { "sh_fsi2",    (kernel_ulong_t)&fsi2_core },
@@ -2259,6 +2178,7 @@ static struct platform_driver fsi_driver = {
        .driver         = {
                .name   = "fsi-pcm-audio",
                .pm     = &fsi_pm_ops,
+               .of_match_table = fsi_of_match,
        },
        .probe          = fsi_probe,
        .remove         = fsi_remove,
index 5fbfb06e80831ed97cc42bb91df52568f779fd7f..b5b3db71e25303813bfe216e7f0c7020dcf97ff3 100644 (file)
@@ -33,6 +33,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
                ret = platform->driver->compr_ops->open(cstream);
                if (ret < 0) {
@@ -61,15 +63,46 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
        codec_dai->active++;
        rtd->codec->active++;
 
+       mutex_unlock(&rtd->pcm_mutex);
+
        return 0;
 
 machine_err:
        if (platform->driver->compr_ops && platform->driver->compr_ops->free)
                platform->driver->compr_ops->free(cstream);
 out:
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
+/*
+ * Power down the audio subsystem pmdown_time msecs after close is called.
+ * This is to ensure there are no pops or clicks in between any music tracks
+ * due to DAPM power cycling.
+ */
+static void close_delayed_work(struct work_struct *work)
+{
+       struct snd_soc_pcm_runtime *rtd =
+                       container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+       dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n",
+                codec_dai->driver->playback.stream_name,
+                codec_dai->playback_active ? "active" : "inactive",
+                rtd->pop_wait ? "yes" : "no");
+
+       /* are we waiting on this codec DAI stream */
+       if (rtd->pop_wait == 1) {
+               rtd->pop_wait = 0;
+               snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
+                                         SND_SOC_DAPM_STREAM_STOP);
+       }
+
+       mutex_unlock(&rtd->pcm_mutex);
+}
+
 static int soc_compr_free(struct snd_compr_stream *cstream)
 {
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -78,6 +111,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_codec *codec = rtd->codec;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (cstream->direction == SND_COMPRESS_PLAYBACK) {
                cpu_dai->playback_active--;
                codec_dai->playback_active--;
@@ -86,7 +121,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
                codec_dai->capture_active--;
        }
 
-       snd_soc_dai_digital_mute(codec_dai, 1);
+       snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction);
 
        cpu_dai->active--;
        codec_dai->active--;
@@ -112,10 +147,11 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
                        snd_soc_dapm_stream_event(rtd,
                                        SNDRV_PCM_STREAM_PLAYBACK,
                                        SND_SOC_DAPM_STREAM_STOP);
-               } else
+               } else {
                        rtd->pop_wait = 1;
                        schedule_delayed_work(&rtd->delayed_work,
                                msecs_to_jiffies(rtd->pmdown_time));
+               }
        } else {
                /* capture streams can be powered down now */
                snd_soc_dapm_stream_event(rtd,
@@ -123,6 +159,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
                        SND_SOC_DAPM_STREAM_STOP);
        }
 
+       mutex_unlock(&rtd->pcm_mutex);
        return 0;
 }
 
@@ -134,17 +171,25 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
                ret = platform->driver->compr_ops->trigger(cstream, cmd);
                if (ret < 0)
-                       return ret;
+                       goto out;
        }
 
-       if (cmd == SNDRV_PCM_TRIGGER_START)
-               snd_soc_dai_digital_mute(codec_dai, 0);
-       else if (cmd == SNDRV_PCM_TRIGGER_STOP)
-               snd_soc_dai_digital_mute(codec_dai, 1);
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction);
+               break;
+       }
 
+out:
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -155,6 +200,8 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream,
        struct snd_soc_platform *platform = rtd->platform;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        /* first we call set_params for the platform driver
         * this should configure the soc side
         * if the machine has compressed ops then we call that as well
@@ -164,18 +211,20 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream,
        if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) {
                ret = platform->driver->compr_ops->set_params(cstream, params);
                if (ret < 0)
-                       return ret;
+                       goto out;
        }
 
        if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) {
                ret = rtd->dai_link->compr_ops->set_params(cstream);
                if (ret < 0)
-                       return ret;
+                       goto out;
        }
 
        snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
                                SND_SOC_DAPM_STREAM_START);
 
+out:
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -186,9 +235,12 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream,
        struct snd_soc_platform *platform = rtd->platform;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->get_params)
                ret = platform->driver->compr_ops->get_params(cstream, params);
 
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -199,9 +251,12 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream,
        struct snd_soc_platform *platform = rtd->platform;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps)
                ret = platform->driver->compr_ops->get_caps(cstream, caps);
 
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -212,9 +267,12 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream,
        struct snd_soc_platform *platform = rtd->platform;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps)
                ret = platform->driver->compr_ops->get_codec_caps(cstream, codec);
 
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -224,9 +282,12 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes)
        struct snd_soc_platform *platform = rtd->platform;
        int ret = 0;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->ack)
                ret = platform->driver->compr_ops->ack(cstream, bytes);
 
+       mutex_unlock(&rtd->pcm_mutex);
        return ret;
 }
 
@@ -236,12 +297,31 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream,
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
        struct snd_soc_platform *platform = rtd->platform;
 
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
        if (platform->driver->compr_ops && platform->driver->compr_ops->pointer)
                 platform->driver->compr_ops->pointer(cstream, tstamp);
 
+       mutex_unlock(&rtd->pcm_mutex);
        return 0;
 }
 
+static int soc_compr_copy(struct snd_compr_stream *cstream,
+                         const char __user *buf, size_t count)
+{
+       struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+       struct snd_soc_platform *platform = rtd->platform;
+       int ret = 0;
+
+       mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+       if (platform->driver->compr_ops && platform->driver->compr_ops->copy)
+               ret = platform->driver->compr_ops->copy(cstream, buf, count);
+
+       mutex_unlock(&rtd->pcm_mutex);
+       return ret;
+}
+
 /* ASoC Compress operations */
 static struct snd_compr_ops soc_compr_ops = {
        .open           = soc_compr_open,
@@ -259,6 +339,7 @@ static struct snd_compr_ops soc_compr_ops = {
 int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
 {
        struct snd_soc_codec *codec = rtd->codec;
+       struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_compr *compr;
@@ -275,20 +356,38 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
                return -ENOMEM;
        }
 
-       compr->ops = &soc_compr_ops;
+       compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops),
+                                 GFP_KERNEL);
+       if (compr->ops == NULL) {
+               dev_err(rtd->card->dev, "Cannot allocate compressed ops\n");
+               ret = -ENOMEM;
+               goto compr_err;
+       }
+       memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops));
+
+       /* Add copy callback for not memory mapped DSPs */
+       if (platform->driver->compr_ops && platform->driver->compr_ops->copy)
+               compr->ops->copy = soc_compr_copy;
+
        mutex_init(&compr->lock);
        ret = snd_compress_new(rtd->card->snd_card, num, direction, compr);
        if (ret < 0) {
                pr_err("compress asoc: can't create compress for codec %s\n",
                        codec->name);
-               kfree(compr);
-               return ret;
+               goto compr_err;
        }
 
+       /* DAPM dai link stream work */
+       INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
+
        rtd->compr = compr;
        compr->private_data = rtd;
 
        printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name,
                cpu_dai->name);
        return ret;
+
+compr_err:
+       kfree(compr);
+       return ret;
 }
index 2370063b58245946f71adfa05f361097d3e53374..8df1b3feaf2b53142a692d399370bca2b80b365f 100644 (file)
@@ -1107,6 +1107,10 @@ static int soc_probe_codec(struct snd_soc_card *card,
                                "ASoC: failed to probe CODEC %d\n", ret);
                        goto err_probe;
                }
+               WARN(codec->dapm.idle_bias_off &&
+                       codec->dapm.bias_level != SND_SOC_BIAS_OFF,
+                       "codec %s can not start from non-off bias"
+                       " with idle_bias_off==1\n", codec->name);
        }
 
        /* If the driver didn't set I/O up try regmap */
@@ -3122,9 +3126,12 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
        if (!codec->using_regmap)
                return -EINVAL;
 
-       data = ucontrol->value.bytes.data;
        len = params->num_regs * codec->val_bytes;
 
+       data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+       if (!data)
+               return -ENOMEM;
+
        /*
         * If we've got a mask then we need to preserve the register
         * bits.  We shouldn't modify the incoming data so take a
@@ -3137,10 +3144,6 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
 
                val &= params->mask;
 
-               data = kmemdup(data, len, GFP_KERNEL);
-               if (!data)
-                       return -ENOMEM;
-
                switch (codec->val_bytes) {
                case 1:
                        ((u8 *)data)[0] &= ~params->mask;
@@ -3162,8 +3165,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
        ret = regmap_raw_write(codec->control_data, params->base,
                               data, len);
 
-       if (params->mask)
-               kfree(data);
+       kfree(data);
 
        return ret;
 }
@@ -3540,12 +3542,20 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
  * snd_soc_dai_digital_mute - configure DAI system or master clock.
  * @dai: DAI
  * @mute: mute enable
+ * @direction: stream to mute
  *
  * Mutes the DAI DAC.
  */
-int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
+                            int direction)
 {
-       if (dai->driver && dai->driver->ops->digital_mute)
+       if (!dai->driver)
+               return -ENOTSUPP;
+
+       if (dai->driver->ops->mute_stream)
+               return dai->driver->ops->mute_stream(dai, mute, direction);
+       else if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
+                dai->driver->ops->digital_mute)
                return dai->driver->ops->digital_mute(dai, mute);
        else
                return -ENOTSUPP;
@@ -4208,6 +4218,113 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing);
 
+unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
+                                    const char *prefix)
+{
+       int ret, i;
+       char prop[128];
+       unsigned int format = 0;
+       int bit, frame;
+       const char *str;
+       struct {
+               char *name;
+               unsigned int val;
+       } of_fmt_table[] = {
+               { "i2s",        SND_SOC_DAIFMT_I2S },
+               { "right_j",    SND_SOC_DAIFMT_RIGHT_J },
+               { "left_j",     SND_SOC_DAIFMT_LEFT_J },
+               { "dsp_a",      SND_SOC_DAIFMT_DSP_A },
+               { "dsp_b",      SND_SOC_DAIFMT_DSP_B },
+               { "ac97",       SND_SOC_DAIFMT_AC97 },
+               { "pdm",        SND_SOC_DAIFMT_PDM},
+               { "msb",        SND_SOC_DAIFMT_MSB },
+               { "lsb",        SND_SOC_DAIFMT_LSB },
+       };
+
+       if (!prefix)
+               prefix = "";
+
+       /*
+        * check "[prefix]format = xxx"
+        * SND_SOC_DAIFMT_FORMAT_MASK area
+        */
+       snprintf(prop, sizeof(prop), "%sformat", prefix);
+       ret = of_property_read_string(np, prop, &str);
+       if (ret == 0) {
+               for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) {
+                       if (strcmp(str, of_fmt_table[i].name) == 0) {
+                               format |= of_fmt_table[i].val;
+                               break;
+                       }
+               }
+       }
+
+       /*
+        * check "[prefix]continuous-clock"
+        * SND_SOC_DAIFMT_CLOCK_MASK area
+        */
+       snprintf(prop, sizeof(prop), "%scontinuous-clock", prefix);
+       if (of_get_property(np, prop, NULL))
+               format |= SND_SOC_DAIFMT_CONT;
+       else
+               format |= SND_SOC_DAIFMT_GATED;
+
+       /*
+        * check "[prefix]bitclock-inversion"
+        * check "[prefix]frame-inversion"
+        * SND_SOC_DAIFMT_INV_MASK area
+        */
+       snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix);
+       bit = !!of_get_property(np, prop, NULL);
+
+       snprintf(prop, sizeof(prop), "%sframe-inversion", prefix);
+       frame = !!of_get_property(np, prop, NULL);
+
+       switch ((bit << 4) + frame) {
+       case 0x11:
+               format |= SND_SOC_DAIFMT_IB_IF;
+               break;
+       case 0x10:
+               format |= SND_SOC_DAIFMT_IB_NF;
+               break;
+       case 0x01:
+               format |= SND_SOC_DAIFMT_NB_IF;
+               break;
+       default:
+               /* SND_SOC_DAIFMT_NB_NF is default */
+               break;
+       }
+
+       /*
+        * check "[prefix]bitclock-master"
+        * check "[prefix]frame-master"
+        * SND_SOC_DAIFMT_MASTER_MASK area
+        */
+       snprintf(prop, sizeof(prop), "%sbitclock-master", prefix);
+       bit = !!of_get_property(np, prop, NULL);
+
+       snprintf(prop, sizeof(prop), "%sframe-master", prefix);
+       frame = !!of_get_property(np, prop, NULL);
+
+       switch ((bit << 4) + frame) {
+       case 0x11:
+               format |= SND_SOC_DAIFMT_CBM_CFM;
+               break;
+       case 0x10:
+               format |= SND_SOC_DAIFMT_CBM_CFS;
+               break;
+       case 0x01:
+               format |= SND_SOC_DAIFMT_CBS_CFM;
+               break;
+       default:
+               format |= SND_SOC_DAIFMT_CBS_CFS;
+               break;
+       }
+
+       return format;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
+
 static int __init snd_soc_init(void)
 {
 #ifdef CONFIG_DEBUG_FS
index 258acadb9e7d238a97b7af6508ffc21bd1a4488e..1d6a9b3ceb27cc0b9a6ea307faecaf5872d6b6c9 100644 (file)
@@ -3255,14 +3255,16 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
                break;
 
        case SND_SOC_DAPM_POST_PMU:
-               ret = snd_soc_dai_digital_mute(sink, 0);
+               ret = snd_soc_dai_digital_mute(sink, 0,
+                                              SNDRV_PCM_STREAM_PLAYBACK);
                if (ret != 0 && ret != -ENOTSUPP)
                        dev_warn(sink->dev, "ASoC: Failed to unmute: %d\n", ret);
                ret = 0;
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
-               ret = snd_soc_dai_digital_mute(sink, 1);
+               ret = snd_soc_dai_digital_mute(sink, 1,
+                                              SNDRV_PCM_STREAM_PLAYBACK);
                if (ret != 0 && ret != -ENOTSUPP)
                        dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret);
                ret = 0;
index cf191e6aebbe1d1bfeb9dd511d0fe9b5e5bd3bbd..73bb8eefa4913d2b77b468a5dabe22566c61687b 100644 (file)
@@ -383,8 +383,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
        /* Muting the DAC suppresses artifacts caused during digital
         * shutdown, for example from stopping clocks.
         */
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               snd_soc_dai_digital_mute(codec_dai, 1);
+       snd_soc_dai_digital_mute(codec_dai, 1, substream->stream);
 
        if (cpu_dai->driver->ops->shutdown)
                cpu_dai->driver->ops->shutdown(substream, cpu_dai);
@@ -488,7 +487,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        snd_soc_dapm_stream_event(rtd, substream->stream,
                        SND_SOC_DAPM_STREAM_START);
 
-       snd_soc_dai_digital_mute(codec_dai, 0);
+       snd_soc_dai_digital_mute(codec_dai, 0, substream->stream);
 
 out:
        mutex_unlock(&rtd->pcm_mutex);
@@ -586,7 +585,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
 
        /* apply codec digital mute */
        if (!codec->active)
-               snd_soc_dai_digital_mute(codec_dai, 1);
+               snd_soc_dai_digital_mute(codec_dai, 1, substream->stream);
 
        /* free any machine hw params */
        if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
@@ -1729,20 +1728,16 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
 
        /* startup must always be called for new BEs */
        ret = dpcm_be_dai_startup(fe, stream);
-       if (ret < 0) {
+       if (ret < 0)
                goto disconnect;
-               return ret;
-       }
 
        /* keep going if FE state is > open */
        if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN)
                return 0;
 
        ret = dpcm_be_dai_hw_params(fe, stream);
-       if (ret < 0) {
+       if (ret < 0)
                goto close;
-               return ret;
-       }
 
        /* keep going if FE state is > hw_params */
        if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS)
@@ -1750,10 +1745,8 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
 
 
        ret = dpcm_be_dai_prepare(fe, stream);
-       if (ret < 0) {
+       if (ret < 0)
                goto hw_free;
-               return ret;
-       }
 
        /* run the stream event for each BE */
        dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
index 19e5fe7cc403ac8e55822a52e05bc69c21d1835f..dbc27ce1d4deb32d14f8e6e40291b828d179fbf7 100644 (file)
@@ -6,6 +6,16 @@ config SND_SOC_TEGRA
        help
          Say Y or M here if you want support for SoC audio on Tegra.
 
+config SND_SOC_TEGRA20_AC97
+       tristate
+       depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
+       select SND_SOC_AC97_BUS
+       select SND_SOC_TEGRA20_DAS
+       help
+         Say Y or M if you want to add support for codecs attached to the
+         Tegra20 AC97 interface. You will also need to select the individual
+         machine drivers to support below.
+
 config SND_SOC_TEGRA20_DAS
        tristate
        depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
@@ -70,6 +80,15 @@ config SND_SOC_TEGRA_WM8903
          boards using the WM8093 codec. Currently, the supported boards are
          Harmony, Ventana, Seaboard, Kaen, and Aebl.
 
+config SND_SOC_TEGRA_WM9712
+       tristate "SoC Audio support for Tegra boards using a WM9712 codec"
+       depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
+       select SND_SOC_TEGRA20_AC97
+       select SND_SOC_WM9712
+       help
+         Say Y or M here if you want to add support for SoC audio on Tegra
+         boards using the WM9712 (or compatible) codec.
+
 config SND_SOC_TEGRA_TRIMSLICE
        tristate "SoC Audio support for TrimSlice board"
        depends on SND_SOC_TEGRA && I2C
index 391e78a34c0602b3dfdb6867dcd0fdd48441c219..416a14bde41b59452dc350fa64633e491b28916b 100644 (file)
@@ -1,6 +1,7 @@
 # Tegra platform Support
 snd-soc-tegra-pcm-objs := tegra_pcm.o
 snd-soc-tegra-utils-objs += tegra_asoc_utils.o
+snd-soc-tegra20-ac97-objs := tegra20_ac97.o
 snd-soc-tegra20-das-objs := tegra20_das.o
 snd-soc-tegra20-i2s-objs := tegra20_i2s.o
 snd-soc-tegra20-spdif-objs := tegra20_spdif.o
@@ -9,6 +10,7 @@ snd-soc-tegra30-i2s-objs := tegra30_i2s.o
 
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
 obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o
+obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o
 obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o
 obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o
 obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o
@@ -18,10 +20,12 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o
 # Tegra machine Support
 snd-soc-tegra-wm8753-objs := tegra_wm8753.o
 snd-soc-tegra-wm8903-objs := tegra_wm8903.o
+snd-soc-tegra-wm9712-objs := tegra_wm9712.o
 snd-soc-tegra-trimslice-objs := trimslice.o
 snd-soc-tegra-alc5632-objs := tegra_alc5632.o
 
 obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o
 obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
+obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o
 obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
 obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
new file mode 100644 (file)
index 0000000..336dcdd
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * tegra20_ac97.c - Tegra20 AC97 platform driver
+ *
+ * Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
+ *
+ * Partly based on code copyright/by:
+ *
+ * Copyright (c) 2011,2012 Toradex Inc.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_asoc_utils.h"
+#include "tegra20_ac97.h"
+
+#define DRV_NAME "tegra20-ac97"
+
+static struct tegra20_ac97 *workdata;
+
+static void tegra20_ac97_codec_reset(struct snd_ac97 *ac97)
+{
+       u32 readback;
+       unsigned long timeout;
+
+       /* reset line is not driven by DAC pad group, have to toggle GPIO */
+       gpio_set_value(workdata->reset_gpio, 0);
+       udelay(2);
+
+       gpio_set_value(workdata->reset_gpio, 1);
+       udelay(2);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+
+       do {
+               regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
+               if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY)
+                       break;
+               usleep_range(1000, 2000);
+       } while (!time_after(jiffies, timeout));
+}
+
+static void tegra20_ac97_codec_warm_reset(struct snd_ac97 *ac97)
+{
+       u32 readback;
+       unsigned long timeout;
+
+       /*
+        * although sync line is driven by the DAC pad group warm reset using
+        * the controller cmd is not working, have to toggle sync line
+        * manually.
+        */
+       gpio_request(workdata->sync_gpio, "codec-sync");
+
+       gpio_direction_output(workdata->sync_gpio, 1);
+
+       udelay(2);
+       gpio_set_value(workdata->sync_gpio, 0);
+       udelay(2);
+       gpio_free(workdata->sync_gpio);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+
+       do {
+               regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
+               if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY)
+                       break;
+               usleep_range(1000, 2000);
+       } while (!time_after(jiffies, timeout));
+}
+
+static unsigned short tegra20_ac97_codec_read(struct snd_ac97 *ac97_snd,
+                                             unsigned short reg)
+{
+       u32 readback;
+       unsigned long timeout;
+
+       regmap_write(workdata->regmap, TEGRA20_AC97_CMD,
+                    (((reg | 0x80) << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) &
+                     TEGRA20_AC97_CMD_CMD_ADDR_MASK) |
+                    TEGRA20_AC97_CMD_BUSY);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+
+       do {
+               regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback);
+               if (readback & TEGRA20_AC97_STATUS1_STA_VALID1)
+                       break;
+               usleep_range(1000, 2000);
+       } while (!time_after(jiffies, timeout));
+
+       return ((readback & TEGRA20_AC97_STATUS1_STA_DATA1_MASK) >>
+               TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT);
+}
+
+static void tegra20_ac97_codec_write(struct snd_ac97 *ac97_snd,
+                                    unsigned short reg, unsigned short val)
+{
+       u32 readback;
+       unsigned long timeout;
+
+       regmap_write(workdata->regmap, TEGRA20_AC97_CMD,
+                    ((reg << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) &
+                     TEGRA20_AC97_CMD_CMD_ADDR_MASK) |
+                    ((val << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) &
+                     TEGRA20_AC97_CMD_CMD_DATA_MASK) |
+                    TEGRA20_AC97_CMD_BUSY);
+
+       timeout = jiffies + msecs_to_jiffies(100);
+
+       do {
+               regmap_read(workdata->regmap, TEGRA20_AC97_CMD, &readback);
+               if (!(readback & TEGRA20_AC97_CMD_BUSY))
+                       break;
+               usleep_range(1000, 2000);
+       } while (!time_after(jiffies, timeout));
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+       .read           = tegra20_ac97_codec_read,
+       .write          = tegra20_ac97_codec_write,
+       .reset          = tegra20_ac97_codec_reset,
+       .warm_reset     = tegra20_ac97_codec_warm_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static inline void tegra20_ac97_start_playback(struct tegra20_ac97 *ac97)
+{
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
+                          TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN,
+                          TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN);
+
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL,
+                          TEGRA20_AC97_CTRL_PCM_DAC_EN |
+                          TEGRA20_AC97_CTRL_STM_EN,
+                          TEGRA20_AC97_CTRL_PCM_DAC_EN |
+                          TEGRA20_AC97_CTRL_STM_EN);
+}
+
+static inline void tegra20_ac97_stop_playback(struct tegra20_ac97 *ac97)
+{
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
+                          TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, 0);
+
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL,
+                          TEGRA20_AC97_CTRL_PCM_DAC_EN, 0);
+}
+
+static inline void tegra20_ac97_start_capture(struct tegra20_ac97 *ac97)
+{
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
+                          TEGRA20_AC97_FIFO_SCR_REC_FULL_EN,
+                          TEGRA20_AC97_FIFO_SCR_REC_FULL_EN);
+}
+
+static inline void tegra20_ac97_stop_capture(struct tegra20_ac97 *ac97)
+{
+       regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR,
+                          TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, 0);
+}
+
+static int tegra20_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+                               struct snd_soc_dai *dai)
+{
+       struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       tegra20_ac97_start_playback(ac97);
+               else
+                       tegra20_ac97_start_capture(ac97);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       tegra20_ac97_stop_playback(ac97);
+               else
+                       tegra20_ac97_stop_capture(ac97);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = {
+       .trigger        = tegra20_ac97_trigger,
+};
+
+static int tegra20_ac97_probe(struct snd_soc_dai *dai)
+{
+       struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai);
+
+       dai->capture_dma_data = &ac97->capture_dma_data;
+       dai->playback_dma_data = &ac97->playback_dma_data;
+
+       return 0;
+}
+
+static struct snd_soc_dai_driver tegra20_ac97_dai = {
+       .name = "tegra-ac97-pcm",
+       .ac97_control = 1,
+       .probe = tegra20_ac97_probe,
+       .playback = {
+               .stream_name = "PCM Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "PCM Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .ops = &tegra20_ac97_dai_ops,
+};
+
+static bool tegra20_ac97_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case TEGRA20_AC97_CTRL:
+       case TEGRA20_AC97_CMD:
+       case TEGRA20_AC97_STATUS1:
+       case TEGRA20_AC97_FIFO1_SCR:
+       case TEGRA20_AC97_FIFO_TX1:
+       case TEGRA20_AC97_FIFO_RX1:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+
+static bool tegra20_ac97_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case TEGRA20_AC97_STATUS1:
+       case TEGRA20_AC97_FIFO1_SCR:
+       case TEGRA20_AC97_FIFO_TX1:
+       case TEGRA20_AC97_FIFO_RX1:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+
+static bool tegra20_ac97_precious_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case TEGRA20_AC97_FIFO_TX1:
+       case TEGRA20_AC97_FIFO_RX1:
+               return true;
+       default:
+               break;
+       }
+
+       return false;
+}
+
+static const struct regmap_config tegra20_ac97_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = TEGRA20_AC97_FIFO_RX1,
+       .writeable_reg = tegra20_ac97_wr_rd_reg,
+       .readable_reg = tegra20_ac97_wr_rd_reg,
+       .volatile_reg = tegra20_ac97_volatile_reg,
+       .precious_reg = tegra20_ac97_precious_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static int tegra20_ac97_platform_probe(struct platform_device *pdev)
+{
+       struct tegra20_ac97 *ac97;
+       struct resource *mem, *memregion;
+       u32 of_dma[2];
+       void __iomem *regs;
+       int ret = 0;
+
+       ac97 = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_ac97),
+                           GFP_KERNEL);
+       if (!ac97) {
+               dev_err(&pdev->dev, "Can't allocate tegra20_ac97\n");
+               ret = -ENOMEM;
+               goto err;
+       }
+       dev_set_drvdata(&pdev->dev, ac97);
+
+       ac97->clk_ac97 = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(ac97->clk_ac97)) {
+               dev_err(&pdev->dev, "Can't retrieve ac97 clock\n");
+               ret = PTR_ERR(ac97->clk_ac97);
+               goto err;
+       }
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(&pdev->dev, "No memory resource\n");
+               ret = -ENODEV;
+               goto err_clk_put;
+       }
+
+       memregion = devm_request_mem_region(&pdev->dev, mem->start,
+                                           resource_size(mem), DRV_NAME);
+       if (!memregion) {
+               dev_err(&pdev->dev, "Memory region already claimed\n");
+               ret = -EBUSY;
+               goto err_clk_put;
+       }
+
+       regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+       if (!regs) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               ret = -ENOMEM;
+               goto err_clk_put;
+       }
+
+       ac97->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+                                           &tegra20_ac97_regmap_config);
+       if (IS_ERR(ac97->regmap)) {
+               dev_err(&pdev->dev, "regmap init failed\n");
+               ret = PTR_ERR(ac97->regmap);
+               goto err_clk_put;
+       }
+
+       if (of_property_read_u32_array(pdev->dev.of_node,
+                                      "nvidia,dma-request-selector",
+                                      of_dma, 2) < 0) {
+               dev_err(&pdev->dev, "No DMA resource\n");
+               ret = -ENODEV;
+               goto err_clk_put;
+       }
+
+       ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node,
+                                            "nvidia,codec-reset-gpio", 0);
+       if (gpio_is_valid(ac97->reset_gpio)) {
+               ret = devm_gpio_request_one(&pdev->dev, ac97->reset_gpio,
+                                           GPIOF_OUT_INIT_HIGH, "codec-reset");
+               if (ret) {
+                       dev_err(&pdev->dev, "could not get codec-reset GPIO\n");
+                       goto err_clk_put;
+               }
+       } else {
+               dev_err(&pdev->dev, "no codec-reset GPIO supplied\n");
+               goto err_clk_put;
+       }
+
+       ac97->sync_gpio = of_get_named_gpio(pdev->dev.of_node,
+                                           "nvidia,codec-sync-gpio", 0);
+       if (!gpio_is_valid(ac97->sync_gpio)) {
+               dev_err(&pdev->dev, "no codec-sync GPIO supplied\n");
+               goto err_clk_put;
+       }
+
+       ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1;
+       ac97->capture_dma_data.wrap = 4;
+       ac97->capture_dma_data.width = 32;
+       ac97->capture_dma_data.req_sel = of_dma[1];
+
+       ac97->playback_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_TX1;
+       ac97->playback_dma_data.wrap = 4;
+       ac97->playback_dma_data.width = 32;
+       ac97->playback_dma_data.req_sel = of_dma[1];
+
+       ret = snd_soc_register_dais(&pdev->dev, &tegra20_ac97_dai, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+               ret = -ENOMEM;
+               goto err_clk_put;
+       }
+
+       ret = tegra_pcm_platform_register(&pdev->dev);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+               goto err_unregister_dai;
+       }
+
+       ret = tegra_asoc_utils_init(&ac97->util_data, &pdev->dev);
+       if (ret)
+               goto err_unregister_pcm;
+
+       ret = tegra_asoc_utils_set_ac97_rate(&ac97->util_data);
+       if (ret)
+               goto err_asoc_utils_fini;
+
+       ret = clk_prepare_enable(ac97->clk_ac97);
+       if (ret) {
+               dev_err(&pdev->dev, "clk_enable failed: %d\n", ret);
+               goto err_asoc_utils_fini;
+       }
+
+       /* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */
+       workdata = ac97;
+
+       return 0;
+
+err_asoc_utils_fini:
+       tegra_asoc_utils_fini(&ac97->util_data);
+err_unregister_pcm:
+       tegra_pcm_platform_unregister(&pdev->dev);
+err_unregister_dai:
+       snd_soc_unregister_dai(&pdev->dev);
+err_clk_put:
+       clk_put(ac97->clk_ac97);
+err:
+       return ret;
+}
+
+static int tegra20_ac97_platform_remove(struct platform_device *pdev)
+{
+       struct tegra20_ac97 *ac97 = dev_get_drvdata(&pdev->dev);
+
+       tegra_pcm_platform_unregister(&pdev->dev);
+       snd_soc_unregister_dai(&pdev->dev);
+
+       tegra_asoc_utils_fini(&ac97->util_data);
+
+       clk_disable_unprepare(ac97->clk_ac97);
+       clk_put(ac97->clk_ac97);
+
+       return 0;
+}
+
+static const struct of_device_id tegra20_ac97_of_match[] = {
+       { .compatible = "nvidia,tegra20-ac97", },
+       {},
+};
+
+static struct platform_driver tegra20_ac97_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = tegra20_ac97_of_match,
+       },
+       .probe = tegra20_ac97_platform_probe,
+       .remove = tegra20_ac97_platform_remove,
+};
+module_platform_driver(tegra20_ac97_driver);
+
+MODULE_AUTHOR("Lucas Stach");
+MODULE_DESCRIPTION("Tegra20 AC97 ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra20_ac97_of_match);
diff --git a/sound/soc/tegra/tegra20_ac97.h b/sound/soc/tegra/tegra20_ac97.h
new file mode 100644 (file)
index 0000000..dddc682
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * tegra20_ac97.h - Definitions for the Tegra20 AC97 controller driver
+ *
+ * Copyright (c) 2012 Lucas Stach <dev@lynxeye.de>
+ *
+ * Partly based on code copyright/by:
+ *
+ * Copyright (c) 2011,2012 Toradex Inc.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __TEGRA20_AC97_H__
+#define __TEGRA20_AC97_H__
+
+#include "tegra_pcm.h"
+
+#define TEGRA20_AC97_CTRL                              0x00
+#define TEGRA20_AC97_CMD                               0x04
+#define TEGRA20_AC97_STATUS1                           0x08
+/* ... */
+#define TEGRA20_AC97_FIFO1_SCR                         0x1c
+/* ... */
+#define TEGRA20_AC97_FIFO_TX1                          0x40
+#define TEGRA20_AC97_FIFO_RX1                          0x80
+
+/* TEGRA20_AC97_CTRL */
+#define TEGRA20_AC97_CTRL_STM2_EN                      (1 << 16)
+#define TEGRA20_AC97_CTRL_DOUBLE_SAMPLING_EN           (1 << 11)
+#define TEGRA20_AC97_CTRL_IO_CNTRL_EN                  (1 << 10)
+#define TEGRA20_AC97_CTRL_HSET_DAC_EN                  (1 << 9)
+#define TEGRA20_AC97_CTRL_LINE2_DAC_EN                 (1 << 8)
+#define TEGRA20_AC97_CTRL_PCM_LFE_EN                   (1 << 7)
+#define TEGRA20_AC97_CTRL_PCM_SUR_EN                   (1 << 6)
+#define TEGRA20_AC97_CTRL_PCM_CEN_DAC_EN               (1 << 5)
+#define TEGRA20_AC97_CTRL_LINE1_DAC_EN                 (1 << 4)
+#define TEGRA20_AC97_CTRL_PCM_DAC_EN                   (1 << 3)
+#define TEGRA20_AC97_CTRL_COLD_RESET                   (1 << 2)
+#define TEGRA20_AC97_CTRL_WARM_RESET                   (1 << 1)
+#define TEGRA20_AC97_CTRL_STM_EN                       (1 << 0)
+
+/* TEGRA20_AC97_CMD */
+#define TEGRA20_AC97_CMD_CMD_ADDR_SHIFT                        24
+#define TEGRA20_AC97_CMD_CMD_ADDR_MASK                 (0xff << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT)
+#define TEGRA20_AC97_CMD_CMD_DATA_SHIFT                        8
+#define TEGRA20_AC97_CMD_CMD_DATA_MASK                 (0xffff << TEGRA20_AC97_CMD_CMD_DATA_SHIFT)
+#define TEGRA20_AC97_CMD_CMD_ID_SHIFT                  2
+#define TEGRA20_AC97_CMD_CMD_ID_MASK                   (0x3 << TEGRA20_AC97_CMD_CMD_ID_SHIFT)
+#define TEGRA20_AC97_CMD_BUSY                          (1 << 0)
+
+/* TEGRA20_AC97_STATUS1 */
+#define TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT           24
+#define TEGRA20_AC97_STATUS1_STA_ADDR1_MASK            (0xff << TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT)
+#define TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT           8
+#define TEGRA20_AC97_STATUS1_STA_DATA1_MASK            (0xffff << TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT)
+#define TEGRA20_AC97_STATUS1_STA_VALID1                        (1 << 2)
+#define TEGRA20_AC97_STATUS1_STANDBY1                  (1 << 1)
+#define TEGRA20_AC97_STATUS1_CODEC1_RDY                        (1 << 0)
+
+/* TEGRA20_AC97_FIFO1_SCR */
+#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT         27
+#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_MASK          (0x1f << TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT)
+#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT          22
+#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_MASK           (0x1f << TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT)
+#define TEGRA20_AC97_FIFO_SCR_REC_OVERRUN_INT_STA      (1 << 19)
+#define TEGRA20_AC97_FIFO_SCR_PB_UNDERRUN_INT_STA      (1 << 18)
+#define TEGRA20_AC97_FIFO_SCR_REC_FORCE_MT             (1 << 17)
+#define TEGRA20_AC97_FIFO_SCR_PB_FORCE_MT              (1 << 16)
+#define TEGRA20_AC97_FIFO_SCR_REC_FULL_EN              (1 << 15)
+#define TEGRA20_AC97_FIFO_SCR_REC_3QRT_FULL_EN         (1 << 14)
+#define TEGRA20_AC97_FIFO_SCR_REC_QRT_FULL_EN          (1 << 13)
+#define TEGRA20_AC97_FIFO_SCR_REC_EMPTY_EN             (1 << 12)
+#define TEGRA20_AC97_FIFO_SCR_PB_NOT_FULL_EN           (1 << 11)
+#define TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN             (1 << 10)
+#define TEGRA20_AC97_FIFO_SCR_PB_3QRT_MT_EN            (1 << 9)
+#define TEGRA20_AC97_FIFO_SCR_PB_EMPTY_MT_EN           (1 << 8)
+
+struct tegra20_ac97 {
+       struct clk *clk_ac97;
+       struct tegra_pcm_dma_params capture_dma_data;
+       struct tegra_pcm_dma_params playback_dma_data;
+       struct regmap *regmap;
+       int reset_gpio;
+       int sync_gpio;
+       struct tegra_asoc_utils_data util_data;
+};
+#endif /* __TEGRA20_AC97_H__ */
index 654318483877e0094e071ba73c1a7fe7e7bc0681..e72392927bd2ba3b7ca192e4ff75dde96b2488bd 100644 (file)
@@ -191,6 +191,19 @@ static int tegra20_das_probe(struct platform_device *pdev)
                goto err;
        }
 
+       ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_3,
+                                            TEGRA20_DAS_DAP_SEL_DAC3);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't set up DAS DAP connection\n");
+               goto err;
+       }
+       ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_3,
+                                            TEGRA20_DAS_DAC_SEL_DAP3);
+       if (ret) {
+               dev_err(&pdev->dev, "Can't set up DAS DAC connection\n");
+               goto err;
+       }
+
        platform_set_drvdata(pdev, das);
 
        return 0;
index f354dc390a0be4c1b57ae473f8b357f9c66cb632..dd146f10fef2acc86737640d57caffe2af48de34 100644 (file)
@@ -580,7 +580,7 @@ err_clk_put_apbif:
        clk_put(ahub->clk_apbif);
 err_clk_put_d_audio:
        clk_put(ahub->clk_d_audio);
-       ahub = 0;
+       ahub = NULL;
 err:
        return ret;
 }
@@ -597,7 +597,7 @@ static int tegra30_ahub_remove(struct platform_device *pdev)
        clk_put(ahub->clk_apbif);
        clk_put(ahub->clk_d_audio);
 
-       ahub = 0;
+       ahub = NULL;
 
        return 0;
 }
index 27e91dd0b91c7d9b8b69fcdfb18056cee31d1c96..f4e1ce82750abf1d8ae02f2389b52476ec7bccbd 100644 (file)
@@ -71,7 +71,7 @@ static int tegra30_i2s_runtime_resume(struct device *dev)
        return 0;
 }
 
-int tegra30_i2s_startup(struct snd_pcm_substream *substream,
+static int tegra30_i2s_startup(struct snd_pcm_substream *substream,
                        struct snd_soc_dai *dai)
 {
        struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
@@ -98,7 +98,7 @@ int tegra30_i2s_startup(struct snd_pcm_substream *substream,
        return ret;
 }
 
-void tegra30_i2s_shutdown(struct snd_pcm_substream *substream,
+static void tegra30_i2s_shutdown(struct snd_pcm_substream *substream,
                        struct snd_soc_dai *dai)
 {
        struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
index 6872c77a1196e948de6827c88581120ec19064f3..ba419f86384d4cbd6bc568be7273e968100c7312 100644 (file)
@@ -112,6 +112,59 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
 }
 EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
 
+int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data)
+{
+       const int pll_rate = 73728000;
+       const int ac97_rate = 24576000;
+       int err;
+
+       clk_disable_unprepare(data->clk_cdev1);
+       clk_disable_unprepare(data->clk_pll_a_out0);
+       clk_disable_unprepare(data->clk_pll_a);
+
+       /*
+        * AC97 rate is fixed at 24.576MHz and is used for both the host
+        * controller and the external codec
+        */
+       err = clk_set_rate(data->clk_pll_a, pll_rate);
+       if (err) {
+               dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
+               return err;
+       }
+
+       err = clk_set_rate(data->clk_pll_a_out0, ac97_rate);
+       if (err) {
+               dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
+               return err;
+       }
+
+       /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
+
+       err = clk_prepare_enable(data->clk_pll_a);
+       if (err) {
+               dev_err(data->dev, "Can't enable pll_a: %d\n", err);
+               return err;
+       }
+
+       err = clk_prepare_enable(data->clk_pll_a_out0);
+       if (err) {
+               dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
+               return err;
+       }
+
+       err = clk_prepare_enable(data->clk_cdev1);
+       if (err) {
+               dev_err(data->dev, "Can't enable cdev1: %d\n", err);
+               return err;
+       }
+
+       data->set_baseclock = pll_rate;
+       data->set_mclk = ac97_rate;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
+
 int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
                          struct device *dev)
 {
index 44db1dbb8f210bebca2e0305fd8bc087e0c0820b..974c9f8830f9062c177bd1f3b7da77079f8b8f3f 100644 (file)
@@ -43,6 +43,7 @@ struct tegra_asoc_utils_data {
 
 int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
                              int mclk);
+int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data);
 int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
                          struct device *dev);
 void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data);
diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c
new file mode 100644 (file)
index 0000000..68d4240
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * tegra20_wm9712.c - Tegra machine ASoC driver for boards using WM9712 codec.
+ *
+ * Copyright 2012 Lucas Stach <dev@lynxeye.de>
+ *
+ * Partly based on code copyright/by:
+ * Copyright 2011,2012 Toradex Inc.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "tegra-snd-wm9712"
+
+struct tegra_wm9712 {
+       struct platform_device *codec;
+};
+
+static const struct snd_soc_dapm_widget tegra_wm9712_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_LINE("LineIn", NULL),
+       SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+       snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+
+       return snd_soc_dapm_sync(dapm);
+}
+
+static struct snd_soc_dai_link tegra_wm9712_dai = {
+       .name = "AC97 HiFi",
+       .stream_name = "AC97 HiFi",
+       .cpu_dai_name = "tegra-ac97-pcm",
+       .codec_dai_name = "wm9712-hifi",
+       .codec_name = "wm9712-codec",
+       .init = tegra_wm9712_init,
+};
+
+static struct snd_soc_card snd_soc_tegra_wm9712 = {
+       .name = "tegra-wm9712",
+       .owner = THIS_MODULE,
+       .dai_link = &tegra_wm9712_dai,
+       .num_links = 1,
+
+       .dapm_widgets = tegra_wm9712_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(tegra_wm9712_dapm_widgets),
+       .fully_routed = true,
+};
+
+static int tegra_wm9712_driver_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct snd_soc_card *card = &snd_soc_tegra_wm9712;
+       struct tegra_wm9712 *machine;
+       int ret;
+
+       if (!pdev->dev.of_node) {
+               dev_err(&pdev->dev, "No platform data supplied\n");
+               return -EINVAL;
+       }
+
+       machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm9712),
+                              GFP_KERNEL);
+       if (!machine) {
+               dev_err(&pdev->dev, "Can't allocate tegra_wm9712 struct\n");
+               return -ENOMEM;
+       }
+
+       card->dev = &pdev->dev;
+       platform_set_drvdata(pdev, card);
+       snd_soc_card_set_drvdata(card, machine);
+
+       machine->codec = platform_device_alloc("wm9712-codec", -1);
+       if (!machine->codec) {
+               dev_err(&pdev->dev, "Can't allocate wm9712 platform device\n");
+               return -ENOMEM;
+       }
+
+       ret = platform_device_add(machine->codec);
+       if (ret)
+               goto codec_put;
+
+       ret = snd_soc_of_parse_card_name(card, "nvidia,model");
+       if (ret)
+               goto codec_unregister;
+
+       ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
+       if (ret)
+               goto codec_unregister;
+
+       tegra_wm9712_dai.cpu_of_node = of_parse_phandle(np,
+                                      "nvidia,ac97-controller", 0);
+       if (!tegra_wm9712_dai.cpu_of_node) {
+               dev_err(&pdev->dev,
+                       "Property 'nvidia,ac97-controller' missing or invalid\n");
+               ret = -EINVAL;
+               goto codec_unregister;
+       }
+
+       tegra_wm9712_dai.platform_of_node = tegra_wm9712_dai.cpu_of_node;
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+                       ret);
+               goto codec_unregister;
+       }
+
+       return 0;
+
+codec_unregister:
+       platform_device_del(machine->codec);
+codec_put:
+       platform_device_put(machine->codec);
+       return ret;
+}
+
+static int tegra_wm9712_driver_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct tegra_wm9712 *machine = snd_soc_card_get_drvdata(card);
+
+       snd_soc_unregister_card(card);
+
+       platform_device_unregister(machine->codec);
+
+       return 0;
+}
+
+static const struct of_device_id tegra_wm9712_of_match[] = {
+       { .compatible = "nvidia,tegra-audio-wm9712", },
+       {},
+};
+
+static struct platform_driver tegra_wm9712_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+               .of_match_table = tegra_wm9712_of_match,
+       },
+       .probe = tegra_wm9712_driver_probe,
+       .remove = tegra_wm9712_driver_remove,
+};
+module_platform_driver(tegra_wm9712_driver);
+
+MODULE_AUTHOR("Lucas Stach");
+MODULE_DESCRIPTION("Tegra+WM9712 machine ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_wm9712_of_match);
index ae6990738783ad18c84cd2914119f90c7a2727dc..204b899c2311e6033f525a9f0c79ea9fb29ee3c1 100644 (file)
@@ -24,7 +24,7 @@
 #include "ux500_pcm.h"
 #include "ux500_msp_dai.h"
 
-#include <mop500_ab8500.h>
+#include "mop500_ab8500.h"
 
 /* Define the whole MOP500 soundcard, linking platform to the codec-drivers  */
 struct snd_soc_dai_link mop500_dai_links[] = {
This page took 0.195531 seconds and 5 git commands to generate.