Merge branch 'topic/remove-irqf_disable' into for-linus
authorTakashi Iwai <tiwai@suse.de>
Wed, 26 Oct 2011 21:51:48 +0000 (23:51 +0200)
committerTakashi Iwai <tiwai@suse.de>
Wed, 26 Oct 2011 21:51:48 +0000 (23:51 +0200)
156 files changed:
Documentation/devicetree/bindings/sound/soc/codecs/fsl-sgtl5000.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8510.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8523.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8580.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8711.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8728.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8731.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8737.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8741.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8750.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8753.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8770.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8776.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8804.txt [new file with mode: 0644]
arch/arm/mach-ep93xx/edb93xx.c
arch/arm/mach-ep93xx/simone.c
arch/arm/mach-ep93xx/snappercl15.c
arch/mips/alchemy/devboards/db1200/platform.c
arch/mips/alchemy/devboards/db1x00/platform.c
drivers/base/regmap/regmap.c
drivers/input/misc/twl6040-vibra.c
drivers/mfd/twl6040-core.c
drivers/regulator/core.c
include/linux/mfd/twl6040.h
include/linux/mfd/wm8994/registers.h
include/linux/regmap.h
include/linux/regulator/consumer.h
include/linux/regulator/driver.h
include/sound/adau1373.h [new file with mode: 0644]
include/sound/initval.h
include/sound/saif.h [new file with mode: 0644]
include/sound/soc-dapm.h
include/sound/soc.h
sound/arm/aaci.c
sound/arm/pxa2xx-ac97-lib.c
sound/drivers/ml403-ac97cr.c
sound/drivers/mpu401/mpu401_uart.c
sound/drivers/mtpav.c
sound/drivers/serial-u16550.c
sound/isa/ad1816a/ad1816a_lib.c
sound/isa/es1688/es1688_lib.c
sound/isa/es18xx.c
sound/isa/gus/gus_main.c
sound/isa/gus/gusmax.c
sound/isa/gus/interwave.c
sound/isa/opl3sa2.c
sound/isa/opti9xx/opti92x-ad1848.c
sound/isa/sb/sb_common.c
sound/isa/wavefront/wavefront.c
sound/isa/wss/wss_lib.c
sound/mips/Kconfig
sound/mips/au1x00.c
sound/pci/sis7019.c
sound/ppc/snd_ps3.c
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/atmel/playpaq_wm8510.c
sound/soc/au1x/Kconfig
sound/soc/au1x/Makefile
sound/soc/au1x/ac97c.c [new file with mode: 0644]
sound/soc/au1x/db1000.c [new file with mode: 0644]
sound/soc/au1x/db1200.c
sound/soc/au1x/dbdma2.c
sound/soc/au1x/dma.c [new file with mode: 0644]
sound/soc/au1x/i2sc.c [new file with mode: 0644]
sound/soc/au1x/psc-ac97.c
sound/soc/au1x/psc-i2s.c
sound/soc/au1x/psc.h
sound/soc/blackfin/Kconfig
sound/soc/blackfin/Makefile
sound/soc/blackfin/bfin-eval-adau1373.c [new file with mode: 0644]
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ad193x.c
sound/soc/codecs/ad193x.h
sound/soc/codecs/ad1980.c
sound/soc/codecs/adau1373.c [new file with mode: 0644]
sound/soc/codecs/adau1373.h [new file with mode: 0644]
sound/soc/codecs/adav80x.c
sound/soc/codecs/alc5623.c
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/sn95031.c
sound/soc/codecs/ssm2602.c
sound/soc/codecs/sta32x.c
sound/soc/codecs/tlv320dac33.c
sound/soc/codecs/tpa6130a2.c
sound/soc/codecs/twl6040.c
sound/soc/codecs/wm1250-ev1.c
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8523.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8711.c
sound/soc/codecs/wm8728.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8737.c
sound/soc/codecs/wm8741.c
sound/soc/codecs/wm8750.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8770.c
sound/soc/codecs/wm8776.c
sound/soc/codecs/wm8804.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8993.c
sound/soc/codecs/wm8994-tables.c
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8994.h
sound/soc/codecs/wm8995.c
sound/soc/codecs/wm8996.c
sound/soc/codecs/wm9081.c
sound/soc/codecs/wm9090.c
sound/soc/codecs/wm_hubs.c
sound/soc/codecs/wm_hubs.h
sound/soc/davinci/davinci-mcasp.c
sound/soc/davinci/davinci-pcm.c
sound/soc/ep93xx/edb93xx.c
sound/soc/ep93xx/ep93xx-pcm.c
sound/soc/ep93xx/simone.c
sound/soc/ep93xx/snappercl15.c
sound/soc/fsl/fsl_dma.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/mpc8610_hpcd.c
sound/soc/fsl/p1022_ds.c
sound/soc/imx/imx-pcm-fiq.c
sound/soc/imx/imx-ssi.c
sound/soc/mid-x86/sst_platform.c
sound/soc/mxs/Kconfig [new file with mode: 0644]
sound/soc/mxs/Makefile [new file with mode: 0644]
sound/soc/mxs/mxs-pcm.c [new file with mode: 0644]
sound/soc/mxs/mxs-pcm.h [new file with mode: 0644]
sound/soc/mxs/mxs-saif.c [new file with mode: 0644]
sound/soc/mxs/mxs-saif.h [new file with mode: 0644]
sound/soc/mxs/mxs-sgtl5000.c [new file with mode: 0644]
sound/soc/nuc900/nuc900-pcm.c
sound/soc/pxa/raumfeld.c
sound/soc/pxa/spitz.c
sound/soc/pxa/z2.c
sound/soc/s6000/s6000-pcm.c
sound/soc/samsung/Kconfig
sound/soc/samsung/ac97.c
sound/soc/samsung/jive_wm8750.c
sound/soc/samsung/s3c2412-i2s.c
sound/soc/samsung/s3c24xx-i2s.c
sound/soc/samsung/s3c24xx_uda134x.c
sound/soc/samsung/smartq_wm8987.c
sound/soc/samsung/smdk_wm8580.c
sound/soc/samsung/smdk_wm8580pcm.c
sound/soc/samsung/speyside.c
sound/soc/samsung/speyside_wm8962.c
sound/soc/sh/fsi.c
sound/soc/soc-cache.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-io.c
sound/soc/soc-pcm.c
sound/soc/txx9/txx9aclc-ac97.c
sound/sparc/amd7930.c

diff --git a/Documentation/devicetree/bindings/sound/soc/codecs/fsl-sgtl5000.txt b/Documentation/devicetree/bindings/sound/soc/codecs/fsl-sgtl5000.txt
new file mode 100644 (file)
index 0000000..2c3cd41
--- /dev/null
@@ -0,0 +1,11 @@
+* Freescale SGTL5000 Stereo Codec
+
+Required properties:
+- compatible : "fsl,sgtl5000".
+
+Example:
+
+codec: sgtl5000@0a {
+       compatible = "fsl,sgtl5000";
+       reg = <0x0a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8510.txt b/Documentation/devicetree/bindings/sound/wm8510.txt
new file mode 100644 (file)
index 0000000..fa1a32b
--- /dev/null
@@ -0,0 +1,18 @@
+WM8510 audio CODEC
+
+This device supports both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+  - compatible : "wlf,wm8510"
+
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+Example:
+
+codec: wm8510@1a {
+       compatible = "wlf,wm8510";
+       reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8523.txt b/Documentation/devicetree/bindings/sound/wm8523.txt
new file mode 100644 (file)
index 0000000..0474618
--- /dev/null
@@ -0,0 +1,16 @@
+WM8523 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+  - compatible : "wlf,wm8523"
+
+  - reg : the I2C address of the device.
+
+Example:
+
+codec: wm8523@1a {
+       compatible = "wlf,wm8523";
+       reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8580.txt b/Documentation/devicetree/bindings/sound/wm8580.txt
new file mode 100644 (file)
index 0000000..7d9821f
--- /dev/null
@@ -0,0 +1,16 @@
+WM8580 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+  - compatible : "wlf,wm8580"
+
+  - reg : the I2C address of the device.
+
+Example:
+
+codec: wm8580@1a {
+       compatible = "wlf,wm8580";
+       reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8711.txt b/Documentation/devicetree/bindings/sound/wm8711.txt
new file mode 100644 (file)
index 0000000..8ed9998
--- /dev/null
@@ -0,0 +1,18 @@
+WM8711 audio CODEC
+
+This device supports both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+  - compatible : "wlf,wm8711"
+
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+Example:
+
+codec: wm8711@1a {
+       compatible = "wlf,wm8711";
+       reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8728.txt b/Documentation/devicetree/bindings/sound/wm8728.txt
new file mode 100644 (file)
index 0000000..a8b5c36
--- /dev/null
@@ -0,0 +1,18 @@
+WM8728 audio CODEC
+
+This device supports both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+  - compatible : "wlf,wm8728"
+
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+Example:
+
+codec: wm8728@1a {
+       compatible = "wlf,wm8728";
+       reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8731.txt b/Documentation/devicetree/bindings/sound/wm8731.txt
new file mode 100644 (file)
index 0000000..15f7004
--- /dev/null
@@ -0,0 +1,18 @@
+WM8731 audio CODEC
+
+This device supports both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+  - compatible : "wlf,wm8731"
+
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+Example:
+
+codec: wm8731@1a {
+       compatible = "wlf,wm8731";
+       reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8737.txt b/Documentation/devicetree/bindings/sound/wm8737.txt
new file mode 100644 (file)
index 0000000..4bc2cea
--- /dev/null
@@ -0,0 +1,18 @@
+WM8737 audio CODEC
+
+This device supports both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+  - compatible : "wlf,wm8737"
+
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+Example:
+
+codec: wm8737@1a {
+       compatible = "wlf,wm8737";
+       reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8741.txt b/Documentation/devicetree/bindings/sound/wm8741.txt
new file mode 100644 (file)
index 0000000..74bda58
--- /dev/null
@@ -0,0 +1,18 @@
+WM8741 audio CODEC
+
+This device supports both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+  - compatible : "wlf,wm8741"
+
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+Example:
+
+codec: wm8741@1a {
+       compatible = "wlf,wm8741";
+       reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8750.txt b/Documentation/devicetree/bindings/sound/wm8750.txt
new file mode 100644 (file)
index 0000000..8db239f
--- /dev/null
@@ -0,0 +1,18 @@
+WM8750 and WM8987 audio CODECs
+
+These devices support both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+  - compatible : "wlf,wm8750" or "wlf,wm8987"
+
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+Example:
+
+codec: wm8750@1a {
+       compatible = "wlf,wm8750";
+       reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8753.txt b/Documentation/devicetree/bindings/sound/wm8753.txt
new file mode 100644 (file)
index 0000000..e65277a
--- /dev/null
@@ -0,0 +1,18 @@
+WM8753 audio CODEC
+
+This device supports both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+  - compatible : "wlf,wm8753"
+
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+Example:
+
+codec: wm8737@1a {
+       compatible = "wlf,wm8753";
+       reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8770.txt b/Documentation/devicetree/bindings/sound/wm8770.txt
new file mode 100644 (file)
index 0000000..866e00c
--- /dev/null
@@ -0,0 +1,16 @@
+WM8770 audio CODEC
+
+This device supports SPI.
+
+Required properties:
+
+  - compatible : "wlf,wm8770"
+
+  - reg : the chip select number.
+
+Example:
+
+codec: wm8770@1 {
+       compatible = "wlf,wm8770";
+       reg = <1>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8776.txt b/Documentation/devicetree/bindings/sound/wm8776.txt
new file mode 100644 (file)
index 0000000..3b9ca49
--- /dev/null
@@ -0,0 +1,18 @@
+WM8776 audio CODEC
+
+This device supports both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+  - compatible : "wlf,wm8776"
+
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+Example:
+
+codec: wm8776@1a {
+       compatible = "wlf,wm8776";
+       reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/wm8804.txt b/Documentation/devicetree/bindings/sound/wm8804.txt
new file mode 100644 (file)
index 0000000..4d3a56f
--- /dev/null
@@ -0,0 +1,18 @@
+WM8804 audio CODEC
+
+This device supports both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+
+  - compatible : "wlf,wm8804"
+
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+Example:
+
+codec: wm8804@1a {
+       compatible = "wlf,wm8804";
+       reg = <0x1a>;
+};
index 9969bb1..8dc51e4 100644 (file)
@@ -159,6 +159,11 @@ static void __init edb93xx_register_spi(void)
 /*************************************************************************
  * EDB93xx I2S
  *************************************************************************/
+static struct platform_device edb93xx_audio_device = {
+       .name           = "edb93xx-audio",
+       .id             = -1,
+};
+
 static int __init edb93xx_has_audio(void)
 {
        return (machine_is_edb9301() || machine_is_edb9302() ||
@@ -170,6 +175,7 @@ static void __init edb93xx_register_i2s(void)
 {
        if (edb93xx_has_audio()) {
                ep93xx_register_i2s();
+               platform_device_register(&edb93xx_audio_device);
        }
 }
 
index 8392e95..1445ce5 100644 (file)
@@ -53,6 +53,17 @@ static struct i2c_board_info __initdata simone_i2c_board_info[] = {
        },
 };
 
+static struct platform_device simone_audio_device = {
+       .name           = "simone-audio",
+       .id             = -1,
+};
+
+static void __init simone_register_audio(void)
+{
+       ep93xx_register_ac97();
+       platform_device_register(&simone_audio_device);
+}
+
 static void __init simone_init_machine(void)
 {
        ep93xx_init_devices();
@@ -61,7 +72,7 @@ static void __init simone_init_machine(void)
        ep93xx_register_fb(&simone_fb_info);
        ep93xx_register_i2c(&simone_i2c_gpio_data, simone_i2c_board_info,
                            ARRAY_SIZE(simone_i2c_board_info));
-       ep93xx_register_ac97();
+       simone_register_audio();
 }
 
 MACHINE_START(SIM_ONE, "Simplemachines Sim.One Board")
index 2e9c614..edd75e3 100644 (file)
@@ -150,6 +150,17 @@ static struct ep93xxfb_mach_info __initdata snappercl15_fb_info = {
        .bpp                    = 16,
 };
 
+static struct platform_device snappercl15_audio_device = {
+       .name           = "snappercl15-audio",
+       .id             = -1,
+};
+
+static void __init snappercl15_register_audio(void)
+{
+       ep93xx_register_i2s();
+       platform_device_register(&snappercl15_audio_device);
+}
+
 static void __init snappercl15_init_machine(void)
 {
        ep93xx_init_devices();
@@ -157,7 +168,7 @@ static void __init snappercl15_init_machine(void)
        ep93xx_register_i2c(&snappercl15_i2c_gpio_data, snappercl15_i2c_data,
                            ARRAY_SIZE(snappercl15_i2c_data));
        ep93xx_register_fb(&snappercl15_fb_info);
-       ep93xx_register_i2s();
+       snappercl15_register_audio();
        platform_device_register(&snappercl15_nand_device);
 }
 
index fbb5593..dda090b 100644 (file)
@@ -422,6 +422,7 @@ static struct resource au1200_psc1_res[] = {
        },
 };
 
+/* AC97 or I2S device */
 static struct platform_device db1200_audio_dev = {
        /* name assigned later based on switch setting */
        .id             = 1,    /* PSC ID */
@@ -429,19 +430,32 @@ static struct platform_device db1200_audio_dev = {
        .resource       = au1200_psc1_res,
 };
 
+/* DB1200 ASoC card device */
+static struct platform_device db1200_sound_dev = {
+       /* name assigned later based on switch setting */
+       .id             = 1,    /* PSC ID */
+};
+
 static struct platform_device db1200_stac_dev = {
        .name           = "ac97-codec",
        .id             = 1,    /* on PSC1 */
 };
 
+static struct platform_device db1200_audiodma_dev = {
+       .name           = "au1xpsc-pcm",
+       .id             = 1,    /* PSC ID */
+};
+
 static struct platform_device *db1200_devs[] __initdata = {
        NULL,           /* PSC0, selected by S6.8 */
        &db1200_ide_dev,
        &db1200_eth_dev,
        &db1200_rtc_dev,
        &db1200_nand_dev,
+       &db1200_audiodma_dev,
        &db1200_audio_dev,
        &db1200_stac_dev,
+       &db1200_sound_dev,
 };
 
 static int __init db1200_dev_init(void)
@@ -501,10 +515,12 @@ static int __init db1200_dev_init(void)
        if (sw == BCSR_SWITCHES_DIP_8) {
                bcsr_mod(BCSR_RESETS, 0, BCSR_RESETS_PSC1MUX);
                db1200_audio_dev.name = "au1xpsc_i2s";
+               db1200_sound_dev.name = "db1200-i2s";
                printk(KERN_INFO " S6.7 ON : PSC1 mode I2S\n");
        } else {
                bcsr_mod(BCSR_RESETS, BCSR_RESETS_PSC1MUX, 0);
                db1200_audio_dev.name = "au1xpsc_ac97";
+               db1200_sound_dev.name = "db1200-ac97";
                printk(KERN_INFO " S6.7 OFF: PSC1 mode AC97\n");
        }
 
index 978d5ab..7057d28 100644 (file)
  */
 
 #include <linux/init.h>
+#include <linux/interrupt.h>
 #include <linux/platform_device.h>
 
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1000_dma.h>
 #include <asm/mach-au1x00/au1xxx.h>
 #include <asm/mach-db1x00/bcsr.h>
 #include "../platform.h"
 #endif
 #endif
 
+static struct resource alchemy_ac97c_res[] = {
+       [0] = {
+               .start  = AU1000_AC97_PHYS_ADDR,
+               .end    = AU1000_AC97_PHYS_ADDR + 0xfff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = DMA_ID_AC97C_TX,
+               .end    = DMA_ID_AC97C_TX,
+               .flags  = IORESOURCE_DMA,
+       },
+       [2] = {
+               .start  = DMA_ID_AC97C_RX,
+               .end    = DMA_ID_AC97C_RX,
+               .flags  = IORESOURCE_DMA,
+       },
+};
+
+static struct platform_device alchemy_ac97c_dev = {
+       .name           = "alchemy-ac97c",
+       .id             = -1,
+       .resource       = alchemy_ac97c_res,
+       .num_resources  = ARRAY_SIZE(alchemy_ac97c_res),
+};
+
+static struct platform_device alchemy_ac97c_dma_dev = {
+       .name           = "alchemy-pcm-dma",
+       .id             = 0,
+};
+
+static struct platform_device db1x00_codec_dev = {
+       .name           = "ac97-codec",
+       .id             = -1,
+};
+
+static struct platform_device db1x00_audio_dev = {
+       .name           = "db1000-audio",
+};
+
 static int __init db1xxx_dev_init(void)
 {
 #ifdef DB1XXX_HAS_PCMCIA
@@ -113,6 +155,12 @@ static int __init db1xxx_dev_init(void)
                                    1);
 #endif
        db1x_register_norflash(BOARD_FLASH_SIZE, BOARD_FLASH_WIDTH, F_SWAPPED);
+
+       platform_device_register(&db1x00_codec_dev);
+       platform_device_register(&alchemy_ac97c_dma_dev);
+       platform_device_register(&alchemy_ac97c_dev);
+       platform_device_register(&db1x00_audio_dev);
+
        return 0;
 }
 device_initcall(db1xxx_dev_init);
index cf3565c..2fa55c5 100644 (file)
@@ -37,6 +37,11 @@ struct regmap {
        void *work_buf;     /* Scratch buffer used to format I/O */
        struct regmap_format format;  /* Buffer format */
        const struct regmap_bus *bus;
+
+       unsigned int max_register;
+       bool (*writeable_reg)(struct device *dev, unsigned int reg);
+       bool (*readable_reg)(struct device *dev, unsigned int reg);
+       bool (*volatile_reg)(struct device *dev, unsigned int reg);
 };
 
 static void regmap_format_4_12_write(struct regmap *map,
@@ -116,6 +121,10 @@ struct regmap *regmap_init(struct device *dev,
        map->format.val_bytes = config->val_bits / 8;
        map->dev = dev;
        map->bus = bus;
+       map->max_register = config->max_register;
+       map->writeable_reg = config->writeable_reg;
+       map->readable_reg = config->readable_reg;
+       map->volatile_reg = config->volatile_reg;
 
        switch (config->reg_bits) {
        case 4:
index c43002e..154b7a3 100644 (file)
@@ -97,7 +97,7 @@ static void twl6040_vibra_enable(struct vibra_info *info)
        }
 
        twl6040_power(info->twl6040, 1);
-       if (twl6040->rev <= TWL6040_REV_ES1_1) {
+       if (twl6040_get_revid(twl6040) <= TWL6040_REV_ES1_1) {
                /*
                 * ERRATA: Disable overcurrent protection for at least
                 * 3ms when enabling vibrator drivers to avoid false
index 24d436c..7dc8c47 100644 (file)
@@ -34,8 +34,6 @@
 #include <linux/mfd/core.h>
 #include <linux/mfd/twl6040.h>
 
-static struct platform_device *twl6040_dev;
-
 int twl6040_reg_read(struct twl6040 *twl6040, unsigned int reg)
 {
        int ret;
@@ -203,11 +201,11 @@ static irqreturn_t twl6040_naudint_handler(int irq, void *data)
        if (intid & TWL6040_THINT) {
                status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
                if (status & TWL6040_TSHUTDET) {
-                       dev_warn(&twl6040_dev->dev,
+                       dev_warn(twl6040->dev,
                                 "Thermal shutdown, powering-off");
                        twl6040_power(twl6040, 0);
                } else {
-                       dev_warn(&twl6040_dev->dev,
+                       dev_warn(twl6040->dev,
                                 "Leaving thermal shutdown, powering-on");
                        twl6040_power(twl6040, 1);
                }
@@ -227,7 +225,7 @@ static int twl6040_power_up_completion(struct twl6040 *twl6040,
        if (!time_left) {
                intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
                if (!(intid & TWL6040_READYINT)) {
-                       dev_err(&twl6040_dev->dev,
+                       dev_err(twl6040->dev,
                                "timeout waiting for READYINT\n");
                        return -ETIMEDOUT;
                }
@@ -255,7 +253,7 @@ int twl6040_power(struct twl6040 *twl6040, int on)
                        /* wait for power-up completion */
                        ret = twl6040_power_up_completion(twl6040, naudint);
                        if (ret) {
-                               dev_err(&twl6040_dev->dev,
+                               dev_err(twl6040->dev,
                                        "automatic power-down failed\n");
                                twl6040->power_count = 0;
                                goto out;
@@ -264,7 +262,7 @@ int twl6040_power(struct twl6040 *twl6040, int on)
                        /* use manual power-up sequence */
                        ret = twl6040_power_up(twl6040);
                        if (ret) {
-                               dev_err(&twl6040_dev->dev,
+                               dev_err(twl6040->dev,
                                        "manual power-up failed\n");
                                twl6040->power_count = 0;
                                goto out;
@@ -276,7 +274,7 @@ int twl6040_power(struct twl6040 *twl6040, int on)
        } else {
                /* already powered-down */
                if (!twl6040->power_count) {
-                       dev_err(&twl6040_dev->dev,
+                       dev_err(twl6040->dev,
                                "device is already powered-off\n");
                        ret = -EPERM;
                        goto out;
@@ -326,7 +324,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
                        lppllctl &= ~TWL6040_LPLLFIN;
                        break;
                default:
-                       dev_err(&twl6040_dev->dev,
+                       dev_err(twl6040->dev,
                                "freq_out %d not supported\n", freq_out);
                        ret = -EINVAL;
                        goto pll_out;
@@ -347,7 +345,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
                                          hppllctl);
                        break;
                default:
-                       dev_err(&twl6040_dev->dev,
+                       dev_err(twl6040->dev,
                                "freq_in %d not supported\n", freq_in);
                        ret = -EINVAL;
                        goto pll_out;
@@ -356,7 +354,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
        case TWL6040_SYSCLK_SEL_HPPLL:
                /* high-performance PLL can provide only 19.2 MHz */
                if (freq_out != 19200000) {
-                       dev_err(&twl6040_dev->dev,
+                       dev_err(twl6040->dev,
                                "freq_out %d not supported\n", freq_out);
                        ret = -EINVAL;
                        goto pll_out;
@@ -389,7 +387,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
                                    TWL6040_HPLLENA;
                        break;
                default:
-                       dev_err(&twl6040_dev->dev,
+                       dev_err(twl6040->dev,
                                "freq_in %d not supported\n", freq_in);
                        ret = -EINVAL;
                        goto pll_out;
@@ -406,7 +404,7 @@ int twl6040_set_pll(struct twl6040 *twl6040, int pll_id,
                twl6040_reg_write(twl6040, TWL6040_REG_LPPLLCTL, lppllctl);
                break;
        default:
-               dev_err(&twl6040_dev->dev, "unknown pll id %d\n", pll_id);
+               dev_err(twl6040->dev, "unknown pll id %d\n", pll_id);
                ret = -EINVAL;
                goto pll_out;
        }
@@ -471,9 +469,7 @@ static int __devinit twl6040_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, twl6040);
 
-       twl6040_dev = pdev;
        twl6040->dev = &pdev->dev;
-       twl6040->audpwron = pdata->audpwron_gpio;
        twl6040->irq = pdata->naudint_irq;
        twl6040->irq_base = pdata->irq_base;
 
@@ -483,6 +479,12 @@ static int __devinit twl6040_probe(struct platform_device *pdev)
 
        twl6040->rev = twl6040_reg_read(twl6040, TWL6040_REG_ASICREV);
 
+       /* ERRATA: Automatic power-up is not possible in ES1.0 */
+       if (twl6040_get_revid(twl6040) > TWL6040_REV_ES1_0)
+               twl6040->audpwron = pdata->audpwron_gpio;
+       else
+               twl6040->audpwron = -EINVAL;
+
        if (gpio_is_valid(twl6040->audpwron)) {
                ret = gpio_request(twl6040->audpwron, "audpwron");
                if (ret)
@@ -493,10 +495,6 @@ static int __devinit twl6040_probe(struct platform_device *pdev)
                        goto gpio2_err;
        }
 
-       /* ERRATA: Automatic power-up is not possible in ES1.0 */
-       if (twl6040->rev == TWL6040_REV_ES1_0)
-               twl6040->audpwron = -EINVAL;
-
        /* codec interrupt */
        ret = twl6040_irq_init(twl6040);
        if (ret)
@@ -566,7 +564,6 @@ gpio2_err:
 gpio1_err:
        platform_set_drvdata(pdev, NULL);
        kfree(twl6040);
-       twl6040_dev = NULL;
        return ret;
 }
 
@@ -586,7 +583,6 @@ static int __devexit twl6040_remove(struct platform_device *pdev)
        mfd_remove_devices(&pdev->dev);
        platform_set_drvdata(pdev, NULL);
        kfree(twl6040);
-       twl6040_dev = NULL;
 
        return 0;
 }
index d8e6a42..d0bde70 100644 (file)
@@ -1552,6 +1552,63 @@ int regulator_force_disable(struct regulator *regulator)
 }
 EXPORT_SYMBOL_GPL(regulator_force_disable);
 
+static void regulator_disable_work(struct work_struct *work)
+{
+       struct regulator_dev *rdev = container_of(work, struct regulator_dev,
+                                                 disable_work.work);
+       int count, i, ret;
+
+       mutex_lock(&rdev->mutex);
+
+       BUG_ON(!rdev->deferred_disables);
+
+       count = rdev->deferred_disables;
+       rdev->deferred_disables = 0;
+
+       for (i = 0; i < count; i++) {
+               ret = _regulator_disable(rdev);
+               if (ret != 0)
+                       rdev_err(rdev, "Deferred disable failed: %d\n", ret);
+       }
+
+       mutex_unlock(&rdev->mutex);
+
+       if (rdev->supply) {
+               for (i = 0; i < count; i++) {
+                       ret = regulator_disable(rdev->supply);
+                       if (ret != 0) {
+                               rdev_err(rdev,
+                                        "Supply disable failed: %d\n", ret);
+                       }
+               }
+       }
+}
+
+/**
+ * regulator_disable_deferred - disable regulator output with delay
+ * @regulator: regulator source
+ * @ms: miliseconds until the regulator is disabled
+ *
+ * Execute regulator_disable() on the regulator after a delay.  This
+ * is intended for use with devices that require some time to quiesce.
+ *
+ * NOTE: this will only disable the regulator output if no other consumer
+ * devices have it enabled, the regulator device supports disabling and
+ * machine constraints permit this operation.
+ */
+int regulator_disable_deferred(struct regulator *regulator, int ms)
+{
+       struct regulator_dev *rdev = regulator->rdev;
+
+       mutex_lock(&rdev->mutex);
+       rdev->deferred_disables++;
+       mutex_unlock(&rdev->mutex);
+
+       return schedule_delayed_work(&rdev->disable_work,
+                                    msecs_to_jiffies(ms));
+}
+EXPORT_SYMBOL_GPL(regulator_disable_deferred);
+
 static int _regulator_is_enabled(struct regulator_dev *rdev)
 {
        /* If we don't know then assume that the regulator is always on */
@@ -2622,6 +2679,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
        INIT_LIST_HEAD(&rdev->consumer_list);
        INIT_LIST_HEAD(&rdev->list);
        BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
+       INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work);
 
        /* preform any regulator specific init */
        if (init_data->regulator_init) {
@@ -2729,6 +2787,7 @@ void regulator_unregister(struct regulator_dev *rdev)
 #ifdef CONFIG_DEBUG_FS
        debugfs_remove_recursive(rdev->debugfs);
 #endif
+       flush_work_sync(&rdev->disable_work.work);
        WARN_ON(rdev->open_count);
        unset_regulator_supplies(rdev);
        list_del(&rdev->list);
index 4c806f6..ec1ec79 100644 (file)
@@ -70,9 +70,6 @@
 
 #define TWL6040_CACHEREGNUM            (TWL6040_REG_STATUS + 1)
 
-#define TWL6040_VIOREGNUM              18
-#define TWL6040_VDDREGNUM              21
-
 /* INTID (0x03) fields */
 
 #define TWL6040_THINT                  0x01
@@ -225,4 +222,9 @@ unsigned int twl6040_get_sysclk(struct twl6040 *twl6040);
 int twl6040_irq_init(struct twl6040 *twl6040);
 void twl6040_irq_exit(struct twl6040 *twl6040);
 
+static inline int twl6040_get_revid(struct twl6040 *twl6040)
+{
+       return twl6040->rev;
+}
+
 #endif  /* End of __TWL6040_CODEC_H__ */
index f3ee842..83ecdcd 100644 (file)
@@ -72,6 +72,7 @@
 #define WM8994_DC_SERVO_2                       0x55
 #define WM8994_DC_SERVO_4                       0x57
 #define WM8994_DC_SERVO_READBACK                0x58
+#define WM8994_DC_SERVO_4E                     0x59
 #define WM8994_ANALOGUE_HP_1                    0x60
 #define WM8958_MIC_DETECT_1                     0xD0
 #define WM8958_MIC_DETECT_2                     0xD1
 #define WM8994_AIF1_DAC1_FILTERS_2              0x421
 #define WM8994_AIF1_DAC2_FILTERS_1              0x422
 #define WM8994_AIF1_DAC2_FILTERS_2              0x423
+#define WM8958_AIF1_DAC1_NOISE_GATE             0x430
+#define WM8958_AIF1_DAC2_NOISE_GATE             0x431
 #define WM8994_AIF1_DRC1_1                      0x440
 #define WM8994_AIF1_DRC1_2                      0x441
 #define WM8994_AIF1_DRC1_3                      0x442
 #define WM8994_AIF2_ADC_FILTERS                 0x510
 #define WM8994_AIF2_DAC_FILTERS_1               0x520
 #define WM8994_AIF2_DAC_FILTERS_2               0x521
+#define WM8958_AIF2_DAC_NOISE_GATE              0x530
 #define WM8994_AIF2_DRC_1                       0x540
 #define WM8994_AIF2_DRC_2                       0x541
 #define WM8994_AIF2_DRC_3                       0x542
 #define WM8994_LDO2_DISCH_WIDTH                      1  /* LDO2_DISCH */
 
 /*
+ * R61 (0x3D) - MICBIAS1
+ */
+#define WM8958_MICB1_RATE                       0x0020  /* MICB1_RATE */
+#define WM8958_MICB1_RATE_MASK                  0x0020  /* MICB1_RATE */
+#define WM8958_MICB1_RATE_SHIFT                      5  /* MICB1_RATE */
+#define WM8958_MICB1_RATE_WIDTH                      1  /* MICB1_RATE */
+#define WM8958_MICB1_MODE                       0x0010  /* MICB1_MODE */
+#define WM8958_MICB1_MODE_MASK                  0x0010  /* MICB1_MODE */
+#define WM8958_MICB1_MODE_SHIFT                      4  /* MICB1_MODE */
+#define WM8958_MICB1_MODE_WIDTH                      1  /* MICB1_MODE */
+#define WM8958_MICB1_LVL_MASK                   0x000E  /* MICB1_LVL - [3:1] */
+#define WM8958_MICB1_LVL_SHIFT                       1  /* MICB1_LVL - [3:1] */
+#define WM8958_MICB1_LVL_WIDTH                       3  /* MICB1_LVL - [3:1] */
+#define WM8958_MICB1_DISCH                      0x0001  /* MICB1_DISCH */
+#define WM8958_MICB1_DISCH_MASK                 0x0001  /* MICB1_DISCH */
+#define WM8958_MICB1_DISCH_SHIFT                     0  /* MICB1_DISCH */
+#define WM8958_MICB1_DISCH_WIDTH                     1  /* MICB1_DISCH */
+
+/*
+ * R62 (0x3E) - MICBIAS2
+ */
+#define WM8958_MICB2_RATE                       0x0020  /* MICB2_RATE */
+#define WM8958_MICB2_RATE_MASK                  0x0020  /* MICB2_RATE */
+#define WM8958_MICB2_RATE_SHIFT                      5  /* MICB2_RATE */
+#define WM8958_MICB2_RATE_WIDTH                      1  /* MICB2_RATE */
+#define WM8958_MICB2_MODE                       0x0010  /* MICB2_MODE */
+#define WM8958_MICB2_MODE_MASK                  0x0010  /* MICB2_MODE */
+#define WM8958_MICB2_MODE_SHIFT                      4  /* MICB2_MODE */
+#define WM8958_MICB2_MODE_WIDTH                      1  /* MICB2_MODE */
+#define WM8958_MICB2_LVL_MASK                   0x000E  /* MICB2_LVL - [3:1] */
+#define WM8958_MICB2_LVL_SHIFT                       1  /* MICB2_LVL - [3:1] */
+#define WM8958_MICB2_LVL_WIDTH                       3  /* MICB2_LVL - [3:1] */
+#define WM8958_MICB2_DISCH                      0x0001  /* MICB2_DISCH */
+#define WM8958_MICB2_DISCH_MASK                 0x0001  /* MICB2_DISCH */
+#define WM8958_MICB2_DISCH_SHIFT                     0  /* MICB2_DISCH */
+#define WM8958_MICB2_DISCH_WIDTH                     1  /* MICB2_DISCH */
+
+/*
  * R76 (0x4C) - Charge Pump (1)
  */
 #define WM8994_CP_ENA                           0x8000  /* CP_ENA */
 #define WM8994_AIF1DAC2_3D_ENA_WIDTH                 1  /* AIF1DAC2_3D_ENA */
 
 /*
+ * R1072 (0x430) - AIF1 DAC1 Noise Gate
+ */
+#define WM8958_AIF1DAC1_NG_HLD_MASK             0x0060  /* AIF1DAC1_NG_HLD - [6:5] */
+#define WM8958_AIF1DAC1_NG_HLD_SHIFT                 5  /* AIF1DAC1_NG_HLD - [6:5] */
+#define WM8958_AIF1DAC1_NG_HLD_WIDTH                 2  /* AIF1DAC1_NG_HLD - [6:5] */
+#define WM8958_AIF1DAC1_NG_THR_MASK             0x000E  /* AIF1DAC1_NG_THR - [3:1] */
+#define WM8958_AIF1DAC1_NG_THR_SHIFT                 1  /* AIF1DAC1_NG_THR - [3:1] */
+#define WM8958_AIF1DAC1_NG_THR_WIDTH                 3  /* AIF1DAC1_NG_THR - [3:1] */
+#define WM8958_AIF1DAC1_NG_ENA                  0x0001  /* AIF1DAC1_NG_ENA */
+#define WM8958_AIF1DAC1_NG_ENA_MASK             0x0001  /* AIF1DAC1_NG_ENA */
+#define WM8958_AIF1DAC1_NG_ENA_SHIFT                 0  /* AIF1DAC1_NG_ENA */
+#define WM8958_AIF1DAC1_NG_ENA_WIDTH                 1  /* AIF1DAC1_NG_ENA */
+
+/*
+ * R1073 (0x431) - AIF1 DAC2 Noise Gate
+ */
+#define WM8958_AIF1DAC2_NG_HLD_MASK             0x0060  /* AIF1DAC2_NG_HLD - [6:5] */
+#define WM8958_AIF1DAC2_NG_HLD_SHIFT                 5  /* AIF1DAC2_NG_HLD - [6:5] */
+#define WM8958_AIF1DAC2_NG_HLD_WIDTH                 2  /* AIF1DAC2_NG_HLD - [6:5] */
+#define WM8958_AIF1DAC2_NG_THR_MASK             0x000E  /* AIF1DAC2_NG_THR - [3:1] */
+#define WM8958_AIF1DAC2_NG_THR_SHIFT                 1  /* AIF1DAC2_NG_THR - [3:1] */
+#define WM8958_AIF1DAC2_NG_THR_WIDTH                 3  /* AIF1DAC2_NG_THR - [3:1] */
+#define WM8958_AIF1DAC2_NG_ENA                  0x0001  /* AIF1DAC2_NG_ENA */
+#define WM8958_AIF1DAC2_NG_ENA_MASK             0x0001  /* AIF1DAC2_NG_ENA */
+#define WM8958_AIF1DAC2_NG_ENA_SHIFT                 0  /* AIF1DAC2_NG_ENA */
+#define WM8958_AIF1DAC2_NG_ENA_WIDTH                 1  /* AIF1DAC2_NG_ENA */
+
+/*
  * R1088 (0x440) - AIF1 DRC1 (1)
  */
 #define WM8994_AIF1DRC1_SIG_DET_RMS_MASK        0xF800  /* AIF1DRC1_SIG_DET_RMS - [15:11] */
 #define WM8994_AIF2DAC_3D_ENA_WIDTH                  1  /* AIF2DAC_3D_ENA */
 
 /*
+ * R1328 (0x530) - AIF2 DAC Noise Gate
+ */
+#define WM8958_AIF2DAC_NG_HLD_MASK              0x0060  /* AIF2DAC_NG_HLD - [6:5] */
+#define WM8958_AIF2DAC_NG_HLD_SHIFT                  5  /* AIF2DAC_NG_HLD - [6:5] */
+#define WM8958_AIF2DAC_NG_HLD_WIDTH                  2  /* AIF2DAC_NG_HLD - [6:5] */
+#define WM8958_AIF2DAC_NG_THR_MASK              0x000E  /* AIF2DAC_NG_THR - [3:1] */
+#define WM8958_AIF2DAC_NG_THR_SHIFT                  1  /* AIF2DAC_NG_THR - [3:1] */
+#define WM8958_AIF2DAC_NG_THR_WIDTH                  3  /* AIF2DAC_NG_THR - [3:1] */
+#define WM8958_AIF2DAC_NG_ENA                   0x0001  /* AIF2DAC_NG_ENA */
+#define WM8958_AIF2DAC_NG_ENA_MASK              0x0001  /* AIF2DAC_NG_ENA */
+#define WM8958_AIF2DAC_NG_ENA_SHIFT                  0  /* AIF2DAC_NG_ENA */
+#define WM8958_AIF2DAC_NG_ENA_WIDTH                  1  /* AIF2DAC_NG_ENA */
+
+/*
  * R1344 (0x540) - AIF2 DRC (1)
  */
 #define WM8994_AIF2DRC_SIG_DET_RMS_MASK         0xF800  /* AIF2DRC_SIG_DET_RMS - [15:11] */
index 60a65cd..003c053 100644 (file)
 struct i2c_client;
 struct spi_device;
 
+/**
+ * Default value for a register.  We use an array of structs rather
+ * than a simple array as many modern devices have very sparse
+ * register maps.
+ *
+ * @reg: Register address.
+ * @def: Register default value.
+ */
+struct reg_default {
+       unsigned int reg;
+       unsigned int def;
+};
+
+/**
+ * Configuration for the register map of a device.
+ *
+ * @reg_bits: Number of bits in a register address, mandatory.
+ * @val_bits: Number of bits in a register value, mandatory.
+ *
+ * @writeable_reg: Optional callback returning true if the register
+ *                 can be written to.
+ * @readable_reg: Optional callback returning true if the register
+ *                can be read from.
+ * @volatile_reg: Optional callback returning true if the register
+ *                value can't be cached.
+ * @precious_reg: Optional callback returning true if the rgister
+ *                should not be read outside of a call from the driver
+ *                (eg, a clear on read interrupt status register).
+ *
+ * @max_register: Optional, specifies the maximum valid register index.
+ * @reg_defaults: Power on reset values for registers (for use with
+ *                register cache support).
+ * @num_reg_defaults: Number of elements in reg_defaults.
+ *
+ * @read_flag_mask: Mask to be set in the top byte of the register when doing
+ *                  a read.
+ * @write_flag_mask: Mask to be set in the top byte of the register when doing
+ *                   a write. If both read_flag_mask and write_flag_mask are
+ *                   empty the regmap_bus default masks are used.
+ */
 struct regmap_config {
        int reg_bits;
        int val_bits;
+
+       bool (*writeable_reg)(struct device *dev, unsigned int reg);
+       bool (*readable_reg)(struct device *dev, unsigned int reg);
+       bool (*volatile_reg)(struct device *dev, unsigned int reg);
+       bool (*precious_reg)(struct device *dev, unsigned int reg);
+
+       unsigned int max_register;
+       struct reg_default *reg_defaults;
+       int num_reg_defaults;
+
+       u8 read_flag_mask;
+       u8 write_flag_mask;
 };
 
 typedef int (*regmap_hw_write)(struct device *dev, const void *data,
index 26f6ea4..6fae97a 100644 (file)
@@ -141,6 +141,7 @@ int regulator_enable(struct regulator *regulator);
 int regulator_disable(struct regulator *regulator);
 int regulator_force_disable(struct regulator *regulator);
 int regulator_is_enabled(struct regulator *regulator);
+int regulator_disable_deferred(struct regulator *regulator, int ms);
 
 int regulator_bulk_get(struct device *dev, int num_consumers,
                       struct regulator_bulk_data *consumers);
@@ -211,6 +212,12 @@ static inline int regulator_disable(struct regulator *regulator)
        return 0;
 }
 
+static inline int regulator_disable_deferred(struct regulator *regulator,
+                                            int ms)
+{
+       return 0;
+}
+
 static inline int regulator_is_enabled(struct regulator *regulator)
 {
        return 1;
index 1a80bc7..12a1aa0 100644 (file)
@@ -199,6 +199,9 @@ struct regulator_dev {
        struct regulation_constraints *constraints;
        struct regulator *supply;       /* for tree */
 
+       struct delayed_work disable_work;
+       int deferred_disables;
+
        void *reg_data;         /* regulator_dev data */
 
 #ifdef CONFIG_DEBUG_FS
diff --git a/include/sound/adau1373.h b/include/sound/adau1373.h
new file mode 100644 (file)
index 0000000..1b19c76
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Analog Devices ADAU1373 Audio Codec drive
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef __SOUND_ADAU1373_H__
+#define __SOUND_ADAU1373_H__
+
+enum adau1373_micbias_voltage {
+       ADAU1373_MICBIAS_2_9V = 0,
+       ADAU1373_MICBIAS_2_2V = 1,
+       ADAU1373_MICBIAS_2_6V = 2,
+       ADAU1373_MICBIAS_1_8V = 3,
+};
+
+#define ADAU1373_DRC_SIZE 13
+
+struct adau1373_platform_data {
+       bool input_differential[4];
+       bool lineout_differential;
+       bool lineout_ground_sense;
+
+       unsigned int num_drc;
+       uint8_t drc_setting[3][ADAU1373_DRC_SIZE];
+
+       enum adau1373_micbias_voltage micbias1;
+       enum adau1373_micbias_voltage micbias2;
+};
+
+#endif
index 1daa6df..f99a0d2 100644 (file)
@@ -62,7 +62,7 @@ static int snd_legacy_find_free_irq(int *irq_table)
 {
        while (*irq_table != -1) {
                if (!request_irq(*irq_table, snd_legacy_empty_irq_handler,
-                                IRQF_DISABLED | IRQF_PROBE_SHARED, "ALSA Test IRQ",
+                                IRQF_PROBE_SHARED, "ALSA Test IRQ",
                                 (void *) irq_table)) {
                        free_irq(*irq_table, (void *) irq_table);
                        return *irq_table;
diff --git a/include/sound/saif.h b/include/sound/saif.h
new file mode 100644 (file)
index 0000000..d0e0de7
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * 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 {
+       int (*init) (void);
+       int (*get_master_id) (unsigned int saif_id);
+};
+#endif
index e0583b7..350b1b3 100644 (file)
@@ -524,6 +524,8 @@ struct snd_soc_dapm_context {
        enum snd_soc_bias_level target_bias_level;
        struct list_head list;
 
+       int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
+
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_dapm;
 #endif
index aa19f5a..24e17be 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/regmap.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/control.h>
@@ -260,6 +261,7 @@ extern struct snd_ac97_bus_ops soc_ac97_ops;
 enum snd_soc_control_type {
        SND_SOC_I2C = 1,
        SND_SOC_SPI,
+       SND_SOC_REGMAP,
 };
 
 enum snd_soc_compress_type {
@@ -274,7 +276,7 @@ enum snd_soc_pcm_subclass {
 };
 
 int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
-                            unsigned int freq, int dir);
+                            int source, unsigned int freq, int dir);
 int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
                          unsigned int freq_in, unsigned int freq_out);
 
@@ -576,6 +578,7 @@ struct snd_soc_codec {
        const void *reg_def_copy;
        const struct snd_soc_cache_ops *cache_ops;
        struct mutex cache_rw_mutex;
+       int val_bytes;
 
        /* dapm */
        struct snd_soc_dapm_context dapm;
@@ -607,7 +610,7 @@ struct snd_soc_codec_driver {
 
        /* codec wide operations */
        int (*set_sysclk)(struct snd_soc_codec *codec,
-                         int clk_id, unsigned int freq, int dir);
+                         int clk_id, int source, unsigned int freq, int dir);
        int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source,
                unsigned int freq_in, unsigned int freq_out);
 
@@ -619,7 +622,7 @@ struct snd_soc_codec_driver {
        int (*volatile_register)(struct snd_soc_codec *, unsigned int);
        int (*readable_register)(struct snd_soc_codec *, unsigned int);
        int (*writable_register)(struct snd_soc_codec *, unsigned int);
-       short reg_cache_size;
+       unsigned int reg_cache_size;
        short reg_cache_step;
        short reg_word_size;
        const void *reg_cache_default;
@@ -630,10 +633,14 @@ struct snd_soc_codec_driver {
        /* codec bias level */
        int (*set_bias_level)(struct snd_soc_codec *,
                              enum snd_soc_bias_level level);
+       bool idle_bias_off;
 
        void (*seq_notifier)(struct snd_soc_dapm_context *,
                             enum snd_soc_dapm_type, int);
 
+       /* codec stream completion event */
+       int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
+
        /* probe ordering - for components with runtime dependencies */
        int probe_order;
        int remove_order;
@@ -669,6 +676,9 @@ struct snd_soc_platform_driver {
        /* platform stream ops */
        struct snd_pcm_ops *ops;
 
+       /* platform stream completion event */
+       int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);
+
        /* probe ordering - for components with runtime dependencies */
        int probe_order;
        int remove_order;
index d0cead3..e518d38 100644 (file)
@@ -443,7 +443,7 @@ static int aaci_pcm_open(struct snd_pcm_substream *substream)
        mutex_lock(&aaci->irq_lock);
        if (!aaci->users++) {
                ret = request_irq(aaci->dev->irq[0], aaci_irq,
-                          IRQF_SHARED | IRQF_DISABLED, DRIVER_NAME, aaci);
+                          IRQF_SHARED, DRIVER_NAME, aaci);
                if (ret != 0)
                        aaci->users--;
        }
index 88eec38..8ad6535 100644 (file)
@@ -359,7 +359,7 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
        if (ret)
                goto err_clk2;
 
-       ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL);
+       ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
        if (ret < 0)
                goto err_irq;
 
index 5cfcb90..2c7a763 100644 (file)
@@ -1153,7 +1153,7 @@ snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
                   "0x%x done\n", (unsigned int)ml403_ac97cr->port);
        /* get irq */
        irq = platform_get_irq(pfdev, 0);
-       if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
+       if (request_irq(irq, snd_ml403_ac97cr_irq, 0,
                        dev_name(&pfdev->dev), (void *)ml403_ac97cr)) {
                snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
                           "unable to grab IRQ %d\n",
@@ -1166,7 +1166,7 @@ snd_ml403_ac97cr_create(struct snd_card *card, struct platform_device *pfdev,
                   "request (playback) irq %d done\n",
                   ml403_ac97cr->irq);
        irq = platform_get_irq(pfdev, 1);
-       if (request_irq(irq, snd_ml403_ac97cr_irq, IRQF_DISABLED,
+       if (request_irq(irq, snd_ml403_ac97cr_irq, 0,
                        dev_name(&pfdev->dev), (void *)ml403_ac97cr)) {
                snd_printk(KERN_ERR SND_ML403_AC97CR_DRIVER ": "
                           "unable to grab IRQ %d\n",
index 9d01c18..e91698a 100644 (file)
@@ -577,7 +577,7 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
        else
                mpu->cport = port + 1;
        if (irq >= 0) {
-               if (request_irq(irq, snd_mpu401_uart_interrupt, IRQF_DISABLED,
+               if (request_irq(irq, snd_mpu401_uart_interrupt, 0,
                                "MPU401 UART", (void *) mpu)) {
                        snd_printk(KERN_ERR "mpu401_uart: "
                                   "unable to grab IRQ %d\n", irq);
index 5c426df..1eef4cc 100644 (file)
@@ -589,7 +589,7 @@ static int __devinit snd_mtpav_get_ISA(struct mtpav * mcard)
                return -EBUSY;
        }
        mcard->port = port;
-       if (request_irq(irq, snd_mtpav_irqh, IRQF_DISABLED, "MOTU MTPAV", mcard)) {
+       if (request_irq(irq, snd_mtpav_irqh, 0, "MOTU MTPAV", mcard)) {
                snd_printk(KERN_ERR "MTVAP IRQ %d busy\n", irq);
                return -EBUSY;
        }
index a25fb7b..fc1d822 100644 (file)
@@ -816,7 +816,7 @@ static int __devinit snd_uart16550_create(struct snd_card *card,
 
        if (irq >= 0 && irq != SNDRV_AUTO_IRQ) {
                if (request_irq(irq, snd_uart16550_interrupt,
-                               IRQF_DISABLED, "Serial MIDI", uart)) {
+                               0, "Serial MIDI", uart)) {
                        snd_printk(KERN_WARNING
                                   "irq %d busy. Using Polling.\n", irq);
                } else {
index 05aef8b..177eed3 100644 (file)
@@ -595,7 +595,7 @@ int __devinit snd_ad1816a_create(struct snd_card *card,
                snd_ad1816a_free(chip);
                return -EBUSY;
        }
-       if (request_irq(irq, snd_ad1816a_interrupt, IRQF_DISABLED, "AD1816A", (void *) chip)) {
+       if (request_irq(irq, snd_ad1816a_interrupt, 0, "AD1816A", (void *) chip)) {
                snd_printk(KERN_ERR "ad1816a: can't grab IRQ %d\n", irq);
                snd_ad1816a_free(chip);
                return -EBUSY;
index 0767620..d3eab6f 100644 (file)
@@ -661,7 +661,7 @@ int snd_es1688_create(struct snd_card *card,
                snd_printk(KERN_ERR "es1688: can't grab port 0x%lx\n", port + 4);
                return -EBUSY;
        }
-       if (request_irq(irq, snd_es1688_interrupt, IRQF_DISABLED, "ES1688", (void *) chip)) {
+       if (request_irq(irq, snd_es1688_interrupt, 0, "ES1688", (void *) chip)) {
                snd_printk(KERN_ERR "es1688: can't grab IRQ %d\n", irq);
                return -EBUSY;
        }
index aeee8f8..bf6ad0b 100644 (file)
@@ -1805,7 +1805,7 @@ static int __devinit snd_es18xx_new_device(struct snd_card *card,
                return -EBUSY;
        }
 
-       if (request_irq(irq, snd_es18xx_interrupt, IRQF_DISABLED, "ES18xx",
+       if (request_irq(irq, snd_es18xx_interrupt, 0, "ES18xx",
                        (void *) card)) {
                snd_es18xx_free(card);
                snd_printk(KERN_ERR PFX "unable to grap IRQ %d\n", irq);
index 12eb98f..3167e5a 100644 (file)
@@ -180,7 +180,7 @@ int snd_gus_create(struct snd_card *card,
                snd_gus_free(gus);
                return -EBUSY;
        }
-       if (irq >= 0 && request_irq(irq, snd_gus_interrupt, IRQF_DISABLED, "GUS GF1", (void *) gus)) {
+       if (irq >= 0 && request_irq(irq, snd_gus_interrupt, 0, "GUS GF1", (void *) gus)) {
                snd_printk(KERN_ERR "gus: can't grab irq %d\n", irq);
                snd_gus_free(gus);
                return -EBUSY;
index 3e4a58b..c43faa0 100644 (file)
@@ -291,7 +291,7 @@ static int __devinit snd_gusmax_probe(struct device *pdev, unsigned int dev)
                goto _err;
        }
 
-       if (request_irq(xirq, snd_gusmax_interrupt, IRQF_DISABLED, "GUS MAX", (void *)maxcard)) {
+       if (request_irq(xirq, snd_gusmax_interrupt, 0, "GUS MAX", (void *)maxcard)) {
                snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq);
                err = -EBUSY;
                goto _err;
index c7b80e4..5f869a3 100644 (file)
@@ -684,7 +684,7 @@ static int __devinit snd_interwave_probe(struct snd_card *card, int dev)
        if ((err = snd_gus_initialize(gus)) < 0)
                return err;
 
-       if (request_irq(xirq, snd_interwave_interrupt, IRQF_DISABLED,
+       if (request_irq(xirq, snd_interwave_interrupt, 0,
                        "InterWave", iwcard)) {
                snd_printk(KERN_ERR PFX "unable to grab IRQ %d\n", xirq);
                return -EBUSY;
index de99f47..bbafb0b 100644 (file)
@@ -667,7 +667,7 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
        err = snd_opl3sa2_detect(card);
        if (err < 0)
                return err;
-       err = request_irq(xirq, snd_opl3sa2_interrupt, IRQF_DISABLED,
+       err = request_irq(xirq, snd_opl3sa2_interrupt, 0,
                          "OPL3-SA2", card);
        if (err) {
                snd_printk(KERN_ERR PFX "can't grab IRQ %d\n", xirq);
index 346e12b..6dbbfa7 100644 (file)
@@ -892,7 +892,7 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
 #endif
 #ifdef OPTi93X
        error = request_irq(irq, snd_opti93x_interrupt,
-                           IRQF_DISABLED, DEV_NAME" - WSS", chip);
+                           0, DEV_NAME" - WSS", chip);
        if (error < 0) {
                snd_printk(KERN_ERR "opti9xx: can't grab IRQ %d\n", irq);
                return error;
index eae6c1c..d2e1921 100644 (file)
@@ -240,7 +240,7 @@ int snd_sbdsp_create(struct snd_card *card,
        if (request_irq(irq, irq_handler,
                        (hardware == SB_HW_ALS4000 ||
                         hardware == SB_HW_CS5530) ?
-                       IRQF_SHARED : IRQF_DISABLED,
+                       IRQF_SHARED : 0,
                        "SoundBlaster", (void *) chip)) {
                snd_printk(KERN_ERR "sb: can't grab irq %d\n", irq);
                snd_sbdsp_free(chip);
index 83f291d..8714297 100644 (file)
@@ -418,7 +418,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
                return -EBUSY;
        }
        if (request_irq(ics2115_irq[dev], snd_wavefront_ics2115_interrupt,
-                       IRQF_DISABLED, "ICS2115", acard)) {
+                       0, "ICS2115", acard)) {
                snd_printk(KERN_ERR "unable to use ICS2115 IRQ %d\n", ics2115_irq[dev]);
                return -EBUSY;
        }
index 2a42cc3..7277c5b 100644 (file)
@@ -1833,7 +1833,7 @@ int snd_wss_create(struct snd_card *card,
        }
        chip->cport = cport;
        if (!(hwshare & WSS_HWSHARE_IRQ))
-               if (request_irq(irq, snd_wss_interrupt, IRQF_DISABLED,
+               if (request_irq(irq, snd_wss_interrupt, 0,
                                "WSS", (void *) chip)) {
                        snd_printk(KERN_ERR "wss: can't grab IRQ %d\n", irq);
                        snd_wss_free(chip);
index a9823fa..77dd0a1 100644 (file)
@@ -23,12 +23,15 @@ config SND_SGI_HAL2
 
 
 config SND_AU1X00
-       tristate "Au1x00 AC97 Port Driver"
+       tristate "Au1x00 AC97 Port Driver (DEPRECATED)"
        depends on SOC_AU1000 || SOC_AU1100 || SOC_AU1500
        select SND_PCM
        select SND_AC97_CODEC
        help
          ALSA Sound driver for the Au1x00's AC97 port.
 
+         Newer drivers for ASoC are available, please do not use
+         this driver as it will be removed in the future.
+
 endif  # SND_MIPS
 
index 446cf97..7567ebd 100644 (file)
@@ -465,13 +465,13 @@ snd_au1000_pcm_new(struct snd_au1000 *au1000)
 
        flags = claim_dma_lock();
        if ((au1000->stream[PLAYBACK]->dma = request_au1000_dma(DMA_ID_AC97C_TX,
-                       "AC97 TX", au1000_dma_interrupt, IRQF_DISABLED,
+                       "AC97 TX", au1000_dma_interrupt, 0,
                        au1000->stream[PLAYBACK])) < 0) {
                release_dma_lock(flags);
                return -EBUSY;
        }
        if ((au1000->stream[CAPTURE]->dma = request_au1000_dma(DMA_ID_AC97C_RX,
-                       "AC97 RX", au1000_dma_interrupt, IRQF_DISABLED,
+                       "AC97 RX", au1000_dma_interrupt, 0,
                        au1000->stream[CAPTURE])) < 0){
                release_dma_lock(flags);
                return -EBUSY;
index bcf6152..5ffb20b 100644 (file)
@@ -1234,7 +1234,7 @@ static int sis_resume(struct pci_dev *pci)
                goto error;
        }
 
-       if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
+       if (request_irq(pci->irq, sis_interrupt, IRQF_SHARED,
                        KBUILD_MODNAME, sis)) {
                printk(KERN_ERR "sis7019: unable to regain IRQ %d\n", pci->irq);
                goto error;
@@ -1340,7 +1340,7 @@ static int __devinit sis_chip_create(struct snd_card *card,
        if (rc)
                goto error_out_cleanup;
 
-       if (request_irq(pci->irq, sis_interrupt, IRQF_DISABLED|IRQF_SHARED,
+       if (request_irq(pci->irq, sis_interrupt, IRQF_SHARED,
                        KBUILD_MODNAME, sis)) {
                printk(KERN_ERR "unable to allocate irq %d\n", sis->irq);
                goto error_out_cleanup;
index bc823a5..775bd95 100644 (file)
@@ -845,7 +845,7 @@ static int __devinit snd_ps3_allocate_irq(void)
                return ret;
        }
 
-       ret = request_irq(the_card.irq_no, snd_ps3_interrupt, IRQF_DISABLED,
+       ret = request_irq(the_card.irq_no, snd_ps3_interrupt, 0,
                          SND_PS3_DRIVER_NAME, &the_card);
        if (ret) {
                pr_info("%s: request_irq failed (%d)\n", __func__, ret);
index 8224db5..1381db8 100644 (file)
@@ -7,6 +7,8 @@ menuconfig SND_SOC
        select SND_PCM
        select AC97_BUS if SND_SOC_AC97_BUS
        select SND_JACK if INPUT=y || INPUT=SND
+       select REGMAP_I2C if I2C
+       select REGMAP_SPI if SPI_MASTER
        ---help---
 
          If you want ASoC support, you should say Y here and also to the
@@ -51,6 +53,7 @@ source "sound/soc/nuc900/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/kirkwood/Kconfig"
 source "sound/soc/mid-x86/Kconfig"
+source "sound/soc/mxs/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/samsung/Kconfig"
 source "sound/soc/s6000/Kconfig"
index 4f91387..9ea8ac8 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_SND_SOC) += fsl/
 obj-$(CONFIG_SND_SOC)   += imx/
 obj-$(CONFIG_SND_SOC)  += jz4740/
 obj-$(CONFIG_SND_SOC)  += mid-x86/
+obj-$(CONFIG_SND_SOC)  += mxs/
 obj-$(CONFIG_SND_SOC)  += nuc900/
 obj-$(CONFIG_SND_SOC)  += omap/
 obj-$(CONFIG_SND_SOC)  += kirkwood/
index 1aac2f4..2909bfa 100644 (file)
@@ -383,14 +383,17 @@ static int __init playpaq_asoc_init(void)
        _gclk0 = clk_get(NULL, "gclk0");
        if (IS_ERR(_gclk0)) {
                _gclk0 = NULL;
+               ret = PTR_ERR(_gclk0);
                goto err_gclk0;
        }
        _pll0 = clk_get(NULL, "pll0");
        if (IS_ERR(_pll0)) {
                _pll0 = NULL;
+               ret = PTR_ERR(_pll0);
                goto err_pll0;
        }
-       if (clk_set_parent(_gclk0, _pll0)) {
+       ret = clk_set_parent(_gclk0, _pll0);
+       if (ret) {
                pr_warning("snd-soc-playpaq: "
                           "Failed to set PLL0 as parent for DAC clock\n");
                goto err_set_clk;
index 4b67140..6d59254 100644 (file)
@@ -18,10 +18,38 @@ config SND_SOC_AU1XPSC_AC97
        select SND_AC97_CODEC
        select SND_SOC_AC97_BUS
 
+##
+## Au1000/1500/1100 DMA + AC97C/I2SC
+##
+config SND_SOC_AU1XAUDIO
+       tristate "SoC Audio for Au1000/Au1500/Au1100"
+       depends on MIPS_ALCHEMY
+       help
+         This is a driver set for the AC97 unit and the
+         old DMA controller as found on the Au1000/Au1500/Au1100 chips.
+
+config SND_SOC_AU1XAC97C
+       tristate
+       select AC97_BUS
+       select SND_AC97_CODEC
+       select SND_SOC_AC97_BUS
+
+config SND_SOC_AU1XI2SC
+       tristate
+
 
 ##
 ## Boards
 ##
+config SND_SOC_DB1000
+       tristate "DB1000 Audio support"
+       depends on SND_SOC_AU1XAUDIO
+       select SND_SOC_AU1XAC97C
+       select SND_SOC_AC97_CODEC
+       help
+         Select this option to enable AC97 audio on the early DB1x00 series
+         of boards (DB1000/DB1500/DB1100).
+
 config SND_SOC_DB1200
        tristate "DB1200 AC97+I2S audio support"
        depends on SND_SOC_AU1XPSC
index 1687307..9207105 100644 (file)
@@ -3,11 +3,21 @@ snd-soc-au1xpsc-dbdma-objs := dbdma2.o
 snd-soc-au1xpsc-i2s-objs := psc-i2s.o
 snd-soc-au1xpsc-ac97-objs := psc-ac97.o
 
+# Au1000/1500/1100 Audio units
+snd-soc-au1x-dma-objs := dma.o
+snd-soc-au1x-ac97c-objs := ac97c.o
+snd-soc-au1x-i2sc-objs := i2sc.o
+
 obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o
 obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o
 obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o
+obj-$(CONFIG_SND_SOC_AU1XAUDIO) += snd-soc-au1x-dma.o
+obj-$(CONFIG_SND_SOC_AU1XAC97C) += snd-soc-au1x-ac97c.o
+obj-$(CONFIG_SND_SOC_AU1XI2SC) += snd-soc-au1x-i2sc.o
 
 # Boards
+snd-soc-db1000-objs := db1000.o
 snd-soc-db1200-objs := db1200.o
 
+obj-$(CONFIG_SND_SOC_DB1000) += snd-soc-db1000.o
 obj-$(CONFIG_SND_SOC_DB1200) += snd-soc-db1200.o
diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c
new file mode 100644 (file)
index 0000000..13802ff
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Au1000/Au1500/Au1100 AC97C controller driver for ASoC
+ *
+ * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
+ *
+ * based on the old ALSA driver originally written by
+ *                     Charles Eidsness <charles@cooper-street.com>
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+
+#include "psc.h"
+
+/* register offsets and bits */
+#define AC97_CONFIG    0x00
+#define AC97_STATUS    0x04
+#define AC97_DATA      0x08
+#define AC97_CMDRESP   0x0c
+#define AC97_ENABLE    0x10
+
+#define CFG_RC(x)      (((x) & 0x3ff) << 13)   /* valid rx slots mask */
+#define CFG_XS(x)      (((x) & 0x3ff) << 3)    /* valid tx slots mask */
+#define CFG_SG         (1 << 2)        /* sync gate */
+#define CFG_SN         (1 << 1)        /* sync control */
+#define CFG_RS         (1 << 0)        /* acrst# control */
+#define STAT_XU                (1 << 11)       /* tx underflow */
+#define STAT_XO                (1 << 10)       /* tx overflow */
+#define STAT_RU                (1 << 9)        /* rx underflow */
+#define STAT_RO                (1 << 8)        /* rx overflow */
+#define STAT_RD                (1 << 7)        /* codec ready */
+#define STAT_CP                (1 << 6)        /* command pending */
+#define STAT_TE                (1 << 4)        /* tx fifo empty */
+#define STAT_TF                (1 << 3)        /* tx fifo full */
+#define STAT_RE                (1 << 1)        /* rx fifo empty */
+#define STAT_RF                (1 << 0)        /* rx fifo full */
+#define CMD_SET_DATA(x)        (((x) & 0xffff) << 16)
+#define CMD_GET_DATA(x)        ((x) & 0xffff)
+#define CMD_READ       (1 << 7)
+#define CMD_WRITE      (0 << 7)
+#define CMD_IDX(x)     ((x) & 0x7f)
+#define EN_D           (1 << 1)        /* DISable bit */
+#define EN_CE          (1 << 0)        /* clock enable bit */
+
+/* how often to retry failed codec register reads/writes */
+#define AC97_RW_RETRIES        5
+
+#define AC97_RATES     \
+       SNDRV_PCM_RATE_CONTINUOUS
+
+#define AC97_FMTS      \
+       (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE)
+
+/* instance data. There can be only one, MacLeod!!!!, fortunately there IS only
+ * once AC97C on early Alchemy chips. The newer ones aren't so lucky.
+ */
+static struct au1xpsc_audio_data *ac97c_workdata;
+#define ac97_to_ctx(x)         ac97c_workdata
+
+static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg)
+{
+       return __raw_readl(ctx->mmio + reg);
+}
+
+static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v)
+{
+       __raw_writel(v, ctx->mmio + reg);
+       wmb();
+}
+
+static unsigned short au1xac97c_ac97_read(struct snd_ac97 *ac97,
+                                         unsigned short r)
+{
+       struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
+       unsigned int tmo, retry;
+       unsigned long data;
+
+       data = ~0;
+       retry = AC97_RW_RETRIES;
+       do {
+               mutex_lock(&ctx->lock);
+
+               tmo = 5;
+               while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--)
+                       udelay(21);     /* wait an ac97 frame time */
+               if (!tmo) {
+                       pr_debug("ac97rd timeout #1\n");
+                       goto next;
+               }
+
+               WR(ctx, AC97_CMDRESP, CMD_IDX(r) | CMD_READ);
+
+               /* stupid errata: data is only valid for 21us, so
+                * poll, Forrest, poll...
+                */
+               tmo = 0x10000;
+               while ((RD(ctx, AC97_STATUS) & STAT_CP) && tmo--)
+                       asm volatile ("nop");
+               data = RD(ctx, AC97_CMDRESP);
+
+               if (!tmo)
+                       pr_debug("ac97rd timeout #2\n");
+
+next:
+               mutex_unlock(&ctx->lock);
+       } while (--retry && !tmo);
+
+       pr_debug("AC97RD %04x %04lx %d\n", r, data, retry);
+
+       return retry ? data & 0xffff : 0xffff;
+}
+
+static void au1xac97c_ac97_write(struct snd_ac97 *ac97, unsigned short r,
+                                unsigned short v)
+{
+       struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
+       unsigned int tmo, retry;
+
+       retry = AC97_RW_RETRIES;
+       do {
+               mutex_lock(&ctx->lock);
+
+               for (tmo = 5; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--)
+                       udelay(21);
+               if (!tmo) {
+                       pr_debug("ac97wr timeout #1\n");
+                       goto next;
+               }
+
+               WR(ctx, AC97_CMDRESP, CMD_WRITE | CMD_IDX(r) | CMD_SET_DATA(v));
+
+               for (tmo = 10; (RD(ctx, AC97_STATUS) & STAT_CP) && tmo; tmo--)
+                       udelay(21);
+               if (!tmo)
+                       pr_debug("ac97wr timeout #2\n");
+next:
+               mutex_unlock(&ctx->lock);
+       } while (--retry && !tmo);
+
+       pr_debug("AC97WR %04x %04x %d\n", r, v, retry);
+}
+
+static void au1xac97c_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+       struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
+
+       WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG | CFG_SN);
+       msleep(20);
+       WR(ctx, AC97_CONFIG, ctx->cfg | CFG_SG);
+       WR(ctx, AC97_CONFIG, ctx->cfg);
+}
+
+static void au1xac97c_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+       struct au1xpsc_audio_data *ctx = ac97_to_ctx(ac97);
+       int i;
+
+       WR(ctx, AC97_CONFIG, ctx->cfg | CFG_RS);
+       msleep(500);
+       WR(ctx, AC97_CONFIG, ctx->cfg);
+
+       /* wait for codec ready */
+       i = 50;
+       while (((RD(ctx, AC97_STATUS) & STAT_RD) == 0) && --i)
+               msleep(20);
+       if (!i)
+               printk(KERN_ERR "ac97c: codec not ready after cold reset\n");
+}
+
+/* AC97 controller operations */
+struct snd_ac97_bus_ops soc_ac97_ops = {
+       .read           = au1xac97c_ac97_read,
+       .write          = au1xac97c_ac97_write,
+       .reset          = au1xac97c_ac97_cold_reset,
+       .warm_reset     = au1xac97c_ac97_warm_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);       /* globals be gone! */
+
+static int alchemy_ac97c_startup(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
+{
+       struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
+       snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]);
+       return 0;
+}
+
+static struct snd_soc_dai_ops alchemy_ac97c_ops = {
+       .startup                = alchemy_ac97c_startup,
+};
+
+static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
+{
+       return ac97c_workdata ? 0 : -ENODEV;
+}
+
+static struct snd_soc_dai_driver au1xac97c_dai_driver = {
+       .name                   = "alchemy-ac97c",
+       .ac97_control           = 1,
+       .probe                  = au1xac97c_dai_probe,
+       .playback = {
+               .rates          = AC97_RATES,
+               .formats        = AC97_FMTS,
+               .channels_min   = 2,
+               .channels_max   = 2,
+       },
+       .capture = {
+               .rates          = AC97_RATES,
+               .formats        = AC97_FMTS,
+               .channels_min   = 2,
+               .channels_max   = 2,
+       },
+       .ops                    = &alchemy_ac97c_ops,
+};
+
+static int __devinit au1xac97c_drvprobe(struct platform_device *pdev)
+{
+       int ret;
+       struct resource *r;
+       struct au1xpsc_audio_data *ctx;
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       mutex_init(&ctx->lock);
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               ret = -ENODEV;
+               goto out0;
+       }
+
+       ret = -EBUSY;
+       if (!request_mem_region(r->start, resource_size(r), pdev->name))
+               goto out0;
+
+       ctx->mmio = ioremap_nocache(r->start, resource_size(r));
+       if (!ctx->mmio)
+               goto out1;
+
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!r)
+               goto out1;
+       ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start;
+
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (!r)
+               goto out1;
+       ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start;
+
+       /* switch it on */
+       WR(ctx, AC97_ENABLE, EN_D | EN_CE);
+       WR(ctx, AC97_ENABLE, EN_CE);
+
+       ctx->cfg = CFG_RC(3) | CFG_XS(3);
+       WR(ctx, AC97_CONFIG, ctx->cfg);
+
+       platform_set_drvdata(pdev, ctx);
+
+       ret = snd_soc_register_dai(&pdev->dev, &au1xac97c_dai_driver);
+       if (ret)
+               goto out1;
+
+       ac97c_workdata = ctx;
+       return 0;
+
+out1:
+       release_mem_region(r->start, resource_size(r));
+out0:
+       kfree(ctx);
+       return ret;
+}
+
+static int __devexit au1xac97c_drvremove(struct platform_device *pdev)
+{
+       struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
+       struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       snd_soc_unregister_dai(&pdev->dev);
+
+       WR(ctx, AC97_ENABLE, EN_D);     /* clock off, disable */
+
+       iounmap(ctx->mmio);
+       release_mem_region(r->start, resource_size(r));
+       kfree(ctx);
+
+       ac97c_workdata = NULL;  /* MDEV */
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int au1xac97c_drvsuspend(struct device *dev)
+{
+       struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
+
+       WR(ctx, AC97_ENABLE, EN_D);     /* clock off, disable */
+
+       return 0;
+}
+
+static int au1xac97c_drvresume(struct device *dev)
+{
+       struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
+
+       WR(ctx, AC97_ENABLE, EN_D | EN_CE);
+       WR(ctx, AC97_ENABLE, EN_CE);
+       WR(ctx, AC97_CONFIG, ctx->cfg);
+
+       return 0;
+}
+
+static const struct dev_pm_ops au1xpscac97_pmops = {
+       .suspend        = au1xac97c_drvsuspend,
+       .resume         = au1xac97c_drvresume,
+};
+
+#define AU1XPSCAC97_PMOPS (&au1xpscac97_pmops)
+
+#else
+
+#define AU1XPSCAC97_PMOPS NULL
+
+#endif
+
+static struct platform_driver au1xac97c_driver = {
+       .driver = {
+               .name   = "alchemy-ac97c",
+               .owner  = THIS_MODULE,
+               .pm     = AU1XPSCAC97_PMOPS,
+       },
+       .probe          = au1xac97c_drvprobe,
+       .remove         = __devexit_p(au1xac97c_drvremove),
+};
+
+static int __init au1xac97c_load(void)
+{
+       ac97c_workdata = NULL;
+       return platform_driver_register(&au1xac97c_driver);
+}
+
+static void __exit au1xac97c_unload(void)
+{
+       platform_driver_unregister(&au1xac97c_driver);
+}
+
+module_init(au1xac97c_load);
+module_exit(au1xac97c_unload);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1000/1500/1100 AC97C ASoC driver");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/db1000.c b/sound/soc/au1x/db1000.c
new file mode 100644 (file)
index 0000000..127477a
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * DB1000/DB1500/DB1100 ASoC audio fabric support code.
+ *
+ * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-db1x00/bcsr.h>
+
+#include "psc.h"
+
+static struct snd_soc_dai_link db1000_ac97_dai = {
+       .name           = "AC97",
+       .stream_name    = "AC97 HiFi",
+       .codec_dai_name = "ac97-hifi",
+       .cpu_dai_name   = "alchemy-ac97c",
+       .platform_name  = "alchemy-pcm-dma.0",
+       .codec_name     = "ac97-codec",
+};
+
+static struct snd_soc_card db1000_ac97 = {
+       .name           = "DB1000_AC97",
+       .dai_link       = &db1000_ac97_dai,
+       .num_links      = 1,
+};
+
+static int __devinit db1000_audio_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &db1000_ac97;
+       card->dev = &pdev->dev;
+       return snd_soc_register_card(card);
+}
+
+static int __devexit db1000_audio_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       snd_soc_unregister_card(card);
+       return 0;
+}
+
+static struct platform_driver db1000_audio_driver = {
+       .driver = {
+               .name   = "db1000-audio",
+               .owner  = THIS_MODULE,
+               .pm     = &snd_soc_pm_ops,
+       },
+       .probe          = db1000_audio_probe,
+       .remove         = __devexit_p(db1000_audio_remove),
+};
+
+static int __init db1000_audio_load(void)
+{
+       return platform_driver_register(&db1000_audio_driver);
+}
+
+static void __exit db1000_audio_unload(void)
+{
+       platform_driver_unregister(&db1000_audio_driver);
+}
+
+module_init(db1000_audio_load);
+module_exit(db1000_audio_unload);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DB1000/DB1500/DB1100 ASoC audio");
+MODULE_AUTHOR("Manuel Lauss");
index 1d3e258..289312c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * DB1200 ASoC audio fabric support code.
  *
- * (c) 2008-9 Manuel Lauss <manuel.lauss@gmail.com>
+ * (c) 2008-2011 Manuel Lauss <manuel.lauss@googlemail.com>
  *
  */
 
 #include "../codecs/wm8731.h"
 #include "psc.h"
 
+static struct platform_device_id db1200_pids[] = {
+       {
+               .name           = "db1200-ac97",
+               .driver_data    = 0,
+       }, {
+               .name           = "db1200-i2s",
+               .driver_data    = 1,
+       },
+       {},
+};
+
 /*-------------------------  AC97 PART  ---------------------------*/
 
 static struct snd_soc_dai_link db1200_ac97_dai = {
@@ -89,36 +100,47 @@ static struct snd_soc_card db1200_i2s_machine = {
 
 /*-------------------------  COMMON PART  ---------------------------*/
 
-static struct platform_device *db1200_asoc_dev;
+static struct snd_soc_card *db1200_cards[] __devinitdata = {
+       &db1200_ac97_machine,
+       &db1200_i2s_machine,
+};
 
-static int __init db1200_audio_load(void)
+static int __devinit db1200_audio_probe(struct platform_device *pdev)
 {
-       int ret;
+       const struct platform_device_id *pid = platform_get_device_id(pdev);
+       struct snd_soc_card *card;
 
-       ret = -ENOMEM;
-       db1200_asoc_dev = platform_device_alloc("soc-audio", 1); /* PSC1 */
-       if (!db1200_asoc_dev)
-               goto out;
+       card = db1200_cards[pid->driver_data];
+       card->dev = &pdev->dev;
+       return snd_soc_register_card(card);
+}
 
-       /* DB1200 board setup set PSC1MUX to preferred audio device */
-       if (bcsr_read(BCSR_RESETS) & BCSR_RESETS_PSC1MUX)
-               platform_set_drvdata(db1200_asoc_dev, &db1200_i2s_machine);
-       else
-               platform_set_drvdata(db1200_asoc_dev, &db1200_ac97_machine);
+static int __devexit db1200_audio_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       snd_soc_unregister_card(card);
+       return 0;
+}
 
-       ret = platform_device_add(db1200_asoc_dev);
+static struct platform_driver db1200_audio_driver = {
+       .driver = {
+               .name   = "db1200-ac97",
+               .owner  = THIS_MODULE,
+               .pm     = &snd_soc_pm_ops,
+       },
+       .id_table       = db1200_pids,
+       .probe          = db1200_audio_probe,
+       .remove         = __devexit_p(db1200_audio_remove),
+};
 
-       if (ret) {
-               platform_device_put(db1200_asoc_dev);
-               db1200_asoc_dev = NULL;
-       }
-out:
-       return ret;
+static int __init db1200_audio_load(void)
+{
+       return platform_driver_register(&db1200_audio_driver);
 }
 
 static void __exit db1200_audio_unload(void)
 {
-       platform_device_unregister(db1200_asoc_dev);
+       platform_driver_unregister(&db1200_audio_driver);
 }
 
 module_init(db1200_audio_load);
index 20bb53a..d7d04e2 100644 (file)
@@ -169,7 +169,7 @@ static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd,
 
        au1x_pcm_dbdma_free(pcd);
 
-       if (stype == PCM_RX)
+       if (stype == SNDRV_PCM_STREAM_CAPTURE)
                pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id,
                                        DSCR_CMD0_ALWAYS,
                                        au1x_pcm_dmarx_cb, (void *)pcd);
@@ -198,7 +198,7 @@ static inline struct au1xpsc_audio_dmadata *to_dmadata(struct snd_pcm_substream
        struct snd_soc_pcm_runtime *rtd = ss->private_data;
        struct au1xpsc_audio_dmadata *pcd =
                                snd_soc_platform_get_drvdata(rtd->platform);
-       return &pcd[SUBSTREAM_TYPE(ss)];
+       return &pcd[ss->stream];
 }
 
 static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -212,7 +212,7 @@ static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream,
        if (ret < 0)
                goto out;
 
-       stype = SUBSTREAM_TYPE(substream);
+       stype = substream->stream;
        pcd = to_dmadata(substream);
 
        DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d "
@@ -255,7 +255,7 @@ static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream)
 
        au1xxx_dbdma_reset(pcd->ddma_chan);
 
-       if (SUBSTREAM_TYPE(substream) == PCM_RX) {
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
                au1x_pcm_queue_rx(pcd);
                au1x_pcm_queue_rx(pcd);
        } else {
@@ -293,6 +293,16 @@ au1xpsc_pcm_pointer(struct snd_pcm_substream *substream)
 
 static int au1xpsc_pcm_open(struct snd_pcm_substream *substream)
 {
+       struct au1xpsc_audio_dmadata *pcd = to_dmadata(substream);
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int stype = substream->stream, *dmaids;
+
+       dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+       if (!dmaids)
+               return -ENODEV; /* whoa, has ordering changed? */
+
+       pcd->ddma_id = dmaids[stype];
+
        snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware);
        return 0;
 }
@@ -340,36 +350,18 @@ struct snd_soc_platform_driver au1xpsc_soc_platform = {
 static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev)
 {
        struct au1xpsc_audio_dmadata *dmadata;
-       struct resource *r;
        int ret;
 
        dmadata = kzalloc(2 * sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL);
        if (!dmadata)
                return -ENOMEM;
 
-       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-       if (!r) {
-               ret = -ENODEV;
-               goto out1;
-       }
-       dmadata[PCM_TX].ddma_id = r->start;
-
-       /* RX DMA */
-       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
-       if (!r) {
-               ret = -ENODEV;
-               goto out1;
-       }
-       dmadata[PCM_RX].ddma_id = r->start;
-
        platform_set_drvdata(pdev, dmadata);
 
        ret = snd_soc_register_platform(&pdev->dev, &au1xpsc_soc_platform);
-       if (!ret)
-               return ret;
+       if (ret)
+               kfree(dmadata);
 
-out1:
-       kfree(dmadata);
        return ret;
 }
 
@@ -405,57 +397,6 @@ static void __exit au1xpsc_audio_dbdma_unload(void)
 module_init(au1xpsc_audio_dbdma_load);
 module_exit(au1xpsc_audio_dbdma_unload);
 
-
-struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev)
-{
-       struct resource *res, *r;
-       struct platform_device *pd;
-       int id[2];
-       int ret;
-
-       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-       if (!r)
-               return NULL;
-       id[0] = r->start;
-
-       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
-       if (!r)
-               return NULL;
-       id[1] = r->start;
-
-       res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL);
-       if (!res)
-               return NULL;
-
-       res[0].start = res[0].end = id[0];
-       res[1].start = res[1].end = id[1];
-       res[0].flags = res[1].flags = IORESOURCE_DMA;
-
-       pd = platform_device_alloc("au1xpsc-pcm", pdev->id);
-       if (!pd)
-               goto out;
-
-       pd->resource = res;
-       pd->num_resources = 2;
-
-       ret = platform_device_add(pd);
-       if (!ret)
-               return pd;
-
-       platform_device_put(pd);
-out:
-       kfree(res);
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(au1xpsc_pcm_add);
-
-void au1xpsc_pcm_destroy(struct platform_device *dmapd)
-{
-       if (dmapd)
-               platform_device_unregister(dmapd);
-}
-EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy);
-
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
 MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/dma.c b/sound/soc/au1x/dma.c
new file mode 100644 (file)
index 0000000..177f713
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * Au1000/Au1500/Au1100 Audio DMA support.
+ *
+ * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
+ *
+ * copied almost verbatim from the old ALSA driver, written by
+ *                     Charles Eidsness <charles@cooper-street.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1000_dma.h>
+
+#include "psc.h"
+
+#define ALCHEMY_PCM_FMTS                                       \
+       (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_U8 |        \
+        SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |    \
+        SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |    \
+        SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |    \
+        SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE |    \
+        0)
+
+struct pcm_period {
+       u32 start;
+       u32 relative_end;       /* relative to start of buffer */
+       struct pcm_period *next;
+};
+
+struct audio_stream {
+       struct snd_pcm_substream *substream;
+       int dma;
+       struct pcm_period *buffer;
+       unsigned int period_size;
+       unsigned int periods;
+};
+
+struct alchemy_pcm_ctx {
+       struct audio_stream stream[2];  /* playback & capture */
+};
+
+static void au1000_release_dma_link(struct audio_stream *stream)
+{
+       struct pcm_period *pointer;
+       struct pcm_period *pointer_next;
+
+       stream->period_size = 0;
+       stream->periods = 0;
+       pointer = stream->buffer;
+       if (!pointer)
+               return;
+       do {
+               pointer_next = pointer->next;
+               kfree(pointer);
+               pointer = pointer_next;
+       } while (pointer != stream->buffer);
+       stream->buffer = NULL;
+}
+
+static int au1000_setup_dma_link(struct audio_stream *stream,
+                                unsigned int period_bytes,
+                                unsigned int periods)
+{
+       struct snd_pcm_substream *substream = stream->substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct pcm_period *pointer;
+       unsigned long dma_start;
+       int i;
+
+       dma_start = virt_to_phys(runtime->dma_area);
+
+       if (stream->period_size == period_bytes &&
+           stream->periods == periods)
+               return 0; /* not changed */
+
+       au1000_release_dma_link(stream);
+
+       stream->period_size = period_bytes;
+       stream->periods = periods;
+
+       stream->buffer = kmalloc(sizeof(struct pcm_period), GFP_KERNEL);
+       if (!stream->buffer)
+               return -ENOMEM;
+       pointer = stream->buffer;
+       for (i = 0; i < periods; i++) {
+               pointer->start = (u32)(dma_start + (i * period_bytes));
+               pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
+               if (i < periods - 1) {
+                       pointer->next = kmalloc(sizeof(struct pcm_period),
+                                               GFP_KERNEL);
+                       if (!pointer->next) {
+                               au1000_release_dma_link(stream);
+                               return -ENOMEM;
+                       }
+                       pointer = pointer->next;
+               }
+       }
+       pointer->next = stream->buffer;
+       return 0;
+}
+
+static void au1000_dma_stop(struct audio_stream *stream)
+{
+       if (stream->buffer)
+               disable_dma(stream->dma);
+}
+
+static void au1000_dma_start(struct audio_stream *stream)
+{
+       if (!stream->buffer)
+               return;
+
+       init_dma(stream->dma);
+       if (get_dma_active_buffer(stream->dma) == 0) {
+               clear_dma_done0(stream->dma);
+               set_dma_addr0(stream->dma, stream->buffer->start);
+               set_dma_count0(stream->dma, stream->period_size >> 1);
+               set_dma_addr1(stream->dma, stream->buffer->next->start);
+               set_dma_count1(stream->dma, stream->period_size >> 1);
+       } else {
+               clear_dma_done1(stream->dma);
+               set_dma_addr1(stream->dma, stream->buffer->start);
+               set_dma_count1(stream->dma, stream->period_size >> 1);
+               set_dma_addr0(stream->dma, stream->buffer->next->start);
+               set_dma_count0(stream->dma, stream->period_size >> 1);
+       }
+       enable_dma_buffers(stream->dma);
+       start_dma(stream->dma);
+}
+
+static irqreturn_t au1000_dma_interrupt(int irq, void *ptr)
+{
+       struct audio_stream *stream = (struct audio_stream *)ptr;
+       struct snd_pcm_substream *substream = stream->substream;
+
+       switch (get_dma_buffer_done(stream->dma)) {
+       case DMA_D0:
+               stream->buffer = stream->buffer->next;
+               clear_dma_done0(stream->dma);
+               set_dma_addr0(stream->dma, stream->buffer->next->start);
+               set_dma_count0(stream->dma, stream->period_size >> 1);
+               enable_dma_buffer0(stream->dma);
+               break;
+       case DMA_D1:
+               stream->buffer = stream->buffer->next;
+               clear_dma_done1(stream->dma);
+               set_dma_addr1(stream->dma, stream->buffer->next->start);
+               set_dma_count1(stream->dma, stream->period_size >> 1);
+               enable_dma_buffer1(stream->dma);
+               break;
+       case (DMA_D0 | DMA_D1):
+               pr_debug("DMA %d missed interrupt.\n", stream->dma);
+               au1000_dma_stop(stream);
+               au1000_dma_start(stream);
+               break;
+       case (~DMA_D0 & ~DMA_D1):
+               pr_debug("DMA %d empty irq.\n", stream->dma);
+       }
+       snd_pcm_period_elapsed(substream);
+       return IRQ_HANDLED;
+}
+
+static const struct snd_pcm_hardware alchemy_pcm_hardware = {
+       .info             = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+                           SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BATCH,
+       .formats          = ALCHEMY_PCM_FMTS,
+       .rates            = SNDRV_PCM_RATE_8000_192000,
+       .rate_min         = SNDRV_PCM_RATE_8000,
+       .rate_max         = SNDRV_PCM_RATE_192000,
+       .channels_min     = 2,
+       .channels_max     = 2,
+       .period_bytes_min = 1024,
+       .period_bytes_max = 16 * 1024 - 1,
+       .periods_min      = 4,
+       .periods_max      = 255,
+       .buffer_bytes_max = 128 * 1024,
+       .fifo_size        = 16,
+};
+
+static inline struct alchemy_pcm_ctx *ss_to_ctx(struct snd_pcm_substream *ss)
+{
+       struct snd_soc_pcm_runtime *rtd = ss->private_data;
+       return snd_soc_platform_get_drvdata(rtd->platform);
+}
+
+static inline struct audio_stream *ss_to_as(struct snd_pcm_substream *ss)
+{
+       struct alchemy_pcm_ctx *ctx = ss_to_ctx(ss);
+       return &(ctx->stream[ss->stream]);
+}
+
+static int alchemy_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int *dmaids, s = substream->stream;
+       char *name;
+
+       dmaids = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+       if (!dmaids)
+               return -ENODEV; /* whoa, has ordering changed? */
+
+       /* DMA setup */
+       name = (s == SNDRV_PCM_STREAM_PLAYBACK) ? "audio-tx" : "audio-rx";
+       ctx->stream[s].dma = request_au1000_dma(dmaids[s], name,
+                                       au1000_dma_interrupt, 0,
+                                       &ctx->stream[s]);
+       set_dma_mode(ctx->stream[s].dma,
+                    get_dma_mode(ctx->stream[s].dma) & ~DMA_NC);
+
+       ctx->stream[s].substream = substream;
+       ctx->stream[s].buffer = NULL;
+       snd_soc_set_runtime_hwparams(substream, &alchemy_pcm_hardware);
+
+       return 0;
+}
+
+static int alchemy_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct alchemy_pcm_ctx *ctx = ss_to_ctx(substream);
+       int stype = substream->stream;
+
+       ctx->stream[stype].substream = NULL;
+       free_au1000_dma(ctx->stream[stype].dma);
+
+       return 0;
+}
+
+static int alchemy_pcm_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *hw_params)
+{
+       struct audio_stream *stream = ss_to_as(substream);
+       int err;
+
+       err = snd_pcm_lib_malloc_pages(substream,
+                                      params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+       err = au1000_setup_dma_link(stream,
+                                   params_period_bytes(hw_params),
+                                   params_periods(hw_params));
+       if (err)
+               snd_pcm_lib_free_pages(substream);
+
+       return err;
+}
+
+static int alchemy_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct audio_stream *stream = ss_to_as(substream);
+       au1000_release_dma_link(stream);
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static int alchemy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct audio_stream *stream = ss_to_as(substream);
+       int err = 0;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               au1000_dma_start(stream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               au1000_dma_stop(stream);
+               break;
+       default:
+               err = -EINVAL;
+               break;
+       }
+       return err;
+}
+
+static snd_pcm_uframes_t alchemy_pcm_pointer(struct snd_pcm_substream *ss)
+{
+       struct audio_stream *stream = ss_to_as(ss);
+       long location;
+
+       location = get_dma_residue(stream->dma);
+       location = stream->buffer->relative_end - location;
+       if (location == -1)
+               location = 0;
+       return bytes_to_frames(ss->runtime, location);
+}
+
+static struct snd_pcm_ops alchemy_pcm_ops = {
+       .open                   = alchemy_pcm_open,
+       .close                  = alchemy_pcm_close,
+       .ioctl                  = snd_pcm_lib_ioctl,
+       .hw_params              = alchemy_pcm_hw_params,
+       .hw_free                = alchemy_pcm_hw_free,
+       .trigger                = alchemy_pcm_trigger,
+       .pointer                = alchemy_pcm_pointer,
+};
+
+static void alchemy_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int alchemy_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_pcm *pcm = rtd->pcm;
+
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+               snd_dma_continuous_data(GFP_KERNEL), 65536, (4096 * 1024) - 1);
+
+       return 0;
+}
+
+struct snd_soc_platform_driver alchemy_pcm_soc_platform = {
+       .ops            = &alchemy_pcm_ops,
+       .pcm_new        = alchemy_pcm_new,
+       .pcm_free       = alchemy_pcm_free_dma_buffers,
+};
+
+static int __devinit alchemy_pcm_drvprobe(struct platform_device *pdev)
+{
+       struct alchemy_pcm_ctx *ctx;
+       int ret;
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, ctx);
+
+       ret = snd_soc_register_platform(&pdev->dev, &alchemy_pcm_soc_platform);
+       if (ret)
+               kfree(ctx);
+
+       return ret;
+}
+
+static int __devexit alchemy_pcm_drvremove(struct platform_device *pdev)
+{
+       struct alchemy_pcm_ctx *ctx = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_platform(&pdev->dev);
+       kfree(ctx);
+
+       return 0;
+}
+
+static struct platform_driver alchemy_pcmdma_driver = {
+       .driver = {
+               .name   = "alchemy-pcm-dma",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = alchemy_pcm_drvprobe,
+       .remove         = __devexit_p(alchemy_pcm_drvremove),
+};
+
+static int __init alchemy_pcmdma_load(void)
+{
+       return platform_driver_register(&alchemy_pcmdma_driver);
+}
+
+static void __exit alchemy_pcmdma_unload(void)
+{
+       platform_driver_unregister(&alchemy_pcmdma_driver);
+}
+
+module_init(alchemy_pcmdma_load);
+module_exit(alchemy_pcmdma_unload);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1000/Au1500/Au1100 Audio DMA driver");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/i2sc.c b/sound/soc/au1x/i2sc.c
new file mode 100644 (file)
index 0000000..19e0d2a
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Au1000/Au1500/Au1100 I2S controller driver for ASoC
+ *
+ * (c) 2011 Manuel Lauss <manuel.lauss@googlemail.com>
+ *
+ * Note: clock supplied to the I2S controller must be 256x samplerate.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/suspend.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <asm/mach-au1x00/au1000.h>
+
+#include "psc.h"
+
+#define I2S_RXTX       0x00
+#define I2S_CFG                0x04
+#define I2S_ENABLE     0x08
+
+#define CFG_XU         (1 << 25)       /* tx underflow */
+#define CFG_XO         (1 << 24)
+#define CFG_RU         (1 << 23)
+#define CFG_RO         (1 << 22)
+#define CFG_TR         (1 << 21)
+#define CFG_TE         (1 << 20)
+#define CFG_TF         (1 << 19)
+#define CFG_RR         (1 << 18)
+#define CFG_RF         (1 << 17)
+#define CFG_ICK                (1 << 12)       /* clock invert */
+#define CFG_PD         (1 << 11)       /* set to make I2SDIO INPUT */
+#define CFG_LB         (1 << 10)       /* loopback */
+#define CFG_IC         (1 << 9)        /* word select invert */
+#define CFG_FM_I2S     (0 << 7)        /* I2S format */
+#define CFG_FM_LJ      (1 << 7)        /* left-justified */
+#define CFG_FM_RJ      (2 << 7)        /* right-justified */
+#define CFG_FM_MASK    (3 << 7)
+#define CFG_TN         (1 << 6)        /* tx fifo en */
+#define CFG_RN         (1 << 5)        /* rx fifo en */
+#define CFG_SZ_8       (0x08)
+#define CFG_SZ_16      (0x10)
+#define CFG_SZ_18      (0x12)
+#define CFG_SZ_20      (0x14)
+#define CFG_SZ_24      (0x18)
+#define CFG_SZ_MASK    (0x1f)
+#define EN_D           (1 << 1)        /* DISable */
+#define EN_CE          (1 << 0)        /* clock enable */
+
+/* only limited by clock generator and board design */
+#define AU1XI2SC_RATES \
+       SNDRV_PCM_RATE_CONTINUOUS
+
+#define AU1XI2SC_FMTS \
+       (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |            \
+       SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |     \
+       SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |     \
+       SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_U18_3LE |   \
+       SNDRV_PCM_FMTBIT_S18_3BE | SNDRV_PCM_FMTBIT_U18_3BE |   \
+       SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE |   \
+       SNDRV_PCM_FMTBIT_S20_3BE | SNDRV_PCM_FMTBIT_U20_3BE |   \
+       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |     \
+       SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |     \
+       0)
+
+static inline unsigned long RD(struct au1xpsc_audio_data *ctx, int reg)
+{
+       return __raw_readl(ctx->mmio + reg);
+}
+
+static inline void WR(struct au1xpsc_audio_data *ctx, int reg, unsigned long v)
+{
+       __raw_writel(v, ctx->mmio + reg);
+       wmb();
+}
+
+static int au1xi2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+       struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(cpu_dai);
+       unsigned long c;
+       int ret;
+
+       ret = -EINVAL;
+       c = ctx->cfg;
+
+       c &= ~CFG_FM_MASK;
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               c |= CFG_FM_I2S;
+               break;
+       case SND_SOC_DAIFMT_MSB:
+               c |= CFG_FM_RJ;
+               break;
+       case SND_SOC_DAIFMT_LSB:
+               c |= CFG_FM_LJ;
+               break;
+       default:
+               goto out;
+       }
+
+       c &= ~(CFG_IC | CFG_ICK);               /* IB-IF */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               c |= CFG_IC | CFG_ICK;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               c |= CFG_IC;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               c |= CFG_ICK;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               break;
+       default:
+               goto out;
+       }
+
+       /* I2S controller only supports master */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:    /* CODEC slave */
+               break;
+       default:
+               goto out;
+       }
+
+       ret = 0;
+       ctx->cfg = c;
+out:
+       return ret;
+}
+
+static int au1xi2s_trigger(struct snd_pcm_substream *substream,
+                          int cmd, struct snd_soc_dai *dai)
+{
+       struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
+       int stype = SUBSTREAM_TYPE(substream);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               /* power up */
+               WR(ctx, I2S_ENABLE, EN_D | EN_CE);
+               WR(ctx, I2S_ENABLE, EN_CE);
+               ctx->cfg |= (stype == PCM_TX) ? CFG_TN : CFG_RN;
+               WR(ctx, I2S_CFG, ctx->cfg);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               ctx->cfg &= ~((stype == PCM_TX) ? CFG_TN : CFG_RN);
+               WR(ctx, I2S_CFG, ctx->cfg);
+               WR(ctx, I2S_ENABLE, EN_D);              /* power off */
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static unsigned long msbits_to_reg(int msbits)
+{
+       switch (msbits) {
+       case 8:
+               return CFG_SZ_8;
+       case 16:
+               return CFG_SZ_16;
+       case 18:
+               return CFG_SZ_18;
+       case 20:
+               return CFG_SZ_20;
+       case 24:
+               return CFG_SZ_24;
+       }
+       return 0;
+}
+
+static int au1xi2s_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params,
+                            struct snd_soc_dai *dai)
+{
+       struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
+       unsigned long v;
+
+       v = msbits_to_reg(params->msbits);
+       if (!v)
+               return -EINVAL;
+
+       ctx->cfg &= ~CFG_SZ_MASK;
+       ctx->cfg |= v;
+       return 0;
+}
+
+static int au1xi2s_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *dai)
+{
+       struct au1xpsc_audio_data *ctx = snd_soc_dai_get_drvdata(dai);
+       snd_soc_dai_set_dma_data(dai, substream, &ctx->dmaids[0]);
+       return 0;
+}
+
+static const struct snd_soc_dai_ops au1xi2s_dai_ops = {
+       .startup        = au1xi2s_startup,
+       .trigger        = au1xi2s_trigger,
+       .hw_params      = au1xi2s_hw_params,
+       .set_fmt        = au1xi2s_set_fmt,
+};
+
+static struct snd_soc_dai_driver au1xi2s_dai_driver = {
+       .symmetric_rates        = 1,
+       .playback = {
+               .rates          = AU1XI2SC_RATES,
+               .formats        = AU1XI2SC_FMTS,
+               .channels_min   = 2,
+               .channels_max   = 2,
+       },
+       .capture = {
+               .rates          = AU1XI2SC_RATES,
+               .formats        = AU1XI2SC_FMTS,
+               .channels_min   = 2,
+               .channels_max   = 2,
+       },
+       .ops = &au1xi2s_dai_ops,
+};
+
+static int __devinit au1xi2s_drvprobe(struct platform_device *pdev)
+{
+       int ret;
+       struct resource *r;
+       struct au1xpsc_audio_data *ctx;
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!r) {
+               ret = -ENODEV;
+               goto out0;
+       }
+
+       ret = -EBUSY;
+       if (!request_mem_region(r->start, resource_size(r), pdev->name))
+               goto out0;
+
+       ctx->mmio = ioremap_nocache(r->start, resource_size(r));
+       if (!ctx->mmio)
+               goto out1;
+
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!r)
+               goto out1;
+       ctx->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start;
+
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (!r)
+               goto out1;
+       ctx->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start;
+
+       platform_set_drvdata(pdev, ctx);
+
+       ret = snd_soc_register_dai(&pdev->dev, &au1xi2s_dai_driver);
+       if (ret)
+               goto out1;
+
+       return 0;
+
+out1:
+       release_mem_region(r->start, resource_size(r));
+out0:
+       kfree(ctx);
+       return ret;
+}
+
+static int __devexit au1xi2s_drvremove(struct platform_device *pdev)
+{
+       struct au1xpsc_audio_data *ctx = platform_get_drvdata(pdev);
+       struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       snd_soc_unregister_dai(&pdev->dev);
+
+       WR(ctx, I2S_ENABLE, EN_D);      /* clock off, disable */
+
+       iounmap(ctx->mmio);
+       release_mem_region(r->start, resource_size(r));
+       kfree(ctx);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int au1xi2s_drvsuspend(struct device *dev)
+{
+       struct au1xpsc_audio_data *ctx = dev_get_drvdata(dev);
+
+       WR(ctx, I2S_ENABLE, EN_D);      /* clock off, disable */
+
+       return 0;
+}
+
+static int au1xi2s_drvresume(struct device *dev)
+{
+       return 0;
+}
+
+static const struct dev_pm_ops au1xi2sc_pmops = {
+       .suspend        = au1xi2s_drvsuspend,
+       .resume         = au1xi2s_drvresume,
+};
+
+#define AU1XI2SC_PMOPS (&au1xi2sc_pmops)
+
+#else
+
+#define AU1XI2SC_PMOPS NULL
+
+#endif
+
+static struct platform_driver au1xi2s_driver = {
+       .driver = {
+               .name   = "alchemy-i2sc",
+               .owner  = THIS_MODULE,
+               .pm     = AU1XI2SC_PMOPS,
+       },
+       .probe          = au1xi2s_drvprobe,
+       .remove         = __devexit_p(au1xi2s_drvremove),
+};
+
+static int __init au1xi2s_load(void)
+{
+       return platform_driver_register(&au1xi2s_driver);
+}
+
+static void __exit au1xi2s_unload(void)
+{
+       platform_driver_unregister(&au1xi2s_driver);
+}
+
+module_init(au1xi2s_load);
+module_exit(au1xi2s_unload);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1000/1500/1100 I2S ASoC driver");
+MODULE_AUTHOR("Manuel Lauss");
index d0db66f..172eefd 100644 (file)
        (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE)
 
 #define AC97PCR_START(stype)   \
-       ((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
+       ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TS : PSC_AC97PCR_RS)
 #define AC97PCR_STOP(stype)    \
-       ((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
+       ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TP : PSC_AC97PCR_RP)
 #define AC97PCR_CLRFIFO(stype) \
-       ((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
+       ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97PCR_TC : PSC_AC97PCR_RC)
 
 #define AC97STAT_BUSY(stype)   \
-       ((stype) == PCM_TX ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
+       ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_AC97STAT_TB : PSC_AC97STAT_RB)
 
 /* instance data. There can be only one, MacLeod!!!! */
 static struct au1xpsc_audio_data *au1xpsc_ac97_workdata;
@@ -215,7 +215,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
 {
        struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
        unsigned long r, ro, stat;
-       int chans, t, stype = SUBSTREAM_TYPE(substream);
+       int chans, t, stype = substream->stream;
 
        chans = params_channels(params);
 
@@ -235,7 +235,7 @@ static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream,
                r |= PSC_AC97CFG_SET_LEN(params->msbits);
 
                /* channels: enable slots for front L/R channel */
-               if (stype == PCM_TX) {
+               if (stype == SNDRV_PCM_STREAM_PLAYBACK) {
                        r &= ~PSC_AC97CFG_TXSLOT_MASK;
                        r |= PSC_AC97CFG_TXSLOT_ENA(3);
                        r |= PSC_AC97CFG_TXSLOT_ENA(4);
@@ -294,7 +294,7 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
                                int cmd, struct snd_soc_dai *dai)
 {
        struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
-       int ret, stype = SUBSTREAM_TYPE(substream);
+       int ret, stype = substream->stream;
 
        ret = 0;
 
@@ -324,12 +324,21 @@ static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream,
        return ret;
 }
 
+static int au1xpsc_ac97_startup(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *dai)
+{
+       struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
+       snd_soc_dai_set_dma_data(dai, substream, &pscdata->dmaids[0]);
+       return 0;
+}
+
 static int au1xpsc_ac97_probe(struct snd_soc_dai *dai)
 {
        return au1xpsc_ac97_workdata ? 0 : -ENODEV;
 }
 
 static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
+       .startup        = au1xpsc_ac97_startup,
        .trigger        = au1xpsc_ac97_trigger,
        .hw_params      = au1xpsc_ac97_hw_params,
 };
@@ -379,6 +388,16 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
        if (!wd->mmio)
                goto out1;
 
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!r)
+               goto out2;
+       wd->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start;
+
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (!r)
+               goto out2;
+       wd->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start;
+
        /* configuration: max dma trigger threshold, enable ac97 */
        wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 |
                  PSC_AC97CFG_DE_ENABLE;
@@ -401,15 +420,13 @@ static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
 
        ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
        if (ret)
-               goto out1;
+               goto out2;
 
-       wd->dmapd = au1xpsc_pcm_add(pdev);
-       if (wd->dmapd) {
-               au1xpsc_ac97_workdata = wd;
-               return 0;
-       }
+       au1xpsc_ac97_workdata = wd;
+       return 0;
 
-       snd_soc_unregister_dai(&pdev->dev);
+out2:
+       iounmap(wd->mmio);
 out1:
        release_mem_region(r->start, resource_size(r));
 out0:
@@ -422,9 +439,6 @@ static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev)
        struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
        struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
-       if (wd->dmapd)
-               au1xpsc_pcm_destroy(wd->dmapd);
-
        snd_soc_unregister_dai(&pdev->dev);
 
        /* disable PSC completely */
index fca0912..7c5ae92 100644 (file)
        (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
 
 #define I2SSTAT_BUSY(stype)    \
-       ((stype) == PCM_TX ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
+       ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB)
 #define I2SPCR_START(stype)    \
-       ((stype) == PCM_TX ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
+       ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TS : PSC_I2SPCR_RS)
 #define I2SPCR_STOP(stype)     \
-       ((stype) == PCM_TX ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
+       ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TP : PSC_I2SPCR_RP)
 #define I2SPCR_CLRFIFO(stype)  \
-       ((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
+       ((stype) == SNDRV_PCM_STREAM_PLAYBACK ? PSC_I2SPCR_TC : PSC_I2SPCR_RC)
 
 
 static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
@@ -240,7 +240,7 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
                               struct snd_soc_dai *dai)
 {
        struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
-       int ret, stype = SUBSTREAM_TYPE(substream);
+       int ret, stype = substream->stream;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
@@ -257,7 +257,16 @@ static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
        return ret;
 }
 
+static int au1xpsc_i2s_startup(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct au1xpsc_audio_data *pscdata = snd_soc_dai_get_drvdata(dai);
+       snd_soc_dai_set_dma_data(dai, substream, &pscdata->dmaids[0]);
+       return 0;
+}
+
 static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
+       .startup        = au1xpsc_i2s_startup,
        .trigger        = au1xpsc_i2s_trigger,
        .hw_params      = au1xpsc_i2s_hw_params,
        .set_fmt        = au1xpsc_i2s_set_fmt,
@@ -304,6 +313,16 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
        if (!wd->mmio)
                goto out1;
 
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!r)
+               goto out2;
+       wd->dmaids[SNDRV_PCM_STREAM_PLAYBACK] = r->start;
+
+       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+       if (!r)
+               goto out2;
+       wd->dmaids[SNDRV_PCM_STREAM_CAPTURE] = r->start;
+
        /* preserve PSC clock source set up by platform (dev.platform_data
         * is already occupied by soc layer)
         */
@@ -330,15 +349,11 @@ static int __devinit au1xpsc_i2s_drvprobe(struct platform_device *pdev)
        platform_set_drvdata(pdev, wd);
 
        ret = snd_soc_register_dai(&pdev->dev, &wd->dai_drv);
-       if (ret)
-               goto out1;
-
-       /* finally add the DMA device for this PSC */
-       wd->dmapd = au1xpsc_pcm_add(pdev);
-       if (wd->dmapd)
+       if (!ret)
                return 0;
 
-       snd_soc_unregister_dai(&pdev->dev);
+out2:
+       iounmap(wd->mmio);
 out1:
        release_mem_region(r->start, resource_size(r));
 out0:
@@ -351,9 +366,6 @@ static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
        struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
        struct resource *r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
-       if (wd->dmapd)
-               au1xpsc_pcm_destroy(wd->dmapd);
-
        snd_soc_unregister_dai(&pdev->dev);
 
        au_writel(0, I2S_CFG(wd));
index b30eadd..b16b2e0 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Au12x0/Au1550 PSC ALSA ASoC audio support.
+ * Alchemy ALSA ASoC audio support.
  *
- * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
+ * (c) 2007-2011 MSC Vertriebsges.m.b.H.,
  *     Manuel Lauss <manuel.lauss@gmail.com>
  *
  * This program is free software; you can redistribute it and/or modify
 #ifndef _AU1X_PCM_H
 #define _AU1X_PCM_H
 
-/* DBDMA helpers */
-extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev);
-extern void au1xpsc_pcm_destroy(struct platform_device *dmapd);
-
 struct au1xpsc_audio_data {
        void __iomem *mmio;
 
@@ -27,15 +23,9 @@ struct au1xpsc_audio_data {
 
        unsigned long pm[2];
        struct mutex lock;
-       struct platform_device *dmapd;
+       int dmaids[2];
 };
 
-#define PCM_TX 0
-#define PCM_RX 1
-
-#define SUBSTREAM_TYPE(substream) \
-       ((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK ? PCM_TX : PCM_RX)
-
 /* easy access macros */
 #define PSC_CTRL(x)    ((unsigned long)((x)->mmio) + PSC_CTRL_OFFSET)
 #define PSC_SEL(x)     ((unsigned long)((x)->mmio) + PSC_SEL_OFFSET)
index fe9d548..9f6bc55 100644 (file)
@@ -27,6 +27,19 @@ config SND_SOC_BFIN_EVAL_ADAU1701
          board connected to one of the Blackfin evaluation boards like the
          BF5XX-STAMP or BF5XX-EZKIT.
 
+config SND_SOC_BFIN_EVAL_ADAU1373
+       tristate "Support for the EVAL-ADAU1373 board on Blackfin eval boards"
+       depends on SND_BF5XX_I2S && I2C
+       select SND_BF5XX_SOC_I2S
+       select SND_SOC_ADAU1373
+       help
+         Say Y if you want to add support for the Analog Devices EVAL-ADAU1373
+         board connected to one of the Blackfin evaluation boards like the
+         BF5XX-STAMP or BF5XX-EZKIT.
+
+         Note: This driver assumes that first ADAU1373 DAI is connected to the
+         first SPORT port on the BF5XX board.
+
 config SND_SOC_BFIN_EVAL_ADAV80X
        tristate "Support for the EVAL-ADAV80X boards on Blackfin eval boards"
        depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
index 6018bf5..1bf86cc 100644 (file)
@@ -21,6 +21,7 @@ snd-ad1980-objs := bf5xx-ad1980.o
 snd-ssm2602-objs := bf5xx-ssm2602.o
 snd-ad73311-objs := bf5xx-ad73311.o
 snd-ad193x-objs := bf5xx-ad193x.o
+snd-soc-bfin-eval-adau1373-objs := bfin-eval-adau1373.o
 snd-soc-bfin-eval-adau1701-objs := bfin-eval-adau1701.o
 snd-soc-bfin-eval-adav80x-objs := bfin-eval-adav80x.o
 
@@ -29,5 +30,6 @@ obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
 obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD193X) += snd-ad193x.o
+obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1373) += snd-soc-bfin-eval-adau1373.o
 obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAU1701) += snd-soc-bfin-eval-adau1701.o
 obj-$(CONFIG_SND_SOC_BFIN_EVAL_ADAV80X) += snd-soc-bfin-eval-adav80x.o
diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c
new file mode 100644 (file)
index 0000000..8df2a3b
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Machine driver for EVAL-ADAU1373 on Analog Devices bfin
+ * evaluation boards.
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "../codecs/adau1373.h"
+
+static const struct snd_soc_dapm_widget bfin_eval_adau1373_dapm_widgets[] = {
+       SND_SOC_DAPM_LINE("Line In1", NULL),
+       SND_SOC_DAPM_LINE("Line In2", NULL),
+       SND_SOC_DAPM_LINE("Line In3", NULL),
+       SND_SOC_DAPM_LINE("Line In4", NULL),
+
+       SND_SOC_DAPM_LINE("Line Out1", NULL),
+       SND_SOC_DAPM_LINE("Line Out2", NULL),
+       SND_SOC_DAPM_LINE("Stereo Out", NULL),
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_HP("Earpiece", NULL),
+       SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route bfin_eval_adau1373_dapm_routes[] = {
+       { "AIN1L", NULL, "Line In1" },
+       { "AIN1R", NULL, "Line In1" },
+       { "AIN2L", NULL, "Line In2" },
+       { "AIN2R", NULL, "Line In2" },
+       { "AIN3L", NULL, "Line In3" },
+       { "AIN3R", NULL, "Line In3" },
+       { "AIN4L", NULL, "Line In4" },
+       { "AIN4R", NULL, "Line In4" },
+
+       /* MICBIAS can be connected via a jumper to the line-in jack, since w
+          don't know which one is going to be used, just power both. */
+       { "Line In1", NULL, "MICBIAS1" },
+       { "Line In2", NULL, "MICBIAS1" },
+       { "Line In3", NULL, "MICBIAS1" },
+       { "Line In4", NULL, "MICBIAS1" },
+       { "Line In1", NULL, "MICBIAS2" },
+       { "Line In2", NULL, "MICBIAS2" },
+       { "Line In3", NULL, "MICBIAS2" },
+       { "Line In4", NULL, "MICBIAS2" },
+
+       { "Line Out1", NULL, "LOUT1L" },
+       { "Line Out1", NULL, "LOUT1R" },
+       { "Line Out2", NULL, "LOUT2L" },
+       { "Line Out2", NULL, "LOUT2R" },
+       { "Headphone", NULL, "HPL" },
+       { "Headphone", NULL, "HPR" },
+       { "Earpiece", NULL, "EP" },
+       { "Speaker", NULL, "SPKL" },
+       { "Stereo Out", NULL, "SPKR" },
+};
+
+static int bfin_eval_adau1373_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 *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+       int pll_rate;
+
+       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
+       if (ret)
+               return ret;
+
+       switch (params_rate(params)) {
+       case 48000:
+       case 8000:
+       case 12000:
+       case 16000:
+       case 24000:
+       case 32000:
+               pll_rate = 48000 * 1024;
+               break;
+       case 44100:
+       case 7350:
+       case 11025:
+       case 14700:
+       case 22050:
+       case 29400:
+               pll_rate = 44100 * 1024;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
+                       ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
+                       SND_SOC_CLOCK_IN);
+
+       return ret;
+}
+
+static int bfin_eval_adau1373_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       unsigned int pll_rate = 48000 * 1024;
+       int ret;
+
+       ret = snd_soc_dai_set_pll(codec_dai, ADAU1373_PLL1,
+                       ADAU1373_PLL_SRC_MCLK1, 12288000, pll_rate);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, ADAU1373_CLK_SRC_PLL1, pll_rate,
+                       SND_SOC_CLOCK_IN);
+
+       return ret;
+}
+static struct snd_soc_ops bfin_eval_adau1373_ops = {
+       .hw_params = bfin_eval_adau1373_hw_params,
+};
+
+static struct snd_soc_dai_link bfin_eval_adau1373_dai = {
+       .name = "adau1373",
+       .stream_name = "adau1373",
+       .cpu_dai_name = "bfin-i2s.0",
+       .codec_dai_name = "adau1373-aif1",
+       .platform_name = "bfin-i2s-pcm-audio",
+       .codec_name = "adau1373.0-001a",
+       .ops = &bfin_eval_adau1373_ops,
+       .init = bfin_eval_adau1373_codec_init,
+};
+
+static struct snd_soc_card bfin_eval_adau1373 = {
+       .name = "bfin-eval-adau1373",
+       .dai_link = &bfin_eval_adau1373_dai,
+       .num_links = 1,
+
+       .dapm_widgets           = bfin_eval_adau1373_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(bfin_eval_adau1373_dapm_widgets),
+       .dapm_routes            = bfin_eval_adau1373_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(bfin_eval_adau1373_dapm_routes),
+};
+
+static int bfin_eval_adau1373_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &bfin_eval_adau1373;
+
+       card->dev = &pdev->dev;
+
+       return snd_soc_register_card(&bfin_eval_adau1373);
+}
+
+static int __devexit bfin_eval_adau1373_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(card);
+
+       return 0;
+}
+
+static struct platform_driver bfin_eval_adau1373_driver = {
+       .driver = {
+               .name = "bfin-eval-adau1373",
+               .owner = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+       },
+       .probe = bfin_eval_adau1373_probe,
+       .remove = __devexit_p(bfin_eval_adau1373_remove),
+};
+
+static int __init bfin_eval_adau1373_init(void)
+{
+       return platform_driver_register(&bfin_eval_adau1373_driver);
+}
+module_init(bfin_eval_adau1373_init);
+
+static void __exit bfin_eval_adau1373_exit(void)
+{
+       platform_driver_unregister(&bfin_eval_adau1373_driver);
+}
+module_exit(bfin_eval_adau1373_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ALSA SoC bfin adau1373 driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:bfin-eval-adau1373");
index 665d924..71b46c8 100644 (file)
@@ -17,6 +17,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
        select SND_SOC_AD1980 if SND_SOC_AC97_BUS
        select SND_SOC_AD73311
+       select SND_SOC_ADAU1373 if I2C
        select SND_SOC_ADAV80X
        select SND_SOC_ADS117X
        select SND_SOC_AK4104 if SPI_MASTER
@@ -139,6 +140,9 @@ config SND_SOC_ADAU1701
        select SIGMA
        tristate
 
+config SND_SOC_ADAU1373
+       tristate
+
 config SND_SOC_ADAV80X
        tristate
 
index 5119a7e..70c1769 100644 (file)
@@ -5,6 +5,7 @@ snd-soc-ad193x-objs := ad193x.o
 snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-adau1701-objs := adau1701.o
+snd-soc-adau1373-objs := adau1373.o
 snd-soc-adav80x-objs := adav80x.o
 snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
@@ -100,6 +101,7 @@ obj-$(CONFIG_SND_SOC_AD1836)        += snd-soc-ad1836.o
 obj-$(CONFIG_SND_SOC_AD193X)   += snd-soc-ad193x.o
 obj-$(CONFIG_SND_SOC_AD1980)   += snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
 obj-$(CONFIG_SND_SOC_ADAU1701)  += snd-soc-adau1701.o
 obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
 obj-$(CONFIG_SND_SOC_ADS117X)  += snd-soc-ads117x.o
index eedb6f5..f934670 100644 (file)
@@ -23,7 +23,7 @@
 
 /* codec private data */
 struct ad193x_priv {
-       enum snd_soc_control_type control_type;
+       struct regmap *regmap;
        int sysclk;
 };
 
@@ -349,10 +349,8 @@ static int ad193x_probe(struct snd_soc_codec *codec)
        struct snd_soc_dapm_context *dapm = &codec->dapm;
        int ret;
 
-       if (ad193x->control_type == SND_SOC_I2C)
-               ret = snd_soc_codec_set_cache_io(codec, 8, 8, ad193x->control_type);
-       else
-               ret = snd_soc_codec_set_cache_io(codec, 16, 8, ad193x->control_type);
+       codec->control_data = ad193x->regmap;
+       ret = snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
        if (ret < 0) {
                dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
                return ret;
@@ -388,6 +386,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ad193x = {
 };
 
 #if defined(CONFIG_SPI_MASTER)
+
+static const struct regmap_config ad193x_spi_regmap_config = {
+       .val_bits = 8,
+       .reg_bits = 16,
+       .read_flag_mask = 0x09,
+       .write_flag_mask = 0x08,
+};
+
 static int __devinit ad193x_spi_probe(struct spi_device *spi)
 {
        struct ad193x_priv *ad193x;
@@ -397,20 +403,36 @@ static int __devinit ad193x_spi_probe(struct spi_device *spi)
        if (ad193x == NULL)
                return -ENOMEM;
 
+       ad193x->regmap = regmap_init_spi(spi, &ad193x_spi_regmap_config);
+       if (IS_ERR(ad193x->regmap)) {
+               ret = PTR_ERR(ad193x->regmap);
+               goto err_free;
+       }
+
        spi_set_drvdata(spi, ad193x);
-       ad193x->control_type = SND_SOC_SPI;
 
        ret = snd_soc_register_codec(&spi->dev,
                        &soc_codec_dev_ad193x, &ad193x_dai, 1);
        if (ret < 0)
-               kfree(ad193x);
+               goto err_regmap_exit;
+
+       return 0;
+
+err_regmap_exit:
+       regmap_exit(ad193x->regmap);
+err_free:
+       kfree(ad193x);
+
        return ret;
 }
 
 static int __devexit ad193x_spi_remove(struct spi_device *spi)
 {
+       struct ad193x_priv *ad193x = spi_get_drvdata(spi);
+
        snd_soc_unregister_codec(&spi->dev);
-       kfree(spi_get_drvdata(spi));
+       regmap_exit(ad193x->regmap);
+       kfree(ad193x);
        return 0;
 }
 
@@ -425,6 +447,12 @@ static struct spi_driver ad193x_spi_driver = {
 #endif
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+
+static const struct regmap_config ad193x_i2c_regmap_config = {
+       .val_bits = 8,
+       .reg_bits = 8,
+};
+
 static const struct i2c_device_id ad193x_id[] = {
        { "ad1936", 0 },
        { "ad1937", 0 },
@@ -442,20 +470,35 @@ static int __devinit ad193x_i2c_probe(struct i2c_client *client,
        if (ad193x == NULL)
                return -ENOMEM;
 
+       ad193x->regmap = regmap_init_i2c(client, &ad193x_i2c_regmap_config);
+       if (IS_ERR(ad193x->regmap)) {
+               ret = PTR_ERR(ad193x->regmap);
+               goto err_free;
+       }
+
        i2c_set_clientdata(client, ad193x);
-       ad193x->control_type = SND_SOC_I2C;
 
        ret =  snd_soc_register_codec(&client->dev,
                        &soc_codec_dev_ad193x, &ad193x_dai, 1);
        if (ret < 0)
-               kfree(ad193x);
+               goto err_regmap_exit;
+
+       return 0;
+
+err_regmap_exit:
+       regmap_exit(ad193x->regmap);
+err_free:
+       kfree(ad193x);
        return ret;
 }
 
 static int __devexit ad193x_i2c_remove(struct i2c_client *client)
 {
+       struct ad193x_priv *ad193x = i2c_get_clientdata(client);
+
        snd_soc_unregister_codec(&client->dev);
-       kfree(i2c_get_clientdata(client));
+       regmap_exit(ad193x->regmap);
+       kfree(ad193x);
        return 0;
 }
 
index cccc2e8..536e5f2 100644 (file)
@@ -9,20 +9,20 @@
 #ifndef __AD193X_H__
 #define __AD193X_H__
 
-#define AD193X_PLL_CLK_CTRL0    0x800
+#define AD193X_PLL_CLK_CTRL0    0x00
 #define AD193X_PLL_POWERDOWN           0x01
 #define AD193X_PLL_INPUT_MASK   (~0x6)
 #define AD193X_PLL_INPUT_256    (0 << 1)
 #define AD193X_PLL_INPUT_384    (1 << 1)
 #define AD193X_PLL_INPUT_512    (2 << 1)
 #define AD193X_PLL_INPUT_768    (3 << 1)
-#define AD193X_PLL_CLK_CTRL1    0x801
-#define AD193X_DAC_CTRL0        0x802
+#define AD193X_PLL_CLK_CTRL1    0x01
+#define AD193X_DAC_CTRL0        0x02
 #define AD193X_DAC_POWERDOWN           0x01
 #define AD193X_DAC_SERFMT_MASK         0xC0
 #define AD193X_DAC_SERFMT_STEREO       (0 << 6)
 #define AD193X_DAC_SERFMT_TDM          (1 << 6)
-#define AD193X_DAC_CTRL1        0x803
+#define AD193X_DAC_CTRL1        0x03
 #define AD193X_DAC_2_CHANNELS   0
 #define AD193X_DAC_4_CHANNELS   1
 #define AD193X_DAC_8_CHANNELS   2
 #define AD193X_DAC_BCLK_MASTER  (1 << 5)
 #define AD193X_DAC_LEFT_HIGH    (1 << 3)
 #define AD193X_DAC_BCLK_INV     (1 << 7)
-#define AD193X_DAC_CTRL2        0x804
+#define AD193X_DAC_CTRL2        0x04
 #define AD193X_DAC_WORD_LEN_SHFT        3
 #define AD193X_DAC_WORD_LEN_MASK        0x18
 #define AD193X_DAC_MASTER_MUTE  1
-#define AD193X_DAC_CHNL_MUTE    0x805
+#define AD193X_DAC_CHNL_MUTE    0x05
 #define AD193X_DACL1_MUTE       0
 #define AD193X_DACR1_MUTE       1
 #define AD193X_DACL2_MUTE       2
 #define AD193X_DACR3_MUTE       5
 #define AD193X_DACL4_MUTE       6
 #define AD193X_DACR4_MUTE       7
-#define AD193X_DAC_L1_VOL       0x806
-#define AD193X_DAC_R1_VOL       0x807
-#define AD193X_DAC_L2_VOL       0x808
-#define AD193X_DAC_R2_VOL       0x809
-#define AD193X_DAC_L3_VOL       0x80a
-#define AD193X_DAC_R3_VOL       0x80b
-#define AD193X_DAC_L4_VOL       0x80c
-#define AD193X_DAC_R4_VOL       0x80d
-#define AD193X_ADC_CTRL0        0x80e
+#define AD193X_DAC_L1_VOL       0x06
+#define AD193X_DAC_R1_VOL       0x07
+#define AD193X_DAC_L2_VOL       0x08
+#define AD193X_DAC_R2_VOL       0x09
+#define AD193X_DAC_L3_VOL       0x0a
+#define AD193X_DAC_R3_VOL       0x0b
+#define AD193X_DAC_L4_VOL       0x0c
+#define AD193X_DAC_R4_VOL       0x0d
+#define AD193X_ADC_CTRL0        0x0e
 #define AD193X_ADC_POWERDOWN           0x01
 #define AD193X_ADC_HIGHPASS_FILTER     1
 #define AD193X_ADCL1_MUTE              2
 #define AD193X_ADCR1_MUTE              3
 #define AD193X_ADCL2_MUTE              4
 #define AD193X_ADCR2_MUTE              5
-#define AD193X_ADC_CTRL1        0x80f
+#define AD193X_ADC_CTRL1        0x0f
 #define AD193X_ADC_SERFMT_MASK         0x60
 #define AD193X_ADC_SERFMT_STEREO       (0 << 5)
 #define AD193X_ADC_SERFMT_TDM          (1 << 5)
 #define AD193X_ADC_SERFMT_AUX          (2 << 5)
 #define AD193X_ADC_WORD_LEN_MASK       0x3
-#define AD193X_ADC_CTRL2        0x810
+#define AD193X_ADC_CTRL2        0x10
 #define AD193X_ADC_2_CHANNELS   0
 #define AD193X_ADC_4_CHANNELS   1
 #define AD193X_ADC_8_CHANNELS   2
index 923b364..4c0fc30 100644 (file)
@@ -200,18 +200,22 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
        }
 
        /* Read out vendor ID to make sure it is ad1980 */
-       if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144)
+       if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) {
+               ret = -ENODEV;
                goto reset_err;
+       }
 
        vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
 
        if (vendor_id2 != 0x5370) {
-               if (vendor_id2 != 0x5374)
+               if (vendor_id2 != 0x5374) {
+                       ret = -ENODEV;
                        goto reset_err;
-               else
+               } else {
                        printk(KERN_WARNING "ad1980: "
                                "Found AD1981 - only 2/2 IN/OUT Channels "
                                "supported\n");
+               }
        }
 
        /* unmute captures and playbacks volume */
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
new file mode 100644 (file)
index 0000000..2aa40c3
--- /dev/null
@@ -0,0 +1,1414 @@
+/*
+ * Analog Devices ADAU1373 Audio Codec drive
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/gcd.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/adau1373.h>
+
+#include "adau1373.h"
+
+struct adau1373_dai {
+       unsigned int clk_src;
+       unsigned int sysclk;
+       bool enable_src;
+       bool master;
+};
+
+struct adau1373 {
+       struct adau1373_dai dais[3];
+};
+
+#define ADAU1373_INPUT_MODE    0x00
+#define ADAU1373_AINL_CTRL(x)  (0x01 + (x) * 2)
+#define ADAU1373_AINR_CTRL(x)  (0x02 + (x) * 2)
+#define ADAU1373_LLINE_OUT(x)  (0x9 + (x) * 2)
+#define ADAU1373_RLINE_OUT(x)  (0xa + (x) * 2)
+#define ADAU1373_LSPK_OUT      0x0d
+#define ADAU1373_RSPK_OUT      0x0e
+#define ADAU1373_LHP_OUT       0x0f
+#define ADAU1373_RHP_OUT       0x10
+#define ADAU1373_ADC_GAIN      0x11
+#define ADAU1373_LADC_MIXER    0x12
+#define ADAU1373_RADC_MIXER    0x13
+#define ADAU1373_LLINE1_MIX    0x14
+#define ADAU1373_RLINE1_MIX    0x15
+#define ADAU1373_LLINE2_MIX    0x16
+#define ADAU1373_RLINE2_MIX    0x17
+#define ADAU1373_LSPK_MIX      0x18
+#define ADAU1373_RSPK_MIX      0x19
+#define ADAU1373_LHP_MIX       0x1a
+#define ADAU1373_RHP_MIX       0x1b
+#define ADAU1373_EP_MIX                0x1c
+#define ADAU1373_HP_CTRL       0x1d
+#define ADAU1373_HP_CTRL2      0x1e
+#define ADAU1373_LS_CTRL       0x1f
+#define ADAU1373_EP_CTRL       0x21
+#define ADAU1373_MICBIAS_CTRL1 0x22
+#define ADAU1373_MICBIAS_CTRL2 0x23
+#define ADAU1373_OUTPUT_CTRL   0x24
+#define ADAU1373_PWDN_CTRL1    0x25
+#define ADAU1373_PWDN_CTRL2    0x26
+#define ADAU1373_PWDN_CTRL3    0x27
+#define ADAU1373_DPLL_CTRL(x)  (0x28 + (x) * 7)
+#define ADAU1373_PLL_CTRL1(x)  (0x29 + (x) * 7)
+#define ADAU1373_PLL_CTRL2(x)  (0x2a + (x) * 7)
+#define ADAU1373_PLL_CTRL3(x)  (0x2b + (x) * 7)
+#define ADAU1373_PLL_CTRL4(x)  (0x2c + (x) * 7)
+#define ADAU1373_PLL_CTRL5(x)  (0x2d + (x) * 7)
+#define ADAU1373_PLL_CTRL6(x)  (0x2e + (x) * 7)
+#define ADAU1373_PLL_CTRL7(x)  (0x2f + (x) * 7)
+#define ADAU1373_HEADDECT      0x36
+#define ADAU1373_ADC_DAC_STATUS        0x37
+#define ADAU1373_ADC_CTRL      0x3c
+#define ADAU1373_DAI(x)                (0x44 + (x))
+#define ADAU1373_CLK_SRC_DIV(x)        (0x40 + (x) * 2)
+#define ADAU1373_BCLKDIV(x)    (0x47 + (x))
+#define ADAU1373_SRC_RATIOA(x) (0x4a + (x) * 2)
+#define ADAU1373_SRC_RATIOB(x) (0x4b + (x) * 2)
+#define ADAU1373_DEEMP_CTRL    0x50
+#define ADAU1373_SRC_DAI_CTRL(x) (0x51 + (x))
+#define ADAU1373_DIN_MIX_CTRL(x) (0x56 + (x))
+#define ADAU1373_DOUT_MIX_CTRL(x) (0x5b + (x))
+#define ADAU1373_DAI_PBL_VOL(x)        (0x62 + (x) * 2)
+#define ADAU1373_DAI_PBR_VOL(x)        (0x63 + (x) * 2)
+#define ADAU1373_DAI_RECL_VOL(x) (0x68 + (x) * 2)
+#define ADAU1373_DAI_RECR_VOL(x) (0x69 + (x) * 2)
+#define ADAU1373_DAC1_PBL_VOL  0x6e
+#define ADAU1373_DAC1_PBR_VOL  0x6f
+#define ADAU1373_DAC2_PBL_VOL  0x70
+#define ADAU1373_DAC2_PBR_VOL  0x71
+#define ADAU1373_ADC_RECL_VOL  0x72
+#define ADAU1373_ADC_RECR_VOL  0x73
+#define ADAU1373_DMIC_RECL_VOL 0x74
+#define ADAU1373_DMIC_RECR_VOL 0x75
+#define ADAU1373_VOL_GAIN1     0x76
+#define ADAU1373_VOL_GAIN2     0x77
+#define ADAU1373_VOL_GAIN3     0x78
+#define ADAU1373_HPF_CTRL      0x7d
+#define ADAU1373_BASS1         0x7e
+#define ADAU1373_BASS2         0x7f
+#define ADAU1373_DRC(x)                (0x80 + (x) * 0x10)
+#define ADAU1373_3D_CTRL1      0xc0
+#define ADAU1373_3D_CTRL2      0xc1
+#define ADAU1373_FDSP_SEL1     0xdc
+#define ADAU1373_FDSP_SEL2     0xdd
+#define ADAU1373_FDSP_SEL3     0xde
+#define ADAU1373_FDSP_SEL4     0xdf
+#define ADAU1373_DIGMICCTRL    0xe2
+#define ADAU1373_DIGEN         0xeb
+#define ADAU1373_SOFT_RESET    0xff
+
+
+#define ADAU1373_PLL_CTRL6_DPLL_BYPASS BIT(1)
+#define ADAU1373_PLL_CTRL6_PLL_EN      BIT(0)
+
+#define ADAU1373_DAI_INVERT_BCLK       BIT(7)
+#define ADAU1373_DAI_MASTER            BIT(6)
+#define ADAU1373_DAI_INVERT_LRCLK      BIT(4)
+#define ADAU1373_DAI_WLEN_16           0x0
+#define ADAU1373_DAI_WLEN_20           0x4
+#define ADAU1373_DAI_WLEN_24           0x8
+#define ADAU1373_DAI_WLEN_32           0xc
+#define ADAU1373_DAI_WLEN_MASK         0xc
+#define ADAU1373_DAI_FORMAT_RIGHT_J    0x0
+#define ADAU1373_DAI_FORMAT_LEFT_J     0x1
+#define ADAU1373_DAI_FORMAT_I2S                0x2
+#define ADAU1373_DAI_FORMAT_DSP                0x3
+
+#define ADAU1373_BCLKDIV_SOURCE                BIT(5)
+#define ADAU1373_BCLKDIV_32            0x03
+#define ADAU1373_BCLKDIV_64            0x02
+#define ADAU1373_BCLKDIV_128           0x01
+#define ADAU1373_BCLKDIV_256           0x00
+
+#define ADAU1373_ADC_CTRL_PEAK_DETECT  BIT(0)
+#define ADAU1373_ADC_CTRL_RESET                BIT(1)
+#define ADAU1373_ADC_CTRL_RESET_FORCE  BIT(2)
+
+#define ADAU1373_OUTPUT_CTRL_LDIFF     BIT(3)
+#define ADAU1373_OUTPUT_CTRL_LNFBEN    BIT(2)
+
+#define ADAU1373_PWDN_CTRL3_PWR_EN BIT(0)
+
+#define ADAU1373_EP_CTRL_MICBIAS1_OFFSET 4
+#define ADAU1373_EP_CTRL_MICBIAS2_OFFSET 2
+
+static const uint8_t adau1373_default_regs[] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x00 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x10 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x20 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 0x30 */
+       0x00, 0x00, 0x00, 0x80, 0x00, 0x01, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x00, /* 0x40 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, /* 0x50 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0x80 */
+       0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+       0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0x90 */
+       0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+       0x78, 0x18, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, /* 0xa0 */
+       0x00, 0xc0, 0x88, 0x7a, 0xdf, 0x20, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0xb0 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0 */
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, /* 0xe0 */
+       0x00, 0x1f, 0x0f, 0x00, 0x00,
+};
+
+static const unsigned int adau1373_out_tlv[] = {
+       TLV_DB_RANGE_HEAD(4),
+       0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
+       8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
+       16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
+       24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
+};
+
+static const DECLARE_TLV_DB_MINMAX(adau1373_digital_tlv, -9563, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_in_pga_tlv, -1300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(adau1373_ep_tlv, -600, 600, 1);
+
+static const DECLARE_TLV_DB_SCALE(adau1373_input_boost_tlv, 0, 2000, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_gain_boost_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adau1373_speaker_boost_tlv, 1200, 600, 0);
+
+static const char *adau1373_fdsp_sel_text[] = {
+       "None",
+       "Channel 1",
+       "Channel 2",
+       "Channel 3",
+       "Channel 4",
+       "Channel 5",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc1_channel_enum,
+       ADAU1373_FDSP_SEL1, 4, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc2_channel_enum,
+       ADAU1373_FDSP_SEL1, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_drc3_channel_enum,
+       ADAU1373_FDSP_SEL2, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_channel_enum,
+       ADAU1373_FDSP_SEL3, 0, adau1373_fdsp_sel_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_channel_enum,
+       ADAU1373_FDSP_SEL4, 4, adau1373_fdsp_sel_text);
+
+static const char *adau1373_hpf_cutoff_text[] = {
+       "3.7Hz", "50Hz", "100Hz", "150Hz", "200Hz", "250Hz", "300Hz", "350Hz",
+       "400Hz", "450Hz", "500Hz", "550Hz", "600Hz", "650Hz", "700Hz", "750Hz",
+       "800Hz",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_hpf_cutoff_enum,
+       ADAU1373_HPF_CTRL, 3, adau1373_hpf_cutoff_text);
+
+static const char *adau1373_bass_lpf_cutoff_text[] = {
+       "801Hz", "1001Hz",
+};
+
+static const char *adau1373_bass_clip_level_text[] = {
+       "0.125", "0.250", "0.370", "0.500", "0.625", "0.750", "0.875",
+};
+
+static const unsigned int adau1373_bass_clip_level_values[] = {
+       1, 2, 3, 4, 5, 6, 7,
+};
+
+static const char *adau1373_bass_hpf_cutoff_text[] = {
+       "158Hz", "232Hz", "347Hz", "520Hz",
+};
+
+static const unsigned int adau1373_bass_tlv[] = {
+       TLV_DB_RANGE_HEAD(4),
+       0, 2, TLV_DB_SCALE_ITEM(-600, 600, 1),
+       3, 4, TLV_DB_SCALE_ITEM(950, 250, 0),
+       5, 7, TLV_DB_SCALE_ITEM(1400, 150, 0),
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_lpf_cutoff_enum,
+       ADAU1373_BASS1, 5, adau1373_bass_lpf_cutoff_text);
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(adau1373_bass_clip_level_enum,
+       ADAU1373_BASS1, 2, 7, adau1373_bass_clip_level_text,
+       adau1373_bass_clip_level_values);
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_bass_hpf_cutoff_enum,
+       ADAU1373_BASS1, 0, adau1373_bass_hpf_cutoff_text);
+
+static const char *adau1373_3d_level_text[] = {
+       "0%", "6.67%", "13.33%", "20%", "26.67%", "33.33%",
+       "40%", "46.67%", "53.33%", "60%", "66.67%", "73.33%",
+       "80%", "86.67", "99.33%", "100%"
+};
+
+static const char *adau1373_3d_cutoff_text[] = {
+       "No 3D", "0.03125 fs", "0.04583 fs", "0.075 fs", "0.11458 fs",
+       "0.16875 fs", "0.27083 fs"
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_3d_level_enum,
+       ADAU1373_3D_CTRL1, 4, adau1373_3d_level_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_3d_cutoff_enum,
+       ADAU1373_3D_CTRL1, 0, adau1373_3d_cutoff_text);
+
+static const unsigned int adau1373_3d_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+       1, 7, TLV_DB_LINEAR_ITEM(-1800, -120),
+};
+
+static const char *adau1373_lr_mux_text[] = {
+       "Mute",
+       "Right Channel (L+R)",
+       "Left Channel (L+R)",
+       "Stereo",
+};
+
+static const SOC_ENUM_SINGLE_DECL(adau1373_lineout1_lr_mux_enum,
+       ADAU1373_OUTPUT_CTRL, 4, adau1373_lr_mux_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_lineout2_lr_mux_enum,
+       ADAU1373_OUTPUT_CTRL, 6, adau1373_lr_mux_text);
+static const SOC_ENUM_SINGLE_DECL(adau1373_speaker_lr_mux_enum,
+       ADAU1373_LS_CTRL, 4, adau1373_lr_mux_text);
+
+static const struct snd_kcontrol_new adau1373_controls[] = {
+       SOC_DOUBLE_R_TLV("AIF1 Capture Volume", ADAU1373_DAI_RECL_VOL(0),
+               ADAU1373_DAI_RECR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+       SOC_DOUBLE_R_TLV("AIF2 Capture Volume", ADAU1373_DAI_RECL_VOL(1),
+               ADAU1373_DAI_RECR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+       SOC_DOUBLE_R_TLV("AIF3 Capture Volume", ADAU1373_DAI_RECL_VOL(2),
+               ADAU1373_DAI_RECR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+       SOC_DOUBLE_R_TLV("ADC Capture Volume", ADAU1373_ADC_RECL_VOL,
+               ADAU1373_ADC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+       SOC_DOUBLE_R_TLV("DMIC Capture Volume", ADAU1373_DMIC_RECL_VOL,
+               ADAU1373_DMIC_RECR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+       SOC_DOUBLE_R_TLV("AIF1 Playback Volume", ADAU1373_DAI_PBL_VOL(0),
+               ADAU1373_DAI_PBR_VOL(0), 0, 0xff, 1, adau1373_digital_tlv),
+       SOC_DOUBLE_R_TLV("AIF2 Playback Volume", ADAU1373_DAI_PBL_VOL(1),
+               ADAU1373_DAI_PBR_VOL(1), 0, 0xff, 1, adau1373_digital_tlv),
+       SOC_DOUBLE_R_TLV("AIF3 Playback Volume", ADAU1373_DAI_PBL_VOL(2),
+               ADAU1373_DAI_PBR_VOL(2), 0, 0xff, 1, adau1373_digital_tlv),
+
+       SOC_DOUBLE_R_TLV("DAC1 Playback Volume", ADAU1373_DAC1_PBL_VOL,
+               ADAU1373_DAC1_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+       SOC_DOUBLE_R_TLV("DAC2 Playback Volume", ADAU1373_DAC2_PBL_VOL,
+               ADAU1373_DAC2_PBR_VOL, 0, 0xff, 1, adau1373_digital_tlv),
+
+       SOC_DOUBLE_R_TLV("Lineout1 Playback Volume", ADAU1373_LLINE_OUT(0),
+               ADAU1373_RLINE_OUT(0), 0, 0x1f, 0, adau1373_out_tlv),
+       SOC_DOUBLE_R_TLV("Speaker Playback Volume", ADAU1373_LSPK_OUT,
+               ADAU1373_RSPK_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+       SOC_DOUBLE_R_TLV("Headphone Playback Volume", ADAU1373_LHP_OUT,
+               ADAU1373_RHP_OUT, 0, 0x1f, 0, adau1373_out_tlv),
+
+       SOC_DOUBLE_R_TLV("Input 1 Capture Volume", ADAU1373_AINL_CTRL(0),
+               ADAU1373_AINR_CTRL(0), 0, 0x1f, 0, adau1373_in_pga_tlv),
+       SOC_DOUBLE_R_TLV("Input 2 Capture Volume", ADAU1373_AINL_CTRL(1),
+               ADAU1373_AINR_CTRL(1), 0, 0x1f, 0, adau1373_in_pga_tlv),
+       SOC_DOUBLE_R_TLV("Input 3 Capture Volume", ADAU1373_AINL_CTRL(2),
+               ADAU1373_AINR_CTRL(2), 0, 0x1f, 0, adau1373_in_pga_tlv),
+       SOC_DOUBLE_R_TLV("Input 4 Capture Volume", ADAU1373_AINL_CTRL(3),
+               ADAU1373_AINR_CTRL(3), 0, 0x1f, 0, adau1373_in_pga_tlv),
+
+       SOC_SINGLE_TLV("Earpiece Playback Volume", ADAU1373_EP_CTRL, 0, 3, 0,
+               adau1373_ep_tlv),
+
+       SOC_DOUBLE_TLV("AIF3 Boost Playback Volume", ADAU1373_VOL_GAIN1, 4, 5,
+               1, 0, adau1373_gain_boost_tlv),
+       SOC_DOUBLE_TLV("AIF2 Boost Playback Volume", ADAU1373_VOL_GAIN1, 2, 3,
+               1, 0, adau1373_gain_boost_tlv),
+       SOC_DOUBLE_TLV("AIF1 Boost Playback Volume", ADAU1373_VOL_GAIN1, 0, 1,
+               1, 0, adau1373_gain_boost_tlv),
+       SOC_DOUBLE_TLV("AIF3 Boost Capture Volume", ADAU1373_VOL_GAIN2, 4, 5,
+               1, 0, adau1373_gain_boost_tlv),
+       SOC_DOUBLE_TLV("AIF2 Boost Capture Volume", ADAU1373_VOL_GAIN2, 2, 3,
+               1, 0, adau1373_gain_boost_tlv),
+       SOC_DOUBLE_TLV("AIF1 Boost Capture Volume", ADAU1373_VOL_GAIN2, 0, 1,
+               1, 0, adau1373_gain_boost_tlv),
+       SOC_DOUBLE_TLV("DMIC Boost Capture Volume", ADAU1373_VOL_GAIN3, 6, 7,
+               1, 0, adau1373_gain_boost_tlv),
+       SOC_DOUBLE_TLV("ADC Boost Capture Volume", ADAU1373_VOL_GAIN3, 4, 5,
+               1, 0, adau1373_gain_boost_tlv),
+       SOC_DOUBLE_TLV("DAC2 Boost Playback Volume", ADAU1373_VOL_GAIN3, 2, 3,
+               1, 0, adau1373_gain_boost_tlv),
+       SOC_DOUBLE_TLV("DAC1 Boost Playback Volume", ADAU1373_VOL_GAIN3, 0, 1,
+               1, 0, adau1373_gain_boost_tlv),
+
+       SOC_DOUBLE_TLV("Input 1 Boost Capture Volume", ADAU1373_ADC_GAIN, 0, 4,
+               1, 0, adau1373_input_boost_tlv),
+       SOC_DOUBLE_TLV("Input 2 Boost Capture Volume", ADAU1373_ADC_GAIN, 1, 5,
+               1, 0, adau1373_input_boost_tlv),
+       SOC_DOUBLE_TLV("Input 3 Boost Capture Volume", ADAU1373_ADC_GAIN, 2, 6,
+               1, 0, adau1373_input_boost_tlv),
+       SOC_DOUBLE_TLV("Input 4 Boost Capture Volume", ADAU1373_ADC_GAIN, 3, 7,
+               1, 0, adau1373_input_boost_tlv),
+
+       SOC_DOUBLE_TLV("Speaker Boost Playback Volume", ADAU1373_LS_CTRL, 2, 3,
+               1, 0, adau1373_speaker_boost_tlv),
+
+       SOC_ENUM("Lineout1 LR Mux", adau1373_lineout1_lr_mux_enum),
+       SOC_ENUM("Speaker LR Mux", adau1373_speaker_lr_mux_enum),
+
+       SOC_ENUM("HPF Cutoff", adau1373_hpf_cutoff_enum),
+       SOC_DOUBLE("HPF Switch", ADAU1373_HPF_CTRL, 1, 0, 1, 0),
+       SOC_ENUM("HPF Channel", adau1373_hpf_channel_enum),
+
+       SOC_ENUM("Bass HPF Cutoff", adau1373_bass_hpf_cutoff_enum),
+       SOC_VALUE_ENUM("Bass Clip Level Threshold",
+           adau1373_bass_clip_level_enum),
+       SOC_ENUM("Bass LPF Cutoff", adau1373_bass_lpf_cutoff_enum),
+       SOC_DOUBLE("Bass Playback Switch", ADAU1373_BASS2, 0, 1, 1, 0),
+       SOC_SINGLE_TLV("Bass Playback Volume", ADAU1373_BASS2, 2, 7, 0,
+           adau1373_bass_tlv),
+       SOC_ENUM("Bass Channel", adau1373_bass_channel_enum),
+
+       SOC_ENUM("3D Freq", adau1373_3d_cutoff_enum),
+       SOC_ENUM("3D Level", adau1373_3d_level_enum),
+       SOC_SINGLE("3D Playback Switch", ADAU1373_3D_CTRL2, 0, 1, 0),
+       SOC_SINGLE_TLV("3D Playback Volume", ADAU1373_3D_CTRL2, 2, 7, 0,
+               adau1373_3d_tlv),
+       SOC_ENUM("3D Channel", adau1373_bass_channel_enum),
+
+       SOC_SINGLE("Zero Cross Switch", ADAU1373_PWDN_CTRL3, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_lineout2_controls[] = {
+       SOC_DOUBLE_R_TLV("Lineout2 Playback Volume", ADAU1373_LLINE_OUT(1),
+               ADAU1373_RLINE_OUT(1), 0, 0x1f, 0, adau1373_out_tlv),
+       SOC_ENUM("Lineout2 LR Mux", adau1373_lineout2_lr_mux_enum),
+};
+
+static const struct snd_kcontrol_new adau1373_drc_controls[] = {
+       SOC_ENUM("DRC1 Channel", adau1373_drc1_channel_enum),
+       SOC_ENUM("DRC2 Channel", adau1373_drc2_channel_enum),
+       SOC_ENUM("DRC3 Channel", adau1373_drc3_channel_enum),
+};
+
+static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       unsigned int pll_id = w->name[3] - '1';
+       unsigned int val;
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               val = ADAU1373_PLL_CTRL6_PLL_EN;
+       else
+               val = 0;
+
+       snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+               ADAU1373_PLL_CTRL6_PLL_EN, val);
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               mdelay(5);
+
+       return 0;
+}
+
+static const char *adau1373_decimator_text[] = {
+       "ADC",
+       "DMIC1",
+};
+
+static const struct soc_enum adau1373_decimator_enum =
+       SOC_ENUM_SINGLE(0, 0, 2, adau1373_decimator_text);
+
+static const struct snd_kcontrol_new adau1373_decimator_mux =
+       SOC_DAPM_ENUM_VIRT("Decimator Mux", adau1373_decimator_enum);
+
+static const struct snd_kcontrol_new adau1373_left_adc_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_LADC_MIXER, 4, 1, 0),
+       SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_LADC_MIXER, 3, 1, 0),
+       SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_LADC_MIXER, 2, 1, 0),
+       SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_LADC_MIXER, 1, 1, 0),
+       SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_LADC_MIXER, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_adc_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DAC1 Switch", ADAU1373_RADC_MIXER, 4, 1, 0),
+       SOC_DAPM_SINGLE("Input 4 Switch", ADAU1373_RADC_MIXER, 3, 1, 0),
+       SOC_DAPM_SINGLE("Input 3 Switch", ADAU1373_RADC_MIXER, 2, 1, 0),
+       SOC_DAPM_SINGLE("Input 2 Switch", ADAU1373_RADC_MIXER, 1, 1, 0),
+       SOC_DAPM_SINGLE("Input 1 Switch", ADAU1373_RADC_MIXER, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+       SOC_DAPM_SINGLE("Left DAC2 Switch", _reg, 7, 1, 0), \
+       SOC_DAPM_SINGLE("Right DAC2 Switch", _reg, 6, 1, 0), \
+       SOC_DAPM_SINGLE("Left DAC1 Switch", _reg, 5, 1, 0), \
+       SOC_DAPM_SINGLE("Right DAC1 Switch", _reg, 4, 1, 0), \
+       SOC_DAPM_SINGLE("Input 4 Bypass Switch", _reg, 3, 1, 0), \
+       SOC_DAPM_SINGLE("Input 3 Bypass Switch", _reg, 2, 1, 0), \
+       SOC_DAPM_SINGLE("Input 2 Bypass Switch", _reg, 1, 1, 0), \
+       SOC_DAPM_SINGLE("Input 1 Bypass Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line1_mixer_controls,
+       ADAU1373_LLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line1_mixer_controls,
+       ADAU1373_RLINE1_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_line2_mixer_controls,
+       ADAU1373_LLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_line2_mixer_controls,
+       ADAU1373_RLINE2_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_left_spk_mixer_controls,
+       ADAU1373_LSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_right_spk_mixer_controls,
+       ADAU1373_RSPK_MIX);
+static DECLARE_ADAU1373_OUTPUT_MIXER_CTRLS(adau1373_ep_mixer_controls,
+       ADAU1373_EP_MIX);
+
+static const struct snd_kcontrol_new adau1373_left_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC1 Switch", ADAU1373_LHP_MIX, 5, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC2 Switch", ADAU1373_LHP_MIX, 4, 1, 0),
+       SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_LHP_MIX, 3, 1, 0),
+       SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_LHP_MIX, 2, 1, 0),
+       SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_LHP_MIX, 1, 1, 0),
+       SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_LHP_MIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new adau1373_right_hp_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Right DAC1 Switch", ADAU1373_RHP_MIX, 5, 1, 0),
+       SOC_DAPM_SINGLE("Right DAC2 Switch", ADAU1373_RHP_MIX, 4, 1, 0),
+       SOC_DAPM_SINGLE("Input 4 Bypass Switch", ADAU1373_RHP_MIX, 3, 1, 0),
+       SOC_DAPM_SINGLE("Input 3 Bypass Switch", ADAU1373_RHP_MIX, 2, 1, 0),
+       SOC_DAPM_SINGLE("Input 2 Bypass Switch", ADAU1373_RHP_MIX, 1, 1, 0),
+       SOC_DAPM_SINGLE("Input 1 Bypass Switch", ADAU1373_RHP_MIX, 0, 1, 0),
+};
+
+#define DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+       SOC_DAPM_SINGLE("DMIC2 Swapped Switch", _reg, 6, 1, 0), \
+       SOC_DAPM_SINGLE("DMIC2 Switch", _reg, 5, 1, 0), \
+       SOC_DAPM_SINGLE("ADC/DMIC1 Swapped Switch", _reg, 4, 1, 0), \
+       SOC_DAPM_SINGLE("ADC/DMIC1 Switch", _reg, 3, 1, 0), \
+       SOC_DAPM_SINGLE("AIF3 Switch", _reg, 2, 1, 0), \
+       SOC_DAPM_SINGLE("AIF2 Switch", _reg, 1, 1, 0), \
+       SOC_DAPM_SINGLE("AIF1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel1_mixer_controls,
+       ADAU1373_DIN_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel2_mixer_controls,
+       ADAU1373_DIN_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel3_mixer_controls,
+       ADAU1373_DIN_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel4_mixer_controls,
+       ADAU1373_DIN_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_CHANNEL_MIXER_CTRLS(adau1373_dsp_channel5_mixer_controls,
+       ADAU1373_DIN_MIX_CTRL(4));
+
+#define DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(_name, _reg) \
+const struct snd_kcontrol_new _name[] = { \
+       SOC_DAPM_SINGLE("DSP Channel5 Switch", _reg, 4, 1, 0), \
+       SOC_DAPM_SINGLE("DSP Channel4 Switch", _reg, 3, 1, 0), \
+       SOC_DAPM_SINGLE("DSP Channel3 Switch", _reg, 2, 1, 0), \
+       SOC_DAPM_SINGLE("DSP Channel2 Switch", _reg, 1, 1, 0), \
+       SOC_DAPM_SINGLE("DSP Channel1 Switch", _reg, 0, 1, 0), \
+}
+
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif1_mixer_controls,
+       ADAU1373_DOUT_MIX_CTRL(0));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif2_mixer_controls,
+       ADAU1373_DOUT_MIX_CTRL(1));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_aif3_mixer_controls,
+       ADAU1373_DOUT_MIX_CTRL(2));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac1_mixer_controls,
+       ADAU1373_DOUT_MIX_CTRL(3));
+static DECLARE_ADAU1373_DSP_OUTPUT_MIXER_CTRLS(adau1373_dac2_mixer_controls,
+       ADAU1373_DOUT_MIX_CTRL(4));
+
+static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
+       /* Datasheet claims Left ADC is bit 6 and Right ADC is bit 7, but that
+        * doesn't seem to be the case. */
+       SND_SOC_DAPM_ADC("Left ADC", NULL, ADAU1373_PWDN_CTRL1, 7, 0),
+       SND_SOC_DAPM_ADC("Right ADC", NULL, ADAU1373_PWDN_CTRL1, 6, 0),
+
+       SND_SOC_DAPM_ADC("DMIC1", NULL, ADAU1373_DIGMICCTRL, 0, 0),
+       SND_SOC_DAPM_ADC("DMIC2", NULL, ADAU1373_DIGMICCTRL, 2, 0),
+
+       SND_SOC_DAPM_VIRT_MUX("Decimator Mux", SND_SOC_NOPM, 0, 0,
+               &adau1373_decimator_mux),
+
+       SND_SOC_DAPM_SUPPLY("MICBIAS2", ADAU1373_PWDN_CTRL1, 5, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("MICBIAS1", ADAU1373_PWDN_CTRL1, 4, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("IN4PGA", ADAU1373_PWDN_CTRL1, 3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IN3PGA", ADAU1373_PWDN_CTRL1, 2, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IN2PGA", ADAU1373_PWDN_CTRL1, 1, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IN1PGA", ADAU1373_PWDN_CTRL1, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_DAC("Left DAC2", NULL, ADAU1373_PWDN_CTRL2, 7, 0),
+       SND_SOC_DAPM_DAC("Right DAC2", NULL, ADAU1373_PWDN_CTRL2, 6, 0),
+       SND_SOC_DAPM_DAC("Left DAC1", NULL, ADAU1373_PWDN_CTRL2, 5, 0),
+       SND_SOC_DAPM_DAC("Right DAC1", NULL, ADAU1373_PWDN_CTRL2, 4, 0),
+
+       SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_left_adc_mixer_controls),
+       SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_right_adc_mixer_controls),
+
+       SOC_MIXER_ARRAY("Left Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 3, 0,
+               adau1373_left_line2_mixer_controls),
+       SOC_MIXER_ARRAY("Right Lineout2 Mixer", ADAU1373_PWDN_CTRL2, 2, 0,
+               adau1373_right_line2_mixer_controls),
+       SOC_MIXER_ARRAY("Left Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 1, 0,
+               adau1373_left_line1_mixer_controls),
+       SOC_MIXER_ARRAY("Right Lineout1 Mixer", ADAU1373_PWDN_CTRL2, 0, 0,
+               adau1373_right_line1_mixer_controls),
+
+       SOC_MIXER_ARRAY("Earpiece Mixer", ADAU1373_PWDN_CTRL3, 4, 0,
+               adau1373_ep_mixer_controls),
+       SOC_MIXER_ARRAY("Left Speaker Mixer", ADAU1373_PWDN_CTRL3, 3, 0,
+               adau1373_left_spk_mixer_controls),
+       SOC_MIXER_ARRAY("Right Speaker Mixer", ADAU1373_PWDN_CTRL3, 2, 0,
+               adau1373_right_spk_mixer_controls),
+       SOC_MIXER_ARRAY("Left Headphone Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_left_hp_mixer_controls),
+       SOC_MIXER_ARRAY("Right Headphone Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_right_hp_mixer_controls),
+       SND_SOC_DAPM_SUPPLY("Headphone Enable", ADAU1373_PWDN_CTRL3, 1, 0,
+               NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("AIF1 CLK", ADAU1373_SRC_DAI_CTRL(0), 0, 0,
+           NULL, 0),
+       SND_SOC_DAPM_SUPPLY("AIF2 CLK", ADAU1373_SRC_DAI_CTRL(1), 0, 0,
+           NULL, 0),
+       SND_SOC_DAPM_SUPPLY("AIF3 CLK", ADAU1373_SRC_DAI_CTRL(2), 0, 0,
+           NULL, 0),
+       SND_SOC_DAPM_SUPPLY("AIF1 IN SRC", ADAU1373_SRC_DAI_CTRL(0), 2, 0,
+           NULL, 0),
+       SND_SOC_DAPM_SUPPLY("AIF1 OUT SRC", ADAU1373_SRC_DAI_CTRL(0), 1, 0,
+           NULL, 0),
+       SND_SOC_DAPM_SUPPLY("AIF2 IN SRC", ADAU1373_SRC_DAI_CTRL(1), 2, 0,
+           NULL, 0),
+       SND_SOC_DAPM_SUPPLY("AIF2 OUT SRC", ADAU1373_SRC_DAI_CTRL(1), 1, 0,
+           NULL, 0),
+       SND_SOC_DAPM_SUPPLY("AIF3 IN SRC", ADAU1373_SRC_DAI_CTRL(2), 2, 0,
+           NULL, 0),
+       SND_SOC_DAPM_SUPPLY("AIF3 OUT SRC", ADAU1373_SRC_DAI_CTRL(2), 1, 0,
+           NULL, 0),
+
+       SND_SOC_DAPM_AIF_IN("AIF1 IN", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF1 OUT", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("AIF2 IN", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF2 OUT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("AIF3 IN", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_OUT("AIF3 OUT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+       SOC_MIXER_ARRAY("DSP Channel1 Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_dsp_channel1_mixer_controls),
+       SOC_MIXER_ARRAY("DSP Channel2 Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_dsp_channel2_mixer_controls),
+       SOC_MIXER_ARRAY("DSP Channel3 Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_dsp_channel3_mixer_controls),
+       SOC_MIXER_ARRAY("DSP Channel4 Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_dsp_channel4_mixer_controls),
+       SOC_MIXER_ARRAY("DSP Channel5 Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_dsp_channel5_mixer_controls),
+
+       SOC_MIXER_ARRAY("AIF1 Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_aif1_mixer_controls),
+       SOC_MIXER_ARRAY("AIF2 Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_aif2_mixer_controls),
+       SOC_MIXER_ARRAY("AIF3 Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_aif3_mixer_controls),
+       SOC_MIXER_ARRAY("DAC1 Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_dac1_mixer_controls),
+       SOC_MIXER_ARRAY("DAC2 Mixer", SND_SOC_NOPM, 0, 0,
+               adau1373_dac2_mixer_controls),
+
+       SND_SOC_DAPM_SUPPLY("DSP", ADAU1373_DIGEN, 4, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Recording Engine B", ADAU1373_DIGEN, 3, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Recording Engine A", ADAU1373_DIGEN, 2, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Playback Engine B", ADAU1373_DIGEN, 1, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("Playback Engine A", ADAU1373_DIGEN, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("PLL1", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("PLL2", SND_SOC_NOPM, 0, 0, adau1373_pll_event,
+               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("SYSCLK1", ADAU1373_CLK_SRC_DIV(0), 7, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("SYSCLK2", ADAU1373_CLK_SRC_DIV(1), 7, 0, NULL, 0),
+
+       SND_SOC_DAPM_INPUT("AIN1L"),
+       SND_SOC_DAPM_INPUT("AIN1R"),
+       SND_SOC_DAPM_INPUT("AIN2L"),
+       SND_SOC_DAPM_INPUT("AIN2R"),
+       SND_SOC_DAPM_INPUT("AIN3L"),
+       SND_SOC_DAPM_INPUT("AIN3R"),
+       SND_SOC_DAPM_INPUT("AIN4L"),
+       SND_SOC_DAPM_INPUT("AIN4R"),
+
+       SND_SOC_DAPM_INPUT("DMIC1DAT"),
+       SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+       SND_SOC_DAPM_OUTPUT("LOUT1L"),
+       SND_SOC_DAPM_OUTPUT("LOUT1R"),
+       SND_SOC_DAPM_OUTPUT("LOUT2L"),
+       SND_SOC_DAPM_OUTPUT("LOUT2R"),
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("SPKL"),
+       SND_SOC_DAPM_OUTPUT("SPKR"),
+       SND_SOC_DAPM_OUTPUT("EP"),
+};
+
+static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
+       struct snd_soc_dapm_widget *sink)
+{
+       struct snd_soc_codec *codec = source->codec;
+       struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+       unsigned int dai;
+       const char *clk;
+
+       dai = sink->name[3] - '1';
+
+       if (!adau1373->dais[dai].master)
+               return 0;
+
+       if (adau1373->dais[dai].clk_src == ADAU1373_CLK_SRC_PLL1)
+               clk = "SYSCLK1";
+       else
+               clk = "SYSCLK2";
+
+       return strcmp(source->name, clk) == 0;
+}
+
+static int adau1373_check_src(struct snd_soc_dapm_widget *source,
+       struct snd_soc_dapm_widget *sink)
+{
+       struct snd_soc_codec *codec = source->codec;
+       struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+       unsigned int dai;
+
+       dai = sink->name[3] - '1';
+
+       return adau1373->dais[dai].enable_src;
+}
+
+#define DSP_CHANNEL_MIXER_ROUTES(_sink) \
+       { _sink, "DMIC2 Swapped Switch", "DMIC2" }, \
+       { _sink, "DMIC2 Switch", "DMIC2" }, \
+       { _sink, "ADC/DMIC1 Swapped Switch", "Decimator Mux" }, \
+       { _sink, "ADC/DMIC1 Switch", "Decimator Mux" }, \
+       { _sink, "AIF1 Switch", "AIF1 IN" }, \
+       { _sink, "AIF2 Switch", "AIF2 IN" }, \
+       { _sink, "AIF3 Switch", "AIF3 IN" }
+
+#define DSP_OUTPUT_MIXER_ROUTES(_sink) \
+       { _sink, "DSP Channel1 Switch", "DSP Channel1 Mixer" }, \
+       { _sink, "DSP Channel2 Switch", "DSP Channel2 Mixer" }, \
+       { _sink, "DSP Channel3 Switch", "DSP Channel3 Mixer" }, \
+       { _sink, "DSP Channel4 Switch", "DSP Channel4 Mixer" }, \
+       { _sink, "DSP Channel5 Switch", "DSP Channel5 Mixer" }
+
+#define LEFT_OUTPUT_MIXER_ROUTES(_sink) \
+       { _sink, "Right DAC2 Switch", "Right DAC2" }, \
+       { _sink, "Left DAC2 Switch", "Left DAC2" }, \
+       { _sink, "Right DAC1 Switch", "Right DAC1" }, \
+       { _sink, "Left DAC1 Switch", "Left DAC1" }, \
+       { _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+       { _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+       { _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+       { _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+#define RIGHT_OUTPUT_MIXER_ROUTES(_sink) \
+       { _sink, "Right DAC2 Switch", "Right DAC2" }, \
+       { _sink, "Left DAC2 Switch", "Left DAC2" }, \
+       { _sink, "Right DAC1 Switch", "Right DAC1" }, \
+       { _sink, "Left DAC1 Switch", "Left DAC1" }, \
+       { _sink, "Input 1 Bypass Switch", "IN1PGA" }, \
+       { _sink, "Input 2 Bypass Switch", "IN2PGA" }, \
+       { _sink, "Input 3 Bypass Switch", "IN3PGA" }, \
+       { _sink, "Input 4 Bypass Switch", "IN4PGA" }
+
+static const struct snd_soc_dapm_route adau1373_dapm_routes[] = {
+       { "Left ADC Mixer", "DAC1 Switch", "Left DAC1" },
+       { "Left ADC Mixer", "Input 1 Switch", "IN1PGA" },
+       { "Left ADC Mixer", "Input 2 Switch", "IN2PGA" },
+       { "Left ADC Mixer", "Input 3 Switch", "IN3PGA" },
+       { "Left ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+       { "Right ADC Mixer", "DAC1 Switch", "Right DAC1" },
+       { "Right ADC Mixer", "Input 1 Switch", "IN1PGA" },
+       { "Right ADC Mixer", "Input 2 Switch", "IN2PGA" },
+       { "Right ADC Mixer", "Input 3 Switch", "IN3PGA" },
+       { "Right ADC Mixer", "Input 4 Switch", "IN4PGA" },
+
+       { "Left ADC", NULL, "Left ADC Mixer" },
+       { "Right ADC", NULL, "Right ADC Mixer" },
+
+       { "Decimator Mux", "ADC", "Left ADC" },
+       { "Decimator Mux", "ADC", "Right ADC" },
+       { "Decimator Mux", "DMIC1", "DMIC1" },
+
+       DSP_CHANNEL_MIXER_ROUTES("DSP Channel1 Mixer"),
+       DSP_CHANNEL_MIXER_ROUTES("DSP Channel2 Mixer"),
+       DSP_CHANNEL_MIXER_ROUTES("DSP Channel3 Mixer"),
+       DSP_CHANNEL_MIXER_ROUTES("DSP Channel4 Mixer"),
+       DSP_CHANNEL_MIXER_ROUTES("DSP Channel5 Mixer"),
+
+       DSP_OUTPUT_MIXER_ROUTES("AIF1 Mixer"),
+       DSP_OUTPUT_MIXER_ROUTES("AIF2 Mixer"),
+       DSP_OUTPUT_MIXER_ROUTES("AIF3 Mixer"),
+       DSP_OUTPUT_MIXER_ROUTES("DAC1 Mixer"),
+       DSP_OUTPUT_MIXER_ROUTES("DAC2 Mixer"),
+
+       { "AIF1 OUT", NULL, "AIF1 Mixer" },
+       { "AIF2 OUT", NULL, "AIF2 Mixer" },
+       { "AIF3 OUT", NULL, "AIF3 Mixer" },
+       { "Left DAC1", NULL, "DAC1 Mixer" },
+       { "Right DAC1", NULL, "DAC1 Mixer" },
+       { "Left DAC2", NULL, "DAC2 Mixer" },
+       { "Right DAC2", NULL, "DAC2 Mixer" },
+
+       LEFT_OUTPUT_MIXER_ROUTES("Left Lineout1 Mixer"),
+       RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout1 Mixer"),
+       LEFT_OUTPUT_MIXER_ROUTES("Left Lineout2 Mixer"),
+       RIGHT_OUTPUT_MIXER_ROUTES("Right Lineout2 Mixer"),
+       LEFT_OUTPUT_MIXER_ROUTES("Left Speaker Mixer"),
+       RIGHT_OUTPUT_MIXER_ROUTES("Right Speaker Mixer"),
+
+       { "Left Headphone Mixer", "Left DAC2 Switch", "Left DAC2" },
+       { "Left Headphone Mixer", "Left DAC1 Switch", "Left DAC1" },
+       { "Left Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+       { "Left Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+       { "Left Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+       { "Left Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+       { "Right Headphone Mixer", "Right DAC2 Switch", "Right DAC2" },
+       { "Right Headphone Mixer", "Right DAC1 Switch", "Right DAC1" },
+       { "Right Headphone Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+       { "Right Headphone Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+       { "Right Headphone Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+       { "Right Headphone Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+       { "Left Headphone Mixer", NULL, "Headphone Enable" },
+       { "Right Headphone Mixer", NULL, "Headphone Enable" },
+
+       { "Earpiece Mixer", "Right DAC2 Switch", "Right DAC2" },
+       { "Earpiece Mixer", "Left DAC2 Switch", "Left DAC2" },
+       { "Earpiece Mixer", "Right DAC1 Switch", "Right DAC1" },
+       { "Earpiece Mixer", "Left DAC1 Switch", "Left DAC1" },
+       { "Earpiece Mixer", "Input 1 Bypass Switch", "IN1PGA" },
+       { "Earpiece Mixer", "Input 2 Bypass Switch", "IN2PGA" },
+       { "Earpiece Mixer", "Input 3 Bypass Switch", "IN3PGA" },
+       { "Earpiece Mixer", "Input 4 Bypass Switch", "IN4PGA" },
+
+       { "LOUT1L", NULL, "Left Lineout1 Mixer" },
+       { "LOUT1R", NULL, "Right Lineout1 Mixer" },
+       { "LOUT2L", NULL, "Left Lineout2 Mixer" },
+       { "LOUT2R", NULL, "Right Lineout2 Mixer" },
+       { "SPKL", NULL, "Left Speaker Mixer" },
+       { "SPKR", NULL, "Right Speaker Mixer" },
+       { "HPL", NULL, "Left Headphone Mixer" },
+       { "HPR", NULL, "Right Headphone Mixer" },
+       { "EP", NULL, "Earpiece Mixer" },
+
+       { "IN1PGA", NULL, "AIN1L" },
+       { "IN2PGA", NULL, "AIN2L" },
+       { "IN3PGA", NULL, "AIN3L" },
+       { "IN4PGA", NULL, "AIN4L" },
+       { "IN1PGA", NULL, "AIN1R" },
+       { "IN2PGA", NULL, "AIN2R" },
+       { "IN3PGA", NULL, "AIN3R" },
+       { "IN4PGA", NULL, "AIN4R" },
+
+       { "SYSCLK1", NULL, "PLL1" },
+       { "SYSCLK2", NULL, "PLL2" },
+
+       { "Left DAC1", NULL, "SYSCLK1" },
+       { "Right DAC1", NULL, "SYSCLK1" },
+       { "Left DAC2", NULL, "SYSCLK1" },
+       { "Right DAC2", NULL, "SYSCLK1" },
+       { "Left ADC", NULL, "SYSCLK1" },
+       { "Right ADC", NULL, "SYSCLK1" },
+
+       { "DSP", NULL, "SYSCLK1" },
+
+       { "AIF1 Mixer", NULL, "DSP" },
+       { "AIF2 Mixer", NULL, "DSP" },
+       { "AIF3 Mixer", NULL, "DSP" },
+       { "DAC1 Mixer", NULL, "DSP" },
+       { "DAC2 Mixer", NULL, "DSP" },
+       { "DAC1 Mixer", NULL, "Playback Engine A" },
+       { "DAC2 Mixer", NULL, "Playback Engine B" },
+       { "Left ADC Mixer", NULL, "Recording Engine A" },
+       { "Right ADC Mixer", NULL, "Recording Engine A" },
+
+       { "AIF1 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+       { "AIF2 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+       { "AIF3 CLK", NULL, "SYSCLK1", adau1373_check_aif_clk },
+       { "AIF1 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+       { "AIF2 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+       { "AIF3 CLK", NULL, "SYSCLK2", adau1373_check_aif_clk },
+
+       { "AIF1 IN", NULL, "AIF1 CLK" },
+       { "AIF1 OUT", NULL, "AIF1 CLK" },
+       { "AIF2 IN", NULL, "AIF2 CLK" },
+       { "AIF2 OUT", NULL, "AIF2 CLK" },
+       { "AIF3 IN", NULL, "AIF3 CLK" },
+       { "AIF3 OUT", NULL, "AIF3 CLK" },
+       { "AIF1 IN", NULL, "AIF1 IN SRC", adau1373_check_src },
+       { "AIF1 OUT", NULL, "AIF1 OUT SRC", adau1373_check_src },
+       { "AIF2 IN", NULL, "AIF2 IN SRC", adau1373_check_src },
+       { "AIF2 OUT", NULL, "AIF2 OUT SRC", adau1373_check_src },
+       { "AIF3 IN", NULL, "AIF3 IN SRC", adau1373_check_src },
+       { "AIF3 OUT", NULL, "AIF3 OUT SRC", adau1373_check_src },
+
+       { "DMIC1", NULL, "DMIC1DAT" },
+       { "DMIC1", NULL, "SYSCLK1" },
+       { "DMIC1", NULL, "Recording Engine A" },
+       { "DMIC2", NULL, "DMIC2DAT" },
+       { "DMIC2", NULL, "SYSCLK1" },
+       { "DMIC2", NULL, "Recording Engine B" },
+};
+
+static int adau1373_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 adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+       struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+       unsigned int div;
+       unsigned int freq;
+       unsigned int ctrl;
+
+       freq = adau1373_dai->sysclk;
+
+       if (freq % params_rate(params) != 0)
+               return -EINVAL;
+
+       switch (freq / params_rate(params)) {
+       case 1024: /* sysclk / 256 */
+               div = 0;
+               break;
+       case 1536: /* 2/3 sysclk / 256 */
+               div = 1;
+               break;
+       case 2048: /* 1/2 sysclk / 256 */
+               div = 2;
+               break;
+       case 3072: /* 1/3 sysclk / 256 */
+               div = 3;
+               break;
+       case 4096: /* 1/4 sysclk / 256 */
+               div = 4;
+               break;
+       case 6144: /* 1/6 sysclk / 256 */
+               div = 5;
+               break;
+       case 5632: /* 2/11 sysclk / 256 */
+               div = 6;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       adau1373_dai->enable_src = (div != 0);
+
+       snd_soc_update_bits(codec, ADAU1373_BCLKDIV(dai->id),
+               ~ADAU1373_BCLKDIV_SOURCE, (div << 2) | ADAU1373_BCLKDIV_64);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               ctrl = ADAU1373_DAI_WLEN_16;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               ctrl = ADAU1373_DAI_WLEN_20;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               ctrl = ADAU1373_DAI_WLEN_24;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               ctrl = ADAU1373_DAI_WLEN_32;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return snd_soc_update_bits(codec, ADAU1373_DAI(dai->id),
+                       ADAU1373_DAI_WLEN_MASK, ctrl);
+}
+
+static int adau1373_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
+       struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+       unsigned int ctrl;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               ctrl = ADAU1373_DAI_MASTER;
+               adau1373_dai->master = true;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               ctrl = 0;
+               adau1373_dai->master = true;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               ctrl |= ADAU1373_DAI_FORMAT_I2S;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               ctrl |= ADAU1373_DAI_FORMAT_LEFT_J;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               ctrl |= ADAU1373_DAI_FORMAT_RIGHT_J;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               ctrl |= ADAU1373_DAI_FORMAT_DSP;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               ctrl |= ADAU1373_DAI_INVERT_BCLK;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               ctrl |= ADAU1373_DAI_INVERT_LRCLK;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               ctrl |= ADAU1373_DAI_INVERT_LRCLK | ADAU1373_DAI_INVERT_BCLK;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, ADAU1373_DAI(dai->id),
+               ~ADAU1373_DAI_WLEN_MASK, ctrl);
+
+       return 0;
+}
+
+static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai,
+       int clk_id, unsigned int freq, int dir)
+{
+       struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(dai->codec);
+       struct adau1373_dai *adau1373_dai = &adau1373->dais[dai->id];
+
+       switch (clk_id) {
+       case ADAU1373_CLK_SRC_PLL1:
+       case ADAU1373_CLK_SRC_PLL2:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       adau1373_dai->sysclk = freq;
+       adau1373_dai->clk_src = clk_id;
+
+       snd_soc_update_bits(dai->codec, ADAU1373_BCLKDIV(dai->id),
+               ADAU1373_BCLKDIV_SOURCE, clk_id << 5);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops adau1373_dai_ops = {
+       .hw_params      = adau1373_hw_params,
+       .set_sysclk     = adau1373_set_dai_sysclk,
+       .set_fmt        = adau1373_set_dai_fmt,
+};
+
+#define ADAU1373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver adau1373_dai_driver[] = {
+       {
+               .id = 0,
+               .name = "adau1373-aif1",
+               .playback = {
+                       .stream_name = "AIF1 Playback",
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_48000,
+                       .formats = ADAU1373_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF1 Capture",
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_48000,
+                       .formats = ADAU1373_FORMATS,
+               },
+               .ops = &adau1373_dai_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .id = 1,
+               .name = "adau1373-aif2",
+               .playback = {
+                       .stream_name = "AIF2 Playback",
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_48000,
+                       .formats = ADAU1373_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF2 Capture",
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_48000,
+                       .formats = ADAU1373_FORMATS,
+               },
+               .ops = &adau1373_dai_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .id = 2,
+               .name = "adau1373-aif3",
+               .playback = {
+                       .stream_name = "AIF3 Playback",
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_48000,
+                       .formats = ADAU1373_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "AIF3 Capture",
+                       .channels_min = 2,
+                       .channels_max = 2,
+                       .rates = SNDRV_PCM_RATE_8000_48000,
+                       .formats = ADAU1373_FORMATS,
+               },
+               .ops = &adau1373_dai_ops,
+               .symmetric_rates = 1,
+       },
+};
+
+static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
+       int source, unsigned int freq_in, unsigned int freq_out)
+{
+       unsigned int dpll_div = 0;
+       unsigned int x, r, n, m, i, j, mode;
+
+       switch (pll_id) {
+       case ADAU1373_PLL1:
+       case ADAU1373_PLL2:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (source) {
+       case ADAU1373_PLL_SRC_BCLK1:
+       case ADAU1373_PLL_SRC_BCLK2:
+       case ADAU1373_PLL_SRC_BCLK3:
+       case ADAU1373_PLL_SRC_LRCLK1:
+       case ADAU1373_PLL_SRC_LRCLK2:
+       case ADAU1373_PLL_SRC_LRCLK3:
+       case ADAU1373_PLL_SRC_MCLK1:
+       case ADAU1373_PLL_SRC_MCLK2:
+       case ADAU1373_PLL_SRC_GPIO1:
+       case ADAU1373_PLL_SRC_GPIO2:
+       case ADAU1373_PLL_SRC_GPIO3:
+       case ADAU1373_PLL_SRC_GPIO4:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (freq_in < 7813 || freq_in > 27000000)
+               return -EINVAL;
+
+       if (freq_out < 45158000 || freq_out > 49152000)
+               return -EINVAL;
+
+       /* APLL input needs to be >= 8Mhz, so in case freq_in is less we use the
+        * DPLL to get it there. DPLL_out = (DPLL_in / div) * 1024 */
+       while (freq_in < 8000000) {
+               freq_in *= 2;
+               dpll_div++;
+       }
+
+       if (freq_out % freq_in != 0) {
+               /* fout = fin * (r + (n/m)) / x */
+               x = DIV_ROUND_UP(freq_in, 13500000);
+               freq_in /= x;
+               r = freq_out / freq_in;
+               i = freq_out % freq_in;
+               j = gcd(i, freq_in);
+               n = i / j;
+               m = freq_in / j;
+               x--;
+               mode = 1;
+       } else {
+               /* fout = fin / r */
+               r = freq_out / freq_in;
+               n = 0;
+               m = 0;
+               x = 0;
+               mode = 0;
+       }
+
+       if (r < 2 || r > 8 || x > 3 || m > 0xffff || n > 0xffff)
+               return -EINVAL;
+
+       if (dpll_div) {
+               dpll_div = 11 - dpll_div;
+               snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+                       ADAU1373_PLL_CTRL6_DPLL_BYPASS, 0);
+       } else {
+               snd_soc_update_bits(codec, ADAU1373_PLL_CTRL6(pll_id),
+                       ADAU1373_PLL_CTRL6_DPLL_BYPASS,
+                       ADAU1373_PLL_CTRL6_DPLL_BYPASS);
+       }
+
+       snd_soc_write(codec, ADAU1373_DPLL_CTRL(pll_id),
+               (source << 4) | dpll_div);
+       snd_soc_write(codec, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff);
+       snd_soc_write(codec, ADAU1373_PLL_CTRL2(pll_id), m & 0xff);
+       snd_soc_write(codec, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff);
+       snd_soc_write(codec, ADAU1373_PLL_CTRL4(pll_id), n & 0xff);
+       snd_soc_write(codec, ADAU1373_PLL_CTRL5(pll_id),
+               (r << 3) | (x << 1) | mode);
+
+       /* Set sysclk to pll_rate / 4 */
+       snd_soc_update_bits(codec, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09);
+
+       return 0;
+}
+
+static void adau1373_load_drc_settings(struct snd_soc_codec *codec,
+       unsigned int nr, uint8_t *drc)
+{
+       unsigned int i;
+
+       for (i = 0; i < ADAU1373_DRC_SIZE; ++i)
+               snd_soc_write(codec, ADAU1373_DRC(nr) + i, drc[i]);
+}
+
+static bool adau1373_valid_micbias(enum adau1373_micbias_voltage micbias)
+{
+       switch (micbias) {
+       case ADAU1373_MICBIAS_2_9V:
+       case ADAU1373_MICBIAS_2_2V:
+       case ADAU1373_MICBIAS_2_6V:
+       case ADAU1373_MICBIAS_1_8V:
+               return true;
+       default:
+               break;
+       }
+       return false;
+}
+
+static int adau1373_probe(struct snd_soc_codec *codec)
+{
+       struct adau1373_platform_data *pdata = codec->dev->platform_data;
+       bool lineout_differential = false;
+       unsigned int val;
+       int ret;
+       int i;
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+       if (ret) {
+               dev_err(codec->dev, "failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+
+       codec->dapm.idle_bias_off = true;
+
+       if (pdata) {
+               if (pdata->num_drc > ARRAY_SIZE(pdata->drc_setting))
+                       return -EINVAL;
+
+               if (!adau1373_valid_micbias(pdata->micbias1) ||
+                       !adau1373_valid_micbias(pdata->micbias2))
+                       return -EINVAL;
+
+               for (i = 0; i < pdata->num_drc; ++i) {
+                       adau1373_load_drc_settings(codec, i,
+                               pdata->drc_setting[i]);
+               }
+
+               snd_soc_add_controls(codec, adau1373_drc_controls,
+                       pdata->num_drc);
+
+               val = 0;
+               for (i = 0; i < 4; ++i) {
+                       if (pdata->input_differential[i])
+                               val |= BIT(i);
+               }
+               snd_soc_write(codec, ADAU1373_INPUT_MODE, val);
+
+               val = 0;
+               if (pdata->lineout_differential)
+                       val |= ADAU1373_OUTPUT_CTRL_LDIFF;
+               if (pdata->lineout_ground_sense)
+                       val |= ADAU1373_OUTPUT_CTRL_LNFBEN;
+               snd_soc_write(codec, ADAU1373_OUTPUT_CTRL, val);
+
+               lineout_differential = pdata->lineout_differential;
+
+               snd_soc_write(codec, ADAU1373_EP_CTRL,
+                       (pdata->micbias1 << ADAU1373_EP_CTRL_MICBIAS1_OFFSET) |
+                       (pdata->micbias2 << ADAU1373_EP_CTRL_MICBIAS2_OFFSET));
+       }
+
+       if (!lineout_differential) {
+               snd_soc_add_controls(codec, adau1373_lineout2_controls,
+                       ARRAY_SIZE(adau1373_lineout2_controls));
+       }
+
+       snd_soc_write(codec, ADAU1373_ADC_CTRL,
+           ADAU1373_ADC_CTRL_RESET_FORCE | ADAU1373_ADC_CTRL_PEAK_DETECT);
+
+       return 0;
+}
+
+static int adau1373_set_bias_level(struct snd_soc_codec *codec,
+       enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               snd_soc_update_bits(codec, ADAU1373_PWDN_CTRL3,
+                       ADAU1373_PWDN_CTRL3_PWR_EN, ADAU1373_PWDN_CTRL3_PWR_EN);
+               break;
+       case SND_SOC_BIAS_OFF:
+               snd_soc_update_bits(codec, ADAU1373_PWDN_CTRL3,
+                       ADAU1373_PWDN_CTRL3_PWR_EN, 0);
+               break;
+       }
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+static int adau1373_remove(struct snd_soc_codec *codec)
+{
+       adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int adau1373_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       return adau1373_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int adau1373_resume(struct snd_soc_codec *codec)
+{
+       adau1373_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       snd_soc_cache_sync(codec);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver adau1373_codec_driver = {
+       .probe =        adau1373_probe,
+       .remove =       adau1373_remove,
+       .suspend =      adau1373_suspend,
+       .resume =       adau1373_resume,
+       .set_bias_level = adau1373_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(adau1373_default_regs),
+       .reg_cache_default = adau1373_default_regs,
+       .reg_word_size = sizeof(uint8_t),
+
+       .set_pll = adau1373_set_pll,
+
+       .controls = adau1373_controls,
+       .num_controls = ARRAY_SIZE(adau1373_controls),
+       .dapm_widgets = adau1373_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(adau1373_dapm_widgets),
+       .dapm_routes = adau1373_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(adau1373_dapm_routes),
+};
+
+static int __devinit adau1373_i2c_probe(struct i2c_client *client,
+       const struct i2c_device_id *id)
+{
+       struct adau1373 *adau1373;
+       int ret;
+
+       adau1373 = kzalloc(sizeof(*adau1373), GFP_KERNEL);
+       if (!adau1373)
+               return -ENOMEM;
+
+       dev_set_drvdata(&client->dev, adau1373);
+
+       ret = snd_soc_register_codec(&client->dev, &adau1373_codec_driver,
+                       adau1373_dai_driver, ARRAY_SIZE(adau1373_dai_driver));
+       if (ret < 0)
+               kfree(adau1373);
+
+       return ret;
+}
+
+static int __devexit adau1373_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       kfree(dev_get_drvdata(&client->dev));
+       return 0;
+}
+
+static const struct i2c_device_id adau1373_i2c_id[] = {
+       { "adau1373", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id);
+
+static struct i2c_driver adau1373_i2c_driver = {
+       .driver = {
+               .name = "adau1373",
+               .owner = THIS_MODULE,
+       },
+       .probe = adau1373_i2c_probe,
+       .remove = __devexit_p(adau1373_i2c_remove),
+       .id_table = adau1373_i2c_id,
+};
+
+static int __init adau1373_init(void)
+{
+       return i2c_add_driver(&adau1373_i2c_driver);
+}
+module_init(adau1373_init);
+
+static void __exit adau1373_exit(void)
+{
+       i2c_del_driver(&adau1373_i2c_driver);
+}
+module_exit(adau1373_exit);
+
+MODULE_DESCRIPTION("ASoC ADAU1373 driver");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau1373.h b/sound/soc/codecs/adau1373.h
new file mode 100644 (file)
index 0000000..c6ab553
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __ADAU1373_H__
+#define __ADAU1373_H__
+
+enum adau1373_pll_src {
+       ADAU1373_PLL_SRC_MCLK1 = 0,
+       ADAU1373_PLL_SRC_BCLK1 = 1,
+       ADAU1373_PLL_SRC_BCLK2 = 2,
+       ADAU1373_PLL_SRC_BCLK3 = 3,
+       ADAU1373_PLL_SRC_LRCLK1 = 4,
+       ADAU1373_PLL_SRC_LRCLK2 = 5,
+       ADAU1373_PLL_SRC_LRCLK3 = 6,
+       ADAU1373_PLL_SRC_GPIO1 = 7,
+       ADAU1373_PLL_SRC_GPIO2 = 8,
+       ADAU1373_PLL_SRC_GPIO3 = 9,
+       ADAU1373_PLL_SRC_GPIO4 = 10,
+       ADAU1373_PLL_SRC_MCLK2 = 11,
+};
+
+enum adau1373_pll {
+       ADAU1373_PLL1 = 0,
+       ADAU1373_PLL2 = 1,
+};
+
+enum adau1373_clk_src {
+       ADAU1373_CLK_SRC_PLL1 = 0,
+       ADAU1373_CLK_SRC_PLL2 = 1,
+};
+
+#endif
index 300c04b..f9f0894 100644 (file)
@@ -523,7 +523,8 @@ static int adav80x_hw_params(struct snd_pcm_substream *substream,
 }
 
 static int adav80x_set_sysclk(struct snd_soc_codec *codec,
-               int clk_id, unsigned int freq, int dir)
+                             int clk_id, int source,
+                             unsigned int freq, int dir)
 {
        struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
 
index eecffb5..0517315 100644 (file)
@@ -41,7 +41,6 @@ MODULE_PARM_DESC(caps_charge, "ALC5623 cap charge time (msecs)");
 struct alc5623_priv {
        enum snd_soc_control_type control_type;
        void *control_data;
-       struct mutex mutex;
        u8 id;
        unsigned int sysclk;
        u16 reg_cache[ALC5623_VENDOR_ID2+2];
@@ -1052,7 +1051,6 @@ static int alc5623_i2c_probe(struct i2c_client *client,
        i2c_set_clientdata(client, alc5623);
        alc5623->control_data = client;
        alc5623->control_type = SND_SOC_I2C;
-       mutex_init(&alc5623->mutex);
 
        ret =  snd_soc_register_codec(&client->dev,
                &soc_codec_device_alc5623, &alc5623_dai, 1);
index 7e4066e..91130fb 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 #include <linux/regulator/consumer.h>
+#include <linux/of_device.h>
 #include <sound/core.h>
 #include <sound/tlv.h>
 #include <sound/pcm.h>
@@ -1436,10 +1437,17 @@ static const struct i2c_device_id sgtl5000_id[] = {
 
 MODULE_DEVICE_TABLE(i2c, sgtl5000_id);
 
+static const struct of_device_id sgtl5000_dt_ids[] = {
+       { .compatible = "fsl,sgtl5000", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sgtl5000_dt_ids);
+
 static struct i2c_driver sgtl5000_i2c_driver = {
        .driver = {
                   .name = "sgtl5000",
                   .owner = THIS_MODULE,
+                  .of_match_table = sgtl5000_dt_ids,
                   },
        .probe = sgtl5000_i2c_probe,
        .remove = __devexit_p(sgtl5000_i2c_remove),
index 84ffdeb..29945b0 100644 (file)
@@ -79,7 +79,7 @@ static void configure_adc(struct snd_soc_codec *sn95031_codec, int val)
  */
 static int find_free_channel(struct snd_soc_codec *sn95031_codec)
 {
-       int ret = 0, i, value;
+       int i, value;
 
        /* check whether ADC is enabled */
        value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
@@ -91,12 +91,10 @@ static int find_free_channel(struct snd_soc_codec *sn95031_codec)
        for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) {
                value = snd_soc_read(sn95031_codec,
                                SN95031_ADC_CHNL_START_ADDR + i);
-               if (value & SN95031_STOPBIT_MASK) {
-                       ret = i;
+               if (value & SN95031_STOPBIT_MASK)
                        break;
-               }
        }
-       return (ret > SN95031_ADC_LOOP_MAX) ? (-EINVAL) : ret;
+       return (i == SN95031_ADC_CHANLS_MAX) ? (-EINVAL) : i;
 }
 
 /* Initialize the ADC for reading micbias values. Can sleep. */
@@ -660,7 +658,7 @@ static int sn95031_pcm_spkr_mute(struct snd_soc_dai *dai, int mute)
        return 0;
 }
 
-int sn95031_pcm_hw_params(struct snd_pcm_substream *substream,
+static int sn95031_pcm_hw_params(struct snd_pcm_substream *substream,
                struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
        unsigned int format, rate;
index 9801cd7..32d6c51 100644 (file)
@@ -294,7 +294,6 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_codec *codec = rtd->codec;
        struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
-       struct i2c_client *i2c = codec->control_data;
        struct snd_pcm_runtime *master_runtime;
 
        /* The DAI has shared clocks so if we already have a playback or
@@ -303,7 +302,7 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
         */
        if (ssm2602->master_substream) {
                master_runtime = ssm2602->master_substream->runtime;
-               dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n",
+               dev_dbg(codec->dev, "Constraining to %d bits at %dHz\n",
                        master_runtime->sample_bits,
                        master_runtime->rate);
 
index fbd7eb9..5c7def3 100644 (file)
@@ -524,13 +524,17 @@ static int sta32x_hw_params(struct snd_pcm_substream *substream,
        rate = params_rate(params);
        pr_debug("rate: %u\n", rate);
        for (i = 0; i < ARRAY_SIZE(interpolation_ratios); i++)
-               if (interpolation_ratios[i].fs == rate)
+               if (interpolation_ratios[i].fs == rate) {
                        ir = interpolation_ratios[i].ir;
+                       break;
+               }
        if (ir < 0)
                return -EINVAL;
        for (i = 0; mclk_ratios[ir][i].ratio; i++)
-               if (mclk_ratios[ir][i].ratio * rate == sta32x->mclk)
+               if (mclk_ratios[ir][i].ratio * rate == sta32x->mclk) {
                        mcs = mclk_ratios[ir][i].mcs;
+                       break;
+               }
        if (mcs < 0)
                return -EINVAL;
 
@@ -808,6 +812,7 @@ static int sta32x_remove(struct snd_soc_codec *codec)
 {
        struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
 
+       sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
        regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
        regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
 
@@ -867,18 +872,8 @@ static __devinit int sta32x_i2c_probe(struct i2c_client *i2c,
 static __devexit int sta32x_i2c_remove(struct i2c_client *client)
 {
        struct sta32x_priv *sta32x = i2c_get_clientdata(client);
-       struct snd_soc_codec *codec = sta32x->codec;
-
-       if (codec)
-               sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       regulator_bulk_free(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
-
-       if (codec) {
-               snd_soc_unregister_codec(&client->dev);
-               snd_soc_codec_set_drvdata(codec, NULL);
-       }
 
+       snd_soc_unregister_codec(&client->dev);
        kfree(sta32x);
        return 0;
 }
index faa5e9f..243d177 100644 (file)
@@ -1431,7 +1431,7 @@ static int dac33_soc_probe(struct snd_soc_codec *codec)
        /* Check if the IRQ number is valid and request it */
        if (dac33->irq >= 0) {
                ret = request_irq(dac33->irq, dac33_interrupt_handler,
-                                 IRQF_TRIGGER_RISING | IRQF_DISABLED,
+                                 IRQF_TRIGGER_RISING,
                                  codec->name, codec);
                if (ret < 0) {
                        dev_err(codec->dev, "Could not request IRQ%d (%d)\n",
index 239e0c4..b2572c4 100644 (file)
@@ -446,7 +446,6 @@ err_regulator:
                gpio_free(data->power_gpio);
 err_gpio:
        kfree(data);
-       i2c_set_clientdata(tpa6130a2_client, NULL);
        tpa6130a2_client = NULL;
 
        return ret;
index 443032b..81645c6 100644 (file)
@@ -118,8 +118,8 @@ static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = {
        0x4A, /* TWL6040_LPPLLDIV       0x09    */
        0x00, /* TWL6040_AMICBCTL       0x0A    */
        0x00, /* TWL6040_DMICBCTL       0x0B    */
-       0x18, /* TWL6040_MICLCTL        0x0C    - No input selected on Left Mic */
-       0x18, /* TWL6040_MICRCTL        0x0D    - No input selected on Right Mic */
+       0x00, /* TWL6040_MICLCTL        0x0C    */
+       0x00, /* TWL6040_MICRCTL        0x0D    */
        0x00, /* TWL6040_MICGAIN        0x0E    */
        0x1B, /* TWL6040_LINEGAIN       0x0F    */
        0x00, /* TWL6040_HSLCTL         0x10    */
@@ -155,41 +155,8 @@ static const u8 twl6040_reg[TWL6040_CACHEREGNUM] = {
        0x00, /* TWL6040_STATUS (ro)    0x2E    */
 };
 
-/*
- * twl6040 vio/gnd registers:
- * registers under vio/gnd supply can be accessed
- * before the power-up sequence, after NRESPWRON goes high
- */
-static const int twl6040_vio_reg[TWL6040_VIOREGNUM] = {
-       TWL6040_REG_ASICID,
-       TWL6040_REG_ASICREV,
-       TWL6040_REG_INTID,
-       TWL6040_REG_INTMR,
-       TWL6040_REG_NCPCTL,
-       TWL6040_REG_LDOCTL,
-       TWL6040_REG_AMICBCTL,
-       TWL6040_REG_DMICBCTL,
-       TWL6040_REG_HKCTL1,
-       TWL6040_REG_HKCTL2,
-       TWL6040_REG_GPOCTL,
-       TWL6040_REG_TRIM1,
-       TWL6040_REG_TRIM2,
-       TWL6040_REG_TRIM3,
-       TWL6040_REG_HSOTRIM,
-       TWL6040_REG_HFOTRIM,
-       TWL6040_REG_ACCCTL,
-       TWL6040_REG_STATUS,
-};
-
-/*
- * twl6040 vdd/vss registers:
- * registers under vdd/vss supplies can only be accessed
- * after the power-up sequence
- */
-static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = {
-       TWL6040_REG_HPPLLCTL,
-       TWL6040_REG_LPPLLCTL,
-       TWL6040_REG_LPPLLDIV,
+/* List of registers to be restored after power up */
+static const int twl6040_restore_list[] = {
        TWL6040_REG_MICLCTL,
        TWL6040_REG_MICRCTL,
        TWL6040_REG_MICGAIN,
@@ -202,12 +169,6 @@ static const int twl6040_vdd_reg[TWL6040_VDDREGNUM] = {
        TWL6040_REG_HFLGAIN,
        TWL6040_REG_HFRCTL,
        TWL6040_REG_HFRGAIN,
-       TWL6040_REG_VIBCTLL,
-       TWL6040_REG_VIBDATL,
-       TWL6040_REG_VIBCTLR,
-       TWL6040_REG_VIBDATR,
-       TWL6040_REG_ALB,
-       TWL6040_REG_DLB,
 };
 
 /* set of rates for each pll: low-power and high-performance */
@@ -296,56 +257,27 @@ static int twl6040_write(struct snd_soc_codec *codec,
        return twl6040_reg_write(twl6040, reg, value);
 }
 
-static void twl6040_init_vio_regs(struct snd_soc_codec *codec)
+static void twl6040_init_chip(struct snd_soc_codec *codec)
 {
-       u8 *cache = codec->reg_cache;
-       int reg, i;
+       struct twl6040 *twl6040 = codec->control_data;
+       u8 val;
 
-       for (i = 0; i < TWL6040_VIOREGNUM; i++) {
-               reg = twl6040_vio_reg[i];
-               /*
-                * skip read-only registers (ASICID, ASICREV, STATUS)
-                * and registers shared among MFD children
-                */
-               switch (reg) {
-               case TWL6040_REG_ASICID:
-               case TWL6040_REG_ASICREV:
-               case TWL6040_REG_INTID:
-               case TWL6040_REG_INTMR:
-               case TWL6040_REG_NCPCTL:
-               case TWL6040_REG_LDOCTL:
-               case TWL6040_REG_GPOCTL:
-               case TWL6040_REG_ACCCTL:
-               case TWL6040_REG_STATUS:
-                       continue;
-               default:
-                       break;
-               }
-               twl6040_write(codec, reg, cache[reg]);
-       }
+       val = twl6040_get_revid(twl6040);
+       twl6040_write_reg_cache(codec, TWL6040_REG_ASICREV, val);
+
+       /* Change chip defaults */
+       /* No imput selected for microphone amplifiers */
+       twl6040_write_reg_cache(codec, TWL6040_REG_MICLCTL, 0x18);
+       twl6040_write_reg_cache(codec, TWL6040_REG_MICRCTL, 0x18);
 }
 
-static void twl6040_init_vdd_regs(struct snd_soc_codec *codec)
+static void twl6040_restore_regs(struct snd_soc_codec *codec)
 {
        u8 *cache = codec->reg_cache;
        int reg, i;
 
-       for (i = 0; i < TWL6040_VDDREGNUM; i++) {
-               reg = twl6040_vdd_reg[i];
-               /* skip vibra and PLL registers */
-               switch (reg) {
-               case TWL6040_REG_VIBCTLL:
-               case TWL6040_REG_VIBDATL:
-               case TWL6040_REG_VIBCTLR:
-               case TWL6040_REG_VIBDATR:
-               case TWL6040_REG_HPPLLCTL:
-               case TWL6040_REG_LPPLLCTL:
-               case TWL6040_REG_LPPLLDIV:
-                       continue;
-               default:
-                       break;
-               }
-
+       for (i = 0; i < ARRAY_SIZE(twl6040_restore_list); i++) {
+               reg = twl6040_restore_list[i];
                twl6040_write(codec, reg, cache[reg]);
        }
 }
@@ -1325,8 +1257,7 @@ static int twl6040_set_bias_level(struct snd_soc_codec *codec,
 
                priv->codec_powered = 1;
 
-               /* initialize vdd/vss registers with reg_cache */
-               twl6040_init_vdd_regs(codec);
+               twl6040_restore_regs(codec);
 
                /* Set external boost GPO */
                twl6040_write(codec, TWL6040_REG_GPOCTL, 0x02);
@@ -1468,7 +1399,7 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
        .playback = {
                .stream_name = "Playback",
                .channels_min = 1,
-               .channels_max = 2,
+               .channels_max = 5,
                .rates = TWL6040_RATES,
                .formats = TWL6040_FORMATS,
        },
@@ -1518,8 +1449,8 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
        .name = "twl6040-vib",
        .playback = {
                .stream_name = "Vibra Playback",
-               .channels_min = 2,
-               .channels_max = 2,
+               .channels_min = 1,
+               .channels_max = 1,
                .rates = SNDRV_PCM_RATE_CONTINUOUS,
                .formats = TWL6040_FORMATS,
        },
@@ -1620,8 +1551,7 @@ static int twl6040_probe(struct snd_soc_codec *codec)
                goto plugirq_err;
        }
 
-       /* init vio registers */
-       twl6040_init_vio_regs(codec);
+       twl6040_init_chip(codec);
 
        /* power on device */
        ret = twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
index bcc2089..4523c4c 100644 (file)
@@ -56,8 +56,26 @@ static struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = {
 };
 
 static int __devinit wm1250_ev1_probe(struct i2c_client *i2c,
-                                     const struct i2c_device_id *id)
+                                     const struct i2c_device_id *i2c_id)
 {
+       int id, board, rev;
+
+       board = i2c_smbus_read_byte_data(i2c, 0);
+       if (board < 0) {
+               dev_err(&i2c->dev, "Failed to read ID: %d\n", board);
+               return board;
+       }
+
+       id = (board & 0xfe) >> 2;
+       rev = board & 0x3;
+
+       if (id != 1) {
+               dev_err(&i2c->dev, "Unknown board ID %d\n", id);
+               return -ENODEV;
+       }
+
+       dev_info(&i2c->dev, "revision %d\n", rev + 1);
+
        return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm1250_ev1,
                                      &wm1250_ev1_dai, 1);
 }
index db0dced..55a4c83 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/slab.h>
+#include <linux/of_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -598,6 +599,11 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8510 = {
        .reg_cache_default =wm8510_reg,
 };
 
+static const struct of_device_id wm8510_of_match[] = {
+       { .compatible = "wlf,wm8510" },
+       { },
+};
+
 #if defined(CONFIG_SPI_MASTER)
 static int __devinit wm8510_spi_probe(struct spi_device *spi)
 {
@@ -628,6 +634,7 @@ static struct spi_driver wm8510_spi_driver = {
        .driver = {
                .name   = "wm8510",
                .owner  = THIS_MODULE,
+               .of_match_table = wm8510_of_match,
        },
        .probe          = wm8510_spi_probe,
        .remove         = __devexit_p(wm8510_spi_remove),
@@ -671,6 +678,7 @@ static struct i2c_driver wm8510_i2c_driver = {
        .driver = {
                .name = "wm8510-codec",
                .owner = THIS_MODULE,
+               .of_match_table = wm8510_of_match,
        },
        .probe =    wm8510_i2c_probe,
        .remove =   __devexit_p(wm8510_i2c_remove),
index 4fd4d8d..5355a7a 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
+#include <linux/of_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -84,7 +85,7 @@ static const char *wm8523_zd_count_text[] = {
 static const struct soc_enum wm8523_zc_count =
        SOC_ENUM_SINGLE(WM8523_ZERO_DETECT, 0, 2, wm8523_zd_count_text);
 
-static const struct snd_kcontrol_new wm8523_snd_controls[] = {
+static const struct snd_kcontrol_new wm8523_controls[] = {
 SOC_DOUBLE_R_TLV("Playback Volume", WM8523_DAC_GAINL, WM8523_DAC_GAINR,
                 0, 448, 0, dac_tlv),
 SOC_SINGLE("ZC Switch", WM8523_DAC_CTRL3, 4, 1, 0),
@@ -101,22 +102,11 @@ SND_SOC_DAPM_OUTPUT("LINEVOUTL"),
 SND_SOC_DAPM_OUTPUT("LINEVOUTR"),
 };
 
-static const struct snd_soc_dapm_route intercon[] = {
+static const struct snd_soc_dapm_route wm8523_dapm_routes[] = {
        { "LINEVOUTL", NULL, "DAC" },
        { "LINEVOUTR", NULL, "DAC" },
 };
 
-static int wm8523_add_widgets(struct snd_soc_codec *codec)
-{
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       snd_soc_dapm_new_controls(dapm, wm8523_dapm_widgets,
-                                 ARRAY_SIZE(wm8523_dapm_widgets));
-       snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
-       return 0;
-}
-
 static struct {
        int value;
        int ratio;
@@ -479,10 +469,6 @@ static int wm8523_probe(struct snd_soc_codec *codec)
        /* Bias level configuration will have done an extra enable */
        regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
 
-       snd_soc_add_controls(codec, wm8523_snd_controls,
-                            ARRAY_SIZE(wm8523_snd_controls));
-       wm8523_add_widgets(codec);
-
        return 0;
 
 err_enable:
@@ -512,6 +498,18 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8523 = {
        .reg_word_size = sizeof(u16),
        .reg_cache_default = wm8523_reg,
        .volatile_register = wm8523_volatile_register,
+
+       .controls = wm8523_controls,
+       .num_controls = ARRAY_SIZE(wm8523_controls),
+       .dapm_widgets = wm8523_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wm8523_dapm_widgets),
+       .dapm_routes = wm8523_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(wm8523_dapm_routes),
+};
+
+static const struct of_device_id wm8523_of_match[] = {
+       { .compatible = "wlf,wm8523" },
+       { },
 };
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
@@ -551,8 +549,9 @@ MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id);
 
 static struct i2c_driver wm8523_i2c_driver = {
        .driver = {
-               .name = "wm8523-codec",
+               .name = "wm8523",
                .owner = THIS_MODULE,
+               .of_match_table = wm8523_of_match,
        },
        .probe =    wm8523_i2c_probe,
        .remove =   __devexit_p(wm8523_i2c_remove),
index 4bbc0a7..4664c3a 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
+#include <linux/of_device.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -907,6 +908,11 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8580 = {
        .reg_cache_default = wm8580_reg,
 };
 
+static const struct of_device_id wm8580_of_match[] = {
+       { .compatible = "wlf,wm8580" },
+       { },
+};
+
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 static int wm8580_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
@@ -943,8 +949,9 @@ MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
 
 static struct i2c_driver wm8580_i2c_driver = {
        .driver = {
-               .name = "wm8580-codec",
+               .name = "wm8580",
                .owner = THIS_MODULE,
+               .of_match_table = wm8580_of_match,
        },
        .probe =    wm8580_i2c_probe,
        .remove =   wm8580_i2c_remove,
index a537e4a..8457d3c 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/slab.h>
+#include <linux/of_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -414,6 +415,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8711 = {
        .num_dapm_routes = ARRAY_SIZE(wm8711_intercon),
 };
 
+static const struct of_device_id wm8711_of_match[] = {
+       { .compatible = "wlf,wm8711", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8711_of_match);
+
 #if defined(CONFIG_SPI_MASTER)
 static int __devinit wm8711_spi_probe(struct spi_device *spi)
 {
@@ -443,8 +450,9 @@ static int __devexit wm8711_spi_remove(struct spi_device *spi)
 
 static struct spi_driver wm8711_spi_driver = {
        .driver = {
-               .name   = "wm8711-codec",
+               .name   = "wm8711",
                .owner  = THIS_MODULE,
+               .of_match_table = wm8711_of_match,
        },
        .probe          = wm8711_spi_probe,
        .remove         = __devexit_p(wm8711_spi_remove),
@@ -487,8 +495,9 @@ MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
 
 static struct i2c_driver wm8711_i2c_driver = {
        .driver = {
-               .name = "wm8711-codec",
+               .name = "wm8711",
                .owner = THIS_MODULE,
+               .of_match_table = wm8711_of_match,
        },
        .probe =    wm8711_i2c_probe,
        .remove =   __devexit_p(wm8711_i2c_remove),
index 86d4718..04b027e 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/slab.h>
+#include <linux/of_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -269,6 +270,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8728 = {
        .num_dapm_routes = ARRAY_SIZE(wm8728_intercon),
 };
 
+static const struct of_device_id wm8728_of_match[] = {
+       { .compatible = "wlf,wm8728", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8728_of_match);
+
 #if defined(CONFIG_SPI_MASTER)
 static int __devinit wm8728_spi_probe(struct spi_device *spi)
 {
@@ -298,8 +305,9 @@ static int __devexit wm8728_spi_remove(struct spi_device *spi)
 
 static struct spi_driver wm8728_spi_driver = {
        .driver = {
-               .name   = "wm8728-codec",
+               .name   = "wm8728",
                .owner  = THIS_MODULE,
+               .of_match_table = wm8728_of_match,
        },
        .probe          = wm8728_spi_probe,
        .remove         = __devexit_p(wm8728_spi_remove),
@@ -342,8 +350,9 @@ MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
 
 static struct i2c_driver wm8728_i2c_driver = {
        .driver = {
-               .name = "wm8728-codec",
+               .name = "wm8728",
                .owner = THIS_MODULE,
+               .of_match_table = wm8728_of_match,
        },
        .probe =    wm8728_i2c_probe,
        .remove =   __devexit_p(wm8728_i2c_remove),
index 76b4361..f76b6fc 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
+#include <linux/of_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -607,6 +608,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8731 = {
        .num_dapm_routes = ARRAY_SIZE(wm8731_intercon),
 };
 
+static const struct of_device_id wm8731_of_match[] = {
+       { .compatible = "wlf,wm8731", },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, wm8731_of_match);
+
 #if defined(CONFIG_SPI_MASTER)
 static int __devinit wm8731_spi_probe(struct spi_device *spi)
 {
@@ -638,6 +646,7 @@ static struct spi_driver wm8731_spi_driver = {
        .driver = {
                .name   = "wm8731",
                .owner  = THIS_MODULE,
+               .of_match_table = wm8731_of_match,
        },
        .probe          = wm8731_spi_probe,
        .remove         = __devexit_p(wm8731_spi_remove),
@@ -682,6 +691,7 @@ static struct i2c_driver wm8731_i2c_driver = {
        .driver = {
                .name = "wm8731",
                .owner = THIS_MODULE,
+               .of_match_table = wm8731_of_match,
        },
        .probe =    wm8731_i2c_probe,
        .remove =   __devexit_p(wm8731_i2c_remove),
index 30c67d0..f6aef58 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 #include <linux/slab.h>
+#include <linux/of_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -634,6 +635,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8737 = {
        .reg_cache_default = wm8737_reg,
 };
 
+static const struct of_device_id wm8737_of_match[] = {
+       { .compatible = "wlf,wm8737", },
+       { }
+};
+
+MODULE_DEVICE_TABLE(of, wm8737_of_match);
+
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 static __devinit int wm8737_i2c_probe(struct i2c_client *i2c,
                                      const struct i2c_device_id *id)
@@ -673,6 +681,7 @@ static struct i2c_driver wm8737_i2c_driver = {
        .driver = {
                .name = "wm8737",
                .owner = THIS_MODULE,
+               .of_match_table = wm8737_of_match,
        },
        .probe =    wm8737_i2c_probe,
        .remove =   __devexit_p(wm8737_i2c_remove),
@@ -711,6 +720,7 @@ static struct spi_driver wm8737_spi_driver = {
        .driver = {
                .name   = "wm8737",
                .owner  = THIS_MODULE,
+               .of_match_table = wm8737_of_match,
        },
        .probe          = wm8737_spi_probe,
        .remove         = __devexit_p(wm8737_spi_remove),
index 25af901..78c9e5a 100644 (file)
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
+#include <linux/spi/spi.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
+#include <linux/of_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -422,17 +424,35 @@ static int wm8741_probe(struct snd_soc_codec *codec)
 {
        struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
        int ret = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
+               wm8741->supplies[i].supply = wm8741_supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8741->supplies),
+                                wm8741->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+               goto err;
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies),
+                                   wm8741->supplies);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+               goto err_get;
+       }
 
        ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8741->control_type);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
-               return ret;
+               goto err_enable;
        }
 
        ret = wm8741_reset(codec);
        if (ret < 0) {
                dev_err(codec->dev, "Failed to issue reset\n");
-               return ret;
+               goto err_enable;
        }
 
        /* Change some default settings - latch VU */
@@ -451,58 +471,61 @@ static int wm8741_probe(struct snd_soc_codec *codec)
 
        dev_dbg(codec->dev, "Successful registration\n");
        return ret;
+
+err_enable:
+       regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
+err_get:
+       regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
+err:
+       return ret;
+}
+
+static int wm8741_remove(struct snd_soc_codec *codec)
+{
+       struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
+
+       regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
+       regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
+
+       return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8741 = {
        .probe =        wm8741_probe,
+       .remove =       wm8741_remove,
        .resume =       wm8741_resume,
        .reg_cache_size = ARRAY_SIZE(wm8741_reg_defaults),
        .reg_word_size = sizeof(u16),
        .reg_cache_default = wm8741_reg_defaults,
 };
 
+static const struct of_device_id wm8741_of_match[] = {
+       { .compatible = "wlf,wm8741", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8741_of_match);
+
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 static int wm8741_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
        struct wm8741_priv *wm8741;
-       int ret, i;
+       int ret;
 
        wm8741 = kzalloc(sizeof(struct wm8741_priv), GFP_KERNEL);
        if (wm8741 == NULL)
                return -ENOMEM;
 
-       for (i = 0; i < ARRAY_SIZE(wm8741->supplies); i++)
-               wm8741->supplies[i].supply = wm8741_supply_names[i];
-
-       ret = regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8741->supplies),
-                                wm8741->supplies);
-       if (ret != 0) {
-               dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
-               goto err;
-       }
-
-       ret = regulator_bulk_enable(ARRAY_SIZE(wm8741->supplies),
-                                   wm8741->supplies);
-       if (ret != 0) {
-               dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
-               goto err_get;
-       }
-
        i2c_set_clientdata(i2c, wm8741);
        wm8741->control_type = SND_SOC_I2C;
 
-       ret =  snd_soc_register_codec(&i2c->dev,
-                       &soc_codec_dev_wm8741, &wm8741_dai, 1);
-       if (ret < 0)
-               goto err_enable;
-       return ret;
+       ret = snd_soc_register_codec(&i2c->dev,
+                                    &soc_codec_dev_wm8741, &wm8741_dai, 1);
+       if (ret != 0)
+               goto err;
 
-err_enable:
-       regulator_bulk_disable(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
+       return ret;
 
-err_get:
-       regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
 err:
        kfree(wm8741);
        return ret;
@@ -510,10 +533,7 @@ err:
 
 static int wm8741_i2c_remove(struct i2c_client *client)
 {
-       struct wm8741_priv *wm8741 = i2c_get_clientdata(client);
-
        snd_soc_unregister_codec(&client->dev);
-       regulator_bulk_free(ARRAY_SIZE(wm8741->supplies), wm8741->supplies);
        kfree(i2c_get_clientdata(client));
        return 0;
 }
@@ -526,8 +546,9 @@ MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id);
 
 static struct i2c_driver wm8741_i2c_driver = {
        .driver = {
-               .name = "wm8741-codec",
+               .name = "wm8741",
                .owner = THIS_MODULE,
+               .of_match_table = wm8741_of_match,
        },
        .probe =    wm8741_i2c_probe,
        .remove =   wm8741_i2c_remove,
@@ -535,6 +556,44 @@ static struct i2c_driver wm8741_i2c_driver = {
 };
 #endif
 
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8741_spi_probe(struct spi_device *spi)
+{
+       struct wm8741_priv *wm8741;
+       int ret;
+
+       wm8741 = kzalloc(sizeof(struct wm8741_priv), GFP_KERNEL);
+       if (wm8741 == NULL)
+               return -ENOMEM;
+
+       wm8741->control_type = SND_SOC_SPI;
+       spi_set_drvdata(spi, wm8741);
+
+       ret = snd_soc_register_codec(&spi->dev,
+                       &soc_codec_dev_wm8741, &wm8741_dai, 1);
+       if (ret < 0)
+               kfree(wm8741);
+       return ret;
+}
+
+static int __devexit wm8741_spi_remove(struct spi_device *spi)
+{
+       snd_soc_unregister_codec(&spi->dev);
+       kfree(spi_get_drvdata(spi));
+       return 0;
+}
+
+static struct spi_driver wm8741_spi_driver = {
+       .driver = {
+               .name   = "wm8741",
+               .owner  = THIS_MODULE,
+               .of_match_table = wm8741_of_match,
+       },
+       .probe          = wm8741_spi_probe,
+       .remove         = __devexit_p(wm8741_spi_remove),
+};
+#endif /* CONFIG_SPI_MASTER */
+
 static int __init wm8741_modinit(void)
 {
        int ret = 0;
@@ -544,6 +603,13 @@ static int __init wm8741_modinit(void)
        if (ret != 0)
                pr_err("Failed to register WM8741 I2C driver: %d\n", ret);
 #endif
+#if defined(CONFIG_SPI_MASTER)
+       ret = spi_register_driver(&wm8741_spi_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register wm8741 SPI driver: %d\n",
+                      ret);
+       }
+#endif
 
        return ret;
 }
@@ -551,6 +617,9 @@ module_init(wm8741_modinit);
 
 static void __exit wm8741_exit(void)
 {
+#if defined(CONFIG_SPI_MASTER)
+       spi_unregister_driver(&wm8741_spi_driver);
+#endif
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
        i2c_del_driver(&wm8741_i2c_driver);
 #endif
index d0003cc..15f0372 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/slab.h>
+#include <linux/of_device.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -751,6 +752,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8750 = {
        .reg_cache_default = wm8750_reg,
 };
 
+static const struct of_device_id wm8750_of_match[] = {
+       { .compatible = "wlf,wm8750", },
+       { .compatible = "wlf,wm8987", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8750_of_match);
+
 #if defined(CONFIG_SPI_MASTER)
 static int __devinit wm8750_spi_probe(struct spi_device *spi)
 {
@@ -787,8 +795,9 @@ MODULE_DEVICE_TABLE(spi, wm8750_spi_ids);
 
 static struct spi_driver wm8750_spi_driver = {
        .driver = {
-               .name   = "wm8750-codec",
+               .name   = "wm8750",
                .owner  = THIS_MODULE,
+               .of_match_table = wm8750_of_match,
        },
        .id_table       = wm8750_spi_ids,
        .probe          = wm8750_spi_probe,
@@ -833,8 +842,9 @@ MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
 
 static struct i2c_driver wm8750_i2c_driver = {
        .driver = {
-               .name = "wm8750-codec",
+               .name = "wm8750",
                .owner = THIS_MODULE,
+               .of_match_table = wm8750_of_match,
        },
        .probe =    wm8750_i2c_probe,
        .remove =   __devexit_p(wm8750_i2c_remove),
index ffa2ffe..fe04a10 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/slab.h>
@@ -1490,6 +1491,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8753 = {
        .reg_cache_default = wm8753_reg,
 };
 
+static const struct of_device_id wm8753_of_match[] = {
+       { .compatible = "wlf,wm8753", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8753_of_match);
+
 #if defined(CONFIG_SPI_MASTER)
 static int __devinit wm8753_spi_probe(struct spi_device *spi)
 {
@@ -1519,8 +1526,9 @@ static int __devexit wm8753_spi_remove(struct spi_device *spi)
 
 static struct spi_driver wm8753_spi_driver = {
        .driver = {
-               .name   = "wm8753-codec",
+               .name   = "wm8753",
                .owner  = THIS_MODULE,
+               .of_match_table = wm8753_of_match,
        },
        .probe          = wm8753_spi_probe,
        .remove         = __devexit_p(wm8753_spi_remove),
@@ -1563,8 +1571,9 @@ MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
 
 static struct i2c_driver wm8753_i2c_driver = {
        .driver = {
-               .name = "wm8753-codec",
+               .name = "wm8753",
                .owner = THIS_MODULE,
+               .of_match_table = wm8753_of_match,
        },
        .probe =    wm8753_i2c_probe,
        .remove =   __devexit_p(wm8753_i2c_remove),
index 19b92ba..aa05e65 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/moduleparam.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/of_device.h>
 #include <linux/pm.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
@@ -684,6 +685,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8770 = {
        .reg_cache_default = wm8770_reg_defs
 };
 
+static const struct of_device_id wm8770_of_match[] = {
+       { .compatible = "wlf,wm8770", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8770_of_match);
+
 #if defined(CONFIG_SPI_MASTER)
 static int __devinit wm8770_spi_probe(struct spi_device *spi)
 {
@@ -715,6 +722,7 @@ static struct spi_driver wm8770_spi_driver = {
        .driver = {
                .name = "wm8770",
                .owner = THIS_MODULE,
+               .of_match_table = wm8770_of_match,
        },
        .probe = wm8770_spi_probe,
        .remove = __devexit_p(wm8770_spi_remove)
index 8e7953b..00d8846 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
 #include <linux/slab.h>
@@ -215,8 +216,6 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream,
        int ratio_shift, master;
        int i;
 
-       iface = 0;
-
        switch (dai->driver->id) {
        case WM8776_DAI_DAC:
                iface_reg = WM8776_DACIFCTRL;
@@ -232,20 +231,23 @@ static int wm8776_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-
        /* Set word length */
-       switch (params_format(params)) {
-       case SNDRV_PCM_FORMAT_S16_LE:
+       switch (snd_pcm_format_width(params_format(params))) {
+       case 16:
+               iface = 0;
+       case 20:
+               iface = 0x10;
                break;
-       case SNDRV_PCM_FORMAT_S20_3LE:
-               iface |= 0x10;
+       case 24:
+               iface = 0x20;
                break;
-       case SNDRV_PCM_FORMAT_S24_LE:
-               iface |= 0x20;
-               break;
-       case SNDRV_PCM_FORMAT_S32_LE:
-               iface |= 0x30;
+       case 32:
+               iface = 0x30;
                break;
+       default:
+               dev_err(codec->dev, "Unsupported sample size: %i\n",
+                       snd_pcm_format_width(params_format(params)));
+               return -EINVAL;
        }
 
        /* Only need to set MCLK/LRCLK ratio if we're master */
@@ -320,11 +322,6 @@ static int wm8776_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-#define WM8776_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
-                     SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
-                     SNDRV_PCM_RATE_96000)
-
-
 #define WM8776_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
 
@@ -349,7 +346,9 @@ static struct snd_soc_dai_driver wm8776_dai[] = {
                        .stream_name = "Playback",
                        .channels_min = 2,
                        .channels_max = 2,
-                       .rates = WM8776_RATES,
+                       .rates = SNDRV_PCM_RATE_CONTINUOUS,
+                       .rate_min = 32000,
+                       .rate_max = 192000,
                        .formats = WM8776_FORMATS,
                },
                .ops = &wm8776_dac_ops,
@@ -361,7 +360,9 @@ static struct snd_soc_dai_driver wm8776_dai[] = {
                        .stream_name = "Capture",
                        .channels_min = 2,
                        .channels_max = 2,
-                       .rates = WM8776_RATES,
+                       .rates = SNDRV_PCM_RATE_CONTINUOUS,
+                       .rate_min = 32000,
+                       .rate_max = 96000,
                        .formats = WM8776_FORMATS,
                },
                .ops = &wm8776_adc_ops,
@@ -452,6 +453,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8776 = {
        .reg_cache_default = wm8776_reg,
 };
 
+static const struct of_device_id wm8776_of_match[] = {
+       { .compatible = "wlf,wm8776", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8776_of_match);
+
 #if defined(CONFIG_SPI_MASTER)
 static int __devinit wm8776_spi_probe(struct spi_device *spi)
 {
@@ -481,8 +488,9 @@ static int __devexit wm8776_spi_remove(struct spi_device *spi)
 
 static struct spi_driver wm8776_spi_driver = {
        .driver = {
-               .name   = "wm8776-codec",
+               .name   = "wm8776",
                .owner  = THIS_MODULE,
+               .of_match_table = wm8776_of_match,
        },
        .probe          = wm8776_spi_probe,
        .remove         = __devexit_p(wm8776_spi_remove),
@@ -525,8 +533,9 @@ MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id);
 
 static struct i2c_driver wm8776_i2c_driver = {
        .driver = {
-               .name = "wm8776-codec",
+               .name = "wm8776",
                .owner = THIS_MODULE,
+               .of_match_table = wm8776_of_match,
        },
        .probe =    wm8776_i2c_probe,
        .remove =   __devexit_p(wm8776_i2c_remove),
index 9a5e67c..9ee072b 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
+#include <linux/of_device.h>
 #include <linux/spi/spi.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
@@ -717,6 +718,12 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8804 = {
        .volatile_register = wm8804_volatile
 };
 
+static const struct of_device_id wm8804_of_match[] = {
+       { .compatible = "wlf,wm8804", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8804_of_match);
+
 #if defined(CONFIG_SPI_MASTER)
 static int __devinit wm8804_spi_probe(struct spi_device *spi)
 {
@@ -748,6 +755,7 @@ static struct spi_driver wm8804_spi_driver = {
        .driver = {
                .name = "wm8804",
                .owner = THIS_MODULE,
+               .of_match_table = wm8804_of_match,
        },
        .probe = wm8804_spi_probe,
        .remove = __devexit_p(wm8804_spi_remove)
@@ -792,6 +800,7 @@ static struct i2c_driver wm8804_i2c_driver = {
        .driver = {
                .name = "wm8804",
                .owner = THIS_MODULE,
+               .of_match_table = wm8804_of_match,
        },
        .probe = wm8804_i2c_probe,
        .remove = __devexit_p(wm8804_i2c_remove),
index d2c315f..3676b38 100644 (file)
@@ -63,6 +63,8 @@ struct wm8962_priv {
        int fll_fref;
        int fll_fout;
 
+       u16 dsp2_ena;
+
        struct delayed_work mic_work;
        struct snd_soc_jack *jack;
 
@@ -837,7 +839,7 @@ static const struct wm8962_reg_access {
        [40] = { 0x00FF, 0x01FF, 0x0000 }, /* R40    - SPKOUTL volume */
        [41] = { 0x00FF, 0x01FF, 0x0000 }, /* R41    - SPKOUTR volume */
 
-       [47] = { 0x000F, 0x0000, 0x0000 }, /* R47    - Thermal Shutdown Status */
+       [47] = { 0x000F, 0x0000, 0xFFFF }, /* R47    - Thermal Shutdown Status */
        [48] = { 0x7EC7, 0x7E07, 0xFFFF }, /* R48    - Additional Control (4) */
        [49] = { 0x00D3, 0x00D7, 0xFFFF }, /* R49    - Class D Control 1 */
        [51] = { 0x0047, 0x0047, 0x0000 }, /* R51    - Class D Control 2 */
@@ -965,7 +967,7 @@ static const struct wm8962_reg_access {
        [584] = { 0x002D, 0x002D, 0x0000 }, /* R584   - IRQ Debounce */
        [586] = { 0xC000, 0xC000, 0x0000 }, /* R586   -  MICINT Source Pol */
        [768] = { 0x0001, 0x0001, 0x0000 }, /* R768   - DSP2 Power Management */
-       [1037] = { 0x0000, 0x003F, 0x0000 }, /* R1037  - DSP2_ExecControl */
+       [1037] = { 0x0000, 0x003F, 0xFFFF }, /* R1037  - DSP2_ExecControl */
        [4096] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4096  - Write Sequencer 0 */
        [4097] = { 0x00FF, 0x00FF, 0x0000 }, /* R4097  - Write Sequencer 1 */
        [4098] = { 0x070F, 0x070F, 0x0000 }, /* R4098  - Write Sequencer 2 */
@@ -1986,6 +1988,122 @@ static const unsigned int classd_tlv[] = {
 };
 static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 
+static int wm8962_dsp2_write_config(struct snd_soc_codec *codec)
+{
+       return 0;
+}
+
+static int wm8962_dsp2_set_enable(struct snd_soc_codec *codec, u16 val)
+{
+       u16 adcl = snd_soc_read(codec, WM8962_LEFT_ADC_VOLUME);
+       u16 adcr = snd_soc_read(codec, WM8962_RIGHT_ADC_VOLUME);
+       u16 dac = snd_soc_read(codec, WM8962_ADC_DAC_CONTROL_1);
+
+       /* Mute the ADCs and DACs */
+       snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, 0);
+       snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, WM8962_ADC_VU);
+       snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
+                           WM8962_DAC_MUTE, WM8962_DAC_MUTE);
+
+       snd_soc_write(codec, WM8962_SOUNDSTAGE_ENABLES_0, val);
+
+       /* Restore the ADCs and DACs */
+       snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, adcl);
+       snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, adcr);
+       snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
+                           WM8962_DAC_MUTE, dac);
+
+       return 0;
+}
+
+static int wm8962_dsp2_start(struct snd_soc_codec *codec)
+{
+       struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+       wm8962_dsp2_write_config(codec);
+
+       snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_RUNR);
+
+       wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
+
+       return 0;
+}
+
+static int wm8962_dsp2_stop(struct snd_soc_codec *codec)
+{
+       wm8962_dsp2_set_enable(codec, 0);
+
+       snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_STOP);
+
+       return 0;
+}
+
+#define WM8962_DSP2_ENABLE(xname, xshift) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = wm8962_dsp2_ena_info, \
+       .get = wm8962_dsp2_ena_get, .put = wm8962_dsp2_ena_put, \
+       .private_value = xshift }
+
+static int wm8962_dsp2_ena_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+
+       return 0;
+}
+
+static int wm8962_dsp2_ena_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       int shift = kcontrol->private_value;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = !!(wm8962->dsp2_ena & 1 << shift);
+
+       return 0;
+}
+
+static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       int shift = kcontrol->private_value;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+       int old = wm8962->dsp2_ena;
+       int ret = 0;
+       int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) &
+               WM8962_DSP2_ENA;
+
+       mutex_lock(&codec->mutex);
+
+       if (ucontrol->value.integer.value[0])
+               wm8962->dsp2_ena |= 1 << shift;
+       else
+               wm8962->dsp2_ena &= ~(1 << shift);
+
+       if (wm8962->dsp2_ena == old)
+               goto out;
+
+       ret = 1;
+
+       if (dsp2_running) {
+               if (wm8962->dsp2_ena)
+                       wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
+               else
+                       wm8962_dsp2_stop(codec);
+       }
+
+out:
+       mutex_unlock(&codec->mutex);
+
+       return ret;
+}
+
 /* The VU bits for the headphones are in a different register to the mute
  * bits and only take effect on the PGA if it is actually powered.
  */
@@ -2049,6 +2167,14 @@ static const char *cap_hpf_mode_text[] = {
 static const struct soc_enum cap_hpf_mode =
        SOC_ENUM_SINGLE(WM8962_ADC_DAC_CONTROL_2, 10, 2, cap_hpf_mode_text);
 
+
+static const char *cap_lhpf_mode_text[] = {
+       "LPF", "HPF"
+};
+
+static const struct soc_enum cap_lhpf_mode =
+       SOC_ENUM_SINGLE(WM8962_LHPF1, 1, 2, cap_lhpf_mode_text);
+
 static const struct snd_kcontrol_new wm8962_snd_controls[] = {
 SOC_DOUBLE("Input Mixer Switch", WM8962_INPUT_MIXER_CONTROL_1, 3, 2, 1, 1),
 
@@ -2077,6 +2203,8 @@ SOC_DOUBLE_R("Capture ZC Switch", WM8962_LEFT_INPUT_VOLUME,
 SOC_SINGLE("Capture HPF Switch", WM8962_ADC_DAC_CONTROL_1, 0, 1, 1),
 SOC_ENUM("Capture HPF Mode", cap_hpf_mode),
 SOC_SINGLE("Capture HPF Cutoff", WM8962_ADC_DAC_CONTROL_2, 7, 7, 0),
+SOC_SINGLE("Capture LHPF Switch", WM8962_LHPF1, 0, 1, 0),
+SOC_ENUM("Capture LHPF Mode", cap_lhpf_mode),
 
 SOC_DOUBLE_R_TLV("Sidetone Volume", WM8962_DAC_DSP_MIXING_1,
                 WM8962_DAC_DSP_MIXING_2, 4, 12, 0, st_tlv),
@@ -2134,6 +2262,11 @@ SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
                 WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
 SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
                 WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
+
+WM8962_DSP2_ENABLE("VSS Switch", WM8962_VSS_ENA_SHIFT),
+WM8962_DSP2_ENABLE("HPF1 Switch", WM8962_HPF1_ENA_SHIFT),
+WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT),
+WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT),
 };
 
 static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
@@ -2395,6 +2528,31 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
        }
 }
 
+static int dsp2_event(struct snd_soc_dapm_widget *w,
+                     struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (wm8962->dsp2_ena)
+                       wm8962_dsp2_start(codec);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               if (wm8962->dsp2_ena)
+                       wm8962_dsp2_stop(codec);
+               break;
+
+       default:
+               BUG();
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static const char *st_text[] = { "None", "Right", "Left" };
 
 static const struct soc_enum str_enum =
@@ -2517,6 +2675,9 @@ SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, sysclk_event,
 SND_SOC_DAPM_SUPPLY("Charge Pump", WM8962_CHARGE_PUMP_1, 0, 0, cp_event,
                    SND_SOC_DAPM_POST_PMU),
 SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY_S("DSP2", 1, WM8962_DSP2_POWER_MANAGEMENT,
+                     WM8962_DSP2_ENA_SHIFT, 0, dsp2_event,
+                     SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 
 SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0,
                   inpgal, ARRAY_SIZE(inpgal)),
@@ -2612,11 +2773,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
        { "ADCL", NULL, "TOCLK" },
        { "ADCL", NULL, "MIXINL" },
        { "ADCL", NULL, "DMIC" },
+       { "ADCL", NULL, "DSP2" },
 
        { "ADCR", NULL, "SYSCLK" },
        { "ADCR", NULL, "TOCLK" },
        { "ADCR", NULL, "MIXINR" },
        { "ADCR", NULL, "DMIC" },
+       { "ADCR", NULL, "DSP2" },
 
        { "STL", "Left", "ADCL" },
        { "STL", "Right", "ADCR" },
@@ -2628,11 +2791,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
        { "DACL", NULL, "TOCLK" },
        { "DACL", NULL, "Beep" },
        { "DACL", NULL, "STL" },
+       { "DACL", NULL, "DSP2" },
 
        { "DACR", NULL, "SYSCLK" },
        { "DACR", NULL, "TOCLK" },
        { "DACR", NULL, "Beep" },
        { "DACR", NULL, "STR" },
+       { "DACR", NULL, "DSP2" },
 
        { "HPMIXL", "IN4L Switch", "IN4L" },
        { "HPMIXL", "IN4R Switch", "IN4R" },
@@ -3403,12 +3568,16 @@ static irqreturn_t wm8962_irq(int irq, void *data)
        struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
        int mask;
        int active;
+       int reg;
 
        mask = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2_MASK);
 
        active = snd_soc_read(codec, WM8962_INTERRUPT_STATUS_2);
        active &= ~mask;
 
+       if (!active)
+               return IRQ_NONE;
+
        /* Acknowledge the interrupts */
        snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active);
 
@@ -3420,9 +3589,21 @@ static irqreturn_t wm8962_irq(int irq, void *data)
        if (active & WM8962_FIFOS_ERR_EINT)
                dev_err(codec->dev, "FIFO error\n");
 
-       if (active & WM8962_TEMP_SHUT_EINT)
+       if (active & WM8962_TEMP_SHUT_EINT) {
                dev_crit(codec->dev, "Thermal shutdown\n");
 
+               reg = snd_soc_read(codec, WM8962_THERMAL_SHUTDOWN_STATUS);
+
+               if (reg & WM8962_TEMP_ERR_HP)
+                       dev_crit(codec->dev, "Headphone thermal error\n");
+               if (reg & WM8962_TEMP_WARN_HP)
+                       dev_crit(codec->dev, "Headphone thermal warning\n");
+               if (reg & WM8962_TEMP_ERR_SPK)
+                       dev_crit(codec->dev, "Speaker thermal error\n");
+               if (reg & WM8962_TEMP_WARN_SPK)
+                       dev_crit(codec->dev, "Speaker thermal warning\n");
+       }
+
        if (active & (WM8962_MICSCD_EINT | WM8962_MICD_EINT)) {
                dev_dbg(codec->dev, "Microphone event detected\n");
 
index 6e85b88..eec8e14 100644 (file)
@@ -847,6 +847,7 @@ SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8993_BUS_CONTROL_1, 1, 0, clk_sys_event,
                    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 SND_SOC_DAPM_SUPPLY("TOCLK", WM8993_CLOCKING_1, 14, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8993_CLOCKING_3, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("VMID", SND_SOC_NOPM, 0, 0, NULL, 0),
 
 SND_SOC_DAPM_ADC("ADCL", NULL, WM8993_POWER_MANAGEMENT_2, 1, 0),
 SND_SOC_DAPM_ADC("ADCR", NULL, WM8993_POWER_MANAGEMENT_2, 0, 0),
@@ -880,6 +881,9 @@ SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
 };
 
 static const struct snd_soc_dapm_route routes[] = {
+       { "MICBIAS1", NULL, "VMID" },
+       { "MICBIAS2", NULL, "VMID" },
+
        { "ADCL", NULL, "CLK_SYS" },
        { "ADCL", NULL, "CLK_DSP" },
        { "ADCR", NULL, "CLK_SYS" },
@@ -1433,7 +1437,8 @@ static int wm8993_probe(struct snd_soc_codec *codec)
        int ret, i, val;
 
        wm8993->hubs_data.hp_startup_mode = 1;
-       wm8993->hubs_data.dcs_codes = -2;
+       wm8993->hubs_data.dcs_codes_l = -2;
+       wm8993->hubs_data.dcs_codes_r = -2;
        wm8993->hubs_data.series_startup = 1;
 
        ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
index a87adbd..df5a8b9 100644 (file)
@@ -1073,8 +1073,8 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
        { 0x0000, 0x0000 }, /* R1069 */
        { 0x0000, 0x0000 }, /* R1070 */
        { 0x0000, 0x0000 }, /* R1071 */
-       { 0x0000, 0x0000 }, /* R1072 */
-       { 0x0000, 0x0000 }, /* R1073 */
+       { 0x006F, 0x006F }, /* R1072  - AIF1 DAC1 Noise Gate */
+       { 0x006F, 0x006F }, /* R1073  - AIF1 DAC2 Noise Gate */
        { 0x0000, 0x0000 }, /* R1074 */
        { 0x0000, 0x0000 }, /* R1075 */
        { 0x0000, 0x0000 }, /* R1076 */
@@ -1329,7 +1329,7 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
        { 0x0000, 0x0000 }, /* R1325 */
        { 0x0000, 0x0000 }, /* R1326 */
        { 0x0000, 0x0000 }, /* R1327 */
-       { 0x0000, 0x0000 }, /* R1328 */
+       { 0x006F, 0x006F }, /* R1328  - AIF2 DAC Noise Gate */
        { 0x0000, 0x0000 }, /* R1329 */
        { 0x0000, 0x0000 }, /* R1330 */
        { 0x0000, 0x0000 }, /* R1331 */
@@ -1635,8 +1635,8 @@ const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
        0x0000,     /* R58    - MICBIAS */
        0x000D,     /* R59    - LDO 1 */
        0x0003,     /* R60    - LDO 2 */
-       0x0000,     /* R61 */
-       0x0000,     /* R62 */
+       0x0039,     /* R61    - MICBIAS1 */
+       0x0039,     /* R62    - MICBIAS2 */
        0x0000,     /* R63 */
        0x0000,     /* R64 */
        0x0000,     /* R65 */
@@ -2646,8 +2646,8 @@ const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
        0x0000,     /* R1069 */
        0x0000,     /* R1070 */
        0x0000,     /* R1071 */
-       0x0000,     /* R1072 */
-       0x0000,     /* R1073 */
+       0x0068,     /* R1072  - AIF1 DAC1 Noise Gate */
+       0x0068,     /* R1073  - AIF1 DAC2 Noise Gate */
        0x0000,     /* R1074 */
        0x0000,     /* R1075 */
        0x0000,     /* R1076 */
@@ -2902,7 +2902,7 @@ const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
        0x0000,     /* R1325 */
        0x0000,     /* R1326 */
        0x0000,     /* R1327 */
-       0x0000,     /* R1328 */
+       0x0068,     /* R1328  - AIF2 DAC Noise Gate */
        0x0000,     /* R1329 */
        0x0000,     /* R1330 */
        0x0000,     /* R1331 */
index b393f9f..e537267 100644 (file)
@@ -107,6 +107,7 @@ static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg)
        case WM8994_LDO_2:
        case WM8958_DSP2_EXECCONTROL:
        case WM8958_MIC_DETECT_3:
+       case WM8994_DC_SERVO_4E:
                return 1;
        default:
                return 0;
@@ -281,6 +282,7 @@ static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
 static const DECLARE_TLV_DB_SCALE(st_tlv, -3600, 300, 0);
 static const DECLARE_TLV_DB_SCALE(wm8994_3d_tlv, -1600, 183, 0);
 static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
 
 #define WM8994_DRC_SWITCH(xname, reg, shift) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@@ -660,8 +662,45 @@ SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0,
               eq_tlv),
 };
 
+static const char *wm8958_ng_text[] = {
+       "30ms", "125ms", "250ms", "500ms",
+};
+
+static const struct soc_enum wm8958_aif1dac1_ng_hold =
+       SOC_ENUM_SINGLE(WM8958_AIF1_DAC1_NOISE_GATE,
+                       WM8958_AIF1DAC1_NG_THR_SHIFT, 4, wm8958_ng_text);
+
+static const struct soc_enum wm8958_aif1dac2_ng_hold =
+       SOC_ENUM_SINGLE(WM8958_AIF1_DAC2_NOISE_GATE,
+                       WM8958_AIF1DAC2_NG_THR_SHIFT, 4, wm8958_ng_text);
+
+static const struct soc_enum wm8958_aif2dac_ng_hold =
+       SOC_ENUM_SINGLE(WM8958_AIF2_DAC_NOISE_GATE,
+                       WM8958_AIF2DAC_NG_THR_SHIFT, 4, wm8958_ng_text);
+
 static const struct snd_kcontrol_new wm8958_snd_controls[] = {
 SOC_SINGLE_TLV("AIF3 Boost Volume", WM8958_AIF3_CONTROL_2, 10, 3, 0, aif_tlv),
+
+SOC_SINGLE("AIF1DAC1 Noise Gate Switch", WM8958_AIF1_DAC1_NOISE_GATE,
+          WM8958_AIF1DAC1_NG_ENA_SHIFT, 1, 0),
+SOC_ENUM("AIF1DAC1 Noise Gate Hold Time", wm8958_aif1dac1_ng_hold),
+SOC_SINGLE_TLV("AIF1DAC1 Noise Gate Threshold Volume",
+              WM8958_AIF1_DAC1_NOISE_GATE, WM8958_AIF1DAC1_NG_THR_SHIFT,
+              7, 1, ng_tlv),
+
+SOC_SINGLE("AIF1DAC2 Noise Gate Switch", WM8958_AIF1_DAC2_NOISE_GATE,
+          WM8958_AIF1DAC2_NG_ENA_SHIFT, 1, 0),
+SOC_ENUM("AIF1DAC2 Noise Gate Hold Time", wm8958_aif1dac2_ng_hold),
+SOC_SINGLE_TLV("AIF1DAC2 Noise Gate Threshold Volume",
+              WM8958_AIF1_DAC2_NOISE_GATE, WM8958_AIF1DAC2_NG_THR_SHIFT,
+              7, 1, ng_tlv),
+
+SOC_SINGLE("AIF2DAC Noise Gate Switch", WM8958_AIF2_DAC_NOISE_GATE,
+          WM8958_AIF2DAC_NG_ENA_SHIFT, 1, 0),
+SOC_ENUM("AIF2DAC Noise Gate Hold Time", wm8958_aif2dac_ng_hold),
+SOC_SINGLE_TLV("AIF2DAC Noise Gate Threshold Volume",
+              WM8958_AIF2_DAC_NOISE_GATE, WM8958_AIF2DAC_NG_THR_SHIFT,
+              7, 1, ng_tlv),
 };
 
 static int clk_sys_event(struct snd_soc_dapm_widget *w,
@@ -681,6 +720,97 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static void vmid_reference(struct snd_soc_codec *codec)
+{
+       struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+       wm8994->vmid_refcount++;
+
+       dev_dbg(codec->dev, "Referencing VMID, refcount is now %d\n",
+               wm8994->vmid_refcount);
+
+       if (wm8994->vmid_refcount == 1) {
+               /* Startup bias, VMID ramp & buffer */
+               snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+                                   WM8994_STARTUP_BIAS_ENA |
+                                   WM8994_VMID_BUF_ENA |
+                                   WM8994_VMID_RAMP_MASK,
+                                   WM8994_STARTUP_BIAS_ENA |
+                                   WM8994_VMID_BUF_ENA |
+                                   (0x11 << WM8994_VMID_RAMP_SHIFT));
+
+               /* Main bias enable, VMID=2x40k */
+               snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+                                   WM8994_BIAS_ENA |
+                                   WM8994_VMID_SEL_MASK,
+                                   WM8994_BIAS_ENA | 0x2);
+
+               msleep(20);
+       }
+}
+
+static void vmid_dereference(struct snd_soc_codec *codec)
+{
+       struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+
+       wm8994->vmid_refcount--;
+
+       dev_dbg(codec->dev, "Dereferencing VMID, refcount is now %d\n",
+               wm8994->vmid_refcount);
+
+       if (wm8994->vmid_refcount == 0) {
+               /* Switch over to startup biases */
+               snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+                                   WM8994_BIAS_SRC |
+                                   WM8994_STARTUP_BIAS_ENA |
+                                   WM8994_VMID_BUF_ENA |
+                                   WM8994_VMID_RAMP_MASK,
+                                   WM8994_BIAS_SRC |
+                                   WM8994_STARTUP_BIAS_ENA |
+                                   WM8994_VMID_BUF_ENA |
+                                   (1 << WM8994_VMID_RAMP_SHIFT));
+
+               /* Disable main biases */
+               snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+                                   WM8994_BIAS_ENA |
+                                   WM8994_VMID_SEL_MASK, 0);
+
+               /* Discharge line */
+               snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
+                                   WM8994_LINEOUT1_DISCH |
+                                   WM8994_LINEOUT2_DISCH,
+                                   WM8994_LINEOUT1_DISCH |
+                                   WM8994_LINEOUT2_DISCH);
+
+               msleep(5);
+
+               /* Switch off startup biases */
+               snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
+                                   WM8994_BIAS_SRC |
+                                   WM8994_STARTUP_BIAS_ENA |
+                                   WM8994_VMID_BUF_ENA |
+                                   WM8994_VMID_RAMP_MASK, 0);
+       }
+}
+
+static int vmid_event(struct snd_soc_dapm_widget *w,
+                     struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               vmid_reference(codec);
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               vmid_dereference(codec);
+               break;
+       }
+
+       return 0;
+}
+
 static void wm8994_update_class_w(struct snd_soc_codec *codec)
 {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
@@ -1208,6 +1338,8 @@ SND_SOC_DAPM_INPUT("Clock"),
 
 SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev,
                      SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_SUPPLY("VMID", SND_SOC_NOPM, 0, 0, vmid_event,
+                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 
 SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
                    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -1525,6 +1657,8 @@ static const struct snd_soc_dapm_route wm8994_revd_intercon[] = {
 static const struct snd_soc_dapm_route wm8994_intercon[] = {
        { "AIF2DACL", NULL, "AIF2DAC Mux" },
        { "AIF2DACR", NULL, "AIF2DAC Mux" },
+       { "MICBIAS1", NULL, "VMID" },
+       { "MICBIAS2", NULL, "VMID" },
 };
 
 static const struct snd_soc_dapm_route wm8958_intercon[] = {
@@ -1629,10 +1763,12 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
                          unsigned int freq_in, unsigned int freq_out)
 {
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
+       struct wm8994 *control = codec->control_data;
        int reg_offset, ret;
        struct fll_div fll;
        u16 reg, aif1, aif2;
        unsigned long timeout;
+       bool was_enabled;
 
        aif1 = snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
                & WM8994_AIF1CLK_ENA;
@@ -1653,6 +1789,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
                return -EINVAL;
        }
 
+       reg = snd_soc_read(codec, WM8994_FLL1_CONTROL_1 + reg_offset);
+       was_enabled = reg & WM8994_FLL1_ENA;
+
        switch (src) {
        case 0:
                /* Allow no source specification when stopping */
@@ -1719,6 +1858,21 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
 
        /* Enable (with fractional mode if required) */
        if (freq_out) {
+               /* Enable VMID if we need it */
+               if (!was_enabled) {
+                       switch (control->type) {
+                       case WM8994:
+                               vmid_reference(codec);
+                               break;
+                       case WM8958:
+                               if (wm8994->revision < 1)
+                                       vmid_reference(codec);
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
                if (fll.k)
                        reg = WM8994_FLL1_ENA | WM8994_FLL1_FRAC;
                else
@@ -1736,6 +1890,20 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src,
                } else {
                        msleep(5);
                }
+       } else {
+               if (was_enabled) {
+                       switch (control->type) {
+                       case WM8994:
+                               vmid_dereference(codec);
+                               break;
+                       case WM8958:
+                               if (wm8994->revision < 1)
+                                       vmid_dereference(codec);
+                               break;
+                       default:
+                               break;
+                       }
+               }
        }
 
        wm8994->fll[id].in = freq_in;
@@ -1852,9 +2020,6 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
                break;
 
        case SND_SOC_BIAS_PREPARE:
-               /* VMID=2x40k */
-               snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
-                                   WM8994_VMID_SEL_MASK, 0x2);
                break;
 
        case SND_SOC_BIAS_STANDBY:
@@ -1896,65 +2061,13 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
                                            WM8994_LINEOUT2_DISCH,
                                            WM8994_LINEOUT1_DISCH |
                                            WM8994_LINEOUT2_DISCH);
-
-                       /* Startup bias, VMID ramp & buffer */
-                       snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
-                                           WM8994_STARTUP_BIAS_ENA |
-                                           WM8994_VMID_BUF_ENA |
-                                           WM8994_VMID_RAMP_MASK,
-                                           WM8994_STARTUP_BIAS_ENA |
-                                           WM8994_VMID_BUF_ENA |
-                                           (0x11 << WM8994_VMID_RAMP_SHIFT));
-
-                       /* Main bias enable, VMID=2x40k */
-                       snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
-                                           WM8994_BIAS_ENA |
-                                           WM8994_VMID_SEL_MASK,
-                                           WM8994_BIAS_ENA | 0x2);
-
-                       msleep(20);
                }
 
-               /* VMID=2x500k */
-               snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
-                                   WM8994_VMID_SEL_MASK, 0x4);
 
                break;
 
        case SND_SOC_BIAS_OFF:
                if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
-                       /* Switch over to startup biases */
-                       snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
-                                           WM8994_BIAS_SRC |
-                                           WM8994_STARTUP_BIAS_ENA |
-                                           WM8994_VMID_BUF_ENA |
-                                           WM8994_VMID_RAMP_MASK,
-                                           WM8994_BIAS_SRC |
-                                           WM8994_STARTUP_BIAS_ENA |
-                                           WM8994_VMID_BUF_ENA |
-                                           (1 << WM8994_VMID_RAMP_SHIFT));
-
-                       /* Disable main biases */
-                       snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
-                                           WM8994_BIAS_ENA |
-                                           WM8994_VMID_SEL_MASK, 0);
-
-                       /* Discharge line */
-                       snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
-                                           WM8994_LINEOUT1_DISCH |
-                                           WM8994_LINEOUT2_DISCH,
-                                           WM8994_LINEOUT1_DISCH |
-                                           WM8994_LINEOUT2_DISCH);
-
-                       msleep(5);
-
-                       /* Switch off startup biases */
-                       snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
-                                           WM8994_BIAS_SRC |
-                                           WM8994_STARTUP_BIAS_ENA |
-                                           WM8994_VMID_BUF_ENA |
-                                           WM8994_VMID_RAMP_MASK, 0);
-
                        wm8994->cur_fw = NULL;
 
                        pm_runtime_put(codec->dev);
@@ -2384,6 +2497,21 @@ static int wm8994_set_tristate(struct snd_soc_dai *codec_dai, int tristate)
        return snd_soc_update_bits(codec, reg, mask, val);
 }
 
+static int wm8994_aif2_probe(struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       /* Disable the pulls on the AIF if we're using it to save power. */
+       snd_soc_update_bits(codec, WM8994_GPIO_3,
+                           WM8994_GPN_PU | WM8994_GPN_PD, 0);
+       snd_soc_update_bits(codec, WM8994_GPIO_4,
+                           WM8994_GPN_PU | WM8994_GPN_PD, 0);
+       snd_soc_update_bits(codec, WM8994_GPIO_5,
+                           WM8994_GPN_PU | WM8994_GPN_PD, 0);
+
+       return 0;
+}
+
 #define WM8994_RATES SNDRV_PCM_RATE_8000_96000
 
 #define WM8994_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
@@ -2451,6 +2579,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
                        .rates = WM8994_RATES,
                        .formats = WM8994_FORMATS,
                },
+               .probe = wm8994_aif2_probe,
                .ops = &wm8994_aif2_dai_ops,
        },
        {
@@ -2916,6 +3045,24 @@ static irqreturn_t wm8994_fifo_error(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t wm8994_temp_warn(int irq, void *data)
+{
+       struct snd_soc_codec *codec = data;
+
+       dev_err(codec->dev, "Thermal warning\n");
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t wm8994_temp_shut(int irq, void *data)
+{
+       struct snd_soc_codec *codec = data;
+
+       dev_crit(codec->dev, "Thermal shutdown\n");
+
+       return IRQ_HANDLED;
+}
+
 static int wm8994_codec_probe(struct snd_soc_codec *codec)
 {
        struct wm8994 *control;
@@ -2972,13 +3119,14 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
                switch (wm8994->revision) {
                case 2:
                case 3:
-                       wm8994->hubs.dcs_codes = -5;
+                       wm8994->hubs.dcs_codes_l = -5;
+                       wm8994->hubs.dcs_codes_r = -5;
                        wm8994->hubs.hp_startup_mode = 1;
                        wm8994->hubs.dcs_readback_mode = 1;
                        wm8994->hubs.series_startup = 1;
                        break;
                default:
-                       wm8994->hubs.dcs_readback_mode = 1;
+                       wm8994->hubs.dcs_readback_mode = 2;
                        break;
                }
                break;
@@ -2993,6 +3141,10 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 
        wm8994_request_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR,
                           wm8994_fifo_error, "FIFO error", codec);
+       wm8994_request_irq(wm8994->control_data, WM8994_IRQ_TEMP_WARN,
+                          wm8994_temp_warn, "Thermal warning", codec);
+       wm8994_request_irq(wm8994->control_data, WM8994_IRQ_TEMP_SHUT,
+                          wm8994_temp_shut, "Thermal shutdown", codec);
 
        ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
                                 wm_hubs_dcs_done, "DC servo done",
@@ -3257,6 +3409,8 @@ err_irq:
        wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
                        &wm8994->hubs);
        wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec);
+       wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_SHUT, codec);
+       wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_WARN, codec);
 err:
        kfree(wm8994);
        return ret;
@@ -3279,6 +3433,8 @@ static int  wm8994_codec_remove(struct snd_soc_codec *codec)
        wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE,
                        &wm8994->hubs);
        wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec);
+       wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_SHUT, codec);
+       wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_WARN, codec);
 
        switch (control->type) {
        case WM8994:
index 1ab2266..f4f1355 100644 (file)
@@ -83,6 +83,8 @@ struct wm8994_priv {
        struct completion fll_locked[2];
        bool fll_locked_irq;
 
+       int vmid_refcount;
+
        int dac_rates[2];
        int lrclk_shared[2];
 
index 5ad873f..74ae599 100644 (file)
@@ -1573,9 +1573,7 @@ static int wm8995_resume(struct snd_soc_codec *codec)
 static int wm8995_remove(struct snd_soc_codec *codec)
 {
        struct wm8995_priv *wm8995;
-       struct i2c_client *i2c;
 
-       i2c = container_of(codec->dev, struct i2c_client, dev);
        wm8995 = snd_soc_codec_get_drvdata(codec);
        wm8995_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
@@ -1642,6 +1640,7 @@ static int wm8995_probe(struct snd_soc_codec *codec)
 
        if (ret != 0x8995) {
                dev_err(codec->dev, "Invalid device ID: %#x\n", ret);
+               ret = -EINVAL;
                goto err_reg_enable;
        }
 
index 0cdb9d1..833df74 100644 (file)
 #define HPOUT2L 4
 #define HPOUT2R 8
 
-#define WM8996_NUM_SUPPLIES 4
+#define WM8996_NUM_SUPPLIES 3
 static const char *wm8996_supply_names[WM8996_NUM_SUPPLIES] = {
        "DBVDD",
        "AVDD1",
        "AVDD2",
-       "CPVDD",
 };
 
 struct wm8996_priv {
@@ -71,6 +70,8 @@ struct wm8996_priv {
 
        struct regulator_bulk_data supplies[WM8996_NUM_SUPPLIES];
        struct notifier_block disable_nb[WM8996_NUM_SUPPLIES];
+       struct regulator *cpvdd;
+       int bg_ena;
 
        struct wm8996_pdata pdata;
 
@@ -112,7 +113,6 @@ static int wm8996_regulator_event_##n(struct notifier_block *nb, \
 WM8996_REGULATOR_EVENT(0)
 WM8996_REGULATOR_EVENT(1)
 WM8996_REGULATOR_EVENT(2)
-WM8996_REGULATOR_EVENT(3)
 
 static const u16 wm8996_reg[WM8996_MAX_REGISTER] = {
        [WM8996_SOFTWARE_RESET] = 0x8996,
@@ -414,6 +414,7 @@ static const DECLARE_TLV_DB_SCALE(out_digital_tlv, -1200, 150, 0);
 static const DECLARE_TLV_DB_SCALE(out_tlv, -900, 75, 0);
 static const DECLARE_TLV_DB_SCALE(spk_tlv, -900, 150, 0);
 static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(threedstereo_tlv, -1600, 183, 1);
 
 static const char *sidetone_hpf_text[] = {
        "2.9kHz", "1.5kHz", "735Hz", "403Hz", "196Hz", "98Hz", "49Hz"
@@ -608,6 +609,14 @@ SOC_SINGLE("DAC High Performance Switch", WM8996_OVERSAMPLING, 0, 1, 0),
 SOC_SINGLE("DAC Soft Mute Switch", WM8996_DAC_SOFTMUTE, 1, 1, 0),
 SOC_SINGLE("DAC Slow Soft Mute Switch", WM8996_DAC_SOFTMUTE, 0, 1, 0),
 
+SOC_SINGLE("DSP1 3D Stereo Switch", WM8996_DSP1_RX_FILTERS_2, 8, 1, 0),
+SOC_SINGLE("DSP2 3D Stereo Switch", WM8996_DSP2_RX_FILTERS_2, 8, 1, 0),
+
+SOC_SINGLE_TLV("DSP1 3D Stereo Volume", WM8996_DSP1_RX_FILTERS_2, 10, 15,
+               0, threedstereo_tlv),
+SOC_SINGLE_TLV("DSP2 3D Stereo Volume", WM8996_DSP2_RX_FILTERS_2, 10, 15,
+               0, threedstereo_tlv),
+
 SOC_DOUBLE_TLV("Digital Output 1 Volume", WM8996_DAC1_HPOUT1_VOLUME, 0, 4,
               8, 0, out_digital_tlv),
 SOC_DOUBLE_TLV("Digital Output 2 Volume", WM8996_DAC2_HPOUT2_VOLUME, 0, 4,
@@ -658,19 +667,75 @@ SOC_SINGLE_TLV("DSP2 EQ B5 Volume", WM8996_DSP2_RX_EQ_GAINS_2, 6, 31, 0,
               eq_tlv),
 };
 
+static void wm8996_bg_enable(struct snd_soc_codec *codec)
+{
+       struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+
+       wm8996->bg_ena++;
+       if (wm8996->bg_ena == 1) {
+               snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
+                                   WM8996_BG_ENA, WM8996_BG_ENA);
+               msleep(2);
+       }
+}
+
+static void wm8996_bg_disable(struct snd_soc_codec *codec)
+{
+       struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+
+       wm8996->bg_ena--;
+       if (!wm8996->bg_ena)
+               snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
+                                   WM8996_BG_ENA, 0);
+}
+
+static int bg_event(struct snd_soc_dapm_widget *w,
+                   struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       int ret = 0;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               wm8996_bg_enable(codec);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               wm8996_bg_disable(codec);
+               break;
+       default:
+               BUG();
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
 static int cp_event(struct snd_soc_dapm_widget *w,
                    struct snd_kcontrol *kcontrol, int event)
 {
+       struct snd_soc_codec *codec = w->codec;
+       struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
        switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               ret = regulator_enable(wm8996->cpvdd);
+               if (ret != 0)
+                       dev_err(codec->dev, "Failed to enable CPVDD: %d\n",
+                               ret);
+               break;
        case SND_SOC_DAPM_POST_PMU:
                msleep(5);
                break;
+       case SND_SOC_DAPM_POST_PMD:
+               regulator_disable_deferred(wm8996->cpvdd, 20);
+               break;
        default:
                BUG();
-               return -EINVAL;
+               ret = -EINVAL;
        }
 
-       return 0;
+       return ret;
 }
 
 static int rmv_short_event(struct snd_soc_dapm_widget *w,
@@ -698,7 +763,7 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
 {
        struct i2c_client *i2c = to_i2c_client(codec->dev);
        struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
-       int i, ret;
+       int ret;
        unsigned long timeout = 200;
 
        snd_soc_write(codec, WM8996_DC_SERVO_2, mask);
@@ -713,15 +778,12 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
 
                } else {
                        msleep(1);
-                       if (--i) {
-                               timeout = 0;
-                               break;
-                       }
+                       timeout--;
                }
 
                ret = snd_soc_read(codec, WM8996_DC_SERVO_2);
                dev_dbg(codec->dev, "DC servo state: %x\n", ret);
-       } while (ret & mask);
+       } while (timeout && ret & mask);
 
        if (timeout == 0)
                dev_err(codec->dev, "DC servo timed out for %x\n", mask);
@@ -979,9 +1041,12 @@ SND_SOC_DAPM_SUPPLY_S("SYSCLK", 1, WM8996_AIF_CLOCKING_1, 0, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY_S("SYSDSPCLK", 2, WM8996_CLOCKING_1, 1, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY_S("AIFCLK", 2, WM8996_CLOCKING_1, 2, 0, NULL, 0),
 SND_SOC_DAPM_SUPPLY_S("Charge Pump", 2, WM8996_CHARGE_PUMP_1, 15, 0, cp_event,
-                     SND_SOC_DAPM_POST_PMU),
-
+                     SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("Bandgap", SND_SOC_NOPM, 0, 0, bg_event,
+                   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
 SND_SOC_DAPM_SUPPLY("LDO2", WM8996_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICB1 Audio", WM8996_MICBIAS_1, 4, 1, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICB2 Audio", WM8996_MICBIAS_2, 4, 1, NULL, 0),
 SND_SOC_DAPM_MICBIAS("MICB2", WM8996_POWER_MANAGEMENT_1, 9, 0),
 SND_SOC_DAPM_MICBIAS("MICB1", WM8996_POWER_MANAGEMENT_1, 8, 0),
 
@@ -1035,14 +1100,14 @@ SND_SOC_DAPM_DAC("DAC2R", NULL, WM8996_POWER_MANAGEMENT_5, 2, 0),
 SND_SOC_DAPM_DAC("DAC1L", NULL, WM8996_POWER_MANAGEMENT_5, 1, 0),
 SND_SOC_DAPM_DAC("DAC1R", NULL, WM8996_POWER_MANAGEMENT_5, 0, 0),
 
-SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 1,
+SND_SOC_DAPM_AIF_IN("AIF2RX1", "AIF2 Playback", 0,
                    WM8996_POWER_MANAGEMENT_4, 9, 0),
-SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 2,
+SND_SOC_DAPM_AIF_IN("AIF2RX0", "AIF2 Playback", 1,
                    WM8996_POWER_MANAGEMENT_4, 8, 0),
 
-SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 1,
+SND_SOC_DAPM_AIF_IN("AIF2TX1", "AIF2 Capture", 0,
                    WM8996_POWER_MANAGEMENT_6, 9, 0),
-SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 2,
+SND_SOC_DAPM_AIF_IN("AIF2TX0", "AIF2 Capture", 1,
                    WM8996_POWER_MANAGEMENT_6, 8, 0),
 
 SND_SOC_DAPM_AIF_IN("AIF1RX5", "AIF1 Playback", 5,
@@ -1137,17 +1202,23 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
        { "Charge Pump", NULL, "SYSCLK" },
 
        { "MICB1", NULL, "LDO2" },
+       { "MICB1", NULL, "MICB1 Audio" },
+       { "MICB1", NULL, "Bandgap" },
        { "MICB2", NULL, "LDO2" },
+       { "MICB2", NULL, "MICB2 Audio" },
+       { "MICB2", NULL, "Bandgap" },
 
        { "IN1L PGA", NULL, "IN2LN" },
        { "IN1L PGA", NULL, "IN2LP" },
        { "IN1L PGA", NULL, "IN1LN" },
        { "IN1L PGA", NULL, "IN1LP" },
+       { "IN1L PGA", NULL, "Bandgap" },
 
        { "IN1R PGA", NULL, "IN2RN" },
        { "IN1R PGA", NULL, "IN2RP" },
        { "IN1R PGA", NULL, "IN1RN" },
        { "IN1R PGA", NULL, "IN1RP" },
+       { "IN1R PGA", NULL, "Bandgap" },
 
        { "ADCL", NULL, "IN1L PGA" },
 
@@ -1281,6 +1352,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
        { "DAC2R", NULL, "DAC2R Mixer" },
 
        { "HPOUT2L PGA", NULL, "Charge Pump" },
+       { "HPOUT2L PGA", NULL, "Bandgap" },
        { "HPOUT2L PGA", NULL, "DAC2L" },
        { "HPOUT2L_DLY", NULL, "HPOUT2L PGA" },
        { "HPOUT2L_DCS", NULL, "HPOUT2L_DLY" },
@@ -1288,6 +1360,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
        { "HPOUT2L_RMV_SHORT", NULL, "HPOUT2L_OUTP" },
 
        { "HPOUT2R PGA", NULL, "Charge Pump" },
+       { "HPOUT2R PGA", NULL, "Bandgap" },
        { "HPOUT2R PGA", NULL, "DAC2R" },
        { "HPOUT2R_DLY", NULL, "HPOUT2R PGA" },
        { "HPOUT2R_DCS", NULL, "HPOUT2R_DLY" },
@@ -1295,6 +1368,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
        { "HPOUT2R_RMV_SHORT", NULL, "HPOUT2R_OUTP" },
 
        { "HPOUT1L PGA", NULL, "Charge Pump" },
+       { "HPOUT1L PGA", NULL, "Bandgap" },
        { "HPOUT1L PGA", NULL, "DAC1L" },
        { "HPOUT1L_DLY", NULL, "HPOUT1L PGA" },
        { "HPOUT1L_DCS", NULL, "HPOUT1L_DLY" },
@@ -1302,6 +1376,7 @@ static const struct snd_soc_dapm_route wm8996_dapm_routes[] = {
        { "HPOUT1L_RMV_SHORT", NULL, "HPOUT1L_OUTP" },
 
        { "HPOUT1R PGA", NULL, "Charge Pump" },
+       { "HPOUT1R PGA", NULL, "Bandgap" },
        { "HPOUT1R PGA", NULL, "DAC1R" },
        { "HPOUT1R_DLY", NULL, "HPOUT1R PGA" },
        { "HPOUT1R_DCS", NULL, "HPOUT1R_DLY" },
@@ -1620,14 +1695,7 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
 
        switch (level) {
        case SND_SOC_BIAS_ON:
-               break;
-
        case SND_SOC_BIAS_PREPARE:
-               if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
-                       snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
-                                           WM8996_BG_ENA, WM8996_BG_ENA);
-                       msleep(2);
-               }
                break;
 
        case SND_SOC_BIAS_STANDBY:
@@ -1650,9 +1718,6 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
                        codec->cache_only = false;
                        snd_soc_cache_sync(codec);
                }
-
-               snd_soc_update_bits(codec, WM8996_POWER_MANAGEMENT_1,
-                                   WM8996_BG_ENA, 0);
                break;
 
        case SND_SOC_BIAS_OFF:
@@ -2041,7 +2106,7 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
        struct i2c_client *i2c = to_i2c_client(codec->dev);
        struct _fll_div fll_div;
        unsigned long timeout;
-       int ret, reg;
+       int ret, reg, retry;
 
        /* Any change? */
        if (source == wm8996->fll_src && Fref == wm8996->fll_fref &&
@@ -2057,6 +2122,8 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
                snd_soc_update_bits(codec, WM8996_FLL_CONTROL_1,
                                    WM8996_FLL_ENA, 0);
 
+               wm8996_bg_disable(codec);
+
                return 0;
        }
 
@@ -2111,6 +2178,11 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
 
        snd_soc_write(codec, WM8996_FLL_EFS_1, fll_div.lambda);
 
+       /* Enable the bandgap if it's not already enabled */
+       ret = snd_soc_read(codec, WM8996_FLL_CONTROL_1);
+       if (!(ret & WM8996_FLL_ENA))
+               wm8996_bg_enable(codec);
+
        /* Clear any pending completions (eg, from failed startups) */
        try_wait_for_completion(&wm8996->fll_lock);
 
@@ -2128,17 +2200,29 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
        else
                timeout = msecs_to_jiffies(2);
 
-       /* Allow substantially longer if we've actually got the IRQ */
+       /* Allow substantially longer if we've actually got the IRQ, poll
+        * at a slightly higher rate if we don't.
+        */
        if (i2c->irq)
-               timeout *= 1000;
+               timeout *= 10;
+       else
+               timeout /= 2;
 
-       ret = wait_for_completion_timeout(&wm8996->fll_lock, timeout);
+       for (retry = 0; retry < 10; retry++) {
+               ret = wait_for_completion_timeout(&wm8996->fll_lock,
+                                                 timeout);
+               if (ret != 0) {
+                       WARN_ON(!i2c->irq);
+                       break;
+               }
 
-       if (ret == 0 && i2c->irq) {
+               ret = snd_soc_read(codec, WM8996_INTERRUPT_RAW_STATUS_2);
+               if (ret & WM8996_FLL_LOCK_STS)
+                       break;
+       }
+       if (retry == 10) {
                dev_err(codec->dev, "Timed out waiting for FLL\n");
                ret = -ETIMEDOUT;
-       } else {
-               ret = 0;
        }
 
        dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
@@ -2297,12 +2381,94 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
 
        /* Enable interrupts and we're off */
        snd_soc_update_bits(codec, WM8996_INTERRUPT_STATUS_2_MASK,
-                           WM8996_IM_MICD_EINT, 0);
+                           WM8996_IM_MICD_EINT | WM8996_HP_DONE_EINT, 0);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(wm8996_detect);
 
+static void wm8996_hpdet_irq(struct snd_soc_codec *codec)
+{
+       struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
+       int val, reg, report;
+
+       /* Assume headphone in error conditions; we need to report
+        * something or we stall our state machine.
+        */
+       report = SND_JACK_HEADPHONE;
+
+       reg = snd_soc_read(codec, WM8996_HEADPHONE_DETECT_2);
+       if (reg < 0) {
+               dev_err(codec->dev, "Failed to read HPDET status\n");
+               goto out;
+       }
+
+       if (!(reg & WM8996_HP_DONE)) {
+               dev_err(codec->dev, "Got HPDET IRQ but HPDET is busy\n");
+               goto out;
+       }
+
+       val = reg & WM8996_HP_LVL_MASK;
+
+       dev_dbg(codec->dev, "HPDET measured %d ohms\n", val);
+
+       /* If we've got high enough impedence then report as line,
+        * otherwise assume headphone.
+        */
+       if (val >= 126)
+               report = SND_JACK_LINEOUT;
+       else
+               report = SND_JACK_HEADPHONE;
+
+out:
+       if (wm8996->jack_mic)
+               report |= SND_JACK_MICROPHONE;
+
+       snd_soc_jack_report(wm8996->jack, report,
+                           SND_JACK_LINEOUT | SND_JACK_HEADSET);
+
+       wm8996->detecting = false;
+
+       /* If the output isn't running re-clamp it */
+       if (!(snd_soc_read(codec, WM8996_POWER_MANAGEMENT_1) &
+             (WM8996_HPOUT1L_ENA | WM8996_HPOUT1R_RMV_SHORT)))
+               snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
+                                   WM8996_HPOUT1L_RMV_SHORT |
+                                   WM8996_HPOUT1R_RMV_SHORT, 0);
+
+       /* Go back to looking at the microphone */
+       snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1,
+                           WM8996_JD_MODE_MASK, 0);
+       snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA,
+                           WM8996_MICD_ENA);
+
+       snd_soc_dapm_disable_pin(&codec->dapm, "Bandgap");
+       snd_soc_dapm_sync(&codec->dapm);
+}
+
+static void wm8996_hpdet_start(struct snd_soc_codec *codec)
+{
+       /* Unclamp the output, we can't measure while we're shorting it */
+       snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
+                           WM8996_HPOUT1L_RMV_SHORT |
+                           WM8996_HPOUT1R_RMV_SHORT,
+                           WM8996_HPOUT1L_RMV_SHORT |
+                           WM8996_HPOUT1R_RMV_SHORT);
+
+       /* We need bandgap for HPDET */
+       snd_soc_dapm_force_enable_pin(&codec->dapm, "Bandgap");
+       snd_soc_dapm_sync(&codec->dapm);
+
+       /* Go into headphone detect left mode */
+       snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0);
+       snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1,
+                           WM8996_JD_MODE_MASK, 1);
+
+       /* Trigger a measurement */
+       snd_soc_update_bits(codec, WM8996_HEADPHONE_DETECT_1,
+                           WM8996_HP_POLL, WM8996_HP_POLL);
+}
+
 static void wm8996_micd(struct snd_soc_codec *codec)
 {
        struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
@@ -2323,28 +2489,36 @@ static void wm8996_micd(struct snd_soc_codec *codec)
                wm8996->jack_mic = false;
                wm8996->detecting = true;
                snd_soc_jack_report(wm8996->jack, 0,
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0);
+                                   SND_JACK_LINEOUT | SND_JACK_HEADSET |
+                                   SND_JACK_BTN_0);
+
                snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
                                    WM8996_MICD_RATE_MASK,
                                    WM8996_MICD_RATE_MASK);
                return;
        }
 
-       /* If the measurement is very high we've got a microphone but
-        * do a little debounce to account for mechanical issues.
+       /* If the measurement is very high we've got a microphone,
+        * either we just detected one or if we already reported then
+        * we've got a button release event.
         */
        if (val & 0x400) {
-               dev_dbg(codec->dev, "Microphone detected\n");
-               snd_soc_jack_report(wm8996->jack, SND_JACK_HEADSET,
-                                   SND_JACK_HEADSET | SND_JACK_BTN_0);
-               wm8996->jack_mic = true;
-               wm8996->detecting = false;
-
-               /* Increase poll rate to give better responsiveness
-                * for buttons */
-               snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
-                                   WM8996_MICD_RATE_MASK,
-                                   5 << WM8996_MICD_RATE_SHIFT);
+               if (wm8996->detecting) {
+                       dev_dbg(codec->dev, "Microphone detected\n");
+                       wm8996->jack_mic = true;
+                       wm8996_hpdet_start(codec);
+
+                       /* Increase poll rate to give better responsiveness
+                        * for buttons */
+                       snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
+                                           WM8996_MICD_RATE_MASK,
+                                           5 << WM8996_MICD_RATE_SHIFT);
+               } else {
+                       dev_dbg(codec->dev, "Mic button up\n");
+                       snd_soc_jack_report(wm8996->jack, 0, SND_JACK_BTN_0);
+               }
+
+               return;
        }
 
        /* If we detected a lower impedence during initial startup
@@ -2376,15 +2550,11 @@ static void wm8996_micd(struct snd_soc_codec *codec)
        if (val & 0x3fc) {
                if (wm8996->jack_mic) {
                        dev_dbg(codec->dev, "Mic button detected\n");
-                       snd_soc_jack_report(wm8996->jack,
-                                           SND_JACK_HEADSET | SND_JACK_BTN_0,
-                                           SND_JACK_HEADSET | SND_JACK_BTN_0);
-               } else {
-                       dev_dbg(codec->dev, "Headphone detected\n");
-                       snd_soc_jack_report(wm8996->jack,
-                                           SND_JACK_HEADPHONE,
-                                           SND_JACK_HEADSET |
+                       snd_soc_jack_report(wm8996->jack, SND_JACK_BTN_0,
                                            SND_JACK_BTN_0);
+               } else if (wm8996->detecting) {
+                       dev_dbg(codec->dev, "Headphone detected\n");
+                       wm8996_hpdet_start(codec);
 
                        /* Increase the detection rate a bit for
                         * responsiveness.
@@ -2392,8 +2562,6 @@ static void wm8996_micd(struct snd_soc_codec *codec)
                        snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
                                            WM8996_MICD_RATE_MASK,
                                            7 << WM8996_MICD_RATE_SHIFT);
-
-                       wm8996->detecting = false;
                }
        }
 }
@@ -2412,6 +2580,9 @@ static irqreturn_t wm8996_irq(int irq, void *data)
        }
        irq_val &= ~snd_soc_read(codec, WM8996_INTERRUPT_STATUS_2_MASK);
 
+       if (!irq_val)
+               return IRQ_NONE;
+
        snd_soc_write(codec, WM8996_INTERRUPT_STATUS_2, irq_val);
 
        if (irq_val & (WM8996_DCS_DONE_01_EINT | WM8996_DCS_DONE_23_EINT)) {
@@ -2430,10 +2601,10 @@ static irqreturn_t wm8996_irq(int irq, void *data)
        if (irq_val & WM8996_MICD_EINT)
                wm8996_micd(codec);
 
-       if (irq_val)
-               return IRQ_HANDLED;
-       else
-               return IRQ_NONE;
+       if (irq_val & WM8996_HP_DONE_EINT)
+               wm8996_hpdet_irq(codec);
+
+       return IRQ_HANDLED;
 }
 
 static irqreturn_t wm8996_edge_irq(int irq, void *data)
@@ -2548,7 +2719,13 @@ static int wm8996_probe(struct snd_soc_codec *codec)
        wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0;
        wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1;
        wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2;
-       wm8996->disable_nb[3].notifier_call = wm8996_regulator_event_3;
+
+       wm8996->cpvdd = regulator_get(&i2c->dev, "CPVDD");
+       if (IS_ERR(wm8996->cpvdd)) {
+               ret = PTR_ERR(wm8996->cpvdd);
+               dev_err(&i2c->dev, "Failed to get CPVDD: %d\n", ret);
+               goto err_get;
+       }
 
        /* This should really be moved into the regulator core */
        for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) {
@@ -2565,7 +2742,7 @@ static int wm8996_probe(struct snd_soc_codec *codec)
                                    wm8996->supplies);
        if (ret != 0) {
                dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
-               goto err_get;
+               goto err_cpvdd;
        }
 
        if (wm8996->pdata.ldo_ena >= 0) {
@@ -2808,6 +2985,8 @@ err_enable:
                gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
 
        regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
+err_cpvdd:
+       regulator_put(wm8996->cpvdd);
 err_get:
        regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
 err:
@@ -2831,6 +3010,7 @@ static int wm8996_remove(struct snd_soc_codec *codec)
        for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++)
                regulator_unregister_notifier(wm8996->supplies[i].consumer,
                                              &wm8996->disable_nb[i]);
+       regulator_put(wm8996->cpvdd);
        regulator_bulk_free(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
 
        return 0;
index a469132..f32ab1e 100644 (file)
@@ -1120,8 +1120,8 @@ static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute)
        return 0;
 }
 
-static int wm9081_set_sysclk(struct snd_soc_codec *codec,
-                            int clk_id, unsigned int freq, int dir)
+static int wm9081_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+                            int source, unsigned int freq, int dir)
 {
        struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
 
index 4de1220..f2f3077 100644 (file)
@@ -139,7 +139,6 @@ static const u16 wm9090_reg_defaults[] = {
 
 /* This struct is used to save the context */
 struct wm9090_priv {
-       struct mutex mutex;
        struct wm9090_platform_data pdata;
        void *control_data;
 };
@@ -663,7 +662,6 @@ static int wm9090_i2c_probe(struct i2c_client *i2c,
 
        i2c_set_clientdata(i2c, wm9090);
        wm9090->control_data = i2c;
-       mutex_init(&wm9090->mutex);
 
        ret =  snd_soc_register_codec(&i2c->dev,
                        &soc_codec_dev_wm9090,  NULL, 0);
index e763c54..ca8ce03 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/mfd/wm8994/registers.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -116,14 +117,23 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
 {
        struct wm_hubs_data *hubs = snd_soc_codec_get_drvdata(codec);
        s8 offset;
-       u16 reg, reg_l, reg_r, dcs_cfg;
+       u16 reg, reg_l, reg_r, dcs_cfg, dcs_reg;
+
+       switch (hubs->dcs_readback_mode) {
+       case 2:
+               dcs_reg = WM8994_DC_SERVO_4E;
+               break;
+       default:
+               dcs_reg = WM8993_DC_SERVO_3;
+               break;
+       }
 
        /* If we're using a digital only path and have a previously
         * callibrated DC servo offset stored then use that. */
        if (hubs->class_w && hubs->class_w_dcs) {
                dev_dbg(codec->dev, "Using cached DC servo offset %x\n",
                        hubs->class_w_dcs);
-               snd_soc_write(codec, WM8993_DC_SERVO_3, hubs->class_w_dcs);
+               snd_soc_write(codec, dcs_reg, hubs->class_w_dcs);
                wait_for_dc_servo(codec,
                                  WM8993_DCS_TRIG_DAC_WR_0 |
                                  WM8993_DCS_TRIG_DAC_WR_1);
@@ -154,8 +164,9 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
                reg_r = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2)
                        & WM8993_DCS_INTEG_CHAN_1_MASK;
                break;
+       case 2:
        case 1:
-               reg = snd_soc_read(codec, WM8993_DC_SERVO_3);
+               reg = snd_soc_read(codec, dcs_reg);
                reg_r = (reg & WM8993_DCS_DAC_WR_VAL_1_MASK)
                        >> WM8993_DCS_DAC_WR_VAL_1_SHIFT;
                reg_l = reg & WM8993_DCS_DAC_WR_VAL_0_MASK;
@@ -168,24 +179,25 @@ static void calibrate_dc_servo(struct snd_soc_codec *codec)
        dev_dbg(codec->dev, "DCS input: %x %x\n", reg_l, reg_r);
 
        /* Apply correction to DC servo result */
-       if (hubs->dcs_codes) {
-               dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
-                       hubs->dcs_codes);
+       if (hubs->dcs_codes_l || hubs->dcs_codes_r) {
+               dev_dbg(codec->dev,
+                       "Applying %d/%d code DC servo correction\n",
+                       hubs->dcs_codes_l, hubs->dcs_codes_r);
 
                /* HPOUT1R */
                offset = reg_r;
-               offset += hubs->dcs_codes;
+               offset += hubs->dcs_codes_r;
                dcs_cfg = (u8)offset << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
 
                /* HPOUT1L */
                offset = reg_l;
-               offset += hubs->dcs_codes;
+               offset += hubs->dcs_codes_l;
                dcs_cfg |= (u8)offset;
 
                dev_dbg(codec->dev, "DCS result: %x\n", dcs_cfg);
 
                /* Do it */
-               snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg);
+               snd_soc_write(codec, dcs_reg, dcs_cfg);
                wait_for_dc_servo(codec,
                                  WM8993_DCS_TRIG_DAC_WR_0 |
                                  WM8993_DCS_TRIG_DAC_WR_1);
@@ -217,7 +229,7 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
 
        /* If we're applying an offset correction then updating the
         * callibration would be likely to introduce further offsets. */
-       if (hubs->dcs_codes || hubs->no_series_update)
+       if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update)
                return ret;
 
        /* Only need to do this if the outputs are active */
@@ -699,6 +711,11 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
        { "IN1L PGA", "IN1LP Switch", "IN1LP" },
        { "IN1L PGA", "IN1LN Switch", "IN1LN" },
 
+       { "IN1L PGA", NULL, "VMID" },
+       { "IN1R PGA", NULL, "VMID" },
+       { "IN2L PGA", NULL, "VMID" },
+       { "IN2R PGA", NULL, "VMID" },
+
        { "IN1R PGA", "IN1RP Switch", "IN1RP" },
        { "IN1R PGA", "IN1RN Switch", "IN1RN" },
 
@@ -716,12 +733,14 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
        { "MIXINL", NULL, "Direct Voice" },
        { "MIXINL", NULL, "IN1LP" },
        { "MIXINL", NULL, "Left Output Mixer" },
+       { "MIXINL", NULL, "VMID" },
 
        { "MIXINR", "IN1R Switch", "IN1R PGA" },
        { "MIXINR", "IN2R Switch", "IN2R PGA" },
        { "MIXINR", NULL, "Direct Voice" },
        { "MIXINR", NULL, "IN1RP" },
        { "MIXINR", NULL, "Right Output Mixer" },
+       { "MIXINR", NULL, "VMID" },
 
        { "ADCL", NULL, "MIXINL" },
        { "ADCR", NULL, "MIXINR" },
@@ -752,6 +771,7 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
        { "Earpiece Mixer", "Left Output Switch", "Left Output PGA" },
        { "Earpiece Mixer", "Right Output Switch", "Right Output PGA" },
 
+       { "Earpiece Driver", NULL, "VMID" },
        { "Earpiece Driver", NULL, "Earpiece Mixer" },
        { "HPOUT2N", NULL, "Earpiece Driver" },
        { "HPOUT2P", NULL, "Earpiece Driver" },
@@ -774,9 +794,11 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
        { "SPKR Boost", "SPKR Switch", "SPKR" },
        { "SPKR Boost", "SPKL Switch", "SPKL" },
 
+       { "SPKL Driver", NULL, "VMID" },
        { "SPKL Driver", NULL, "SPKL Boost" },
        { "SPKL Driver", NULL, "CLK_SYS" },
 
+       { "SPKR Driver", NULL, "VMID" },
        { "SPKR Driver", NULL, "SPKR Boost" },
        { "SPKR Driver", NULL, "CLK_SYS" },
 
@@ -790,12 +812,18 @@ static const struct snd_soc_dapm_route analogue_routes[] = {
 
        { "Headphone PGA", NULL, "Left Headphone Mux" },
        { "Headphone PGA", NULL, "Right Headphone Mux" },
+       { "Headphone PGA", NULL, "VMID" },
        { "Headphone PGA", NULL, "CLK_SYS" },
        { "Headphone PGA", NULL, "Headphone Supply" },
 
        { "HPOUT1L", NULL, "Headphone PGA" },
        { "HPOUT1R", NULL, "Headphone PGA" },
 
+       { "LINEOUT1N Driver", NULL, "VMID" },
+       { "LINEOUT1P Driver", NULL, "VMID" },
+       { "LINEOUT2N Driver", NULL, "VMID" },
+       { "LINEOUT2P Driver", NULL, "VMID" },
+
        { "LINEOUT1N", NULL, "LINEOUT1N Driver" },
        { "LINEOUT1P", NULL, "LINEOUT1P Driver" },
        { "LINEOUT2N", NULL, "LINEOUT2N Driver" },
index 676b125..c674c7a 100644 (file)
@@ -23,7 +23,8 @@ extern const unsigned int wm_hubs_spkmix_tlv[];
 
 /* This *must* be the first element of the codec->private_data struct */
 struct wm_hubs_data {
-       int dcs_codes;
+       int dcs_codes_l;
+       int dcs_codes_r;
        int dcs_readback_mode;
        int hp_startup_mode;
        int series_startup;
index 8566238..7173df2 100644 (file)
@@ -732,16 +732,19 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
                davinci_hw_param(dev, substream->stream);
 
        switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_U8:
        case SNDRV_PCM_FORMAT_S8:
                dma_params->data_type = 1;
                word_length = DAVINCI_AUDIO_WORD_8;
                break;
 
+       case SNDRV_PCM_FORMAT_U16_LE:
        case SNDRV_PCM_FORMAT_S16_LE:
                dma_params->data_type = 2;
                word_length = DAVINCI_AUDIO_WORD_16;
                break;
 
+       case SNDRV_PCM_FORMAT_U32_LE:
        case SNDRV_PCM_FORMAT_S32_LE:
                dma_params->data_type = 4;
                word_length = DAVINCI_AUDIO_WORD_32;
@@ -818,6 +821,13 @@ static struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
 
 };
 
+#define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \
+                               SNDRV_PCM_FMTBIT_U8 | \
+                               SNDRV_PCM_FMTBIT_S16_LE | \
+                               SNDRV_PCM_FMTBIT_U16_LE | \
+                               SNDRV_PCM_FMTBIT_S32_LE | \
+                               SNDRV_PCM_FMTBIT_U32_LE)
+
 static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
        {
                .name           = "davinci-mcasp.0",
@@ -825,17 +835,13 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
                        .channels_min   = 2,
                        .channels_max   = 2,
                        .rates          = DAVINCI_MCASP_RATES,
-                       .formats        = SNDRV_PCM_FMTBIT_S8 |
-                                               SNDRV_PCM_FMTBIT_S16_LE |
-                                               SNDRV_PCM_FMTBIT_S32_LE,
+                       .formats        = DAVINCI_MCASP_PCM_FMTS,
                },
                .capture        = {
                        .channels_min   = 2,
                        .channels_max   = 2,
                        .rates          = DAVINCI_MCASP_RATES,
-                       .formats        = SNDRV_PCM_FMTBIT_S8 |
-                                               SNDRV_PCM_FMTBIT_S16_LE |
-                                               SNDRV_PCM_FMTBIT_S32_LE,
+                       .formats        = DAVINCI_MCASP_PCM_FMTS,
                },
                .ops            = &davinci_mcasp_dai_ops,
 
@@ -846,7 +852,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
                        .channels_min   = 1,
                        .channels_max   = 384,
                        .rates          = DAVINCI_MCASP_RATES,
-                       .formats        = SNDRV_PCM_FMTBIT_S16_LE,
+                       .formats        = DAVINCI_MCASP_PCM_FMTS,
                },
                .ops            = &davinci_mcasp_dai_ops,
        },
index a49e667..d5fe08c 100644 (file)
@@ -180,7 +180,6 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
 {
        struct davinci_runtime_data *prtd = substream->runtime->private_data;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       int link = prtd->asp_link[0];
        unsigned int period_size;
        unsigned int dma_offset;
        dma_addr_t dma_pos;
@@ -198,7 +197,8 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
        fifo_level = prtd->params->fifo_level;
 
        pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
-               "dma_ptr = %x period_size=%x\n", link, dma_pos, period_size);
+               "dma_ptr = %x period_size=%x\n", prtd->asp_link[0], dma_pos,
+               period_size);
 
        data_type = prtd->params->data_type;
        count = period_size / data_type;
@@ -222,17 +222,19 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream)
        }
 
        acnt = prtd->params->acnt;
-       edma_set_src(link, src, INCR, W8BIT);
-       edma_set_dest(link, dst, INCR, W8BIT);
+       edma_set_src(prtd->asp_link[0], src, INCR, W8BIT);
+       edma_set_dest(prtd->asp_link[0], dst, INCR, W8BIT);
 
-       edma_set_src_index(link, src_bidx, src_cidx);
-       edma_set_dest_index(link, dst_bidx, dst_cidx);
+       edma_set_src_index(prtd->asp_link[0], src_bidx, src_cidx);
+       edma_set_dest_index(prtd->asp_link[0], dst_bidx, dst_cidx);
 
        if (!fifo_level)
-               edma_set_transfer_params(link, acnt, count, 1, 0, ASYNC);
+               edma_set_transfer_params(prtd->asp_link[0], acnt, count, 1, 0,
+                                                       ASYNC);
        else
-               edma_set_transfer_params(link, acnt, fifo_level, count,
-                                                       fifo_level, ABSYNC);
+               edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level,
+                                                       count, fifo_level,
+                                                       ABSYNC);
 }
 
 static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data)
@@ -305,7 +307,6 @@ static int ping_pong_dma_setup(struct snd_pcm_substream *substream)
        unsigned int acnt = params->acnt;
        /* divide by 2 for ping/pong */
        unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1;
-       int link = prtd->asp_link[1];
        unsigned int fifo_level = prtd->params->fifo_level;
        unsigned int count;
        if ((data_type == 0) || (data_type > 4)) {
@@ -316,28 +317,26 @@ static int ping_pong_dma_setup(struct snd_pcm_substream *substream)
                dma_addr_t asp_src_pong = iram_dma->addr + ping_size;
                ram_src_cidx = ping_size;
                ram_dst_cidx = -ping_size;
-               edma_set_src(link, asp_src_pong, INCR, W8BIT);
+               edma_set_src(prtd->asp_link[1], asp_src_pong, INCR, W8BIT);
 
-               link = prtd->asp_link[0];
-               edma_set_src_index(link, data_type, data_type * fifo_level);
-               link = prtd->asp_link[1];
-               edma_set_src_index(link, data_type, data_type * fifo_level);
+               edma_set_src_index(prtd->asp_link[0], data_type,
+                               data_type * fifo_level);
+               edma_set_src_index(prtd->asp_link[1], data_type,
+                               data_type * fifo_level);
 
-               link = prtd->ram_link;
-               edma_set_src(link, runtime->dma_addr, INCR, W32BIT);
+               edma_set_src(prtd->ram_link, runtime->dma_addr, INCR, W32BIT);
        } else {
                dma_addr_t asp_dst_pong = iram_dma->addr + ping_size;
                ram_src_cidx = -ping_size;
                ram_dst_cidx = ping_size;
-               edma_set_dest(link, asp_dst_pong, INCR, W8BIT);
+               edma_set_dest(prtd->asp_link[1], asp_dst_pong, INCR, W8BIT);
 
-               link = prtd->asp_link[0];
-               edma_set_dest_index(link, data_type, data_type * fifo_level);
-               link = prtd->asp_link[1];
-               edma_set_dest_index(link, data_type, data_type * fifo_level);
+               edma_set_dest_index(prtd->asp_link[0], data_type,
+                               data_type * fifo_level);
+               edma_set_dest_index(prtd->asp_link[1], data_type,
+                               data_type * fifo_level);
 
-               link = prtd->ram_link;
-               edma_set_dest(link, runtime->dma_addr, INCR, W32BIT);
+               edma_set_dest(prtd->ram_link, runtime->dma_addr, INCR, W32BIT);
        }
 
        if (!fifo_level) {
@@ -354,10 +353,9 @@ static int ping_pong_dma_setup(struct snd_pcm_substream *substream)
                                count, fifo_level, ABSYNC);
        }
 
-       link = prtd->ram_link;
-       edma_set_src_index(link, ping_size, ram_src_cidx);
-       edma_set_dest_index(link, ping_size, ram_dst_cidx);
-       edma_set_transfer_params(link, ping_size, 2,
+       edma_set_src_index(prtd->ram_link, ping_size, ram_src_cidx);
+       edma_set_dest_index(prtd->ram_link, ping_size, ram_dst_cidx);
+       edma_set_transfer_params(prtd->ram_link, ping_size, 2,
                        runtime->periods, 2, ASYNC);
 
        /* init master params */
@@ -406,32 +404,32 @@ static int request_ping_pong(struct snd_pcm_substream *substream,
 {
        dma_addr_t asp_src_ping;
        dma_addr_t asp_dst_ping;
-       int link;
+       int ret;
        struct davinci_pcm_dma_params *params = prtd->params;
 
        /* Request ram master channel */
-       link = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY,
+       ret = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY,
                                  davinci_pcm_dma_irq, substream,
                                  prtd->params->ram_chan_q);
-       if (link < 0)
+       if (ret < 0)
                goto exit1;
 
        /* Request ram link channel */
-       link = prtd->ram_link = edma_alloc_slot(
+       ret = prtd->ram_link = edma_alloc_slot(
                        EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
-       if (link < 0)
+       if (ret < 0)
                goto exit2;
 
-       link = prtd->asp_link[1] = edma_alloc_slot(
+       ret = prtd->asp_link[1] = edma_alloc_slot(
                        EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
-       if (link < 0)
+       if (ret < 0)
                goto exit3;
 
        prtd->ram_link2 = -1;
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               link = prtd->ram_link2 = edma_alloc_slot(
+               ret = prtd->ram_link2 = edma_alloc_slot(
                        EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY);
-               if (link < 0)
+               if (ret < 0)
                        goto exit4;
        }
        /* circle ping-pong buffers */
@@ -448,36 +446,33 @@ static int request_ping_pong(struct snd_pcm_substream *substream,
                asp_dst_ping = iram_dma->addr;
        }
        /* ping */
-       link = prtd->asp_link[0];
-       edma_set_src(link, asp_src_ping, INCR, W16BIT);
-       edma_set_dest(link, asp_dst_ping, INCR, W16BIT);
-       edma_set_src_index(link, 0, 0);
-       edma_set_dest_index(link, 0, 0);
+       edma_set_src(prtd->asp_link[0], asp_src_ping, INCR, W16BIT);
+       edma_set_dest(prtd->asp_link[0], asp_dst_ping, INCR, W16BIT);
+       edma_set_src_index(prtd->asp_link[0], 0, 0);
+       edma_set_dest_index(prtd->asp_link[0], 0, 0);
 
-       edma_read_slot(link, &prtd->asp_params);
+       edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
        prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN);
        prtd->asp_params.opt |= TCCHEN |
                EDMA_TCC(prtd->ram_channel & 0x3f);
-       edma_write_slot(link, &prtd->asp_params);
+       edma_write_slot(prtd->asp_link[0], &prtd->asp_params);
 
        /* pong */
-       link = prtd->asp_link[1];
-       edma_set_src(link, asp_src_ping, INCR, W16BIT);
-       edma_set_dest(link, asp_dst_ping, INCR, W16BIT);
-       edma_set_src_index(link, 0, 0);
-       edma_set_dest_index(link, 0, 0);
+       edma_set_src(prtd->asp_link[1], asp_src_ping, INCR, W16BIT);
+       edma_set_dest(prtd->asp_link[1], asp_dst_ping, INCR, W16BIT);
+       edma_set_src_index(prtd->asp_link[1], 0, 0);
+       edma_set_dest_index(prtd->asp_link[1], 0, 0);
 
-       edma_read_slot(link, &prtd->asp_params);
+       edma_read_slot(prtd->asp_link[1], &prtd->asp_params);
        prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f));
        /* interrupt after every pong completion */
        prtd->asp_params.opt |= TCINTEN | TCCHEN |
                EDMA_TCC(prtd->ram_channel & 0x3f);
-       edma_write_slot(link, &prtd->asp_params);
+       edma_write_slot(prtd->asp_link[1], &prtd->asp_params);
 
        /* ram */
-       link = prtd->ram_link;
-       edma_set_src(link, iram_dma->addr, INCR, W32BIT);
-       edma_set_dest(link, iram_dma->addr, INCR, W32BIT);
+       edma_set_src(prtd->ram_link, iram_dma->addr, INCR, W32BIT);
+       edma_set_dest(prtd->ram_link, iram_dma->addr, INCR, W32BIT);
        pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u,"
                "for asp:%u %u %u\n", __func__,
                prtd->ram_channel, prtd->ram_link, prtd->ram_link2,
@@ -494,7 +489,7 @@ exit2:
        edma_free_channel(prtd->ram_channel);
        prtd->ram_channel = -1;
 exit1:
-       return link;
+       return ret;
 }
 
 static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
@@ -502,22 +497,22 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
        struct snd_dma_buffer *iram_dma;
        struct davinci_runtime_data *prtd = substream->runtime->private_data;
        struct davinci_pcm_dma_params *params = prtd->params;
-       int link;
+       int ret;
 
        if (!params)
                return -ENODEV;
 
        /* Request asp master DMA channel */
-       link = prtd->asp_channel = edma_alloc_channel(params->channel,
+       ret = prtd->asp_channel = edma_alloc_channel(params->channel,
                        davinci_pcm_dma_irq, substream,
                        prtd->params->asp_chan_q);
-       if (link < 0)
+       if (ret < 0)
                goto exit1;
 
        /* Request asp link channels */
-       link = prtd->asp_link[0] = edma_alloc_slot(
+       ret = prtd->asp_link[0] = edma_alloc_slot(
                        EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY);
-       if (link < 0)
+       if (ret < 0)
                goto exit2;
 
        iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data;
@@ -537,17 +532,17 @@ static int davinci_pcm_dma_request(struct snd_pcm_substream *substream)
         * the buffer and its length (ccnt) ... use it as a template
         * so davinci_pcm_enqueue_dma() takes less time in IRQ.
         */
-       edma_read_slot(link, &prtd->asp_params);
+       edma_read_slot(prtd->asp_link[0], &prtd->asp_params);
        prtd->asp_params.opt |= TCINTEN |
                EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel));
-       prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(link) << 5;
-       edma_write_slot(link, &prtd->asp_params);
+       prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(prtd->asp_link[0]) << 5;
+       edma_write_slot(prtd->asp_link[0], &prtd->asp_params);
        return 0;
 exit2:
        edma_free_channel(prtd->asp_channel);
        prtd->asp_channel = -1;
 exit1:
-       return link;
+       return ret;
 }
 
 static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
index d3aa151..0134d4e 100644 (file)
 #include <mach/hardware.h>
 #include "ep93xx-pcm.h"
 
-#define edb93xx_has_audio() (machine_is_edb9301() ||   \
-                            machine_is_edb9302() ||    \
-                            machine_is_edb9302a() ||   \
-                            machine_is_edb9307a() ||   \
-                            machine_is_edb9315a())
-
 static int edb93xx_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *params)
 {
@@ -94,49 +88,61 @@ static struct snd_soc_card snd_soc_edb93xx = {
        .num_links      = 1,
 };
 
-static struct platform_device *edb93xx_snd_device;
-
-static int __init edb93xx_init(void)
+static int __devinit edb93xx_probe(struct platform_device *pdev)
 {
+       struct snd_soc_card *card = &snd_soc_edb93xx;
        int ret;
 
-       if (!edb93xx_has_audio())
-               return -ENODEV;
-
        ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
                                 EP93XX_SYSCON_I2SCLKDIV_ORIDE |
                                 EP93XX_SYSCON_I2SCLKDIV_SPOL);
        if (ret)
                return ret;
 
-       edb93xx_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!edb93xx_snd_device) {
-               ret = -ENOMEM;
-               goto free_i2s;
+       card->dev = &pdev->dev;
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+                       ret);
+               ep93xx_i2s_release();
        }
 
-       platform_set_drvdata(edb93xx_snd_device, &snd_soc_edb93xx);
-       ret = platform_device_add(edb93xx_snd_device);
-       if (ret)
-               goto device_put;
+       return ret;
+}
 
-       return 0;
+static int __devexit edb93xx_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
 
-device_put:
-       platform_device_put(edb93xx_snd_device);
-free_i2s:
+       snd_soc_unregister_card(card);
        ep93xx_i2s_release();
-       return ret;
+
+       return 0;
+}
+
+static struct platform_driver edb93xx_driver = {
+       .driver         = {
+               .name   = "edb93xx-audio",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = edb93xx_probe,
+       .remove         = __devexit_p(edb93xx_remove),
+};
+
+static int __init edb93xx_init(void)
+{
+       return platform_driver_register(&edb93xx_driver);
 }
 module_init(edb93xx_init);
 
 static void __exit edb93xx_exit(void)
 {
-       platform_device_unregister(edb93xx_snd_device);
-       ep93xx_i2s_release();
+       platform_driver_unregister(&edb93xx_driver);
 }
 module_exit(edb93xx_exit);
 
 MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
 MODULE_DESCRIPTION("ALSA SoC EDB93xx");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:edb93xx-audio");
index 8dfd3ad..d00230a 100644 (file)
@@ -355,3 +355,4 @@ module_exit(ep93xx_soc_platform_exit);
 MODULE_AUTHOR("Ryan Mallon");
 MODULE_DESCRIPTION("EP93xx ALSA PCM interface");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ep93xx-pcm-audio");
index 2868179..968cb31 100644 (file)
@@ -39,53 +39,61 @@ static struct snd_soc_card snd_soc_simone = {
 };
 
 static struct platform_device *simone_snd_ac97_device;
-static struct platform_device *simone_snd_device;
 
-static int __init simone_init(void)
+static int __devinit simone_probe(struct platform_device *pdev)
 {
+       struct snd_soc_card *card = &snd_soc_simone;
        int ret;
 
-       if (!machine_is_sim_one())
-               return -ENODEV;
-
-       simone_snd_ac97_device = platform_device_alloc("ac97-codec", -1);
-       if (!simone_snd_ac97_device)
-               return -ENOMEM;
+       simone_snd_ac97_device = platform_device_register_simple("ac97-codec",
+                                                                -1, NULL, 0);
+       if (IS_ERR(simone_snd_ac97_device))
+               return PTR_ERR(simone_snd_ac97_device);
 
-       ret = platform_device_add(simone_snd_ac97_device);
-       if (ret)
-               goto fail1;
+       card->dev = &pdev->dev;
 
-       simone_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!simone_snd_device) {
-               ret = -ENOMEM;
-               goto fail2;
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+                       ret);
+               platform_device_unregister(simone_snd_ac97_device);
        }
 
-       platform_set_drvdata(simone_snd_device, &snd_soc_simone);
-       ret = platform_device_add(simone_snd_device);
-       if (ret)
-               goto fail3;
+       return ret;
+}
+
+static int __devexit simone_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(card);
+       platform_device_unregister(simone_snd_ac97_device);
 
        return 0;
+}
 
-fail3:
-       platform_device_put(simone_snd_device);
-fail2:
-       platform_device_del(simone_snd_ac97_device);
-fail1:
-       platform_device_put(simone_snd_ac97_device);
-       return ret;
+static struct platform_driver simone_driver = {
+       .driver         = {
+               .name   = "simone-audio",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = simone_probe,
+       .remove         = __devexit_p(simone_remove),
+};
+
+static int __init simone_init(void)
+{
+       return platform_driver_register(&simone_driver);
 }
 module_init(simone_init);
 
 static void __exit simone_exit(void)
 {
-       platform_device_unregister(simone_snd_device);
-       platform_device_unregister(simone_snd_ac97_device);
+       platform_driver_unregister(&simone_driver);
 }
 module_exit(simone_exit);
 
 MODULE_DESCRIPTION("ALSA SoC Simplemachines Sim.One");
 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:simone-audio");
index c8aa8a5..f74ac54 100644 (file)
@@ -104,37 +104,56 @@ static struct snd_soc_card snd_soc_snappercl15 = {
        .num_links      = 1,
 };
 
-static struct platform_device *snappercl15_snd_device;
-
-static int __init snappercl15_init(void)
+static int __devinit snappercl15_probe(struct platform_device *pdev)
 {
+       struct snd_soc_card *card = &snd_soc_snappercl15;
        int ret;
 
-       if (!machine_is_snapper_cl15())
-               return -ENODEV;
-
        ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
                                 EP93XX_SYSCON_I2SCLKDIV_ORIDE |
                                 EP93XX_SYSCON_I2SCLKDIV_SPOL);
        if (ret)
                return ret;
 
-       snappercl15_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!snappercl15_snd_device)
-               return -ENOMEM;
-       
-       platform_set_drvdata(snappercl15_snd_device, &snd_soc_snappercl15);
-       ret = platform_device_add(snappercl15_snd_device);
-       if (ret)
-               platform_device_put(snappercl15_snd_device);
+       card->dev = &pdev->dev;
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+                       ret);
+               ep93xx_i2s_release();
+       }
 
        return ret;
 }
 
-static void __exit snappercl15_exit(void)
+static int __devexit snappercl15_remove(struct platform_device *pdev)
 {
-       platform_device_unregister(snappercl15_snd_device);
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(card);
        ep93xx_i2s_release();
+
+       return 0;
+}
+
+static struct platform_driver snappercl15_driver = {
+       .driver         = {
+               .name   = "snappercl15-audio",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = snappercl15_probe,
+       .remove         = __devexit_p(snappercl15_remove),
+};
+
+static int __init snappercl15_init(void)
+{
+       return platform_driver_register(&snappercl15_driver);
+}
+
+static void __exit snappercl15_exit(void)
+{
+       platform_driver_unregister(&snappercl15_driver);
 }
 
 module_init(snappercl15_init);
@@ -143,4 +162,4 @@ module_exit(snappercl15_exit);
 MODULE_AUTHOR("Ryan Mallon");
 MODULE_DESCRIPTION("ALSA SoC Snapper CL15");
 MODULE_LICENSE("GPL");
-
+MODULE_ALIAS("platform:snappercl15-audio");
index cb50598..ef15402 100644 (file)
@@ -297,7 +297,6 @@ static irqreturn_t fsl_dma_isr(int irq, void *dev_id)
 static int fsl_dma_new(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_card *card = rtd->card->snd_card;
-       struct snd_soc_dai *dai = rtd->cpu_dai;
        struct snd_pcm *pcm = rtd->pcm;
        static u64 fsl_dma_dmamask = DMA_BIT_MASK(36);
        int ret;
index d48afea..0268cf9 100644 (file)
@@ -78,7 +78,6 @@
  * @second_stream: pointer to second stream
  * @playback: the number of playback streams opened
  * @capture: the number of capture streams opened
- * @asynchronous: 0=synchronous mode, 1=asynchronous mode
  * @cpu_dai: the CPU DAI for this device
  * @dev_attr: the sysfs device attribute structure
  * @stats: SSI statistics
@@ -90,9 +89,6 @@ struct fsl_ssi_private {
        unsigned int irq;
        struct snd_pcm_substream *first_stream;
        struct snd_pcm_substream *second_stream;
-       unsigned int playback;
-       unsigned int capture;
-       int asynchronous;
        unsigned int fifo_depth;
        struct snd_soc_dai_driver cpu_dai_drv;
        struct device_attribute dev_attr;
@@ -281,24 +277,18 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
                           struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+       struct fsl_ssi_private *ssi_private =
+               snd_soc_dai_get_drvdata(rtd->cpu_dai);
+       int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
 
        /*
         * If this is the first stream opened, then request the IRQ
         * and initialize the SSI registers.
         */
-       if (!ssi_private->playback && !ssi_private->capture) {
+       if (!ssi_private->first_stream) {
                struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-               int ret;
-
-               /* The 'name' should not have any slashes in it. */
-               ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0,
-                                 ssi_private->name, ssi_private);
-               if (ret < 0) {
-                       dev_err(substream->pcm->card->dev,
-                               "could not claim irq %u\n", ssi_private->irq);
-                       return ret;
-               }
+
+               ssi_private->first_stream = substream;
 
                /*
                 * Section 16.5 of the MPC8610 reference manual says that the
@@ -316,7 +306,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
                clrsetbits_be32(&ssi->scr,
                        CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
                        CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
-                       | (ssi_private->asynchronous ? 0 : CCSR_SSI_SCR_SYN));
+                       | (synchronous ? CCSR_SSI_SCR_SYN : 0));
 
                out_be32(&ssi->stcr,
                         CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
@@ -333,7 +323,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
                 * master.
                 */
 
-               /* 4. Enable the interrupts and DMA requests */
+               /* Enable the interrupts and DMA requests */
                out_be32(&ssi->sier, SIER_FLAGS);
 
                /*
@@ -362,58 +352,47 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
                 * this is bad is because at this point, the PCM driver has not
                 * finished initializing the DMA controller.
                 */
-       }
+       } else {
+               if (synchronous) {
+                       struct snd_pcm_runtime *first_runtime =
+                               ssi_private->first_stream->runtime;
+                       /*
+                        * This is the second stream open, and we're in
+                        * synchronous mode, so we need to impose sample
+                        * sample size constraints. This is because STCCR is
+                        * used for playback and capture in synchronous mode,
+                        * so there's no way to specify different word
+                        * lengths.
+                        *
+                        * Note that this can cause a race condition if the
+                        * second stream is opened before the first stream is
+                        * fully initialized.  We provide some protection by
+                        * checking to make sure the first stream is
+                        * initialized, but it's not perfect.  ALSA sometimes
+                        * re-initializes the driver with a different sample
+                        * rate or size.  If the second stream is opened
+                        * before the first stream has received its final
+                        * parameters, then the second stream may be
+                        * constrained to the wrong sample rate or size.
+                        */
+                       if (!first_runtime->sample_bits) {
+                               dev_err(substream->pcm->card->dev,
+                                       "set sample size in %s stream first\n",
+                                       substream->stream ==
+                                       SNDRV_PCM_STREAM_PLAYBACK
+                                       ? "capture" : "playback");
+                               return -EAGAIN;
+                       }
 
-       if (!ssi_private->first_stream)
-               ssi_private->first_stream = substream;
-       else {
-               /* This is the second stream open, so we need to impose sample
-                * rate and maybe sample size constraints.  Note that this can
-                * cause a race condition if the second stream is opened before
-                * the first stream is fully initialized.
-                *
-                * We provide some protection by checking to make sure the first
-                * stream is initialized, but it's not perfect.  ALSA sometimes
-                * re-initializes the driver with a different sample rate or
-                * size.  If the second stream is opened before the first stream
-                * has received its final parameters, then the second stream may
-                * be constrained to the wrong sample rate or size.
-                *
-                * FIXME: This code does not handle opening and closing streams
-                * repeatedly.  If you open two streams and then close the first
-                * one, you may not be able to open another stream until you
-                * close the second one as well.
-                */
-               struct snd_pcm_runtime *first_runtime =
-                       ssi_private->first_stream->runtime;
-
-               if (!first_runtime->sample_bits) {
-                       dev_err(substream->pcm->card->dev,
-                               "set sample size in %s stream first\n",
-                               substream->stream == SNDRV_PCM_STREAM_PLAYBACK
-                               ? "capture" : "playback");
-                       return -EAGAIN;
-               }
-
-               /* If we're in synchronous mode, then we need to constrain
-                * the sample size as well.  We don't support independent sample
-                * rates in asynchronous mode.
-                */
-               if (!ssi_private->asynchronous)
                        snd_pcm_hw_constraint_minmax(substream->runtime,
                                SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
                                first_runtime->sample_bits,
                                first_runtime->sample_bits);
+               }
 
                ssi_private->second_stream = substream;
        }
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               ssi_private->playback++;
-
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-               ssi_private->capture++;
-
        return 0;
 }
 
@@ -434,24 +413,35 @@ static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
 {
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(cpu_dai);
+       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       unsigned int sample_size =
+               snd_pcm_format_width(params_format(hw_params));
+       u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
+       int enabled = in_be32(&ssi->scr) & CCSR_SSI_SCR_SSIEN;
 
-       if (substream == ssi_private->first_stream) {
-               struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-               unsigned int sample_size =
-                       snd_pcm_format_width(params_format(hw_params));
-               u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
+       /*
+        * If we're in synchronous mode, and the SSI is already enabled,
+        * then STCCR is already set properly.
+        */
+       if (enabled && ssi_private->cpu_dai_drv.symmetric_rates)
+               return 0;
 
-               /* The SSI should always be disabled at this points (SSIEN=0) */
+       /*
+        * FIXME: The documentation says that SxCCR[WL] should not be
+        * modified while the SSI is enabled.  The only time this can
+        * happen is if we're trying to do simultaneous playback and
+        * capture in asynchronous mode.  Unfortunately, I have been enable
+        * to get that to work at all on the P1022DS.  Therefore, we don't
+        * bother to disable/enable the SSI when setting SxCCR[WL], because
+        * the SSI will stop anyway.  Maybe one day, this will get fixed.
+        */
 
-               /* In synchronous mode, the SSI uses STCCR for capture */
-               if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
-                   !ssi_private->asynchronous)
-                       clrsetbits_be32(&ssi->stccr,
-                                       CCSR_SSI_SxCCR_WL_MASK, wl);
-               else
-                       clrsetbits_be32(&ssi->srccr,
-                                       CCSR_SSI_SxCCR_WL_MASK, wl);
-       }
+       /* In synchronous mode, the SSI uses STCCR for capture */
+       if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
+           ssi_private->cpu_dai_drv.symmetric_rates)
+               clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
+       else
+               clrsetbits_be32(&ssi->srccr, CCSR_SSI_SxCCR_WL_MASK, wl);
 
        return 0;
 }
@@ -474,7 +464,6 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                        setbits32(&ssi->scr,
@@ -510,27 +499,18 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               ssi_private->playback--;
-
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-               ssi_private->capture--;
-
        if (ssi_private->first_stream == substream)
                ssi_private->first_stream = ssi_private->second_stream;
 
        ssi_private->second_stream = NULL;
 
        /*
-        * If this is the last active substream, disable the SSI and release
-        * the IRQ.
+        * If this is the last active substream, disable the SSI.
         */
-       if (!ssi_private->playback && !ssi_private->capture) {
+       if (!ssi_private->first_stream) {
                struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
 
                clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
-
-               free_irq(ssi_private->irq, ssi_private);
        }
 }
 
@@ -675,22 +655,33 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
        ret = of_address_to_resource(np, 0, &res);
        if (ret) {
                dev_err(&pdev->dev, "could not determine device resources\n");
-               kfree(ssi_private);
-               return ret;
+               goto error_kmalloc;
        }
        ssi_private->ssi = of_iomap(np, 0);
        if (!ssi_private->ssi) {
                dev_err(&pdev->dev, "could not map device resources\n");
-               kfree(ssi_private);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto error_kmalloc;
        }
        ssi_private->ssi_phys = res.start;
+
        ssi_private->irq = irq_of_parse_and_map(np, 0);
+       if (ssi_private->irq == NO_IRQ) {
+               dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+               ret = -ENXIO;
+               goto error_iomap;
+       }
+
+       /* The 'name' should not have any slashes in it. */
+       ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name,
+                         ssi_private);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq);
+               goto error_irqmap;
+       }
 
        /* Are the RX and the TX clocks locked? */
-       if (of_find_property(np, "fsl,ssi-asynchronous", NULL))
-               ssi_private->asynchronous = 1;
-       else
+       if (!of_find_property(np, "fsl,ssi-asynchronous", NULL))
                ssi_private->cpu_dai_drv.symmetric_rates = 1;
 
        /* Determine the FIFO depth. */
@@ -711,7 +702,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
        if (ret) {
                dev_err(&pdev->dev, "could not create sysfs %s file\n",
                        ssi_private->dev_attr.attr.name);
-               goto error;
+               goto error_irq;
        }
 
        /* Register with ASoC */
@@ -720,7 +711,7 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
        ret = snd_soc_register_dai(&pdev->dev, &ssi_private->cpu_dai_drv);
        if (ret) {
                dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
-               goto error;
+               goto error_dev;
        }
 
        /* Trigger the machine driver's probe function.  The platform driver
@@ -741,18 +732,28 @@ static int __devinit fsl_ssi_probe(struct platform_device *pdev)
        if (IS_ERR(ssi_private->pdev)) {
                ret = PTR_ERR(ssi_private->pdev);
                dev_err(&pdev->dev, "failed to register platform: %d\n", ret);
-               goto error;
+               goto error_dai;
        }
 
        return 0;
 
-error:
+error_dai:
        snd_soc_unregister_dai(&pdev->dev);
+
+error_dev:
        dev_set_drvdata(&pdev->dev, NULL);
-       if (dev_attr)
-               device_remove_file(&pdev->dev, dev_attr);
+       device_remove_file(&pdev->dev, dev_attr);
+
+error_irq:
+       free_irq(ssi_private->irq, ssi_private);
+
+error_irqmap:
        irq_dispose_mapping(ssi_private->irq);
+
+error_iomap:
        iounmap(ssi_private->ssi);
+
+error_kmalloc:
        kfree(ssi_private);
 
        return ret;
@@ -766,6 +767,9 @@ static int fsl_ssi_remove(struct platform_device *pdev)
        snd_soc_unregister_dai(&pdev->dev);
        device_remove_file(&pdev->dev, &ssi_private->dev_attr);
 
+       free_irq(ssi_private->irq, ssi_private);
+       irq_dispose_mapping(ssi_private->irq);
+
        kfree(ssi_private);
        dev_set_drvdata(&pdev->dev, NULL);
 
index 358f0ba..31af405 100644 (file)
@@ -505,7 +505,7 @@ static int mpc8610_hpcd_probe(struct platform_device *pdev)
        return 0;
 
 error_sound:
-       platform_device_unregister(sound_device);
+       platform_device_put(sound_device);
 error:
        kfree(machine_data);
 error_alloc:
index fcb862e..2c064a9 100644 (file)
@@ -267,7 +267,7 @@ static int codec_node_dev_name(struct device_node *np, char *buf, size_t len)
        if (bus < 0)
                return bus;
 
-       snprintf(buf, len, "%s-codec.%u-%04x", temp, bus, addr);
+       snprintf(buf, len, "%s.%u-%04x", temp, bus, addr);
 
        return 0;
 }
@@ -506,7 +506,7 @@ static int p1022_ds_probe(struct platform_device *pdev)
 
 error:
        if (sound_device)
-               platform_device_unregister(sound_device);
+               platform_device_put(sound_device);
 
        kfree(mdata);
 error_put:
index 7945625..c8527ea 100644 (file)
@@ -242,23 +242,22 @@ static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_dai *dai = rtd->cpu_dai;
        struct snd_pcm *pcm = rtd->pcm;
+       struct snd_pcm_substream *substream;
        int ret;
 
        ret = imx_pcm_new(rtd);
        if (ret)
                return ret;
 
-       if (dai->driver->playback.channels_min) {
-               struct snd_pcm_substream *substream =
-                       pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       if (substream) {
                struct snd_dma_buffer *buf = &substream->dma_buffer;
 
                imx_ssi_fiq_tx_buffer = (unsigned long)buf->area;
        }
 
-       if (dai->driver->capture.channels_min) {
-               struct snd_pcm_substream *substream =
-                       pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+       substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+       if (substream) {
                struct snd_dma_buffer *buf = &substream->dma_buffer;
 
                imx_ssi_fiq_rx_buffer = (unsigned long)buf->area;
index 10a8e27..4297cb6 100644 (file)
@@ -357,8 +357,8 @@ int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
        struct snd_pcm_runtime *runtime = substream->runtime;
        int ret;
 
-       ret = dma_mmap_coherent(NULL, vma, runtime->dma_area,
-                       runtime->dma_addr, runtime->dma_bytes);
+       ret = dma_mmap_writecombine(substream->pcm->card->dev, vma,
+               runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
 
        pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret,
                        runtime->dma_area,
@@ -399,14 +399,14 @@ int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
                card->dev->dma_mask = &imx_pcm_dmamask;
        if (!card->dev->coherent_dma_mask)
                card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
-       if (dai->driver->playback.channels_min) {
+       if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
                ret = imx_pcm_preallocate_dma_buffer(pcm,
                        SNDRV_PCM_STREAM_PLAYBACK);
                if (ret)
                        goto out;
        }
 
-       if (dai->driver->capture.channels_min) {
+       if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
                ret = imx_pcm_preallocate_dma_buffer(pcm,
                        SNDRV_PCM_STREAM_CAPTURE);
                if (ret)
index 3e78260..9925d20 100644 (file)
@@ -226,13 +226,18 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
 
 static int sst_platform_open(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_runtime *runtime;
+       struct snd_pcm_runtime *runtime = substream->runtime;
        struct sst_runtime_stream *stream;
        int ret_val = 0;
 
        pr_debug("sst_platform_open called\n");
-       runtime = substream->runtime;
-       runtime->hw = sst_platform_pcm_hw;
+
+       snd_soc_set_runtime_hwparams(substream, &sst_platform_pcm_hw);
+       ret_val = snd_pcm_hw_constraint_integer(runtime,
+                                               SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret_val < 0)
+               return ret_val;
+
        stream = kzalloc(sizeof(*stream), GFP_KERNEL);
        if (!stream)
                return -ENOMEM;
@@ -259,8 +264,8 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
                return ret_val;
        }
        runtime->private_data = stream;
-       return snd_pcm_hw_constraint_integer(runtime,
-                        SNDRV_PCM_HW_PARAM_PERIODS);
+
+       return 0;
 }
 
 static int sst_platform_close(struct snd_pcm_substream *substream)
@@ -469,7 +474,7 @@ static struct platform_driver sst_platform_driver = {
 static int __init sst_soc_platform_init(void)
 {
        pr_debug("sst_soc_platform_init called\n");
-       return  platform_driver_register(&sst_platform_driver);
+       return platform_driver_register(&sst_platform_driver);
 }
 module_init(sst_soc_platform_init);
 
diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig
new file mode 100644 (file)
index 0000000..e4ba8d5
--- /dev/null
@@ -0,0 +1,20 @@
+menuconfig SND_MXS_SOC
+       tristate "SoC Audio for Freescale MXS CPUs"
+       depends on ARCH_MXS
+       select SND_PCM
+       help
+         Say Y or M if you want to add support for codecs attached to
+         the MXS SAIF interface.
+
+
+if SND_MXS_SOC
+
+config SND_SOC_MXS_SGTL5000
+       tristate "SoC Audio support for i.MX boards with sgtl5000"
+       depends on I2C
+       select SND_SOC_SGTL5000
+       help
+         Say Y if you want to add support for SoC audio on an MXS board with
+         a sgtl5000 codec.
+
+endif  # SND_MXS_SOC
diff --git a/sound/soc/mxs/Makefile b/sound/soc/mxs/Makefile
new file mode 100644 (file)
index 0000000..565b5b5
--- /dev/null
@@ -0,0 +1,10 @@
+# MXS Platform Support
+snd-soc-mxs-objs := mxs-saif.o
+snd-soc-mxs-pcm-objs := mxs-pcm.o
+
+obj-$(CONFIG_SND_MXS_SOC) += snd-soc-mxs.o snd-soc-mxs-pcm.o
+
+# i.MX Machine Support
+snd-soc-mxs-sgtl5000-objs := mxs-sgtl5000.o
+
+obj-$(CONFIG_SND_SOC_MXS_SGTL5000) += snd-soc-mxs-sgtl5000.o
diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c
new file mode 100644 (file)
index 0000000..dea5aa4
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Based on sound/soc/imx/imx-pcm-dma-mx2.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dmaengine.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/dma.h>
+#include "mxs-pcm.h"
+
+static struct snd_pcm_hardware snd_mxs_hardware = {
+       .info                   = SNDRV_PCM_INFO_MMAP |
+                                 SNDRV_PCM_INFO_MMAP_VALID |
+                                 SNDRV_PCM_INFO_PAUSE |
+                                 SNDRV_PCM_INFO_RESUME |
+                                 SNDRV_PCM_INFO_INTERLEAVED,
+       .formats                = SNDRV_PCM_FMTBIT_S16_LE |
+                                 SNDRV_PCM_FMTBIT_S20_3LE |
+                                 SNDRV_PCM_FMTBIT_S24_LE,
+       .channels_min           = 2,
+       .channels_max           = 2,
+       .period_bytes_min       = 32,
+       .period_bytes_max       = 8192,
+       .periods_min            = 1,
+       .periods_max            = 52,
+       .buffer_bytes_max       = 64 * 1024,
+       .fifo_size              = 32,
+
+};
+
+static void audio_dma_irq(void *data)
+{
+       struct snd_pcm_substream *substream = (struct snd_pcm_substream *)data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct mxs_pcm_runtime_data *iprtd = runtime->private_data;
+
+       iprtd->offset += iprtd->period_bytes;
+       iprtd->offset %= iprtd->period_bytes * iprtd->periods;
+       snd_pcm_period_elapsed(substream);
+}
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+       struct mxs_pcm_runtime_data *iprtd = param;
+       struct mxs_pcm_dma_params *dma_params = iprtd->dma_params;
+
+       if (!mxs_dma_is_apbx(chan))
+               return false;
+
+       if (chan->chan_id != dma_params->chan_num)
+               return false;
+
+       chan->private = &iprtd->dma_data;
+
+       return true;
+}
+
+static int mxs_dma_alloc(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct mxs_pcm_runtime_data *iprtd = runtime->private_data;
+       dma_cap_mask_t mask;
+
+       iprtd->dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       iprtd->dma_data.chan_irq = iprtd->dma_params->chan_irq;
+       iprtd->dma_chan = dma_request_channel(mask, filter, iprtd);
+       if (!iprtd->dma_chan)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int snd_mxs_pcm_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct mxs_pcm_runtime_data *iprtd = runtime->private_data;
+       unsigned long dma_addr;
+       struct dma_chan *chan;
+       int ret;
+
+       ret = mxs_dma_alloc(substream, params);
+       if (ret)
+               return ret;
+       chan = iprtd->dma_chan;
+
+       iprtd->size = params_buffer_bytes(params);
+       iprtd->periods = params_periods(params);
+       iprtd->period_bytes = params_period_bytes(params);
+       iprtd->offset = 0;
+       iprtd->period_time = HZ / (params_rate(params) /
+                       params_period_size(params));
+
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+       dma_addr = runtime->dma_addr;
+
+       iprtd->buf = substream->dma_buffer.area;
+
+       iprtd->desc = chan->device->device_prep_dma_cyclic(chan, dma_addr,
+                       iprtd->period_bytes * iprtd->periods,
+                       iprtd->period_bytes,
+                       substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+                       DMA_TO_DEVICE : DMA_FROM_DEVICE);
+       if (!iprtd->desc) {
+               dev_err(&chan->dev->device, "cannot prepare slave dma\n");
+               return -EINVAL;
+       }
+
+       iprtd->desc->callback = audio_dma_irq;
+       iprtd->desc->callback_param = substream;
+
+       return 0;
+}
+
+static int snd_mxs_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct mxs_pcm_runtime_data *iprtd = runtime->private_data;
+
+       if (iprtd->dma_chan) {
+               dma_release_channel(iprtd->dma_chan);
+               iprtd->dma_chan = NULL;
+       }
+
+       return 0;
+}
+
+static int snd_mxs_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct mxs_pcm_runtime_data *iprtd = runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               dmaengine_submit(iprtd->desc);
+
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               dmaengine_terminate_all(iprtd->dma_chan);
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t snd_mxs_pcm_pointer(
+               struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct mxs_pcm_runtime_data *iprtd = runtime->private_data;
+
+       return bytes_to_frames(substream->runtime, iprtd->offset);
+}
+
+static int snd_mxs_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct mxs_pcm_runtime_data *iprtd;
+       int ret;
+
+       iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
+       if (iprtd == NULL)
+               return -ENOMEM;
+       runtime->private_data = iprtd;
+
+       ret = snd_pcm_hw_constraint_integer(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_PERIODS);
+       if (ret < 0) {
+               kfree(iprtd);
+               return ret;
+       }
+
+       snd_soc_set_runtime_hwparams(substream, &snd_mxs_hardware);
+
+       return 0;
+}
+
+static int snd_mxs_close(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct mxs_pcm_runtime_data *iprtd = runtime->private_data;
+
+       kfree(iprtd);
+
+       return 0;
+}
+
+static int snd_mxs_pcm_mmap(struct snd_pcm_substream *substream,
+               struct vm_area_struct *vma)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+                                       runtime->dma_area,
+                                       runtime->dma_addr,
+                                       runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops mxs_pcm_ops = {
+       .open           = snd_mxs_open,
+       .close          = snd_mxs_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = snd_mxs_pcm_hw_params,
+       .hw_free        = snd_mxs_pcm_hw_free,
+       .trigger        = snd_mxs_pcm_trigger,
+       .pointer        = snd_mxs_pcm_pointer,
+       .mmap           = snd_mxs_pcm_mmap,
+};
+
+static int mxs_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+       struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+       struct snd_dma_buffer *buf = &substream->dma_buffer;
+       size_t size = snd_mxs_hardware.buffer_bytes_max;
+
+       buf->dev.type = SNDRV_DMA_TYPE_DEV;
+       buf->dev.dev = pcm->card->dev;
+       buf->private_data = NULL;
+       buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+                                          &buf->addr, GFP_KERNEL);
+       if (!buf->area)
+               return -ENOMEM;
+       buf->bytes = size;
+
+       return 0;
+}
+
+static u64 mxs_pcm_dmamask = DMA_BIT_MASK(32);
+static int mxs_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_pcm *pcm = rtd->pcm;
+       int ret = 0;
+
+       if (!card->dev->dma_mask)
+               card->dev->dma_mask = &mxs_pcm_dmamask;
+       if (!card->dev->coherent_dma_mask)
+               card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+       if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+               ret = mxs_pcm_preallocate_dma_buffer(pcm,
+                       SNDRV_PCM_STREAM_PLAYBACK);
+               if (ret)
+                       goto out;
+       }
+
+       if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+               ret = mxs_pcm_preallocate_dma_buffer(pcm,
+                       SNDRV_PCM_STREAM_CAPTURE);
+               if (ret)
+                       goto out;
+       }
+
+out:
+       return ret;
+}
+
+static void mxs_pcm_free(struct snd_pcm *pcm)
+{
+       struct snd_pcm_substream *substream;
+       struct snd_dma_buffer *buf;
+       int stream;
+
+       for (stream = 0; stream < 2; stream++) {
+               substream = pcm->streams[stream].substream;
+               if (!substream)
+                       continue;
+
+               buf = &substream->dma_buffer;
+               if (!buf->area)
+                       continue;
+
+               dma_free_writecombine(pcm->card->dev, buf->bytes,
+                                     buf->area, buf->addr);
+               buf->area = NULL;
+       }
+}
+
+static struct snd_soc_platform_driver mxs_soc_platform = {
+       .ops            = &mxs_pcm_ops,
+       .pcm_new        = mxs_pcm_new,
+       .pcm_free       = mxs_pcm_free,
+};
+
+static int __devinit mxs_soc_platform_probe(struct platform_device *pdev)
+{
+       return snd_soc_register_platform(&pdev->dev, &mxs_soc_platform);
+}
+
+static int __devexit mxs_soc_platform_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_platform(&pdev->dev);
+
+       return 0;
+}
+
+static struct platform_driver mxs_pcm_driver = {
+       .driver = {
+               .name = "mxs-pcm-audio",
+               .owner = THIS_MODULE,
+       },
+       .probe = mxs_soc_platform_probe,
+       .remove = __devexit_p(mxs_soc_platform_remove),
+};
+
+static int __init snd_mxs_pcm_init(void)
+{
+       return platform_driver_register(&mxs_pcm_driver);
+}
+module_init(snd_mxs_pcm_init);
+
+static void __exit snd_mxs_pcm_exit(void)
+{
+       platform_driver_unregister(&mxs_pcm_driver);
+}
+module_exit(snd_mxs_pcm_exit);
diff --git a/sound/soc/mxs/mxs-pcm.h b/sound/soc/mxs/mxs-pcm.h
new file mode 100644 (file)
index 0000000..f55ac4f
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _MXS_PCM_H
+#define _MXS_PCM_H
+
+#include <mach/dma.h>
+
+struct mxs_pcm_dma_params {
+       int chan_irq;
+       int chan_num;
+};
+
+struct mxs_pcm_runtime_data {
+       int period_bytes;
+       int periods;
+       int dma;
+       unsigned long offset;
+       unsigned long size;
+       void *buf;
+       int period_time;
+       struct dma_async_tx_descriptor *desc;
+       struct dma_chan *dma_chan;
+       struct mxs_dma_data dma_data;
+       struct mxs_pcm_dma_params *dma_params;
+};
+
+#endif
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
new file mode 100644 (file)
index 0000000..401944c
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/saif.h>
+#include <mach/dma.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/mxs.h>
+
+#include "mxs-saif.h"
+
+static struct mxs_saif *mxs_saif[2];
+
+/*
+ * SAIF is a little different with other normal SOC DAIs on clock using.
+ *
+ * For MXS, two SAIF modules are instantiated on-chip.
+ * Each SAIF has a set of clock pins and can be operating in master
+ * mode simultaneously if they are connected to different off-chip codecs.
+ * Also, one of the two SAIFs can master or drive the clock pins while the
+ * other SAIF, in slave mode, receives clocking from the master SAIF.
+ * This also means that both SAIFs must operate at the same sample rate.
+ *
+ * We abstract this as each saif has a master, the master could be
+ * himself or other saifs. In the generic saif driver, saif does not need
+ * to know the different clkmux. Saif only needs to know who is his master
+ * and operating his master to generate the proper clock rate for him.
+ * The master id is provided in mach-specific layer according to different
+ * clkmux setting.
+ */
+
+static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+                       int clk_id, unsigned int freq, int dir)
+{
+       struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
+
+       switch (clk_id) {
+       case MXS_SAIF_MCLK:
+               saif->mclk = freq;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/*
+ * Since SAIF may work on EXTMASTER mode, IOW, it's working BITCLK&LRCLK
+ * is provided by other SAIF, we provide a interface here to get its master
+ * from its master_id.
+ * Note that the master could be himself.
+ */
+static inline struct mxs_saif *mxs_saif_get_master(struct mxs_saif * saif)
+{
+       return mxs_saif[saif->master_id];
+}
+
+/*
+ * Set SAIF clock and MCLK
+ */
+static int mxs_saif_set_clk(struct mxs_saif *saif,
+                                 unsigned int mclk,
+                                 unsigned int rate)
+{
+       u32 scr;
+       int ret;
+       struct mxs_saif *master_saif;
+
+       dev_dbg(saif->dev, "mclk %d rate %d\n", mclk, rate);
+
+       /* Set master saif to generate proper clock */
+       master_saif = mxs_saif_get_master(saif);
+       if (!master_saif)
+               return -EINVAL;
+
+       dev_dbg(saif->dev, "master saif%d\n", master_saif->id);
+
+       /* Checking if can playback and capture simutaneously */
+       if (master_saif->ongoing && rate != master_saif->cur_rate) {
+               dev_err(saif->dev,
+                       "can not change clock, master saif%d(rate %d) is ongoing\n",
+                       master_saif->id, master_saif->cur_rate);
+               return -EINVAL;
+       }
+
+       scr = __raw_readl(master_saif->base + SAIF_CTRL);
+       scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE;
+       scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
+
+       /*
+        * Set SAIF clock
+        *
+        * The SAIF clock should be either 384*fs or 512*fs.
+        * If MCLK is used, the SAIF clk ratio need to match mclk ratio.
+        *  For 32x mclk, set saif clk as 512*fs.
+        *  For 48x mclk, set saif clk as 384*fs.
+        *
+        * If MCLK is not used, we just set saif clk to 512*fs.
+        */
+       if (master_saif->mclk_in_use) {
+               if (mclk % 32 == 0) {
+                       scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
+                       ret = clk_set_rate(master_saif->clk, 512 * rate);
+               } else if (mclk % 48 == 0) {
+                       scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE;
+                       ret = clk_set_rate(master_saif->clk, 384 * rate);
+               } else {
+                       /* SAIF MCLK should be either 32x or 48x */
+                       return -EINVAL;
+               }
+       } else {
+               ret = clk_set_rate(master_saif->clk, 512 * rate);
+               scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE;
+       }
+
+       if (ret)
+               return ret;
+
+       master_saif->cur_rate = rate;
+
+       if (!master_saif->mclk_in_use) {
+               __raw_writel(scr, master_saif->base + SAIF_CTRL);
+               return 0;
+       }
+
+       /*
+        * Program the over-sample rate for MCLK output
+        *
+        * The available MCLK range is 32x, 48x... 512x. The rate
+        * could be from 8kHz to 192kH.
+        */
+       switch (mclk / rate) {
+       case 32:
+               scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(4);
+               break;
+       case 64:
+               scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3);
+               break;
+       case 128:
+               scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2);
+               break;
+       case 256:
+               scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1);
+               break;
+       case 512:
+               scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0);
+               break;
+       case 48:
+               scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(3);
+               break;
+       case 96:
+               scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(2);
+               break;
+       case 192:
+               scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(1);
+               break;
+       case 384:
+               scr |= BF_SAIF_CTRL_BITCLK_MULT_RATE(0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       __raw_writel(scr, master_saif->base + SAIF_CTRL);
+
+       return 0;
+}
+
+/*
+ * Put and disable MCLK.
+ */
+int mxs_saif_put_mclk(unsigned int saif_id)
+{
+       struct mxs_saif *saif = mxs_saif[saif_id];
+       u32 stat;
+
+       if (!saif)
+               return -EINVAL;
+
+       stat = __raw_readl(saif->base + SAIF_STAT);
+       if (stat & BM_SAIF_STAT_BUSY) {
+               dev_err(saif->dev, "error: busy\n");
+               return -EBUSY;
+       }
+
+       clk_disable(saif->clk);
+
+       /* disable MCLK output */
+       __raw_writel(BM_SAIF_CTRL_CLKGATE,
+               saif->base + SAIF_CTRL + MXS_SET_ADDR);
+       __raw_writel(BM_SAIF_CTRL_RUN,
+               saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+
+       saif->mclk_in_use = 0;
+       return 0;
+}
+
+/*
+ * Get MCLK and set clock rate, then enable it
+ *
+ * This interface is used for codecs who are using MCLK provided
+ * by saif.
+ */
+int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk,
+                                       unsigned int rate)
+{
+       struct mxs_saif *saif = mxs_saif[saif_id];
+       u32 stat;
+       int ret;
+       struct mxs_saif *master_saif;
+
+       if (!saif)
+               return -EINVAL;
+
+       /* Clear Reset */
+       __raw_writel(BM_SAIF_CTRL_SFTRST,
+               saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+
+       /* FIXME: need clear clk gate for register r/w */
+       __raw_writel(BM_SAIF_CTRL_CLKGATE,
+               saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+
+       master_saif = mxs_saif_get_master(saif);
+       if (saif != master_saif) {
+               dev_err(saif->dev, "can not get mclk from a non-master saif\n");
+               return -EINVAL;
+       }
+
+       stat = __raw_readl(saif->base + SAIF_STAT);
+       if (stat & BM_SAIF_STAT_BUSY) {
+               dev_err(saif->dev, "error: busy\n");
+               return -EBUSY;
+       }
+
+       saif->mclk_in_use = 1;
+       ret = mxs_saif_set_clk(saif, mclk, rate);
+       if (ret)
+               return ret;
+
+       ret = clk_enable(saif->clk);
+       if (ret)
+               return ret;
+
+       /* enable MCLK output */
+       __raw_writel(BM_SAIF_CTRL_RUN,
+               saif->base + SAIF_CTRL + MXS_SET_ADDR);
+
+       return 0;
+}
+
+/*
+ * SAIF DAI format configuration.
+ * Should only be called when port is inactive.
+ */
+static int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+       u32 scr, stat;
+       u32 scr0;
+       struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
+
+       stat = __raw_readl(saif->base + SAIF_STAT);
+       if (stat & BM_SAIF_STAT_BUSY) {
+               dev_err(cpu_dai->dev, "error: busy\n");
+               return -EBUSY;
+       }
+
+       scr0 = __raw_readl(saif->base + SAIF_CTRL);
+       scr0 = scr0 & ~BM_SAIF_CTRL_BITCLK_EDGE & ~BM_SAIF_CTRL_LRCLK_POLARITY \
+               & ~BM_SAIF_CTRL_JUSTIFY & ~BM_SAIF_CTRL_DELAY;
+       scr = 0;
+
+       /* DAI mode */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               /* data frame low 1clk before data */
+               scr |= BM_SAIF_CTRL_DELAY;
+               scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               /* data frame high with data */
+               scr &= ~BM_SAIF_CTRL_DELAY;
+               scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
+               scr &= ~BM_SAIF_CTRL_JUSTIFY;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* DAI clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_IB_IF:
+               scr |= BM_SAIF_CTRL_BITCLK_EDGE;
+               scr |= BM_SAIF_CTRL_LRCLK_POLARITY;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               scr |= BM_SAIF_CTRL_BITCLK_EDGE;
+               scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               scr &= ~BM_SAIF_CTRL_BITCLK_EDGE;
+               scr |= BM_SAIF_CTRL_LRCLK_POLARITY;
+               break;
+       case SND_SOC_DAIFMT_NB_NF:
+               scr &= ~BM_SAIF_CTRL_BITCLK_EDGE;
+               scr &= ~BM_SAIF_CTRL_LRCLK_POLARITY;
+               break;
+       }
+
+       /*
+        * Note: We simply just support master mode since SAIF TX can only
+        * work as master.
+        * Here the master is relative to codec side.
+        * Saif internally could be slave when working on EXTMASTER mode.
+        * We just hide this to machine driver.
+        */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               if (saif->id == saif->master_id)
+                       scr &= ~BM_SAIF_CTRL_SLAVE_MODE;
+               else
+                       scr |= BM_SAIF_CTRL_SLAVE_MODE;
+
+               __raw_writel(scr | scr0, saif->base + SAIF_CTRL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int mxs_saif_startup(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *cpu_dai)
+{
+       struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
+       snd_soc_dai_set_dma_data(cpu_dai, substream, &saif->dma_param);
+
+       /* clear error status to 0 for each re-open */
+       saif->fifo_underrun = 0;
+       saif->fifo_overrun = 0;
+
+       /* Clear Reset for normal operations */
+       __raw_writel(BM_SAIF_CTRL_SFTRST,
+               saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+
+       /* clear clock gate */
+       __raw_writel(BM_SAIF_CTRL_CLKGATE,
+               saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+
+       return 0;
+}
+
+/*
+ * Should only be called when port is inactive.
+ * although can be called multiple times by upper layers.
+ */
+static int mxs_saif_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params,
+                            struct snd_soc_dai *cpu_dai)
+{
+       struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
+       u32 scr, stat;
+       int ret;
+
+       /* mclk should already be set */
+       if (!saif->mclk && saif->mclk_in_use) {
+               dev_err(cpu_dai->dev, "set mclk first\n");
+               return -EINVAL;
+       }
+
+       stat = __raw_readl(saif->base + SAIF_STAT);
+       if (stat & BM_SAIF_STAT_BUSY) {
+               dev_err(cpu_dai->dev, "error: busy\n");
+               return -EBUSY;
+       }
+
+       /*
+        * Set saif clk based on sample rate.
+        * If mclk is used, we also set mclk, if not, saif->mclk is
+        * default 0, means not used.
+        */
+       ret = mxs_saif_set_clk(saif, saif->mclk, params_rate(params));
+       if (ret) {
+               dev_err(cpu_dai->dev, "unable to get proper clk\n");
+               return ret;
+       }
+
+       scr = __raw_readl(saif->base + SAIF_CTRL);
+
+       scr &= ~BM_SAIF_CTRL_WORD_LENGTH;
+       scr &= ~BM_SAIF_CTRL_BITCLK_48XFS_ENABLE;
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               scr |= BF_SAIF_CTRL_WORD_LENGTH(0);
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               scr |= BF_SAIF_CTRL_WORD_LENGTH(4);
+               scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               scr |= BF_SAIF_CTRL_WORD_LENGTH(8);
+               scr |= BM_SAIF_CTRL_BITCLK_48XFS_ENABLE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Tx/Rx config */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* enable TX mode */
+               scr &= ~BM_SAIF_CTRL_READ_MODE;
+       } else {
+               /* enable RX mode */
+               scr |= BM_SAIF_CTRL_READ_MODE;
+       }
+
+       __raw_writel(scr, saif->base + SAIF_CTRL);
+       return 0;
+}
+
+static int mxs_saif_prepare(struct snd_pcm_substream *substream,
+                          struct snd_soc_dai *cpu_dai)
+{
+       struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
+
+       /* enable FIFO error irqs */
+       __raw_writel(BM_SAIF_CTRL_FIFO_ERROR_IRQ_EN,
+               saif->base + SAIF_CTRL + MXS_SET_ADDR);
+
+       return 0;
+}
+
+static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd,
+                               struct snd_soc_dai *cpu_dai)
+{
+       struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
+       struct mxs_saif *master_saif;
+       u32 delay;
+
+       master_saif = mxs_saif_get_master(saif);
+       if (!master_saif)
+               return -EINVAL;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               dev_dbg(cpu_dai->dev, "start\n");
+
+               clk_enable(master_saif->clk);
+               if (!master_saif->mclk_in_use)
+                       __raw_writel(BM_SAIF_CTRL_RUN,
+                               master_saif->base + SAIF_CTRL + MXS_SET_ADDR);
+
+               /*
+                * If the saif's master is not himself, we also need to enable
+                * itself clk for its internal basic logic to work.
+                */
+               if (saif != master_saif) {
+                       clk_enable(saif->clk);
+                       __raw_writel(BM_SAIF_CTRL_RUN,
+                               saif->base + SAIF_CTRL + MXS_SET_ADDR);
+               }
+
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       /*
+                        * write a data to saif data register to trigger
+                        * the transfer
+                        */
+                       __raw_writel(0, saif->base + SAIF_DATA);
+               } else {
+                       /*
+                        * read a data from saif data register to trigger
+                        * the receive
+                        */
+                       __raw_readl(saif->base + SAIF_DATA);
+               }
+
+               master_saif->ongoing = 1;
+
+               dev_dbg(saif->dev, "CTRL 0x%x STAT 0x%x\n",
+                       __raw_readl(saif->base + SAIF_CTRL),
+                       __raw_readl(saif->base + SAIF_STAT));
+
+               dev_dbg(master_saif->dev, "CTRL 0x%x STAT 0x%x\n",
+                       __raw_readl(master_saif->base + SAIF_CTRL),
+                       __raw_readl(master_saif->base + SAIF_STAT));
+               break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               dev_dbg(cpu_dai->dev, "stop\n");
+
+               /* wait a while for the current sample to complete */
+               delay = USEC_PER_SEC / master_saif->cur_rate;
+
+               if (!master_saif->mclk_in_use) {
+                       __raw_writel(BM_SAIF_CTRL_RUN,
+                               master_saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+                       udelay(delay);
+               }
+               clk_disable(master_saif->clk);
+
+               if (saif != master_saif) {
+                       __raw_writel(BM_SAIF_CTRL_RUN,
+                               saif->base + SAIF_CTRL + MXS_CLR_ADDR);
+                       udelay(delay);
+                       clk_disable(saif->clk);
+               }
+
+               master_saif->ongoing = 0;
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+#define MXS_SAIF_RATES         SNDRV_PCM_RATE_8000_192000
+#define MXS_SAIF_FORMATS \
+       (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+       SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops mxs_saif_dai_ops = {
+       .startup = mxs_saif_startup,
+       .trigger = mxs_saif_trigger,
+       .prepare = mxs_saif_prepare,
+       .hw_params = mxs_saif_hw_params,
+       .set_sysclk = mxs_saif_set_dai_sysclk,
+       .set_fmt = mxs_saif_set_dai_fmt,
+};
+
+static int mxs_saif_dai_probe(struct snd_soc_dai *dai)
+{
+       struct mxs_saif *saif = dev_get_drvdata(dai->dev);
+
+       snd_soc_dai_set_drvdata(dai, saif);
+
+       return 0;
+}
+
+static struct snd_soc_dai_driver mxs_saif_dai = {
+       .name = "mxs-saif",
+       .probe = mxs_saif_dai_probe,
+       .playback = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = MXS_SAIF_RATES,
+               .formats = MXS_SAIF_FORMATS,
+       },
+       .capture = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = MXS_SAIF_RATES,
+               .formats = MXS_SAIF_FORMATS,
+       },
+       .ops = &mxs_saif_dai_ops,
+};
+
+static irqreturn_t mxs_saif_irq(int irq, void *dev_id)
+{
+       struct mxs_saif *saif = dev_id;
+       unsigned int stat;
+
+       stat = __raw_readl(saif->base + SAIF_STAT);
+       if (!(stat & (BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ |
+                       BM_SAIF_STAT_FIFO_OVERFLOW_IRQ)))
+               return IRQ_NONE;
+
+       if (stat & BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ) {
+               dev_dbg(saif->dev, "underrun!!! %d\n", ++saif->fifo_underrun);
+               __raw_writel(BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ,
+                               saif->base + SAIF_STAT + MXS_CLR_ADDR);
+       }
+
+       if (stat & BM_SAIF_STAT_FIFO_OVERFLOW_IRQ) {
+               dev_dbg(saif->dev, "overrun!!! %d\n", ++saif->fifo_overrun);
+               __raw_writel(BM_SAIF_STAT_FIFO_OVERFLOW_IRQ,
+                               saif->base + SAIF_STAT + MXS_CLR_ADDR);
+       }
+
+       dev_dbg(saif->dev, "SAIF_CTRL %x SAIF_STAT %x\n",
+              __raw_readl(saif->base + SAIF_CTRL),
+              __raw_readl(saif->base + SAIF_STAT));
+
+       return IRQ_HANDLED;
+}
+
+static int mxs_saif_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       struct mxs_saif *saif;
+       struct mxs_saif_platform_data *pdata;
+       int ret = 0;
+
+       if (pdev->id >= ARRAY_SIZE(mxs_saif))
+               return -EINVAL;
+
+       pdata = pdev->dev.platform_data;
+       if (pdata && pdata->init) {
+               ret = pdata->init();
+               if (ret)
+                       return ret;
+       }
+
+       saif = kzalloc(sizeof(*saif), GFP_KERNEL);
+       if (!saif)
+               return -ENOMEM;
+
+       mxs_saif[pdev->id] = saif;
+       saif->id = pdev->id;
+
+       saif->master_id = saif->id;
+       if (pdata && pdata->get_master_id) {
+               saif->master_id = pdata->get_master_id(saif->id);
+               if (saif->master_id < 0 ||
+                       saif->master_id >= ARRAY_SIZE(mxs_saif))
+                       return -EINVAL;
+       }
+
+       saif->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(saif->clk)) {
+               ret = PTR_ERR(saif->clk);
+               dev_err(&pdev->dev, "Cannot get the clock: %d\n",
+                       ret);
+               goto failed_clk;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "failed to get io resource: %d\n",
+                       ret);
+               goto failed_get_resource;
+       }
+
+       if (!request_mem_region(res->start, resource_size(res), "mxs-saif")) {
+               dev_err(&pdev->dev, "request_mem_region failed\n");
+               ret = -EBUSY;
+               goto failed_get_resource;
+       }
+
+       saif->base = ioremap(res->start, resource_size(res));
+       if (!saif->base) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               ret = -ENODEV;
+               goto failed_ioremap;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+       if (!res) {
+               ret = -ENODEV;
+               dev_err(&pdev->dev, "failed to get dma resource: %d\n",
+                       ret);
+               goto failed_ioremap;
+       }
+       saif->dma_param.chan_num = res->start;
+
+       saif->irq = platform_get_irq(pdev, 0);
+       if (saif->irq < 0) {
+               ret = saif->irq;
+               dev_err(&pdev->dev, "failed to get irq resource: %d\n",
+                       ret);
+               goto failed_get_irq1;
+       }
+
+       saif->dev = &pdev->dev;
+       ret = request_irq(saif->irq, mxs_saif_irq, 0, "mxs-saif", saif);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to request irq\n");
+               goto failed_get_irq1;
+       }
+
+       saif->dma_param.chan_irq = platform_get_irq(pdev, 1);
+       if (saif->dma_param.chan_irq < 0) {
+               ret = saif->dma_param.chan_irq;
+               dev_err(&pdev->dev, "failed to get dma irq resource: %d\n",
+                       ret);
+               goto failed_get_irq2;
+       }
+
+       platform_set_drvdata(pdev, saif);
+
+       ret = snd_soc_register_dai(&pdev->dev, &mxs_saif_dai);
+       if (ret) {
+               dev_err(&pdev->dev, "register DAI failed\n");
+               goto failed_register;
+       }
+
+       saif->soc_platform_pdev = platform_device_alloc(
+                                       "mxs-pcm-audio", pdev->id);
+       if (!saif->soc_platform_pdev) {
+               ret = -ENOMEM;
+               goto failed_pdev_alloc;
+       }
+
+       platform_set_drvdata(saif->soc_platform_pdev, saif);
+       ret = platform_device_add(saif->soc_platform_pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to add soc platform device\n");
+               goto failed_pdev_add;
+       }
+
+       return 0;
+
+failed_pdev_add:
+       platform_device_put(saif->soc_platform_pdev);
+failed_pdev_alloc:
+       snd_soc_unregister_dai(&pdev->dev);
+failed_register:
+failed_get_irq2:
+       free_irq(saif->irq, saif);
+failed_get_irq1:
+       iounmap(saif->base);
+failed_ioremap:
+       release_mem_region(res->start, resource_size(res));
+failed_get_resource:
+       clk_put(saif->clk);
+failed_clk:
+       kfree(saif);
+
+       return ret;
+}
+
+static int __devexit mxs_saif_remove(struct platform_device *pdev)
+{
+       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       struct mxs_saif *saif = platform_get_drvdata(pdev);
+
+       platform_device_unregister(saif->soc_platform_pdev);
+
+       snd_soc_unregister_dai(&pdev->dev);
+
+       iounmap(saif->base);
+       release_mem_region(res->start, resource_size(res));
+       free_irq(saif->irq, saif);
+
+       clk_put(saif->clk);
+       kfree(saif);
+
+       return 0;
+}
+
+static struct platform_driver mxs_saif_driver = {
+       .probe = mxs_saif_probe,
+       .remove = __devexit_p(mxs_saif_remove),
+
+       .driver = {
+               .name = "mxs-saif",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init mxs_saif_init(void)
+{
+       return platform_driver_register(&mxs_saif_driver);
+}
+
+static void __exit mxs_saif_exit(void)
+{
+       platform_driver_unregister(&mxs_saif_driver);
+}
+
+module_init(mxs_saif_init);
+module_exit(mxs_saif_exit);
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXS ASoC SAIF driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h
new file mode 100644 (file)
index 0000000..12c91e4
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+#ifndef _MXS_SAIF_H
+#define _MXS_SAIF_H
+
+#define SAIF_CTRL      0x0
+#define SAIF_STAT      0x10
+#define SAIF_DATA      0x20
+#define SAIF_VERSION   0X30
+
+/* SAIF_CTRL */
+#define BM_SAIF_CTRL_SFTRST            0x80000000
+#define BM_SAIF_CTRL_CLKGATE           0x40000000
+#define BP_SAIF_CTRL_BITCLK_MULT_RATE  27
+#define BM_SAIF_CTRL_BITCLK_MULT_RATE  0x38000000
+#define BF_SAIF_CTRL_BITCLK_MULT_RATE(v) \
+               (((v) << 27) & BM_SAIF_CTRL_BITCLK_MULT_RATE)
+#define BM_SAIF_CTRL_BITCLK_BASE_RATE  0x04000000
+#define BM_SAIF_CTRL_FIFO_ERROR_IRQ_EN 0x02000000
+#define BM_SAIF_CTRL_FIFO_SERVICE_IRQ_EN       0x01000000
+#define BP_SAIF_CTRL_RSRVD2            21
+#define BM_SAIF_CTRL_RSRVD2            0x00E00000
+
+#define BP_SAIF_CTRL_DMAWAIT_COUNT     16
+#define BM_SAIF_CTRL_DMAWAIT_COUNT     0x001F0000
+#define BF_SAIF_CTRL_DMAWAIT_COUNT(v) \
+               (((v) << 16) & BM_SAIF_CTRL_DMAWAIT_COUNT)
+#define BP_SAIF_CTRL_CHANNEL_NUM_SELECT 14
+#define BM_SAIF_CTRL_CHANNEL_NUM_SELECT 0x0000C000
+#define BF_SAIF_CTRL_CHANNEL_NUM_SELECT(v) \
+               (((v) << 14) & BM_SAIF_CTRL_CHANNEL_NUM_SELECT)
+#define BM_SAIF_CTRL_LRCLK_PULSE       0x00002000
+#define BM_SAIF_CTRL_BIT_ORDER         0x00001000
+#define BM_SAIF_CTRL_DELAY             0x00000800
+#define BM_SAIF_CTRL_JUSTIFY           0x00000400
+#define BM_SAIF_CTRL_LRCLK_POLARITY    0x00000200
+#define BM_SAIF_CTRL_BITCLK_EDGE       0x00000100
+#define BP_SAIF_CTRL_WORD_LENGTH       4
+#define BM_SAIF_CTRL_WORD_LENGTH       0x000000F0
+#define BF_SAIF_CTRL_WORD_LENGTH(v) \
+               (((v) << 4) & BM_SAIF_CTRL_WORD_LENGTH)
+#define BM_SAIF_CTRL_BITCLK_48XFS_ENABLE       0x00000008
+#define BM_SAIF_CTRL_SLAVE_MODE                0x00000004
+#define BM_SAIF_CTRL_READ_MODE         0x00000002
+#define BM_SAIF_CTRL_RUN               0x00000001
+
+/* SAIF_STAT */
+#define BM_SAIF_STAT_PRESENT           0x80000000
+#define BP_SAIF_STAT_RSRVD2            17
+#define BM_SAIF_STAT_RSRVD2            0x7FFE0000
+#define BF_SAIF_STAT_RSRVD2(v) \
+               (((v) << 17) & BM_SAIF_STAT_RSRVD2)
+#define BM_SAIF_STAT_DMA_PREQ          0x00010000
+#define BP_SAIF_STAT_RSRVD1            7
+#define BM_SAIF_STAT_RSRVD1            0x0000FF80
+#define BF_SAIF_STAT_RSRVD1(v) \
+               (((v) << 7) & BM_SAIF_STAT_RSRVD1)
+
+#define BM_SAIF_STAT_FIFO_UNDERFLOW_IRQ 0x00000040
+#define BM_SAIF_STAT_FIFO_OVERFLOW_IRQ 0x00000020
+#define BM_SAIF_STAT_FIFO_SERVICE_IRQ  0x00000010
+#define BP_SAIF_STAT_RSRVD0            1
+#define BM_SAIF_STAT_RSRVD0            0x0000000E
+#define BF_SAIF_STAT_RSRVD0(v) \
+               (((v) << 1) & BM_SAIF_STAT_RSRVD0)
+#define BM_SAIF_STAT_BUSY              0x00000001
+
+/* SAFI_DATA */
+#define BP_SAIF_DATA_PCM_RIGHT         16
+#define BM_SAIF_DATA_PCM_RIGHT         0xFFFF0000
+#define BF_SAIF_DATA_PCM_RIGHT(v) \
+               (((v) << 16) & BM_SAIF_DATA_PCM_RIGHT)
+#define BP_SAIF_DATA_PCM_LEFT          0
+#define BM_SAIF_DATA_PCM_LEFT          0x0000FFFF
+#define BF_SAIF_DATA_PCM_LEFT(v)       \
+               (((v) << 0) & BM_SAIF_DATA_PCM_LEFT)
+
+/* SAIF_VERSION */
+#define BP_SAIF_VERSION_MAJOR          24
+#define BM_SAIF_VERSION_MAJOR          0xFF000000
+#define BF_SAIF_VERSION_MAJOR(v) \
+               (((v) << 24) & BM_SAIF_VERSION_MAJOR)
+#define BP_SAIF_VERSION_MINOR          16
+#define BM_SAIF_VERSION_MINOR          0x00FF0000
+#define BF_SAIF_VERSION_MINOR(v) \
+               (((v) << 16) & BM_SAIF_VERSION_MINOR)
+#define BP_SAIF_VERSION_STEP           0
+#define BM_SAIF_VERSION_STEP           0x0000FFFF
+#define BF_SAIF_VERSION_STEP(v) \
+               (((v) << 0) & BM_SAIF_VERSION_STEP)
+
+#define MXS_SAIF_MCLK          0
+
+#include "mxs-pcm.h"
+
+struct mxs_saif {
+       struct device *dev;
+       struct clk *clk;
+       unsigned int mclk;
+       unsigned int mclk_in_use;
+       void __iomem *base;
+       int irq;
+       struct mxs_pcm_dma_params dma_param;
+       unsigned int id;
+       unsigned int master_id;
+       unsigned int cur_rate;
+       unsigned int ongoing;
+
+       struct platform_device *soc_platform_pdev;
+       u32 fifo_underrun;
+       u32 fifo_overrun;
+};
+
+extern int mxs_saif_put_mclk(unsigned int saif_id);
+extern int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk,
+                                       unsigned int rate);
+#endif
diff --git a/sound/soc/mxs/mxs-sgtl5000.c b/sound/soc/mxs/mxs-sgtl5000.c
new file mode 100644 (file)
index 0000000..7fbeaec
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-types.h>
+
+#include "../codecs/sgtl5000.h"
+#include "mxs-saif.h"
+
+static int mxs_sgtl5000_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;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       unsigned int rate = params_rate(params);
+       u32 dai_format, mclk;
+       int ret;
+
+       /* sgtl5000 does not support 512*rate when in 96000 fs */
+       switch (rate) {
+       case 96000:
+               mclk = 256 * rate;
+               break;
+       default:
+               mclk = 512 * rate;
+               break;
+       }
+
+       /* Sgtl5000 sysclk should be >= 8MHz and <= 27M */
+       if (mclk < 8000000 || mclk > 27000000)
+               return -EINVAL;
+
+       /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */
+       ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0);
+       if (ret)
+               return ret;
+
+       /* The SAIF MCLK should be the same as SGTL5000_SYSCLK */
+       ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0);
+       if (ret)
+               return ret;
+
+       /* set codec to slave mode */
+       dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+                       SND_SOC_DAIFMT_CBS_CFS;
+
+       /* set codec DAI configuration */
+       ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
+       if (ret)
+               return ret;
+
+       /* set cpu DAI configuration */
+       ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_ops mxs_sgtl5000_hifi_ops = {
+       .hw_params = mxs_sgtl5000_hw_params,
+};
+
+static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
+       {
+               .name           = "HiFi Tx",
+               .stream_name    = "HiFi Playback",
+               .codec_dai_name = "sgtl5000",
+               .codec_name     = "sgtl5000.0-000a",
+               .cpu_dai_name   = "mxs-saif.0",
+               .platform_name  = "mxs-pcm-audio.0",
+               .ops            = &mxs_sgtl5000_hifi_ops,
+       }, {
+               .name           = "HiFi Rx",
+               .stream_name    = "HiFi Capture",
+               .codec_dai_name = "sgtl5000",
+               .codec_name     = "sgtl5000.0-000a",
+               .cpu_dai_name   = "mxs-saif.1",
+               .platform_name  = "mxs-pcm-audio.1",
+               .ops            = &mxs_sgtl5000_hifi_ops,
+       },
+};
+
+static struct snd_soc_card mxs_sgtl5000 = {
+       .name           = "mxs_sgtl5000",
+       .dai_link       = mxs_sgtl5000_dai,
+       .num_links      = ARRAY_SIZE(mxs_sgtl5000_dai),
+};
+
+static int __devinit mxs_sgtl5000_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &mxs_sgtl5000;
+       int ret;
+
+       /*
+        * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w).
+        * The Sgtl5000 sysclk is derived from saif0 mclk and it's range
+        * should be >= 8MHz and <= 27M.
+        */
+       ret = mxs_saif_get_mclk(0, 44100 * 256, 44100);
+       if (ret)
+               return ret;
+
+       card->dev = &pdev->dev;
+       platform_set_drvdata(pdev, card);
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int __devexit mxs_sgtl5000_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       mxs_saif_put_mclk(0);
+
+       snd_soc_unregister_card(card);
+
+       return 0;
+}
+
+static struct platform_driver mxs_sgtl5000_audio_driver = {
+       .driver = {
+               .name = "mxs-sgtl5000",
+               .owner = THIS_MODULE,
+       },
+       .probe = mxs_sgtl5000_probe,
+       .remove = __devexit_p(mxs_sgtl5000_remove),
+};
+
+static int __init mxs_sgtl5000_init(void)
+{
+       return platform_driver_register(&mxs_sgtl5000_audio_driver);
+}
+module_init(mxs_sgtl5000_init);
+
+static void __exit mxs_sgtl5000_exit(void)
+{
+       platform_driver_unregister(&mxs_sgtl5000_audio_driver);
+}
+module_exit(mxs_sgtl5000_exit);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXS ALSA SoC Machine driver");
+MODULE_LICENSE("GPL");
index d589ef1..865b288 100644 (file)
@@ -268,7 +268,7 @@ static int nuc900_dma_open(struct snd_pcm_substream *substream)
        nuc900_audio = nuc900_ac97_data;
 
        if (request_irq(nuc900_audio->irq_num, nuc900_dma_interrupt,
-                       IRQF_DISABLED, "nuc900-dma", substream))
+                       0, "nuc900-dma", substream))
                return -EBUSY;
 
        runtime->private_data = nuc900_audio;
@@ -318,7 +318,6 @@ static u64 nuc900_pcm_dmamask = DMA_BIT_MASK(32);
 static int nuc900_dma_new(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_card *card = rtd->card->snd_card;
-       struct snd_soc_dai *dai = rtd->cpu_dai;
        struct snd_pcm *pcm = rtd->pcm;
 
        if (!card->dev->dma_mask)
index 1a591f1..b899a3b 100644 (file)
@@ -306,8 +306,10 @@ static int __init raumfeld_audio_init(void)
                                     &snd_soc_raumfeld_connector);
 
        ret = platform_device_add(raumfeld_audio_device);
-       if (ret < 0)
+       if (ret < 0) {
+               platform_device_put(raumfeld_audio_device);
                return ret;
+       }
 
        raumfeld_enable_audio(true);
        return 0;
index b253d86..ce920e3 100644 (file)
@@ -312,7 +312,7 @@ static struct snd_soc_dai_link spitz_dai = {
        .cpu_dai_name = "pxa2xx-i2s",
        .codec_dai_name = "wm8750-hifi",
        .platform_name = "pxa-pcm-audio",
-       .codec_name = "wm8750-codec.0-001b",
+       .codec_name = "wm8750.0-001b",
        .init = spitz_wm8750_init,
        .ops = &spitz_ops,
 };
index d69d9fc..4b81ffd 100644 (file)
@@ -198,7 +198,7 @@ static struct snd_soc_dai_link z2_dai = {
        .cpu_dai_name   = "pxa2xx-i2s",
        .codec_dai_name = "wm8750-hifi",
        .platform_name = "pxa-pcm-audio",
-       .codec_name     = "wm8750-codec.0-001b",
+       .codec_name     = "wm8750.0-001b",
        .init           = z2_wm8750_init,
        .ops            = &z2_ops,
 };
index 80c85fd..55efc2b 100644 (file)
@@ -446,7 +446,6 @@ static u64 s6000_pcm_dmamask = DMA_BIT_MASK(32);
 static int s6000_pcm_new(struct snd_soc_pcm_runtime *runtime)
 {
        struct snd_card *card = runtime->card->snd_card;
-       struct snd_soc_dai *dai = runtime->cpu_dai;
        struct snd_pcm *pcm = runtime->pcm;
        struct s6000_pcm_dma_params *params;
        int res;
index 65f980e..dd3b3ea 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)
+       depends on SND_SOC_SAMSUNG && (MACH_SMDKV310 || MACH_SMDKC210 || MACH_SMDK4212)
        select SND_SOC_WM8994
        select SND_SAMSUNG_I2S
        help
@@ -158,7 +158,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)
+       depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210 || MACH_SMDKV310 || MACH_SMDK4212)
        select SND_SAMSUNG_SPDIF
        help
          Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
@@ -173,7 +173,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)
+       depends on SND_SOC_SAMSUNG && (MACH_SMDKC210 || MACH_SMDKV310 || MACH_SMDK4212)
        select SND_SOC_WM8994
        select SND_SAMSUNG_PCM
        help
index f97110e..884c8a1 100644 (file)
@@ -444,7 +444,7 @@ static __devinit int s3c_ac97_probe(struct platform_device *pdev)
        }
 
        ret = request_irq(irq_res->start, s3c_ac97_irq,
-                                       IRQF_DISABLED, "AC97", NULL);
+                                       0, "AC97", NULL);
        if (ret < 0) {
                dev_err(&pdev->dev, "ac97: interrupt request failed.\n");
                goto err4;
index 14eb6ea..ed8f13a 100644 (file)
@@ -131,7 +131,7 @@ static struct snd_soc_dai_link jive_dai = {
        .cpu_dai_name   = "s3c2412-i2s",
        .codec_dai_name = "wm8750-hifi",
        .platform_name  = "samsung-audio",
-       .codec_name     = "wm8750-codec.0-001a",
+       .codec_name     = "wm8750.0-001a",
        .init           = jive_wm8750_init,
        .ops            = &jive_ops,
 };
index 841ab14..7ab8e2c 100644 (file)
@@ -69,10 +69,10 @@ static int s3c2412_i2s_probe(struct snd_soc_dai *dai)
        s3c2412_i2s.dma_playback = &s3c2412_i2s_pcm_stereo_out;
 
        s3c2412_i2s.iis_cclk = clk_get(dai->dev, "i2sclk");
-       if (s3c2412_i2s.iis_cclk == NULL) {
+       if (IS_ERR(s3c2412_i2s.iis_cclk)) {
                pr_err("failed to get i2sclk clock\n");
                iounmap(s3c2412_i2s.regs);
-               return -ENODEV;
+               return PTR_ERR(s3c2412_i2s.iis_cclk);
        }
 
        /* Set MPLL as the source for IIS CLK */
index 63d8849..21c92e2 100644 (file)
@@ -383,10 +383,10 @@ static int s3c24xx_i2s_probe(struct snd_soc_dai *dai)
                return -ENXIO;
 
        s3c24xx_i2s.iis_clk = clk_get(dai->dev, "iis");
-       if (s3c24xx_i2s.iis_clk == NULL) {
+       if (IS_ERR(s3c24xx_i2s.iis_clk)) {
                pr_err("failed to get iis_clock\n");
                iounmap(s3c24xx_i2s.regs);
-               return -ENODEV;
+               return PTR_ERR(s3c24xx_i2s.iis_clk);
        }
        clk_enable(s3c24xx_i2s.iis_clk);
 
index dc9d551..65c1cfd 100644 (file)
@@ -66,17 +66,17 @@ static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
        pr_debug("%s %d\n", __func__, clk_users);
        if (clk_users == 0) {
                xtal = clk_get(&s3c24xx_uda134x_snd_device->dev, "xtal");
-               if (!xtal) {
+               if (IS_ERR(xtal)) {
                        printk(KERN_ERR "%s cannot get xtal\n", __func__);
-                       ret = -EBUSY;
+                       ret = PTR_ERR(xtal);
                } else {
                        pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
                                       "pclk");
-                       if (!pclk) {
+                       if (IS_ERR(pclk)) {
                                printk(KERN_ERR "%s cannot get pclk\n",
                                       __func__);
                                clk_put(xtal);
-                               ret = -EBUSY;
+                               ret = PTR_ERR(pclk);
                        }
                }
                if (!ret) {
index 0a2c4f2..bbd1476 100644 (file)
@@ -207,7 +207,7 @@ static struct snd_soc_dai_link smartq_dai[] = {
                .cpu_dai_name   = "samsung-i2s.0",
                .codec_dai_name = "wm8750-hifi",
                .platform_name  = "samsung-audio",
-               .codec_name     = "wm8750-codec.0-0x1a",
+               .codec_name     = "wm8750.0-0x1a",
                .init           = smartq_wm8987_init,
                .ops            = &smartq_hifi_ops,
        },
index 3d26f66..20deecf 100644 (file)
@@ -210,7 +210,7 @@ static struct snd_soc_dai_link smdk_dai[] = {
                .cpu_dai_name = "samsung-i2s.0",
                .codec_dai_name = "wm8580-hifi-playback",
                .platform_name = "samsung-audio",
-               .codec_name = "wm8580-codec.0-001b",
+               .codec_name = "wm8580.0-001b",
                .init = smdk_wm8580_init_paifrx,
                .ops = &smdk_ops,
        },
@@ -220,7 +220,7 @@ static struct snd_soc_dai_link smdk_dai[] = {
                .cpu_dai_name = "samsung-i2s.0",
                .codec_dai_name = "wm8580-hifi-capture",
                .platform_name = "samsung-audio",
-               .codec_name = "wm8580-codec.0-001b",
+               .codec_name = "wm8580.0-001b",
                .init = smdk_wm8580_init_paiftx,
                .ops = &smdk_ops,
        },
@@ -230,7 +230,7 @@ static struct snd_soc_dai_link smdk_dai[] = {
                .cpu_dai_name = "samsung-i2s.x",
                .codec_dai_name = "wm8580-hifi-playback",
                .platform_name = "samsung-audio",
-               .codec_name = "wm8580-codec.0-001b",
+               .codec_name = "wm8580.0-001b",
                .init = smdk_wm8580_init_paifrx,
                .ops = &smdk_ops,
        },
index 0d12092..4b9c734 100644 (file)
@@ -127,7 +127,7 @@ static struct snd_soc_dai_link smdk_dai[] = {
                .cpu_dai_name = "samsung-pcm.0",
                .codec_dai_name = "wm8580-hifi-playback",
                .platform_name = "samsung-audio",
-               .codec_name = "wm8580-codec.0-001b",
+               .codec_name = "wm8580.0-001b",
                .ops = &smdk_wm8580_pcm_ops,
        }, {
                .name = "WM8580 PAIF PCM TX",
@@ -135,7 +135,7 @@ static struct snd_soc_dai_link smdk_dai[] = {
                .cpu_dai_name = "samsung-pcm.0",
                .codec_dai_name = "wm8580-hifi-capture",
                .platform_name = "samsung-audio",
-               .codec_name = "wm8580-codec.0-001b",
+               .codec_name = "wm8580.0-001b",
                .ops = &smdk_wm8580_pcm_ops,
        },
 };
index 590e927..b9e213f 100644 (file)
@@ -125,10 +125,6 @@ static struct snd_soc_jack_pin speyside_headset_pins[] = {
                .pin = "Headset Mic",
                .mask = SND_JACK_MICROPHONE,
        },
-       {
-               .pin = "Headphone",
-               .mask = SND_JACK_HEADPHONE,
-       },
 };
 
 /* Default the headphone selection to active high */
@@ -171,7 +167,8 @@ static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd)
        gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity);
 
        ret = snd_soc_jack_new(codec, "Headset",
-                              SND_JACK_HEADSET | SND_JACK_BTN_0,
+                              SND_JACK_LINEOUT | SND_JACK_HEADSET |
+                              SND_JACK_BTN_0,
                               &speyside_headset);
        if (ret)
                return ret;
@@ -227,7 +224,7 @@ static int speyside_wm9081_init(struct snd_soc_dapm_context *dapm)
        snd_soc_dapm_nc_pin(dapm, "LINEOUT");
 
        /* At any time the WM9081 is active it will have this clock */
-       return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK,
+       return snd_soc_codec_set_sysclk(dapm->codec, WM9081_SYSCLK_MCLK, 0,
                                        48000 * 256, 0);
 }
 
@@ -252,6 +249,7 @@ static const struct snd_kcontrol_new controls[] = {
        SOC_DAPM_PIN_SWITCH("Main AMIC"),
        SOC_DAPM_PIN_SWITCH("WM1250 Input"),
        SOC_DAPM_PIN_SWITCH("WM1250 Output"),
+       SOC_DAPM_PIN_SWITCH("Headphone"),
 };
 
 static struct snd_soc_dapm_widget widgets[] = {
index 72535f2..3820a6b 100644 (file)
@@ -31,13 +31,13 @@ static int speyside_wm8962_set_bias_level(struct snd_soc_card *card,
                if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
                        ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
                                                  WM8962_FLL_MCLK, 32768,
-                                                 44100 * 256);
+                                                 44100 * 512);
                        if (ret < 0)
                                pr_err("Failed to start FLL: %d\n", ret);
 
                        ret = snd_soc_dai_set_sysclk(codec_dai,
                                                     WM8962_SYSCLK_FLL,
-                                                    44100 * 256,
+                                                    44100 * 512,
                                                     SND_SOC_CLOCK_IN);
                        if (ret < 0) {
                                pr_err("Failed to set SYSCLK: %d\n", ret);
index 8e112cc..1493ebf 100644 (file)
@@ -1285,7 +1285,7 @@ static int fsi_probe(struct platform_device *pdev)
        pm_runtime_enable(&pdev->dev);
        dev_set_drvdata(&pdev->dev, master);
 
-       ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED,
+       ret = request_irq(irq, &fsi_interrupt, 0,
                          id_entry->name, master);
        if (ret) {
                dev_err(&pdev->dev, "irq request err\n");
index 20b7f3b..143c705 100644 (file)
@@ -548,9 +548,6 @@ static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec,
 
 static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec)
 {
-       const struct snd_soc_codec_driver *codec_drv;
-
-       codec_drv = codec->driver;
        return DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count());
 }
 
@@ -868,10 +865,6 @@ static int snd_soc_flat_cache_exit(struct snd_soc_codec *codec)
 
 static int snd_soc_flat_cache_init(struct snd_soc_codec *codec)
 {
-       const struct snd_soc_codec_driver *codec_drv;
-
-       codec_drv = codec->driver;
-
        if (codec->reg_def_copy)
                codec->reg_cache = kmemdup(codec->reg_def_copy,
                                           codec->reg_size, GFP_KERNEL);
index ef69f5a..bd20154 100644 (file)
@@ -106,7 +106,7 @@ static int format_register_str(struct snd_soc_codec *codec,
        if (wordsize + regsize + 2 + 1 != len)
                return -EINVAL;
 
-       ret = snd_soc_read(codec , reg);
+       ret = snd_soc_read(codec, reg);
        if (ret < 0) {
                memset(regbuf, 'X', regsize);
                regbuf[regsize] = '\0';
@@ -144,7 +144,7 @@ static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf,
                step = codec->driver->reg_cache_step;
 
        for (i = 0; i < codec->driver->reg_cache_size; i += step) {
-               if (codec->readable_register && !codec->readable_register(codec, i))
+               if (!snd_soc_codec_readable_register(codec, i))
                        continue;
                if (codec->driver->display_register) {
                        count += codec->driver->display_register(codec, buf + count,
@@ -245,7 +245,6 @@ static ssize_t codec_reg_write_file(struct file *file,
        size_t buf_size;
        char *start = buf;
        unsigned long reg, value;
-       int step = 1;
        struct snd_soc_codec *codec = file->private_data;
 
        buf_size = min(count, (sizeof(buf)-1));
@@ -253,9 +252,6 @@ static ssize_t codec_reg_write_file(struct file *file,
                return -EFAULT;
        buf[buf_size] = 0;
 
-       if (codec->driver->reg_cache_step)
-               step = codec->driver->reg_cache_step;
-
        while (*start == ' ')
                start++;
        reg = simple_strtoul(start, &start, 16);
@@ -957,6 +953,8 @@ static int soc_probe_codec(struct snd_soc_card *card,
                snd_soc_dapm_new_controls(&codec->dapm, driver->dapm_widgets,
                                          driver->num_dapm_widgets);
 
+       codec->dapm.idle_bias_off = driver->idle_bias_off;
+
        if (driver->probe) {
                ret = driver->probe(codec);
                if (ret < 0) {
@@ -2680,7 +2678,7 @@ int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
        if (dai->driver && dai->driver->ops->set_sysclk)
                return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir);
        else if (dai->codec && dai->codec->driver->set_sysclk)
-               return dai->codec->driver->set_sysclk(dai->codec, clk_id,
+               return dai->codec->driver->set_sysclk(dai->codec, clk_id, 0,
                                                      freq, dir);
        else
                return -EINVAL;
@@ -2691,16 +2689,18 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
  * snd_soc_codec_set_sysclk - configure CODEC system or master clock.
  * @codec: CODEC
  * @clk_id: DAI specific clock ID
+ * @source: Source for the clock
  * @freq: new clock frequency in Hz
  * @dir: new clock direction - input/output.
  *
  * Configures the CODEC master (MCLK) or system (SYSCLK) clocking.
  */
 int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
-       unsigned int freq, int dir)
+                            int source, unsigned int freq, int dir)
 {
        if (codec->driver->set_sysclk)
-               return codec->driver->set_sysclk(codec, clk_id, freq, dir);
+               return codec->driver->set_sysclk(codec, clk_id, source,
+                                                freq, dir);
        else
                return -EINVAL;
 }
@@ -3153,6 +3153,7 @@ int snd_soc_register_platform(struct device *dev,
        platform->driver = platform_drv;
        platform->dapm.dev = dev;
        platform->dapm.platform = platform;
+       platform->dapm.stream_event = platform_drv->stream_event;
 
        mutex_lock(&client_mutex);
        list_add(&platform->list, &platform_list);
@@ -3265,6 +3266,7 @@ int snd_soc_register_codec(struct device *dev,
        codec->dapm.dev = dev;
        codec->dapm.codec = codec;
        codec->dapm.seq_notifier = codec_drv->seq_notifier;
+       codec->dapm.stream_event = codec_drv->stream_event;
        codec->dev = dev;
        codec->driver = codec_drv;
        codec->num_dai = num_dai;
index d67c637..4a440b5 100644 (file)
@@ -443,6 +443,11 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
                        if (path->name != (char *)w->kcontrol_news[i].name)
                                continue;
 
+                       if (w->kcontrols[i]) {
+                               path->kcontrol = w->kcontrols[i];
+                               continue;
+                       }
+
                        wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
                                    sizeof(struct snd_soc_dapm_widget *),
                        wlist = kzalloc(wlistsize, GFP_KERNEL);
@@ -579,8 +584,8 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
                                        name + prefix_len, prefix);
                ret = snd_ctl_add(card, kcontrol);
                if (ret < 0) {
-                       dev_err(dapm->dev,
-                               "asoc: failed to add kcontrol %s\n", w->name);
+                       dev_err(dapm->dev, "failed to add kcontrol %s: %d\n",
+                               w->name, ret);
                        kfree(wlist);
                        return ret;
                }
@@ -1556,7 +1561,6 @@ static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
                /* found, now check type */
                found = 1;
                path->connect = connect;
-               break;
        }
 
        if (found)
@@ -2584,7 +2588,7 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
        {
                if (!w->sname || w->dapm != dapm)
                        continue;
-               dev_dbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n",
+               dev_vdbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n",
                        w->name, w->sname, stream, event);
                if (strstr(w->sname, stream)) {
                        switch(event) {
@@ -2604,6 +2608,10 @@ static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm,
        }
 
        dapm_power_widgets(dapm, event);
+
+       /* do we need to notify any clients that DAPM stream is complete */
+       if (dapm->stream_event)
+               dapm->stream_event(dapm, event);
 }
 
 /**
index a62f7dd..66fcccd 100644 (file)
 
 #include <linux/i2c.h>
 #include <linux/spi/spi.h>
+#include <linux/regmap.h>
 #include <sound/soc.h>
 
 #include <trace/events/asoc.h>
 
-#ifdef CONFIG_SPI_MASTER
-static int do_spi_write(void *control, const char *data, int len)
-{
-       struct spi_device *spi = control;
-       int ret;
-
-       ret = spi_write(spi, data, len);
-       if (ret < 0)
-               return ret;
-
-       return len;
-}
-#endif
-
-static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
-                      unsigned int value, const void *data, int len)
+#ifdef CONFIG_REGMAP
+static int hw_write(struct snd_soc_codec *codec, unsigned int reg,
+                   unsigned int value)
 {
        int ret;
 
@@ -49,13 +37,7 @@ static int do_hw_write(struct snd_soc_codec *codec, unsigned int reg,
                return 0;
        }
 
-       ret = codec->hw_write(codec->control_data, data, len);
-       if (ret == len)
-               return 0;
-       if (ret < 0)
-               return ret;
-       else
-               return -EIO;
+       return regmap_write(codec->control_data, reg, value);
 }
 
 static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg)
@@ -69,8 +51,11 @@ static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg)
                if (codec->cache_only)
                        return -1;
 
-               BUG_ON(!codec->hw_read);
-               return codec->hw_read(codec, reg);
+               ret = regmap_read(codec->control_data, reg, &val);
+               if (ret == 0)
+                       return val;
+               else
+                       return ret;
        }
 
        ret = snd_soc_cache_read(codec, reg, &val);
@@ -79,202 +64,18 @@ static unsigned int hw_read(struct snd_soc_codec *codec, unsigned int reg)
        return val;
 }
 
-static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
-                             unsigned int value)
-{
-       u16 data;
-
-       data = cpu_to_be16((reg << 12) | (value & 0xffffff));
-
-       return do_hw_write(codec, reg, value, &data, 2);
-}
-
-static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
-                            unsigned int value)
-{
-       u16 data;
-
-       data = cpu_to_be16((reg << 9) | (value & 0x1ff));
-
-       return do_hw_write(codec, reg, value, &data, 2);
-}
-
-static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
-                            unsigned int value)
-{
-       u8 data[2];
-
-       reg &= 0xff;
-       data[0] = reg;
-       data[1] = value & 0xff;
-
-       return do_hw_write(codec, reg, value, data, 2);
-}
-
-static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
-                             unsigned int value)
-{
-       u8 data[3];
-       u16 val = cpu_to_be16(value);
-
-       data[0] = reg;
-       memcpy(&data[1], &val, sizeof(val));
-
-       return do_hw_write(codec, reg, value, data, 3);
-}
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int do_i2c_read(struct snd_soc_codec *codec,
-                               void *reg, int reglen,
-                               void *data, int datalen)
-{
-       struct i2c_msg xfer[2];
-       int ret;
-       struct i2c_client *client = codec->control_data;
-
-       /* Write register */
-       xfer[0].addr = client->addr;
-       xfer[0].flags = 0;
-       xfer[0].len = reglen;
-       xfer[0].buf = reg;
-
-       /* Read data */
-       xfer[1].addr = client->addr;
-       xfer[1].flags = I2C_M_RD;
-       xfer[1].len = datalen;
-       xfer[1].buf = data;
-
-       ret = i2c_transfer(client->adapter, xfer, 2);
-       if (ret == 2)
-               return 0;
-       else if (ret < 0)
-               return ret;
-       else
-               return -EIO;
-}
-#endif
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_8_8_read_i2c(struct snd_soc_codec *codec,
-                                        unsigned int r)
-{
-       u8 reg = r;
-       u8 data;
-       int ret;
-
-       ret = do_i2c_read(codec, &reg, 1, &data, 1);
-       if (ret < 0)
-               return 0;
-       return data;
-}
-#else
-#define snd_soc_8_8_read_i2c NULL
-#endif
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
-                                         unsigned int r)
-{
-       u8 reg = r;
-       u16 data;
-       int ret;
-
-       ret = do_i2c_read(codec, &reg, 1, &data, 2);
-       if (ret < 0)
-               return 0;
-       return (data >> 8) | ((data & 0xff) << 8);
-}
-#else
-#define snd_soc_8_16_read_i2c NULL
-#endif
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_16_8_read_i2c(struct snd_soc_codec *codec,
-                                         unsigned int r)
-{
-       u16 reg = r;
-       u8 data;
-       int ret;
-
-       ret = do_i2c_read(codec, &reg, 2, &data, 1);
-       if (ret < 0)
-               return 0;
-       return data;
-}
-#else
-#define snd_soc_16_8_read_i2c NULL
-#endif
-
-#if defined(CONFIG_SPI_MASTER)
-static unsigned int snd_soc_16_8_read_spi(struct snd_soc_codec *codec,
-                                         unsigned int r)
-{
-       struct spi_device *spi = codec->control_data;
-
-       const u16 reg = cpu_to_be16(r | 0x100);
-       u8 data;
-       int ret;
-
-       ret = spi_write_then_read(spi, &reg, 2, &data, 1);
-       if (ret < 0)
-               return 0;
-       return data;
-}
-#else
-#define snd_soc_16_8_read_spi NULL
-#endif
-
-static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
-                             unsigned int value)
-{
-       u8 data[3];
-       u16 rval = cpu_to_be16(reg);
-
-       memcpy(data, &rval, sizeof(rval));
-       data[2] = value;
-
-       return do_hw_write(codec, reg, value, data, 3);
-}
-
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-static unsigned int snd_soc_16_16_read_i2c(struct snd_soc_codec *codec,
-                                          unsigned int r)
-{
-       u16 reg = cpu_to_be16(r);
-       u16 data;
-       int ret;
-
-       ret = do_i2c_read(codec, &reg, 2, &data, 2);
-       if (ret < 0)
-               return 0;
-       return be16_to_cpu(data);
-}
-#else
-#define snd_soc_16_16_read_i2c NULL
-#endif
-
-static int snd_soc_16_16_write(struct snd_soc_codec *codec, unsigned int reg,
-                              unsigned int value)
-{
-       u16 data[2];
-
-       data[0] = cpu_to_be16(reg);
-       data[1] = cpu_to_be16(value);
-
-       return do_hw_write(codec, reg, value, data, sizeof(data));
-}
-
 /* Primitive bulk write support for soc-cache.  The data pointed to by
- * `data' needs to already be in the form the hardware expects
- * including any leading register specific data.  Any data written
- * through this function will not go through the cache as it only
- * handles writing to volatile or out of bounds registers.
+ * `data' needs to already be in the form the hardware expects.  Any
+ * data written through this function will not go through the cache as
+ * it only handles writing to volatile or out of bounds registers.
+ *
+ * This is currently only supported for devices using the regmap API
+ * wrappers.
  */
-static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int reg,
+static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec,
+                                    unsigned int reg,
                                     const void *data, size_t len)
 {
-       int ret;
-
        /* To ensure that we don't get out of sync with the cache, check
         * whether the base register is volatile or if we've directly asked
         * to bypass the cache.  Out of bounds registers are considered
@@ -285,68 +86,9 @@ static int snd_soc_hw_bulk_write_raw(struct snd_soc_codec *codec, unsigned int r
            && reg < codec->driver->reg_cache_size)
                return -EINVAL;
 
-       switch (codec->control_type) {
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-       case SND_SOC_I2C:
-               ret = i2c_master_send(to_i2c_client(codec->dev), data, len);
-               break;
-#endif
-#if defined(CONFIG_SPI_MASTER)
-       case SND_SOC_SPI:
-               ret = spi_write(to_spi_device(codec->dev), data, len);
-               break;
-#endif
-       default:
-               BUG();
-       }
-
-       if (ret == len)
-               return 0;
-       if (ret < 0)
-               return ret;
-       else
-               return -EIO;
+       return regmap_raw_write(codec->control_data, reg, data, len);
 }
 
-static struct {
-       int addr_bits;
-       int data_bits;
-       int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
-       unsigned int (*read)(struct snd_soc_codec *, unsigned int);
-       unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
-       unsigned int (*spi_read)(struct snd_soc_codec *, unsigned int);
-} io_types[] = {
-       {
-               .addr_bits = 4, .data_bits = 12,
-               .write = snd_soc_4_12_write,
-       },
-       {
-               .addr_bits = 7, .data_bits = 9,
-               .write = snd_soc_7_9_write,
-       },
-       {
-               .addr_bits = 8, .data_bits = 8,
-               .write = snd_soc_8_8_write,
-               .i2c_read = snd_soc_8_8_read_i2c,
-       },
-       {
-               .addr_bits = 8, .data_bits = 16,
-               .write = snd_soc_8_16_write,
-               .i2c_read = snd_soc_8_16_read_i2c,
-       },
-       {
-               .addr_bits = 16, .data_bits = 8,
-               .write = snd_soc_16_8_write,
-               .i2c_read = snd_soc_16_8_read_i2c,
-               .spi_read = snd_soc_16_8_read_spi,
-       },
-       {
-               .addr_bits = 16, .data_bits = 16,
-               .write = snd_soc_16_16_write,
-               .i2c_read = snd_soc_16_16_read_i2c,
-       },
-};
-
 /**
  * snd_soc_codec_set_cache_io: Set up standard I/O functions.
  *
@@ -370,50 +112,51 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
                               int addr_bits, int data_bits,
                               enum snd_soc_control_type control)
 {
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(io_types); i++)
-               if (io_types[i].addr_bits == addr_bits &&
-                   io_types[i].data_bits == data_bits)
-                       break;
-       if (i == ARRAY_SIZE(io_types)) {
-               printk(KERN_ERR
-                      "No I/O functions for %d bit address %d bit data\n",
-                      addr_bits, data_bits);
-               return -EINVAL;
-       }
+       struct regmap_config config;
 
-       codec->write = io_types[i].write;
+       memset(&config, 0, sizeof(config));
+       codec->write = hw_write;
        codec->read = hw_read;
        codec->bulk_write_raw = snd_soc_hw_bulk_write_raw;
 
+       config.reg_bits = addr_bits;
+       config.val_bits = data_bits;
+
        switch (control) {
+#if defined(CONFIG_REGMAP_I2C) || defined(CONFIG_REGMAP_I2C_MODULE)
        case SND_SOC_I2C:
-#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
-               codec->hw_write = (hw_write_t)i2c_master_send;
-#endif
-               if (io_types[i].i2c_read)
-                       codec->hw_read = io_types[i].i2c_read;
-
-               codec->control_data = container_of(codec->dev,
-                                                  struct i2c_client,
-                                                  dev);
+               codec->control_data = regmap_init_i2c(to_i2c_client(codec->dev),
+                                                     &config);
                break;
+#endif
 
+#if defined(CONFIG_REGMAP_SPI) || defined(CONFIG_REGMAP_SPI_MODULE)
        case SND_SOC_SPI:
-#ifdef CONFIG_SPI_MASTER
-               codec->hw_write = do_spi_write;
+               codec->control_data = regmap_init_spi(to_spi_device(codec->dev),
+                                                     &config);
+               break;
 #endif
-               if (io_types[i].spi_read)
-                       codec->hw_read = io_types[i].spi_read;
 
-               codec->control_data = container_of(codec->dev,
-                                                  struct spi_device,
-                                                  dev);
+       case SND_SOC_REGMAP:
+               /* Device has made its own regmap arrangements */
                break;
+
+       default:
+               return -EINVAL;
        }
 
+       if (IS_ERR(codec->control_data))
+               return PTR_ERR(codec->control_data);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
-
+#else
+int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
+                              int addr_bits, int data_bits,
+                              enum snd_soc_control_type control)
+{
+       return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
+#endif
index 2879c88..1aee9fc 100644 (file)
@@ -27,8 +27,6 @@
 #include <sound/soc.h>
 #include <sound/initval.h>
 
-static DEFINE_MUTEX(pcm_mutex);
-
 static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
index 743d07b..a4e3f55 100644 (file)
@@ -201,7 +201,7 @@ static int __devinit txx9aclc_ac97_dev_probe(struct platform_device *pdev)
        if (!drvdata->base)
                return -EBUSY;
        err = devm_request_irq(&pdev->dev, irq, txx9aclc_ac97_irq,
-                              IRQF_DISABLED, dev_name(&pdev->dev), drvdata);
+                              0, dev_name(&pdev->dev), drvdata);
        if (err < 0)
                return err;
 
index ad7d4d7..f036776 100644 (file)
@@ -962,7 +962,7 @@ static int __devinit snd_amd7930_create(struct snd_card *card,
        amd7930_idle(amd);
 
        if (request_irq(irq, snd_amd7930_interrupt,
-                       IRQF_DISABLED | IRQF_SHARED, "amd7930", amd)) {
+                       IRQF_SHARED, "amd7930", amd)) {
                snd_printk(KERN_ERR "amd7930-%d: Unable to grab IRQ %d\n",
                           dev, irq);
                snd_amd7930_free(amd);