Merge branch 'fix/misc' into for-linus
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 736d456..e7848a0 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -199,6 +199,10 @@
 			acpi_display_output=video
 			See above.
 
+	acpi_early_pdc_eval	[HW,ACPI] Evaluate processor _PDC methods
+				early. Needed on some platforms to properly
+				initialize the EC.
+
 	acpi_irq_balance [HW,ACPI]
 			ACPI will balance active IRQs
 			default in APIC mode
@@ -311,6 +315,11 @@
 	aic79xx=	[HW,SCSI]
 			See Documentation/scsi/aic79xx.txt.
 
+	alignment=	[KNL,ARM]
+			Allow the default userspace alignment fault handler
+			behaviour to be specified.  Bit 0 enables warnings,
+			bit 1 enables fixups, and bit 2 sends a segfault.
+
 	amd_iommu=	[HW,X86-84]
 			Pass parameters to the AMD IOMMU driver in the system.
 			Possible values are:
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt
index 006b39d..e87f3cdc 100644
--- a/Documentation/networking/ip-sysctl.txt
+++ b/Documentation/networking/ip-sysctl.txt
@@ -1074,10 +1074,10 @@
 	Default: 5
 
 max_addresses - INTEGER
-	Number of maximum addresses per interface.  0 disables limitation.
-	It is recommended not set too large value (or 0) because it would
-	be too easy way to crash kernel to allow to create too much of
-	autoconfigured addresses.
+	Maximum number of autoconfigured addresses per interface.  Setting
+	to zero disables the limitation.  It is not recommended to set this
+	value too large (or to zero) because it would be an easy way to
+	crash the kernel by allowing too many addresses to be created.
 	Default: 16
 
 disable_ipv6 - BOOLEAN
diff --git a/Documentation/sound/alsa/HD-Audio-Models.txt b/Documentation/sound/alsa/HD-Audio-Models.txt
index e72cee9..1d38b0d 100644
--- a/Documentation/sound/alsa/HD-Audio-Models.txt
+++ b/Documentation/sound/alsa/HD-Audio-Models.txt
@@ -124,6 +124,8 @@
   asus-a7m	ASUS A7M
   macpro	MacPro support
   mb5		Macbook 5,1
+  macmini3	Macmini 3,1
+  mba21		Macbook Air 2,1
   mbp3		Macbook Pro rev3
   imac24	iMac 24'' with jack detection
   imac91	iMac 9,1
@@ -279,13 +281,16 @@
   laptop	Basic Laptop config (default)
   hp		HP Spartan laptop
   hp-dv6736	HP dv6736
+  hp-f700	HP Compaq Presario F700
   lenovo-x200	Lenovo X200 laptop
+  toshiba	Toshiba Satellite M300
 
 Conexant 5066
 =============
   laptop	Basic Laptop config (default)
   dell-laptop	Dell laptops
   olpc-xo-1_5	OLPC XO 1.5
+  ideapad       Lenovo IdeaPad U150
 
 STAC9200
 ========
diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt
index 6325bec..f4dd3bf 100644
--- a/Documentation/sound/alsa/HD-Audio.txt
+++ b/Documentation/sound/alsa/HD-Audio.txt
@@ -452,6 +452,33 @@
 sysfs entries, and the lines after `[hint]` are parsed as `hints`
 sysfs entries, respectively.
 
+Another example to override the codec vendor id from 0x12345678 to
+0xdeadbeef is like below:
+------------------------------------------------------------------------
+  [codec]
+  0x12345678 0xabcd1234 2
+
+  [vendor_id]
+  0xdeadbeef
+------------------------------------------------------------------------
+
+In the similar way, you can override the codec subsystem_id via
+`[subsystem_id]`, the revision id via `[revision_id]` line.
+Also, the codec chip name can be rewritten via `[chip_name]` line.
+------------------------------------------------------------------------
+  [codec]
+  0x12345678 0xabcd1234 2
+
+  [subsystem_id]
+  0xffff1111
+
+  [revision_id]
+  0x10
+
+  [chip_name]
+  My-own NEWS-0002
+------------------------------------------------------------------------
+
 The hd-audio driver reads the file via request_firmware().  Thus,
 a patch file has to be located on the appropriate firmware path,
 typically, /lib/firmware.  For example, when you pass the option
diff --git a/MAINTAINERS b/MAINTAINERS
index 412eff6..2533fc4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -616,10 +616,10 @@
 S:	Maintained
 
 ARM/CORTINA SYSTEMS GEMINI ARM ARCHITECTURE
-M:	Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+M:	Paulius Zaleckas <paulius.zaleckas@gmail.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 T:	git git://gitorious.org/linux-gemini/mainline.git
-S:	Maintained
+S:	Odd Fixes
 F:	arch/arm/mach-gemini/
 
 ARM/EBSA110 MACHINE SUPPORT
@@ -641,9 +641,9 @@
 F:	arch/arm/mach-pxa/ezx.c
 
 ARM/FARADAY FA526 PORT
-M:	Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
+M:	Paulius Zaleckas <paulius.zaleckas@gmail.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
-S:	Maintained
+S:	Odd Fixes
 F:	arch/arm/mm/*-fa*
 
 ARM/FOOTBRIDGE ARCHITECTURE
@@ -1733,10 +1733,9 @@
 F:	net/dccp/
 
 DECnet NETWORK LAYER
-M:	Christine Caulfield <christine.caulfield@googlemail.com>
 W:	http://linux-decnet.sourceforge.net
 L:	linux-decnet-user@lists.sourceforge.net
-S:	Maintained
+S:	Orphan
 F:	Documentation/networking/decnet.txt
 F:	net/decnet/
 
@@ -3490,9 +3489,9 @@
 F:	drivers/net/wireless/libertas/
 
 MARVELL MV643XX ETHERNET DRIVER
-M:	Lennert Buytenhek <buytenh@marvell.com>
+M:	Lennert Buytenhek <buytenh@wantstofly.org>
 L:	netdev@vger.kernel.org
-S:	Supported
+S:	Maintained
 F:	drivers/net/mv643xx_eth.*
 F:	include/linux/mv643xx.h
 
diff --git a/Makefile b/Makefile
index 12b1aa1..1b24895 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 33
-EXTRAVERSION = -rc8
+EXTRAVERSION =
 NAME = Man-Eating Seals of Antiquity
 
 # *DOCUMENTATION*
diff --git a/arch/arm/include/asm/cacheflush.h b/arch/arm/include/asm/cacheflush.h
index c77d2fa..8113bb5 100644
--- a/arch/arm/include/asm/cacheflush.h
+++ b/arch/arm/include/asm/cacheflush.h
@@ -42,7 +42,8 @@
 #endif
 
 #if defined(CONFIG_CPU_ARM920T) || defined(CONFIG_CPU_ARM922T) || \
-    defined(CONFIG_CPU_ARM925T) || defined(CONFIG_CPU_ARM1020)
+    defined(CONFIG_CPU_ARM925T) || defined(CONFIG_CPU_ARM1020) || \
+    defined(CONFIG_CPU_ARM1026)
 # define MULTI_CACHE 1
 #endif
 
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index c6c57b6..621acad 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -102,6 +102,7 @@
 #endif
 #ifdef CONFIG_OUTER_CACHE
 struct outer_cache_fns outer_cache;
+EXPORT_SYMBOL(outer_cache);
 #endif
 
 struct stack {
diff --git a/arch/arm/mach-gemini/gpio.c b/arch/arm/mach-gemini/gpio.c
index e726385..fe3bd5a 100644
--- a/arch/arm/mach-gemini/gpio.c
+++ b/arch/arm/mach-gemini/gpio.c
@@ -86,7 +86,7 @@
 	unsigned int reg_both, reg_level, reg_type;
 
 	reg_type = __raw_readl(base + GPIO_INT_TYPE);
-	reg_level = __raw_readl(base + GPIO_INT_BOTH_EDGE);
+	reg_level = __raw_readl(base + GPIO_INT_LEVEL);
 	reg_both = __raw_readl(base + GPIO_INT_BOTH_EDGE);
 
 	switch (type) {
@@ -117,7 +117,7 @@
 	}
 
 	__raw_writel(reg_type, base + GPIO_INT_TYPE);
-	__raw_writel(reg_level, base + GPIO_INT_BOTH_EDGE);
+	__raw_writel(reg_level, base + GPIO_INT_LEVEL);
 	__raw_writel(reg_both, base + GPIO_INT_BOTH_EDGE);
 
 	gpio_ack_irq(irq);
diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c
index 5fedc50..5fef73f 100644
--- a/arch/arm/mach-omap2/mux.c
+++ b/arch/arm/mach-omap2/mux.c
@@ -961,16 +961,14 @@
 	while (superset->reg_offset !=  OMAP_MUX_TERMINATOR) {
 		struct omap_mux *entry;
 
-#ifndef CONFIG_OMAP_MUX
-		/* Skip pins that are not muxed as GPIO by bootloader */
-		if (!OMAP_MODE_GPIO(omap_mux_read(superset->reg_offset))) {
+#ifdef CONFIG_OMAP_MUX
+		if (!superset->muxnames || !superset->muxnames[0]) {
 			superset++;
 			continue;
 		}
-#endif
-
-#if defined(CONFIG_OMAP_MUX) && defined(CONFIG_DEBUG_FS)
-		if (!superset->muxnames || !superset->muxnames[0]) {
+#else
+		/* Skip pins that are not muxed as GPIO by bootloader */
+		if (!OMAP_MODE_GPIO(omap_mux_read(superset->reg_offset))) {
 			superset++;
 			continue;
 		}
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index b270d62..62820ed 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -11,6 +11,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#include <linux/moduleparam.h>
 #include <linux/compiler.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -77,6 +78,8 @@
 static unsigned long ai_multi;
 static int ai_usermode;
 
+core_param(alignment, ai_usermode, int, 0600);
+
 #define UM_WARN		(1 << 0)
 #define UM_FIXUP	(1 << 1)
 #define UM_SIGNAL	(1 << 2)
diff --git a/arch/arm/plat-mxc/Makefile b/arch/arm/plat-mxc/Makefile
index 996cbac..6cee38d 100644
--- a/arch/arm/plat-mxc/Makefile
+++ b/arch/arm/plat-mxc/Makefile
@@ -13,3 +13,7 @@
 obj-$(CONFIG_MXC_ULPI) += ulpi.o
 obj-$(CONFIG_ARCH_MXC_AUDMUX_V1) += audmux-v1.o
 obj-$(CONFIG_ARCH_MXC_AUDMUX_V2) += audmux-v2.o
+ifdef CONFIG_SND_IMX_SOC
+obj-y += ssi-fiq.o
+obj-y += ssi-fiq-ksym.o
+endif
diff --git a/arch/arm/plat-mxc/ssi-fiq-ksym.c b/arch/arm/plat-mxc/ssi-fiq-ksym.c
new file mode 100644
index 0000000..b5fad45
--- /dev/null
+++ b/arch/arm/plat-mxc/ssi-fiq-ksym.c
@@ -0,0 +1,20 @@
+/*
+ * Exported ksyms for the SSI FIQ handler
+ *
+ * Copyright (C) 2009, Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+
+#include <mach/ssi.h>
+
+EXPORT_SYMBOL(imx_ssi_fiq_tx_buffer);
+EXPORT_SYMBOL(imx_ssi_fiq_rx_buffer);
+EXPORT_SYMBOL(imx_ssi_fiq_start);
+EXPORT_SYMBOL(imx_ssi_fiq_end);
+EXPORT_SYMBOL(imx_ssi_fiq_base);
+
diff --git a/arch/arm/plat-mxc/ssi-fiq.S b/arch/arm/plat-mxc/ssi-fiq.S
new file mode 100644
index 0000000..4ddce56
--- /dev/null
+++ b/arch/arm/plat-mxc/ssi-fiq.S
@@ -0,0 +1,134 @@
+/*
+ *  Copyright (C) 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+/*
+ * r8  = bit 0-15: tx offset, bit 16-31: tx buffer size
+ * r9  = bit 0-15: rx offset, bit 16-31: rx buffer size
+ */
+
+#define SSI_STX0	0x00
+#define SSI_SRX0	0x08
+#define SSI_SISR	0x14
+#define SSI_SIER	0x18
+#define SSI_SACNT	0x38
+
+#define SSI_SACNT_AC97EN	(1 << 0)
+
+#define SSI_SIER_TFE0_EN	(1 << 0)
+#define SSI_SISR_TFE0		(1 << 0)
+#define SSI_SISR_RFF0		(1 << 2)
+#define SSI_SIER_RFF0_EN	(1 << 2)
+
+		.text
+		.global	imx_ssi_fiq_start
+		.global	imx_ssi_fiq_end
+		.global imx_ssi_fiq_base
+		.global imx_ssi_fiq_rx_buffer
+		.global imx_ssi_fiq_tx_buffer
+
+imx_ssi_fiq_start:
+		ldr r12, imx_ssi_fiq_base
+
+		/* TX */
+		ldr r11, imx_ssi_fiq_tx_buffer
+
+		/* shall we send? */
+		ldr r13, [r12, #SSI_SIER]
+		tst r13, #SSI_SIER_TFE0_EN
+		beq 1f
+
+		/* TX FIFO empty? */
+		ldr r13, [r12, #SSI_SISR]
+		tst r13, #SSI_SISR_TFE0
+		beq 1f
+
+		mov r10, #0x10000
+		sub r10, #1
+		and r10, r10, r8	/* r10: current buffer offset */
+
+		add r11, r11, r10
+
+		ldrh r13, [r11]
+		strh r13, [r12, #SSI_STX0]
+
+		ldrh r13, [r11, #2]
+		strh r13, [r12, #SSI_STX0]
+
+		ldrh r13, [r11, #4]
+		strh r13, [r12, #SSI_STX0]
+
+		ldrh r13, [r11, #6]
+		strh r13, [r12, #SSI_STX0]
+
+		add r10, #8
+		lsr r13, r8, #16	/* r13: buffer size */
+		cmp r10, r13
+		lslgt r8, r13, #16
+		addle r8, #8
+1:
+		/* RX */
+
+		/* shall we receive? */
+		ldr r13, [r12, #SSI_SIER]
+		tst r13, #SSI_SIER_RFF0_EN
+		beq 1f
+
+		/* RX FIFO full? */
+		ldr r13, [r12, #SSI_SISR]
+		tst r13, #SSI_SISR_RFF0
+		beq 1f
+
+		ldr r11, imx_ssi_fiq_rx_buffer
+
+		mov r10, #0x10000
+		sub r10, #1
+		and r10, r10, r9	/* r10: current buffer offset */
+
+		add r11, r11, r10
+
+		ldr r13, [r12, #SSI_SACNT]
+		tst r13, #SSI_SACNT_AC97EN
+
+		ldr r13, [r12, #SSI_SRX0]
+		strh r13, [r11]
+
+		ldr r13, [r12, #SSI_SRX0]
+		strh r13, [r11, #2]
+
+		/* dummy read to skip slot 12 */
+		ldrne r13, [r12, #SSI_SRX0]
+
+		ldr r13, [r12, #SSI_SRX0]
+		strh r13, [r11, #4]
+
+		ldr r13, [r12, #SSI_SRX0]
+		strh r13, [r11, #6]
+
+		/* dummy read to skip slot 12 */
+		ldrne r13, [r12, #SSI_SRX0]
+
+		add r10, #8
+		lsr r13, r9, #16	/* r13: buffer size */
+		cmp r10, r13
+		lslgt r9, r13, #16
+		addle r9, #8
+
+1:
+		@ return from FIQ
+		subs	pc, lr, #4
+imx_ssi_fiq_base:
+		.word 0x0
+imx_ssi_fiq_rx_buffer:
+		.word 0x0
+imx_ssi_fiq_tx_buffer:
+		.word 0x0
+imx_ssi_fiq_end:
+
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index 5a79fc6..31c2f4c 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -12,7 +12,7 @@
 #
 #   http://www.arm.linux.org.uk/developer/machines/?action=new
 #
-# Last update: Thu Jan 28 22:15:54 2010
+# Last update: Sat Feb 20 14:16:15 2010
 #
 # machine_is_xxx	CONFIG_xxxx		MACH_TYPE_xxx		number
 #
@@ -2257,7 +2257,7 @@
 oratismadi		MACH_ORATISMADI		ORATISMADI		2269
 oratisot16		MACH_ORATISOT16		ORATISOT16		2270
 oratisdesk		MACH_ORATISDESK		ORATISDESK		2271
-v2_ca9			MACH_V2P_CA9		V2P_CA9			2272
+vexpress		MACH_VEXPRESS		VEXPRESS		2272
 sintexo			MACH_SINTEXO		SINTEXO			2273
 cm3389			MACH_CM3389		CM3389			2274
 omap3_cio		MACH_OMAP3_CIO		OMAP3_CIO		2275
@@ -2636,3 +2636,45 @@
 dm365_leopard		MACH_DM365_LEOPARD	DM365_LEOPARD		2649
 mityomapl138		MACH_MITYOMAPL138	MITYOMAPL138		2650
 scat110			MACH_SCAT110		SCAT110			2651
+acer_a1			MACH_ACER_A1		ACER_A1			2652
+cmcontrol		MACH_CMCONTROL		CMCONTROL		2653
+pelco_lamar		MACH_PELCO_LAMAR	PELCO_LAMAR		2654
+rfp43			MACH_RFP43		RFP43			2655
+sk86r0301		MACH_SK86R0301		SK86R0301		2656
+ctpxa			MACH_CTPXA		CTPXA			2657
+epb_arm9_a		MACH_EPB_ARM9_A		EPB_ARM9_A		2658
+guruplug		MACH_GURUPLUG		GURUPLUG		2659
+spear310		MACH_SPEAR310		SPEAR310		2660
+spear320		MACH_SPEAR320		SPEAR320		2661
+robotx			MACH_ROBOTX		ROBOTX			2662
+lsxhl			MACH_LSXHL		LSXHL			2663
+smartlite		MACH_SMARTLITE		SMARTLITE		2664
+cws2			MACH_CWS2		CWS2			2665
+m619			MACH_M619		M619			2666
+smartview		MACH_SMARTVIEW		SMARTVIEW		2667
+lsa_salsa		MACH_LSA_SALSA		LSA_SALSA		2668
+kizbox			MACH_KIZBOX		KIZBOX			2669
+htccharmer		MACH_HTCCHARMER		HTCCHARMER		2670
+guf_neso_lt		MACH_GUF_NESO_LT	GUF_NESO_LT		2671
+pm9g45			MACH_PM9G45		PM9G45			2672
+htcpanther		MACH_HTCPANTHER		HTCPANTHER		2673
+htcpanther_cdma		MACH_HTCPANTHER_CDMA	HTCPANTHER_CDMA		2674
+reb01			MACH_REB01		REB01			2675
+aquila			MACH_AQUILA		AQUILA			2676
+spark_sls_hw2		MACH_SPARK_SLS_HW2	SPARK_SLS_HW2		2677
+sheeva_esata		MACH_ESATA_SHEEVAPLUG	ESATA_SHEEVAPLUG	2678
+surf7x30		MACH_SURF7X30		SURF7X30		2679
+micro2440		MACH_MICRO2440		MICRO2440		2680
+am2440			MACH_AM2440		AM2440			2681
+tq2440			MACH_TQ2440		TQ2440			2682
+lpc2478oem		MACH_LPC2478OEM		LPC2478OEM		2683
+ak880x			MACH_AK880X		AK880X			2684
+cobra3530		MACH_COBRA3530		COBRA3530		2685
+pmppb			MACH_PMPPB		PMPPB			2686
+u6715			MACH_U6715		U6715			2687
+axar1500_sender		MACH_AXAR1500_SENDER	AXAR1500_SENDER		2688
+g30_dvb			MACH_G30_DVB		G30_DVB			2689
+vc088x			MACH_VC088X		VC088X			2690
+mioa702			MACH_MIOA702		MIOA702			2691
+hpmin			MACH_HPMIN		HPMIN			2692
+ak880xak		MACH_AK880XAK		AK880XAK		2693
diff --git a/arch/ia64/include/asm/acpi.h b/arch/ia64/include/asm/acpi.h
index 7ae5889..e97b255 100644
--- a/arch/ia64/include/asm/acpi.h
+++ b/arch/ia64/include/asm/acpi.h
@@ -94,6 +94,7 @@
 #define acpi_noirq 0	/* ACPI always enabled on IA64 */
 #define acpi_pci_disabled 0 /* ACPI PCI always enabled on IA64 */
 #define acpi_strict 1	/* no ACPI spec workarounds on IA64 */
+#define acpi_ht 0	/* no HT-only mode on IA64 */
 #endif
 #define acpi_processor_cstate_check(x) (x) /* no idle limits on IA64 :) */
 static inline void disable_acpi(void) { }
diff --git a/arch/ia64/include/asm/elf.h b/arch/ia64/include/asm/elf.h
index e14108b..4c41656 100644
--- a/arch/ia64/include/asm/elf.h
+++ b/arch/ia64/include/asm/elf.h
@@ -201,7 +201,9 @@
    relevant until we have real hardware to play with... */
 #define ELF_PLATFORM	NULL
 
-#define SET_PERSONALITY(ex)	set_personality(PER_LINUX)
+#define SET_PERSONALITY(ex)	\
+	set_personality((current->personality & ~PER_MASK) | PER_LINUX)
+
 #define elf_read_implies_exec(ex, executable_stack)					\
 	((executable_stack!=EXSTACK_DISABLE_X) && ((ex).e_flags & EF_IA_64_LINUX_EXECUTABLE_STACK) != 0)
 
diff --git a/arch/ia64/sn/kernel/setup.c b/arch/ia64/sn/kernel/setup.c
index ece1bf9..e456f06 100644
--- a/arch/ia64/sn/kernel/setup.c
+++ b/arch/ia64/sn/kernel/setup.c
@@ -71,7 +71,7 @@
 DEFINE_PER_CPU(struct sn_hub_info_s, __sn_hub_info);
 EXPORT_PER_CPU_SYMBOL(__sn_hub_info);
 
-DEFINE_PER_CPU(short [MAX_COMPACT_NODES], __sn_cnodeid_to_nasid);
+DEFINE_PER_CPU(short, __sn_cnodeid_to_nasid[MAX_COMPACT_NODES]);
 EXPORT_PER_CPU_SYMBOL(__sn_cnodeid_to_nasid);
 
 DEFINE_PER_CPU(struct nodepda_s *, __sn_nodepda);
diff --git a/arch/microblaze/include/asm/io.h b/arch/microblaze/include/asm/io.h
index fc9997b..267c7c7 100644
--- a/arch/microblaze/include/asm/io.h
+++ b/arch/microblaze/include/asm/io.h
@@ -217,7 +217,7 @@
  * Little endian
  */
 
-#define out_le32(a, v) __raw_writel(__cpu_to_le32(v), (a));
+#define out_le32(a, v) __raw_writel(__cpu_to_le32(v), (a))
 #define out_le16(a, v) __raw_writew(__cpu_to_le16(v), (a))
 
 #define in_le32(a) __le32_to_cpu(__raw_readl(a))
diff --git a/arch/microblaze/kernel/cpu/cache.c b/arch/microblaze/kernel/cpu/cache.c
index d9d6383..2a56bcc 100644
--- a/arch/microblaze/kernel/cpu/cache.c
+++ b/arch/microblaze/kernel/cpu/cache.c
@@ -172,16 +172,15 @@
 /* It is used only first parameter for OP - for wic, wdc */
 #define CACHE_RANGE_LOOP_1(start, end, line_length, op)			\
 do {									\
-	int step = -line_length;					\
-	int count = end - start;					\
-	BUG_ON(count <= 0);						\
+	int volatile temp;						\
+	BUG_ON(end - start <= 0);					\
 									\
-	__asm__ __volatile__ (" 1:	addk	%0, %0, %1;		\
-					" #op " %0, r0;			\
-					bgtid   %1, 1b;			\
-					addk    %1, %1, %2;		\
-					" : : "r" (start), "r" (count),	\
-					"r" (step) : "memory");		\
+	__asm__ __volatile__ (" 1:	" #op " %1, r0;			\
+					cmpu	%0, %1, %2;		\
+					bgtid	%0, 1b;			\
+					addk	%1, %1, %3;		\
+				" : : "r" (temp), "r" (start), "r" (end),\
+					"r" (line_length) : "memory");	\
 } while (0);
 
 static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)
@@ -313,16 +312,6 @@
 	pr_debug("%s\n", __func__);
 	CACHE_ALL_LOOP2(cpuinfo.dcache_size, cpuinfo.dcache_line_length,
 					wdc.clear)
-
-#if 0
-	unsigned int i;
-
-	pr_debug("%s\n", __func__);
-
-	/* Just loop through cache size and invalidate it */
-	for (i = 0; i < cpuinfo.dcache_size; i += cpuinfo.dcache_line_length)
-			__invalidate_dcache(0, i);
-#endif
 }
 
 static void __invalidate_dcache_range_wb(unsigned long start,
diff --git a/arch/mips/bcm47xx/prom.c b/arch/mips/bcm47xx/prom.c
index c51405e..29d3cbf 100644
--- a/arch/mips/bcm47xx/prom.c
+++ b/arch/mips/bcm47xx/prom.c
@@ -141,6 +141,14 @@
 			break;
 	}
 
+	/* Ignoring the last page when ddr size is 128M. Cached
+	 * accesses to last page is causing the processor to prefetch
+	 * using address above 128M stepping out of the ddr address
+	 * space.
+	 */
+	if (mem == 0x8000000)
+		mem -= 0x1000;
+
 	add_memory_region(0, mem, BOOT_MEM_RAM);
 }
 
diff --git a/arch/mips/mm/highmem.c b/arch/mips/mm/highmem.c
index e274fda..127d732 100644
--- a/arch/mips/mm/highmem.c
+++ b/arch/mips/mm/highmem.c
@@ -1,5 +1,6 @@
 #include <linux/module.h>
 #include <linux/highmem.h>
+#include <linux/sched.h>
 #include <linux/smp.h>
 #include <asm/fixmap.h>
 #include <asm/tlbflush.h>
diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig
index 524d935..f388dc6 100644
--- a/arch/parisc/Kconfig
+++ b/arch/parisc/Kconfig
@@ -18,7 +18,6 @@
 	select BUG
 	select HAVE_PERF_EVENTS
 	select GENERIC_ATOMIC64 if !64BIT
-	select HAVE_ARCH_TRACEHOOK
 	help
 	  The PA-RISC microprocessor is designed by Hewlett-Packard and used
 	  in many of their workstations & servers (HP9000 700 and 800 series,
diff --git a/arch/parisc/kernel/pci.c b/arch/parisc/kernel/pci.c
index f7064ab..9e74bfe 100644
--- a/arch/parisc/kernel/pci.c
+++ b/arch/parisc/kernel/pci.c
@@ -18,7 +18,6 @@
 
 #include <asm/io.h>
 #include <asm/system.h>
-#include <asm/cache.h>		/* for L1_CACHE_BYTES */
 #include <asm/superio.h>
 
 #define DEBUG_RESOURCES 0
@@ -123,6 +122,10 @@
 	} else {
 		printk(KERN_WARNING "pci_bios != NULL but init() is!\n");
 	}
+
+	/* Set the CLS for PCI as early as possible. */
+	pci_cache_line_size = pci_dfl_cache_line_size;
+
 	return 0;
 }
 
@@ -171,7 +174,7 @@
 	** upper byte is PCI_LATENCY_TIMER.
 	*/
 	pci_write_config_word(dev, PCI_CACHE_LINE_SIZE,
-				(0x80 << 8) | (L1_CACHE_BYTES / sizeof(u32)));
+			      (0x80 << 8) | pci_cache_line_size);
 }
 
 
diff --git a/arch/powerpc/platforms/85xx/mpc85xx_mds.c b/arch/powerpc/platforms/85xx/mpc85xx_mds.c
index 21f61b8..cc29c0f 100644
--- a/arch/powerpc/platforms/85xx/mpc85xx_mds.c
+++ b/arch/powerpc/platforms/85xx/mpc85xx_mds.c
@@ -338,7 +338,8 @@
 	}
 
 	mpic = mpic_alloc(np, r.start,
-			MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN,
+			MPIC_PRIMARY | MPIC_WANTS_RESET | MPIC_BIG_ENDIAN |
+			MPIC_BROKEN_FRR_NIRQS,
 			0, 256, " OpenPIC  ");
 	BUG_ON(mpic == NULL);
 	of_node_put(np);
diff --git a/arch/powerpc/platforms/85xx/smp.c b/arch/powerpc/platforms/85xx/smp.c
index 04160a4..a15f582 100644
--- a/arch/powerpc/platforms/85xx/smp.c
+++ b/arch/powerpc/platforms/85xx/smp.c
@@ -46,6 +46,7 @@
 	__iomem u32 *bptr_vaddr;
 	struct device_node *np;
 	int n = 0;
+	int ioremappable;
 
 	WARN_ON (nr < 0 || nr >= NR_CPUS);
 
@@ -59,21 +60,37 @@
 		return;
 	}
 
+	/*
+	 * A secondary core could be in a spinloop in the bootpage
+	 * (0xfffff000), somewhere in highmem, or somewhere in lowmem.
+	 * The bootpage and highmem can be accessed via ioremap(), but
+	 * we need to directly access the spinloop if its in lowmem.
+	 */
+	ioremappable = *cpu_rel_addr > virt_to_phys(high_memory);
+
 	/* Map the spin table */
-	bptr_vaddr = ioremap(*cpu_rel_addr, SIZE_BOOT_ENTRY);
+	if (ioremappable)
+		bptr_vaddr = ioremap(*cpu_rel_addr, SIZE_BOOT_ENTRY);
+	else
+		bptr_vaddr = phys_to_virt(*cpu_rel_addr);
 
 	local_irq_save(flags);
 
 	out_be32(bptr_vaddr + BOOT_ENTRY_PIR, nr);
 	out_be32(bptr_vaddr + BOOT_ENTRY_ADDR_LOWER, __pa(__early_start));
 
+	if (!ioremappable)
+		flush_dcache_range((ulong)bptr_vaddr,
+				(ulong)(bptr_vaddr + SIZE_BOOT_ENTRY));
+
 	/* Wait a bit for the CPU to ack. */
 	while ((__secondary_hold_acknowledge != nr) && (++n < 1000))
 		mdelay(1);
 
 	local_irq_restore(flags);
 
-	iounmap(bptr_vaddr);
+	if (ioremappable)
+		iounmap(bptr_vaddr);
 
 	pr_debug("waited %d msecs for CPU #%d.\n", n, nr);
 }
diff --git a/arch/sh/include/asm/siu.h b/arch/sh/include/asm/siu.h
new file mode 100644
index 0000000..57565a3
--- /dev/null
+++ b/arch/sh/include/asm/siu.h
@@ -0,0 +1,26 @@
+/*
+ * platform header for the SIU ASoC driver
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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 ASM_SIU_H
+#define ASM_SIU_H
+
+#include <asm/dma-sh.h>
+
+struct device;
+
+struct siu_platform {
+	struct device *dma_dev;
+	enum sh_dmae_slave_chan_id dma_slave_tx_a;
+	enum sh_dmae_slave_chan_id dma_slave_rx_a;
+	enum sh_dmae_slave_chan_id dma_slave_tx_b;
+	enum sh_dmae_slave_chan_id dma_slave_rx_b;
+};
+
+#endif /* ASM_SIU_H */
diff --git a/arch/sh/kernel/ptrace_64.c b/arch/sh/kernel/ptrace_64.c
index 873ebdc..b063eb8 100644
--- a/arch/sh/kernel/ptrace_64.c
+++ b/arch/sh/kernel/ptrace_64.c
@@ -133,6 +133,8 @@
 	struct pt_regs *regs = child->thread.uregs;
 
 	regs->sr |= SR_SSTEP;	/* auto-resetting upon exception */
+
+	set_tsk_thread_flag(child, TIF_SINGLESTEP);
 }
 
 void user_disable_single_step(struct task_struct *child)
@@ -140,6 +142,8 @@
 	struct pt_regs *regs = child->thread.uregs;
 
 	regs->sr &= ~SR_SSTEP;
+
+	clear_tsk_thread_flag(child, TIF_SINGLESTEP);
 }
 
 static int genregs_get(struct task_struct *target,
@@ -454,6 +458,8 @@
 
 asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
 {
+	int step;
+
 	if (unlikely(current->audit_context))
 		audit_syscall_exit(AUDITSC_RESULT(regs->regs[9]),
 				   regs->regs[9]);
@@ -461,8 +467,9 @@
 	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
 		trace_sys_exit(regs, regs->regs[9]);
 
-	if (test_thread_flag(TIF_SYSCALL_TRACE))
-		tracehook_report_syscall_exit(regs, 0);
+	step = test_thread_flag(TIF_SINGLESTEP);
+	if (step || test_thread_flag(TIF_SYSCALL_TRACE))
+		tracehook_report_syscall_exit(regs, step);
 }
 
 /* Called with interrupts disabled */
diff --git a/arch/sh/kernel/signal_64.c b/arch/sh/kernel/signal_64.c
index ce76dbd..580e97d 100644
--- a/arch/sh/kernel/signal_64.c
+++ b/arch/sh/kernel/signal_64.c
@@ -118,7 +118,9 @@
 			 * clear the TS_RESTORE_SIGMASK flag.
 			 */
 			current_thread_info()->status &= ~TS_RESTORE_SIGMASK;
-			tracehook_signal_handler(signr, &info, &ka, regs, 0);
+
+			tracehook_signal_handler(signr, &info, &ka, regs,
+					test_thread_flag(TIF_SINGLESTEP));
 			return 1;
 		}
 	}
diff --git a/arch/sparc/include/asm/stat.h b/arch/sparc/include/asm/stat.h
index 55db5ec..39327d6 100644
--- a/arch/sparc/include/asm/stat.h
+++ b/arch/sparc/include/asm/stat.h
@@ -53,8 +53,8 @@
 	ino_t		st_ino;
 	mode_t		st_mode;
 	short		st_nlink;
-	uid_t		st_uid;
-	gid_t		st_gid;
+	uid16_t		st_uid;
+	gid16_t		st_gid;
 	unsigned short	st_rdev;
 	off_t		st_size;
 	time_t		st_atime;
diff --git a/arch/sparc/kernel/kstack.h b/arch/sparc/kernel/kstack.h
index 4248d96..5247283 100644
--- a/arch/sparc/kernel/kstack.h
+++ b/arch/sparc/kernel/kstack.h
@@ -11,6 +11,10 @@
 {
 	unsigned long base = (unsigned long) tp;
 
+	/* Stack pointer must be 16-byte aligned.  */
+	if (sp & (16UL - 1))
+		return false;
+
 	if (sp >= (base + sizeof(struct thread_info)) &&
 	    sp <= (base + THREAD_SIZE - sizeof(struct sparc_stackf)))
 		return true;
diff --git a/arch/sparc/kernel/of_device_32.c b/arch/sparc/kernel/of_device_32.c
index 4c26eb5..53a58b3 100644
--- a/arch/sparc/kernel/of_device_32.c
+++ b/arch/sparc/kernel/of_device_32.c
@@ -105,7 +105,7 @@
 
 static int of_bus_ambapp_match(struct device_node *np)
 {
-	return !strcmp(np->name, "ambapp");
+	return !strcmp(np->type, "ambapp");
 }
 
 static void of_bus_ambapp_count_cells(struct device_node *child,
diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c
index 539e83f..592b03d 100644
--- a/arch/sparc/kernel/pci.c
+++ b/arch/sparc/kernel/pci.c
@@ -247,6 +247,7 @@
 					 struct pci_bus *bus, int devfn)
 {
 	struct dev_archdata *sd;
+	struct pci_slot *slot;
 	struct of_device *op;
 	struct pci_dev *dev;
 	const char *type;
@@ -286,6 +287,11 @@
 	dev->dev.bus = &pci_bus_type;
 	dev->devfn = devfn;
 	dev->multifunction = 0;		/* maybe a lie? */
+	set_pcie_port_type(dev);
+
+	list_for_each_entry(slot, &dev->bus->slots, list)
+		if (PCI_SLOT(dev->devfn) == slot->number)
+			dev->slot = slot;
 
 	dev->vendor = of_getintprop_default(node, "vendor-id", 0xffff);
 	dev->device = of_getintprop_default(node, "device-id", 0xffff);
@@ -322,6 +328,7 @@
 
 	dev->current_state = 4;		/* unknown power state */
 	dev->error_state = pci_channel_io_normal;
+	dev->dma_mask = 0xffffffff;
 
 	if (!strcmp(node->name, "pci")) {
 		/* a PCI-PCI bridge */
diff --git a/arch/sparc/kernel/tsb.S b/arch/sparc/kernel/tsb.S
index 8c91d9b..db15d12 100644
--- a/arch/sparc/kernel/tsb.S
+++ b/arch/sparc/kernel/tsb.S
@@ -191,10 +191,12 @@
 
 tsb_itlb_load:
 	/* Executable bit must be set.  */
-661:	andcc		%g5, _PAGE_EXEC_4U, %g0
-	.section	.sun4v_1insn_patch, "ax"
+661:	sethi		%hi(_PAGE_EXEC_4U), %g4
+	andcc		%g5, %g4, %g0
+	.section	.sun4v_2insn_patch, "ax"
 	.word		661b
 	andcc		%g5, _PAGE_EXEC_4V, %g0
+	nop
 	.previous
 
 	be,pn		%xcc, tsb_do_fault
diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h
index 1994d3f..f2ad216 100644
--- a/arch/x86/include/asm/elf.h
+++ b/arch/x86/include/asm/elf.h
@@ -170,10 +170,7 @@
 }
 
 #define ELF_PLAT_INIT(_r, load_addr)			\
-do {							\
-	elf_common_init(&current->thread, _r, 0);	\
-	clear_thread_flag(TIF_IA32);			\
-} while (0)
+	elf_common_init(&current->thread, _r, 0)
 
 #define	COMPAT_ELF_PLAT_INIT(regs, load_addr)		\
 	elf_common_init(&current->thread, regs, __USER_DS)
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index fc801ba..b753ea5 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -450,6 +450,8 @@
 	struct perf_event	*ptrace_bps[HBP_NUM];
 	/* Debug status used for traps, single steps, etc... */
 	unsigned long           debugreg6;
+	/* Keep track of the exact dr7 value set by the user */
+	unsigned long           ptrace_dr7;
 	/* Fault info: */
 	unsigned long		cr2;
 	unsigned long		trap_no;
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 0acbcdf..af1c583 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -1344,14 +1344,6 @@
 	 },
 	{
 	 .callback = force_acpi_ht,
-	 .ident = "ASUS P2B-DS",
-	 .matches = {
-		     DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
-		     DMI_MATCH(DMI_BOARD_NAME, "P2B-DS"),
-		     },
-	 },
-	{
-	 .callback = force_acpi_ht,
 	 .ident = "ASUS CUR-DLS",
 	 .matches = {
 		     DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
diff --git a/arch/x86/kernel/hw_breakpoint.c b/arch/x86/kernel/hw_breakpoint.c
index 05d5fec..bb6006e 100644
--- a/arch/x86/kernel/hw_breakpoint.c
+++ b/arch/x86/kernel/hw_breakpoint.c
@@ -212,25 +212,6 @@
 	return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
 }
 
-/*
- * Store a breakpoint's encoded address, length, and type.
- */
-static int arch_store_info(struct perf_event *bp)
-{
-	struct arch_hw_breakpoint *info = counter_arch_bp(bp);
-	/*
-	 * For kernel-addresses, either the address or symbol name can be
-	 * specified.
-	 */
-	if (info->name)
-		info->address = (unsigned long)
-				kallsyms_lookup_name(info->name);
-	if (info->address)
-		return 0;
-
-	return -EINVAL;
-}
-
 int arch_bp_generic_fields(int x86_len, int x86_type,
 			   int *gen_len, int *gen_type)
 {
@@ -362,10 +343,13 @@
 		return ret;
 	}
 
-	ret = arch_store_info(bp);
-
-	if (ret < 0)
-		return ret;
+	/*
+	 * For kernel-addresses, either the address or symbol name can be
+	 * specified.
+	 */
+	if (info->name)
+		info->address = (unsigned long)
+				kallsyms_lookup_name(info->name);
 	/*
 	 * Check that the low-order bits of the address are appropriate
 	 * for the alignment implied by len.
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 41a26a8..126f0b4 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -527,6 +527,7 @@
 
 	/* Make sure to be in 32bit mode */
 	set_thread_flag(TIF_IA32);
+	current->personality |= force_personality32;
 
 	/* Prepare the first "return" to user space */
 	current_thread_info()->status |= TS_COMPAT;
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 017d937..0c1033d 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -702,7 +702,7 @@
 	} else if (n == 6) {
 		val = thread->debugreg6;
 	 } else if (n == 7) {
-		val = ptrace_get_dr7(thread->ptrace_bps);
+		val = thread->ptrace_dr7;
 	}
 	return val;
 }
@@ -778,8 +778,11 @@
 			return rc;
 	}
 	/* All that's left is DR7 */
-	if (n == 7)
+	if (n == 7) {
 		rc = ptrace_write_dr7(tsk, val);
+		if (!rc)
+			thread->ptrace_dr7 = val;
+	}
 
 ret_path:
 	return rc;
diff --git a/block/blk-core.c b/block/blk-core.c
index 718897e6..d1a9a0a 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1147,7 +1147,7 @@
  */
 static inline bool queue_should_plug(struct request_queue *q)
 {
-	return !(blk_queue_nonrot(q) && blk_queue_queuing(q));
+	return !(blk_queue_nonrot(q) && blk_queue_tagged(q));
 }
 
 static int __make_request(struct request_queue *q, struct bio *bio)
@@ -1859,15 +1859,8 @@
 	 * and to it is freed is accounted as io that is in progress at
 	 * the driver side.
 	 */
-	if (blk_account_rq(rq)) {
+	if (blk_account_rq(rq))
 		q->in_flight[rq_is_sync(rq)]++;
-		/*
-		 * Mark this device as supporting hardware queuing, if
-		 * we have more IOs in flight than 4.
-		 */
-		if (!blk_queue_queuing(q) && queue_in_flight(q) > 4)
-			set_bit(QUEUE_FLAG_CQ, &q->queue_flags);
-	}
 }
 
 /**
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index bbc2c13..b2586f5 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -935,6 +935,7 @@
 	struct platform_device *dd;
 
 	id = dock_station_count;
+	memset(&ds, 0, sizeof(ds));
 	dd = platform_device_register_data(NULL, "dock", id, &ds, sizeof(ds));
 	if (IS_ERR(dd))
 		return PTR_ERR(dd);
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 7c0441f..cc978a8 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -110,6 +110,14 @@
 	  DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"),
 	  DMI_MATCH(DMI_BIOS_VERSION,"SHE845M0.86C.0013.D.0302131307")},
 	 (void *)2},
+	{ set_max_cstate, "Pavilion zv5000", {
+	  DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+	  DMI_MATCH(DMI_PRODUCT_NAME,"Pavilion zv5000 (DS502A#ABA)")},
+	 (void *)1},
+	{ set_max_cstate, "Asus L8400B", {
+	  DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."),
+	  DMI_MATCH(DMI_PRODUCT_NAME,"L8400B series Notebook PC")},
+	 (void *)1},
 	{},
 };
 
@@ -872,12 +880,14 @@
 		return(acpi_idle_enter_c1(dev, state));
 
 	local_irq_disable();
-	current_thread_info()->status &= ~TS_POLLING;
-	/*
-	 * TS_POLLING-cleared state must be visible before we test
-	 * NEED_RESCHED:
-	 */
-	smp_mb();
+	if (cx->entry_method != ACPI_CSTATE_FFH) {
+		current_thread_info()->status &= ~TS_POLLING;
+		/*
+		 * TS_POLLING-cleared state must be visible before we test
+		 * NEED_RESCHED:
+		 */
+		smp_mb();
+	}
 
 	if (unlikely(need_resched())) {
 		current_thread_info()->status |= TS_POLLING;
@@ -957,12 +967,14 @@
 	}
 
 	local_irq_disable();
-	current_thread_info()->status &= ~TS_POLLING;
-	/*
-	 * TS_POLLING-cleared state must be visible before we test
-	 * NEED_RESCHED:
-	 */
-	smp_mb();
+	if (cx->entry_method != ACPI_CSTATE_FFH) {
+		current_thread_info()->status &= ~TS_POLLING;
+		/*
+		 * TS_POLLING-cleared state must be visible before we test
+		 * NEED_RESCHED:
+		 */
+		smp_mb();
+	}
 
 	if (unlikely(need_resched())) {
 		current_thread_info()->status |= TS_POLLING;
diff --git a/drivers/acpi/processor_pdc.c b/drivers/acpi/processor_pdc.c
index 7247819..e306ba9 100644
--- a/drivers/acpi/processor_pdc.c
+++ b/drivers/acpi/processor_pdc.c
@@ -125,6 +125,8 @@
 	return status;
 }
 
+static int early_pdc_done;
+
 void acpi_processor_set_pdc(acpi_handle handle)
 {
 	struct acpi_object_list *obj_list;
@@ -132,6 +134,9 @@
 	if (arch_has_acpi_pdc() == false)
 		return;
 
+	if (early_pdc_done)
+		return;
+
 	obj_list = acpi_processor_alloc_pdc();
 	if (!obj_list)
 		return;
@@ -151,6 +156,13 @@
 	return 0;
 }
 
+static int param_early_pdc_optin(char *s)
+{
+	early_pdc_optin = 1;
+	return 1;
+}
+__setup("acpi_early_pdc_eval", param_early_pdc_optin);
+
 static struct dmi_system_id __cpuinitdata early_pdc_optin_table[] = {
 	{
 	set_early_pdc_optin, "HP Envy", {
@@ -192,4 +204,6 @@
 	acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
 			    ACPI_UINT32_MAX,
 			    early_init_pdc, NULL, NULL, NULL);
+
+	early_pdc_done = 1;
 }
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index 2cabadc..a959f6a 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -413,7 +413,11 @@
 	if (result)
 		goto update_bios;
 
-	return 0;
+	/* We need to call _PPC once when cpufreq starts */
+	if (ignore_ppc != 1)
+		result = acpi_processor_get_platform_limit(pr);
+
+	return result;
 
 	/*
 	 * Having _PPC but missing frequencies (_PSS, _PCT) is a very good hint that
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index ff9f622..3e00967 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1336,9 +1336,25 @@
 
 	if (child)
 		*child = device;
-	return 0;
+
+	if (device)
+		return 0;
+	else
+		return -ENODEV;
 }
 
+/*
+ * acpi_bus_add and acpi_bus_start
+ *
+ * scan a given ACPI tree and (probably recently hot-plugged)
+ * create and add or starts found devices.
+ *
+ * If no devices were found -ENODEV is returned which does not
+ * mean that this is a real error, there just have been no suitable
+ * ACPI objects in the table trunk from which the kernel could create
+ * a device and add/start an appropriate driver.
+ */
+
 int
 acpi_bus_add(struct acpi_device **child,
 	     struct acpi_device *parent, acpi_handle handle, int type)
@@ -1348,8 +1364,7 @@
 	memset(&ops, 0, sizeof(ops));
 	ops.acpi_op_add = 1;
 
-	acpi_bus_scan(handle, &ops, child);
-	return 0;
+	return acpi_bus_scan(handle, &ops, child);
 }
 EXPORT_SYMBOL(acpi_bus_add);
 
@@ -1357,11 +1372,13 @@
 {
 	struct acpi_bus_ops ops;
 
+	if (!device)
+		return -EINVAL;
+
 	memset(&ops, 0, sizeof(ops));
 	ops.acpi_op_start = 1;
 
-	acpi_bus_scan(device->handle, &ops, NULL);
-	return 0;
+	return acpi_bus_scan(device->handle, &ops, NULL);
 }
 EXPORT_SYMBOL(acpi_bus_start);
 
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
index f336bca7..8a0ed28 100644
--- a/drivers/acpi/tables.c
+++ b/drivers/acpi/tables.c
@@ -213,7 +213,7 @@
 	unsigned long table_end;
 	acpi_size tbl_size;
 
-	if (acpi_disabled)
+	if (acpi_disabled && !acpi_ht)
 		return -ENODEV;
 
 	if (!handler)
@@ -280,7 +280,7 @@
 	struct acpi_table_header *table = NULL;
 	acpi_size tbl_size;
 
-	if (acpi_disabled)
+	if (acpi_disabled && !acpi_ht)
 		return -ENODEV;
 
 	if (!handler)
diff --git a/drivers/base/class.c b/drivers/base/class.c
index 161746d..6e2c3b0 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -59,6 +59,8 @@
 	else
 		pr_debug("class '%s' does not have a release() function, "
 			 "be careful\n", class->name);
+
+	kfree(cp);
 }
 
 static struct sysfs_ops class_sysfs_ops = {
diff --git a/drivers/clocksource/cs5535-clockevt.c b/drivers/clocksource/cs5535-clockevt.c
index 27d20fa..b314a99 100644
--- a/drivers/clocksource/cs5535-clockevt.c
+++ b/drivers/clocksource/cs5535-clockevt.c
@@ -21,7 +21,7 @@
 
 #define DRV_NAME "cs5535-clockevt"
 
-static int timer_irq = CONFIG_CS5535_MFGPT_DEFAULT_IRQ;
+static int timer_irq;
 module_param_named(irq, timer_irq, int, 0644);
 MODULE_PARM_DESC(irq, "Which IRQ to use for the clock source MFGPT ticks.");
 
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index cbaf420..2d3dc7d 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -893,20 +893,31 @@
 
 static struct kmem_cache *fwnet_packet_task_cache;
 
+static void fwnet_free_ptask(struct fwnet_packet_task *ptask)
+{
+	dev_kfree_skb_any(ptask->skb);
+	kmem_cache_free(fwnet_packet_task_cache, ptask);
+}
+
 static int fwnet_send_packet(struct fwnet_packet_task *ptask);
 
 static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
 {
-	struct fwnet_device *dev;
+	struct fwnet_device *dev = ptask->dev;
 	unsigned long flags;
-
-	dev = ptask->dev;
+	bool free;
 
 	spin_lock_irqsave(&dev->lock, flags);
-	list_del(&ptask->pt_link);
-	spin_unlock_irqrestore(&dev->lock, flags);
 
-	ptask->outstanding_pkts--; /* FIXME access inside lock */
+	ptask->outstanding_pkts--;
+
+	/* Check whether we or the networking TX soft-IRQ is last user. */
+	free = (ptask->outstanding_pkts == 0 && !list_empty(&ptask->pt_link));
+
+	if (ptask->outstanding_pkts == 0)
+		list_del(&ptask->pt_link);
+
+	spin_unlock_irqrestore(&dev->lock, flags);
 
 	if (ptask->outstanding_pkts > 0) {
 		u16 dg_size;
@@ -951,10 +962,10 @@
 			ptask->max_payload = skb->len + RFC2374_FRAG_HDR_SIZE;
 		}
 		fwnet_send_packet(ptask);
-	} else {
-		dev_kfree_skb_any(ptask->skb);
-		kmem_cache_free(fwnet_packet_task_cache, ptask);
 	}
+
+	if (free)
+		fwnet_free_ptask(ptask);
 }
 
 static void fwnet_write_complete(struct fw_card *card, int rcode,
@@ -977,6 +988,7 @@
 	unsigned tx_len;
 	struct rfc2734_header *bufhdr;
 	unsigned long flags;
+	bool free;
 
 	dev = ptask->dev;
 	tx_len = ptask->max_payload;
@@ -1022,12 +1034,16 @@
 				generation, SCODE_100, 0ULL, ptask->skb->data,
 				tx_len + 8, fwnet_write_complete, ptask);
 
-		/* FIXME race? */
 		spin_lock_irqsave(&dev->lock, flags);
-		list_add_tail(&ptask->pt_link, &dev->broadcasted_list);
+
+		/* If the AT tasklet already ran, we may be last user. */
+		free = (ptask->outstanding_pkts == 0 && list_empty(&ptask->pt_link));
+		if (!free)
+			list_add_tail(&ptask->pt_link, &dev->broadcasted_list);
+
 		spin_unlock_irqrestore(&dev->lock, flags);
 
-		return 0;
+		goto out;
 	}
 
 	fw_send_request(dev->card, &ptask->transaction,
@@ -1035,12 +1051,19 @@
 			ptask->generation, ptask->speed, ptask->fifo_addr,
 			ptask->skb->data, tx_len, fwnet_write_complete, ptask);
 
-	/* FIXME race? */
 	spin_lock_irqsave(&dev->lock, flags);
-	list_add_tail(&ptask->pt_link, &dev->sent_list);
+
+	/* If the AT tasklet already ran, we may be last user. */
+	free = (ptask->outstanding_pkts == 0 && list_empty(&ptask->pt_link));
+	if (!free)
+		list_add_tail(&ptask->pt_link, &dev->sent_list);
+
 	spin_unlock_irqrestore(&dev->lock, flags);
 
 	dev->netdev->trans_start = jiffies;
+ out:
+	if (free)
+		fwnet_free_ptask(ptask);
 
 	return 0;
 }
@@ -1298,6 +1321,8 @@
 	spin_unlock_irqrestore(&dev->lock, flags);
 
 	ptask->max_payload = max_payload;
+	INIT_LIST_HEAD(&ptask->pt_link);
+
 	fwnet_send_packet(ptask);
 
 	return NETDEV_TX_OK;
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 2345d41..43ebf33 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -2101,11 +2101,6 @@
 	u32 payload_index, payload_end_index, next_page_index;
 	int page, end_page, i, length, offset;
 
-	/*
-	 * FIXME: Cycle lost behavior should be configurable: lose
-	 * packet, retransmit or terminate..
-	 */
-
 	p = packet;
 	payload_index = payload;
 
@@ -2135,6 +2130,14 @@
 	if (!p->skip) {
 		d[0].control   = cpu_to_le16(DESCRIPTOR_KEY_IMMEDIATE);
 		d[0].req_count = cpu_to_le16(8);
+		/*
+		 * Link the skip address to this descriptor itself.  This causes
+		 * a context to skip a cycle whenever lost cycles or FIFO
+		 * overruns occur, without dropping the data.  The application
+		 * should then decide whether this is an error condition or not.
+		 * FIXME:  Make the context's cycle-lost behaviour configurable?
+		 */
+		d[0].branch_address = cpu_to_le32(d_bus | z);
 
 		header = (__le32 *) &d[1];
 		header[0] = cpu_to_le32(IT_HEADER_SY(p->sy) |
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index f665b05..ab6c973 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -598,6 +598,50 @@
 	return mode;
 }
 
+/*
+ * EDID is delightfully ambiguous about how interlaced modes are to be
+ * encoded.  Our internal representation is of frame height, but some
+ * HDTV detailed timings are encoded as field height.
+ *
+ * The format list here is from CEA, in frame size.  Technically we
+ * should be checking refresh rate too.  Whatever.
+ */
+static void
+drm_mode_do_interlace_quirk(struct drm_display_mode *mode,
+			    struct detailed_pixel_timing *pt)
+{
+	int i;
+	static const struct {
+		int w, h;
+	} cea_interlaced[] = {
+		{ 1920, 1080 },
+		{  720,  480 },
+		{ 1440,  480 },
+		{ 2880,  480 },
+		{  720,  576 },
+		{ 1440,  576 },
+		{ 2880,  576 },
+	};
+	static const int n_sizes =
+		sizeof(cea_interlaced)/sizeof(cea_interlaced[0]);
+
+	if (!(pt->misc & DRM_EDID_PT_INTERLACED))
+		return;
+
+	for (i = 0; i < n_sizes; i++) {
+		if ((mode->hdisplay == cea_interlaced[i].w) &&
+		    (mode->vdisplay == cea_interlaced[i].h / 2)) {
+			mode->vdisplay *= 2;
+			mode->vsync_start *= 2;
+			mode->vsync_end *= 2;
+			mode->vtotal *= 2;
+			mode->vtotal |= 1;
+		}
+	}
+
+	mode->flags |= DRM_MODE_FLAG_INTERLACE;
+}
+
 /**
  * drm_mode_detailed - create a new mode from an EDID detailed timing section
  * @dev: DRM device (needed to create new mode)
@@ -680,8 +724,7 @@
 
 	drm_mode_set_name(mode);
 
-	if (pt->misc & DRM_EDID_PT_INTERLACED)
-		mode->flags |= DRM_MODE_FLAG_INTERLACE;
+	drm_mode_do_interlace_quirk(mode, pt);
 
 	if (quirks & EDID_QUIRK_DETAILED_SYNC_PP) {
 		pt->misc |= DRM_EDID_PT_HSYNC_POSITIVE | DRM_EDID_PT_VSYNC_POSITIVE;
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index cdec329..2ac074c8 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -405,7 +405,8 @@
 				wasted += alignment - tmp;
 		}
 
-		if (entry->size >= size + wasted) {
+		if (entry->size >= size + wasted &&
+		    (entry->start + wasted + size) <= end) {
 			if (!best_match)
 				return entry;
 			if (entry->size < best_size) {
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 79beffc..cf4cb3e 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -176,6 +176,8 @@
 
 static int i915_drm_freeze(struct drm_device *dev)
 {
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
 	pci_save_state(dev->pdev);
 
 	/* If KMS is active, we do the leavevt stuff here */
@@ -191,17 +193,12 @@
 
 	i915_save_state(dev);
 
-	return 0;
-}
-
-static void i915_drm_suspend(struct drm_device *dev)
-{
-	struct drm_i915_private *dev_priv = dev->dev_private;
-
 	intel_opregion_free(dev, 1);
 
 	/* Modeset on resume, not lid events */
 	dev_priv->modeset_on_lid = 0;
+
+	return 0;
 }
 
 static int i915_suspend(struct drm_device *dev, pm_message_t state)
@@ -221,8 +218,6 @@
 	if (error)
 		return error;
 
-	i915_drm_suspend(dev);
-
 	if (state.event == PM_EVENT_SUSPEND) {
 		/* Shut down the device */
 		pci_disable_device(dev->pdev);
@@ -237,6 +232,10 @@
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	int error = 0;
 
+	i915_restore_state(dev);
+
+	intel_opregion_init(dev, 1);
+
 	/* KMS EnterVT equivalent */
 	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
 		mutex_lock(&dev->struct_mutex);
@@ -263,10 +262,6 @@
 
 	pci_set_master(dev->pdev);
 
-	i915_restore_state(dev);
-
-	intel_opregion_init(dev, 1);
-
 	return i915_drm_thaw(dev);
 }
 
@@ -423,8 +418,6 @@
 	if (error)
 		return error;
 
-	i915_drm_suspend(drm_dev);
-
 	pci_disable_device(pdev);
 	pci_set_power_state(pdev, PCI_D3hot);
 
@@ -464,13 +457,8 @@
 {
 	struct pci_dev *pdev = to_pci_dev(dev);
 	struct drm_device *drm_dev = pci_get_drvdata(pdev);
-	int error;
 
-	error = i915_drm_freeze(drm_dev);
-	if (!error)
-		i915_drm_suspend(drm_dev);
-
-	return error;
+	return i915_drm_freeze(drm_dev);
 }
 
 const struct dev_pm_ops i915_pm_ops = {
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index b1d0acb..c2e8a45 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -636,6 +636,13 @@
 			DMI_MATCH(DMI_PRODUCT_NAME, "PC-81005"),
 		},
 	},
+	{
+		.ident = "Clevo M5x0N",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
+			DMI_MATCH(DMI_BOARD_NAME, "M5x0N"),
+		},
+	},
 	{ }
 };
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 2cd0fad..0e9cd1d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -5861,13 +5861,12 @@
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nvbios *bios = &dev_priv->VBIOS;
 	struct init_exec iexec = { true, false };
-	unsigned long flags;
 
-	spin_lock_irqsave(&bios->lock, flags);
+	mutex_lock(&bios->lock);
 	bios->display.output = dcbent;
 	parse_init_table(bios, table, &iexec);
 	bios->display.output = NULL;
-	spin_unlock_irqrestore(&bios->lock, flags);
+	mutex_unlock(&bios->lock);
 }
 
 static bool NVInitVBIOS(struct drm_device *dev)
@@ -5876,7 +5875,7 @@
 	struct nvbios *bios = &dev_priv->VBIOS;
 
 	memset(bios, 0, sizeof(struct nvbios));
-	spin_lock_init(&bios->lock);
+	mutex_init(&bios->lock);
 	bios->dev = dev;
 
 	if (!NVShadowVBIOS(dev, bios->data))
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
index 68446fd..fd94bd6 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -205,7 +205,7 @@
 	struct drm_device *dev;
 	struct nouveau_bios_info pub;
 
-	spinlock_t lock;
+	struct mutex lock;
 
 	uint8_t data[NV_PROM_SIZE];
 	unsigned int length;
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 5445cef..1c15ef3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -583,6 +583,7 @@
 	uint64_t vm_end;
 	struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
 	int vm_vram_pt_nr;
+	uint64_t vram_sys_base;
 
 	/* the mtrr covering the FB */
 	int fb_mtrr;
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 8f3a12f..2dc09db 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -285,53 +285,50 @@
 			uint32_t flags, uint64_t phys)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_gpuobj **pgt;
-	unsigned psz, pfl, pages;
+	struct nouveau_gpuobj *pgt;
+	unsigned block;
+	int i;
 
-	if (virt >= dev_priv->vm_gart_base &&
-	    (virt + size) < (dev_priv->vm_gart_base + dev_priv->vm_gart_size)) {
-		psz = 12;
-		pgt = &dev_priv->gart_info.sg_ctxdma;
-		pfl = 0x21;
-		virt -= dev_priv->vm_gart_base;
-	} else
-	if (virt >= dev_priv->vm_vram_base &&
-	    (virt + size) < (dev_priv->vm_vram_base + dev_priv->vm_vram_size)) {
-		psz = 16;
-		pgt = dev_priv->vm_vram_pt;
-		pfl = 0x01;
-		virt -= dev_priv->vm_vram_base;
-	} else {
-		NV_ERROR(dev, "Invalid address: 0x%16llx-0x%16llx\n",
-			 virt, virt + size - 1);
-		return -EINVAL;
+	virt = ((virt - dev_priv->vm_vram_base) >> 16) << 1;
+	size = (size >> 16) << 1;
+
+	phys |= ((uint64_t)flags << 32);
+	phys |= 1;
+	if (dev_priv->vram_sys_base) {
+		phys += dev_priv->vram_sys_base;
+		phys |= 0x30;
 	}
 
-	pages = size >> psz;
-
 	dev_priv->engine.instmem.prepare_access(dev, true);
-	if (flags & 0x80000000) {
-		while (pages--) {
-			struct nouveau_gpuobj *pt = pgt[virt >> 29];
-			unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
+	while (size) {
+		unsigned offset_h = upper_32_bits(phys);
+		unsigned offset_l = lower_32_bits(phys);
+		unsigned pte, end;
 
-			nv_wo32(dev, pt, pte++, 0x00000000);
-			nv_wo32(dev, pt, pte++, 0x00000000);
-
-			virt += (1 << psz);
+		for (i = 7; i >= 0; i--) {
+			block = 1 << (i + 1);
+			if (size >= block && !(virt & (block - 1)))
+				break;
 		}
-	} else {
-		while (pages--) {
-			struct nouveau_gpuobj *pt = pgt[virt >> 29];
-			unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
-			unsigned offset_h = upper_32_bits(phys) & 0xff;
-			unsigned offset_l = lower_32_bits(phys);
+		offset_l |= (i << 7);
 
-			nv_wo32(dev, pt, pte++, offset_l | pfl);
-			nv_wo32(dev, pt, pte++, offset_h | flags);
+		phys += block << 15;
+		size -= block;
 
-			phys += (1 << psz);
-			virt += (1 << psz);
+		while (block) {
+			pgt = dev_priv->vm_vram_pt[virt >> 14];
+			pte = virt & 0x3ffe;
+
+			end = pte + block;
+			if (end > 16384)
+				end = 16384;
+			block -= (end - pte);
+			virt  += (end - pte);
+
+			while (pte < end) {
+				nv_wo32(dev, pgt, pte++, offset_l);
+				nv_wo32(dev, pgt, pte++, offset_h);
+			}
 		}
 	}
 	dev_priv->engine.instmem.finish_access(dev);
@@ -356,7 +353,41 @@
 void
 nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
 {
-	nv50_mem_vm_bind_linear(dev, virt, size, 0x80000000, 0);
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_gpuobj *pgt;
+	unsigned pages, pte, end;
+
+	virt -= dev_priv->vm_vram_base;
+	pages = (size >> 16) << 1;
+
+	dev_priv->engine.instmem.prepare_access(dev, true);
+	while (pages) {
+		pgt = dev_priv->vm_vram_pt[virt >> 29];
+		pte = (virt & 0x1ffe0000ULL) >> 15;
+
+		end = pte + pages;
+		if (end > 16384)
+			end = 16384;
+		pages -= (end - pte);
+		virt  += (end - pte) << 15;
+
+		while (pte < end)
+			nv_wo32(dev, pgt, pte++, 0);
+	}
+	dev_priv->engine.instmem.finish_access(dev);
+
+	nv_wr32(dev, 0x100c80, 0x00050001);
+	if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+		NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+		NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+		return;
+	}
+
+	nv_wr32(dev, 0x100c80, 0x00000001);
+	if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+		NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+		NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+	}
 }
 
 /*
diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c
index d0e038d..1d73b15 100644
--- a/drivers/gpu/drm/nouveau/nv04_dac.c
+++ b/drivers/gpu/drm/nouveau/nv04_dac.c
@@ -119,7 +119,7 @@
 						 struct drm_connector *connector)
 {
 	struct drm_device *dev = encoder->dev;
-	uint8_t saved_seq1, saved_pi, saved_rpc1;
+	uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode;
 	uint8_t saved_palette0[3], saved_palette_mask;
 	uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
 	int i;
@@ -135,6 +135,9 @@
 		/* only implemented for head A for now */
 		NVSetOwner(dev, 0);
 
+	saved_cr_mode = NVReadVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX);
+	NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode | 0x80);
+
 	saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
 	NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
 
@@ -203,6 +206,7 @@
 	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
 	NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
 	NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
+	NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode);
 
 	if (blue == 0x18) {
 		NV_INFO(dev, "Load detected on head A\n");
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c
index 58b917c..21ac6e4 100644
--- a/drivers/gpu/drm/nouveau/nv17_tv.c
+++ b/drivers/gpu/drm/nouveau/nv17_tv.c
@@ -579,6 +579,8 @@
 				nouveau_encoder(encoder)->restore.output);
 
 	nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state);
+
+	nouveau_encoder(encoder)->last_dpms = NV_DPMS_CLEARED;
 }
 
 static int nv17_tv_create_resources(struct drm_encoder *encoder,
diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
index 94400f7..f0dc4e3 100644
--- a/drivers/gpu/drm/nouveau/nv50_instmem.c
+++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
@@ -76,6 +76,11 @@
 	for (i = 0x1700; i <= 0x1710; i += 4)
 		priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i);
 
+	if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac)
+		dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10) << 12;
+	else
+		dev_priv->vram_sys_base = 0;
+
 	/* Reserve the last MiB of VRAM, we should probably try to avoid
 	 * setting up the below tables over the top of the VBIOS image at
 	 * some point.
@@ -172,16 +177,28 @@
 	 * We map the entire fake channel into the start of the PRAMIN BAR
 	 */
 	ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pt_size, 0x1000,
-							0, &priv->pramin_pt);
+				     0, &priv->pramin_pt);
 	if (ret)
 		return ret;
 
-	for (i = 0, v = c_offset; i < pt_size; i += 8, v += 0x1000) {
-		if (v < (c_offset + c_size))
-			BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v | 1);
-		else
-			BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000009);
+	v = c_offset | 1;
+	if (dev_priv->vram_sys_base) {
+		v += dev_priv->vram_sys_base;
+		v |= 0x30;
+	}
+
+	i = 0;
+	while (v < dev_priv->vram_sys_base + c_offset + c_size) {
+		BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v);
 		BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
+		v += 0x1000;
+		i += 8;
+	}
+
+	while (i < pt_size) {
+		BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000000);
+		BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
+		i += 8;
 	}
 
 	BAR0_WI32(chan->vm_pd, 0x00, priv->pramin_pt->instance | 0x63);
@@ -416,7 +433,9 @@
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
-	uint32_t pte, pte_end, vram;
+	struct nouveau_gpuobj *pramin_pt = priv->pramin_pt->gpuobj;
+	uint32_t pte, pte_end;
+	uint64_t vram;
 
 	if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound)
 		return -EINVAL;
@@ -424,20 +443,24 @@
 	NV_DEBUG(dev, "st=0x%0llx sz=0x%0llx\n",
 		 gpuobj->im_pramin->start, gpuobj->im_pramin->size);
 
-	pte     = (gpuobj->im_pramin->start >> 12) << 3;
-	pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
+	pte     = (gpuobj->im_pramin->start >> 12) << 1;
+	pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte;
 	vram    = gpuobj->im_backing_start;
 
 	NV_DEBUG(dev, "pramin=0x%llx, pte=%d, pte_end=%d\n",
 		 gpuobj->im_pramin->start, pte, pte_end);
 	NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start);
 
+	vram |= 1;
+	if (dev_priv->vram_sys_base) {
+		vram += dev_priv->vram_sys_base;
+		vram |= 0x30;
+	}
+
 	dev_priv->engine.instmem.prepare_access(dev, true);
 	while (pte < pte_end) {
-		nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, vram | 1);
-		nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
-
-		pte += 8;
+		nv_wo32(dev, pramin_pt, pte++, lower_32_bits(vram));
+		nv_wo32(dev, pramin_pt, pte++, upper_32_bits(vram));
 		vram += NV50_INSTMEM_PAGE_SIZE;
 	}
 	dev_priv->engine.instmem.finish_access(dev);
@@ -470,14 +493,13 @@
 	if (gpuobj->im_bound == 0)
 		return -EINVAL;
 
-	pte     = (gpuobj->im_pramin->start >> 12) << 3;
-	pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
+	pte     = (gpuobj->im_pramin->start >> 12) << 1;
+	pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte;
 
 	dev_priv->engine.instmem.prepare_access(dev, true);
 	while (pte < pte_end) {
-		nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, 0x00000009);
-		nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
-		pte += 8;
+		nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000);
+		nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000);
 	}
 	dev_priv->engine.instmem.finish_access(dev);
 
diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c
index e3b4456..7f152f6 100644
--- a/drivers/gpu/drm/radeon/atom.c
+++ b/drivers/gpu/drm/radeon/atom.c
@@ -24,6 +24,7 @@
 
 #include <linux/module.h>
 #include <linux/sched.h>
+#include <asm/unaligned.h>
 
 #define ATOM_DEBUG
 
@@ -212,7 +213,9 @@
 	case ATOM_ARG_PS:
 		idx = U8(*ptr);
 		(*ptr)++;
-		val = le32_to_cpu(ctx->ps[idx]);
+		/* get_unaligned_le32 avoids unaligned accesses from atombios
+		 * tables, noticed on a DEC Alpha. */
+		val = get_unaligned_le32((u32 *)&ctx->ps[idx]);
 		if (print)
 			DEBUG("PS[0x%02X,0x%04X]", idx, val);
 		break;
@@ -640,7 +643,7 @@
 	uint8_t count = U8((*ptr)++);
 	SDEBUG("   count: %d\n", count);
 	if (arg == ATOM_UNIT_MICROSEC)
-		schedule_timeout_uninterruptible(usecs_to_jiffies(count));
+		udelay(count);
 	else
 		schedule_timeout_uninterruptible(msecs_to_jiffies(count));
 }
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index b32eeea..99915a6 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -350,7 +350,7 @@
 	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
 
 	if (args.ucReplyStatus && !args.ucDataOutLen) {
-		if (args.ucReplyStatus == 0x20 && retry_count < 10)
+		if (args.ucReplyStatus == 0x20 && retry_count++ < 10)
 			goto retry;
 		DRM_DEBUG("failed to get auxch %02x%02x %02x %02x 0x%02x %02x after %d retries\n",
 			  req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3],
diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c
index af1c3ca..446b765 100644
--- a/drivers/gpu/drm/radeon/r600_blit_kms.c
+++ b/drivers/gpu/drm/radeon/r600_blit_kms.c
@@ -543,9 +543,6 @@
 void r600_vb_ib_put(struct radeon_device *rdev)
 {
 	radeon_fence_emit(rdev, rdev->r600_blit.vb_ib->fence);
-	mutex_lock(&rdev->ib_pool.mutex);
-	list_add_tail(&rdev->r600_blit.vb_ib->list, &rdev->ib_pool.scheduled_ibs);
-	mutex_unlock(&rdev->ib_pool.mutex);
 	radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
 }
 
diff --git a/drivers/gpu/drm/radeon/r600_cp.c b/drivers/gpu/drm/radeon/r600_cp.c
index 6d5a711..75bcf35 100644
--- a/drivers/gpu/drm/radeon/r600_cp.c
+++ b/drivers/gpu/drm/radeon/r600_cp.c
@@ -1428,9 +1428,12 @@
 
 	gb_tiling_config |= R600_BANK_SWAPS(1);
 
-	backend_map = r700_get_tile_pipe_to_backend_map(dev_priv->r600_max_tile_pipes,
-							dev_priv->r600_max_backends,
-							(0xff << dev_priv->r600_max_backends) & 0xff);
+	if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV740)
+		backend_map = 0x28;
+	else
+		backend_map = r700_get_tile_pipe_to_backend_map(dev_priv->r600_max_tile_pipes,
+								dev_priv->r600_max_backends,
+								(0xff << dev_priv->r600_max_backends) & 0xff);
 	gb_tiling_config |= R600_BACKEND_MAP(backend_map);
 
 	cc_gc_shader_pipe_config =
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index f57480b..c0356bb 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -96,6 +96,7 @@
  * symbol;
  */
 #define RADEON_MAX_USEC_TIMEOUT		100000	/* 100 ms */
+/* RADEON_IB_POOL_SIZE must be a power of 2 */
 #define RADEON_IB_POOL_SIZE		16
 #define RADEON_DEBUGFS_MAX_NUM_FILES	32
 #define RADEONFB_CONN_LIMIT		4
@@ -363,11 +364,12 @@
  */
 struct radeon_ib {
 	struct list_head	list;
-	unsigned long		idx;
+	unsigned		idx;
 	uint64_t		gpu_addr;
 	struct radeon_fence	*fence;
-	uint32_t	*ptr;
+	uint32_t		*ptr;
 	uint32_t		length_dw;
+	bool			free;
 };
 
 /*
@@ -377,10 +379,9 @@
 struct radeon_ib_pool {
 	struct mutex		mutex;
 	struct radeon_bo	*robj;
-	struct list_head	scheduled_ibs;
 	struct radeon_ib	ibs[RADEON_IB_POOL_SIZE];
 	bool			ready;
-	DECLARE_BITMAP(alloc_bm, RADEON_IB_POOL_SIZE);
+	unsigned		head_id;
 };
 
 struct radeon_cp {
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 2dcda61..4d88315 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -206,6 +206,15 @@
 			*connector_type = DRM_MODE_CONNECTOR_DVID;
 	}
 
+	/* Asrock RS600 board lists the DVI port as HDMI */
+	if ((dev->pdev->device == 0x7941) &&
+	    (dev->pdev->subsystem_vendor == 0x1849) &&
+	    (dev->pdev->subsystem_device == 0x7941)) {
+		if ((*connector_type == DRM_MODE_CONNECTOR_HDMIA) &&
+		    (supported_device == ATOM_DEVICE_DFP3_SUPPORT))
+			*connector_type = DRM_MODE_CONNECTOR_DVID;
+	}
+
 	/* a-bit f-i90hd - ciaranm on #radeonhd - this board has no DVI */
 	if ((dev->pdev->device == 0x7941) &&
 	    (dev->pdev->subsystem_vendor == 0x147b) &&
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 2381885..65f8194 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -780,7 +780,7 @@
 			 * connected and the DVI port disconnected.  If the edid doesn't
 			 * say HDMI, vice versa.
 			 */
-			if (radeon_connector->shared_ddc && connector_status_connected) {
+			if (radeon_connector->shared_ddc && (ret == connector_status_connected)) {
 				struct drm_device *dev = connector->dev;
 				struct drm_connector *list_connector;
 				struct radeon_connector *list_radeon_connector;
@@ -1060,8 +1060,7 @@
 			return;
 		}
 		if (radeon_connector->ddc_bus && i2c_bus->valid) {
-			if (memcmp(&radeon_connector->ddc_bus->rec, i2c_bus,
-				    sizeof(struct radeon_i2c_bus_rec)) == 0) {
+			if (radeon_connector->ddc_bus->rec.i2c_id == i2c_bus->i2c_id) {
 				radeon_connector->shared_ddc = true;
 				shared_ddc = true;
 			}
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index 1190148..e9d0850 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -86,7 +86,7 @@
 						&p->validated);
 		}
 	}
-	return radeon_bo_list_validate(&p->validated, p->ib->fence);
+	return radeon_bo_list_validate(&p->validated);
 }
 
 int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
@@ -189,12 +189,10 @@
 {
 	unsigned i;
 
-	if (error && parser->ib) {
-		radeon_bo_list_unvalidate(&parser->validated,
-						parser->ib->fence);
-	} else {
-		radeon_bo_list_unreserve(&parser->validated);
+	if (!error && parser->ib) {
+		radeon_bo_list_fence(&parser->validated, parser->ib->fence);
 	}
+	radeon_bo_list_unreserve(&parser->validated);
 	for (i = 0; i < parser->nrelocs; i++) {
 		if (parser->relocs[i].gobj) {
 			mutex_lock(&parser->rdev->ddev->struct_mutex);
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
index e137852..c57ad60 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.h
+++ b/drivers/gpu/drm/radeon/radeon_drv.h
@@ -106,9 +106,10 @@
  * 1.29- R500 3D cmd buffer support
  * 1.30- Add support for occlusion queries
  * 1.31- Add support for num Z pipes from GET_PARAM
+ * 1.32- fixes for rv740 setup
  */
 #define DRIVER_MAJOR		1
-#define DRIVER_MINOR		31
+#define DRIVER_MINOR		32
 #define DRIVER_PATCHLEVEL	0
 
 enum radeon_cp_microcode_version {
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index d72a71b..f1da370 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -306,11 +306,10 @@
 	}
 }
 
-int radeon_bo_list_validate(struct list_head *head, void *fence)
+int radeon_bo_list_validate(struct list_head *head)
 {
 	struct radeon_bo_list *lobj;
 	struct radeon_bo *bo;
-	struct radeon_fence *old_fence = NULL;
 	int r;
 
 	r = radeon_bo_list_reserve(head);
@@ -334,32 +333,27 @@
 		}
 		lobj->gpu_offset = radeon_bo_gpu_offset(bo);
 		lobj->tiling_flags = bo->tiling_flags;
-		if (fence) {
-			old_fence = (struct radeon_fence *)bo->tbo.sync_obj;
-			bo->tbo.sync_obj = radeon_fence_ref(fence);
-			bo->tbo.sync_obj_arg = NULL;
-		}
-		if (old_fence) {
-			radeon_fence_unref(&old_fence);
-		}
 	}
 	return 0;
 }
 
-void radeon_bo_list_unvalidate(struct list_head *head, void *fence)
+void radeon_bo_list_fence(struct list_head *head, void *fence)
 {
 	struct radeon_bo_list *lobj;
-	struct radeon_fence *old_fence;
+	struct radeon_bo *bo;
+	struct radeon_fence *old_fence = NULL;
 
-	if (fence)
-		list_for_each_entry(lobj, head, list) {
-			old_fence = to_radeon_fence(lobj->bo->tbo.sync_obj);
-			if (old_fence == fence) {
-				lobj->bo->tbo.sync_obj = NULL;
-				radeon_fence_unref(&old_fence);
-			}
+	list_for_each_entry(lobj, head, list) {
+		bo = lobj->bo;
+		spin_lock(&bo->tbo.lock);
+		old_fence = (struct radeon_fence *)bo->tbo.sync_obj;
+		bo->tbo.sync_obj = radeon_fence_ref(fence);
+		bo->tbo.sync_obj_arg = NULL;
+		spin_unlock(&bo->tbo.lock);
+		if (old_fence) {
+			radeon_fence_unref(&old_fence);
 		}
-	radeon_bo_list_unreserve(head);
+	}
 }
 
 int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index a02f180..7ab43de 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -156,8 +156,8 @@
 				struct list_head *head);
 extern int radeon_bo_list_reserve(struct list_head *head);
 extern void radeon_bo_list_unreserve(struct list_head *head);
-extern int radeon_bo_list_validate(struct list_head *head, void *fence);
-extern void radeon_bo_list_unvalidate(struct list_head *head, void *fence);
+extern int radeon_bo_list_validate(struct list_head *head);
+extern void radeon_bo_list_fence(struct list_head *head, void *fence);
 extern int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
 				struct vm_area_struct *vma);
 extern int radeon_bo_set_tiling_flags(struct radeon_bo *bo,
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index 4d12b2d..6579eb4 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -41,68 +41,55 @@
 {
 	struct radeon_fence *fence;
 	struct radeon_ib *nib;
-	unsigned long i;
-	int r = 0;
+	int r = 0, i, c;
 
 	*ib = NULL;
 	r = radeon_fence_create(rdev, &fence);
 	if (r) {
-		DRM_ERROR("failed to create fence for new IB\n");
+		dev_err(rdev->dev, "failed to create fence for new IB\n");
 		return r;
 	}
 	mutex_lock(&rdev->ib_pool.mutex);
-	i = find_first_zero_bit(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
-	if (i < RADEON_IB_POOL_SIZE) {
-		set_bit(i, rdev->ib_pool.alloc_bm);
-		rdev->ib_pool.ibs[i].length_dw = 0;
-		*ib = &rdev->ib_pool.ibs[i];
-		mutex_unlock(&rdev->ib_pool.mutex);
-		goto out;
+	for (i = rdev->ib_pool.head_id, c = 0, nib = NULL; c < RADEON_IB_POOL_SIZE; c++, i++) {
+		i &= (RADEON_IB_POOL_SIZE - 1);
+		if (rdev->ib_pool.ibs[i].free) {
+			nib = &rdev->ib_pool.ibs[i];
+			break;
+		}
 	}
-	if (list_empty(&rdev->ib_pool.scheduled_ibs)) {
-		/* we go do nothings here */
+	if (nib == NULL) {
+		/* This should never happen, it means we allocated all
+		 * IB and haven't scheduled one yet, return EBUSY to
+		 * userspace hoping that on ioctl recall we get better
+		 * luck
+		 */
+		dev_err(rdev->dev, "no free indirect buffer !\n");
 		mutex_unlock(&rdev->ib_pool.mutex);
-		DRM_ERROR("all IB allocated none scheduled.\n");
-		r = -EINVAL;
-		goto out;
+		radeon_fence_unref(&fence);
+		return -EBUSY;
 	}
-	/* get the first ib on the scheduled list */
-	nib = list_entry(rdev->ib_pool.scheduled_ibs.next,
-			 struct radeon_ib, list);
-	if (nib->fence == NULL) {
-		/* we go do nothings here */
+	rdev->ib_pool.head_id = (nib->idx + 1) & (RADEON_IB_POOL_SIZE - 1);
+	nib->free = false;
+	if (nib->fence) {
 		mutex_unlock(&rdev->ib_pool.mutex);
-		DRM_ERROR("IB %lu scheduled without a fence.\n", nib->idx);
-		r = -EINVAL;
-		goto out;
-	}
-	mutex_unlock(&rdev->ib_pool.mutex);
-
-	r = radeon_fence_wait(nib->fence, false);
-	if (r) {
-		DRM_ERROR("radeon: IB(%lu:0x%016lX:%u)\n", nib->idx,
-			  (unsigned long)nib->gpu_addr, nib->length_dw);
-		DRM_ERROR("radeon: GPU lockup detected, fail to get a IB\n");
-		goto out;
+		r = radeon_fence_wait(nib->fence, false);
+		if (r) {
+			dev_err(rdev->dev, "error waiting fence of IB(%u:0x%016lX:%u)\n",
+				nib->idx, (unsigned long)nib->gpu_addr, nib->length_dw);
+			mutex_lock(&rdev->ib_pool.mutex);
+			nib->free = true;
+			mutex_unlock(&rdev->ib_pool.mutex);
+			radeon_fence_unref(&fence);
+			return r;
+		}
+		mutex_lock(&rdev->ib_pool.mutex);
 	}
 	radeon_fence_unref(&nib->fence);
-
+	nib->fence = fence;
 	nib->length_dw = 0;
-
-	/* scheduled list is accessed here */
-	mutex_lock(&rdev->ib_pool.mutex);
-	list_del(&nib->list);
-	INIT_LIST_HEAD(&nib->list);
 	mutex_unlock(&rdev->ib_pool.mutex);
-
 	*ib = nib;
-out:
-	if (r) {
-		radeon_fence_unref(&fence);
-	} else {
-		(*ib)->fence = fence;
-	}
-	return r;
+	return 0;
 }
 
 void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib)
@@ -113,19 +100,10 @@
 	if (tmp == NULL) {
 		return;
 	}
-	mutex_lock(&rdev->ib_pool.mutex);
-	if (!list_empty(&tmp->list) && !radeon_fence_signaled(tmp->fence)) {
-		/* IB is scheduled & not signaled don't do anythings */
-		mutex_unlock(&rdev->ib_pool.mutex);
-		return;
-	}
-	list_del(&tmp->list);
-	INIT_LIST_HEAD(&tmp->list);
-	if (tmp->fence)
+	if (!tmp->fence->emited)
 		radeon_fence_unref(&tmp->fence);
-
-	tmp->length_dw = 0;
-	clear_bit(tmp->idx, rdev->ib_pool.alloc_bm);
+	mutex_lock(&rdev->ib_pool.mutex);
+	tmp->free = true;
 	mutex_unlock(&rdev->ib_pool.mutex);
 }
 
@@ -135,7 +113,7 @@
 
 	if (!ib->length_dw || !rdev->cp.ready) {
 		/* TODO: Nothings in the ib we should report. */
-		DRM_ERROR("radeon: couldn't schedule IB(%lu).\n", ib->idx);
+		DRM_ERROR("radeon: couldn't schedule IB(%u).\n", ib->idx);
 		return -EINVAL;
 	}
 
@@ -148,7 +126,8 @@
 	radeon_ring_ib_execute(rdev, ib);
 	radeon_fence_emit(rdev, ib->fence);
 	mutex_lock(&rdev->ib_pool.mutex);
-	list_add_tail(&ib->list, &rdev->ib_pool.scheduled_ibs);
+	/* once scheduled IB is considered free and protected by the fence */
+	ib->free = true;
 	mutex_unlock(&rdev->ib_pool.mutex);
 	radeon_ring_unlock_commit(rdev);
 	return 0;
@@ -164,7 +143,6 @@
 	if (rdev->ib_pool.robj)
 		return 0;
 	/* Allocate 1M object buffer */
-	INIT_LIST_HEAD(&rdev->ib_pool.scheduled_ibs);
 	r = radeon_bo_create(rdev, NULL,  RADEON_IB_POOL_SIZE*64*1024,
 				true, RADEON_GEM_DOMAIN_GTT,
 				&rdev->ib_pool.robj);
@@ -195,9 +173,9 @@
 		rdev->ib_pool.ibs[i].ptr = ptr + offset;
 		rdev->ib_pool.ibs[i].idx = i;
 		rdev->ib_pool.ibs[i].length_dw = 0;
-		INIT_LIST_HEAD(&rdev->ib_pool.ibs[i].list);
+		rdev->ib_pool.ibs[i].free = true;
 	}
-	bitmap_zero(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
+	rdev->ib_pool.head_id = 0;
 	rdev->ib_pool.ready = true;
 	DRM_INFO("radeon: ib pool ready.\n");
 	if (radeon_debugfs_ib_init(rdev)) {
@@ -214,7 +192,6 @@
 		return;
 	}
 	mutex_lock(&rdev->ib_pool.mutex);
-	bitmap_zero(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
 	if (rdev->ib_pool.robj) {
 		r = radeon_bo_reserve(rdev->ib_pool.robj, false);
 		if (likely(r == 0)) {
@@ -363,7 +340,7 @@
 	if (ib == NULL) {
 		return 0;
 	}
-	seq_printf(m, "IB %04lu\n", ib->idx);
+	seq_printf(m, "IB %04u\n", ib->idx);
 	seq_printf(m, "IB fence %p\n", ib->fence);
 	seq_printf(m, "IB size %05u dwords\n", ib->length_dw);
 	for (i = 0; i < ib->length_dw; i++) {
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index 5943d56..0302167 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -549,9 +549,12 @@
 
 	gb_tiling_config |= BANK_SWAPS(1);
 
-	backend_map = r700_get_tile_pipe_to_backend_map(rdev->config.rv770.max_tile_pipes,
-							rdev->config.rv770.max_backends,
-							(0xff << rdev->config.rv770.max_backends) & 0xff);
+	if (rdev->family == CHIP_RV740)
+		backend_map = 0x28;
+	else
+		backend_map = r700_get_tile_pipe_to_backend_map(rdev->config.rv770.max_tile_pipes,
+								rdev->config.rv770.max_backends,
+								(0xff << rdev->config.rv770.max_backends) & 0xff);
 	gb_tiling_config |= BACKEND_MAP(backend_map);
 
 	cc_gc_shader_pipe_config =
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 1a3e909..c7320ce 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -1020,6 +1020,12 @@
 			     struct ttm_mem_reg *mem)
 {
 	int i;
+	struct drm_mm_node *node = mem->mm_node;
+
+	if (node && placement->lpfn != 0 &&
+	    (node->start < placement->fpfn ||
+	     node->start + node->size > placement->lpfn))
+		return -1;
 
 	for (i = 0; i < placement->num_placement; i++) {
 		if ((placement->placement[i] & mem->placement &
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
index e2123af..3d47a2c 100644
--- a/drivers/gpu/drm/ttm/ttm_tt.c
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
@@ -196,14 +196,15 @@
 
 #ifdef CONFIG_X86
 static inline int ttm_tt_set_page_caching(struct page *p,
-					  enum ttm_caching_state c_state)
+					  enum ttm_caching_state c_old,
+					  enum ttm_caching_state c_new)
 {
 	int ret = 0;
 
 	if (PageHighMem(p))
 		return 0;
 
-	if (get_page_memtype(p) != -1) {
+	if (c_old != tt_cached) {
 		/* p isn't in the default caching state, set it to
 		 * writeback first to free its current memtype. */
 
@@ -212,16 +213,17 @@
 			return ret;
 	}
 
-	if (c_state == tt_wc)
+	if (c_new == tt_wc)
 		ret = set_memory_wc((unsigned long) page_address(p), 1);
-	else if (c_state == tt_uncached)
+	else if (c_new == tt_uncached)
 		ret = set_pages_uc(p, 1);
 
 	return ret;
 }
 #else /* CONFIG_X86 */
 static inline int ttm_tt_set_page_caching(struct page *p,
-					  enum ttm_caching_state c_state)
+					  enum ttm_caching_state c_old,
+					  enum ttm_caching_state c_new)
 {
 	return 0;
 }
@@ -254,7 +256,9 @@
 	for (i = 0; i < ttm->num_pages; ++i) {
 		cur_page = ttm->pages[i];
 		if (likely(cur_page != NULL)) {
-			ret = ttm_tt_set_page_caching(cur_page, c_state);
+			ret = ttm_tt_set_page_caching(cur_page,
+						      ttm->caching_state,
+						      c_state);
 			if (unlikely(ret != 0))
 				goto out_err;
 		}
@@ -268,7 +272,7 @@
 	for (j = 0; j < i; ++j) {
 		cur_page = ttm->pages[j];
 		if (likely(cur_page != NULL)) {
-			(void)ttm_tt_set_page_caching(cur_page,
+			(void)ttm_tt_set_page_caching(cur_page, c_state,
 						      ttm->caching_state);
 		}
 	}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index a6e8f68..0c9c081 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -348,22 +348,19 @@
 		 */
 
 		DRM_INFO("It appears like vesafb is loaded. "
-			 "Ignore above error if any. Entering stealth mode.\n");
+			 "Ignore above error if any.\n");
 		ret = pci_request_region(dev->pdev, 2, "vmwgfx stealth probe");
 		if (unlikely(ret != 0)) {
 			DRM_ERROR("Failed reserving the SVGA MMIO resource.\n");
 			goto out_no_device;
 		}
-		vmw_kms_init(dev_priv);
-		vmw_overlay_init(dev_priv);
-	} else {
-		ret = vmw_request_device(dev_priv);
-		if (unlikely(ret != 0))
-			goto out_no_device;
-		vmw_kms_init(dev_priv);
-		vmw_overlay_init(dev_priv);
-		vmw_fb_init(dev_priv);
 	}
+	ret = vmw_request_device(dev_priv);
+	if (unlikely(ret != 0))
+		goto out_no_device;
+	vmw_kms_init(dev_priv);
+	vmw_overlay_init(dev_priv);
+	vmw_fb_init(dev_priv);
 
 	dev_priv->pm_nb.notifier_call = vmwgfx_pm_notifier;
 	register_pm_notifier(&dev_priv->pm_nb);
@@ -406,17 +403,15 @@
 
 	unregister_pm_notifier(&dev_priv->pm_nb);
 
-	if (!dev_priv->stealth) {
-		vmw_fb_close(dev_priv);
-		vmw_kms_close(dev_priv);
-		vmw_overlay_close(dev_priv);
-		vmw_release_device(dev_priv);
-		pci_release_regions(dev->pdev);
-	} else {
-		vmw_kms_close(dev_priv);
-		vmw_overlay_close(dev_priv);
+	vmw_fb_close(dev_priv);
+	vmw_kms_close(dev_priv);
+	vmw_overlay_close(dev_priv);
+	vmw_release_device(dev_priv);
+	if (dev_priv->stealth)
 		pci_release_region(dev->pdev, 2);
-	}
+	else
+		pci_release_regions(dev->pdev);
+
 	if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
 		drm_irq_uninstall(dev_priv->dev);
 	if (dev->devname == vmw_devname)
@@ -585,11 +580,6 @@
 	int ret = 0;
 
 	DRM_INFO("Master set.\n");
-	if (dev_priv->stealth) {
-		ret = vmw_request_device(dev_priv);
-		if (unlikely(ret != 0))
-			return ret;
-	}
 
 	if (active) {
 		BUG_ON(active != &dev_priv->fbdev_master);
@@ -649,18 +639,11 @@
 
 	ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
 
-	if (dev_priv->stealth) {
-		ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
-		if (unlikely(ret != 0))
-			DRM_ERROR("Unable to clean VRAM on master drop.\n");
-		vmw_release_device(dev_priv);
-	}
 	dev_priv->active_master = &dev_priv->fbdev_master;
 	ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM);
 	ttm_vt_unlock(&dev_priv->fbdev_master.lock);
 
-	if (!dev_priv->stealth)
-		vmw_fb_on(dev_priv);
+	vmw_fb_on(dev_priv);
 }
 
 
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index d69caf9..0897359 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -182,25 +182,19 @@
 	return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.sid);
 }
 
-static int vmw_cmd_dma(struct vmw_private *dev_priv,
-		       struct vmw_sw_context *sw_context,
-		       SVGA3dCmdHeader *header)
+static int vmw_translate_guest_ptr(struct vmw_private *dev_priv,
+				   struct vmw_sw_context *sw_context,
+				   SVGAGuestPtr *ptr,
+				   struct vmw_dma_buffer **vmw_bo_p)
 {
-	uint32_t handle;
 	struct vmw_dma_buffer *vmw_bo = NULL;
 	struct ttm_buffer_object *bo;
-	struct vmw_surface *srf = NULL;
-	struct vmw_dma_cmd {
-		SVGA3dCmdHeader header;
-		SVGA3dCmdSurfaceDMA dma;
-	} *cmd;
+	uint32_t handle = ptr->gmrId;
 	struct vmw_relocation *reloc;
-	int ret;
 	uint32_t cur_validate_node;
 	struct ttm_validate_buffer *val_buf;
+	int ret;
 
-	cmd = container_of(header, struct vmw_dma_cmd, header);
-	handle = cmd->dma.guest.ptr.gmrId;
 	ret = vmw_user_dmabuf_lookup(sw_context->tfile, handle, &vmw_bo);
 	if (unlikely(ret != 0)) {
 		DRM_ERROR("Could not find or use GMR region.\n");
@@ -209,14 +203,14 @@
 	bo = &vmw_bo->base;
 
 	if (unlikely(sw_context->cur_reloc >= VMWGFX_MAX_RELOCATIONS)) {
-		DRM_ERROR("Max number of DMA commands per submission"
+		DRM_ERROR("Max number relocations per submission"
 			  " exceeded\n");
 		ret = -EINVAL;
 		goto out_no_reloc;
 	}
 
 	reloc = &sw_context->relocs[sw_context->cur_reloc++];
-	reloc->location = &cmd->dma.guest.ptr;
+	reloc->location = ptr;
 
 	cur_validate_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf);
 	if (unlikely(cur_validate_node >= VMWGFX_MAX_GMRS)) {
@@ -234,7 +228,89 @@
 		list_add_tail(&val_buf->head, &sw_context->validate_nodes);
 		++sw_context->cur_val_buf;
 	}
+	*vmw_bo_p = vmw_bo;
+	return 0;
 
+out_no_reloc:
+	vmw_dmabuf_unreference(&vmw_bo);
+	vmw_bo_p = NULL;
+	return ret;
+}
+
+static int vmw_cmd_end_query(struct vmw_private *dev_priv,
+			     struct vmw_sw_context *sw_context,
+			     SVGA3dCmdHeader *header)
+{
+	struct vmw_dma_buffer *vmw_bo;
+	struct vmw_query_cmd {
+		SVGA3dCmdHeader header;
+		SVGA3dCmdEndQuery q;
+	} *cmd;
+	int ret;
+
+	cmd = container_of(header, struct vmw_query_cmd, header);
+	ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
+	if (unlikely(ret != 0))
+		return ret;
+
+	ret = vmw_translate_guest_ptr(dev_priv, sw_context,
+				      &cmd->q.guestResult,
+				      &vmw_bo);
+	if (unlikely(ret != 0))
+		return ret;
+
+	vmw_dmabuf_unreference(&vmw_bo);
+	return 0;
+}
+
+static int vmw_cmd_wait_query(struct vmw_private *dev_priv,
+			      struct vmw_sw_context *sw_context,
+			      SVGA3dCmdHeader *header)
+{
+	struct vmw_dma_buffer *vmw_bo;
+	struct vmw_query_cmd {
+		SVGA3dCmdHeader header;
+		SVGA3dCmdWaitForQuery q;
+	} *cmd;
+	int ret;
+
+	cmd = container_of(header, struct vmw_query_cmd, header);
+	ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
+	if (unlikely(ret != 0))
+		return ret;
+
+	ret = vmw_translate_guest_ptr(dev_priv, sw_context,
+				      &cmd->q.guestResult,
+				      &vmw_bo);
+	if (unlikely(ret != 0))
+		return ret;
+
+	vmw_dmabuf_unreference(&vmw_bo);
+	return 0;
+}
+
+
+static int vmw_cmd_dma(struct vmw_private *dev_priv,
+		       struct vmw_sw_context *sw_context,
+		       SVGA3dCmdHeader *header)
+{
+	struct vmw_dma_buffer *vmw_bo = NULL;
+	struct ttm_buffer_object *bo;
+	struct vmw_surface *srf = NULL;
+	struct vmw_dma_cmd {
+		SVGA3dCmdHeader header;
+		SVGA3dCmdSurfaceDMA dma;
+	} *cmd;
+	int ret;
+
+	cmd = container_of(header, struct vmw_dma_cmd, header);
+	ret = vmw_translate_guest_ptr(dev_priv, sw_context,
+				      &cmd->dma.guest.ptr,
+				      &vmw_bo);
+	if (unlikely(ret != 0))
+		return ret;
+
+	bo = &vmw_bo->base;
 	ret = vmw_user_surface_lookup_handle(dev_priv, sw_context->tfile,
 					     cmd->dma.host.sid, &srf);
 	if (ret) {
@@ -379,8 +455,8 @@
 	VMW_CMD_DEF(SVGA_3D_CMD_DRAW_PRIMITIVES, &vmw_cmd_draw),
 	VMW_CMD_DEF(SVGA_3D_CMD_SETSCISSORRECT, &vmw_cmd_cid_check),
 	VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_QUERY, &vmw_cmd_cid_check),
-	VMW_CMD_DEF(SVGA_3D_CMD_END_QUERY, &vmw_cmd_cid_check),
-	VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_QUERY, &vmw_cmd_cid_check),
+	VMW_CMD_DEF(SVGA_3D_CMD_END_QUERY, &vmw_cmd_end_query),
+	VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_QUERY, &vmw_cmd_wait_query),
 	VMW_CMD_DEF(SVGA_3D_CMD_PRESENT_READBACK, &vmw_cmd_ok),
 	VMW_CMD_DEF(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN,
 		    &vmw_cmd_blt_surf_screen_check)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index 4f4f643..a933670 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -559,6 +559,9 @@
 	info->pixmap.scan_align = 1;
 #endif
 
+	info->aperture_base = vmw_priv->vram_start;
+	info->aperture_size = vmw_priv->vram_size;
+
 	/*
 	 * Dirty & Deferred IO
 	 */
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c
index 24b56dc..2f6cf69 100644
--- a/drivers/gpu/vga/vgaarb.c
+++ b/drivers/gpu/vga/vgaarb.c
@@ -961,7 +961,7 @@
 		remaining -= 7;
 		pr_devel("client 0x%p called 'target'\n", priv);
 		/* if target is default */
-		if (!strncmp(kbuf, "default", 7))
+		if (!strncmp(curr_pos, "default", 7))
 			pdev = pci_dev_get(vga_default_device());
 		else {
 			if (!vga_pci_str_to_vars(curr_pos, remaining,
diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c
index aa6713b..291d939 100644
--- a/drivers/input/input-polldev.c
+++ b/drivers/input/input-polldev.c
@@ -100,6 +100,12 @@
 	struct input_polled_dev *dev = input_get_drvdata(input);
 
 	cancel_delayed_work_sync(&dev->work);
+	/*
+	 * Clean up work struct to remove references to the workqueue.
+	 * It may be destroyed by the next call. This causes problems
+	 * at next device open-close in case of poll_interval == 0.
+	 */
+	INIT_DELAYED_WORK(&dev->work, dev->work.work.func);
 	input_polldev_stop_workqueue();
 
 	if (dev->close)
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index d84a36e..b54aee7 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -1161,9 +1161,17 @@
 	return 0;
 }
 
+static int i8042_pm_thaw(struct device *dev)
+{
+	i8042_interrupt(0, NULL);
+
+	return 0;
+}
+
 static const struct dev_pm_ops i8042_pm_ops = {
 	.suspend	= i8042_pm_reset,
 	.resume		= i8042_pm_restore,
+	.thaw		= i8042_pm_thaw,
 	.poweroff	= i8042_pm_reset,
 	.restore	= i8042_pm_restore,
 };
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index 09a5e73..5256123 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -618,8 +618,8 @@
 #ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
 static int general_touch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
 {
-	dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1] ;
-	dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3] ;
+	dev->x = (pkt[2] << 8) | pkt[1];
+	dev->y = (pkt[4] << 8) | pkt[3];
 	dev->press = pkt[5] & 0xff;
 	dev->touch = pkt[0] & 0x01;
 
@@ -809,9 +809,9 @@
 #ifdef CONFIG_TOUCHSCREEN_USB_GENERAL_TOUCH
 	[DEVTYPE_GENERAL_TOUCH] = {
 		.min_xc		= 0x0,
-		.max_xc		= 0x0500,
+		.max_xc		= 0x7fff,
 		.min_yc		= 0x0,
-		.max_yc		= 0x0500,
+		.max_yc		= 0x7fff,
 		.rept_size	= 7,
 		.read_data	= general_touch_read_data,
 	},
diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c
index 54abf9e..f1c8cae 100644
--- a/drivers/md/dm-log-userspace-transfer.c
+++ b/drivers/md/dm-log-userspace-transfer.c
@@ -172,11 +172,15 @@
 {
 	int r = 0;
 	size_t dummy = 0;
-	int overhead_size =
-		sizeof(struct dm_ulog_request *) + sizeof(struct cn_msg);
+	int overhead_size = sizeof(struct dm_ulog_request) + sizeof(struct cn_msg);
 	struct dm_ulog_request *tfr = prealloced_ulog_tfr;
 	struct receiving_pkg pkg;
 
+	/*
+	 * Given the space needed to hold the 'struct cn_msg' and
+	 * 'struct dm_ulog_request' - do we have enough payload
+	 * space remaining?
+	 */
 	if (data_size > (DM_ULOG_PREALLOCED_SIZE - overhead_size)) {
 		DMINFO("Size of tfr exceeds preallocated size");
 		return -EINVAL;
@@ -191,7 +195,7 @@
 	 */
 	mutex_lock(&dm_ulog_lock);
 
-	memset(tfr, 0, DM_ULOG_PREALLOCED_SIZE - overhead_size);
+	memset(tfr, 0, DM_ULOG_PREALLOCED_SIZE - sizeof(struct cn_msg));
 	memcpy(tfr->uuid, uuid, DM_UUID_LEN);
 	tfr->luid = luid;
 	tfr->seq = dm_ulog_seq++;
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index ad779bd..6c1046d 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -724,7 +724,7 @@
 	/*
 	 * Dispatch io.
 	 */
-	if (unlikely(ms->log_failure)) {
+	if (unlikely(ms->log_failure) && errors_handled(ms)) {
 		spin_lock_irq(&ms->lock);
 		bio_list_merge(&ms->failures, &sync);
 		spin_unlock_irq(&ms->lock);
diff --git a/drivers/md/dm-region-hash.c b/drivers/md/dm-region-hash.c
index 5f19ceb..168bd38 100644
--- a/drivers/md/dm-region-hash.c
+++ b/drivers/md/dm-region-hash.c
@@ -660,10 +660,9 @@
 	spin_lock_irq(&rh->region_lock);
 	if (success)
 		list_add(&reg->list, &reg->rh->recovered_regions);
-	else {
-		reg->state = DM_RH_NOSYNC;
+	else
 		list_add(&reg->list, &reg->rh->failed_recovered_regions);
-	}
+
 	spin_unlock_irq(&rh->region_lock);
 
 	rh->wakeup_workers(rh->context);
diff --git a/drivers/md/dm-snap-persistent.c b/drivers/md/dm-snap-persistent.c
index 7d08879..c097d8a 100644
--- a/drivers/md/dm-snap-persistent.c
+++ b/drivers/md/dm-snap-persistent.c
@@ -254,7 +254,7 @@
 	 * Issue the synchronous I/O from a different thread
 	 * to avoid generic_make_request recursion.
 	 */
-	INIT_WORK(&req.work, do_metadata);
+	INIT_WORK_ON_STACK(&req.work, do_metadata);
 	queue_work(ps->metadata_wq, &req.work);
 	flush_workqueue(ps->metadata_wq);
 
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index e0efc1a..bd58703 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -110,7 +110,7 @@
 	}
 
 	stripes = simple_strtoul(argv[0], &end, 10);
-	if (*end) {
+	if (!stripes || *end) {
 		ti->error = "Invalid stripe count";
 		return -EINVAL;
 	}
diff --git a/drivers/md/dm-sysfs.c b/drivers/md/dm-sysfs.c
index f53392d..f91b409 100644
--- a/drivers/md/dm-sysfs.c
+++ b/drivers/md/dm-sysfs.c
@@ -80,20 +80,12 @@
 };
 
 /*
- * The sysfs structure is embedded in md struct, nothing to do here
- */
-static void dm_sysfs_release(struct kobject *kobj)
-{
-}
-
-/*
  * dm kobject is embedded in mapped_device structure
  * no need to define release function here
  */
 static struct kobj_type dm_ktype = {
 	.sysfs_ops	= &dm_sysfs_ops,
 	.default_attrs	= dm_attrs,
-	.release	= dm_sysfs_release
 };
 
 /*
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 3167480..aa4e2aa 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1595,10 +1595,15 @@
 	return BLKPREP_OK;
 }
 
-static void map_request(struct dm_target *ti, struct request *clone,
-			struct mapped_device *md)
+/*
+ * Returns:
+ * 0  : the request has been processed (not requeued)
+ * !0 : the request has been requeued
+ */
+static int map_request(struct dm_target *ti, struct request *clone,
+		       struct mapped_device *md)
 {
-	int r;
+	int r, requeued = 0;
 	struct dm_rq_target_io *tio = clone->end_io_data;
 
 	/*
@@ -1625,6 +1630,7 @@
 	case DM_MAPIO_REQUEUE:
 		/* The target wants to requeue the I/O */
 		dm_requeue_unmapped_request(clone);
+		requeued = 1;
 		break;
 	default:
 		if (r > 0) {
@@ -1636,6 +1642,8 @@
 		dm_kill_unmapped_request(clone, r);
 		break;
 	}
+
+	return requeued;
 }
 
 /*
@@ -1677,12 +1685,17 @@
 		atomic_inc(&md->pending[rq_data_dir(clone)]);
 
 		spin_unlock(q->queue_lock);
-		map_request(ti, clone, md);
+		if (map_request(ti, clone, md))
+			goto requeued;
+
 		spin_lock_irq(q->queue_lock);
 	}
 
 	goto out;
 
+requeued:
+	spin_lock_irq(q->queue_lock);
+
 plug_and_out:
 	if (!elv_queue_empty(q))
 		/* Some requests still remain, retry later */
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 1b24989..465295b 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -112,11 +112,13 @@
 	select DVB_MT352 if !DVB_FE_CUSTOMISE
 	select DVB_ZL10353 if !DVB_FE_CUSTOMISE
 	select DVB_DIB7000P if !DVB_FE_CUSTOMISE
-	select DVB_LGS8GL5 if !DVB_FE_CUSTOMISE
 	select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE
+	select DVB_ATBM8830 if !DVB_FE_CUSTOMISE
+	select DVB_LGS8GXX if !DVB_FE_CUSTOMISE
 	select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
 	select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+	select MEDIA_TUNER_MAX2165 if !MEDIA_TUNER_CUSTOMISE
 	help
 	  Say Y here to support the Conexant USB2.0 hybrid reference design.
 	  Currently, only DVB and ATSC modes are supported, analog mode
diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c
index 3051b64..445fa10 100644
--- a/drivers/media/dvb/frontends/l64781.c
+++ b/drivers/media/dvb/frontends/l64781.c
@@ -192,8 +192,8 @@
 	spi_bias *= qam_tab[p->constellation];
 	spi_bias /= p->code_rate_HP + 1;
 	spi_bias /= (guard_tab[p->guard_interval] + 32);
-	spi_bias *= 1000ULL;
-	spi_bias /= 1000ULL + ppm/1000;
+	spi_bias *= 1000;
+	spi_bias /= 1000 + ppm/1000;
 	spi_bias *= p->code_rate_HP;
 
 	val0x04 = (p->transmission_mode << 2) | p->guard_interval;
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 3182a40..ae08b07 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -4461,6 +4461,7 @@
 		request_modules(btv);
 	}
 
+	init_bttv_i2c_ir(btv);
 	bttv_input_init(btv);
 
 	/* everything is fine */
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index 63aa31a..407fa61 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -388,7 +388,12 @@
 	if (0 == btv->i2c_rc && i2c_scan)
 		do_i2c_scan(btv->c.v4l2_dev.name, &btv->i2c_client);
 
-	/* Instantiate the IR receiver device, if present */
+	return btv->i2c_rc;
+}
+
+/* Instantiate the I2C IR receiver device, if present */
+void __devinit init_bttv_i2c_ir(struct bttv *btv)
+{
 	if (0 == btv->i2c_rc) {
 		struct i2c_board_info info;
 		/* The external IR receiver is at i2c address 0x34 (0x35 for
@@ -408,7 +413,6 @@
 		strlcpy(info.type, "ir_video", I2C_NAME_SIZE);
 		i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list);
 	}
-	return btv->i2c_rc;
 }
 
 int __devexit fini_bttv_i2c(struct bttv *btv)
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index a1d0e9c..6cccc2a 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -279,6 +279,7 @@
 extern unsigned int bttv_gpio;
 extern void bttv_gpio_tracking(struct bttv *btv, char *comment);
 extern int init_bttv_i2c(struct bttv *btv);
+extern void init_bttv_i2c_ir(struct bttv *btv);
 extern int fini_bttv_i2c(struct bttv *btv);
 
 #define bttv_printk if (bttv_verbose) printk
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
index fc4dd60..7438f8d 100644
--- a/drivers/media/video/mt9t112.c
+++ b/drivers/media/video/mt9t112.c
@@ -514,7 +514,7 @@
 	/* poll to verify out of standby. Must Poll this bit */
 	for (i = 0; i < 100; i++) {
 		mt9t112_reg_read(data, client, 0x0018);
-		if (0x4000 & data)
+		if (!(0x4000 & data))
 			break;
 
 		mdelay(10);
diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c
index 50b415e..f7f7e04 100644
--- a/drivers/media/video/pwc/pwc-ctrl.c
+++ b/drivers/media/video/pwc/pwc-ctrl.c
@@ -753,7 +753,7 @@
 		buf[0] = 0xff; /* fixed */
 
 	ret = send_control_msg(pdev,
-		SET_LUM_CTL, SHUTTER_MODE_FORMATTER, &buf, sizeof(buf));
+		SET_LUM_CTL, SHUTTER_MODE_FORMATTER, &buf, 1);
 
 	if (!mode && ret >= 0) {
 		if (value < 0)
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c
index 2a76065..19a930d 100644
--- a/drivers/mfd/twl-core.c
+++ b/drivers/mfd/twl-core.c
@@ -115,7 +115,8 @@
 #define twl_has_watchdog()        false
 #endif
 
-#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE)
+#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE) ||\
+	defined(CONFIG_SND_SOC_TWL6030) || defined(CONFIG_SND_SOC_TWL6030_MODULE)
 #define twl_has_codec()	true
 #else
 #define twl_has_codec()	false
@@ -711,8 +712,19 @@
 			return PTR_ERR(child);
 	}
 
-	if (twl_has_codec() && pdata->codec) {
-		child = add_child(1, "twl4030_codec",
+	if (twl_has_codec() && pdata->codec && twl_class_is_4030()) {
+		sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
+		child = add_child(sub_chip_id, "twl4030_codec",
+				pdata->codec, sizeof(*pdata->codec),
+				false, 0, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
+	/* Phoenix*/
+	if (twl_has_codec() && pdata->codec && twl_class_is_6030()) {
+		sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid;
+		child = add_child(sub_chip_id, "twl6030_codec",
 				pdata->codec, sizeof(*pdata->codec),
 				false, 0, 0);
 		if (IS_ERR(child))
diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c
index fee6eee..006cb2ef 100644
--- a/drivers/net/benet/be_cmds.c
+++ b/drivers/net/benet/be_cmds.c
@@ -296,6 +296,7 @@
 	req_hdr->opcode = opcode;
 	req_hdr->subsystem = subsystem;
 	req_hdr->request_length = cpu_to_le32(cmd_len - sizeof(*req_hdr));
+	req_hdr->version = 0;
 }
 
 static void be_cmd_page_addrs_prepare(struct phys_addr *pages, u32 max_pages,
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index d29bb53..7655436 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -4006,11 +4006,21 @@
 			}
 		}
 
-		if (!buffer_info->dma)
+		if (!buffer_info->dma) {
 			buffer_info->dma = pci_map_page(pdev,
 			                                buffer_info->page, 0,
 			                                buffer_info->length,
 			                                PCI_DMA_FROMDEVICE);
+			if (pci_dma_mapping_error(pdev, buffer_info->dma)) {
+				put_page(buffer_info->page);
+				dev_kfree_skb(skb);
+				buffer_info->page = NULL;
+				buffer_info->skb = NULL;
+				buffer_info->dma = 0;
+				adapter->alloc_rx_buff_failed++;
+				break; /* while !buffer_info->skb */
+			}
+		}
 
 		rx_desc = E1000_RX_DESC(*rx_ring, i);
 		rx_desc->buffer_addr = cpu_to_le64(buffer_info->dma);
@@ -4101,6 +4111,13 @@
 						  skb->data,
 						  buffer_info->length,
 						  PCI_DMA_FROMDEVICE);
+		if (pci_dma_mapping_error(pdev, buffer_info->dma)) {
+			dev_kfree_skb(skb);
+			buffer_info->skb = NULL;
+			buffer_info->dma = 0;
+			adapter->alloc_rx_buff_failed++;
+			break; /* while !buffer_info->skb */
+		}
 
 		/*
 		 * XXX if it was allocated cleanly it will never map to a
diff --git a/drivers/net/ixgbe/ixgbe_82598.c b/drivers/net/ixgbe/ixgbe_82598.c
index 3103f41..35a06b4758 100644
--- a/drivers/net/ixgbe/ixgbe_82598.c
+++ b/drivers/net/ixgbe/ixgbe_82598.c
@@ -357,12 +357,34 @@
 	u32 fctrl_reg;
 	u32 rmcs_reg;
 	u32 reg;
+	u32 link_speed = 0;
+	bool link_up;
 
 #ifdef CONFIG_DCB
 	if (hw->fc.requested_mode == ixgbe_fc_pfc)
 		goto out;
 
 #endif /* CONFIG_DCB */
+	/*
+	 * On 82598 having Rx FC on causes resets while doing 1G
+	 * so if it's on turn it off once we know link_speed. For
+	 * more details see 82598 Specification update.
+	 */
+	hw->mac.ops.check_link(hw, &link_speed, &link_up, false);
+	if (link_up && link_speed == IXGBE_LINK_SPEED_1GB_FULL) {
+		switch (hw->fc.requested_mode) {
+		case ixgbe_fc_full:
+			hw->fc.requested_mode = ixgbe_fc_tx_pause;
+			break;
+		case ixgbe_fc_rx_pause:
+			hw->fc.requested_mode = ixgbe_fc_none;
+			break;
+		default:
+			/* no change */
+			break;
+		}
+	}
+
 	/* Negotiate the fc mode to use */
 	ret_val = ixgbe_fc_autoneg(hw);
 	if (ret_val)
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
index 7b7c848..951b73c 100644
--- a/drivers/net/ixgbe/ixgbe_main.c
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -5763,6 +5763,10 @@
 	if (err)
 		goto err_sw_init;
 
+	/* Make it possible the adapter to be woken up via WOL */
+	if (adapter->hw.mac.type == ixgbe_mac_82599EB)
+		IXGBE_WRITE_REG(&adapter->hw, IXGBE_WUS, ~0);
+
 	/*
 	 * If there is a fan on this device and it has failed log the
 	 * failure.
diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c
index 103e8b0..46997e1 100644
--- a/drivers/net/sfc/efx.c
+++ b/drivers/net/sfc/efx.c
@@ -2284,6 +2284,7 @@
  fail2:
 	efx_fini_struct(efx);
  fail1:
+	WARN_ON(rc > 0);
 	EFX_LOG(efx, "initialisation failed. rc=%d\n", rc);
 	free_netdev(net_dev);
 	return rc;
diff --git a/drivers/net/sfc/falcon_boards.c b/drivers/net/sfc/falcon_boards.c
index bf0b96a..5712fdd 100644
--- a/drivers/net/sfc/falcon_boards.c
+++ b/drivers/net/sfc/falcon_boards.c
@@ -29,6 +29,15 @@
 #define FALCON_BOARD_SFN4111T 0x51
 #define FALCON_BOARD_SFN4112F 0x52
 
+/* Board temperature is about 15°C above ambient when air flow is
+ * limited. */
+#define FALCON_BOARD_TEMP_BIAS	15
+
+/* SFC4000 datasheet says: 'The maximum permitted junction temperature
+ * is 125°C; the thermal design of the environment for the SFC4000
+ * should aim to keep this well below 100°C.' */
+#define FALCON_JUNC_TEMP_MAX	90
+
 /*****************************************************************************
  * Support for LM87 sensor chip used on several boards
  */
@@ -548,16 +557,16 @@
 static u8 sfe4002_lm87_channel = 0x03; /* use AIN not FAN inputs */
 
 static const u8 sfe4002_lm87_regs[] = {
-	LM87_IN_LIMITS(0, 0x83, 0x91),		/* 2.5V:  1.8V +/- 5% */
-	LM87_IN_LIMITS(1, 0x51, 0x5a),		/* Vccp1: 1.2V +/- 5% */
-	LM87_IN_LIMITS(2, 0xb6, 0xca),		/* 3.3V:  3.3V +/- 5% */
-	LM87_IN_LIMITS(3, 0xb0, 0xc9),		/* 5V:    4.6-5.2V */
-	LM87_IN_LIMITS(4, 0xb0, 0xe0),		/* 12V:   11-14V */
-	LM87_IN_LIMITS(5, 0x44, 0x4b),		/* Vccp2: 1.0V +/- 5% */
-	LM87_AIN_LIMITS(0, 0xa0, 0xb2),		/* AIN1:  1.66V +/- 5% */
-	LM87_AIN_LIMITS(1, 0x91, 0xa1),		/* AIN2:  1.5V +/- 5% */
-	LM87_TEMP_INT_LIMITS(10, 60),		/* board */
-	LM87_TEMP_EXT1_LIMITS(10, 70),		/* Falcon */
+	LM87_IN_LIMITS(0, 0x7c, 0x99),		/* 2.5V:  1.8V +/- 10% */
+	LM87_IN_LIMITS(1, 0x4c, 0x5e),		/* Vccp1: 1.2V +/- 10% */
+	LM87_IN_LIMITS(2, 0xac, 0xd4),		/* 3.3V:  3.3V +/- 10% */
+	LM87_IN_LIMITS(3, 0xac, 0xd4),		/* 5V:    5.0V +/- 10% */
+	LM87_IN_LIMITS(4, 0xac, 0xe0),		/* 12V:   10.8-14V */
+	LM87_IN_LIMITS(5, 0x3f, 0x4f),		/* Vccp2: 1.0V +/- 10% */
+	LM87_AIN_LIMITS(0, 0x98, 0xbb),		/* AIN1:  1.66V +/- 10% */
+	LM87_AIN_LIMITS(1, 0x8a, 0xa9),		/* AIN2:  1.5V +/- 10% */
+	LM87_TEMP_INT_LIMITS(0, 80 + FALCON_BOARD_TEMP_BIAS),
+	LM87_TEMP_EXT1_LIMITS(0, FALCON_JUNC_TEMP_MAX),
 	0
 };
 
@@ -619,14 +628,14 @@
 static u8 sfn4112f_lm87_channel = 0x03; /* use AIN not FAN inputs */
 
 static const u8 sfn4112f_lm87_regs[] = {
-	LM87_IN_LIMITS(0, 0x83, 0x91),		/* 2.5V:  1.8V +/- 5% */
-	LM87_IN_LIMITS(1, 0x51, 0x5a),		/* Vccp1: 1.2V +/- 5% */
-	LM87_IN_LIMITS(2, 0xb6, 0xca),		/* 3.3V:  3.3V +/- 5% */
-	LM87_IN_LIMITS(4, 0xb0, 0xe0),		/* 12V:   11-14V */
-	LM87_IN_LIMITS(5, 0x44, 0x4b),		/* Vccp2: 1.0V +/- 5% */
-	LM87_AIN_LIMITS(1, 0x91, 0xa1),		/* AIN2:  1.5V +/- 5% */
-	LM87_TEMP_INT_LIMITS(10, 60),		/* board */
-	LM87_TEMP_EXT1_LIMITS(10, 70),		/* Falcon */
+	LM87_IN_LIMITS(0, 0x7c, 0x99),		/* 2.5V:  1.8V +/- 10% */
+	LM87_IN_LIMITS(1, 0x4c, 0x5e),		/* Vccp1: 1.2V +/- 10% */
+	LM87_IN_LIMITS(2, 0xac, 0xd4),		/* 3.3V:  3.3V +/- 10% */
+	LM87_IN_LIMITS(4, 0xac, 0xe0),		/* 12V:   10.8-14V */
+	LM87_IN_LIMITS(5, 0x3f, 0x4f),		/* Vccp2: 1.0V +/- 10% */
+	LM87_AIN_LIMITS(1, 0x8a, 0xa9),		/* AIN2:  1.5V +/- 10% */
+	LM87_TEMP_INT_LIMITS(0, 60 + FALCON_BOARD_TEMP_BIAS),
+	LM87_TEMP_EXT1_LIMITS(0, FALCON_JUNC_TEMP_MAX),
 	0
 };
 
diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/sfc/mcdi.c
index 9f035b9..f66b3da 100644
--- a/drivers/net/sfc/mcdi.c
+++ b/drivers/net/sfc/mcdi.c
@@ -127,7 +127,7 @@
 	efx_dword_t reg;
 
 	/* Check for a reboot atomically with respect to efx_mcdi_copyout() */
-	rc = efx_mcdi_poll_reboot(efx);
+	rc = -efx_mcdi_poll_reboot(efx);
 	if (rc)
 		goto out;
 
diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c
index e0d13a4..67eec7a 100644
--- a/drivers/net/sfc/qt202x_phy.c
+++ b/drivers/net/sfc/qt202x_phy.c
@@ -320,7 +320,7 @@
 
 	falcon_board(efx)->type->init_phy(efx);
 
-	return rc;
+	return 0;
 
  fail:
 	EFX_ERR(efx, "PHY reset timed out\n");
diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c
index 75a669d..d71c197 100644
--- a/drivers/net/tc35815.c
+++ b/drivers/net/tc35815.c
@@ -1437,7 +1437,6 @@
 		/* Transmit complete. */
 		lp->lstats.tx_ints++;
 		tc35815_txdone(dev);
-		netif_wake_queue(dev);
 		if (ret < 0)
 			ret = 0;
 	}
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index 4f27f02..5f3b9ea 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -584,6 +584,11 @@
 			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
 	.driver_info = (unsigned long) &mbm_info,
 }, {
+	/* Ericsson C3607w ver 2 */
+	USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x190b, USB_CLASS_COMM,
+			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
+	.driver_info = (unsigned long) &mbm_info,
+}, {
 	/* Toshiba F3507g */
 	USB_DEVICE_AND_INTERFACE_INFO(0x0930, 0x130b, USB_CLASS_COMM,
 			USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE),
diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c
index c93f58f..317aa34 100644
--- a/drivers/net/via-velocity.c
+++ b/drivers/net/via-velocity.c
@@ -1877,13 +1877,12 @@
 /**
  *	tx_srv		-	transmit interrupt service
  *	@vptr; Velocity
- *	@status:
  *
  *	Scan the queues looking for transmitted packets that
  *	we can complete and clean up. Update any statistics as
  *	necessary/
  */
-static int velocity_tx_srv(struct velocity_info *vptr, u32 status)
+static int velocity_tx_srv(struct velocity_info *vptr)
 {
 	struct tx_desc *td;
 	int qnum;
@@ -2090,14 +2089,12 @@
 /**
  *	velocity_rx_srv		-	service RX interrupt
  *	@vptr: velocity
- *	@status: adapter status (unused)
  *
  *	Walk the receive ring of the velocity adapter and remove
  *	any received packets from the receive queue. Hand the ring
  *	slots back to the adapter for reuse.
  */
-static int velocity_rx_srv(struct velocity_info *vptr, int status,
-		int budget_left)
+static int velocity_rx_srv(struct velocity_info *vptr, int budget_left)
 {
 	struct net_device_stats *stats = &vptr->dev->stats;
 	int rd_curr = vptr->rx.curr;
@@ -2151,32 +2148,24 @@
 	struct velocity_info *vptr = container_of(napi,
 			struct velocity_info, napi);
 	unsigned int rx_done;
-	u32 isr_status;
+	unsigned long flags;
 
-	spin_lock(&vptr->lock);
-	isr_status = mac_read_isr(vptr->mac_regs);
-
-	/* Ack the interrupt */
-	mac_write_isr(vptr->mac_regs, isr_status);
-	if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI)))
-		velocity_error(vptr, isr_status);
-
+	spin_lock_irqsave(&vptr->lock, flags);
 	/*
 	 * Do rx and tx twice for performance (taken from the VIA
 	 * out-of-tree driver).
 	 */
-	rx_done = velocity_rx_srv(vptr, isr_status, budget / 2);
-	velocity_tx_srv(vptr, isr_status);
-	rx_done += velocity_rx_srv(vptr, isr_status, budget - rx_done);
-	velocity_tx_srv(vptr, isr_status);
-
-	spin_unlock(&vptr->lock);
+	rx_done = velocity_rx_srv(vptr, budget / 2);
+	velocity_tx_srv(vptr);
+	rx_done += velocity_rx_srv(vptr, budget - rx_done);
+	velocity_tx_srv(vptr);
 
 	/* If budget not fully consumed, exit the polling mode */
 	if (rx_done < budget) {
 		napi_complete(napi);
 		mac_enable_int(vptr->mac_regs);
 	}
+	spin_unlock_irqrestore(&vptr->lock, flags);
 
 	return rx_done;
 }
@@ -2206,10 +2195,17 @@
 		return IRQ_NONE;
 	}
 
+	/* Ack the interrupt */
+	mac_write_isr(vptr->mac_regs, isr_status);
+
 	if (likely(napi_schedule_prep(&vptr->napi))) {
 		mac_disable_int(vptr->mac_regs);
 		__napi_schedule(&vptr->napi);
 	}
+
+	if (isr_status & (~(ISR_PRXI | ISR_PPRXI | ISR_PTXI | ISR_PPTXI)))
+		velocity_error(vptr, isr_status);
+
 	spin_unlock(&vptr->lock);
 
 	return IRQ_HANDLED;
@@ -3100,7 +3096,7 @@
 	velocity_init_registers(vptr, VELOCITY_INIT_WOL);
 	mac_disable_int(vptr->mac_regs);
 
-	velocity_tx_srv(vptr, 0);
+	velocity_tx_srv(vptr);
 
 	for (i = 0; i < vptr->tx.numq; i++) {
 		if (vptr->tx.used[i])
@@ -3344,6 +3340,7 @@
 {
 	struct velocity_info *vptr = netdev_priv(dev);
 	int max_us = 0x3f * 64;
+	unsigned long flags;
 
 	/* 6 bits of  */
 	if (ecmd->tx_coalesce_usecs > max_us)
@@ -3365,6 +3362,7 @@
 			ecmd->tx_coalesce_usecs);
 
 	/* Setup the interrupt suppression and queue timers */
+	spin_lock_irqsave(&vptr->lock, flags);
 	mac_disable_int(vptr->mac_regs);
 	setup_adaptive_interrupts(vptr);
 	setup_queue_timers(vptr);
@@ -3372,6 +3370,7 @@
 	mac_write_int_mask(vptr->int_mask, vptr->mac_regs);
 	mac_clear_isr(vptr->mac_regs);
 	mac_enable_int(vptr->mac_regs);
+	spin_unlock_irqrestore(&vptr->lock, flags);
 
 	return 0;
 }
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index fa12b90..29bf336 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1615,7 +1615,7 @@
 		bf->bf_frmlen -= padsize;
 	}
 
-	if (conf_is_ht(&hw->conf) && !is_pae(skb))
+	if (conf_is_ht(&hw->conf))
 		bf->bf_state.bf_type |= BUF_HT;
 
 	bf->bf_flags = setup_tx_flags(sc, skb, txctl->txq);
@@ -1701,7 +1701,7 @@
 			goto tx_done;
 		}
 
-		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+		if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && !is_pae(skb)) {
 			/*
 			 * Try aggregation if it's a unicast data frame
 			 * and the destination is HT capable.
diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h
index fe3bf94..c484cc2 100644
--- a/drivers/net/wireless/b43/b43.h
+++ b/drivers/net/wireless/b43/b43.h
@@ -115,6 +115,7 @@
 #define B43_MMIO_TSF_2			0x636	/* core rev < 3 only */
 #define B43_MMIO_TSF_3			0x638	/* core rev < 3 only */
 #define B43_MMIO_RNG			0x65A
+#define B43_MMIO_IFSSLOT		0x684	/* Interframe slot time */
 #define B43_MMIO_IFSCTL			0x688 /* Interframe space control */
 #define  B43_MMIO_IFSCTL_USE_EDCF	0x0004
 #define B43_MMIO_POWERUP_DELAY		0x6A8
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index 4c41cfe..490fb45d 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -628,10 +628,17 @@
 static void b43_set_slot_time(struct b43_wldev *dev, u16 slot_time)
 {
 	/* slot_time is in usec. */
-	if (dev->phy.type != B43_PHYTYPE_G)
+	/* This test used to exit for all but a G PHY. */
+	if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ)
 		return;
-	b43_write16(dev, 0x684, 510 + slot_time);
-	b43_shm_write16(dev, B43_SHM_SHARED, 0x0010, slot_time);
+	b43_write16(dev, B43_MMIO_IFSSLOT, 510 + slot_time);
+	/* Shared memory location 0x0010 is the slot time and should be
+	 * set to slot_time; however, this register is initially 0 and changing
+	 * the value adversely affects the transmit rate for BCM4311
+	 * devices. Until this behavior is unterstood, delete this step
+	 *
+	 * b43_shm_write16(dev, B43_SHM_SHARED, 0x0010, slot_time);
+	 */
 }
 
 static void b43_short_slot_timing_enable(struct b43_wldev *dev)
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c
index 9b4b8b5..3146281 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965.c
@@ -2008,7 +2008,7 @@
 			IWL_DEBUG_TX_REPLY(priv, "Retry scheduler reclaim scd_ssn "
 					   "%d index %d\n", scd_ssn , index);
 			freed = iwl_tx_queue_reclaim(priv, txq_id, index);
-			priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+			iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
 
 			if (priv->mac80211_registered &&
 			    (iwl_queue_space(&txq->q) > txq->q.low_mark) &&
diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c
index de45f30..cffaae7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-5000.c
+++ b/drivers/net/wireless/iwlwifi/iwl-5000.c
@@ -1125,7 +1125,7 @@
 					scd_ssn , index, txq_id, txq->swq_id);
 
 			freed = iwl_tx_queue_reclaim(priv, txq_id, index);
-			priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+			iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
 
 			if (priv->mac80211_registered &&
 			    (iwl_queue_space(&txq->q) > txq->q.low_mark) &&
@@ -1153,16 +1153,14 @@
 				   tx_resp->failure_frame);
 
 		freed = iwl_tx_queue_reclaim(priv, txq_id, index);
-		if (ieee80211_is_data_qos(tx_resp->frame_ctrl))
-			priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+		iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
 
 		if (priv->mac80211_registered &&
 		    (iwl_queue_space(&txq->q) > txq->q.low_mark))
 			iwl_wake_queue(priv, txq_id);
 	}
 
-	if (ieee80211_is_data_qos(tx_resp->frame_ctrl))
-		iwl_txq_check_empty(priv, sta_id, tid, txq_id);
+	iwl_txq_check_empty(priv, sta_id, tid, txq_id);
 
 	if (iwl_check_bits(status, TX_ABORT_REQUIRED_MSK))
 		IWL_ERR(priv, "TODO:  Implement Tx ABORT REQUIRED!!!\n");
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index 5461f10..f36f804 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -2745,6 +2745,7 @@
 			priv->staging_rxon.flags = 0;
 
 		iwl_set_rxon_channel(priv, conf->channel);
+		iwl_set_rxon_ht(priv, ht_conf);
 
 		iwl_set_flags_for_band(priv, conf->channel->band);
 		spin_unlock_irqrestore(&priv->lock, flags);
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 27ca859..b69e972 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -446,6 +446,8 @@
 int iwl_hw_tx_queue_init(struct iwl_priv *priv,
 			 struct iwl_tx_queue *txq);
 int iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq);
+void iwl_free_tfds_in_queue(struct iwl_priv *priv,
+			    int sta_id, int tid, int freed);
 int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq,
 		      int slots_num, u32 txq_id);
 void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id);
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index 6f36b6e..2dbce85 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -928,7 +928,10 @@
 	if (ieee80211_is_mgmt(fc) ||
 	    ieee80211_has_protected(fc) ||
 	    ieee80211_has_morefrags(fc) ||
-	    le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)
+	    le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG ||
+	    (ieee80211_is_data_qos(fc) &&
+	     *ieee80211_get_qos_ctl(hdr) &
+	     IEEE80211_QOS_CONTROL_A_MSDU_PRESENT))
 		ret = skb_linearize(skb);
 	else
 		ret = __pskb_pull_tail(skb, min_t(u16, IWL_LINK_HDR_MAX, len)) ?
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index 87ce2bd..8f40715 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -120,6 +120,20 @@
 EXPORT_SYMBOL(iwl_txq_update_write_ptr);
 
 
+void iwl_free_tfds_in_queue(struct iwl_priv *priv,
+			    int sta_id, int tid, int freed)
+{
+	if (priv->stations[sta_id].tid[tid].tfds_in_queue >= freed)
+		priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+	else {
+		IWL_ERR(priv, "free more than tfds_in_queue (%u:%d)\n",
+			priv->stations[sta_id].tid[tid].tfds_in_queue,
+			freed);
+		priv->stations[sta_id].tid[tid].tfds_in_queue = 0;
+	}
+}
+EXPORT_SYMBOL(iwl_free_tfds_in_queue);
+
 /**
  * iwl_tx_queue_free - Deallocate DMA queue.
  * @txq: Transmit queue to deallocate.
@@ -1131,6 +1145,7 @@
 	struct iwl_queue *q = &txq->q;
 	struct iwl_tx_info *tx_info;
 	int nfreed = 0;
+	struct ieee80211_hdr *hdr;
 
 	if ((index >= q->n_bd) || (iwl_queue_used(q, index) == 0)) {
 		IWL_ERR(priv, "Read index for DMA queue txq id (%d), index %d, "
@@ -1145,13 +1160,16 @@
 
 		tx_info = &txq->txb[txq->q.read_ptr];
 		iwl_tx_status(priv, tx_info->skb[0]);
+
+		hdr = (struct ieee80211_hdr *)tx_info->skb[0]->data;
+		if (hdr && ieee80211_is_data_qos(hdr->frame_control))
+			nfreed++;
 		tx_info->skb[0] = NULL;
 
 		if (priv->cfg->ops->lib->txq_inval_byte_cnt_tbl)
 			priv->cfg->ops->lib->txq_inval_byte_cnt_tbl(priv, txq);
 
 		priv->cfg->ops->lib->txq_free_tfd(priv, txq);
-		nfreed++;
 	}
 	return nfreed;
 }
@@ -1559,7 +1577,7 @@
 	if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) {
 		/* calculate mac80211 ampdu sw queue to wake */
 		int freed = iwl_tx_queue_reclaim(priv, scd_flow, index);
-		priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
+		iwl_free_tfds_in_queue(priv, sta_id, tid, freed);
 
 		if ((iwl_queue_space(&txq->q) > txq->q.low_mark) &&
 		    priv->mac80211_registered &&
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c
index 6d6ed74..f727b4a 100644
--- a/drivers/net/wireless/iwmc3200wifi/rx.c
+++ b/drivers/net/wireless/iwmc3200wifi/rx.c
@@ -794,7 +794,7 @@
 	}
 
 	bss->bss = kzalloc(bss_len, GFP_KERNEL);
-	if (!bss) {
+	if (!bss->bss) {
 		kfree(bss);
 		IWM_ERR(iwm, "Couldn't allocate bss\n");
 		return -ENOMEM;
diff --git a/drivers/net/wireless/rtl818x/rtl8187_dev.c b/drivers/net/wireless/rtl818x/rtl8187_dev.c
index bc5726d..7ba3052 100644
--- a/drivers/net/wireless/rtl818x/rtl8187_dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187_dev.c
@@ -65,6 +65,7 @@
 	/* Sitecom */
 	{USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187},
 	{USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B},
+	{USB_DEVICE(0x0df6, 0x0029), .driver_info = DEVICE_RTL8187B},
 	/* Sphairon Access Systems GmbH */
 	{USB_DEVICE(0x114B, 0x0150), .driver_info = DEVICE_RTL8187},
 	/* Dick Smith Electronics */
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 8e952fd..cb2fd01 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -720,12 +720,6 @@
 			-ret_val);
 		goto acpiphp_bus_add_out;
 	}
-	/*
-	 * try to start anyway.  We could have failed to add
-	 * simply because this bus had previously been added
-	 * on another add.  Don't bother with the return value
-	 * we just keep going.
-	 */
 	ret_val = acpi_bus_start(device);
 
 acpiphp_bus_add_out:
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index 07d14df..226b3e9 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -934,7 +934,7 @@
 	acer_backlight_device = bd;
 
 	bd->props.power = FB_BLANK_UNBLANK;
-	bd->props.brightness = max_brightness;
+	bd->props.brightness = read_brightness(bd);
 	bd->props.max_brightness = max_brightness;
 	backlight_update_status(bd);
 	return 0;
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index e67e4fe..eb603f1d5 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -5771,7 +5771,7 @@
 	case TPACPI_THERMAL_ACPI_TMP07:
 	case TPACPI_THERMAL_ACPI_UPDT:
 		sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
-				   &thermal_temp_input16_group);
+				   &thermal_temp_input8_group);
 		break;
 	case TPACPI_THERMAL_NONE:
 	default:
diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c
index 4775426..9e71ac6 100644
--- a/drivers/scsi/arm/fas216.c
+++ b/drivers/scsi/arm/fas216.c
@@ -2516,7 +2516,7 @@
 		if (info->scsi.phase == PHASE_IDLE)
 			fas216_kick(info);
 
-		mod_timer(&info->eh_timer, 30 * HZ);
+		mod_timer(&info->eh_timer, jiffies + 30 * HZ);
 		spin_unlock_irqrestore(&info->host_lock, flags);
 
 		/*
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 10be9f3..2f47ae7 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -2009,6 +2009,8 @@
 	fcoe_interface_cleanup(fcoe);
 	rtnl_unlock();
 	fcoe_if_destroy(fcoe->ctlr.lp);
+	module_put(THIS_MODULE);
+
 out_putdev:
 	dev_put(netdev);
 out_nodev:
@@ -2059,6 +2061,11 @@
 	}
 #endif
 
+	if (!try_module_get(THIS_MODULE)) {
+		rc = -EINVAL;
+		goto out_nomod;
+	}
+
 	rtnl_lock();
 	netdev = fcoe_if_to_netdev(buffer);
 	if (!netdev) {
@@ -2099,17 +2106,24 @@
 	if (!fcoe_link_ok(lport))
 		fcoe_ctlr_link_up(&fcoe->ctlr);
 
-	rc = 0;
-out_free:
 	/*
 	 * Release from init in fcoe_interface_create(), on success lport
 	 * should be holding a reference taken in fcoe_if_create().
 	 */
 	fcoe_interface_put(fcoe);
+	dev_put(netdev);
+	rtnl_unlock();
+	mutex_unlock(&fcoe_config_mutex);
+
+	return 0;
+out_free:
+	fcoe_interface_put(fcoe);
 out_putdev:
 	dev_put(netdev);
 out_nodev:
 	rtnl_unlock();
+	module_put(THIS_MODULE);
+out_nomod:
 	mutex_unlock(&fcoe_config_mutex);
 	return rc;
 }
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index 9823291..511cb6b 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -1187,7 +1187,7 @@
 			next_timer = fip->ctlr_ka_time;
 
 		if (time_after_eq(jiffies, fip->port_ka_time)) {
-			fip->port_ka_time += jiffies +
+			fip->port_ka_time = jiffies +
 				msecs_to_jiffies(FIP_VN_KA_PERIOD);
 			fip->send_port_ka = 1;
 		}
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 19d711c..7f43647 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1890,7 +1890,7 @@
 	fc_exch_setup_hdr(ep, fp, ep->f_ctl);
 	sp->cnt++;
 
-	if (ep->xid <= lport->lro_xid)
+	if (ep->xid <= lport->lro_xid && fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD)
 		fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
 
 	if (unlikely(lport->tt.frame_send(lport, fp)))
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index 881d5df..6fde2fa 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -298,9 +298,6 @@
 {
 	struct fc_lport *lport;
 
-	if (!fsp)
-		return;
-
 	lport = fsp->lp;
 	if ((fsp->req_flags & FC_SRB_READ) &&
 	    (lport->lro_enabled) && (lport->tt.ddp_setup)) {
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 0b16502..7ec8ce7 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -1800,7 +1800,8 @@
 	u32 did;
 
 	job->reply->reply_payload_rcv_len = 0;
-	rsp->resid_len = job->reply_payload.payload_len;
+	if (rsp)
+		rsp->resid_len = job->reply_payload.payload_len;
 
 	mutex_lock(&lport->lp_mutex);
 
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 0230052..97923bb 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -623,7 +623,7 @@
 
 		tov = ntohl(plp->fl_csp.sp_e_d_tov);
 		if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
-			tov /= 1000;
+			tov /= 1000000;
 		if (tov > rdata->e_d_tov)
 			rdata->e_d_tov = tov;
 		csp_seq = ntohs(plp->fl_csp.sp_tot_seq);
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index db6856c..4ad87fd 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -992,12 +992,10 @@
 		if (r2t == NULL) {
 			if (kfifo_out(&tcp_task->r2tqueue,
 			    (void *)&tcp_task->r2t, sizeof(void *)) !=
-			    sizeof(void *)) {
-				WARN_ONCE(1, "unexpected fifo state");
+			    sizeof(void *))
 				r2t = NULL;
-			}
-
-			r2t = tcp_task->r2t;
+			else
+				r2t = tcp_task->r2t;
 		}
 		spin_unlock_bh(&session->lock);
 	}
diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c
index 708ea31..d9b8ca5 100644
--- a/drivers/scsi/megaraid/megaraid_sas.c
+++ b/drivers/scsi/megaraid/megaraid_sas.c
@@ -3781,6 +3781,7 @@
 	    compat_alloc_user_space(sizeof(struct megasas_iocpacket));
 	int i;
 	int error = 0;
+	compat_uptr_t ptr;
 
 	if (clear_user(ioc, sizeof(*ioc)))
 		return -EFAULT;
@@ -3793,9 +3794,22 @@
 	    copy_in_user(&ioc->sge_count, &cioc->sge_count, sizeof(u32)))
 		return -EFAULT;
 
-	for (i = 0; i < MAX_IOCTL_SGE; i++) {
-		compat_uptr_t ptr;
+	/*
+	 * The sense_ptr is used in megasas_mgmt_fw_ioctl only when
+	 * sense_len is not null, so prepare the 64bit value under
+	 * the same condition.
+	 */
+	if (ioc->sense_len) {
+		void __user **sense_ioc_ptr =
+			(void __user **)(ioc->frame.raw + ioc->sense_off);
+		compat_uptr_t *sense_cioc_ptr =
+			(compat_uptr_t *)(cioc->frame.raw + cioc->sense_off);
+		if (get_user(ptr, sense_cioc_ptr) ||
+		    put_user(compat_ptr(ptr), sense_ioc_ptr))
+			return -EFAULT;
+	}
 
+	for (i = 0; i < MAX_IOCTL_SGE; i++) {
 		if (get_user(ptr, &cioc->sgl[i].iov_base) ||
 		    put_user(compat_ptr(ptr), &ioc->sgl[i].iov_base) ||
 		    copy_in_user(&ioc->sgl[i].iov_len,
diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
index c3e37c8e..e9b15c3 100644
--- a/drivers/serial/8250.c
+++ b/drivers/serial/8250.c
@@ -83,6 +83,9 @@
 
 #define PASS_LIMIT	256
 
+#define BOTH_EMPTY 	(UART_LSR_TEMT | UART_LSR_THRE)
+
+
 /*
  * We default to IRQ0 for the "no irq" hack.   Some
  * machine types want others as well - they're free
@@ -1792,7 +1795,7 @@
 	up->lsr_saved_flags |= lsr & LSR_SAVE_FLAGS;
 	spin_unlock_irqrestore(&up->port.lock, flags);
 
-	return lsr & UART_LSR_TEMT ? TIOCSER_TEMT : 0;
+	return (lsr & BOTH_EMPTY) == BOTH_EMPTY ? TIOCSER_TEMT : 0;
 }
 
 static unsigned int serial8250_get_mctrl(struct uart_port *port)
@@ -1850,8 +1853,6 @@
 	spin_unlock_irqrestore(&up->port.lock, flags);
 }
 
-#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-
 /*
  *	Wait for transmitter & holding register to empty
  */
diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c
index 5681ebe..03dfd27 100644
--- a/drivers/ssb/main.c
+++ b/drivers/ssb/main.c
@@ -494,8 +494,7 @@
 #endif
 			break;
 		case SSB_BUSTYPE_SDIO:
-#ifdef CONFIG_SSB_SDIO
-			sdev->irq = bus->host_sdio->dev.irq;
+#ifdef CONFIG_SSB_SDIOHOST
 			dev->parent = &bus->host_sdio->dev;
 #endif
 			break;
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 6e8bcdf..a678186 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1312,9 +1312,9 @@
 	void __user *addr = as->userurb;
 	unsigned int i;
 
-	if (as->userbuffer)
+	if (as->userbuffer && urb->actual_length)
 		if (copy_to_user(as->userbuffer, urb->transfer_buffer,
-				 urb->transfer_buffer_length))
+				 urb->actual_length))
 			goto err_out;
 	if (put_user(as->status, &userurb->status))
 		goto err_out;
@@ -1334,14 +1334,11 @@
 		}
 	}
 
-	free_async(as);
-
 	if (put_user(addr, (void __user * __user *)arg))
 		return -EFAULT;
 	return 0;
 
 err_out:
-	free_async(as);
 	return -EFAULT;
 }
 
@@ -1371,8 +1368,11 @@
 static int proc_reapurb(struct dev_state *ps, void __user *arg)
 {
 	struct async *as = reap_as(ps);
-	if (as)
-		return processcompl(as, (void __user * __user *)arg);
+	if (as) {
+		int retval = processcompl(as, (void __user * __user *)arg);
+		free_async(as);
+		return retval;
+	}
 	if (signal_pending(current))
 		return -EINTR;
 	return -EIO;
@@ -1380,11 +1380,16 @@
 
 static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
 {
+	int retval;
 	struct async *as;
 
-	if (!(as = async_getcompleted(ps)))
-		return -EAGAIN;
-	return processcompl(as, (void __user * __user *)arg);
+	as = async_getcompleted(ps);
+	retval = -EAGAIN;
+	if (as) {
+		retval = processcompl(as, (void __user * __user *)arg);
+		free_async(as);
+	}
+	return retval;
 }
 
 #ifdef CONFIG_COMPAT
@@ -1475,9 +1480,9 @@
 	void __user *addr = as->userurb;
 	unsigned int i;
 
-	if (as->userbuffer)
+	if (as->userbuffer && urb->actual_length)
 		if (copy_to_user(as->userbuffer, urb->transfer_buffer,
-				 urb->transfer_buffer_length))
+				 urb->actual_length))
 			return -EFAULT;
 	if (put_user(as->status, &userurb->status))
 		return -EFAULT;
@@ -1497,7 +1502,6 @@
 		}
 	}
 
-	free_async(as);
 	if (put_user(ptr_to_compat(addr), (u32 __user *)arg))
 		return -EFAULT;
 	return 0;
@@ -1506,8 +1510,11 @@
 static int proc_reapurb_compat(struct dev_state *ps, void __user *arg)
 {
 	struct async *as = reap_as(ps);
-	if (as)
-		return processcompl_compat(as, (void __user * __user *)arg);
+	if (as) {
+		int retval = processcompl_compat(as, (void __user * __user *)arg);
+		free_async(as);
+		return retval;
+	}
 	if (signal_pending(current))
 		return -EINTR;
 	return -EIO;
@@ -1515,11 +1522,16 @@
 
 static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg)
 {
+	int retval;
 	struct async *as;
 
-	if (!(as = async_getcompleted(ps)))
-		return -EAGAIN;
-	return processcompl_compat(as, (void __user * __user *)arg);
+	retval = -EAGAIN;
+	as = async_getcompleted(ps);
+	if (as) {
+		retval = processcompl_compat(as, (void __user * __user *)arg);
+		free_async(as);
+	}
+	return retval;
 }
 
 
diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/f_eem.c
index 0a577d5..d4f0db5 100644
--- a/drivers/usb/gadget/f_eem.c
+++ b/drivers/usb/gadget/f_eem.c
@@ -358,7 +358,7 @@
 	 * b15:		bmType (0 == data)
 	 */
 	len = skb->len;
-	put_unaligned_le16((len & 0x3FFF) | BIT(14), skb_push(skb, 2));
+	put_unaligned_le16(len & 0x3FFF, skb_push(skb, 2));
 
 	/* add a zero-length EEM packet, if needed */
 	if (padlen)
@@ -464,7 +464,6 @@
 			}
 
 			/* validate CRC */
-			crc = get_unaligned_le32(skb->data + len - ETH_FCS_LEN);
 			if (header & BIT(14)) {
 				crc = get_unaligned_le32(skb->data + len
 							- ETH_FCS_LEN);
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index 4295601..76496f5 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -29,7 +29,7 @@
 #if defined USB_ETH_RNDIS
 #  undef USB_ETH_RNDIS
 #endif
-#ifdef CONFIG_USB_ETH_RNDIS
+#ifdef CONFIG_USB_G_MULTI_RNDIS
 #  define USB_ETH_RNDIS y
 #endif
 
diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c
index e220fb8..8b45145 100644
--- a/drivers/usb/gadget/r8a66597-udc.c
+++ b/drivers/usb/gadget/r8a66597-udc.c
@@ -26,6 +26,7 @@
 #include <linux/io.h>
 #include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/err.h>
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index 4b5dbd0..5fc80a1 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -2582,6 +2582,7 @@
 	hsotg->gadget.dev.driver = NULL;
 	return ret;
 }
+EXPORT_SYMBOL(usb_gadget_register_driver);
 
 int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
 {
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index c75d927..1937267 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -196,7 +196,9 @@
 			if (hostpc_reg) {
 				u32	t3;
 
+				spin_unlock_irq(&ehci->lock);
 				msleep(5);/* 5ms for HCD enter low pwr mode */
+				spin_lock_irq(&ehci->lock);
 				t3 = ehci_readl(ehci, hostpc_reg);
 				ehci_writel(ehci, t3 | HOSTPC_PHCD, hostpc_reg);
 				t3 = ehci_readl(ehci, hostpc_reg);
@@ -904,17 +906,18 @@
 			if ((temp & PORT_PE) == 0
 					|| (temp & PORT_RESET) != 0)
 				goto error;
-			ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+
 			/* After above check the port must be connected.
 			 * Set appropriate bit thus could put phy into low power
 			 * mode if we have hostpc feature
 			 */
+			temp &= ~PORT_WKCONN_E;
+			temp |= PORT_WKDISC_E | PORT_WKOC_E;
+			ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
 			if (hostpc_reg) {
-				temp &= ~PORT_WKCONN_E;
-				temp |= (PORT_WKDISC_E | PORT_WKOC_E);
-				ehci_writel(ehci, temp | PORT_SUSPEND,
-							status_reg);
+				spin_unlock_irqrestore(&ehci->lock, flags);
 				msleep(5);/* 5ms for HCD enter low pwr mode */
+				spin_lock_irqsave(&ehci->lock, flags);
 				temp1 = ehci_readl(ehci, hostpc_reg);
 				ehci_writel(ehci, temp1 | HOSTPC_PHCD,
 					hostpc_reg);
diff --git a/drivers/usb/host/fhci-tds.c b/drivers/usb/host/fhci-tds.c
index d224ab4..e123289 100644
--- a/drivers/usb/host/fhci-tds.c
+++ b/drivers/usb/host/fhci-tds.c
@@ -105,7 +105,7 @@
 		if (ep->td_base)
 			cpm_muram_free(cpm_muram_offset(ep->td_base));
 
-		if (ep->conf_frame_Q) {
+		if (kfifo_initialized(&ep->conf_frame_Q)) {
 			size = cq_howmany(&ep->conf_frame_Q);
 			for (; size; size--) {
 				struct packet *pkt = cq_get(&ep->conf_frame_Q);
@@ -115,7 +115,7 @@
 			cq_delete(&ep->conf_frame_Q);
 		}
 
-		if (ep->empty_frame_Q) {
+		if (kfifo_initialized(&ep->empty_frame_Q)) {
 			size = cq_howmany(&ep->empty_frame_Q);
 			for (; size; size--) {
 				struct packet *pkt = cq_get(&ep->empty_frame_Q);
@@ -125,7 +125,7 @@
 			cq_delete(&ep->empty_frame_Q);
 		}
 
-		if (ep->dummy_packets_Q) {
+		if (kfifo_initialized(&ep->dummy_packets_Q)) {
 			size = cq_howmany(&ep->dummy_packets_Q);
 			for (; size; size--) {
 				u8 *buff = cq_get(&ep->dummy_packets_Q);
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index 0025847..8b37a4b 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -3245,6 +3245,7 @@
 	{ USB_DEVICE(0x0711, 0x0902) },
 	{ USB_DEVICE(0x0711, 0x0903) },
 	{ USB_DEVICE(0x0711, 0x0918) },
+	{ USB_DEVICE(0x0711, 0x0920) },
 	{ USB_DEVICE(0x182d, 0x021c) },
 	{ USB_DEVICE(0x182d, 0x0269) },
 	{ }
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index de56b3d..3d2d3e5 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -44,6 +44,7 @@
 config USB_ULPI
 	bool "Generic ULPI Transceiver Driver"
 	depends on ARM
+	select USB_OTG_UTILS
 	help
 	  Enable this to support ULPI connected USB OTG transceivers which
 	  are likely found on embedded boards.
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 216f187..7638828 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -50,7 +50,7 @@
  * Version Information
  */
 #define DRIVER_VERSION "v1.5.0"
-#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>"
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>, Andreas Mohr"
 #define DRIVER_DESC "USB FTDI Serial Converters Driver"
 
 static int debug;
@@ -145,10 +145,15 @@
 
 
 
+/*
+ * Device ID not listed? Test via module params product/vendor or
+ * /sys/bus/usb/ftdi_sio/new_id, then send patch/report!
+ */
 static struct usb_device_id id_table_combined [] = {
 	{ USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_NXTCAM_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_0_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_1_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_SCS_DEVICE_2_PID) },
@@ -552,9 +557,16 @@
 	{ USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) },
 	/*
-	 * Due to many user requests for multiple ELV devices we enable
-	 * them by default.
+	 * ELV devices:
 	 */
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_USR_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_MSM1_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_KL100_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS550_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_EC3000_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS888_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_TWS550_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_FEM_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_ELV_CLI7000_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_ELV_PPS7330_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_ELV_TFM100_PID) },
@@ -571,11 +583,17 @@
 	{ USB_DEVICE(FTDI_VID, FTDI_ELV_PCK100_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_ELV_RFP500_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_ELV_FS20SIG_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_UTP8_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS300PC_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS444PC_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1300PC_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_ELV_EM1010PC_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS500_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_ELV_HS485_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_UMS100_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_TFD128_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_FM3RX_PID) },
+	{ USB_DEVICE(FTDI_VID, FTDI_ELV_WS777_PID) },
 	{ USB_DEVICE(FTDI_VID, LINX_SDMUSBQSS_PID) },
 	{ USB_DEVICE(FTDI_VID, LINX_MASTERDEVEL2_PID) },
 	{ USB_DEVICE(FTDI_VID, LINX_FUTURE_0_PID) },
@@ -697,6 +715,7 @@
 	{ USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) },
 	{ USB_DEVICE(FTDI_VID, FTDI_REU_TINY_PID) },
 	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_QUIDO4x4_PID) },
+	{ USB_DEVICE(PAPOUCH_VID, PAPOUCH_AD4USB_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DGQG_PID) },
 	{ USB_DEVICE(FTDI_VID, FTDI_DOMINTELL_DUSB_PID) },
 	{ USB_DEVICE(ALTI2_VID, ALTI2_N3_PID) },
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index da92b49..c8951ae 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -38,6 +38,8 @@
 /* www.candapter.com Ewert Energy Systems CANdapter device */
 #define FTDI_CANDAPTER_PID 0x9F80 /* Product Id */
 
+#define FTDI_NXTCAM_PID		0xABB8 /* NXTCam for Mindstorms NXT */
+
 /* OOCDlink by Joern Kaipf <joernk@web.de>
  * (http://www.joernonline.de/dw/doku.php?id=start&idx=projects:oocdlink) */
 #define FTDI_OOCDLINK_PID	0xbaf8	/* Amontec JTAGkey */
@@ -161,22 +163,37 @@
 /*
  * ELV USB devices submitted by Christian Abt of ELV (www.elv.de).
  * All of these devices use FTDI's vendor ID (0x0403).
+ * Further IDs taken from ELV Windows .inf file.
  *
  * The previously included PID for the UO 100 module was incorrect.
  * In fact, that PID was for ELV's UR 100 USB-RS232 converter (0xFB58).
  *
  * Armin Laeuger originally sent the PID for the UM 100 module.
  */
+#define FTDI_ELV_USR_PID	0xE000	/* ELV Universal-Sound-Recorder */
+#define FTDI_ELV_MSM1_PID	0xE001	/* ELV Mini-Sound-Modul */
+#define FTDI_ELV_KL100_PID	0xE002	/* ELV Kfz-Leistungsmesser KL 100 */
+#define FTDI_ELV_WS550_PID	0xE004	/* WS 550 */
+#define FTDI_ELV_EC3000_PID	0xE006	/* ENERGY CONTROL 3000 USB */
+#define FTDI_ELV_WS888_PID	0xE008	/* WS 888 */
+#define FTDI_ELV_TWS550_PID	0xE009	/* Technoline WS 550 */
+#define FTDI_ELV_FEM_PID	0xE00A	/* Funk Energie Monitor */
 #define FTDI_ELV_FHZ1300PC_PID	0xE0E8	/* FHZ 1300 PC */
 #define FTDI_ELV_WS500_PID	0xE0E9	/* PC-Wetterstation (WS 500) */
 #define FTDI_ELV_HS485_PID	0xE0EA	/* USB to RS-485 adapter */
+#define FTDI_ELV_UMS100_PID	0xE0EB	/* ELV USB Master-Slave Schaltsteckdose UMS 100 */
+#define FTDI_ELV_TFD128_PID	0xE0EC	/* ELV Temperatur-Feuchte-Datenlogger TFD 128 */
+#define FTDI_ELV_FM3RX_PID	0xE0ED	/* ELV Messwertuebertragung FM3 RX */
+#define FTDI_ELV_WS777_PID	0xE0EE	/* Conrad WS 777 */
 #define FTDI_ELV_EM1010PC_PID	0xE0EF	/* Engery monitor EM 1010 PC */
 #define FTDI_ELV_CSI8_PID	0xE0F0	/* Computer-Schalt-Interface (CSI 8) */
 #define FTDI_ELV_EM1000DL_PID	0xE0F1	/* PC-Datenlogger fuer Energiemonitor (EM 1000 DL) */
 #define FTDI_ELV_PCK100_PID	0xE0F2	/* PC-Kabeltester (PCK 100) */
 #define FTDI_ELV_RFP500_PID	0xE0F3	/* HF-Leistungsmesser (RFP 500) */
 #define FTDI_ELV_FS20SIG_PID	0xE0F4	/* Signalgeber (FS 20 SIG) */
+#define FTDI_ELV_UTP8_PID	0xE0F5	/* ELV UTP 8 */
 #define FTDI_ELV_WS300PC_PID	0xE0F6	/* PC-Wetterstation (WS 300 PC) */
+#define FTDI_ELV_WS444PC_PID	0xE0F7	/* Conrad WS 444 PC */
 #define FTDI_PHI_FISCO_PID      0xE40B  /* PHI Fisco USB to Serial cable */
 #define FTDI_ELV_UAD8_PID	0xF068	/* USB-AD-Wandler (UAD 8) */
 #define FTDI_ELV_UDA7_PID	0xF069	/* USB-DA-Wandler (UDA 7) */
@@ -968,6 +985,7 @@
 #define PAPOUCH_VID			0x5050	/* Vendor ID */
 #define PAPOUCH_TMU_PID			0x0400	/* TMU USB Thermometer */
 #define PAPOUCH_QUIDO4x4_PID		0x0900	/* Quido 4/4 Module */
+#define PAPOUCH_AD4USB_PID		0x8003	/* AD4USB Measurement Module */
 
 /*
  * Marvell SheevaPlug
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index ac1b644..3eb6143 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -298,6 +298,7 @@
 	{ USB_DEVICE(0x1199, 0x68A3), 	/* Sierra Wireless Direct IP modems */
 	  .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist
 	},
+       { USB_DEVICE(0x413C, 0x08133) }, /* Dell Computer Corp. Wireless 5720 VZW Mobile Broadband (EVDO Rev-A) Minicard GPS Port */
 
 	{ }
 };
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index c932f90..49575fba 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -941,7 +941,7 @@
 UNUSUAL_DEV(  0x07af, 0x0004, 0x0100, 0x0133,
 		"Microtech",
 		"USB-SCSI-DB25",
-		US_SC_SCSI, US_PR_BULK, usb_stor_euscsi_init,
+		US_SC_DEVICE, US_PR_DEVICE, usb_stor_euscsi_init,
 		US_FL_SCM_MULT_TARG ), 
 
 UNUSUAL_DEV(  0x07af, 0x0005, 0x0100, 0x0100,
diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c
index eb12182..d25df51 100644
--- a/drivers/video/efifb.c
+++ b/drivers/video/efifb.c
@@ -161,8 +161,17 @@
 	return 0;
 }
 
+static void efifb_destroy(struct fb_info *info)
+{
+	if (info->screen_base)
+		iounmap(info->screen_base);
+	release_mem_region(info->aperture_base, info->aperture_size);
+	framebuffer_release(info);
+}
+
 static struct fb_ops efifb_ops = {
 	.owner		= THIS_MODULE,
+	.fb_destroy	= efifb_destroy,
 	.fb_setcolreg	= efifb_setcolreg,
 	.fb_fillrect	= cfb_fillrect,
 	.fb_copyarea	= cfb_copyarea,
@@ -281,7 +290,7 @@
 	info->par = NULL;
 
 	info->aperture_base = efifb_fix.smem_start;
-	info->aperture_size = size_total;
+	info->aperture_size = size_remap;
 
 	info->screen_base = ioremap(efifb_fix.smem_start, efifb_fix.smem_len);
 	if (!info->screen_base) {
diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c
index c7b3f9d..2159e66 100644
--- a/drivers/watchdog/bfin_wdt.c
+++ b/drivers/watchdog/bfin_wdt.c
@@ -1,9 +1,8 @@
 /*
  * Blackfin On-Chip Watchdog Driver
- *  Supports BF53[123]/BF53[467]/BF54[2489]/BF561
  *
  * Originally based on softdog.c
- * Copyright 2006-2007 Analog Devices Inc.
+ * Copyright 2006-2010 Analog Devices Inc.
  * Copyright 2006-2007 Michele d'Amico
  * Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>
  *
@@ -137,13 +136,15 @@
  */
 static int bfin_wdt_set_timeout(unsigned long t)
 {
-	u32 cnt;
+	u32 cnt, max_t, sclk;
 	unsigned long flags;
 
-	stampit();
+	sclk = get_sclk();
+	max_t = -1 / sclk;
+	cnt = t * sclk;
+	stamp("maxtimeout=%us newtimeout=%lus (cnt=%#x)", max_t, t, cnt);
 
-	cnt = t * get_sclk();
-	if (cnt < get_sclk()) {
+	if (t > max_t) {
 		printk(KERN_WARNING PFX "timeout value is too large\n");
 		return -EINVAL;
 	}
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 9d08096..6ed434a 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -720,13 +720,15 @@
 					inode->i_ino, orig_offset);
 		BUG_ON(ret);
 	}
-	fi = btrfs_item_ptr(leaf, path->slots[0],
-			   struct btrfs_file_extent_item);
 	if (del_nr == 0) {
+		fi = btrfs_item_ptr(leaf, path->slots[0],
+			   struct btrfs_file_extent_item);
 		btrfs_set_file_extent_type(leaf, fi,
 					   BTRFS_FILE_EXTENT_REG);
 		btrfs_mark_buffer_dirty(leaf);
 	} else {
+		fi = btrfs_item_ptr(leaf, del_slot - 1,
+			   struct btrfs_file_extent_item);
 		btrfs_set_file_extent_type(leaf, fi,
 					   BTRFS_FILE_EXTENT_REG);
 		btrfs_set_file_extent_num_bytes(leaf, fi,
diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
index 14ac480..eeb4986 100644
--- a/fs/cachefiles/namei.c
+++ b/fs/cachefiles/namei.c
@@ -348,7 +348,17 @@
 	dir = dget_parent(object->dentry);
 
 	mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT);
-	ret = cachefiles_bury_object(cache, dir, object->dentry);
+
+	/* we need to check that our parent is _still_ our parent - it may have
+	 * been renamed */
+	if (dir == object->dentry->d_parent) {
+		ret = cachefiles_bury_object(cache, dir, object->dentry);
+	} else {
+		/* it got moved, presumably by cachefilesd culling it, so it's
+		 * no longer in the key path and we can ignore it */
+		mutex_unlock(&dir->d_inode->i_mutex);
+		ret = 0;
+	}
 
 	dput(dir);
 	_leave(" = %d", ret);
diff --git a/fs/exec.c b/fs/exec.c
index e95c692..cce6bbd 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -637,7 +637,6 @@
 	 * will align it up.
 	 */
 	rlim_stack = rlimit(RLIMIT_STACK) & PAGE_MASK;
-	rlim_stack = min(rlim_stack, stack_size);
 #ifdef CONFIG_STACK_GROWSUP
 	if (stack_size + stack_expand > rlim_stack)
 		stack_base = vma->vm_start + rlim_stack;
diff --git a/fs/namei.c b/fs/namei.c
index d62fdc8..a4855af 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -823,6 +823,17 @@
 }
 
 /*
+ * This is a temporary kludge to deal with "automount" symlinks; proper
+ * solution is to trigger them on follow_mount(), so that do_lookup()
+ * would DTRT.  To be killed before 2.6.34-final.
+ */
+static inline int follow_on_final(struct inode *inode, unsigned lookup_flags)
+{
+	return inode && unlikely(inode->i_op->follow_link) &&
+		((lookup_flags & LOOKUP_FOLLOW) || S_ISDIR(inode->i_mode));
+}
+
+/*
  * Name resolution.
  * This is the basic name resolution function, turning a pathname into
  * the final dentry. We expect 'base' to be positive and a directory.
@@ -942,8 +953,7 @@
 		if (err)
 			break;
 		inode = next.dentry->d_inode;
-		if ((lookup_flags & LOOKUP_FOLLOW)
-		    && inode && inode->i_op->follow_link) {
+		if (follow_on_final(inode, lookup_flags)) {
 			err = do_follow_link(&next, nd);
 			if (err)
 				goto return_err;
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index e1d415e..0d28982 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -342,6 +342,7 @@
 		data->res.fattr = &data->fattr;
 		data->res.eof = 0;
 		data->res.count = bytes;
+		nfs_fattr_init(&data->fattr);
 		msg.rpc_argp = &data->args;
 		msg.rpc_resp = &data->res;
 
@@ -575,6 +576,7 @@
 	data->res.count = 0;
 	data->res.fattr = &data->fattr;
 	data->res.verf = &data->verf;
+	nfs_fattr_init(&data->fattr);
 
 	NFS_PROTO(data->inode)->commit_setup(data, &msg);
 
@@ -766,6 +768,7 @@
 		data->res.fattr = &data->fattr;
 		data->res.count = bytes;
 		data->res.verf = &data->verf;
+		nfs_fattr_init(&data->fattr);
 
 		task_setup_data.task = &data->task;
 		task_setup_data.callback_data = data;
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 97d79ef..8715d19 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -752,7 +752,8 @@
 			    flags, current_cred());
 	if (IS_ERR(*filp))
 		host_err = PTR_ERR(*filp);
-	host_err = ima_file_check(*filp, access);
+	else
+		host_err = ima_file_check(*filp, access);
 out_nfserr:
 	err = nfserrno(host_err);
 out:
diff --git a/fs/proc/base.c b/fs/proc/base.c
index e42bbd8..58324c2 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -2369,16 +2369,30 @@
 {
 	struct pid_namespace *ns = dentry->d_sb->s_fs_info;
 	pid_t tgid = task_tgid_nr_ns(current, ns);
-	char tmp[PROC_NUMBUF];
-	if (!tgid)
-		return ERR_PTR(-ENOENT);
-	sprintf(tmp, "%d", task_tgid_nr_ns(current, ns));
-	return ERR_PTR(vfs_follow_link(nd,tmp));
+	char *name = ERR_PTR(-ENOENT);
+	if (tgid) {
+		name = __getname();
+		if (!name)
+			name = ERR_PTR(-ENOMEM);
+		else
+			sprintf(name, "%d", tgid);
+	}
+	nd_set_link(nd, name);
+	return NULL;
+}
+
+static void proc_self_put_link(struct dentry *dentry, struct nameidata *nd,
+				void *cookie)
+{
+	char *s = nd_get_link(nd);
+	if (!IS_ERR(s))
+		__putname(s);
 }
 
 static const struct inode_operations proc_self_inode_operations = {
 	.readlink	= proc_self_readlink,
 	.follow_link	= proc_self_follow_link,
+	.put_link	= proc_self_put_link,
 };
 
 /*
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 9087b10..2df0f5c 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -1497,9 +1497,11 @@
 
 	args.objectid = key->on_disk_key.k_objectid;
 	args.dirid = key->on_disk_key.k_dir_id;
+	reiserfs_write_unlock(s);
 	inode = iget5_locked(s, key->on_disk_key.k_objectid,
 			     reiserfs_find_actor, reiserfs_init_locked_inode,
 			     (void *)(&args));
+	reiserfs_write_lock(s);
 	if (!inode)
 		return ERR_PTR(-ENOMEM);
 
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
index 220b758..6a06a1d 100644
--- a/fs/sysfs/inode.c
+++ b/fs/sysfs/inode.c
@@ -81,24 +81,23 @@
 		if (!sd_attrs)
 			return -ENOMEM;
 		sd->s_iattr = sd_attrs;
-	} else {
-		/* attributes were changed at least once in past */
-		iattrs = &sd_attrs->ia_iattr;
+	}
+	/* attributes were changed at least once in past */
+	iattrs = &sd_attrs->ia_iattr;
 
-		if (ia_valid & ATTR_UID)
-			iattrs->ia_uid = iattr->ia_uid;
-		if (ia_valid & ATTR_GID)
-			iattrs->ia_gid = iattr->ia_gid;
-		if (ia_valid & ATTR_ATIME)
-			iattrs->ia_atime = iattr->ia_atime;
-		if (ia_valid & ATTR_MTIME)
-			iattrs->ia_mtime = iattr->ia_mtime;
-		if (ia_valid & ATTR_CTIME)
-			iattrs->ia_ctime = iattr->ia_ctime;
-		if (ia_valid & ATTR_MODE) {
-			umode_t mode = iattr->ia_mode;
-			iattrs->ia_mode = sd->s_mode = mode;
-		}
+	if (ia_valid & ATTR_UID)
+		iattrs->ia_uid = iattr->ia_uid;
+	if (ia_valid & ATTR_GID)
+		iattrs->ia_gid = iattr->ia_gid;
+	if (ia_valid & ATTR_ATIME)
+		iattrs->ia_atime = iattr->ia_atime;
+	if (ia_valid & ATTR_MTIME)
+		iattrs->ia_mtime = iattr->ia_mtime;
+	if (ia_valid & ATTR_CTIME)
+		iattrs->ia_ctime = iattr->ia_ctime;
+	if (ia_valid & ATTR_MODE) {
+		umode_t mode = iattr->ia_mode;
+		iattrs->ia_mode = sd->s_mode = mode;
 	}
 	return 0;
 }
diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h
index ab94335..6816be6 100644
--- a/include/linux/amba/bus.h
+++ b/include/linux/amba/bus.h
@@ -1,5 +1,9 @@
 /*
- *  linux/include/asm-arm/hardware/amba.h
+ *  linux/include/amba/bus.h
+ *
+ *  This device type deals with ARM PrimeCells and anything else that
+ *  presents a proper CID (0xB105F00D) at the end of the I/O register
+ *  region or that is derived from a PrimeCell.
  *
  *  Copyright (C) 2003 Deep Blue Solutions Ltd, All Rights Reserved.
  *
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 5c80189..1896e86 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -461,8 +461,7 @@
 #define QUEUE_FLAG_NONROT      14	/* non-rotational device (SSD) */
 #define QUEUE_FLAG_VIRT        QUEUE_FLAG_NONROT /* paravirt device */
 #define QUEUE_FLAG_IO_STAT     15	/* do IO stats */
-#define QUEUE_FLAG_CQ	       16	/* hardware does queuing */
-#define QUEUE_FLAG_DISCARD     17	/* supports DISCARD */
+#define QUEUE_FLAG_DISCARD     16	/* supports DISCARD */
 
 #define QUEUE_FLAG_DEFAULT	((1 << QUEUE_FLAG_IO_STAT) |		\
 				 (1 << QUEUE_FLAG_CLUSTER) |		\
@@ -586,7 +585,6 @@
 
 #define blk_queue_plugged(q)	test_bit(QUEUE_FLAG_PLUGGED, &(q)->queue_flags)
 #define blk_queue_tagged(q)	test_bit(QUEUE_FLAG_QUEUED, &(q)->queue_flags)
-#define blk_queue_queuing(q)	test_bit(QUEUE_FLAG_CQ, &(q)->queue_flags)
 #define blk_queue_stopped(q)	test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags)
 #define blk_queue_nomerges(q)	test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags)
 #define blk_queue_nonrot(q)	test_bit(QUEUE_FLAG_NONROT, &(q)->queue_flags)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b1bcb27..ebb1cd5 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -729,6 +729,7 @@
 	uid_t			i_uid;
 	gid_t			i_gid;
 	dev_t			i_rdev;
+	unsigned int		i_blkbits;
 	u64			i_version;
 	loff_t			i_size;
 #ifdef __NEED_I_SIZE_ORDERED
@@ -738,7 +739,6 @@
 	struct timespec		i_mtime;
 	struct timespec		i_ctime;
 	blkcnt_t		i_blocks;
-	unsigned int		i_blkbits;
 	unsigned short          i_bytes;
 	umode_t			i_mode;
 	spinlock_t		i_lock;	/* i_blocks, i_bytes, maybe i_size */
diff --git a/include/linux/hw_breakpoint.h b/include/linux/hw_breakpoint.h
index 070ba06..5977b72 100644
--- a/include/linux/hw_breakpoint.h
+++ b/include/linux/hw_breakpoint.h
@@ -44,7 +44,7 @@
 	return bp->attr.bp_type;
 }
 
-static inline int hw_breakpoint_len(struct perf_event *bp)
+static inline unsigned long hw_breakpoint_len(struct perf_event *bp)
 {
 	return bp->attr.bp_len;
 }
diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h
index bf1c5be..7897f30 100644
--- a/include/linux/i2c/twl.h
+++ b/include/linux/i2c/twl.h
@@ -547,6 +547,10 @@
 	unsigned int	audio_mclk;
 	struct twl4030_codec_audio_data		*audio;
 	struct twl4030_codec_vibra_data		*vibra;
+
+	/* twl6030 */
+	int audpwron_gpio;      /* audio power-on gpio */
+	int naudint_irq;        /* audio interrupt */
 };
 
 struct twl4030_platform_data {
diff --git a/include/linux/input.h b/include/linux/input.h
index 735ceaf..663208a 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -376,6 +376,7 @@
 #define KEY_DISPLAY_OFF		245	/* display device to off state */
 
 #define KEY_WIMAX		246
+#define KEY_RFKILL		247	/* Key that controls all radios */
 
 /* Range 248 - 255 is reserved for special needs of AT keyboard driver */
 
diff --git a/include/linux/kfifo.h b/include/linux/kfifo.h
index 6f6c5f3..bc0fc79 100644
--- a/include/linux/kfifo.h
+++ b/include/linux/kfifo.h
@@ -124,7 +124,7 @@
  */
 static inline bool kfifo_initialized(struct kfifo *fifo)
 {
-	return fifo->buffer != 0;
+	return fifo->buffer != NULL;
 }
 
 /**
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 8fa7187..a177698 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -211,11 +211,9 @@
 		__u32		wakeup_watermark; /* bytes before wakeup   */
 	};
 
-	__u32			__reserved_2;
-
-	__u64			bp_addr;
 	__u32			bp_type;
-	__u32			bp_len;
+	__u64			bp_addr;
+	__u64			bp_len;
 };
 
 /*
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index ca24e7f..061f16d 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -16,6 +16,8 @@
 
 #include <linux/list.h>
 
+#include <sound/soc.h>
+
 struct snd_pcm_substream;
 
 /*
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index c5c95e1d..c0922a0 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -95,6 +95,21 @@
 	.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
 	.num_kcontrols = 1}
 
+/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
+#define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
+	 wcontrols) \
+{	.id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
+#define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \
+	 wcontrols)\
+{	.id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
+#define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \
+	 wcontrols)\
+{       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
+	.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
+	.num_kcontrols = ARRAY_SIZE(wcontrols)}
+
 /* path domain with event - event handler must return 0 for success */
 #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
 	wncontrols, wevent, wflags) \
@@ -126,6 +141,23 @@
 	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
 	.event = wevent, .event_flags = wflags}
 
+/* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
+#define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
+	wevent, wflags) \
+{	.id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
+	.event = wevent, .event_flags = wflags}
+#define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
+	wevent, wflags) \
+{	.id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
+	.event = wevent, .event_flags = wflags}
+#define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \
+	wcontrols, wevent, wflags) \
+{       .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
+	.invert = winvert, .kcontrols = wcontrols, \
+	.num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags}
+
 /* events that are pre and post DAPM */
 #define SND_SOC_DAPM_PRE(wname, wevent) \
 {	.id = snd_soc_dapm_pre, .name = wname, .kcontrols = NULL, \
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 0d7718f9..5d234a8 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -169,6 +169,23 @@
 	.private_value = (unsigned long)&xenum }
 
 /*
+ * Simplified versions of above macros, declaring a struct and calculating
+ * ARRAY_SIZE internally
+ */
+#define SOC_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xtexts) \
+	struct soc_enum name = SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, \
+						ARRAY_SIZE(xtexts), xtexts)
+#define SOC_ENUM_SINGLE_DECL(name, xreg, xshift, xtexts) \
+	SOC_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xtexts)
+#define SOC_ENUM_SINGLE_EXT_DECL(name, xtexts) \
+	struct soc_enum name = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(xtexts), xtexts)
+#define SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift_l, xshift_r, xmask, xtexts, xvalues) \
+	struct soc_enum name = SOC_VALUE_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, \
+							ARRAY_SIZE(xtexts), xtexts, xvalues)
+#define SOC_VALUE_ENUM_SINGLE_DECL(name, xreg, xshift, xmask, xtexts, xvalues) \
+	SOC_VALUE_ENUM_DOUBLE_DECL(name, xreg, xshift, xshift, xmask, xtexts, xvalues)
+
+/*
  * Bias levels
  *
  * @ON:      Bias is fully on for audio playback and capture operations.
@@ -253,6 +270,9 @@
 /* codec register bit access */
 int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
 				unsigned int mask, unsigned int value);
+int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
+			       unsigned short reg, unsigned int mask,
+			       unsigned int value);
 int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
 				unsigned int mask, unsigned int value);
 
@@ -402,6 +422,10 @@
 	short reg_cache_size;
 	short reg_cache_step;
 
+	unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
+	unsigned int cache_only:1;  /* Suppress writes to hardware */
+	unsigned int cache_sync:1; /* Cache needs to be synced to hardware */
+
 	/* dapm */
 	u32 pop_time;
 	struct list_head dapm_widgets;
@@ -497,6 +521,8 @@
 	int (*set_bias_level)(struct snd_soc_card *,
 			      enum snd_soc_bias_level level);
 
+	long pmdown_time;
+
 	/* CPU <--> Codec DAI links  */
 	struct snd_soc_dai_link *dai_link;
 	int num_links;
diff --git a/include/sound/tlv320dac33-plat.h b/include/sound/tlv320dac33-plat.h
index 5858d06..ac06652 100644
--- a/include/sound/tlv320dac33-plat.h
+++ b/include/sound/tlv320dac33-plat.h
@@ -15,6 +15,7 @@
 
 struct tlv320dac33_platform_data {
 	int power_gpio;
+	u8 burst_bclkdiv;
 };
 
 #endif /* __TLV320DAC33_PLAT_H */
diff --git a/include/sound/tpa6130a2-plat.h b/include/sound/tpa6130a2-plat.h
index e8c901e..e29fde6 100644
--- a/include/sound/tpa6130a2-plat.h
+++ b/include/sound/tpa6130a2-plat.h
@@ -23,7 +23,13 @@
 #ifndef TPA6130A2_PLAT_H
 #define TPA6130A2_PLAT_H
 
+enum tpa_model {
+	TPA6130A2,
+	TPA6140A2,
+};
+
 struct tpa6130a2_platform_data {
+	enum tpa_model id;
 	int power_gpio;
 };
 
diff --git a/include/sound/wm2000.h b/include/sound/wm2000.h
new file mode 100644
index 0000000..aa388ca
--- /dev/null
+++ b/include/sound/wm2000.h
@@ -0,0 +1,26 @@
+/*
+ * linux/sound/wm2000.h -- Platform data for WM2000
+ *
+ * Copyright 2010 Wolfson Microelectronics. PLC.
+ *
+ * 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 __LINUX_SND_WM2000_H
+#define __LINUX_SND_WM2000_H
+
+struct wm2000_platform_data {
+	/** Filename for system-specific image to download to device. */
+	const char *download_file;
+
+	/** Divide MCLK by 2 for system clock? */
+	unsigned int mclkdiv2:1;
+
+	/** Disable speech clarity enhancement, for use when an
+	 * external algorithm is used. */
+	unsigned int speech_enh_disable:1;
+};
+
+#endif
diff --git a/include/sound/wm8904.h b/include/sound/wm8904.h
new file mode 100644
index 0000000..d66575a
--- /dev/null
+++ b/include/sound/wm8904.h
@@ -0,0 +1,57 @@
+/*
+ * Platform data for WM8904
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __MFD_WM8994_PDATA_H__
+#define __MFD_WM8994_PDATA_H__
+
+#define WM8904_DRC_REGS 4
+#define WM8904_EQ_REGS  25
+
+/**
+ * DRC configurations are specified with a label and a set of register
+ * values to write (the enable bits will be ignored).  At runtime an
+ * enumerated control will be presented for each DRC block allowing
+ * the user to choose the configration to use.
+ *
+ * Configurations may be generated by hand or by using the DRC control
+ * panel provided by the WISCE - see  http://www.wolfsonmicro.com/wisce/
+ * for details.
+ */
+struct wm8904_drc_cfg {
+	const char *name;
+	u16 regs[WM8904_DRC_REGS];
+};
+
+/**
+ * ReTune Mobile configurations are specified with a label, sample
+ * rate and set of values to write (the enable bits will be ignored).
+ *
+ * Configurations are expected to be generated using the ReTune Mobile
+ * control panel in WISCE - see http://www.wolfsonmicro.com/wisce/
+ */
+struct wm8904_retune_mobile_cfg {
+	const char *name;
+	unsigned int rate;
+	u16 regs[WM8904_EQ_REGS];
+};
+
+struct wm8904_pdata {
+	int num_drc_cfgs;
+	struct wm8904_drc_cfg *drc_cfgs;
+
+	int num_retune_mobile_cfgs;
+	struct wm8904_retune_mobile_cfg *retune_mobile_cfgs;
+};
+
+#endif
diff --git a/include/sound/wm8955.h b/include/sound/wm8955.h
new file mode 100644
index 0000000..5074ef4
--- /dev/null
+++ b/include/sound/wm8955.h
@@ -0,0 +1,26 @@
+/*
+ * Platform data for WM8955
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef __WM8955_PDATA_H__
+#define __WM8955_PDATA_H__
+
+struct wm8955_pdata {
+	/* Configure LOUT2/ROUT2 to drive a speaker */
+	unsigned int out2_speaker:1;
+
+	/* Configure MONOIN+/- in differential mode */
+	unsigned int monoin_diff:1;
+};
+
+#endif
diff --git a/kernel/hw_breakpoint.c b/kernel/hw_breakpoint.c
index 8a5c7d5..967e6614 100644
--- a/kernel/hw_breakpoint.c
+++ b/kernel/hw_breakpoint.c
@@ -360,8 +360,8 @@
 int modify_user_hw_breakpoint(struct perf_event *bp, struct perf_event_attr *attr)
 {
 	u64 old_addr = bp->attr.bp_addr;
+	u64 old_len = bp->attr.bp_len;
 	int old_type = bp->attr.bp_type;
-	int old_len = bp->attr.bp_len;
 	int err = 0;
 
 	perf_event_disable(bp);
diff --git a/kernel/kfifo.c b/kernel/kfifo.c
index 498cabb..35edbe22 100644
--- a/kernel/kfifo.c
+++ b/kernel/kfifo.c
@@ -80,7 +80,7 @@
 
 	buffer = kmalloc(size, gfp_mask);
 	if (!buffer) {
-		_kfifo_init(fifo, 0, 0);
+		_kfifo_init(fifo, NULL, 0);
 		return -ENOMEM;
 	}
 
@@ -97,6 +97,7 @@
 void kfifo_free(struct kfifo *fifo)
 {
 	kfree(fifo->buffer);
+	_kfifo_init(fifo, NULL, 0);
 }
 EXPORT_SYMBOL(kfifo_free);
 
diff --git a/kernel/perf_event.c b/kernel/perf_event.c
index d27746b..2ae7409 100644
--- a/kernel/perf_event.c
+++ b/kernel/perf_event.c
@@ -3259,8 +3259,6 @@
 	task_event->event_id.tid = perf_event_tid(event, task);
 	task_event->event_id.ptid = perf_event_tid(event, current);
 
-	task_event->event_id.time = perf_clock();
-
 	perf_output_put(&handle, task_event->event_id);
 
 	perf_output_end(&handle);
@@ -3268,7 +3266,7 @@
 
 static int perf_event_task_match(struct perf_event *event)
 {
-	if (event->state != PERF_EVENT_STATE_ACTIVE)
+	if (event->state < PERF_EVENT_STATE_INACTIVE)
 		return 0;
 
 	if (event->cpu != -1 && event->cpu != smp_processor_id())
@@ -3300,7 +3298,7 @@
 	cpuctx = &get_cpu_var(perf_cpu_context);
 	perf_event_task_ctx(&cpuctx->ctx, task_event);
 	if (!ctx)
-		ctx = rcu_dereference(task_event->task->perf_event_ctxp);
+		ctx = rcu_dereference(current->perf_event_ctxp);
 	if (ctx)
 		perf_event_task_ctx(ctx, task_event);
 	put_cpu_var(perf_cpu_context);
@@ -3331,6 +3329,7 @@
 			/* .ppid */
 			/* .tid  */
 			/* .ptid */
+			.time = perf_clock(),
 		},
 	};
 
@@ -3380,7 +3379,7 @@
 
 static int perf_event_comm_match(struct perf_event *event)
 {
-	if (event->state != PERF_EVENT_STATE_ACTIVE)
+	if (event->state < PERF_EVENT_STATE_INACTIVE)
 		return 0;
 
 	if (event->cpu != -1 && event->cpu != smp_processor_id())
@@ -3500,7 +3499,7 @@
 static int perf_event_mmap_match(struct perf_event *event,
 				   struct perf_mmap_event *mmap_event)
 {
-	if (event->state != PERF_EVENT_STATE_ACTIVE)
+	if (event->state < PERF_EVENT_STATE_INACTIVE)
 		return 0;
 
 	if (event->cpu != -1 && event->cpu != smp_processor_id())
@@ -4580,7 +4579,7 @@
 	if (attr->type >= PERF_TYPE_MAX)
 		return -EINVAL;
 
-	if (attr->__reserved_1 || attr->__reserved_2)
+	if (attr->__reserved_1)
 		return -EINVAL;
 
 	if (attr->sample_type & ~(PERF_SAMPLE_MAX-1))
diff --git a/kernel/softirq.c b/kernel/softirq.c
index a09502e..7c1a67e 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -500,22 +500,17 @@
  */
 
 /*
- * The trampoline is called when the hrtimer expires. If this is
- * called from the hrtimer interrupt then we schedule the tasklet as
- * the timer callback function expects to run in softirq context. If
- * it's called in softirq context anyway (i.e. high resolution timers
- * disabled) then the hrtimer callback is called right away.
+ * The trampoline is called when the hrtimer expires. It schedules a tasklet
+ * to run __tasklet_hrtimer_trampoline() which in turn will call the intended
+ * hrtimer callback, but from softirq context.
  */
 static enum hrtimer_restart __hrtimer_tasklet_trampoline(struct hrtimer *timer)
 {
 	struct tasklet_hrtimer *ttimer =
 		container_of(timer, struct tasklet_hrtimer, timer);
 
-	if (hrtimer_is_hres_active(timer)) {
-		tasklet_hi_schedule(&ttimer->tasklet);
-		return HRTIMER_NORESTART;
-	}
-	return ttimer->function(timer);
+	tasklet_hi_schedule(&ttimer->tasklet);
+	return HRTIMER_NORESTART;
 }
 
 /*
diff --git a/kernel/sys.c b/kernel/sys.c
index 26a6b73..18bde97 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -222,6 +222,7 @@
 	if (which > PRIO_USER || which < PRIO_PROCESS)
 		return -EINVAL;
 
+	rcu_read_lock();
 	read_lock(&tasklist_lock);
 	switch (which) {
 		case PRIO_PROCESS:
@@ -267,6 +268,7 @@
 	}
 out_unlock:
 	read_unlock(&tasklist_lock);
+	rcu_read_unlock();
 
 	return retval;
 }
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index 6ea90c0..50b1b82 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -689,7 +689,7 @@
 			return -EINVAL;
 		}
 		/* an address specified */
-		ret = strict_strtoul(&argv[0][2], 0, (unsigned long *)&addr);
+		ret = strict_strtoul(&argv[1][0], 0, (unsigned long *)&addr);
 		if (ret) {
 			pr_info("Failed to parse address.\n");
 			return ret;
diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c
index 678a512..f4bc9b2 100644
--- a/kernel/trace/trace_stack.c
+++ b/kernel/trace/trace_stack.c
@@ -157,6 +157,7 @@
 	unsigned long val, flags;
 	char buf[64];
 	int ret;
+	int cpu;
 
 	if (count >= sizeof(buf))
 		return -EINVAL;
@@ -171,9 +172,20 @@
 		return ret;
 
 	local_irq_save(flags);
+
+	/*
+	 * In case we trace inside arch_spin_lock() or after (NMI),
+	 * we will cause circular lock, so we also need to increase
+	 * the percpu trace_active here.
+	 */
+	cpu = smp_processor_id();
+	per_cpu(trace_active, cpu)++;
+
 	arch_spin_lock(&max_stack_lock);
 	*ptr = val;
 	arch_spin_unlock(&max_stack_lock);
+
+	per_cpu(trace_active, cpu)--;
 	local_irq_restore(flags);
 
 	return count;
@@ -206,7 +218,13 @@
 
 static void *t_start(struct seq_file *m, loff_t *pos)
 {
+	int cpu;
+
 	local_irq_disable();
+
+	cpu = smp_processor_id();
+	per_cpu(trace_active, cpu)++;
+
 	arch_spin_lock(&max_stack_lock);
 
 	if (*pos == 0)
@@ -217,7 +235,13 @@
 
 static void t_stop(struct seq_file *m, void *p)
 {
+	int cpu;
+
 	arch_spin_unlock(&max_stack_lock);
+
+	cpu = smp_processor_id();
+	per_cpu(trace_active, cpu)--;
+
 	local_irq_enable();
 }
 
diff --git a/lib/idr.c b/lib/idr.c
index 1cac726..0dc7822 100644
--- a/lib/idr.c
+++ b/lib/idr.c
@@ -156,10 +156,12 @@
 			id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1;
 
 			/* if already at the top layer, we need to grow */
-			if (!(p = pa[l])) {
+			if (id >= 1 << (idp->layers * IDR_BITS)) {
 				*starting_id = id;
 				return IDR_NEED_TO_GROW;
 			}
+			p = pa[l];
+			BUG_ON(!p);
 
 			/* If we need to go up one layer, continue the
 			 * loop; otherwise, restart from the top.
diff --git a/mm/migrate.c b/mm/migrate.c
index 9a0db5b..880bd59 100644
--- a/mm/migrate.c
+++ b/mm/migrate.c
@@ -1002,33 +1002,27 @@
 #define DO_PAGES_STAT_CHUNK_NR 16
 	const void __user *chunk_pages[DO_PAGES_STAT_CHUNK_NR];
 	int chunk_status[DO_PAGES_STAT_CHUNK_NR];
-	unsigned long i, chunk_nr = DO_PAGES_STAT_CHUNK_NR;
-	int err;
 
-	for (i = 0; i < nr_pages; i += chunk_nr) {
-		if (chunk_nr > nr_pages - i)
-			chunk_nr = nr_pages - i;
+	while (nr_pages) {
+		unsigned long chunk_nr;
 
-		err = copy_from_user(chunk_pages, &pages[i],
-				     chunk_nr * sizeof(*chunk_pages));
-		if (err) {
-			err = -EFAULT;
-			goto out;
-		}
+		chunk_nr = nr_pages;
+		if (chunk_nr > DO_PAGES_STAT_CHUNK_NR)
+			chunk_nr = DO_PAGES_STAT_CHUNK_NR;
+
+		if (copy_from_user(chunk_pages, pages, chunk_nr * sizeof(*chunk_pages)))
+			break;
 
 		do_pages_stat_array(mm, chunk_nr, chunk_pages, chunk_status);
 
-		err = copy_to_user(&status[i], chunk_status,
-				   chunk_nr * sizeof(*chunk_status));
-		if (err) {
-			err = -EFAULT;
-			goto out;
-		}
-	}
-	err = 0;
+		if (copy_to_user(status, chunk_status, chunk_nr * sizeof(*status)))
+			break;
 
-out:
-	return err;
+		pages += chunk_nr;
+		status += chunk_nr;
+		nr_pages -= chunk_nr;
+	}
+	return nr_pages ? -EFAULT : 0;
 }
 
 /*
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index f52481b..2370504 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -459,6 +459,8 @@
 	list_for_each_entry(c, &p->children, sibling) {
 		if (c->mm == p->mm)
 			continue;
+		if (mem && !task_in_mem_cgroup(c, mem))
+			continue;
 		if (!oom_kill_task(c))
 			return 0;
 	}
diff --git a/net/core/dev.c b/net/core/dev.c
index be9924f..ec87421 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2761,7 +2761,7 @@
 	switch (ret) {
 	case GRO_NORMAL:
 	case GRO_HELD:
-		skb->protocol = eth_type_trans(skb, napi->dev);
+		skb->protocol = eth_type_trans(skb, skb->dev);
 
 		if (ret == GRO_HELD)
 			skb_gro_pull(skb, -ETH_HLEN);
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index d8aee58..236a998 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -927,6 +927,7 @@
 	case ETHTOOL_GPERMADDR:
 	case ETHTOOL_GUFO:
 	case ETHTOOL_GGSO:
+	case ETHTOOL_GGRO:
 	case ETHTOOL_GFLAGS:
 	case ETHTOOL_GPFLAGS:
 	case ETHTOOL_GRXFH:
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c
index fbc1c74..099c753 100644
--- a/net/core/net-sysfs.c
+++ b/net/core/net-sysfs.c
@@ -410,7 +410,8 @@
 	const struct iw_statistics *iw;
 	ssize_t ret = -EINVAL;
 
-	rtnl_lock();
+	if (!rtnl_trylock())
+		return restart_syscall();
 	if (dev_isalive(dev)) {
 		iw = get_wireless_stats(dev);
 		if (iw)
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 040c4f0..26dec2b 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -1317,14 +1317,19 @@
 {
 	int *valp = ctl->data;
 	int val = *valp;
+	loff_t pos = *ppos;
 	int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
 	if (write && *valp != val) {
 		struct net *net = ctl->extra2;
 
 		if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
-			if (!rtnl_trylock())
+			if (!rtnl_trylock()) {
+				/* Restore the original values before restarting */
+				*valp = val;
+				*ppos = pos;
 				return restart_syscall();
+			}
 			if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
 				inet_forward_change(net);
 			} else if (*valp) {
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 76c0840..a42f658 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -946,7 +946,6 @@
 		break;
 	case IGMP_HOST_MEMBERSHIP_REPORT:
 	case IGMPV2_HOST_MEMBERSHIP_REPORT:
-	case IGMPV3_HOST_MEMBERSHIP_REPORT:
 		/* Is it our report looped back? */
 		if (skb_rtable(skb)->fl.iif == 0)
 			break;
@@ -960,6 +959,7 @@
 		in_dev_put(in_dev);
 		return pim_rcv_v1(skb);
 #endif
+	case IGMPV3_HOST_MEMBERSHIP_REPORT:
 	case IGMP_DVMRP:
 	case IGMP_TRACE:
 	case IGMP_HOST_LEAVE_MESSAGE:
diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c
index 38fbf04..544ce08 100644
--- a/net/ipv4/ipcomp.c
+++ b/net/ipv4/ipcomp.c
@@ -124,16 +124,12 @@
 	if (x->props.mode == XFRM_MODE_TUNNEL) {
 		err = ipcomp_tunnel_attach(x);
 		if (err)
-			goto error_tunnel;
+			goto out;
 	}
 
 	err = 0;
 out:
 	return err;
-
-error_tunnel:
-	ipcomp_destroy(x);
-	goto out;
 }
 
 static const struct xfrm_type ipcomp_type = {
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 28e0296..3fddc69 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5783,11 +5783,9 @@
 
 				/* tcp_ack considers this ACK as duplicate
 				 * and does not calculate rtt.
-				 * Fix it at least with timestamps.
+				 * Force it here.
 				 */
-				if (tp->rx_opt.saw_tstamp &&
-				    tp->rx_opt.rcv_tsecr && !tp->srtt)
-					tcp_ack_saw_tstamp(sk, 0);
+				tcp_ack_update_rtt(sk, 0, 0);
 
 				if (tp->rx_opt.tstamp_ok)
 					tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index de7a194..143791d 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -502,8 +502,11 @@
 	if (p == &net->ipv6.devconf_dflt->forwarding)
 		return 0;
 
-	if (!rtnl_trylock())
+	if (!rtnl_trylock()) {
+		/* Restore the original values before restarting */
+		*p = old;
 		return restart_syscall();
+	}
 
 	if (p == &net->ipv6.devconf_all->forwarding) {
 		__s32 newf = net->ipv6.devconf_all->forwarding;
@@ -4028,12 +4031,15 @@
 {
 	int *valp = ctl->data;
 	int val = *valp;
+	loff_t pos = *ppos;
 	int ret;
 
 	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
 	if (write)
 		ret = addrconf_fixup_forwarding(ctl, valp, val);
+	if (ret)
+		*ppos = pos;
 	return ret;
 }
 
@@ -4075,8 +4081,11 @@
 	if (p == &net->ipv6.devconf_dflt->disable_ipv6)
 		return 0;
 
-	if (!rtnl_trylock())
+	if (!rtnl_trylock()) {
+		/* Restore the original values before restarting */
+		*p = old;
 		return restart_syscall();
+	}
 
 	if (p == &net->ipv6.devconf_all->disable_ipv6) {
 		__s32 newf = net->ipv6.devconf_all->disable_ipv6;
@@ -4095,12 +4104,15 @@
 {
 	int *valp = ctl->data;
 	int val = *valp;
+	loff_t pos = *ppos;
 	int ret;
 
 	ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
 	if (write)
 		ret = addrconf_disable_ipv6(ctl, valp, val);
+	if (ret)
+		*ppos = pos;
 	return ret;
 }
 
diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c
index 2f2a5ca..002e6ee 100644
--- a/net/ipv6/ipcomp6.c
+++ b/net/ipv6/ipcomp6.c
@@ -154,16 +154,12 @@
 	if (x->props.mode == XFRM_MODE_TUNNEL) {
 		err = ipcomp6_tunnel_attach(x);
 		if (err)
-			goto error_tunnel;
+			goto out;
 	}
 
 	err = 0;
 out:
 	return err;
-error_tunnel:
-	ipcomp_destroy(x);
-
-	goto out;
 }
 
 static const struct xfrm_type ipcomp6_type =
diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c
index 1f2db64..22f0c2a 100644
--- a/net/mac80211/ibss.c
+++ b/net/mac80211/ibss.c
@@ -647,7 +647,7 @@
 	}
 	if (pos[1] != 0 &&
 	    (pos[1] != ifibss->ssid_len ||
-	     !memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
+	     memcmp(pos + 2, ifibss->ssid, ifibss->ssid_len))) {
 		/* Ignore ProbeReq for foreign SSID */
 		return;
 	}
diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c
index b9007f8..12a2bff 100644
--- a/net/mac80211/rate.c
+++ b/net/mac80211/rate.c
@@ -245,6 +245,9 @@
 		info->control.rates[i].count = 1;
 	}
 
+	if (sdata->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
+		return;
+
 	if (sta && sdata->force_unicast_rateidx > -1) {
 		info->control.rates[0].idx = sdata->force_unicast_rateidx;
 	} else {
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index f934c96..bc17cf7 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -439,6 +439,16 @@
 	if (local->scan_req)
 		return -EBUSY;
 
+	if (req != local->int_scan_req &&
+	    sdata->vif.type == NL80211_IFTYPE_STATION &&
+	    !list_empty(&ifmgd->work_list)) {
+		/* actually wait for the work it's doing to finish/time out */
+		set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request);
+		local->scan_req = req;
+		local->scan_sdata = sdata;
+		return 0;
+	}
+
 	if (local->ops->hw_scan) {
 		u8 *ies;
 
@@ -463,14 +473,6 @@
 	local->scan_req = req;
 	local->scan_sdata = sdata;
 
-	if (req != local->int_scan_req &&
-	    sdata->vif.type == NL80211_IFTYPE_STATION &&
-	    !list_empty(&ifmgd->work_list)) {
-		/* actually wait for the work it's doing to finish/time out */
-		set_bit(IEEE80211_STA_REQ_SCAN, &ifmgd->request);
-		return 0;
-	}
-
 	if (local->ops->hw_scan)
 		__set_bit(SCAN_HW_SCANNING, &local->scanning);
 	else
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index b36cc34..f445ea1 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -1102,7 +1102,7 @@
 	int err = -ENOMEM;
 	struct xfrm_state *x = xfrm_state_alloc(net);
 	if (!x)
-		goto error;
+		goto out;
 
 	memcpy(&x->id, &orig->id, sizeof(x->id));
 	memcpy(&x->sel, &orig->sel, sizeof(x->sel));
@@ -1160,16 +1160,10 @@
 	return x;
 
  error:
+	xfrm_state_put(x);
+out:
 	if (errp)
 		*errp = err;
-	if (x) {
-		kfree(x->aalg);
-		kfree(x->ealg);
-		kfree(x->calg);
-		kfree(x->encap);
-		kfree(x->coaddr);
-	}
-	kfree(x);
 	return NULL;
 }
 
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index 556cff9..567348b 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -157,7 +157,7 @@
 
 config SND_HDA_ELD
 	def_bool y
-	depends on SND_HDA_CODEC_INTELHDMI
+	depends on SND_HDA_CODEC_INTELHDMI || SND_HDA_CODEC_NVHDMI
 
 config SND_HDA_CODEC_CIRRUS
 	bool "Build Cirrus Logic codec support"
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 315a1c4..24bc195 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -3,7 +3,7 @@
 snd-hda-codec-y := hda_codec.o
 snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
 snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
-# snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
+snd-hda-codec-$(CONFIG_SND_HDA_ELD) += hda_eld.o
 snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
 snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o
 
@@ -18,7 +18,7 @@
 snd-hda-codec-conexant-objs :=	patch_conexant.o
 snd-hda-codec-via-objs :=	patch_via.o
 snd-hda-codec-nvhdmi-objs :=	patch_nvhdmi.o
-snd-hda-codec-intelhdmi-objs :=	patch_intelhdmi.o hda_eld.o
+snd-hda-codec-intelhdmi-objs :=	patch_intelhdmi.o
 
 # common driver
 obj-$(CONFIG_SND_HDA_INTEL) := snd-hda-codec.o
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 26ceace..5bd7cf4 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -978,8 +978,9 @@
  *
  * Returns 0 if successful, or a negative error code.
  */
-int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
-				    struct hda_codec **codecp)
+int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
+				unsigned int codec_addr,
+				struct hda_codec **codecp)
 {
 	struct hda_codec *codec;
 	char component[31];
@@ -1186,7 +1187,7 @@
  */
 
 /* FIXME: more better hash key? */
-#define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
+#define HDA_HASH_KEY(nid, dir, idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
 #define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
 #define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
 #define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
@@ -1356,7 +1357,8 @@
 	if (!codec->no_trigger_sense) {
 		pincap = snd_hda_query_pin_caps(codec, nid);
 		if (pincap & AC_PINCAP_TRIG_REQ) /* need trigger? */
-			snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+			snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_SET_PIN_SENSE, 0);
 	}
 	return snd_hda_codec_read(codec, nid, 0,
 				  AC_VERB_GET_PIN_SENSE, 0);
@@ -1372,8 +1374,8 @@
  */
 int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
 {
-        u32 sense = snd_hda_pin_sense(codec, nid);
-        return !!(sense & AC_PINSENSE_PRESENCE);
+	u32 sense = snd_hda_pin_sense(codec, nid);
+	return !!(sense & AC_PINSENSE_PRESENCE);
 }
 EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
 
@@ -1952,7 +1954,7 @@
 	err = snd_hda_ctl_add(codec, 0, kctl);
 	if (err < 0)
 		return err;
-	
+
 	for (s = slaves; *s; s++) {
 		struct snd_kcontrol *sctl;
 		int i = 0;
@@ -2439,27 +2441,27 @@
 	{
 		.access = SNDRV_CTL_ELEM_ACCESS_READ,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
 		.info = snd_hda_spdif_mask_info,
 		.get = snd_hda_spdif_cmask_get,
 	},
 	{
 		.access = SNDRV_CTL_ELEM_ACCESS_READ,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
 		.info = snd_hda_spdif_mask_info,
 		.get = snd_hda_spdif_pmask_get,
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
 		.info = snd_hda_spdif_mask_info,
 		.get = snd_hda_spdif_default_get,
 		.put = snd_hda_spdif_default_put,
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
+		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, SWITCH),
 		.info = snd_hda_spdif_out_switch_info,
 		.get = snd_hda_spdif_out_switch_get,
 		.put = snd_hda_spdif_out_switch_put,
@@ -2610,7 +2612,7 @@
 static struct snd_kcontrol_new dig_in_ctls[] = {
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH),
+		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, SWITCH),
 		.info = snd_hda_spdif_in_switch_info,
 		.get = snd_hda_spdif_in_switch_get,
 		.put = snd_hda_spdif_in_switch_put,
@@ -2618,7 +2620,7 @@
 	{
 		.access = SNDRV_CTL_ELEM_ACCESS_READ,
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
+		.name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
 		.info = snd_hda_spdif_mask_info,
 		.get = snd_hda_spdif_in_status_get,
 	},
@@ -2767,7 +2769,8 @@
 	snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
 			    power_state);
 	/* partial workaround for "azx_get_response timeout" */
-	if (power_state == AC_PWRST_D0)
+	if (power_state == AC_PWRST_D0 &&
+	    (codec->vendor_id & 0xffff0000) == 0x14f10000)
 		msleep(10);
 
 	nid = codec->start_nid;
@@ -2801,7 +2804,6 @@
 	if (power_state == AC_PWRST_D0) {
 		unsigned long end_time;
 		int state;
-		msleep(10);
 		/* wait until the codec reachs to D0 */
 		end_time = jiffies + msecs_to_jiffies(500);
 		do {
@@ -2883,7 +2885,7 @@
 		int err = snd_hda_codec_build_controls(codec);
 		if (err < 0) {
 			printk(KERN_ERR "hda_codec: cannot build controls"
-			       "for #%d (error %d)\n", codec->addr, err); 
+			       "for #%d (error %d)\n", codec->addr, err);
 			err = snd_hda_codec_reset(codec);
 			if (err < 0) {
 				printk(KERN_ERR
@@ -2979,8 +2981,12 @@
 	val |= channels - 1;
 
 	switch (snd_pcm_format_width(format)) {
-	case 8:  val |= 0x00; break;
-	case 16: val |= 0x10; break;
+	case 8:
+		val |= 0x00;
+		break;
+	case 16:
+		val |= 0x10;
+		break;
 	case 20:
 	case 24:
 	case 32:
@@ -3275,6 +3281,8 @@
 
 /*
  * get the empty PCM device number to assign
+ *
+ * note the max device number is limited by HDA_MAX_PCMS, currently 10
  */
 static int get_empty_pcm_device(struct hda_bus *bus, int type)
 {
@@ -3296,7 +3304,8 @@
 		if (!test_and_set_bit(audio_idx[type][i], bus->pcm_dev_bits))
 			return audio_idx[type][i];
 
-	snd_printk(KERN_WARNING "Too many %s devices\n", snd_hda_pcm_type_name[type]);
+	snd_printk(KERN_WARNING "Too many %s devices\n",
+		snd_hda_pcm_type_name[type]);
 	return -EAGAIN;
 }
 
@@ -3334,7 +3343,7 @@
 		err = codec->patch_ops.build_pcms(codec);
 		if (err < 0) {
 			printk(KERN_ERR "hda_codec: cannot build PCMs"
-			       "for #%d (error %d)\n", codec->addr, err); 
+			       "for #%d (error %d)\n", codec->addr, err);
 			err = snd_hda_codec_reset(codec);
 			if (err < 0) {
 				printk(KERN_ERR
@@ -3464,8 +3473,8 @@
 
 /**
  * snd_hda_check_board_codec_sid_config - compare the current codec
-				          subsystem ID with the
-					  config table
+					subsystem ID with the
+					config table
 
 	   This is important for Gateway notebooks with SB450 HDA Audio
 	   where the vendor ID of the PCI device is:
@@ -3605,7 +3614,7 @@
  *
  * Increment the power-up counter and power up the hardware really when
  * not turned on yet.
- */ 
+ */
 void snd_hda_power_up(struct hda_codec *codec)
 {
 	struct hda_bus *bus = codec->bus;
@@ -3634,7 +3643,7 @@
  *
  * Decrement the power-up counter and schedules the power-off work if
  * the counter rearches to zero.
- */ 
+ */
 void snd_hda_power_down(struct hda_codec *codec)
 {
 	--codec->power_count;
@@ -3660,7 +3669,7 @@
  *
  * This function is supposed to be set or called from the check_power_status
  * patch ops.
- */ 
+ */
 int snd_hda_check_amp_list_power(struct hda_codec *codec,
 				 struct hda_loopback_check *check,
 				 hda_nid_t nid)
@@ -3828,7 +3837,7 @@
 {
 	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
 	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
-		set_dig_out_convert(codec, nid, 
+		set_dig_out_convert(codec, nid,
 				    codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff,
 				    -1);
 	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
@@ -4087,13 +4096,13 @@
 /*
  * Sort an associated group of pins according to their sequence numbers.
  */
-static void sort_pins_by_sequence(hda_nid_t * pins, short * sequences,
+static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
 				  int num_pins)
 {
 	int i, j;
 	short seq;
 	hda_nid_t nid;
-	
+
 	for (i = 0; i < num_pins; i++) {
 		for (j = i + 1; j < num_pins; j++) {
 			if (sequences[i] > sequences[j]) {
@@ -4121,7 +4130,7 @@
  * is detected, one of speaker of HP pins is assigned as the primary
  * output, i.e. to line_out_pins[0].  So, line_outs is always positive
  * if any analog output exists.
- * 
+ *
  * The analog input pins are assigned to input_pins array.
  * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
  * respectively.
@@ -4184,9 +4193,9 @@
 		case AC_JACK_SPEAKER:
 			seq = get_defcfg_sequence(def_conf);
 			assoc = get_defcfg_association(def_conf);
-			if (! assoc)
+			if (!assoc)
 				continue;
-			if (! assoc_speaker)
+			if (!assoc_speaker)
 				assoc_speaker = assoc;
 			else if (assoc_speaker != assoc)
 				continue;
@@ -4284,7 +4293,7 @@
 			      cfg->speaker_outs);
 	sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
 			      cfg->hp_outs);
-	
+
 	/* if we have only one mic, make it AUTO_PIN_MIC */
 	if (!cfg->input_pins[AUTO_PIN_MIC] &&
 	    cfg->input_pins[AUTO_PIN_FRONT_MIC]) {
@@ -4434,7 +4443,7 @@
 /**
  * snd_array_new - get a new element from the given array
  * @array: the array object
- * 
+ *
  * Get a new element from the given array.  If it exceeds the
  * pre-allocated array size, re-allocate the array.
  *
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 0c8f05c..b75da47 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -527,6 +527,9 @@
 /* max. codec address */
 #define HDA_MAX_CODEC_ADDRESS	0x0f
 
+/* max number of PCM devics per card */
+#define HDA_MAX_PCMS		10
+
 /*
  * generic arrays
  */
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index 4228f2f..dcd2244 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -331,6 +331,7 @@
 	return snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
 						 AC_DIPSIZE_ELD_BUF);
 }
+EXPORT_SYMBOL_HDA(snd_hdmi_get_eld_size);
 
 int snd_hdmi_get_eld(struct hdmi_eld *eld,
 		     struct hda_codec *codec, hda_nid_t nid)
@@ -366,6 +367,7 @@
 	kfree(buf);
 	return ret;
 }
+EXPORT_SYMBOL_HDA(snd_hdmi_get_eld);
 
 static void hdmi_show_short_audio_desc(struct cea_sad *a)
 {
@@ -404,6 +406,7 @@
 	}
 	buf[j] = '\0';	/* necessary when j == 0 */
 }
+EXPORT_SYMBOL_HDA(snd_print_channel_allocation);
 
 void snd_hdmi_show_eld(struct hdmi_eld *e)
 {
@@ -422,6 +425,7 @@
 	for (i = 0; i < e->sad_count; i++)
 		hdmi_show_short_audio_desc(e->sad + i);
 }
+EXPORT_SYMBOL_HDA(snd_hdmi_show_eld);
 
 #ifdef CONFIG_PROC_FS
 
@@ -580,6 +584,7 @@
 
 	return 0;
 }
+EXPORT_SYMBOL_HDA(snd_hda_eld_proc_new);
 
 void snd_hda_eld_proc_free(struct hda_codec *codec, struct hdmi_eld *eld)
 {
@@ -588,5 +593,6 @@
 		eld->proc_entry = NULL;
 	}
 }
+EXPORT_SYMBOL_HDA(snd_hda_eld_proc_free);
 
 #endif /* CONFIG_PROC_FS */
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index b36919c..a1fc837 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -625,6 +625,10 @@
 	LINE_MODE_PINCFG,
 	LINE_MODE_VERB,
 	LINE_MODE_HINT,
+	LINE_MODE_VENDOR_ID,
+	LINE_MODE_SUBSYSTEM_ID,
+	LINE_MODE_REVISION_ID,
+	LINE_MODE_CHIP_NAME,
 	NUM_LINE_MODES,
 };
 
@@ -654,53 +658,71 @@
 }
 
 /* parse the contents after the other command tags, [pincfg], [verb],
- * [hint] and [model]
+ * [vendor_id], [subsystem_id], [revision_id], [chip_name], [hint] and [model]
  * just pass to the sysfs helper (only when any codec was specified)
  */
 static void parse_pincfg_mode(char *buf, struct hda_bus *bus,
 			      struct hda_codec **codecp)
 {
-	if (!*codecp)
-		return;
 	parse_user_pin_configs(*codecp, buf);
 }
 
 static void parse_verb_mode(char *buf, struct hda_bus *bus,
 			    struct hda_codec **codecp)
 {
-	if (!*codecp)
-		return;
 	parse_init_verbs(*codecp, buf);
 }
 
 static void parse_hint_mode(char *buf, struct hda_bus *bus,
 			    struct hda_codec **codecp)
 {
-	if (!*codecp)
-		return;
 	parse_hints(*codecp, buf);
 }
 
 static void parse_model_mode(char *buf, struct hda_bus *bus,
 			     struct hda_codec **codecp)
 {
-	if (!*codecp)
-		return;
 	kfree((*codecp)->modelname);
 	(*codecp)->modelname = kstrdup(buf, GFP_KERNEL);
 }
 
+static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
+				 struct hda_codec **codecp)
+{
+	kfree((*codecp)->chip_name);
+	(*codecp)->chip_name = kstrdup(buf, GFP_KERNEL);
+}
+
+#define DEFINE_PARSE_ID_MODE(name) \
+static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
+				 struct hda_codec **codecp) \
+{ \
+	unsigned long val; \
+	if (!strict_strtoul(buf, 0, &val)) \
+		(*codecp)->name = val; \
+}
+
+DEFINE_PARSE_ID_MODE(vendor_id);
+DEFINE_PARSE_ID_MODE(subsystem_id);
+DEFINE_PARSE_ID_MODE(revision_id);
+
+
 struct hda_patch_item {
 	const char *tag;
 	void (*parser)(char *buf, struct hda_bus *bus, struct hda_codec **retc);
+	int need_codec;
 };
 
 static struct hda_patch_item patch_items[NUM_LINE_MODES] = {
-	[LINE_MODE_CODEC] = { "[codec]", parse_codec_mode },
-	[LINE_MODE_MODEL] = { "[model]", parse_model_mode },
-	[LINE_MODE_VERB] = { "[verb]", parse_verb_mode },
-	[LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode },
-	[LINE_MODE_HINT] = { "[hint]", parse_hint_mode },
+	[LINE_MODE_CODEC] = { "[codec]", parse_codec_mode, 0 },
+	[LINE_MODE_MODEL] = { "[model]", parse_model_mode, 1 },
+	[LINE_MODE_VERB] = { "[verb]", parse_verb_mode, 1 },
+	[LINE_MODE_PINCFG] = { "[pincfg]", parse_pincfg_mode, 1 },
+	[LINE_MODE_HINT] = { "[hint]", parse_hint_mode, 1 },
+	[LINE_MODE_VENDOR_ID] = { "[vendor_id]", parse_vendor_id_mode, 1 },
+	[LINE_MODE_SUBSYSTEM_ID] = { "[subsystem_id]", parse_subsystem_id_mode, 1 },
+	[LINE_MODE_REVISION_ID] = { "[revision_id]", parse_revision_id_mode, 1 },
+	[LINE_MODE_CHIP_NAME] = { "[chip_name]", parse_chip_name_mode, 1 },
 };
 
 /* check the line starting with '[' -- change the parser mode accodingly */
@@ -783,7 +805,8 @@
 			continue;
 		if (*buf == '[')
 			line_mode = parse_line_mode(buf, bus);
-		else if (patch_items[line_mode].parser)
+		else if (patch_items[line_mode].parser &&
+			 (codec || !patch_items[line_mode].need_codec))
 			patch_items[line_mode].parser(buf, bus, &codec);
 	}
 	release_firmware(fw);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 8d8e0b5..da1ac90 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -125,6 +125,7 @@
 			 "{Intel, ICH9},"
 			 "{Intel, ICH10},"
 			 "{Intel, PCH},"
+			 "{Intel, CPT},"
 			 "{Intel, SCH},"
 			 "{ATI, SB450},"
 			 "{ATI, SB600},"
@@ -259,8 +260,6 @@
 #define AZX_MAX_FRAG		32
 /* max buffer size - no h/w limit, you can increase as you like */
 #define AZX_MAX_BUF_SIZE	(1024*1024*1024)
-/* max number of PCM devics per card */
-#define AZX_MAX_PCMS		8
 
 /* RIRB int mask: overrun[2], response[0] */
 #define RIRB_INT_RESPONSE	0x01
@@ -268,7 +267,8 @@
 #define RIRB_INT_MASK		0x05
 
 /* STATESTS int mask: S3,SD2,SD1,SD0 */
-#define AZX_MAX_CODECS		4
+#define AZX_MAX_CODECS		8
+#define AZX_DEFAULT_CODECS	4
 #define STATESTS_INT_MASK	((1 << AZX_MAX_CODECS) - 1)
 
 /* SD_CTL bits */
@@ -408,7 +408,7 @@
 	struct azx_dev *azx_dev;
 
 	/* PCM */
-	struct snd_pcm *pcm[AZX_MAX_PCMS];
+	struct snd_pcm *pcm[HDA_MAX_PCMS];
 
 	/* HD codec */
 	unsigned short codec_mask;
@@ -449,6 +449,7 @@
 /* driver types */
 enum {
 	AZX_DRIVER_ICH,
+	AZX_DRIVER_PCH,
 	AZX_DRIVER_SCH,
 	AZX_DRIVER_ATI,
 	AZX_DRIVER_ATIHDMI,
@@ -463,6 +464,7 @@
 
 static char *driver_short_names[] __devinitdata = {
 	[AZX_DRIVER_ICH] = "HDA Intel",
+	[AZX_DRIVER_PCH] = "HDA Intel PCH",
 	[AZX_DRIVER_SCH] = "HDA Intel MID",
 	[AZX_DRIVER_ATI] = "HDA ATI SB",
 	[AZX_DRIVER_ATIHDMI] = "HDA ATI HDMI",
@@ -968,8 +970,8 @@
 	azx_dev->insufficient = 1;
 
 	/* enable SIE */
-	azx_writeb(chip, INTCTL,
-		   azx_readb(chip, INTCTL) | (1 << azx_dev->index));
+	azx_writel(chip, INTCTL,
+		   azx_readl(chip, INTCTL) | (1 << azx_dev->index));
 	/* set DMA start and interrupt mask */
 	azx_sd_writeb(azx_dev, SD_CTL, azx_sd_readb(azx_dev, SD_CTL) |
 		      SD_CTL_DMA_START | SD_INT_MASK);
@@ -988,8 +990,8 @@
 {
 	azx_stream_clear(chip, azx_dev);
 	/* disable SIE */
-	azx_writeb(chip, INTCTL,
-		   azx_readb(chip, INTCTL) & ~(1 << azx_dev->index));
+	azx_writel(chip, INTCTL,
+		   azx_readl(chip, INTCTL) & ~(1 << azx_dev->index));
 }
 
 
@@ -1065,6 +1067,7 @@
 				0x01, NVIDIA_HDA_ENABLE_COHBIT);
 		break;
 	case AZX_DRIVER_SCH:
+	case AZX_DRIVER_PCH:
 		pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
 		if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) {
 			pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC,
@@ -1350,7 +1353,7 @@
 	if (chip->initialized) {
 		int i;
 
-		for (i = 0; i < AZX_MAX_PCMS; i++)
+		for (i = 0; i < HDA_MAX_PCMS; i++)
 			snd_pcm_suspend_all(chip->pcm[i]);
 		snd_hda_suspend(chip->bus);
 		snd_hda_resume(chip->bus);
@@ -1365,6 +1368,7 @@
 
 /* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
 static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] __devinitdata = {
+	[AZX_DRIVER_NVIDIA] = 8,
 	[AZX_DRIVER_TERA] = 1,
 };
 
@@ -1397,7 +1401,7 @@
 	codecs = 0;
 	max_slots = azx_max_codecs[chip->driver_type];
 	if (!max_slots)
-		max_slots = AZX_MAX_CODECS;
+		max_slots = AZX_DEFAULT_CODECS;
 
 	/* First try to probe all given codec slots */
 	for (c = 0; c < max_slots; c++) {
@@ -1412,7 +1416,7 @@
 				chip->codec_mask &= ~(1 << c);
 				/* More badly, accessing to a non-existing
 				 * codec often screws up the controller chip,
-				 * and distrubs the further communications.
+				 * and disturbs the further communications.
 				 * Thus if an error occurs during probing,
 				 * better to reset the controller chip to
 				 * get back to the sanity state.
@@ -1983,7 +1987,7 @@
 	int pcm_dev = cpcm->device;
 	int s, err;
 
-	if (pcm_dev >= AZX_MAX_PCMS) {
+	if (pcm_dev >= HDA_MAX_PCMS) {
 		snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
 			   pcm_dev);
 		return -EINVAL;
@@ -2139,7 +2143,7 @@
 
 	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
 	azx_clear_irq_pending(chip);
-	for (i = 0; i < AZX_MAX_PCMS; i++)
+	for (i = 0; i < HDA_MAX_PCMS; i++)
 		snd_pcm_suspend_all(chip->pcm[i]);
 	if (chip->initialized)
 		snd_hda_suspend(chip->bus);
@@ -2261,9 +2265,12 @@
 static struct snd_pci_quirk position_fix_list[] __devinitdata = {
 	SND_PCI_QUIRK(0x1028, 0x01cc, "Dell D820", POS_FIX_LPIB),
 	SND_PCI_QUIRK(0x1028, 0x01de, "Dell Precision 390", POS_FIX_LPIB),
+	SND_PCI_QUIRK(0x1028, 0x01f6, "Dell Latitude 131L", POS_FIX_LPIB),
 	SND_PCI_QUIRK(0x103c, 0x306d, "HP dv3", POS_FIX_LPIB),
+	SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB),
 	SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB),
 	SND_PCI_QUIRK(0x1462, 0x1002, "MSI Wind U115", POS_FIX_LPIB),
+	SND_PCI_QUIRK(0x1565, 0x820f, "Biostar Microtech", POS_FIX_LPIB),
 	{}
 };
 
@@ -2350,7 +2357,9 @@
  */
 static struct snd_pci_quirk msi_black_list[] __devinitdata = {
 	SND_PCI_QUIRK(0x1043, 0x81f2, "ASUS", 0), /* Athlon64 X2 + nvidia */
-	SND_PCI_QUIRK(0x1043, 0x829c, "ASUS", 0), /* nvidia */
+	SND_PCI_QUIRK(0x1043, 0x81f6, "ASUS", 0), /* nvidia */
+	SND_PCI_QUIRK(0x1043, 0x822d, "ASUS", 0), /* Athlon64 X2 + nvidia MCP55 */
+	SND_PCI_QUIRK(0x1849, 0x0888, "ASRock", 0), /* Athlon64 X2 + nvidia */
 	{}
 };
 
@@ -2418,6 +2427,7 @@
 	if (bdl_pos_adj[dev] < 0) {
 		switch (chip->driver_type) {
 		case AZX_DRIVER_ICH:
+		case AZX_DRIVER_PCH:
 			bdl_pos_adj[dev] = 1;
 			break;
 		default:
@@ -2696,6 +2706,8 @@
 	{ PCI_DEVICE(0x8086, 0x3a6e), .driver_data = AZX_DRIVER_ICH },
 	/* PCH */
 	{ PCI_DEVICE(0x8086, 0x3b56), .driver_data = AZX_DRIVER_ICH },
+	/* CPT */
+	{ PCI_DEVICE(0x8086, 0x1c20), .driver_data = AZX_DRIVER_PCH },
 	/* SCH */
 	{ PCI_DEVICE(0x8086, 0x811b), .driver_data = AZX_DRIVER_SCH },
 	/* ATI SB 450/600 */
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c
index 214301d..e6d1bdf 100644
--- a/sound/pci/hda/patch_analog.c
+++ b/sound/pci/hda/patch_analog.c
@@ -1098,7 +1098,7 @@
 	SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
-	SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD),
+	SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
 	SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
 	SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 685015a..194a28c 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -42,10 +42,12 @@
 
 /* Conexant 5051 specific */
 
-#define CXT5051_SPDIF_OUT	0x1C
+#define CXT5051_SPDIF_OUT	0x12
 #define CXT5051_PORTB_EVENT	0x38
 #define CXT5051_PORTC_EVENT	0x39
 
+#define AUTO_MIC_PORTB		(1 << 1)
+#define AUTO_MIC_PORTC		(1 << 2)
 
 struct conexant_jack {
 
@@ -74,7 +76,7 @@
 					 */
 	unsigned int cur_eapd;
 	unsigned int hp_present;
-	unsigned int no_auto_mic;
+	unsigned int auto_mic;
 	unsigned int need_dac_fix;
 
 	/* capture */
@@ -111,7 +113,8 @@
 
 	unsigned int dell_automute;
 	unsigned int port_d_mode;
-	unsigned int dell_vostro;
+	unsigned int dell_vostro:1;
+	unsigned int ideapad:1;
 
 	unsigned int ext_mic_present;
 	unsigned int recording;
@@ -1603,6 +1606,11 @@
 {
 	struct conexant_spec *spec = codec->spec;
 	unsigned int pinctl;
+	/* headphone pin */
+	pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0;
+	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+			    pinctl);
+	/* speaker pin */
 	pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
 			    pinctl);
@@ -1626,7 +1634,7 @@
 	struct conexant_spec *spec = codec->spec;
 	unsigned int present;
 
-	if (spec->no_auto_mic)
+	if (!(spec->auto_mic & AUTO_MIC_PORTB))
 		return;
 	present = snd_hda_jack_detect(codec, 0x17);
 	snd_hda_codec_write(codec, 0x14, 0,
@@ -1641,7 +1649,7 @@
 	unsigned int present;
 	hda_nid_t new_adc;
 
-	if (spec->no_auto_mic)
+	if (!(spec->auto_mic & AUTO_MIC_PORTC))
 		return;
 	present = snd_hda_jack_detect(codec, 0x18);
 	if (present)
@@ -1687,13 +1695,7 @@
 	conexant_report_jack(codec, nid);
 }
 
-static struct snd_kcontrol_new cxt5051_mixers[] = {
-	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
-	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
-	HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
-	HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
-	HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
-	HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
+static struct snd_kcontrol_new cxt5051_playback_mixers[] = {
 	HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1703,7 +1705,16 @@
 		.put = cxt5051_hp_master_sw_put,
 		.private_value = 0x1a,
 	},
+	{}
+};
 
+static struct snd_kcontrol_new cxt5051_capture_mixers[] = {
+	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Docking Mic Volume", 0x15, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Docking Mic Switch", 0x15, 0x00, HDA_INPUT),
 	{}
 };
 
@@ -1712,48 +1723,26 @@
 	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
 	HDA_CODEC_VOLUME("External Mic Volume", 0x15, 0x00, HDA_INPUT),
 	HDA_CODEC_MUTE("External Mic Switch", 0x15, 0x00, HDA_INPUT),
-	HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Master Playback Switch",
-		.info = cxt_eapd_info,
-		.get = cxt_eapd_get,
-		.put = cxt5051_hp_master_sw_put,
-		.private_value = 0x1a,
-	},
-
 	{}
 };
 
 static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = {
-	HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x00, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Switch", 0x14, 0x00, HDA_INPUT),
-	HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Master Playback Switch",
-		.info = cxt_eapd_info,
-		.get = cxt_eapd_get,
-		.put = cxt5051_hp_master_sw_put,
-		.private_value = 0x1a,
-	},
-
+	HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x14, 0x00, HDA_INPUT),
 	{}
 };
 
 static struct snd_kcontrol_new cxt5051_f700_mixers[] = {
-	HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT),
-	HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Master Playback Switch",
-		.info = cxt_eapd_info,
-		.get = cxt_eapd_get,
-		.put = cxt5051_hp_master_sw_put,
-		.private_value = 0x1a,
-	},
+	HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x14, 0x01, HDA_INPUT),
+	{}
+};
 
+static struct snd_kcontrol_new cxt5051_toshiba_mixers[] = {
+	HDA_CODEC_VOLUME("Internal Mic Volume", 0x14, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Internal Mic Switch", 0x14, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("External Mic Volume", 0x14, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("External Mic Switch", 0x14, 0x01, HDA_INPUT),
 	{}
 };
 
@@ -1782,8 +1771,6 @@
 	/* EAPD */
 	{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */ 
 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
-	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
-	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
 	{ } /* end */
 };
 
@@ -1809,7 +1796,6 @@
 	/* EAPD */
 	{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
-	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
 	{ } /* end */
 };
 
@@ -1841,15 +1827,13 @@
 	/* EAPD */
 	{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
-	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
-	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTC_EVENT},
 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
 	{ } /* end */
 };
 
 static struct hda_verb cxt5051_f700_init_verbs[] = {
 	/* Line in, Mic */
-	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x03},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
 	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
 	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
@@ -1869,15 +1853,34 @@
 	/* EAPD */
 	{0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
-	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
 	{ } /* end */
 };
 
+static void cxt5051_init_mic_port(struct hda_codec *codec, hda_nid_t nid,
+				 unsigned int event)
+{
+	snd_hda_codec_write(codec, nid, 0,
+			    AC_VERB_SET_UNSOLICITED_ENABLE,
+			    AC_USRSP_EN | event);
+#ifdef CONFIG_SND_HDA_INPUT_JACK
+	conexant_add_jack(codec, nid, SND_JACK_MICROPHONE);
+	conexant_report_jack(codec, nid);
+#endif
+}
+
 /* initialize jack-sensing, too */
 static int cxt5051_init(struct hda_codec *codec)
 {
+	struct conexant_spec *spec = codec->spec;
+
 	conexant_init(codec);
 	conexant_init_jacks(codec);
+
+	if (spec->auto_mic & AUTO_MIC_PORTB)
+		cxt5051_init_mic_port(codec, 0x17, CXT5051_PORTB_EVENT);
+	if (spec->auto_mic & AUTO_MIC_PORTC)
+		cxt5051_init_mic_port(codec, 0x18, CXT5051_PORTC_EVENT);
+
 	if (codec->patch_ops.unsol_event) {
 		cxt5051_hp_automute(codec);
 		cxt5051_portb_automic(codec);
@@ -1893,6 +1896,7 @@
 	CXT5051_HP_DV6736,	/* HP without mic switch */
 	CXT5051_LENOVO_X200,	/* Lenovo X200 laptop */
 	CXT5051_F700,       /* HP Compaq Presario F700 */
+	CXT5051_TOSHIBA,	/* Toshiba M300 & co */
 	CXT5051_MODELS
 };
 
@@ -1901,17 +1905,19 @@
 	[CXT5051_HP]		= "hp",
 	[CXT5051_HP_DV6736]	= "hp-dv6736",
 	[CXT5051_LENOVO_X200]	= "lenovo-x200",
-	[CXT5051_F700]          = "hp 700"
+	[CXT5051_F700]          = "hp-700",
+	[CXT5051_TOSHIBA]	= "toshiba",
 };
 
 static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x103c, 0x30cf, "HP DV6736", CXT5051_HP_DV6736),
 	SND_PCI_QUIRK(0x103c, 0x360b, "Compaq Presario CQ60", CXT5051_HP),
+	SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700),
+	SND_PCI_QUIRK(0x1179, 0xff50, "Toshiba M30x", CXT5051_TOSHIBA),
 	SND_PCI_QUIRK(0x14f1, 0x0101, "Conexant Reference board",
 		      CXT5051_LAPTOP),
 	SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
 	SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
-	SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700),
 	{}
 };
 
@@ -1935,8 +1941,9 @@
 	spec->multiout.dig_out_nid = CXT5051_SPDIF_OUT;
 	spec->num_adc_nids = 1; /* not 2; via auto-mic switch */
 	spec->adc_nids = cxt5051_adc_nids;
-	spec->num_mixers = 1;
-	spec->mixers[0] = cxt5051_mixers;
+	spec->num_mixers = 2;
+	spec->mixers[0] = cxt5051_capture_mixers;
+	spec->mixers[1] = cxt5051_playback_mixers;
 	spec->num_init_verbs = 1;
 	spec->init_verbs[0] = cxt5051_init_verbs;
 	spec->spdif_route = 0;
@@ -1950,6 +1957,7 @@
 	board_config = snd_hda_check_board_config(codec, CXT5051_MODELS,
 						  cxt5051_models,
 						  cxt5051_cfg_tbl);
+	spec->auto_mic = AUTO_MIC_PORTB | AUTO_MIC_PORTC;
 	switch (board_config) {
 	case CXT5051_HP:
 		spec->mixers[0] = cxt5051_hp_mixers;
@@ -1957,7 +1965,7 @@
 	case CXT5051_HP_DV6736:
 		spec->init_verbs[0] = cxt5051_hp_dv6736_init_verbs;
 		spec->mixers[0] = cxt5051_hp_dv6736_mixers;
-		spec->no_auto_mic = 1;
+		spec->auto_mic = 0;
 		break;
 	case CXT5051_LENOVO_X200:
 		spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
@@ -1965,7 +1973,11 @@
 	case CXT5051_F700:
 		spec->init_verbs[0] = cxt5051_f700_init_verbs;
 		spec->mixers[0] = cxt5051_f700_mixers;
-		spec->no_auto_mic = 1;
+		spec->auto_mic = 0;
+		break;
+	case CXT5051_TOSHIBA:
+		spec->mixers[0] = cxt5051_toshiba_mixers;
+		spec->auto_mic = AUTO_MIC_PORTB;
 		break;
 	}
 
@@ -2156,6 +2168,34 @@
 	}
 }
 
+/* toggle input of built-in digital mic and mic jack appropriately */
+static void cxt5066_ideapad_automic(struct hda_codec *codec)
+{
+	unsigned int present;
+
+	struct hda_verb ext_mic_present[] = {
+		{0x14, AC_VERB_SET_CONNECT_SEL, 0},
+		{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+		{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+		{}
+	};
+	static struct hda_verb ext_mic_absent[] = {
+		{0x14, AC_VERB_SET_CONNECT_SEL, 2},
+		{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+		{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+		{}
+	};
+
+	present = snd_hda_jack_detect(codec, 0x1b);
+	if (present) {
+		snd_printdd("CXT5066: external microphone detected\n");
+		snd_hda_sequence_write(codec, ext_mic_present);
+	} else {
+		snd_printdd("CXT5066: external microphone absent\n");
+		snd_hda_sequence_write(codec, ext_mic_absent);
+	}
+}
+
 /* mute internal speaker if HP is plugged */
 static void cxt5066_hp_automute(struct hda_codec *codec)
 {
@@ -2205,6 +2245,20 @@
 	}
 }
 
+/* unsolicited event for jack sensing */
+static void cxt5066_ideapad_event(struct hda_codec *codec, unsigned int res)
+{
+	snd_printdd("CXT5066_ideapad: unsol event %x (%x)\n", res, res >> 26);
+	switch (res >> 26) {
+	case CONEXANT_HP_EVENT:
+		cxt5066_hp_automute(codec);
+		break;
+	case CONEXANT_MIC_EVENT:
+		cxt5066_ideapad_automic(codec);
+		break;
+	}
+}
+
 static const struct hda_input_mux cxt5066_analog_mic_boost = {
 	.num_items = 5,
 	.items = {
@@ -2216,13 +2270,21 @@
 	},
 };
 
-static int cxt5066_set_mic_boost(struct hda_codec *codec)
+static void cxt5066_set_mic_boost(struct hda_codec *codec)
 {
 	struct conexant_spec *spec = codec->spec;
-	return snd_hda_codec_write_cache(codec, 0x17, 0,
+	snd_hda_codec_write_cache(codec, 0x17, 0,
 		AC_VERB_SET_AMP_GAIN_MUTE,
 		AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
 			cxt5066_analog_mic_boost.items[spec->mic_boost].index);
+	if (spec->ideapad) {
+		/* adjust the internal mic as well...it is not through 0x17 */
+		snd_hda_codec_write_cache(codec, 0x23, 0,
+			AC_VERB_SET_AMP_GAIN_MUTE,
+			AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_INPUT |
+				cxt5066_analog_mic_boost.
+					items[spec->mic_boost].index);
+	}
 }
 
 static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol,
@@ -2653,6 +2715,56 @@
 	{ } /* end */
 };
 
+static struct hda_verb cxt5066_init_verbs_ideapad[] = {
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port B */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, /* Port C */
+	{0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port F */
+	{0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* Port E */
+
+	/* Speakers  */
+	{0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1f, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+	/* HP, Amp  */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x1c, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
+
+	/* DAC1 */
+	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	/* Node 14 connections: 0x17 0x18 0x23 0x24 0x27 */
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x50},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2) | 0x50},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 2},	/* default to internal mic */
+
+	/* Audio input selector */
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x2},
+	{0x17, AC_VERB_SET_CONNECT_SEL, 1},	/* route ext mic */
+
+	/* SPDIF route: PCM */
+	{0x20, AC_VERB_SET_CONNECT_SEL, 0x0},
+	{0x22, AC_VERB_SET_CONNECT_SEL, 0x0},
+
+	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+
+	/* internal microphone */
+	{0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, /* enable int mic */
+
+	/* EAPD */
+	{0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+
+	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_HP_EVENT},
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | CONEXANT_MIC_EVENT},
+	{ } /* end */
+};
+
 static struct hda_verb cxt5066_init_verbs_portd_lo[] = {
 	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 	{ } /* end */
@@ -2669,6 +2781,8 @@
 		cxt5066_hp_automute(codec);
 		if (spec->dell_vostro)
 			cxt5066_vostro_automic(codec);
+		else if (spec->ideapad)
+			cxt5066_ideapad_automic(codec);
 	}
 	cxt5066_set_mic_boost(codec);
 	return 0;
@@ -2694,6 +2808,7 @@
 	CXT5066_DELL_LAPTOP,	/* Dell Laptop */
 	CXT5066_OLPC_XO_1_5,	/* OLPC XO 1.5 */
 	CXT5066_DELL_VOSTO,	/* Dell Vostro 1015i */
+	CXT5066_IDEAPAD,	/* Lenovo IdeaPad U150 */
 	CXT5066_MODELS
 };
 
@@ -2701,7 +2816,8 @@
 	[CXT5066_LAPTOP]		= "laptop",
 	[CXT5066_DELL_LAPTOP]	= "dell-laptop",
 	[CXT5066_OLPC_XO_1_5]	= "olpc-xo-1_5",
-	[CXT5066_DELL_VOSTO]    = "dell-vostro"
+	[CXT5066_DELL_VOSTO]    = "dell-vostro",
+	[CXT5066_IDEAPAD]	= "ideapad",
 };
 
 static struct snd_pci_quirk cxt5066_cfg_tbl[] = {
@@ -2711,6 +2827,7 @@
 		      CXT5066_DELL_LAPTOP),
 	SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT5066_OLPC_XO_1_5),
 	SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTO),
+	SND_PCI_QUIRK(0x17aa, 0x3a0d, "ideapad", CXT5066_IDEAPAD),
 	{}
 };
 
@@ -2802,6 +2919,22 @@
 		/* input source automatically selected */
 		spec->input_mux = NULL;
 		break;
+	case CXT5066_IDEAPAD:
+		codec->patch_ops.init = cxt5066_init;
+		codec->patch_ops.unsol_event = cxt5066_ideapad_event;
+		spec->mixers[spec->num_mixers++] = cxt5066_mixer_master;
+		spec->mixers[spec->num_mixers++] = cxt5066_mixers;
+		spec->init_verbs[0] = cxt5066_init_verbs_ideapad;
+		spec->port_d_mode = 0;
+		spec->ideapad = 1;
+		spec->mic_boost = 2;	/* default 20dB gain */
+
+		/* no S/PDIF out */
+		spec->multiout.dig_out_nid = 0;
+
+		/* input source automatically selected */
+		spec->input_mux = NULL;
+		break;
 	}
 
 	return 0;
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
new file mode 100644
index 0000000..2c2bafb
--- /dev/null
+++ b/sound/pci/hda/patch_hdmi.c
@@ -0,0 +1,849 @@
+/*
+ *
+ *  patch_hdmi.c - routines for HDMI/DisplayPort codecs
+ *
+ *  Copyright(c) 2008-2010 Intel Corporation. All rights reserved.
+ *
+ *  Authors:
+ *			Wu Fengguang <wfg@linux.intel.com>
+ *
+ *  Maintained by:
+ *			Wu Fengguang <wfg@linux.intel.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the Free
+ *  Software Foundation; either version 2 of the License, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ *  or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ *  for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software Foundation,
+ *  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+
+struct hdmi_spec {
+	int num_cvts;
+	int num_pins;
+	hda_nid_t cvt[MAX_HDMI_CVTS+1];  /* audio sources */
+	hda_nid_t pin[MAX_HDMI_PINS+1];  /* audio sinks */
+
+	/*
+	 * source connection for each pin
+	 */
+	hda_nid_t pin_cvt[MAX_HDMI_PINS+1];
+
+	/*
+	 * HDMI sink attached to each pin
+	 */
+	struct hdmi_eld sink_eld[MAX_HDMI_PINS];
+
+	/*
+	 * export one pcm per pipe
+	 */
+	struct hda_pcm	pcm_rec[MAX_HDMI_CVTS];
+
+	/*
+	 * nvhdmi specific
+	 */
+	struct hda_multi_out multiout;
+	unsigned int codec_type;
+};
+
+
+struct hdmi_audio_infoframe {
+	u8 type; /* 0x84 */
+	u8 ver;  /* 0x01 */
+	u8 len;  /* 0x0a */
+
+	u8 checksum;	/* PB0 */
+	u8 CC02_CT47;	/* CC in bits 0:2, CT in 4:7 */
+	u8 SS01_SF24;
+	u8 CXT04;
+	u8 CA;
+	u8 LFEPBL01_LSV36_DM_INH7;
+	u8 reserved[5];	/* PB6 - PB10 */
+};
+
+/*
+ * CEA speaker placement:
+ *
+ *        FLH       FCH        FRH
+ *  FLW    FL  FLC   FC   FRC   FR   FRW
+ *
+ *                                  LFE
+ *                     TC
+ *
+ *          RL  RLC   RC   RRC   RR
+ *
+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ */
+enum cea_speaker_placement {
+	FL  = (1 <<  0),	/* Front Left           */
+	FC  = (1 <<  1),	/* Front Center         */
+	FR  = (1 <<  2),	/* Front Right          */
+	FLC = (1 <<  3),	/* Front Left Center    */
+	FRC = (1 <<  4),	/* Front Right Center   */
+	RL  = (1 <<  5),	/* Rear Left            */
+	RC  = (1 <<  6),	/* Rear Center          */
+	RR  = (1 <<  7),	/* Rear Right           */
+	RLC = (1 <<  8),	/* Rear Left Center     */
+	RRC = (1 <<  9),	/* Rear Right Center    */
+	LFE = (1 << 10),	/* Low Frequency Effect */
+	FLW = (1 << 11),	/* Front Left Wide      */
+	FRW = (1 << 12),	/* Front Right Wide     */
+	FLH = (1 << 13),	/* Front Left High      */
+	FCH = (1 << 14),	/* Front Center High    */
+	FRH = (1 << 15),	/* Front Right High     */
+	TC  = (1 << 16),	/* Top Center           */
+};
+
+/*
+ * ELD SA bits in the CEA Speaker Allocation data block
+ */
+static int eld_speaker_allocation_bits[] = {
+	[0] = FL | FR,
+	[1] = LFE,
+	[2] = FC,
+	[3] = RL | RR,
+	[4] = RC,
+	[5] = FLC | FRC,
+	[6] = RLC | RRC,
+	/* the following are not defined in ELD yet */
+	[7] = FLW | FRW,
+	[8] = FLH | FRH,
+	[9] = TC,
+	[10] = FCH,
+};
+
+struct cea_channel_speaker_allocation {
+	int ca_index;
+	int speakers[8];
+
+	/* derived values, just for convenience */
+	int channels;
+	int spk_mask;
+};
+
+/*
+ * ALSA sequence is:
+ *
+ *       surround40   surround41   surround50   surround51   surround71
+ * ch0   front left   =            =            =            =
+ * ch1   front right  =            =            =            =
+ * ch2   rear left    =            =            =            =
+ * ch3   rear right   =            =            =            =
+ * ch4                LFE          center       center       center
+ * ch5                                          LFE          LFE
+ * ch6                                                       side left
+ * ch7                                                       side right
+ *
+ * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
+ */
+static int hdmi_channel_mapping[0x32][8] = {
+	/* stereo */
+	[0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+	/* 2.1 */
+	[0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+	/* Dolby Surround */
+	[0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
+	/* surround40 */
+	[0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
+	/* 4ch */
+	[0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
+	/* surround41 */
+	[0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 },
+	/* surround50 */
+	[0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
+	/* surround51 */
+	[0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
+	/* 7.1 */
+	[0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
+};
+
+/*
+ * This is an ordered list!
+ *
+ * The preceding ones have better chances to be selected by
+ * hdmi_setup_channel_allocation().
+ */
+static struct cea_channel_speaker_allocation channel_allocations[] = {
+/*			  channel:   7     6    5    4    3     2    1    0  */
+{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
+				 /* 2.1 */
+{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
+				 /* Dolby Surround */
+{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
+				 /* surround40 */
+{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
+				 /* surround41 */
+{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
+				 /* surround50 */
+{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+				 /* surround51 */
+{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+				 /* 6.1 */
+{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+				 /* surround71 */
+{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+
+{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
+{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
+};
+
+
+/*
+ * HDMI routines
+ */
+
+static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
+{
+	int i;
+
+	for (i = 0; nids[i]; i++)
+		if (nids[i] == nid)
+			return i;
+
+	snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
+	return -EINVAL;
+}
+
+static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
+			      struct hdmi_eld *eld)
+{
+	if (!snd_hdmi_get_eld(eld, codec, pin_nid))
+		snd_hdmi_show_eld(eld);
+}
+
+#ifdef BE_PARANOID
+static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
+				int *packet_index, int *byte_index)
+{
+	int val;
+
+	val = snd_hda_codec_read(codec, pin_nid, 0,
+				 AC_VERB_GET_HDMI_DIP_INDEX, 0);
+
+	*packet_index = val >> 5;
+	*byte_index = val & 0x1f;
+}
+#endif
+
+static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
+				int packet_index, int byte_index)
+{
+	int val;
+
+	val = (packet_index << 5) | (byte_index & 0x1f);
+
+	snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
+}
+
+static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
+				unsigned char val)
+{
+	snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
+}
+
+static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+	/* Unmute */
+	if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
+		snd_hda_codec_write(codec, pin_nid, 0,
+				AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+	/* Enable pin out */
+	snd_hda_codec_write(codec, pin_nid, 0,
+			    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+}
+
+static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
+{
+	return 1 + snd_hda_codec_read(codec, nid, 0,
+					AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hda_codec *codec,
+				   hda_nid_t nid, int chs)
+{
+	if (chs != hdmi_get_channel_count(codec, nid))
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+}
+
+
+/*
+ * Channel mapping routines
+ */
+
+/*
+ * Compute derived values in channel_allocations[].
+ */
+static void init_channel_allocations(void)
+{
+	int i, j;
+	struct cea_channel_speaker_allocation *p;
+
+	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+		p = channel_allocations + i;
+		p->channels = 0;
+		p->spk_mask = 0;
+		for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
+			if (p->speakers[j]) {
+				p->channels++;
+				p->spk_mask |= p->speakers[j];
+			}
+	}
+}
+
+/*
+ * The transformation takes two steps:
+ *
+ *	eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
+ *	      spk_mask => (channel_allocations[])         => ai->CA
+ *
+ * TODO: it could select the wrong CA from multiple candidates.
+*/
+static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
+					 struct hdmi_audio_infoframe *ai)
+{
+	struct hdmi_spec *spec = codec->spec;
+	struct hdmi_eld *eld;
+	int i;
+	int spk_mask = 0;
+	int channels = 1 + (ai->CC02_CT47 & 0x7);
+	char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+	/*
+	 * CA defaults to 0 for basic stereo audio
+	 */
+	if (channels <= 2)
+		return 0;
+
+	i = hda_node_index(spec->pin_cvt, nid);
+	if (i < 0)
+		return 0;
+	eld = &spec->sink_eld[i];
+
+	/*
+	 * HDMI sink's ELD info cannot always be retrieved for now, e.g.
+	 * in console or for audio devices. Assume the highest speakers
+	 * configuration, to _not_ prohibit multi-channel audio playback.
+	 */
+	if (!eld->spk_alloc)
+		eld->spk_alloc = 0xffff;
+
+	/*
+	 * expand ELD's speaker allocation mask
+	 *
+	 * ELD tells the speaker mask in a compact(paired) form,
+	 * expand ELD's notions to match the ones used by Audio InfoFrame.
+	 */
+	for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+		if (eld->spk_alloc & (1 << i))
+			spk_mask |= eld_speaker_allocation_bits[i];
+	}
+
+	/* search for the first working match in the CA table */
+	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+		if (channels == channel_allocations[i].channels &&
+		    (spk_mask & channel_allocations[i].spk_mask) ==
+				channel_allocations[i].spk_mask) {
+			ai->CA = channel_allocations[i].ca_index;
+			break;
+		}
+	}
+
+	snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
+	snd_printdd("HDMI: select CA 0x%x for %d-channel allocation: %s\n",
+		    ai->CA, channels, buf);
+
+	return ai->CA;
+}
+
+static void hdmi_debug_channel_mapping(struct hda_codec *codec,
+				       hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+	int i;
+	int slot;
+
+	for (i = 0; i < 8; i++) {
+		slot = snd_hda_codec_read(codec, pin_nid, 0,
+						AC_VERB_GET_HDMI_CHAN_SLOT, i);
+		printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
+						slot >> 4, slot & 0xf);
+	}
+#endif
+}
+
+
+static void hdmi_setup_channel_mapping(struct hda_codec *codec,
+				       hda_nid_t pin_nid,
+				       struct hdmi_audio_infoframe *ai)
+{
+	int i;
+	int ca = ai->CA;
+	int err;
+
+	if (hdmi_channel_mapping[ca][1] == 0) {
+		for (i = 0; i < channel_allocations[ca].channels; i++)
+			hdmi_channel_mapping[ca][i] = i | (i << 4);
+		for (; i < 8; i++)
+			hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
+	}
+
+	for (i = 0; i < 8; i++) {
+		err = snd_hda_codec_write(codec, pin_nid, 0,
+					  AC_VERB_SET_HDMI_CHAN_SLOT,
+					  hdmi_channel_mapping[ca][i]);
+		if (err) {
+			snd_printdd(KERN_NOTICE
+				    "HDMI: channel mapping failed\n");
+			break;
+		}
+	}
+
+	hdmi_debug_channel_mapping(codec, pin_nid);
+}
+
+
+/*
+ * Audio InfoFrame routines
+ */
+
+/*
+ * Enable Audio InfoFrame Transmission
+ */
+static void hdmi_start_infoframe_trans(struct hda_codec *codec,
+				       hda_nid_t pin_nid)
+{
+	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+	snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+						AC_DIPXMIT_BEST);
+}
+
+/*
+ * Disable Audio InfoFrame Transmission
+ */
+static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
+				      hda_nid_t pin_nid)
+{
+	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+	snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
+						AC_DIPXMIT_DISABLE);
+}
+
+static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+	int i;
+	int size;
+
+	size = snd_hdmi_get_eld_size(codec, pin_nid);
+	printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
+
+	for (i = 0; i < 8; i++) {
+		size = snd_hda_codec_read(codec, pin_nid, 0,
+						AC_VERB_GET_HDMI_DIP_SIZE, i);
+		printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
+	}
+#endif
+}
+
+static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+#ifdef BE_PARANOID
+	int i, j;
+	int size;
+	int pi, bi;
+	for (i = 0; i < 8; i++) {
+		size = snd_hda_codec_read(codec, pin_nid, 0,
+						AC_VERB_GET_HDMI_DIP_SIZE, i);
+		if (size == 0)
+			continue;
+
+		hdmi_set_dip_index(codec, pin_nid, i, 0x0);
+		for (j = 1; j < 1000; j++) {
+			hdmi_write_dip_byte(codec, pin_nid, 0x0);
+			hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
+			if (pi != i)
+				snd_printd(KERN_INFO "dip index %d: %d != %d\n",
+						bi, pi, i);
+			if (bi == 0) /* byte index wrapped around */
+				break;
+		}
+		snd_printd(KERN_INFO
+			"HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
+			i, size, j);
+	}
+#endif
+}
+
+static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
+{
+	u8 *bytes = (u8 *)ai;
+	u8 sum = 0;
+	int i;
+
+	ai->checksum = 0;
+
+	for (i = 0; i < sizeof(*ai); i++)
+		sum += bytes[i];
+
+	ai->checksum = -sum;
+}
+
+static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
+				      hda_nid_t pin_nid,
+				      struct hdmi_audio_infoframe *ai)
+{
+	u8 *bytes = (u8 *)ai;
+	int i;
+
+	hdmi_debug_dip_size(codec, pin_nid);
+	hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
+
+	hdmi_checksum_audio_infoframe(ai);
+
+	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+	for (i = 0; i < sizeof(*ai); i++)
+		hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
+}
+
+static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
+				    struct hdmi_audio_infoframe *ai)
+{
+	u8 *bytes = (u8 *)ai;
+	u8 val;
+	int i;
+
+	if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
+							    != AC_DIPXMIT_BEST)
+		return false;
+
+	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
+	for (i = 0; i < sizeof(*ai); i++) {
+		val = snd_hda_codec_read(codec, pin_nid, 0,
+					 AC_VERB_GET_HDMI_DIP_DATA, 0);
+		if (val != bytes[i])
+			return false;
+	}
+
+	return true;
+}
+
+static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
+					struct snd_pcm_substream *substream)
+{
+	struct hdmi_spec *spec = codec->spec;
+	hda_nid_t pin_nid;
+	int i;
+	struct hdmi_audio_infoframe ai = {
+		.type		= 0x84,
+		.ver		= 0x01,
+		.len		= 0x0a,
+		.CC02_CT47	= substream->runtime->channels - 1,
+	};
+
+	hdmi_setup_channel_allocation(codec, nid, &ai);
+
+	for (i = 0; i < spec->num_pins; i++) {
+		if (spec->pin_cvt[i] != nid)
+			continue;
+		if (!spec->sink_eld[i].monitor_present)
+			continue;
+
+		pin_nid = spec->pin[i];
+		if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
+			snd_printdd("hdmi_setup_audio_infoframe: "
+				    "cvt=%d pin=%d channels=%d\n",
+				    nid, pin_nid,
+				    substream->runtime->channels);
+			hdmi_setup_channel_mapping(codec, pin_nid, &ai);
+			hdmi_stop_infoframe_trans(codec, pin_nid);
+			hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
+			hdmi_start_infoframe_trans(codec, pin_nid);
+		}
+	}
+}
+
+
+/*
+ * Unsolicited events
+ */
+
+static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+	struct hdmi_spec *spec = codec->spec;
+	int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+	int pind = !!(res & AC_UNSOL_RES_PD);
+	int eldv = !!(res & AC_UNSOL_RES_ELDV);
+	int index;
+
+	printk(KERN_INFO
+		"HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
+		tag, pind, eldv);
+
+	index = hda_node_index(spec->pin, tag);
+	if (index < 0)
+		return;
+
+	spec->sink_eld[index].monitor_present = pind;
+	spec->sink_eld[index].eld_valid = eldv;
+
+	if (pind && eldv) {
+		hdmi_get_show_eld(codec, spec->pin[index],
+				  &spec->sink_eld[index]);
+		/* TODO: do real things about ELD */
+	}
+}
+
+static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
+{
+	int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+	int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+	int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
+	int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
+
+	printk(KERN_INFO
+		"HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
+		tag,
+		subtag,
+		cp_state,
+		cp_ready);
+
+	/* TODO */
+	if (cp_state)
+		;
+	if (cp_ready)
+		;
+}
+
+
+static void hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	struct hdmi_spec *spec = codec->spec;
+	int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
+	int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
+
+	if (hda_node_index(spec->pin, tag) < 0) {
+		snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
+		return;
+	}
+
+	if (subtag == 0)
+		hdmi_intrinsic_event(codec, res);
+	else
+		hdmi_non_intrinsic_event(codec, res);
+}
+
+/*
+ * Callbacks
+ */
+
+static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
+			      u32 stream_tag, int format)
+{
+	int tag;
+	int fmt;
+
+	tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
+	fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
+
+	snd_printdd("hdmi_setup_stream: "
+		    "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
+		    nid,
+		    tag == stream_tag ? "" : "new-",
+		    stream_tag,
+		    fmt == format ? "" : "new-",
+		    format);
+
+	if (tag != stream_tag)
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_CHANNEL_STREAMID,
+				    stream_tag << 4);
+	if (fmt != format)
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_STREAM_FORMAT, format);
+}
+
+/*
+ * HDA/HDMI auto parsing
+ */
+
+static int hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+	struct hdmi_spec *spec = codec->spec;
+	hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
+	int conn_len, curr;
+	int index;
+
+	if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
+		snd_printk(KERN_WARNING
+			   "HDMI: pin %d wcaps %#x "
+			   "does not support connection list\n",
+			   pin_nid, get_wcaps(codec, pin_nid));
+		return -EINVAL;
+	}
+
+	conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
+					   HDA_MAX_CONNECTIONS);
+	if (conn_len > 1)
+		curr = snd_hda_codec_read(codec, pin_nid, 0,
+					  AC_VERB_GET_CONNECT_SEL, 0);
+	else
+		curr = 0;
+
+	index = hda_node_index(spec->pin, pin_nid);
+	if (index < 0)
+		return -EINVAL;
+
+	spec->pin_cvt[index] = conn_list[curr];
+
+	return 0;
+}
+
+static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
+			       struct hdmi_eld *eld)
+{
+	int present = snd_hda_pin_sense(codec, pin_nid);
+
+	eld->monitor_present	= !!(present & AC_PINSENSE_PRESENCE);
+	eld->eld_valid		= !!(present & AC_PINSENSE_ELDV);
+
+	if (present & AC_PINSENSE_ELDV)
+		hdmi_get_show_eld(codec, pin_nid, eld);
+}
+
+static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
+{
+	struct hdmi_spec *spec = codec->spec;
+
+	if (spec->num_pins >= MAX_HDMI_PINS) {
+		snd_printk(KERN_WARNING
+			   "HDMI: no space for pin %d\n", pin_nid);
+		return -EINVAL;
+	}
+
+	hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
+
+	spec->pin[spec->num_pins] = pin_nid;
+	spec->num_pins++;
+
+	/*
+	 * It is assumed that converter nodes come first in the node list and
+	 * hence have been registered and usable now.
+	 */
+	return hdmi_read_pin_conn(codec, pin_nid);
+}
+
+static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
+{
+	struct hdmi_spec *spec = codec->spec;
+
+	if (spec->num_cvts >= MAX_HDMI_CVTS) {
+		snd_printk(KERN_WARNING
+			   "HDMI: no space for converter %d\n", nid);
+		return -EINVAL;
+	}
+
+	spec->cvt[spec->num_cvts] = nid;
+	spec->num_cvts++;
+
+	return 0;
+}
+
+static int hdmi_parse_codec(struct hda_codec *codec)
+{
+	hda_nid_t nid;
+	int i, nodes;
+
+	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
+	if (!nid || nodes < 0) {
+		snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < nodes; i++, nid++) {
+		unsigned int caps;
+		unsigned int type;
+
+		caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
+		type = get_wcaps_type(caps);
+
+		if (!(caps & AC_WCAP_DIGITAL))
+			continue;
+
+		switch (type) {
+		case AC_WID_AUD_OUT:
+			if (hdmi_add_cvt(codec, nid) < 0)
+				return -EINVAL;
+			break;
+		case AC_WID_PIN:
+			caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+			if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
+				continue;
+			if (hdmi_add_pin(codec, nid) < 0)
+				return -EINVAL;
+			break;
+		}
+	}
+
+	/*
+	 * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
+	 * can be lost and presence sense verb will become inaccurate if the
+	 * HDA link is powered off at hot plug or hw initialization time.
+	 */
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+	if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
+	      AC_PWRST_EPSS))
+		codec->bus->power_keep_link_on = 1;
+#endif
+
+	return 0;
+}
+
diff --git a/sound/pci/hda/patch_intelhdmi.c b/sound/pci/hda/patch_intelhdmi.c
index 918f403..88d0351 100644
--- a/sound/pci/hda/patch_intelhdmi.c
+++ b/sound/pci/hda/patch_intelhdmi.c
@@ -40,814 +40,19 @@
  *
  * The HDA correspondence of pipes/ports are converter/pin nodes.
  */
-#define INTEL_HDMI_CVTS	2
-#define INTEL_HDMI_PINS	3
+#define MAX_HDMI_CVTS	2
+#define MAX_HDMI_PINS	3
 
-static char *intel_hdmi_pcm_names[INTEL_HDMI_CVTS] = {
+#include "patch_hdmi.c"
+
+static char *intel_hdmi_pcm_names[MAX_HDMI_CVTS] = {
 	"INTEL HDMI 0",
 	"INTEL HDMI 1",
 };
 
-struct intel_hdmi_spec {
-	int num_cvts;
-	int num_pins;
-	hda_nid_t cvt[INTEL_HDMI_CVTS+1];  /* audio sources */
-	hda_nid_t pin[INTEL_HDMI_PINS+1];  /* audio sinks */
-
-	/*
-	 * source connection for each pin
-	 */
-	hda_nid_t pin_cvt[INTEL_HDMI_PINS+1];
-
-	/*
-	 * HDMI sink attached to each pin
-	 */
-	struct hdmi_eld sink_eld[INTEL_HDMI_PINS];
-
-	/*
-	 * export one pcm per pipe
-	 */
-	struct hda_pcm	pcm_rec[INTEL_HDMI_CVTS];
-};
-
-struct hdmi_audio_infoframe {
-	u8 type; /* 0x84 */
-	u8 ver;  /* 0x01 */
-	u8 len;  /* 0x0a */
-
-	u8 checksum;	/* PB0 */
-	u8 CC02_CT47;	/* CC in bits 0:2, CT in 4:7 */
-	u8 SS01_SF24;
-	u8 CXT04;
-	u8 CA;
-	u8 LFEPBL01_LSV36_DM_INH7;
-	u8 reserved[5];	/* PB6 - PB10 */
-};
-
 /*
- * CEA speaker placement:
- *
- *        FLH       FCH        FRH
- *  FLW    FL  FLC   FC   FRC   FR   FRW
- *
- *                                  LFE
- *                     TC
- *
- *          RL  RLC   RC   RRC   RR
- *
- * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
- * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ * HDMI callbacks
  */
-enum cea_speaker_placement {
-	FL  = (1 <<  0),	/* Front Left           */
-	FC  = (1 <<  1),	/* Front Center         */
-	FR  = (1 <<  2),	/* Front Right          */
-	FLC = (1 <<  3),	/* Front Left Center    */
-	FRC = (1 <<  4),	/* Front Right Center   */
-	RL  = (1 <<  5),	/* Rear Left            */
-	RC  = (1 <<  6),	/* Rear Center          */
-	RR  = (1 <<  7),	/* Rear Right           */
-	RLC = (1 <<  8),	/* Rear Left Center     */
-	RRC = (1 <<  9),	/* Rear Right Center    */
-	LFE = (1 << 10),	/* Low Frequency Effect */
-	FLW = (1 << 11),	/* Front Left Wide      */
-	FRW = (1 << 12),	/* Front Right Wide     */
-	FLH = (1 << 13),	/* Front Left High      */
-	FCH = (1 << 14),	/* Front Center High    */
-	FRH = (1 << 15),	/* Front Right High     */
-	TC  = (1 << 16),	/* Top Center           */
-};
-
-/*
- * ELD SA bits in the CEA Speaker Allocation data block
- */
-static int eld_speaker_allocation_bits[] = {
-	[0] = FL | FR,
-	[1] = LFE,
-	[2] = FC,
-	[3] = RL | RR,
-	[4] = RC,
-	[5] = FLC | FRC,
-	[6] = RLC | RRC,
-	/* the following are not defined in ELD yet */
-	[7] = FLW | FRW,
-	[8] = FLH | FRH,
-	[9] = TC,
-	[10] = FCH,
-};
-
-struct cea_channel_speaker_allocation {
-	int ca_index;
-	int speakers[8];
-
-	/* derived values, just for convenience */
-	int channels;
-	int spk_mask;
-};
-
-/*
- * ALSA sequence is:
- *
- *       surround40   surround41   surround50   surround51   surround71
- * ch0   front left   =            =            =            =
- * ch1   front right  =            =            =            =
- * ch2   rear left    =            =            =            =
- * ch3   rear right   =            =            =            =
- * ch4                LFE          center       center       center
- * ch5                                          LFE          LFE
- * ch6                                                       side left
- * ch7                                                       side right
- *
- * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
- */
-static int hdmi_channel_mapping[0x32][8] = {
-	/* stereo */
-	[0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-	/* 2.1 */
-	[0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-	/* Dolby Surround */
-	[0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
-	/* surround40 */
-	[0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
-	/* 4ch */
-	[0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
-	/* surround41 */
-	[0x09] = { 0x00, 0x11, 0x24, 0x34, 0x43, 0xf2, 0xf6, 0xf7 },
-	/* surround50 */
-	[0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
-	/* surround51 */
-	[0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
-	/* 7.1 */
-	[0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
-};
-
-/*
- * This is an ordered list!
- *
- * The preceding ones have better chances to be selected by
- * hdmi_setup_channel_allocation().
- */
-static struct cea_channel_speaker_allocation channel_allocations[] = {
-/* 			  channel:   7     6    5    4    3     2    1    0  */
-{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
-				 /* 2.1 */
-{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
-				 /* Dolby Surround */
-{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
-				 /* surround40 */
-{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
-				 /* surround41 */
-{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
-				 /* surround50 */
-{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-				 /* surround51 */
-{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-				 /* 6.1 */
-{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-				 /* surround71 */
-{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-
-{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
-{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
-};
-
-/*
- * HDA/HDMI auto parsing
- */
-
-static int hda_node_index(hda_nid_t *nids, hda_nid_t nid)
-{
-	int i;
-
-	for (i = 0; nids[i]; i++)
-		if (nids[i] == nid)
-			return i;
-
-	snd_printk(KERN_WARNING "HDMI: nid %d not registered\n", nid);
-	return -EINVAL;
-}
-
-static int intel_hdmi_read_pin_conn(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-	struct intel_hdmi_spec *spec = codec->spec;
-	hda_nid_t conn_list[HDA_MAX_CONNECTIONS];
-	int conn_len, curr;
-	int index;
-
-	if (!(get_wcaps(codec, pin_nid) & AC_WCAP_CONN_LIST)) {
-		snd_printk(KERN_WARNING
-			   "HDMI: pin %d wcaps %#x "
-			   "does not support connection list\n",
-			   pin_nid, get_wcaps(codec, pin_nid));
-		return -EINVAL;
-	}
-
-	conn_len = snd_hda_get_connections(codec, pin_nid, conn_list,
-					   HDA_MAX_CONNECTIONS);
-	if (conn_len > 1)
-		curr = snd_hda_codec_read(codec, pin_nid, 0,
-					  AC_VERB_GET_CONNECT_SEL, 0);
-	else
-		curr = 0;
-
-	index = hda_node_index(spec->pin, pin_nid);
-	if (index < 0)
-		return -EINVAL;
-
-	spec->pin_cvt[index] = conn_list[curr];
-
-	return 0;
-}
-
-static void hdmi_get_show_eld(struct hda_codec *codec, hda_nid_t pin_nid,
-			      struct hdmi_eld *eld)
-{
-	if (!snd_hdmi_get_eld(eld, codec, pin_nid))
-		snd_hdmi_show_eld(eld);
-}
-
-static void hdmi_present_sense(struct hda_codec *codec, hda_nid_t pin_nid,
-			       struct hdmi_eld *eld)
-{
-	int present = snd_hda_pin_sense(codec, pin_nid);
-
-	eld->monitor_present	= !!(present & AC_PINSENSE_PRESENCE);
-	eld->eld_valid		= !!(present & AC_PINSENSE_ELDV);
-
-	if (present & AC_PINSENSE_ELDV)
-		hdmi_get_show_eld(codec, pin_nid, eld);
-}
-
-static int intel_hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-	struct intel_hdmi_spec *spec = codec->spec;
-
-	if (spec->num_pins >= INTEL_HDMI_PINS) {
-		snd_printk(KERN_WARNING
-			   "HDMI: no space for pin %d \n", pin_nid);
-		return -EINVAL;
-	}
-
-	hdmi_present_sense(codec, pin_nid, &spec->sink_eld[spec->num_pins]);
-
-	spec->pin[spec->num_pins] = pin_nid;
-	spec->num_pins++;
-
-	/*
-	 * It is assumed that converter nodes come first in the node list and
-	 * hence have been registered and usable now.
-	 */
-	return intel_hdmi_read_pin_conn(codec, pin_nid);
-}
-
-static int intel_hdmi_add_cvt(struct hda_codec *codec, hda_nid_t nid)
-{
-	struct intel_hdmi_spec *spec = codec->spec;
-
-	if (spec->num_cvts >= INTEL_HDMI_CVTS) {
-		snd_printk(KERN_WARNING
-			   "HDMI: no space for converter %d \n", nid);
-		return -EINVAL;
-	}
-
-	spec->cvt[spec->num_cvts] = nid;
-	spec->num_cvts++;
-
-	return 0;
-}
-
-static int intel_hdmi_parse_codec(struct hda_codec *codec)
-{
-	hda_nid_t nid;
-	int i, nodes;
-
-	nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid);
-	if (!nid || nodes < 0) {
-		snd_printk(KERN_WARNING "HDMI: failed to get afg sub nodes\n");
-		return -EINVAL;
-	}
-
-	for (i = 0; i < nodes; i++, nid++) {
-		unsigned int caps;
-		unsigned int type;
-
-		caps = snd_hda_param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP);
-		type = get_wcaps_type(caps);
-
-		if (!(caps & AC_WCAP_DIGITAL))
-			continue;
-
-		switch (type) {
-		case AC_WID_AUD_OUT:
-			if (intel_hdmi_add_cvt(codec, nid) < 0)
-				return -EINVAL;
-			break;
-		case AC_WID_PIN:
-			caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
-			if (!(caps & (AC_PINCAP_HDMI | AC_PINCAP_DP)))
-				continue;
-			if (intel_hdmi_add_pin(codec, nid) < 0)
-				return -EINVAL;
-			break;
-		}
-	}
-
-	/*
-	 * G45/IbexPeak don't support EPSS: the unsolicited pin hot plug event
-	 * can be lost and presence sense verb will become inaccurate if the
-	 * HDA link is powered off at hot plug or hw initialization time.
-	 */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-	if (!(snd_hda_param_read(codec, codec->afg, AC_PAR_POWER_STATE) &
-	      AC_PWRST_EPSS))
-		codec->bus->power_keep_link_on = 1;
-#endif
-
-	return 0;
-}
-
-/*
- * HDMI routines
- */
-
-#ifdef BE_PARANOID
-static void hdmi_get_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
-				int *packet_index, int *byte_index)
-{
-	int val;
-
-	val = snd_hda_codec_read(codec, pin_nid, 0,
-				 AC_VERB_GET_HDMI_DIP_INDEX, 0);
-
-	*packet_index = val >> 5;
-	*byte_index = val & 0x1f;
-}
-#endif
-
-static void hdmi_set_dip_index(struct hda_codec *codec, hda_nid_t pin_nid,
-				int packet_index, int byte_index)
-{
-	int val;
-
-	val = (packet_index << 5) | (byte_index & 0x1f);
-
-	snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_INDEX, val);
-}
-
-static void hdmi_write_dip_byte(struct hda_codec *codec, hda_nid_t pin_nid,
-				unsigned char val)
-{
-	snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_DATA, val);
-}
-
-static void hdmi_enable_output(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-	/* Unmute */
-	if (get_wcaps(codec, pin_nid) & AC_WCAP_OUT_AMP)
-		snd_hda_codec_write(codec, pin_nid, 0,
-				AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
-	/* Enable pin out */
-	snd_hda_codec_write(codec, pin_nid, 0,
-			    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
-}
-
-/*
- * Enable Audio InfoFrame Transmission
- */
-static void hdmi_start_infoframe_trans(struct hda_codec *codec,
-				       hda_nid_t pin_nid)
-{
-	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-	snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
-						AC_DIPXMIT_BEST);
-}
-
-/*
- * Disable Audio InfoFrame Transmission
- */
-static void hdmi_stop_infoframe_trans(struct hda_codec *codec,
-				      hda_nid_t pin_nid)
-{
-	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-	snd_hda_codec_write(codec, pin_nid, 0, AC_VERB_SET_HDMI_DIP_XMIT,
-						AC_DIPXMIT_DISABLE);
-}
-
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t nid)
-{
-	return 1 + snd_hda_codec_read(codec, nid, 0,
-					AC_VERB_GET_CVT_CHAN_COUNT, 0);
-}
-
-static void hdmi_set_channel_count(struct hda_codec *codec,
-				   hda_nid_t nid, int chs)
-{
-	if (chs != hdmi_get_channel_count(codec, nid))
-		snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
-}
-
-static void hdmi_debug_channel_mapping(struct hda_codec *codec,
-				       hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-	int i;
-	int slot;
-
-	for (i = 0; i < 8; i++) {
-		slot = snd_hda_codec_read(codec, pin_nid, 0,
-						AC_VERB_GET_HDMI_CHAN_SLOT, i);
-		printk(KERN_DEBUG "HDMI: ASP channel %d => slot %d\n",
-						slot >> 4, slot & 0xf);
-	}
-#endif
-}
-
-
-/*
- * Audio InfoFrame routines
- */
-
-static void hdmi_debug_dip_size(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-	int i;
-	int size;
-
-	size = snd_hdmi_get_eld_size(codec, pin_nid);
-	printk(KERN_DEBUG "HDMI: ELD buf size is %d\n", size);
-
-	for (i = 0; i < 8; i++) {
-		size = snd_hda_codec_read(codec, pin_nid, 0,
-						AC_VERB_GET_HDMI_DIP_SIZE, i);
-		printk(KERN_DEBUG "HDMI: DIP GP[%d] buf size is %d\n", i, size);
-	}
-#endif
-}
-
-static void hdmi_clear_dip_buffers(struct hda_codec *codec, hda_nid_t pin_nid)
-{
-#ifdef BE_PARANOID
-	int i, j;
-	int size;
-	int pi, bi;
-	for (i = 0; i < 8; i++) {
-		size = snd_hda_codec_read(codec, pin_nid, 0,
-						AC_VERB_GET_HDMI_DIP_SIZE, i);
-		if (size == 0)
-			continue;
-
-		hdmi_set_dip_index(codec, pin_nid, i, 0x0);
-		for (j = 1; j < 1000; j++) {
-			hdmi_write_dip_byte(codec, pin_nid, 0x0);
-			hdmi_get_dip_index(codec, pin_nid, &pi, &bi);
-			if (pi != i)
-				snd_printd(KERN_INFO "dip index %d: %d != %d\n",
-						bi, pi, i);
-			if (bi == 0) /* byte index wrapped around */
-				break;
-		}
-		snd_printd(KERN_INFO
-			"HDMI: DIP GP[%d] buf reported size=%d, written=%d\n",
-			i, size, j);
-	}
-#endif
-}
-
-static void hdmi_checksum_audio_infoframe(struct hdmi_audio_infoframe *ai)
-{
-	u8 *bytes = (u8 *)ai;
-	u8 sum = 0;
-	int i;
-
-	ai->checksum = 0;
-
-	for (i = 0; i < sizeof(*ai); i++)
-		sum += bytes[i];
-
-	ai->checksum = - sum;
-}
-
-static void hdmi_fill_audio_infoframe(struct hda_codec *codec,
-				      hda_nid_t pin_nid,
-				      struct hdmi_audio_infoframe *ai)
-{
-	u8 *bytes = (u8 *)ai;
-	int i;
-
-	hdmi_debug_dip_size(codec, pin_nid);
-	hdmi_clear_dip_buffers(codec, pin_nid); /* be paranoid */
-
-	hdmi_checksum_audio_infoframe(ai);
-
-	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-	for (i = 0; i < sizeof(*ai); i++)
-		hdmi_write_dip_byte(codec, pin_nid, bytes[i]);
-}
-
-/*
- * Compute derived values in channel_allocations[].
- */
-static void init_channel_allocations(void)
-{
-	int i, j;
-	struct cea_channel_speaker_allocation *p;
-
-	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-		p = channel_allocations + i;
-		p->channels = 0;
-		p->spk_mask = 0;
-		for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
-			if (p->speakers[j]) {
-				p->channels++;
-				p->spk_mask |= p->speakers[j];
-			}
-	}
-}
-
-/*
- * The transformation takes two steps:
- *
- * 	eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
- * 	      spk_mask => (channel_allocations[])         => ai->CA
- *
- * TODO: it could select the wrong CA from multiple candidates.
-*/
-static int hdmi_setup_channel_allocation(struct hda_codec *codec, hda_nid_t nid,
-					 struct hdmi_audio_infoframe *ai)
-{
-	struct intel_hdmi_spec *spec = codec->spec;
-	struct hdmi_eld *eld;
-	int i;
-	int spk_mask = 0;
-	int channels = 1 + (ai->CC02_CT47 & 0x7);
-	char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
-
-	/*
-	 * CA defaults to 0 for basic stereo audio
-	 */
-	if (channels <= 2)
-		return 0;
-
-	i = hda_node_index(spec->pin_cvt, nid);
-	if (i < 0)
-		return 0;
-	eld = &spec->sink_eld[i];
-
-	/*
-	 * HDMI sink's ELD info cannot always be retrieved for now, e.g.
-	 * in console or for audio devices. Assume the highest speakers
-	 * configuration, to _not_ prohibit multi-channel audio playback.
-	 */
-	if (!eld->spk_alloc)
-		eld->spk_alloc = 0xffff;
-
-	/*
-	 * expand ELD's speaker allocation mask
-	 *
-	 * ELD tells the speaker mask in a compact(paired) form,
-	 * expand ELD's notions to match the ones used by Audio InfoFrame.
-	 */
-	for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
-		if (eld->spk_alloc & (1 << i))
-			spk_mask |= eld_speaker_allocation_bits[i];
-	}
-
-	/* search for the first working match in the CA table */
-	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-		if (channels == channel_allocations[i].channels &&
-		    (spk_mask & channel_allocations[i].spk_mask) ==
-				channel_allocations[i].spk_mask) {
-			ai->CA = channel_allocations[i].ca_index;
-			break;
-		}
-	}
-
-	snd_print_channel_allocation(eld->spk_alloc, buf, sizeof(buf));
-	snd_printdd(KERN_INFO
-			"HDMI: select CA 0x%x for %d-channel allocation: %s\n",
-			ai->CA, channels, buf);
-
-	return ai->CA;
-}
-
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
-				       hda_nid_t pin_nid,
-				       struct hdmi_audio_infoframe *ai)
-{
-	int i;
-	int ca = ai->CA;
-	int err;
-
-	if (hdmi_channel_mapping[ca][1] == 0) {
-		for (i = 0; i < channel_allocations[ca].channels; i++)
-			hdmi_channel_mapping[ca][i] = i | (i << 4);
-		for (; i < 8; i++)
-			hdmi_channel_mapping[ca][i] = 0xf | (i << 4);
-	}
-
-	for (i = 0; i < 8; i++) {
-		err = snd_hda_codec_write(codec, pin_nid, 0,
-					  AC_VERB_SET_HDMI_CHAN_SLOT,
-					  hdmi_channel_mapping[ca][i]);
-		if (err) {
-			snd_printdd(KERN_INFO "HDMI: channel mapping failed\n");
-			break;
-		}
-	}
-
-	hdmi_debug_channel_mapping(codec, pin_nid);
-}
-
-static bool hdmi_infoframe_uptodate(struct hda_codec *codec, hda_nid_t pin_nid,
-				    struct hdmi_audio_infoframe *ai)
-{
-	u8 *bytes = (u8 *)ai;
-	u8 val;
-	int i;
-
-	if (snd_hda_codec_read(codec, pin_nid, 0, AC_VERB_GET_HDMI_DIP_XMIT, 0)
-							    != AC_DIPXMIT_BEST)
-		return false;
-
-	hdmi_set_dip_index(codec, pin_nid, 0x0, 0x0);
-	for (i = 0; i < sizeof(*ai); i++) {
-		val = snd_hda_codec_read(codec, pin_nid, 0,
-					 AC_VERB_GET_HDMI_DIP_DATA, 0);
-		if (val != bytes[i])
-			return false;
-	}
-
-	return true;
-}
-
-static void hdmi_setup_audio_infoframe(struct hda_codec *codec, hda_nid_t nid,
-					struct snd_pcm_substream *substream)
-{
-	struct intel_hdmi_spec *spec = codec->spec;
-	hda_nid_t pin_nid;
-	int i;
-	struct hdmi_audio_infoframe ai = {
-		.type		= 0x84,
-		.ver		= 0x01,
-		.len		= 0x0a,
-		.CC02_CT47	= substream->runtime->channels - 1,
-	};
-
-	hdmi_setup_channel_allocation(codec, nid, &ai);
-
-	for (i = 0; i < spec->num_pins; i++) {
-		if (spec->pin_cvt[i] != nid)
-			continue;
-		if (!spec->sink_eld[i].monitor_present)
-			continue;
-
-		pin_nid = spec->pin[i];
-		if (!hdmi_infoframe_uptodate(codec, pin_nid, &ai)) {
-			hdmi_setup_channel_mapping(codec, pin_nid, &ai);
-			hdmi_stop_infoframe_trans(codec, pin_nid);
-			hdmi_fill_audio_infoframe(codec, pin_nid, &ai);
-			hdmi_start_infoframe_trans(codec, pin_nid);
-		}
-	}
-}
-
-
-/*
- * Unsolicited events
- */
-
-static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
-{
-	struct intel_hdmi_spec *spec = codec->spec;
-	int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
-	int pind = !!(res & AC_UNSOL_RES_PD);
-	int eldv = !!(res & AC_UNSOL_RES_ELDV);
-	int index;
-
-	printk(KERN_INFO
-		"HDMI hot plug event: Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
-		tag, pind, eldv);
-
-	index = hda_node_index(spec->pin, tag);
-	if (index < 0)
-		return;
-
-	spec->sink_eld[index].monitor_present = pind;
-	spec->sink_eld[index].eld_valid = eldv;
-
-	if (pind && eldv) {
-		hdmi_get_show_eld(codec, spec->pin[index], &spec->sink_eld[index]);
-		/* TODO: do real things about ELD */
-	}
-}
-
-static void hdmi_non_intrinsic_event(struct hda_codec *codec, unsigned int res)
-{
-	int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
-	int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
-	int cp_state = !!(res & AC_UNSOL_RES_CP_STATE);
-	int cp_ready = !!(res & AC_UNSOL_RES_CP_READY);
-
-	printk(KERN_INFO
-		"HDMI CP event: PIN=%d SUBTAG=0x%x CP_STATE=%d CP_READY=%d\n",
-		tag,
-		subtag,
-		cp_state,
-		cp_ready);
-
-	/* TODO */
-	if (cp_state)
-		;
-	if (cp_ready)
-		;
-}
-
-
-static void intel_hdmi_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-	struct intel_hdmi_spec *spec = codec->spec;
-	int tag = res >> AC_UNSOL_RES_TAG_SHIFT;
-	int subtag = (res & AC_UNSOL_RES_SUBTAG) >> AC_UNSOL_RES_SUBTAG_SHIFT;
-
-	if (hda_node_index(spec->pin, tag) < 0) {
-		snd_printd(KERN_INFO "Unexpected HDMI event tag 0x%x\n", tag);
-		return;
-	}
-
-	if (subtag == 0)
-		hdmi_intrinsic_event(codec, res);
-	else
-		hdmi_non_intrinsic_event(codec, res);
-}
-
-/*
- * Callbacks
- */
-
-static void hdmi_setup_stream(struct hda_codec *codec, hda_nid_t nid,
-			      u32 stream_tag, int format)
-{
-	int tag;
-	int fmt;
-
-	tag = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0) >> 4;
-	fmt = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_STREAM_FORMAT, 0);
-
-	snd_printdd("hdmi_setup_stream: "
-		    "NID=0x%x, %sstream=0x%x, %sformat=0x%x\n",
-		    nid,
-		    tag == stream_tag ? "" : "new-",
-		    stream_tag,
-		    fmt == format ? "" : "new-",
-		    format);
-
-	if (tag != stream_tag)
-		snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_CHANNEL_STREAMID, stream_tag << 4);
-	if (fmt != format)
-		snd_hda_codec_write(codec, nid, 0,
-				    AC_VERB_SET_STREAM_FORMAT, format);
-}
 
 static int intel_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 					   struct hda_codec *codec,
@@ -882,7 +87,7 @@
 
 static int intel_hdmi_build_pcms(struct hda_codec *codec)
 {
-	struct intel_hdmi_spec *spec = codec->spec;
+	struct hdmi_spec *spec = codec->spec;
 	struct hda_pcm *info = spec->pcm_rec;
 	int i;
 
@@ -908,7 +113,7 @@
 
 static int intel_hdmi_build_controls(struct hda_codec *codec)
 {
-	struct intel_hdmi_spec *spec = codec->spec;
+	struct hdmi_spec *spec = codec->spec;
 	int err;
 	int i;
 
@@ -923,7 +128,7 @@
 
 static int intel_hdmi_init(struct hda_codec *codec)
 {
-	struct intel_hdmi_spec *spec = codec->spec;
+	struct hdmi_spec *spec = codec->spec;
 	int i;
 
 	for (i = 0; spec->pin[i]; i++) {
@@ -937,7 +142,7 @@
 
 static void intel_hdmi_free(struct hda_codec *codec)
 {
-	struct intel_hdmi_spec *spec = codec->spec;
+	struct hdmi_spec *spec = codec->spec;
 	int i;
 
 	for (i = 0; i < spec->num_pins; i++)
@@ -951,12 +156,12 @@
 	.free			= intel_hdmi_free,
 	.build_pcms		= intel_hdmi_build_pcms,
 	.build_controls 	= intel_hdmi_build_controls,
-	.unsol_event		= intel_hdmi_unsol_event,
+	.unsol_event		= hdmi_unsol_event,
 };
 
 static int patch_intel_hdmi(struct hda_codec *codec)
 {
-	struct intel_hdmi_spec *spec;
+	struct hdmi_spec *spec;
 	int i;
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
@@ -964,7 +169,7 @@
 		return -ENOMEM;
 
 	codec->spec = spec;
-	if (intel_hdmi_parse_codec(codec) < 0) {
+	if (hdmi_parse_codec(codec) < 0) {
 		codec->spec = NULL;
 		kfree(spec);
 		return -EINVAL;
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
index 6afdab0..70669a2 100644
--- a/sound/pci/hda/patch_nvhdmi.c
+++ b/sound/pci/hda/patch_nvhdmi.c
@@ -29,13 +29,23 @@
 #include "hda_codec.h"
 #include "hda_local.h"
 
+#define MAX_HDMI_CVTS	1
+#define MAX_HDMI_PINS	1
+
+#include "patch_hdmi.c"
+
+static char *nvhdmi_pcm_names[MAX_HDMI_CVTS] = {
+	"NVIDIA HDMI",
+};
+
 /* define below to restrict the supported rates and formats */
 /* #define LIMITED_RATE_FMT_SUPPORT */
 
-struct nvhdmi_spec {
-	struct hda_multi_out multiout;
-
-	struct hda_pcm pcm_rec;
+enum HDACodec {
+	HDA_CODEC_NVIDIA_MCP7X,
+	HDA_CODEC_NVIDIA_MCP89,
+	HDA_CODEC_NVIDIA_GT21X,
+	HDA_CODEC_INVALID
 };
 
 #define Nv_VERB_SET_Channel_Allocation          0xF79
@@ -43,15 +53,18 @@
 #define Nv_VERB_SET_Audio_Protection_On         0xF98
 #define Nv_VERB_SET_Audio_Protection_Off        0xF99
 
-#define Nv_Master_Convert_nid   0x04
-#define Nv_Master_Pin_nid       0x05
+#define nvhdmi_master_con_nid_7x	0x04
+#define nvhdmi_master_pin_nid_7x	0x05
 
-static hda_nid_t nvhdmi_convert_nids[4] = {
+#define nvhdmi_master_con_nid_89	0x04
+#define nvhdmi_master_pin_nid_89	0x05
+
+static hda_nid_t nvhdmi_con_nids_7x[4] = {
 	/*front, rear, clfe, rear_surr */
 	0x6, 0x8, 0xa, 0xc,
 };
 
-static struct hda_verb nvhdmi_basic_init[] = {
+static struct hda_verb nvhdmi_basic_init_7x[] = {
 	/* set audio protect on */
 	{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
 	/* enable digital output on pin widget */
@@ -84,22 +97,60 @@
  */
 static int nvhdmi_build_controls(struct hda_codec *codec)
 {
-	struct nvhdmi_spec *spec = codec->spec;
+	struct hdmi_spec *spec = codec->spec;
 	int err;
+	int i;
 
-	err = snd_hda_create_spdif_out_ctls(codec, spec->multiout.dig_out_nid);
-	if (err < 0)
-		return err;
+	if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
+	|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
+		for (i = 0; i < codec->num_pcms; i++) {
+			err = snd_hda_create_spdif_out_ctls(codec,
+							    spec->cvt[i]);
+			if (err < 0)
+				return err;
+		}
+	} else {
+		err = snd_hda_create_spdif_out_ctls(codec,
+						    spec->multiout.dig_out_nid);
+		if (err < 0)
+			return err;
+	}
 
 	return 0;
 }
 
 static int nvhdmi_init(struct hda_codec *codec)
 {
-	snd_hda_sequence_write(codec, nvhdmi_basic_init);
+	struct hdmi_spec *spec = codec->spec;
+	int i;
+	if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
+	|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
+		for (i = 0; spec->pin[i]; i++) {
+			hdmi_enable_output(codec, spec->pin[i]);
+			snd_hda_codec_write(codec, spec->pin[i], 0,
+					    AC_VERB_SET_UNSOLICITED_ENABLE,
+					    AC_USRSP_EN | spec->pin[i]);
+		}
+	} else {
+		snd_hda_sequence_write(codec, nvhdmi_basic_init_7x);
+	}
 	return 0;
 }
 
+static void nvhdmi_free(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec = codec->spec;
+	int i;
+
+	if ((spec->codec_type == HDA_CODEC_NVIDIA_MCP89)
+	|| (spec->codec_type == HDA_CODEC_NVIDIA_GT21X)) {
+		for (i = 0; i < spec->num_pins; i++)
+			snd_hda_eld_proc_free(codec, &spec->sink_eld[i]);
+	}
+
+	kfree(spec);
+}
+
 /*
  * Digital out
  */
@@ -107,25 +158,25 @@
 					struct hda_codec *codec,
 					struct snd_pcm_substream *substream)
 {
-	struct nvhdmi_spec *spec = codec->spec;
+	struct hdmi_spec *spec = codec->spec;
 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
 }
 
-static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo,
+static int nvhdmi_dig_playback_pcm_close_8ch_7x(struct hda_pcm_stream *hinfo,
 					struct hda_codec *codec,
 					struct snd_pcm_substream *substream)
 {
-	struct nvhdmi_spec *spec = codec->spec;
+	struct hdmi_spec *spec = codec->spec;
 	int i;
 
-	snd_hda_codec_write(codec, Nv_Master_Convert_nid,
+	snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x,
 			0, AC_VERB_SET_CHANNEL_STREAMID, 0);
 	for (i = 0; i < 4; i++) {
 		/* set the stream id */
-		snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
+		snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
 				AC_VERB_SET_CHANNEL_STREAMID, 0);
 		/* set the stream format */
-		snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
+		snd_hda_codec_write(codec, nvhdmi_con_nids_7x[i], 0,
 				AC_VERB_SET_STREAM_FORMAT, 0);
 	}
 
@@ -136,10 +187,25 @@
 					struct hda_codec *codec,
 					struct snd_pcm_substream *substream)
 {
-	struct nvhdmi_spec *spec = codec->spec;
+	struct hdmi_spec *spec = codec->spec;
 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
+static int nvhdmi_dig_playback_pcm_prepare_8ch_89(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					unsigned int stream_tag,
+					unsigned int format,
+					struct snd_pcm_substream *substream)
+{
+	hdmi_set_channel_count(codec, hinfo->nid,
+			       substream->runtime->channels);
+
+	hdmi_setup_audio_infoframe(codec, hinfo->nid, substream);
+
+	hdmi_setup_stream(codec, hinfo->nid, stream_tag, format);
+	return 0;
+}
+
 static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
 					struct hda_codec *codec,
 					unsigned int stream_tag,
@@ -181,29 +247,29 @@
 	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
 	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
 		snd_hda_codec_write(codec,
-				Nv_Master_Convert_nid,
+				nvhdmi_master_con_nid_7x,
 				0,
 				AC_VERB_SET_DIGI_CONVERT_1,
 				codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
 
 	/* set the stream id */
-	snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
+	snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
 			AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
 
 	/* set the stream format */
-	snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
+	snd_hda_codec_write(codec, nvhdmi_master_con_nid_7x, 0,
 			AC_VERB_SET_STREAM_FORMAT, format);
 
 	/* turn on again (if needed) */
 	/* enable and set the channel status audio/data flag */
 	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
 		snd_hda_codec_write(codec,
-				Nv_Master_Convert_nid,
+				nvhdmi_master_con_nid_7x,
 				0,
 				AC_VERB_SET_DIGI_CONVERT_1,
 				codec->spdif_ctls & 0xff);
 		snd_hda_codec_write(codec,
-				Nv_Master_Convert_nid,
+				nvhdmi_master_con_nid_7x,
 				0,
 				AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
 	}
@@ -220,19 +286,19 @@
 		if (codec->spdif_status_reset &&
 		(codec->spdif_ctls & AC_DIG1_ENABLE))
 			snd_hda_codec_write(codec,
-				nvhdmi_convert_nids[i],
+				nvhdmi_con_nids_7x[i],
 				0,
 				AC_VERB_SET_DIGI_CONVERT_1,
 				codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
 		/* set the stream id */
 		snd_hda_codec_write(codec,
-				nvhdmi_convert_nids[i],
+				nvhdmi_con_nids_7x[i],
 				0,
 				AC_VERB_SET_CHANNEL_STREAMID,
 				(stream_tag << 4) | channel_id);
 		/* set the stream format */
 		snd_hda_codec_write(codec,
-				nvhdmi_convert_nids[i],
+				nvhdmi_con_nids_7x[i],
 				0,
 				AC_VERB_SET_STREAM_FORMAT,
 				format);
@@ -241,12 +307,12 @@
 		if (codec->spdif_status_reset &&
 		(codec->spdif_ctls & AC_DIG1_ENABLE)) {
 			snd_hda_codec_write(codec,
-					nvhdmi_convert_nids[i],
+					nvhdmi_con_nids_7x[i],
 					0,
 					AC_VERB_SET_DIGI_CONVERT_1,
 					codec->spdif_ctls & 0xff);
 			snd_hda_codec_write(codec,
-					nvhdmi_convert_nids[i],
+					nvhdmi_con_nids_7x[i],
 					0,
 					AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
 		}
@@ -261,28 +327,47 @@
 	return 0;
 }
 
+static int nvhdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+					   struct hda_codec *codec,
+					   struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
 static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
 					struct hda_codec *codec,
 					unsigned int stream_tag,
 					unsigned int format,
 					struct snd_pcm_substream *substream)
 {
-	struct nvhdmi_spec *spec = codec->spec;
+	struct hdmi_spec *spec = codec->spec;
 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
 					format, substream);
 }
 
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = {
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_89 = {
+	.substreams = 1,
+	.channels_min = 2,
+	.rates = SUPPORTED_RATES,
+	.maxbps = SUPPORTED_MAXBPS,
+	.formats = SUPPORTED_FORMATS,
+	.ops = {
+		.prepare = nvhdmi_dig_playback_pcm_prepare_8ch_89,
+		.cleanup = nvhdmi_playback_pcm_cleanup,
+	},
+};
+
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch_7x = {
 	.substreams = 1,
 	.channels_min = 2,
 	.channels_max = 8,
-	.nid = Nv_Master_Convert_nid,
+	.nid = nvhdmi_master_con_nid_7x,
 	.rates = SUPPORTED_RATES,
 	.maxbps = SUPPORTED_MAXBPS,
 	.formats = SUPPORTED_FORMATS,
 	.ops = {
 		.open = nvhdmi_dig_playback_pcm_open,
-		.close = nvhdmi_dig_playback_pcm_close_8ch,
+		.close = nvhdmi_dig_playback_pcm_close_8ch_7x,
 		.prepare = nvhdmi_dig_playback_pcm_prepare_8ch
 	},
 };
@@ -291,7 +376,7 @@
 	.substreams = 1,
 	.channels_min = 2,
 	.channels_max = 2,
-	.nid = Nv_Master_Convert_nid,
+	.nid = nvhdmi_master_con_nid_7x,
 	.rates = SUPPORTED_RATES,
 	.maxbps = SUPPORTED_MAXBPS,
 	.formats = SUPPORTED_FORMATS,
@@ -302,10 +387,36 @@
 	},
 };
 
-static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
+static int nvhdmi_build_pcms_8ch_89(struct hda_codec *codec)
 {
-	struct nvhdmi_spec *spec = codec->spec;
-	struct hda_pcm *info = &spec->pcm_rec;
+	struct hdmi_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
+	int i;
+
+	codec->num_pcms = spec->num_cvts;
+	codec->pcm_info = info;
+
+	for (i = 0; i < codec->num_pcms; i++, info++) {
+		unsigned int chans;
+
+		chans = get_wcaps(codec, spec->cvt[i]);
+		chans = get_wcaps_channels(chans);
+
+		info->name = nvhdmi_pcm_names[i];
+		info->pcm_type = HDA_PCM_TYPE_HDMI;
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK]
+					= nvhdmi_pcm_digital_playback_8ch_89;
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->cvt[i];
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = chans;
+	}
+
+	return 0;
+}
+
+static int nvhdmi_build_pcms_8ch_7x(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
 
 	codec->num_pcms = 1;
 	codec->pcm_info = info;
@@ -313,15 +424,15 @@
 	info->name = "NVIDIA HDMI";
 	info->pcm_type = HDA_PCM_TYPE_HDMI;
 	info->stream[SNDRV_PCM_STREAM_PLAYBACK]
-					= nvhdmi_pcm_digital_playback_8ch;
+					= nvhdmi_pcm_digital_playback_8ch_7x;
 
 	return 0;
 }
 
 static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
 {
-	struct nvhdmi_spec *spec = codec->spec;
-	struct hda_pcm *info = &spec->pcm_rec;
+	struct hdmi_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
 
 	codec->num_pcms = 1;
 	codec->pcm_info = info;
@@ -334,14 +445,17 @@
 	return 0;
 }
 
-static void nvhdmi_free(struct hda_codec *codec)
-{
-	kfree(codec->spec);
-}
-
-static struct hda_codec_ops nvhdmi_patch_ops_8ch = {
+static struct hda_codec_ops nvhdmi_patch_ops_8ch_89 = {
 	.build_controls = nvhdmi_build_controls,
-	.build_pcms = nvhdmi_build_pcms_8ch,
+	.build_pcms = nvhdmi_build_pcms_8ch_89,
+	.init = nvhdmi_init,
+	.free = nvhdmi_free,
+	.unsol_event = hdmi_unsol_event,
+};
+
+static struct hda_codec_ops nvhdmi_patch_ops_8ch_7x = {
+	.build_controls = nvhdmi_build_controls,
+	.build_pcms = nvhdmi_build_pcms_8ch_7x,
 	.init = nvhdmi_init,
 	.free = nvhdmi_free,
 };
@@ -353,9 +467,36 @@
 	.free = nvhdmi_free,
 };
 
-static int patch_nvhdmi_8ch(struct hda_codec *codec)
+static int patch_nvhdmi_8ch_89(struct hda_codec *codec)
 {
-	struct nvhdmi_spec *spec;
+	struct hdmi_spec *spec;
+	int i;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+	spec->codec_type = HDA_CODEC_NVIDIA_MCP89;
+
+	if (hdmi_parse_codec(codec) < 0) {
+		codec->spec = NULL;
+		kfree(spec);
+		return -EINVAL;
+	}
+	codec->patch_ops = nvhdmi_patch_ops_8ch_89;
+
+	for (i = 0; i < spec->num_pins; i++)
+		snd_hda_eld_proc_new(codec, &spec->sink_eld[i], i);
+
+	init_channel_allocations();
+
+	return 0;
+}
+
+static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
+{
+	struct hdmi_spec *spec;
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
@@ -365,16 +506,17 @@
 
 	spec->multiout.num_dacs = 0;  /* no analog */
 	spec->multiout.max_channels = 8;
-	spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
+	spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
+	spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
 
-	codec->patch_ops = nvhdmi_patch_ops_8ch;
+	codec->patch_ops = nvhdmi_patch_ops_8ch_7x;
 
 	return 0;
 }
 
 static int patch_nvhdmi_2ch(struct hda_codec *codec)
 {
-	struct nvhdmi_spec *spec;
+	struct hdmi_spec *spec;
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
@@ -384,7 +526,8 @@
 
 	spec->multiout.num_dacs = 0;  /* no analog */
 	spec->multiout.max_channels = 2;
-	spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
+	spec->multiout.dig_out_nid = nvhdmi_master_con_nid_7x;
+	spec->codec_type = HDA_CODEC_NVIDIA_MCP7X;
 
 	codec->patch_ops = nvhdmi_patch_ops_2ch;
 
@@ -395,13 +538,24 @@
  * patch entries
  */
 static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
-	{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
-	{ .id = 0x10de0003, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
-	{ .id = 0x10de0005, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
-	{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
-	{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
 	{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
 	{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
+	{ .id = 0x10de0002, .name = "MCP77/78 HDMI",
+	  .patch = patch_nvhdmi_8ch_7x },
+	{ .id = 0x10de0003, .name = "MCP77/78 HDMI",
+	  .patch = patch_nvhdmi_8ch_7x },
+	{ .id = 0x10de0005, .name = "MCP77/78 HDMI",
+	  .patch = patch_nvhdmi_8ch_7x },
+	{ .id = 0x10de0006, .name = "MCP77/78 HDMI",
+	  .patch = patch_nvhdmi_8ch_7x },
+	{ .id = 0x10de0007, .name = "MCP79/7A HDMI",
+	  .patch = patch_nvhdmi_8ch_7x },
+	{ .id = 0x10de000c, .name = "MCP89 HDMI",
+	  .patch = patch_nvhdmi_8ch_89 },
+	{ .id = 0x10de000b, .name = "GT21x HDMI",
+	  .patch = patch_nvhdmi_8ch_89 },
+	{ .id = 0x10de000d, .name = "GT240 HDMI",
+	  .patch = patch_nvhdmi_8ch_89 },
 	{} /* terminator */
 };
 
@@ -412,9 +566,12 @@
 MODULE_ALIAS("snd-hda-codec-id:10de0007");
 MODULE_ALIAS("snd-hda-codec-id:10de0067");
 MODULE_ALIAS("snd-hda-codec-id:10de8001");
+MODULE_ALIAS("snd-hda-codec-id:10de000c");
+MODULE_ALIAS("snd-hda-codec-id:10de000b");
+MODULE_ALIAS("snd-hda-codec-id:10de000d");
 
 MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Nvidia HDMI HD-audio codec");
+MODULE_DESCRIPTION("NVIDIA HDMI HD-audio codec");
 
 static struct hda_codec_preset_list nvhdmi_list = {
 	.preset = snd_hda_preset_nvhdmi,
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index f628c33..3a83719 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -131,8 +131,10 @@
 enum {
 	ALC269_BASIC,
 	ALC269_QUANTA_FL1,
-	ALC269_ASUS_AMIC,
-	ALC269_ASUS_DMIC,
+	ALC269_AMIC,
+	ALC269_DMIC,
+	ALC269VB_AMIC,
+	ALC269VB_DMIC,
 	ALC269_FUJITSU,
 	ALC269_LIFEBOOK,
 	ALC269_AUTO,
@@ -207,8 +209,10 @@
 	ALC882_ASUS_A7J,
 	ALC882_ASUS_A7M,
 	ALC885_MACPRO,
+	ALC885_MBA21,
 	ALC885_MBP3,
 	ALC885_MB5,
+	ALC885_MACMINI3,
 	ALC885_IMAC24,
 	ALC885_IMAC91,
 	ALC883_3ST_2ch_DIG,
@@ -407,6 +411,8 @@
 	unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id);
 	if (mux_idx >= spec->num_mux_defs)
 		mux_idx = 0;
+	if (!spec->input_mux[mux_idx].num_items && mux_idx > 0)
+		mux_idx = 0;
 	return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo);
 }
 
@@ -435,6 +441,8 @@
 
 	mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
 	imux = &spec->input_mux[mux_idx];
+	if (!imux->num_items && mux_idx > 0)
+		imux = &spec->input_mux[0];
 
 	type = get_wcaps_type(get_wcaps(codec, nid));
 	if (type == AC_WID_AUD_MIX) {
@@ -841,27 +849,6 @@
 	spec->init_verbs[spec->num_init_verbs++] = verb;
 }
 
-#ifdef CONFIG_PROC_FS
-/*
- * hook for proc
- */
-static void print_realtek_coef(struct snd_info_buffer *buffer,
-			       struct hda_codec *codec, hda_nid_t nid)
-{
-	int coeff;
-
-	if (nid != 0x20)
-		return;
-	coeff = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PROC_COEF, 0);
-	snd_iprintf(buffer, "  Processing Coefficient: 0x%02x\n", coeff);
-	coeff = snd_hda_codec_read(codec, nid, 0,
-				   AC_VERB_GET_COEF_INDEX, 0);
-	snd_iprintf(buffer, "  Coefficient Index: 0x%02x\n", coeff);
-}
-#else
-#define print_realtek_coef	NULL
-#endif
-
 /*
  * set up from the preset table
  */
@@ -1166,6 +1153,7 @@
 		case 0x10ec0888:
 			alc888_coef_init(codec);
 			break;
+#if 0 /* XXX: This may cause the silent output on speaker on some machines */
 		case 0x10ec0267:
 		case 0x10ec0268:
 			snd_hda_codec_write(codec, 0x20, 0,
@@ -1178,6 +1166,7 @@
 					    AC_VERB_SET_PROC_COEF,
 					    tmp | 0x3000);
 			break;
+#endif /* XXX */
 		}
 		break;
 	}
@@ -1269,7 +1258,7 @@
  */
 static int alc_subsystem_id(struct hda_codec *codec,
 			    hda_nid_t porta, hda_nid_t porte,
-			    hda_nid_t portd)
+			    hda_nid_t portd, hda_nid_t porti)
 {
 	unsigned int ass, tmp, i;
 	unsigned nid;
@@ -1295,7 +1284,7 @@
 	snd_printd("realtek: No valid SSID, "
 		   "checking pincfg 0x%08x for NID 0x%x\n",
 		   ass, nid);
-	if (!(ass & 1) && !(ass & 0x100000))
+	if (!(ass & 1))
 		return 0;
 	if ((ass >> 30) != 1)	/* no physical connection */
 		return 0;
@@ -1355,6 +1344,8 @@
 			nid = porte;
 		else if (tmp == 2)
 			nid = portd;
+		else if (tmp == 3)
+			nid = porti;
 		else
 			return 1;
 		for (i = 0; i < spec->autocfg.line_outs; i++)
@@ -1369,9 +1360,10 @@
 }
 
 static void alc_ssid_check(struct hda_codec *codec,
-			   hda_nid_t porta, hda_nid_t porte, hda_nid_t portd)
+			   hda_nid_t porta, hda_nid_t porte,
+			   hda_nid_t portd, hda_nid_t porti)
 {
-	if (!alc_subsystem_id(codec, porta, porte, portd)) {
+	if (!alc_subsystem_id(codec, porta, porte, portd, porti)) {
 		struct alc_spec *spec = codec->spec;
 		snd_printd("realtek: "
 			   "Enable default setup for auto mode as fallback\n");
@@ -3729,25 +3721,22 @@
 	/* We currently only handle front, HP */
 	switch (codec->vendor_id) {
 	case 0x10ec0260:
-		snd_hda_codec_write(codec, 0x0f, 0,
-				    AC_VERB_SET_EAPD_BTLENABLE, 0x00);
-		snd_hda_codec_write(codec, 0x10, 0,
-				    AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+		set_eapd(codec, 0x0f, 0);
+		set_eapd(codec, 0x10, 0);
 		break;
 	case 0x10ec0262:
 	case 0x10ec0267:
 	case 0x10ec0268:
 	case 0x10ec0269:
+	case 0x10ec0270:
 	case 0x10ec0272:
 	case 0x10ec0660:
 	case 0x10ec0662:
 	case 0x10ec0663:
 	case 0x10ec0862:
 	case 0x10ec0889:
-		snd_hda_codec_write(codec, 0x14, 0,
-				    AC_VERB_SET_EAPD_BTLENABLE, 0x00);
-		snd_hda_codec_write(codec, 0x15, 0,
-				    AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+		set_eapd(codec, 0x14, 0);
+		set_eapd(codec, 0x15, 0);
 		break;
 	}
 }
@@ -4877,7 +4866,7 @@
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux[0];
 
-	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
 
 	return 1;
 }
@@ -4930,7 +4919,7 @@
 static void fixup_single_adc(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
-	hda_nid_t pin;
+	hda_nid_t pin = 0;
 	int i;
 
 	/* search for the input pin; there must be only one */
@@ -5081,7 +5070,6 @@
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc880_loopbacks;
 #endif
-	codec->proc_widget_hook = print_realtek_coef;
 
 	return 0;
 }
@@ -6412,7 +6400,7 @@
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux[0];
 
-	alc_ssid_check(codec, 0x10, 0x15, 0x0f);
+	alc_ssid_check(codec, 0x10, 0x15, 0x0f, 0);
 
 	return 1;
 }
@@ -6691,7 +6679,6 @@
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc260_loopbacks;
 #endif
-	codec->proc_widget_hook = print_realtek_coef;
 
 	return 0;
 }
@@ -6773,6 +6760,14 @@
 	},
 };
 
+static struct hda_input_mux macmini3_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+
 static struct hda_input_mux alc883_3stack_6ch_intel = {
 	.num_items = 4,
 	.items = {
@@ -6961,6 +6956,13 @@
 	{ 8, alc882_sixstack_ch8_init },
 };
 
+
+/* Macbook Air 2,1 */
+
+static struct hda_channel_mode alc885_mba21_ch_modes[1] = {
+      { 2, NULL },
+};
+
 /*
  * macbook pro ALC885 can switch LineIn to LineOut without losing Mic
  */
@@ -7021,6 +7023,7 @@
 	{ 6, alc885_mb5_ch6_init },
 };
 
+#define alc885_macmini3_6ch_modes	alc885_mb5_6ch_modes
 
 /*
  * 2ch mode
@@ -7232,6 +7235,15 @@
 	{ } /* end */
 };
 
+/* Macbook Air 2,1 same control for HP and internal Speaker */
+
+static struct snd_kcontrol_new alc885_mba21_mixer[] = {
+      HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+      HDA_BIND_MUTE("Speaker Playback Switch", 0x0c, 0x02, HDA_OUTPUT),
+     { }
+};
+
+
 static struct snd_kcontrol_new alc885_mbp3_mixer[] = {
 	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
 	HDA_BIND_MUTE   ("Speaker Playback Switch", 0x0c, 0x02, HDA_INPUT),
@@ -7265,6 +7277,21 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc885_macmini3_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+	HDA_BIND_MUTE   ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
+	HDA_BIND_MUTE   ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
+	HDA_BIND_MUTE   ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0f, 0x00, HDA_OUTPUT),
+	HDA_BIND_MUTE   ("Headphone Playback Switch", 0x0f, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x07, HDA_INPUT),
+	HDA_CODEC_MUTE  ("Line Playback Switch", 0x0b, 0x07, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Boost", 0x15, 0x00, HDA_INPUT),
+	{ } /* end */
+};
+
 static struct snd_kcontrol_new alc885_imac91_mixer[] = {
 	HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
 	HDA_BIND_MUTE   ("Line-Out Playback Switch", 0x0c, 0x02, HDA_INPUT),
@@ -7356,29 +7383,18 @@
 
 static struct hda_verb alc882_base_init_verbs[] = {
 	/* Front mixer: unmute input/output amp left and right (volume = 0) */
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 	/* Rear mixer */
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
 	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 	/* CLFE mixer */
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
 	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 	/* Side mixer */
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
 	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
 
-	/* mute analog input loopbacks */
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
 	/* Front Pin: output 0 (0x0c) */
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
@@ -7415,14 +7431,8 @@
 	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
 	/* Input mixer2 */
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	/* Input mixer3 */
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 	/* ADC2: mute amp left and right */
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x08, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -7466,26 +7476,17 @@
 
 static struct hda_verb alc885_init_verbs[] = {
 	/* Front mixer: unmute input/output amp left and right (volume = 0) */
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	/* Rear mixer */
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	/* CLFE mixer */
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 	/* Side mixer */
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-
-	/* mute analog input loopbacks */
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 
 	/* Front HP Pin: output 0 (0x0c) */
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
@@ -7519,17 +7520,11 @@
 
 	/* Mixer elements: 0x18, , 0x1a, 0x1b */
 	/* Input mixer1 */
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	/* Input mixer2 */
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
 	/* Input mixer3 */
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	/* ADC2: mute amp left and right */
 	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	/* ADC3: mute amp left and right */
@@ -7671,6 +7666,76 @@
 	{ }
 };
 
+/* Macmini 3,1 */
+static struct hda_verb alc885_macmini3_init_verbs[] = {
+	/* DACs */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Front mixer */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Surround mixer */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* LFE mixer */
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* HP mixer */
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Front Pin (0x0c) */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* LFE Pin (0x0e) */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* HP Pin (0x0f) */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x03},
+	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	/* Line In pin */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	{ }
+};
+
+
+static struct hda_verb alc885_mba21_init_verbs[] = {
+	/*Internal and HP Speaker Mixer*/
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	/*Internal Speaker Pin (0x0c)*/
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, (PIN_OUT | AC_PINCTL_VREF_50) },
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* HP Pin: output 0 (0x0e) */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, (ALC880_HP_EVENT | AC_USRSP_EN)},
+	/* Line in (is hp when jack connected)*/
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_VREF_50},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+	{ }
+ };
+
+
 /* Macbook Pro rev3 */
 static struct hda_verb alc885_mbp3_init_verbs[] = {
 	/* Front mixer: unmute input/output amp left and right (volume = 0) */
@@ -7833,6 +7898,20 @@
 	spec->autocfg.speaker_pins[1] = 0x1a;
 }
 
+#define alc885_mb5_setup	alc885_imac24_setup
+#define alc885_macmini3_setup	alc885_imac24_setup
+
+/* Macbook Air 2,1 */
+static void alc885_mba21_setup(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+
+       spec->autocfg.hp_pins[0] = 0x14;
+       spec->autocfg.speaker_pins[0] = 0x18;
+}
+
+
+
 static void alc885_mbp3_setup(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
@@ -7841,46 +7920,13 @@
 	spec->autocfg.speaker_pins[0] = 0x14;
 }
 
-static void alc885_mb5_automute(struct hda_codec *codec)
+static void alc885_imac91_setup(struct hda_codec *codec)
 {
-	unsigned int present;
+	struct alc_spec *spec = codec->spec;
 
-	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-	snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-
-}
-
-static void alc885_mb5_unsol_event(struct hda_codec *codec,
-				    unsigned int res)
-{
-	/* Headphone insertion or removal. */
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc885_mb5_automute(codec);
-}
-
-static void alc885_imac91_automute(struct hda_codec *codec)
-{
- 	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-	snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-
-}
-
-static void alc885_imac91_unsol_event(struct hda_codec *codec,
-				    unsigned int res)
-{
-	/* Headphone insertion or removal. */
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc885_imac91_automute(codec);
+	spec->autocfg.hp_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[1] = 0x1a;
 }
 
 static struct hda_verb alc882_targa_verbs[] = {
@@ -8015,18 +8061,6 @@
 	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
 	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
-	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-	 * mixer widget
-	 * Note: PASD motherboards uses the Line In 2 as the input for
-	 * front panel mic (mic 2)
-	 */
-	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
 	/*
 	 * Set up output mixers (0x0c - 0x0f)
 	 */
@@ -8051,16 +8085,9 @@
 	/* FIXME: use matrix-type input source selection */
 	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
 	/* Input mixer2 */
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	/* Input mixer3 */
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
-
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{ }
 };
 
@@ -9047,6 +9074,8 @@
 	[ALC882_ASUS_A7M]	= "asus-a7m",
 	[ALC885_MACPRO]		= "macpro",
 	[ALC885_MB5]		= "mb5",
+	[ALC885_MACMINI3]	= "macmini3",
+	[ALC885_MBA21]		= "mba21",
 	[ALC885_MBP3]		= "mbp3",
 	[ALC885_IMAC24]		= "imac24",
 	[ALC885_IMAC91]		= "imac91",
@@ -9230,6 +9259,7 @@
 	 */
 	SND_PCI_QUIRK(0x106b, 0x4000, "MacbookPro 5,1", ALC885_MB5),
 	SND_PCI_QUIRK(0x106b, 0x4600, "MacbookPro 5,2", ALC885_MB5),
+	SND_PCI_QUIRK(0x106b, 0x4100, "Macmini 3,1", ALC885_MACMINI3),
 	{} /* terminator */
 };
 
@@ -9281,6 +9311,18 @@
 		.input_mux = &alc882_capture_source,
 		.dig_out_nid = ALC882_DIGOUT_NID,
 	},
+	   [ALC885_MBA21] = {
+			.mixers = { alc885_mba21_mixer },
+			.init_verbs = { alc885_mba21_init_verbs, alc880_gpio1_init_verbs },
+			.num_dacs = 2,
+			.dac_nids = alc882_dac_nids,
+			.channel_mode = alc885_mba21_ch_modes,
+			.num_channel_mode = ARRAY_SIZE(alc885_mba21_ch_modes),
+			.input_mux = &alc882_capture_source,
+			.unsol_event = alc_automute_amp_unsol_event,
+			.setup = alc885_mba21_setup,
+			.init_hook = alc_automute_amp,
+       },
 	[ALC885_MBP3] = {
 		.mixers = { alc885_mbp3_mixer, alc882_chmode_mixer },
 		.init_verbs = { alc885_mbp3_init_verbs,
@@ -9308,8 +9350,24 @@
 		.input_mux = &mb5_capture_source,
 		.dig_out_nid = ALC882_DIGOUT_NID,
 		.dig_in_nid = ALC882_DIGIN_NID,
-		.unsol_event = alc885_mb5_unsol_event,
-		.init_hook = alc885_mb5_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.setup = alc885_mb5_setup,
+		.init_hook = alc_automute_amp,
+	},
+	[ALC885_MACMINI3] = {
+		.mixers = { alc885_macmini3_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc885_macmini3_init_verbs,
+				alc880_gpio1_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.channel_mode = alc885_macmini3_6ch_modes,
+		.num_channel_mode = ARRAY_SIZE(alc885_macmini3_6ch_modes),
+		.input_mux = &macmini3_capture_source,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.setup = alc885_macmini3_setup,
+		.init_hook = alc_automute_amp,
 	},
 	[ALC885_MACPRO] = {
 		.mixers = { alc882_macpro_mixer },
@@ -9348,8 +9406,9 @@
 		.input_mux = &alc882_capture_source,
 		.dig_out_nid = ALC882_DIGOUT_NID,
 		.dig_in_nid = ALC882_DIGIN_NID,
-		.unsol_event = alc885_imac91_unsol_event,
-		.init_hook = alc885_imac91_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.setup = alc885_imac91_setup,
+		.init_hook = alc_automute_amp,
 	},
 	[ALC882_TARGA] = {
 		.mixers = { alc882_targa_mixer, alc882_chmode_mixer },
@@ -10050,6 +10109,8 @@
 			continue;
 		mux_idx = c >= spec->num_mux_defs ? 0 : c;
 		imux = &spec->input_mux[mux_idx];
+		if (!imux->num_items && mux_idx > 0)
+			imux = &spec->input_mux[0];
 		for (idx = 0; idx < conns; idx++) {
 			/* if the current connection is the selected one,
 			 * unmute it as default - otherwise mute it
@@ -10172,7 +10233,7 @@
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux[0];
 
-	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
 
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
@@ -10310,7 +10371,6 @@
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc882_loopbacks;
 #endif
-	codec->proc_widget_hook = print_realtek_coef;
 
 	return 0;
 }
@@ -11731,7 +11791,7 @@
 	if (err < 0)
 		return err;
 
-	alc_ssid_check(codec, 0x15, 0x14, 0x1b);
+	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
 
 	return 1;
 }
@@ -12174,7 +12234,6 @@
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc262_loopbacks;
 #endif
-	codec->proc_widget_hook = print_realtek_coef;
 
 	return 0;
 }
@@ -12683,7 +12742,6 @@
 		dac = 0x02;
 		break;
 	case 0x15:
-	case 0x21:
 		dac = 0x03;
 		break;
 	default:
@@ -12904,7 +12962,7 @@
 	if (err < 0)
 		return err;
 
-	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
 
 	return 1;
 }
@@ -13149,7 +13207,7 @@
 
 	if (board_config < 0 || board_config >= ALC268_MODEL_LAST)
 		board_config = snd_hda_check_board_codec_sid_config(codec,
-			ALC882_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl);
+			ALC268_MODEL_LAST, alc268_models, alc268_ssid_cfg_tbl);
 
 	if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
 		printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
@@ -13241,8 +13299,6 @@
 	if (board_config == ALC268_AUTO)
 		spec->init_hook = alc268_auto_init;
 
-	codec->proc_widget_hook = print_realtek_coef;
-
 	return 0;
 }
 
@@ -13262,6 +13318,15 @@
 	0x23,
 };
 
+static hda_nid_t alc269vb_adc_nids[1] = {
+	/* ADC1 */
+	0x09,
+};
+
+static hda_nid_t alc269vb_capsrc_nids[1] = {
+	0x22,
+};
+
 /* NOTE: ADC2 (0x07) is connected from a recording *MIXER* (0x24),
  *       not a mux!
  */
@@ -13330,7 +13395,7 @@
 	{ }
 };
 
-static struct snd_kcontrol_new alc269_eeepc_mixer[] = {
+static struct snd_kcontrol_new alc269_laptop_mixer[] = {
 	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
@@ -13338,16 +13403,47 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc269vb_laptop_mixer[] = {
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+	{ } /* end */
+};
+
 /* capture mixer elements */
-static struct snd_kcontrol_new alc269_epc_capture_mixer[] = {
+static struct snd_kcontrol_new alc269_laptop_analog_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("IntMic Boost", 0x19, 0, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc269_laptop_digital_capture_mixer[] = {
 	HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc269vb_laptop_analog_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("IntMic Boost", 0x19, 0, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc269vb_laptop_digital_capture_mixer[] = {
+	HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	{ } /* end */
+};
+
 /* FSC amilo */
-#define alc269_fujitsu_mixer	alc269_eeepc_mixer
+#define alc269_fujitsu_mixer	alc269_laptop_mixer
 
 static struct hda_verb alc269_quanta_fl1_verbs[] = {
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
@@ -13471,6 +13567,8 @@
 static void alc269_quanta_fl1_setup(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->ext_mic.pin = 0x18;
 	spec->ext_mic.mux_idx = 0;
 	spec->int_mic.pin = 0x19;
@@ -13490,7 +13588,7 @@
 	alc269_lifebook_mic_autoswitch(codec);
 }
 
-static struct hda_verb alc269_eeepc_dmic_init_verbs[] = {
+static struct hda_verb alc269_laptop_dmic_init_verbs[] = {
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
 	{0x23, AC_VERB_SET_CONNECT_SEL, 0x05},
 	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
@@ -13501,7 +13599,7 @@
 	{}
 };
 
-static struct hda_verb alc269_eeepc_amic_init_verbs[] = {
+static struct hda_verb alc269_laptop_amic_init_verbs[] = {
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
 	{0x23, AC_VERB_SET_CONNECT_SEL, 0x01},
 	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
@@ -13511,6 +13609,28 @@
 	{}
 };
 
+static struct hda_verb alc269vb_laptop_dmic_init_verbs[] = {
+	{0x21, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x22, AC_VERB_SET_CONNECT_SEL, 0x06},
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
+static struct hda_verb alc269vb_laptop_amic_init_verbs[] = {
+	{0x21, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x22, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, 0xb026 },
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, (0x7019 | (0x00 << 8))},
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_MIC_EVENT},
+	{0x21, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
+	{}
+};
+
 /* toggle speaker-output according to the hp-jack state */
 static void alc269_speaker_automute(struct hda_codec *codec)
 {
@@ -13528,7 +13648,7 @@
 }
 
 /* unsolicited event for HP jack sensing */
-static void alc269_eeepc_unsol_event(struct hda_codec *codec,
+static void alc269_laptop_unsol_event(struct hda_codec *codec,
 				     unsigned int res)
 {
 	switch (res >> 26) {
@@ -13541,9 +13661,11 @@
 	}
 }
 
-static void alc269_eeepc_dmic_setup(struct hda_codec *codec)
+static void alc269_laptop_dmic_setup(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->ext_mic.pin = 0x18;
 	spec->ext_mic.mux_idx = 0;
 	spec->int_mic.pin = 0x12;
@@ -13551,9 +13673,23 @@
 	spec->auto_mic = 1;
 }
 
-static void alc269_eeepc_amic_setup(struct hda_codec *codec)
+static void alc269vb_laptop_dmic_setup(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	spec->ext_mic.pin = 0x18;
+	spec->ext_mic.mux_idx = 0;
+	spec->int_mic.pin = 0x12;
+	spec->int_mic.mux_idx = 6;
+	spec->auto_mic = 1;
+}
+
+static void alc269_laptop_amic_setup(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
 	spec->ext_mic.pin = 0x18;
 	spec->ext_mic.mux_idx = 0;
 	spec->int_mic.pin = 0x19;
@@ -13561,7 +13697,7 @@
 	spec->auto_mic = 1;
 }
 
-static void alc269_eeepc_inithook(struct hda_codec *codec)
+static void alc269_laptop_inithook(struct hda_codec *codec)
 {
 	alc269_speaker_automute(codec);
 	alc_mic_automute(codec);
@@ -13574,22 +13710,10 @@
 	/*
 	 * Unmute ADC0 and set the default input to mic-in
 	 */
-	{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-	/* Mute input amps (PCBeep, Line In, Mic 1 & Mic 2) of the
-	 * analog-loopback mixer widget
-	 * Note: PASD motherboards uses the Line In 2 as the input for
-	 * front panel mic (mic 2)
-	 */
-	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 
 	/*
-	 * Set up output mixers (0x0c - 0x0e)
+	 * Set up output mixers (0x02 - 0x03)
 	 */
 	/* set vol=0 to output mixers */
 	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
@@ -13614,26 +13738,57 @@
 
 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
 
-	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
-
-	/* FIXME: use matrix-type input source selection */
+	/* FIXME: use Mux-type input source selection */
 	/* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
 	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x23, AC_VERB_SET_CONNECT_SEL, 0x00},
 
 	/* set EAPD */
 	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
-	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 2},
+	{ }
+};
+
+static struct hda_verb alc269vb_init_verbs[] = {
+	/*
+	 * Unmute ADC0 and set the default input to mic-in
+	 */
+	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+
+	/*
+	 * Set up output mixers (0x02 - 0x03)
+	 */
+	/* set vol=0 to output mixers */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+
+	/* set up input amps for analog loopback */
+	/* Amp Indices: DAC = 0, mixer = 1 */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+	/* FIXME: use Mux-type input source selection */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1d, 0b */
+	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	{0x22, AC_VERB_SET_CONNECT_SEL, 0x00},
+
+	/* set EAPD */
+	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 2},
 	{ }
 };
 
@@ -13681,6 +13836,7 @@
 	struct alc_spec *spec = codec->spec;
 	int err;
 	static hda_nid_t alc269_ignore[] = { 0x1d, 0 };
+	hda_nid_t real_capsrc_nids;
 
 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
 					   alc269_ignore);
@@ -13702,11 +13858,20 @@
 	if (spec->kctls.list)
 		add_mixer(spec, spec->kctls.list);
 
-	add_verb(spec, alc269_init_verbs);
+	if ((alc_read_coef_idx(codec, 0) & 0x00f0) == 0x0010) {
+		add_verb(spec, alc269vb_init_verbs);
+		real_capsrc_nids = alc269vb_capsrc_nids[0];
+		alc_ssid_check(codec, 0, 0x1b, 0x14, 0x21);
+	} else {
+		add_verb(spec, alc269_init_verbs);
+		real_capsrc_nids = alc269_capsrc_nids[0];
+		alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
+	}
+
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux[0];
 	/* set default input source */
-	snd_hda_codec_write_cache(codec, alc269_capsrc_nids[0],
+	snd_hda_codec_write_cache(codec, real_capsrc_nids,
 				  0, AC_VERB_SET_CONNECT_SEL,
 				  spec->input_mux->items[0].index);
 
@@ -13717,8 +13882,6 @@
 	if (!spec->cap_mixer && !spec->no_analog)
 		set_capture_mixer(codec);
 
-	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
-
 	return 1;
 }
 
@@ -13744,8 +13907,8 @@
 static const char *alc269_models[ALC269_MODEL_LAST] = {
 	[ALC269_BASIC]			= "basic",
 	[ALC269_QUANTA_FL1]		= "quanta",
-	[ALC269_ASUS_AMIC]		= "asus-amic",
-	[ALC269_ASUS_DMIC]		= "asus-dmic",
+	[ALC269_AMIC]			= "laptop-amic",
+	[ALC269_DMIC]			= "laptop-dmic",
 	[ALC269_FUJITSU]		= "fujitsu",
 	[ALC269_LIFEBOOK]		= "lifebook",
 	[ALC269_AUTO]			= "auto",
@@ -13754,43 +13917,57 @@
 static struct snd_pci_quirk alc269_cfg_tbl[] = {
 	SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_QUANTA_FL1),
 	SND_PCI_QUIRK(0x1043, 0x8330, "ASUS Eeepc P703 P900A",
-		      ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80JT", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82Jv", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_ASUS_DMIC),
-	SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_ASUS_AMIC),
-	SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_ASUS_AMIC),
+		      ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1013, "ASUS N61Da", ALC269VB_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1113, "ASUS N63Jn", ALC269VB_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1143, "ASUS B53f", ALC269VB_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1133, "ASUS UJ20ft", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1183, "ASUS K72DR", ALC269VB_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x11b3, "ASUS K52DR", ALC269VB_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x11e3, "ASUS U33Jc", ALC269VB_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1273, "ASUS UL80Jt", ALC269VB_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1283, "ASUS U53Jc", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS N82Jv", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x12d3, "ASUS N61Jv", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x13a3, "ASUS UL30Vt", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1373, "ASUS G73JX", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1383, "ASUS UJ30Jc", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x13d3, "ASUS N61JA", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1413, "ASUS UL50", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1443, "ASUS UL30", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1453, "ASUS M60Jv", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1483, "ASUS UL80", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x14f3, "ASUS F83Vf", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x14e3, "ASUS UL20", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1513, "ASUS UX30", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1593, "ASUS N51Vn", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x15a3, "ASUS N60Jv", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x15b3, "ASUS N60Dp", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x15c3, "ASUS N70De", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x15e3, "ASUS F83T", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1643, "ASUS M60J", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1653, "ASUS U50", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1693, "ASUS F50N", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x16a3, "ASUS F5Q", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_DMIC),
+	SND_PCI_QUIRK(0x1043, 0x1723, "ASUS P80", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1743, "ASUS U80", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1773, "ASUS U20A", ALC269_AMIC),
+	SND_PCI_QUIRK(0x1043, 0x1883, "ASUS F81Se", ALC269_AMIC),
 	SND_PCI_QUIRK(0x1043, 0x831a, "ASUS Eeepc P901",
-		      ALC269_ASUS_DMIC),
+		      ALC269_DMIC),
 	SND_PCI_QUIRK(0x1043, 0x834a, "ASUS Eeepc S101",
-		      ALC269_ASUS_DMIC),
-	SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_ASUS_DMIC),
-	SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_ASUS_DMIC),
-	SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU),
+		      ALC269_DMIC),
+	SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005HA", ALC269_DMIC),
+	SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005HA", ALC269_DMIC),
+	SND_PCI_QUIRK(0x104d, 0x9071, "SONY XTB", ALC269_DMIC),
 	SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook ICH9M-based", ALC269_LIFEBOOK),
+	SND_PCI_QUIRK(0x152d, 0x1778, "Quanta ON1", ALC269_DMIC),
+	SND_PCI_QUIRK(0x1734, 0x115d, "FSC Amilo", ALC269_FUJITSU),
+	SND_PCI_QUIRK(0x17aa, 0x3be9, "Quanta Wistron", ALC269_AMIC),
+	SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_AMIC),
+	SND_PCI_QUIRK(0x17ff, 0x059a, "Quanta EL3", ALC269_DMIC),
+	SND_PCI_QUIRK(0x17ff, 0x059b, "Quanta JR1", ALC269_DMIC),
 	{}
 };
 
@@ -13818,47 +13995,75 @@
 		.setup = alc269_quanta_fl1_setup,
 		.init_hook = alc269_quanta_fl1_init_hook,
 	},
-	[ALC269_ASUS_AMIC] = {
-		.mixers = { alc269_eeepc_mixer },
-		.cap_mixer = alc269_epc_capture_mixer,
+	[ALC269_AMIC] = {
+		.mixers = { alc269_laptop_mixer },
+		.cap_mixer = alc269_laptop_analog_capture_mixer,
 		.init_verbs = { alc269_init_verbs,
-				alc269_eeepc_amic_init_verbs },
+				alc269_laptop_amic_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
 		.dac_nids = alc269_dac_nids,
 		.hp_nid = 0x03,
 		.num_channel_mode = ARRAY_SIZE(alc269_modes),
 		.channel_mode = alc269_modes,
-		.unsol_event = alc269_eeepc_unsol_event,
-		.setup = alc269_eeepc_amic_setup,
-		.init_hook = alc269_eeepc_inithook,
+		.unsol_event = alc269_laptop_unsol_event,
+		.setup = alc269_laptop_amic_setup,
+		.init_hook = alc269_laptop_inithook,
 	},
-	[ALC269_ASUS_DMIC] = {
-		.mixers = { alc269_eeepc_mixer },
-		.cap_mixer = alc269_epc_capture_mixer,
+	[ALC269_DMIC] = {
+		.mixers = { alc269_laptop_mixer },
+		.cap_mixer = alc269_laptop_digital_capture_mixer,
 		.init_verbs = { alc269_init_verbs,
-				alc269_eeepc_dmic_init_verbs },
+				alc269_laptop_dmic_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
 		.dac_nids = alc269_dac_nids,
 		.hp_nid = 0x03,
 		.num_channel_mode = ARRAY_SIZE(alc269_modes),
 		.channel_mode = alc269_modes,
-		.unsol_event = alc269_eeepc_unsol_event,
-		.setup = alc269_eeepc_dmic_setup,
-		.init_hook = alc269_eeepc_inithook,
+		.unsol_event = alc269_laptop_unsol_event,
+		.setup = alc269_laptop_dmic_setup,
+		.init_hook = alc269_laptop_inithook,
+	},
+	[ALC269VB_AMIC] = {
+		.mixers = { alc269vb_laptop_mixer },
+		.cap_mixer = alc269vb_laptop_analog_capture_mixer,
+		.init_verbs = { alc269vb_init_verbs,
+				alc269vb_laptop_amic_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
+		.dac_nids = alc269_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc269_modes),
+		.channel_mode = alc269_modes,
+		.unsol_event = alc269_laptop_unsol_event,
+		.setup = alc269_laptop_amic_setup,
+		.init_hook = alc269_laptop_inithook,
+	},
+	[ALC269VB_DMIC] = {
+		.mixers = { alc269vb_laptop_mixer },
+		.cap_mixer = alc269vb_laptop_digital_capture_mixer,
+		.init_verbs = { alc269vb_init_verbs,
+				alc269vb_laptop_dmic_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
+		.dac_nids = alc269_dac_nids,
+		.hp_nid = 0x03,
+		.num_channel_mode = ARRAY_SIZE(alc269_modes),
+		.channel_mode = alc269_modes,
+		.unsol_event = alc269_laptop_unsol_event,
+		.setup = alc269vb_laptop_dmic_setup,
+		.init_hook = alc269_laptop_inithook,
 	},
 	[ALC269_FUJITSU] = {
 		.mixers = { alc269_fujitsu_mixer },
-		.cap_mixer = alc269_epc_capture_mixer,
+		.cap_mixer = alc269_laptop_digital_capture_mixer,
 		.init_verbs = { alc269_init_verbs,
-				alc269_eeepc_dmic_init_verbs },
+				alc269_laptop_dmic_init_verbs },
 		.num_dacs = ARRAY_SIZE(alc269_dac_nids),
 		.dac_nids = alc269_dac_nids,
 		.hp_nid = 0x03,
 		.num_channel_mode = ARRAY_SIZE(alc269_modes),
 		.channel_mode = alc269_modes,
-		.unsol_event = alc269_eeepc_unsol_event,
-		.setup = alc269_eeepc_dmic_setup,
-		.init_hook = alc269_eeepc_inithook,
+		.unsol_event = alc269_laptop_unsol_event,
+		.setup = alc269_laptop_dmic_setup,
+		.init_hook = alc269_laptop_inithook,
 	},
 	[ALC269_LIFEBOOK] = {
 		.mixers = { alc269_lifebook_mixer },
@@ -13879,6 +14084,7 @@
 	struct alc_spec *spec;
 	int board_config;
 	int err;
+	int is_alc269vb = 0;
 
 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
@@ -13895,6 +14101,7 @@
 			alc_free(codec);
 			return -ENOMEM;
 		}
+		is_alc269vb = 1;
 	}
 
 	board_config = snd_hda_check_board_config(codec, ALC269_MODEL_LAST,
@@ -13930,7 +14137,7 @@
 	if (board_config != ALC269_AUTO)
 		setup_preset(codec, &alc269_presets[board_config]);
 
-	if (codec->subsystem_id == 0x17aa3bf8) {
+	if (board_config == ALC269_QUANTA_FL1) {
 		/* Due to a hardware problem on Lenovo Ideadpad, we need to
 		 * fix the sample rate of analog I/O to 44.1kHz
 		 */
@@ -13943,9 +14150,16 @@
 	spec->stream_digital_playback = &alc269_pcm_digital_playback;
 	spec->stream_digital_capture = &alc269_pcm_digital_capture;
 
-	spec->adc_nids = alc269_adc_nids;
-	spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
-	spec->capsrc_nids = alc269_capsrc_nids;
+	if (!is_alc269vb) {
+		spec->adc_nids = alc269_adc_nids;
+		spec->num_adc_nids = ARRAY_SIZE(alc269_adc_nids);
+		spec->capsrc_nids = alc269_capsrc_nids;
+	} else {
+		spec->adc_nids = alc269vb_adc_nids;
+		spec->num_adc_nids = ARRAY_SIZE(alc269vb_adc_nids);
+		spec->capsrc_nids = alc269vb_capsrc_nids;
+	}
+
 	if (!spec->cap_mixer)
 		set_capture_mixer(codec);
 	set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
@@ -13959,7 +14173,6 @@
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc269_loopbacks;
 #endif
-	codec->proc_widget_hook = print_realtek_coef;
 
 	return 0;
 }
@@ -14822,7 +15035,7 @@
 	spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
 	set_capture_mixer(codec);
 
-	alc_ssid_check(codec, 0x0e, 0x0f, 0x0b);
+	alc_ssid_check(codec, 0x0e, 0x0f, 0x0b, 0);
 
 	return 1;
 }
@@ -15087,7 +15300,6 @@
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc861_loopbacks;
 #endif
-	codec->proc_widget_hook = print_realtek_coef;
 
 	return 0;
 }
@@ -15714,7 +15926,7 @@
 static int alc861vd_auto_create_input_ctls(struct hda_codec *codec,
 						const struct auto_pin_cfg *cfg)
 {
-	return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x22, 0);
+	return alc_auto_create_input_ctls(codec, cfg, 0x15, 0x09, 0);
 }
 
 
@@ -15950,7 +16162,7 @@
 	if (err < 0)
 		return err;
 
-	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+	alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
 
 	return 1;
 }
@@ -16067,7 +16279,6 @@
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc861vd_loopbacks;
 #endif
-	codec->proc_widget_hook = print_realtek_coef;
 
 	return 0;
 }
@@ -16534,13 +16745,6 @@
 	/* ADC: mute amp left and right */
 	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
 	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-	/* Front mixer: unmute input/output amp left and right (volume = 0) */
-
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
 
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
@@ -16590,6 +16794,28 @@
 	{ }
 };
 
+static struct hda_verb alc663_init_verbs[] = {
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{ }
+};
+
+static struct hda_verb alc272_init_verbs[] = {
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{ }
+};
+
 static struct hda_verb alc662_sue_init_verbs[] = {
 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT},
 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT},
@@ -16609,61 +16835,6 @@
 	{}
 };
 
-/*
- * generic initialization of ADC, input mixers and output mixers
- */
-static struct hda_verb alc662_auto_init_verbs[] = {
-	/*
-	 * Unmute ADC and set the default input to mic-in
-	 */
-	{0x09, AC_VERB_SET_CONNECT_SEL, 0x00},
-	{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-
-	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
-	 * mixer widget
-	 * Note: PASD motherboards uses the Line In 2 as the input for front
-	 * panel mic (mic 2)
-	 */
-	/* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-	{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-
-	/*
-	 * Set up output mixers (0x0c - 0x0f)
-	 */
-	/* set vol=0 to output mixers */
-	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-
-	/* set up input amps for analog loopback */
-	/* Amp Indices: DAC = 0, mixer = 1 */
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-
-
-	/* FIXME: use matrix-type input source selection */
-	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
-	/* Input mixer */
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{ }
-};
-
-/* additional verbs for ALC663 */
-static struct hda_verb alc663_auto_init_verbs[] = {
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-	{ }
-};
-
 static struct hda_verb alc663_m51va_init_verbs[] = {
 	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
 	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
@@ -17414,6 +17585,7 @@
 	SND_PCI_QUIRK(0x1028, 0x02f4, "DELL ZM1", ALC272_DELL_ZM1),
 	SND_PCI_QUIRK(0x1043, 0x1000, "ASUS N50Vm", ALC663_ASUS_MODE1),
 	SND_PCI_QUIRK(0x1043, 0x1092, "ASUS NB", ALC663_ASUS_MODE3),
+	SND_PCI_QUIRK(0x1043, 0x1173, "ASUS K73Jn", ALC663_ASUS_MODE1),
 	SND_PCI_QUIRK(0x1043, 0x11c3, "ASUS M70V", ALC663_ASUS_MODE3),
 	SND_PCI_QUIRK(0x1043, 0x11d3, "ASUS NB", ALC663_ASUS_MODE1),
 	SND_PCI_QUIRK(0x1043, 0x11f3, "ASUS NB", ALC662_ASUS_MODE2),
@@ -17449,6 +17621,7 @@
 	SND_PCI_QUIRK(0x1043, 0x1893, "ASUS M50Vm", ALC663_ASUS_MODE3),
 	SND_PCI_QUIRK(0x1043, 0x1894, "ASUS X55", ALC663_ASUS_MODE3),
 	SND_PCI_QUIRK(0x1043, 0x18b3, "ASUS N80Vc", ALC663_ASUS_MODE1),
+	SND_PCI_QUIRK(0x1043, 0x18c3, "ASUS VX5", ALC663_ASUS_MODE1),
 	SND_PCI_QUIRK(0x1043, 0x18d3, "ASUS N81Te", ALC663_ASUS_MODE1),
 	SND_PCI_QUIRK(0x1043, 0x18f3, "ASUS N505Tp", ALC663_ASUS_MODE1),
 	SND_PCI_QUIRK(0x1043, 0x1903, "ASUS F5GL", ALC663_ASUS_MODE1),
@@ -17476,6 +17649,7 @@
 	SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10),
 	SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L",
 		      ALC662_3ST_6ch_DIG),
+	SND_PCI_QUIRK(0x152d, 0x2304, "Quanta WH1", ALC663_ASUS_H13),
 	SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x1631, 0xc10c, "PB RS65", ALC663_ASUS_M51VA),
 	SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E),
@@ -18094,15 +18268,23 @@
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux[0];
 
-	add_verb(spec, alc662_auto_init_verbs);
-	if (codec->vendor_id == 0x10ec0663)
-		add_verb(spec, alc663_auto_init_verbs);
+	add_verb(spec, alc662_init_verbs);
+	if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
+	    codec->vendor_id == 0x10ec0665)
+		add_verb(spec, alc663_init_verbs);
+
+	if (codec->vendor_id == 0x10ec0272)
+		add_verb(spec, alc272_init_verbs);
 
 	err = alc_auto_add_mic_boost(codec);
 	if (err < 0)
 		return err;
 
-	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+	if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 ||
+	    codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670)
+	    alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0x21);
+	else
+	    alc_ssid_check(codec, 0x15, 0x1b, 0x14, 0);
 
 	return 1;
 }
@@ -18188,11 +18370,20 @@
 
 	if (!spec->cap_mixer)
 		set_capture_mixer(codec);
-	if (codec->vendor_id == 0x10ec0662)
-		set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
-	else
-		set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
 
+	switch (codec->vendor_id) {
+	case 0x10ec0662:
+		set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
+		break;
+	case 0x10ec0272:
+	case 0x10ec0663:
+	case 0x10ec0665:
+		set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT);
+		break;
+	case 0x10ec0273:
+		set_beep_amp(spec, 0x0b, 0x03, HDA_INPUT);
+		break;
+	}
 	spec->vmaster_nid = 0x02;
 
 	codec->patch_ops = alc_patch_ops;
@@ -18202,7 +18393,6 @@
 	if (!spec->loopback.amplist)
 		spec->loopback.amplist = alc662_loopbacks;
 #endif
-	codec->proc_widget_hook = print_realtek_coef;
 
 	return 0;
 }
@@ -18243,6 +18433,8 @@
 	{ .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
 	  .patch = patch_alc662 },
 	{ .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
+	{ .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
+	{ .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
 	{ .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
 	{ .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
 	{ .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index dbffb5b..8c416bb 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -568,6 +568,11 @@
 	0x0f, 0x10, 0x11, 0x1f, 0x20,
 };
 
+static hda_nid_t stac92hd88xxx_pin_nids[10] = {
+	0x0a, 0x0b, 0x0c, 0x0d,
+	0x0f, 0x11, 0x1f, 0x20,
+};
+
 #define STAC92HD71BXX_NUM_PINS 13
 static hda_nid_t stac92hd71bxx_pin_nids_4port[STAC92HD71BXX_NUM_PINS] = {
 	0x0a, 0x0b, 0x0c, 0x0d, 0x00,
@@ -2873,6 +2878,13 @@
 
 	conn_len = snd_hda_get_connections(codec, nid, conn,
 					   HDA_MAX_CONNECTIONS);
+	/* 92HD88: trace back up the link of nids to find the DAC */
+	while (conn_len == 1 && (get_wcaps_type(get_wcaps(codec, conn[0]))
+					!= AC_WID_AUD_OUT)) {
+		nid = conn[0];
+		conn_len = snd_hda_get_connections(codec, nid, conn,
+			HDA_MAX_CONNECTIONS);
+	}
 	for (j = 0; j < conn_len; j++) {
 		wcaps = get_wcaps(codec, conn[j]);
 		wtype = get_wcaps_type(wcaps);
@@ -4351,6 +4363,12 @@
 		if (enable_pin_detect(codec, nid, STAC_PWR_EVENT))
 			stac_issue_unsol_event(codec, nid);
 	}
+
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+	/* sync mute LED */
+	if (spec->gpio_led && codec->patch_ops.check_power_status)
+		codec->patch_ops.check_power_status(codec, 0x01);
+#endif	
 	if (spec->dac_list)
 		stac92xx_power_down(codec);
 	return 0;
@@ -4742,19 +4760,14 @@
 static void set_hp_led_gpio(struct hda_codec *codec)
 {
 	struct sigmatel_spec *spec = codec->spec;
-	switch (codec->vendor_id) {
-	case 0x111d7608:
-		/* GPIO 0 */
-		spec->gpio_led = 0x01;
-		break;
-	case 0x111d7600:
-	case 0x111d7601:
-	case 0x111d7602:
-	case 0x111d7603:
-		/* GPIO 3 */
-		spec->gpio_led = 0x08;
-		break;
-	}
+	unsigned int gpio;
+
+	gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP);
+	gpio &= AC_GPIO_IO_COUNT;
+	if (gpio > 3)
+		spec->gpio_led = 0x08; /* GPIO 3 */
+	else
+		spec->gpio_led = 0x01; /* GPIO 0 */
 }
 
 /*
@@ -4777,7 +4790,7 @@
  * Need more information on whether it is true across the entire series.
  * -- kunal
  */
-static int find_mute_led_gpio(struct hda_codec *codec)
+static int find_mute_led_gpio(struct hda_codec *codec, int default_polarity)
 {
 	struct sigmatel_spec *spec = codec->spec;
 	const struct dmi_device *dev = NULL;
@@ -4804,7 +4817,7 @@
 		 */
 		if (!hp_blike_system(codec->subsystem_id)) {
 			set_hp_led_gpio(codec);
-			spec->gpio_led_polarity = 1;
+			spec->gpio_led_polarity = default_polarity;
 			return 1;
 		}
 	}
@@ -4902,6 +4915,11 @@
 			stac_issue_unsol_event(codec,
 					       spec->autocfg.line_out_pins[0]);
 	}
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+	/* sync mute LED */
+	if (spec->gpio_led && codec->patch_ops.check_power_status)
+		codec->patch_ops.check_power_status(codec, 0x01);
+#endif	
 	return 0;
 }
 
@@ -4921,43 +4939,29 @@
 					      hda_nid_t nid)
 {
 	struct sigmatel_spec *spec = codec->spec;
+	int i, muted = 1;
 
-	if (nid == 0x10) {
-		if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
-		    HDA_AMP_MUTE)
-			spec->gpio_data &= ~spec->gpio_led; /* orange */
-		else
-			spec->gpio_data |= spec->gpio_led; /* white */
-
-		if (!spec->gpio_led_polarity) {
-			/* LED state is inverted on these systems */
-			spec->gpio_data ^= spec->gpio_led;
+	for (i = 0; i < spec->multiout.num_dacs; i++) {
+		nid = spec->multiout.dac_nids[i];
+		if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
+		      HDA_AMP_MUTE)) {
+			muted = 0; /* something heard */
+			break;
 		}
+	}
+	if (muted)
+		spec->gpio_data &= ~spec->gpio_led; /* orange */
+	else
+		spec->gpio_data |= spec->gpio_led; /* white */
 
-		stac_gpio_set(codec, spec->gpio_mask,
-			      spec->gpio_dir,
-			      spec->gpio_data);
+	if (!spec->gpio_led_polarity) {
+		/* LED state is inverted on these systems */
+		spec->gpio_data ^= spec->gpio_led;
 	}
 
-	return 0;
-}
-
-static int idt92hd83xxx_hp_check_power_status(struct hda_codec *codec,
-					      hda_nid_t nid)
-{
-	struct sigmatel_spec *spec = codec->spec;
-
-	if (nid != 0x13)
-		return 0;
-	if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & HDA_AMP_MUTE)
-		spec->gpio_data |= spec->gpio_led; /* mute LED on */
-	else
-		spec->gpio_data &= ~spec->gpio_led; /* mute LED off */
 	stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
-
 	return 0;
 }
-
 #endif
 
 static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
@@ -5279,7 +5283,6 @@
 	hda_nid_t conn[STAC92HD83_DAC_COUNT + 1];
 	int err;
 	int num_dacs;
-	hda_nid_t nid;
 
 	spec  = kzalloc(sizeof(*spec), GFP_KERNEL);
 	if (spec == NULL)
@@ -5318,7 +5321,18 @@
 				stac92hd83xxx_brd_tbl[spec->board_config]);
 
 	switch (codec->vendor_id) {
+	case 0x111d7666:
+	case 0x111d7667:
+	case 0x111d7668:
+	case 0x111d7669:
+		spec->num_pins = ARRAY_SIZE(stac92hd88xxx_pin_nids);
+		spec->pin_nids = stac92hd88xxx_pin_nids;
+		spec->mono_nid = 0;
+		spec->digbeep_nid = 0;
+		spec->num_pwrs = 0;
+		break;
 	case 0x111d7604:
+	case 0x111d76d4:
 	case 0x111d7605:
 	case 0x111d76d5:
 		if (spec->board_config == STAC_92HD83XXX_PWR_REF)
@@ -5329,8 +5343,10 @@
 
 	codec->patch_ops = stac92xx_patch_ops;
 
-	if (spec->board_config == STAC_92HD83XXX_HP)
-		spec->gpio_led = 0x01;
+	if (find_mute_led_gpio(codec, 0))
+		snd_printd("mute LED gpio %d polarity %d\n",
+				spec->gpio_led,
+				spec->gpio_led_polarity);
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	if (spec->gpio_led) {
@@ -5339,7 +5355,7 @@
 		spec->gpio_data |= spec->gpio_led;
 		/* register check_power_status callback. */
 		codec->patch_ops.check_power_status =
-			idt92hd83xxx_hp_check_power_status;
+			stac92xx_hp_check_power_status;
 	}
 #endif	
 
@@ -5359,24 +5375,21 @@
 		return err;
 	}
 
-	switch (spec->board_config) {
-	case STAC_DELL_S14:
-		nid = 0xf;
-		break;
-	default:
-		nid = 0xe;
-		break;
-	}
-
-	num_dacs = snd_hda_get_connections(codec, nid,
+	/* docking output support */
+	num_dacs = snd_hda_get_connections(codec, 0xF,
 				conn, STAC92HD83_DAC_COUNT + 1) - 1;
-	if (num_dacs < 0)
-		num_dacs = STAC92HD83_DAC_COUNT;
-
-	/* set port X to select the last DAC
-	 */
-	snd_hda_codec_write_cache(codec, nid, 0,
+	/* skip non-DAC connections */
+	while (num_dacs >= 0 &&
+			(get_wcaps_type(get_wcaps(codec, conn[num_dacs]))
+					!= AC_WID_AUD_OUT))
+		num_dacs--;
+	/* set port E and F to select the last DAC */
+	if (num_dacs >= 0) {
+		snd_hda_codec_write_cache(codec, 0xE, 0,
 			AC_VERB_SET_CONNECT_SEL, num_dacs);
+		snd_hda_codec_write_cache(codec, 0xF, 0,
+			AC_VERB_SET_CONNECT_SEL, num_dacs);
+	}
 
 	codec->proc_widget_hook = stac92hd_proc_hook;
 
@@ -5657,7 +5670,6 @@
 		 */
 		spec->num_smuxes = 1;
 		spec->num_dmuxes = 1;
-		spec->gpio_led = 0x01;
 		/* fallthrough */
 	case STAC_HP_DV5:
 		snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
@@ -5672,8 +5684,6 @@
 		spec->num_dmics = 1;
 		spec->num_dmuxes = 1;
 		spec->num_smuxes = 1;
-		/* orange/white mute led on GPIO3, orange=0, white=1 */
-		spec->gpio_led = 0x08;
 		break;
 	}
 
@@ -5695,7 +5705,7 @@
 		}
 	}
 
-	if (find_mute_led_gpio(codec))
+	if (find_mute_led_gpio(codec, 1))
 		snd_printd("mute LED gpio %d polarity %d\n",
 				spec->gpio_led,
 				spec->gpio_led_polarity);
@@ -6236,8 +6246,13 @@
  	{ .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
 	{ .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
 	{ .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
+	{ .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx},
 	{ .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
 	{ .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
+	{ .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx},
+	{ .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx},
+	{ .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx},
+	{ .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx},
 	{ .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
 	{ .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
 	{ .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
index cf0dfb7..67cbfe7 100644
--- a/sound/soc/blackfin/bf5xx-ac97-pcm.c
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -349,9 +349,7 @@
 			sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
 				size, &sport_handle->tx_dma_phy, GFP_KERNEL);
 			if (!sport_handle->tx_dma_buf) {
-				pr_err("Failed to allocate memory for tx dma \
-					buf - Please increase uncached DMA \
-					memory region\n");
+				pr_err("Failed to allocate memory for tx dma buf - Please increase uncached DMA memory region\n");
 				return -ENOMEM;
 			} else
 				memset(sport_handle->tx_dma_buf, 0, size);
@@ -362,9 +360,7 @@
 			sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \
 				size, &sport_handle->rx_dma_phy, GFP_KERNEL);
 			if (!sport_handle->rx_dma_buf) {
-				pr_err("Failed to allocate memory for rx dma \
-					buf - Please increase uncached DMA \
-					memory region\n");
+				pr_err("Failed to allocate memory for rx dma buf - Please increase uncached DMA memory region\n");
 				return -ENOMEM;
 			} else
 				memset(sport_handle->rx_dma_buf, 0, size);
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
index 62fbb84..c6c6a4a7 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -207,8 +207,7 @@
 	buf->area = dma_alloc_coherent(pcm->card->dev, size,
 			&buf->addr, GFP_KERNEL);
 	if (!buf->area) {
-		pr_err("Failed to allocate dma memory \
-			Please increase uncached DMA memory region\n");
+		pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n");
 		return -ENOMEM;
 	}
 	buf->bytes = size;
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
index a8c73cb..5e03bb2 100644
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.c
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c
@@ -244,8 +244,7 @@
 	buf->area = dma_alloc_coherent(pcm->card->dev, size * 4,
 		&buf->addr, GFP_KERNEL);
 	if (!buf->area) {
-		pr_err("Failed to allocate dma memory \
-			Please increase uncached DMA memory region\n");
+		pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n");
 		return -ENOMEM;
 	}
 	buf->bytes = size;
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 52b005f..1743d56 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -23,6 +23,7 @@
 	select SND_SOC_AK4671 if I2C
 	select SND_SOC_CS4270 if I2C
 	select SND_SOC_MAX9877 if I2C
+	select SND_SOC_DA7210 if I2C
 	select SND_SOC_PCM3008
 	select SND_SOC_SPDIF
 	select SND_SOC_SSM2602 if I2C
@@ -35,6 +36,7 @@
 	select SND_SOC_TWL4030 if TWL4030_CORE
 	select SND_SOC_UDA134X
 	select SND_SOC_UDA1380 if I2C
+	select SND_SOC_WM2000 if I2C
 	select SND_SOC_WM8350 if MFD_WM8350
 	select SND_SOC_WM8400 if MFD_WM8400
 	select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
@@ -49,14 +51,18 @@
 	select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8900 if I2C
 	select SND_SOC_WM8903 if I2C
+	select SND_SOC_WM8904 if I2C
 	select SND_SOC_WM8940 if I2C
+	select SND_SOC_WM8955 if I2C
 	select SND_SOC_WM8960 if I2C
 	select SND_SOC_WM8961 if I2C
 	select SND_SOC_WM8971 if I2C
 	select SND_SOC_WM8974 if I2C
+	select SND_SOC_WM8978 if I2C
 	select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8990 if I2C
 	select SND_SOC_WM8993 if I2C
+	select SND_SOC_WM8994 if MFD_WM8994
 	select SND_SOC_WM9081 if I2C
 	select SND_SOC_WM9705 if SND_SOC_AC97_BUS
 	select SND_SOC_WM9712 if SND_SOC_AC97_BUS
@@ -112,6 +118,9 @@
 config SND_SOC_CS4270
 	tristate
 
+config SND_SOC_DA7210
+        tristate
+
 # Cirrus Logic CS4270 Codec VD = 3.3V Errata
 # Select if you are affected by the errata where the part will not function
 # if MCLK divide-by-1.5 is selected and VD is set to 3.3V.  The driver will
@@ -203,9 +212,15 @@
 config SND_SOC_WM8903
 	tristate
 
+config SND_SOC_WM8904
+	tristate
+
 config SND_SOC_WM8940
         tristate
 
+config SND_SOC_WM8955
+	tristate
+
 config SND_SOC_WM8960
 	tristate
 
@@ -218,6 +233,9 @@
 config SND_SOC_WM8974
 	tristate
 
+config SND_SOC_WM8978
+	tristate
+
 config SND_SOC_WM8988
 	tristate
 
@@ -227,6 +245,9 @@
 config SND_SOC_WM8993
 	tristate
 
+config SND_SOC_WM8994
+	tristate
+
 config SND_SOC_WM9081
 	tristate
 
@@ -245,3 +266,6 @@
 
 config SND_SOC_TPA6130A2
 	tristate
+
+config SND_SOC_WM2000
+	tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index dbaecb1..dd5ce6d 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -10,6 +10,7 @@
 snd-soc-ak4671-objs := ak4671.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
+snd-soc-da7210-objs := da7210.o
 snd-soc-l3-objs := l3.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-spdif-objs := spdif_transciever.o
@@ -36,14 +37,18 @@
 snd-soc-wm8776-objs := wm8776.o
 snd-soc-wm8900-objs := wm8900.o
 snd-soc-wm8903-objs := wm8903.o
+snd-soc-wm8904-objs := wm8904.o
 snd-soc-wm8940-objs := wm8940.o
+snd-soc-wm8955-objs := wm8955.o
 snd-soc-wm8960-objs := wm8960.o
 snd-soc-wm8961-objs := wm8961.o
 snd-soc-wm8971-objs := wm8971.o
 snd-soc-wm8974-objs := wm8974.o
+snd-soc-wm8978-objs := wm8978.o
 snd-soc-wm8988-objs := wm8988.o
 snd-soc-wm8990-objs := wm8990.o
 snd-soc-wm8993-objs := wm8993.o
+snd-soc-wm8994-objs := wm8994.o
 snd-soc-wm9081-objs := wm9081.o
 snd-soc-wm9705-objs := wm9705.o
 snd-soc-wm9712-objs := wm9712.o
@@ -53,6 +58,7 @@
 # Amp
 snd-soc-max9877-objs := max9877.o
 snd-soc-tpa6130a2-objs := tpa6130a2.o
+snd-soc-wm2000-objs := wm2000.o
 
 obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o
 obj-$(CONFIG_SND_SOC_AD1836)	+= snd-soc-ad1836.o
@@ -66,6 +72,7 @@
 obj-$(CONFIG_SND_SOC_AK4671)	+= snd-soc-ak4671.o
 obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
+obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_PCM3008)	+= snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_SPDIF)	+= snd-soc-spdif.o
@@ -92,14 +99,18 @@
 obj-$(CONFIG_SND_SOC_WM8776)	+= snd-soc-wm8776.o
 obj-$(CONFIG_SND_SOC_WM8900)	+= snd-soc-wm8900.o
 obj-$(CONFIG_SND_SOC_WM8903)	+= snd-soc-wm8903.o
-obj-$(CONFIG_SND_SOC_WM8971)	+= snd-soc-wm8971.o
-obj-$(CONFIG_SND_SOC_WM8974)	+= snd-soc-wm8974.o
+obj-$(CONFIG_SND_SOC_WM8904)	+= snd-soc-wm8904.o
 obj-$(CONFIG_SND_SOC_WM8940)	+= snd-soc-wm8940.o
+obj-$(CONFIG_SND_SOC_WM8955)	+= snd-soc-wm8955.o
 obj-$(CONFIG_SND_SOC_WM8960)	+= snd-soc-wm8960.o
 obj-$(CONFIG_SND_SOC_WM8961)	+= snd-soc-wm8961.o
+obj-$(CONFIG_SND_SOC_WM8971)	+= snd-soc-wm8971.o
+obj-$(CONFIG_SND_SOC_WM8974)	+= snd-soc-wm8974.o
+obj-$(CONFIG_SND_SOC_WM8978)	+= snd-soc-wm8978.o
 obj-$(CONFIG_SND_SOC_WM8988)	+= snd-soc-wm8988.o
 obj-$(CONFIG_SND_SOC_WM8990)	+= snd-soc-wm8990.o
 obj-$(CONFIG_SND_SOC_WM8993)	+= snd-soc-wm8993.o
+obj-$(CONFIG_SND_SOC_WM8994)	+= snd-soc-wm8994.o
 obj-$(CONFIG_SND_SOC_WM9081)	+= snd-soc-wm9081.o
 obj-$(CONFIG_SND_SOC_WM9705)	+= snd-soc-wm9705.o
 obj-$(CONFIG_SND_SOC_WM9712)	+= snd-soc-wm9712.o
@@ -109,3 +120,4 @@
 # Amp
 obj-$(CONFIG_SND_SOC_MAX9877)	+= snd-soc-max9877.o
 obj-$(CONFIG_SND_SOC_TPA6130A2)	+= snd-soc-tpa6130a2.o
+obj-$(CONFIG_SND_SOC_WM2000)	+= snd-soc-wm2000.o
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index 2c18e3d..3c80137 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -171,57 +171,35 @@
 	return 0;
 }
 
-
-/*
- * interface to read/write ad1836 register
- */
-#define AD1836_SPI_REG_SHFT 12
-#define AD1836_SPI_READ     (1 << 11)
-#define AD1836_SPI_VAL_MSK  0x3FF
-
-/*
- * write to the ad1836 register space
- */
-
-static int ad1836_write_reg(struct snd_soc_codec *codec, unsigned int reg,
-		unsigned int value)
+#ifdef CONFIG_PM
+static int ad1836_soc_suspend(struct platform_device *pdev,
+		pm_message_t state)
 {
-	u16 *reg_cache = codec->reg_cache;
-	int ret = 0;
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
 
-	if (value != reg_cache[reg]) {
-		unsigned short buf;
-		struct spi_transfer t = {
-			.tx_buf = &buf,
-			.len = 2,
-		};
-		struct spi_message m;
+	/* reset clock control mode */
+	u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
+	adc_ctrl2 &= ~AD1836_ADC_SERFMT_MASK;
 
-		buf = (reg << AD1836_SPI_REG_SHFT) |
-			(value & AD1836_SPI_VAL_MSK);
-		spi_message_init(&m);
-		spi_message_add_tail(&t, &m);
-		ret = spi_sync(codec->control_data, &m);
-		if (ret == 0)
-			reg_cache[reg] = value;
-	}
-
-	return ret;
+	return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
 }
 
-/*
- * read from the ad1836 register space cache
- */
-static unsigned int ad1836_read_reg_cache(struct snd_soc_codec *codec,
-					  unsigned int reg)
+static int ad1836_soc_resume(struct platform_device *pdev)
 {
-	u16 *reg_cache = codec->reg_cache;
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
 
-	if (reg >= codec->reg_cache_size)
-		return -EINVAL;
+	/* restore clock control mode */
+	u16 adc_ctrl2 = snd_soc_read(codec, AD1836_ADC_CTRL2);
+	adc_ctrl2 |= AD1836_ADC_AUX;
 
-	return reg_cache[reg];
+	return snd_soc_write(codec, AD1836_ADC_CTRL2, adc_ctrl2);
 }
+#else
+#define ad1836_soc_suspend NULL
+#define ad1836_soc_resume  NULL
+#endif
 
 static int __devinit ad1836_spi_probe(struct spi_device *spi)
 {
@@ -306,32 +284,38 @@
 	codec->owner = THIS_MODULE;
 	codec->dai = &ad1836_dai;
 	codec->num_dai = 1;
-	codec->write = ad1836_write_reg;
-	codec->read = ad1836_read_reg_cache;
 	INIT_LIST_HEAD(&codec->dapm_widgets);
 	INIT_LIST_HEAD(&codec->dapm_paths);
 
 	ad1836_dai.dev = codec->dev;
 	ad1836_codec = codec;
 
+	ret = snd_soc_codec_set_cache_io(codec, 4, 12, SND_SOC_SPI);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to set cache I/O: %d\n",
+				ret);
+		kfree(ad1836);
+		return ret;
+	}
+
 	/* default setting for ad1836 */
 	/* de-emphasis: 48kHz, power-on dac */
-	codec->write(codec, AD1836_DAC_CTRL1, 0x300);
+	snd_soc_write(codec, AD1836_DAC_CTRL1, 0x300);
 	/* unmute dac channels */
-	codec->write(codec, AD1836_DAC_CTRL2, 0x0);
+	snd_soc_write(codec, AD1836_DAC_CTRL2, 0x0);
 	/* high-pass filter enable, power-on adc */
-	codec->write(codec, AD1836_ADC_CTRL1, 0x100);
+	snd_soc_write(codec, AD1836_ADC_CTRL1, 0x100);
 	/* unmute adc channles, adc aux mode */
-	codec->write(codec, AD1836_ADC_CTRL2, 0x180);
+	snd_soc_write(codec, AD1836_ADC_CTRL2, 0x180);
 	/* left/right diff:PGA/MUX */
-	codec->write(codec, AD1836_ADC_CTRL3, 0x3A);
+	snd_soc_write(codec, AD1836_ADC_CTRL3, 0x3A);
 	/* volume */
-	codec->write(codec, AD1836_DAC_L1_VOL, 0x3FF);
-	codec->write(codec, AD1836_DAC_R1_VOL, 0x3FF);
-	codec->write(codec, AD1836_DAC_L2_VOL, 0x3FF);
-	codec->write(codec, AD1836_DAC_R2_VOL, 0x3FF);
-	codec->write(codec, AD1836_DAC_L3_VOL, 0x3FF);
-	codec->write(codec, AD1836_DAC_R3_VOL, 0x3FF);
+	snd_soc_write(codec, AD1836_DAC_L1_VOL, 0x3FF);
+	snd_soc_write(codec, AD1836_DAC_R1_VOL, 0x3FF);
+	snd_soc_write(codec, AD1836_DAC_L2_VOL, 0x3FF);
+	snd_soc_write(codec, AD1836_DAC_R2_VOL, 0x3FF);
+	snd_soc_write(codec, AD1836_DAC_L3_VOL, 0x3FF);
+	snd_soc_write(codec, AD1836_DAC_R3_VOL, 0x3FF);
 
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
@@ -404,6 +388,8 @@
 struct snd_soc_codec_device soc_codec_dev_ad1836 = {
 	.probe = 	ad1836_probe,
 	.remove = 	ad1836_remove,
+	.suspend =      ad1836_soc_suspend,
+	.resume =       ad1836_soc_resume,
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_ad1836);
 
diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h
index 7660ee6..e9d90d3 100644
--- a/sound/soc/codecs/ad1836.h
+++ b/sound/soc/codecs/ad1836.h
@@ -54,6 +54,7 @@
 #define AD1836_ADC_SERFMT_MASK	       (7 << 6)
 #define AD1836_ADC_SERFMT_PCK256       (0x4 << 6)
 #define AD1836_ADC_SERFMT_PCK128       (0x5 << 6)
+#define AD1836_ADC_AUX                 (0x6 << 6)
 
 #define AD1836_ADC_CTRL3               14
 
diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c
index 5d48918..c233810 100644
--- a/sound/soc/codecs/ad1938.c
+++ b/sound/soc/codecs/ad1938.c
@@ -46,6 +46,11 @@
 	u8 reg_cache[AD1938_NUM_REGS];
 };
 
+/* ad1938 register cache & default register settings */
+static const u8 ad1938_reg[AD1938_NUM_REGS] = {
+	0, 0, 0, 0, 0, 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0, 0, 0,
+};
+
 static struct snd_soc_codec *ad1938_codec;
 struct snd_soc_codec_device soc_codec_dev_ad1938;
 static int ad1938_register(struct ad1938_priv *ad1938);
@@ -97,6 +102,7 @@
 static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = {
 	SND_SOC_DAPM_DAC("DAC", "Playback", AD1938_DAC_CTRL0, 0, 1),
 	SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_SUPPLY("PLL_PWR", AD1938_PLL_CLK_CTRL0, 0, 1, NULL, 0),
 	SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0),
 	SND_SOC_DAPM_OUTPUT("DAC1OUT"),
 	SND_SOC_DAPM_OUTPUT("DAC2OUT"),
@@ -107,6 +113,8 @@
 };
 
 static const struct snd_soc_dapm_route audio_paths[] = {
+	{ "DAC", NULL, "PLL_PWR" },
+	{ "ADC", NULL, "PLL_PWR" },
 	{ "DAC", NULL, "ADC_PWR" },
 	{ "ADC", NULL, "ADC_PWR" },
 	{ "DAC1OUT", "DAC1 Switch", "DAC" },
@@ -126,30 +134,20 @@
 	struct snd_soc_codec *codec = dai->codec;
 	int reg;
 
-	reg = codec->read(codec, AD1938_DAC_CTRL2);
+	reg = snd_soc_read(codec, AD1938_DAC_CTRL2);
 	reg = (mute > 0) ? reg | AD1938_DAC_MASTER_MUTE : reg &
 		(~AD1938_DAC_MASTER_MUTE);
-	codec->write(codec, AD1938_DAC_CTRL2, reg);
-
-	return 0;
-}
-
-static inline int ad1938_pll_powerctrl(struct snd_soc_codec *codec, int cmd)
-{
-	int reg = codec->read(codec, AD1938_PLL_CLK_CTRL0);
-	reg = (cmd > 0) ? reg & (~AD1938_PLL_POWERDOWN) : reg |
-		AD1938_PLL_POWERDOWN;
-	codec->write(codec, AD1938_PLL_CLK_CTRL0, reg);
+	snd_soc_write(codec, AD1938_DAC_CTRL2, reg);
 
 	return 0;
 }
 
 static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
-			       unsigned int mask, int slots, int width)
+			       unsigned int rx_mask, int slots, int width)
 {
 	struct snd_soc_codec *codec = dai->codec;
-	int dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
-	int adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
+	int dac_reg = snd_soc_read(codec, AD1938_DAC_CTRL1);
+	int adc_reg = snd_soc_read(codec, AD1938_ADC_CTRL2);
 
 	dac_reg &= ~AD1938_DAC_CHAN_MASK;
 	adc_reg &= ~AD1938_ADC_CHAN_MASK;
@@ -175,8 +173,8 @@
 		return -EINVAL;
 	}
 
-	codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
-	codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
+	snd_soc_write(codec, AD1938_DAC_CTRL1, dac_reg);
+	snd_soc_write(codec, AD1938_ADC_CTRL2, adc_reg);
 
 	return 0;
 }
@@ -187,8 +185,8 @@
 	struct snd_soc_codec *codec = codec_dai->codec;
 	int adc_reg, dac_reg;
 
-	adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
-	dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
+	adc_reg = snd_soc_read(codec, AD1938_ADC_CTRL2);
+	dac_reg = snd_soc_read(codec, AD1938_DAC_CTRL1);
 
 	/* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S
 	 * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
@@ -265,8 +263,8 @@
 		return -EINVAL;
 	}
 
-	codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
-	codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
+	snd_soc_write(codec, AD1938_ADC_CTRL2, adc_reg);
+	snd_soc_write(codec, AD1938_DAC_CTRL1, dac_reg);
 
 	return 0;
 }
@@ -295,134 +293,13 @@
 		break;
 	}
 
-	reg = codec->read(codec, AD1938_DAC_CTRL2);
+	reg = snd_soc_read(codec, AD1938_DAC_CTRL2);
 	reg = (reg & (~AD1938_DAC_WORD_LEN_MASK)) | word_len;
-	codec->write(codec, AD1938_DAC_CTRL2, reg);
+	snd_soc_write(codec, AD1938_DAC_CTRL2, reg);
 
-	reg = codec->read(codec, AD1938_ADC_CTRL1);
+	reg = snd_soc_read(codec, AD1938_ADC_CTRL1);
 	reg = (reg & (~AD1938_ADC_WORD_LEN_MASK)) | word_len;
-	codec->write(codec, AD1938_ADC_CTRL1, reg);
-
-	return 0;
-}
-
-static int ad1938_set_bias_level(struct snd_soc_codec *codec,
-		enum snd_soc_bias_level level)
-{
-	switch (level) {
-	case SND_SOC_BIAS_ON:
-		ad1938_pll_powerctrl(codec, 1);
-		break;
-	case SND_SOC_BIAS_PREPARE:
-		break;
-	case SND_SOC_BIAS_STANDBY:
-	case SND_SOC_BIAS_OFF:
-		ad1938_pll_powerctrl(codec, 0);
-		break;
-	}
-	codec->bias_level = level;
-	return 0;
-}
-
-/*
- * interface to read/write ad1938 register
- */
-
-#define AD1938_SPI_ADDR    0x4
-#define AD1938_SPI_READ    0x1
-#define AD1938_SPI_BUFLEN  3
-
-/*
- * write to the ad1938 register space
- */
-
-static int ad1938_write_reg(struct snd_soc_codec *codec, unsigned int reg,
-		unsigned int value)
-{
-	u8 *reg_cache = codec->reg_cache;
-	int ret = 0;
-
-	if (value != reg_cache[reg]) {
-		uint8_t buf[AD1938_SPI_BUFLEN];
-		struct spi_transfer t = {
-			.tx_buf = buf,
-			.len = AD1938_SPI_BUFLEN,
-		};
-		struct spi_message m;
-
-		buf[0] = AD1938_SPI_ADDR << 1;
-		buf[1] = reg;
-		buf[2] = value;
-		spi_message_init(&m);
-		spi_message_add_tail(&t, &m);
-		ret = spi_sync(codec->control_data, &m);
-		if (ret == 0)
-			reg_cache[reg] = value;
-	}
-
-	return ret;
-}
-
-/*
- * read from the ad1938 register space cache
- */
-
-static unsigned int ad1938_read_reg_cache(struct snd_soc_codec *codec,
-					  unsigned int reg)
-{
-	u8 *reg_cache = codec->reg_cache;
-
-	if (reg >= codec->reg_cache_size)
-		return -EINVAL;
-
-	return reg_cache[reg];
-}
-
-/*
- * read from the ad1938 register space
- */
-
-static unsigned int ad1938_read_reg(struct snd_soc_codec *codec,
-						unsigned int reg)
-{
-	char w_buf[AD1938_SPI_BUFLEN];
-	char r_buf[AD1938_SPI_BUFLEN];
-	int ret;
-
-	struct spi_transfer t = {
-		.tx_buf = w_buf,
-		.rx_buf = r_buf,
-		.len = AD1938_SPI_BUFLEN,
-	};
-	struct spi_message m;
-
-	w_buf[0] = (AD1938_SPI_ADDR << 1) | AD1938_SPI_READ;
-	w_buf[1] = reg;
-	w_buf[2] = 0;
-
-	spi_message_init(&m);
-	spi_message_add_tail(&t, &m);
-	ret = spi_sync(codec->control_data, &m);
-	if (ret == 0)
-		return	r_buf[2];
-	else
-		return -EIO;
-}
-
-static int ad1938_fill_cache(struct snd_soc_codec *codec)
-{
-	int i;
-	u8 *reg_cache = codec->reg_cache;
-	struct spi_device *spi = codec->control_data;
-
-	for (i = 0; i < codec->reg_cache_size; i++) {
-		int ret = ad1938_read_reg(codec, i);
-		if (ret == -EIO) {
-			dev_err(&spi->dev, "AD1938 SPI read failure\n");
-			return ret;
-		}
-		reg_cache[i] = ret;
-	}
+	snd_soc_write(codec, AD1938_ADC_CTRL1, reg);
 
 	return 0;
 }
@@ -512,32 +389,37 @@
 	codec->owner = THIS_MODULE;
 	codec->dai = &ad1938_dai;
 	codec->num_dai = 1;
-	codec->write = ad1938_write_reg;
-	codec->read = ad1938_read_reg_cache;
-	codec->set_bias_level = ad1938_set_bias_level;
 	INIT_LIST_HEAD(&codec->dapm_widgets);
 	INIT_LIST_HEAD(&codec->dapm_paths);
 
 	ad1938_dai.dev = codec->dev;
 	ad1938_codec = codec;
 
+	memcpy(codec->reg_cache, ad1938_reg, AD1938_NUM_REGS);
+
+	ret = snd_soc_codec_set_cache_io(codec, 16, 8, SND_SOC_SPI);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to set cache I/O: %d\n",
+				ret);
+		kfree(ad1938);
+		return ret;
+	}
+
 	/* default setting for ad1938 */
 
 	/* unmute dac channels */
-	codec->write(codec, AD1938_DAC_CHNL_MUTE, 0x0);
+	snd_soc_write(codec, AD1938_DAC_CHNL_MUTE, 0x0);
 	/* de-emphasis: 48kHz, powedown dac */
-	codec->write(codec, AD1938_DAC_CTRL2, 0x1A);
+	snd_soc_write(codec, AD1938_DAC_CTRL2, 0x1A);
 	/* powerdown dac, dac in tdm mode */
-	codec->write(codec, AD1938_DAC_CTRL0, 0x41);
+	snd_soc_write(codec, AD1938_DAC_CTRL0, 0x41);
 	/* high-pass filter enable */
-	codec->write(codec, AD1938_ADC_CTRL0, 0x3);
+	snd_soc_write(codec, AD1938_ADC_CTRL0, 0x3);
 	/* sata delay=1, adc aux mode */
-	codec->write(codec, AD1938_ADC_CTRL1, 0x43);
+	snd_soc_write(codec, AD1938_ADC_CTRL1, 0x43);
 	/* pll input: mclki/xi */
-	codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9D);
-	codec->write(codec, AD1938_PLL_CLK_CTRL1, 0x04);
-
-	ad1938_fill_cache(codec);
+	snd_soc_write(codec, AD1938_PLL_CLK_CTRL0, 0x9D);
+	snd_soc_write(codec, AD1938_PLL_CLK_CTRL1, 0x04);
 
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
@@ -559,7 +441,6 @@
 
 static void ad1938_unregister(struct ad1938_priv *ad1938)
 {
-	ad1938_set_bias_level(&ad1938->codec, SND_SOC_BIAS_OFF);
 	snd_soc_unregister_dai(&ad1938_dai);
 	snd_soc_unregister_codec(&ad1938->codec);
 	kfree(ad1938);
@@ -593,7 +474,6 @@
 				  ARRAY_SIZE(ad1938_dapm_widgets));
 	snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
 
-	ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 pcm_err:
 	return ret;
@@ -610,37 +490,9 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int ad1938_suspend(struct platform_device *pdev,
-		pm_message_t state)
-{
-	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct snd_soc_codec *codec = socdev->card->codec;
-
-	ad1938_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	return 0;
-}
-
-static int ad1938_resume(struct platform_device *pdev)
-{
-	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct snd_soc_codec *codec = socdev->card->codec;
-
-	if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
-		ad1938_set_bias_level(codec, SND_SOC_BIAS_ON);
-
-	return 0;
-}
-#else
-#define ad1938_suspend NULL
-#define ad1938_resume NULL
-#endif
-
 struct snd_soc_codec_device soc_codec_dev_ad1938 = {
 	.probe = 	ad1938_probe,
 	.remove = 	ad1938_remove,
-	.suspend =      ad1938_suspend,
-	.resume =       ad1938_resume,
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938);
 
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index 3a14c6f..b68d99f 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -90,12 +90,10 @@
 	if (reg >= codec->reg_cache_size)
 		return -EINVAL;
 
-	reg &= AK4104_REG_MASK;
-	reg |= AK4104_WRITE;
-
 	/* only write to the hardware if value has changed */
 	if (cache[reg] != value) {
-		u8 tmp[2] = { reg, value };
+		u8 tmp[2] = { (reg & AK4104_REG_MASK) | AK4104_WRITE, value };
+
 		if (spi_write(spi, tmp, sizeof(tmp))) {
 			dev_err(&spi->dev, "SPI write failed\n");
 			return -EIO;
@@ -185,9 +183,7 @@
 		.stream_name = "Playback",
 		.channels_min = 2,
 		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_44100 |
-			 SNDRV_PCM_RATE_48000 |
-			 SNDRV_PCM_RATE_32000,
+		.rates = SNDRV_PCM_RATE_8000_192000,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE  |
 			   SNDRV_PCM_FMTBIT_S24_3LE |
 			   SNDRV_PCM_FMTBIT_S24_LE
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index ffe122d..dfbeb2d 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -28,6 +28,7 @@
 #include <sound/initval.h>
 #include <linux/i2c.h>
 #include <linux/delay.h>
+#include <linux/regulator/consumer.h>
 
 #include "cs4270.h"
 
@@ -106,6 +107,10 @@
 #define CS4270_MUTE_DAC_A	0x01
 #define CS4270_MUTE_DAC_B	0x02
 
+static const char *supply_names[] = {
+	"va", "vd", "vlc"
+};
+
 /* Private data for the CS4270 */
 struct cs4270_private {
 	struct snd_soc_codec codec;
@@ -114,6 +119,9 @@
 	unsigned int mode; /* The mode (I2S or left-justified) */
 	unsigned int slave_mode;
 	unsigned int manual_mute;
+
+	/* power domain regulators */
+	struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
 };
 
 /**
@@ -192,6 +200,11 @@
  * This function must be called by the machine driver's 'startup' function,
  * otherwise the list of supported sample rates will not be available in
  * time for ALSA.
+ *
+ * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause
+ * theoretically possible sample rates to be enabled. Call it again with a
+ * proper value set one the external clock is set (most probably you would do
+ * that from a machine's driver 'hw_param' hook.
  */
 static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 				 int clk_id, unsigned int freq, int dir)
@@ -205,20 +218,27 @@
 
 	cs4270->mclk = freq;
 
-	for (i = 0; i < NUM_MCLK_RATIOS; i++) {
-		unsigned int rate = freq / cs4270_mode_ratios[i].ratio;
-		rates |= snd_pcm_rate_to_rate_bit(rate);
-		if (rate < rate_min)
-			rate_min = rate;
-		if (rate > rate_max)
-			rate_max = rate;
-	}
-	/* FIXME: soc should support a rate list */
-	rates &= ~SNDRV_PCM_RATE_KNOT;
+	if (cs4270->mclk) {
+		for (i = 0; i < NUM_MCLK_RATIOS; i++) {
+			unsigned int rate = freq / cs4270_mode_ratios[i].ratio;
+			rates |= snd_pcm_rate_to_rate_bit(rate);
+			if (rate < rate_min)
+				rate_min = rate;
+			if (rate > rate_max)
+				rate_max = rate;
+		}
+		/* FIXME: soc should support a rate list */
+		rates &= ~SNDRV_PCM_RATE_KNOT;
 
-	if (!rates) {
-		dev_err(codec->dev, "could not find a valid sample rate\n");
-		return -EINVAL;
+		if (!rates) {
+			dev_err(codec->dev, "could not find a valid sample rate\n");
+			return -EINVAL;
+		}
+	} else {
+		/* enable all possible rates */
+		rates = SNDRV_PCM_RATE_8000_192000;
+		rate_min = 8000;
+		rate_max = 192000;
 	}
 
 	codec_dai->playback.rates = rates;
@@ -579,7 +599,8 @@
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = cs4270_codec;
-	int ret;
+	struct cs4270_private *cs4270 = codec->private_data;
+	int i, ret;
 
 	/* Connect the codec to the socdev.  snd_soc_new_pcms() needs this. */
 	socdev->card->codec = codec;
@@ -599,8 +620,26 @@
 		goto error_free_pcms;
 	}
 
+	/* get the power supply regulators */
+	for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+		cs4270->supplies[i].supply = supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(cs4270->supplies),
+				 cs4270->supplies);
+	if (ret < 0)
+		goto error_free_pcms;
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
+				    cs4270->supplies);
+	if (ret < 0)
+		goto error_free_regulators;
+
 	return 0;
 
+error_free_regulators:
+	regulator_bulk_free(ARRAY_SIZE(cs4270->supplies),
+			    cs4270->supplies);
+
 error_free_pcms:
 	snd_soc_free_pcms(socdev);
 
@@ -616,8 +655,12 @@
 static int cs4270_remove(struct platform_device *pdev)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = cs4270_codec;
+	struct cs4270_private *cs4270 = codec->private_data;
 
 	snd_soc_free_pcms(socdev);
+	regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
+	regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies);
 
 	return 0;
 };
@@ -799,17 +842,33 @@
 static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
 {
 	struct snd_soc_codec *codec = cs4270_codec;
-	int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+	struct cs4270_private *cs4270 = codec->private_data;
+	int reg, ret;
 
-	return snd_soc_write(codec, CS4270_PWRCTL, reg);
+	reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+	if (reg < 0)
+		return reg;
+
+	ret = snd_soc_write(codec, CS4270_PWRCTL, reg);
+	if (ret < 0)
+		return ret;
+
+	regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies),
+			       cs4270->supplies);
+
+	return 0;
 }
 
 static int cs4270_soc_resume(struct platform_device *pdev)
 {
 	struct snd_soc_codec *codec = cs4270_codec;
+	struct cs4270_private *cs4270 = codec->private_data;
 	struct i2c_client *i2c_client = codec->control_data;
 	int reg;
 
+	regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies),
+			      cs4270->supplies);
+
 	/* In case the device was put to hard reset during sleep, we need to
 	 * wait 500ns here before any I2C communication. */
 	ndelay(500);
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
new file mode 100644
index 0000000..cf2975a
--- /dev/null
+++ b/sound/soc/codecs/da7210.c
@@ -0,0 +1,589 @@
+/*
+ * DA7210 ALSA Soc codec driver
+ *
+ * Copyright (c) 2009 Dialog Semiconductor
+ * Written by David Chen <Dajun.chen@diasemi.com>
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * Tested on SuperH Ecovec24 board with S16/S24 LE in 48KHz using I2S
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+
+#include "da7210.h"
+
+/* DA7210 register space */
+#define DA7210_STATUS			0x02
+#define DA7210_STARTUP1			0x03
+#define DA7210_MIC_L			0x07
+#define DA7210_MIC_R			0x08
+#define DA7210_INMIX_L			0x0D
+#define DA7210_INMIX_R			0x0E
+#define DA7210_ADC_HPF			0x0F
+#define DA7210_ADC			0x10
+#define DA7210_DAC_HPF			0x14
+#define DA7210_DAC_L			0x15
+#define DA7210_DAC_R			0x16
+#define DA7210_DAC_SEL			0x17
+#define DA7210_OUTMIX_L			0x1C
+#define DA7210_OUTMIX_R			0x1D
+#define DA7210_HP_L_VOL			0x21
+#define DA7210_HP_R_VOL			0x22
+#define DA7210_HP_CFG			0x23
+#define DA7210_DAI_SRC_SEL		0x25
+#define DA7210_DAI_CFG1			0x26
+#define DA7210_DAI_CFG3			0x28
+#define DA7210_PLL_DIV3			0x2B
+#define DA7210_PLL			0x2C
+
+/* STARTUP1 bit fields */
+#define DA7210_SC_MST_EN		(1 << 0)
+
+/* MIC_L bit fields */
+#define DA7210_MICBIAS_EN		(1 << 6)
+#define DA7210_MIC_L_EN			(1 << 7)
+
+/* MIC_R bit fields */
+#define DA7210_MIC_R_EN			(1 << 7)
+
+/* INMIX_L bit fields */
+#define DA7210_IN_L_EN			(1 << 7)
+
+/* INMIX_R bit fields */
+#define DA7210_IN_R_EN			(1 << 7)
+
+/* ADC_HPF bit fields */
+#define DA7210_ADC_VOICE_EN		(1 << 7)
+
+/* ADC bit fields */
+#define DA7210_ADC_L_EN			(1 << 3)
+#define DA7210_ADC_R_EN			(1 << 7)
+
+/* DAC_HPF fields */
+#define DA7210_DAC_VOICE_EN		(1 << 7)
+
+/* DAC_SEL bit fields */
+#define DA7210_DAC_L_SRC_DAI_L		(4 << 0)
+#define DA7210_DAC_L_EN			(1 << 3)
+#define DA7210_DAC_R_SRC_DAI_R		(5 << 4)
+#define DA7210_DAC_R_EN			(1 << 7)
+
+/* OUTMIX_L bit fields */
+#define DA7210_OUT_L_EN			(1 << 7)
+
+/* OUTMIX_R bit fields */
+#define DA7210_OUT_R_EN			(1 << 7)
+
+/* HP_CFG bit fields */
+#define DA7210_HP_2CAP_MODE		(1 << 1)
+#define DA7210_HP_SENSE_EN		(1 << 2)
+#define DA7210_HP_L_EN			(1 << 3)
+#define DA7210_HP_MODE			(1 << 6)
+#define DA7210_HP_R_EN			(1 << 7)
+
+/* DAI_SRC_SEL bit fields */
+#define DA7210_DAI_OUT_L_SRC		(6 << 0)
+#define DA7210_DAI_OUT_R_SRC		(7 << 4)
+
+/* DAI_CFG1 bit fields */
+#define DA7210_DAI_WORD_S16_LE		(0 << 0)
+#define DA7210_DAI_WORD_S24_LE		(2 << 0)
+#define DA7210_DAI_FLEN_64BIT		(1 << 2)
+#define DA7210_DAI_MODE_MASTER		(1 << 7)
+
+/* DAI_CFG3 bit fields */
+#define DA7210_DAI_FORMAT_I2SMODE	(0 << 0)
+#define DA7210_DAI_OE			(1 << 3)
+#define DA7210_DAI_EN			(1 << 7)
+
+/*PLL_DIV3 bit fields */
+#define DA7210_MCLK_RANGE_10_20_MHZ	(1 << 4)
+#define DA7210_PLL_BYP			(1 << 6)
+
+/* PLL bit fields */
+#define DA7210_PLL_FS_48000		(11 << 0)
+
+#define DA7210_VERSION "0.0.1"
+
+/* Codec private data */
+struct da7210_priv {
+	struct snd_soc_codec codec;
+};
+
+static struct snd_soc_codec *da7210_codec;
+
+/*
+ * Register cache
+ */
+static const u8 da7210_reg[] = {
+	0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* R0  - R7  */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,	/* R8  - RF  */
+	0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x54,	/* R10 - R17 */
+	0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* R18 - R1F */
+	0x00, 0x00, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00,	/* R20 - R27 */
+	0x04, 0x00, 0x00, 0x30, 0x2A, 0x00, 0x40, 0x00,	/* R28 - R2F */
+	0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x40, 0x00,	/* R30 - R37 */
+	0x40, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00,	/* R38 - R3F */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* R40 - R4F */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* R48 - R4F */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* R50 - R57 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* R58 - R5F */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* R60 - R67 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* R68 - R6F */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* R70 - R77 */
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x54, 0x00,	/* R78 - R7F */
+	0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00,	/* R80 - R87 */
+	0x00,						/* R88       */
+};
+
+/*
+ * Read da7210 register cache
+ */
+static inline u32 da7210_read_reg_cache(struct snd_soc_codec *codec, u32 reg)
+{
+	u8 *cache = codec->reg_cache;
+	BUG_ON(reg > ARRAY_SIZE(da7210_reg));
+	return cache[reg];
+}
+
+/*
+ * Write to the da7210 register space
+ */
+static int da7210_write(struct snd_soc_codec *codec, u32 reg, u32 value)
+{
+	u8 *cache = codec->reg_cache;
+	u8 data[2];
+
+	BUG_ON(codec->volatile_register);
+
+	data[0] = reg & 0xff;
+	data[1] = value & 0xff;
+
+	if (reg >= codec->reg_cache_size)
+		return -EIO;
+
+	if (2 != codec->hw_write(codec->control_data, data, 2))
+		return -EIO;
+
+	cache[reg] = value;
+	return 0;
+}
+
+/*
+ * Read from the da7210 register space.
+ */
+static inline u32 da7210_read(struct snd_soc_codec *codec, u32 reg)
+{
+	if (DA7210_STATUS == reg)
+		return i2c_smbus_read_byte_data(codec->control_data, reg);
+
+	return da7210_read_reg_cache(codec, reg);
+}
+
+static int da7210_startup(struct snd_pcm_substream *substream,
+			  struct snd_soc_dai *dai)
+{
+	int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+	struct snd_soc_codec *codec = dai->codec;
+
+	if (is_play) {
+		/* PlayBack Volume 40 */
+		snd_soc_update_bits(codec, DA7210_HP_L_VOL, 0x3F, 40);
+		snd_soc_update_bits(codec, DA7210_HP_R_VOL, 0x3F, 40);
+
+		/* Enable Out */
+		snd_soc_update_bits(codec, DA7210_OUTMIX_L, 0x1F, 0x10);
+		snd_soc_update_bits(codec, DA7210_OUTMIX_R, 0x1F, 0x10);
+
+	} else {
+		/* Volume 7 */
+		snd_soc_update_bits(codec, DA7210_MIC_L, 0x7, 0x7);
+		snd_soc_update_bits(codec, DA7210_MIC_R, 0x7, 0x7);
+
+		/* Enable Mic */
+		snd_soc_update_bits(codec, DA7210_INMIX_L, 0x1F, 0x1);
+		snd_soc_update_bits(codec, DA7210_INMIX_R, 0x1F, 0x1);
+	}
+
+	return 0;
+}
+
+/*
+ * Set PCM DAI word length.
+ */
+static int da7210_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	u32 dai_cfg1;
+	u32 reg, mask;
+
+	/* set DAI source to Left and Right ADC */
+	da7210_write(codec, DA7210_DAI_SRC_SEL,
+		     DA7210_DAI_OUT_R_SRC | DA7210_DAI_OUT_L_SRC);
+
+	/* Enable DAI */
+	da7210_write(codec, DA7210_DAI_CFG3, DA7210_DAI_OE | DA7210_DAI_EN);
+
+	dai_cfg1 = 0xFC & da7210_read(codec, DA7210_DAI_CFG1);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		dai_cfg1 |= DA7210_DAI_WORD_S16_LE;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dai_cfg1 |= DA7210_DAI_WORD_S24_LE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
+
+	/* FIXME
+	 *
+	 * It support 48K only now
+	 */
+	switch (params_rate(params)) {
+	case 48000:
+		if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
+			reg  = DA7210_DAC_HPF;
+			mask = DA7210_DAC_VOICE_EN;
+		} else {
+			reg  = DA7210_ADC_HPF;
+			mask = DA7210_ADC_VOICE_EN;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, reg, mask, 0);
+
+	return 0;
+}
+
+/*
+ * Set DAI mode and Format
+ */
+static int da7210_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u32 dai_cfg1;
+	u32 dai_cfg3;
+
+	dai_cfg1 = 0x7f & da7210_read(codec, DA7210_DAI_CFG1);
+	dai_cfg3 = 0xfc & da7210_read(codec, DA7210_DAI_CFG3);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		dai_cfg1 |= DA7210_DAI_MODE_MASTER;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* FIXME
+	 *
+	 * It support I2S only now
+	 */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		dai_cfg3 |= DA7210_DAI_FORMAT_I2SMODE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* FIXME
+	 *
+	 * It support 64bit data transmission only now
+	 */
+	dai_cfg1 |= DA7210_DAI_FLEN_64BIT;
+
+	da7210_write(codec, DA7210_DAI_CFG1, dai_cfg1);
+	da7210_write(codec, DA7210_DAI_CFG3, dai_cfg3);
+
+	return 0;
+}
+
+#define DA7210_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+/* DAI operations */
+static struct snd_soc_dai_ops da7210_dai_ops = {
+	.startup	= da7210_startup,
+	.hw_params	= da7210_hw_params,
+	.set_fmt	= da7210_set_dai_fmt,
+};
+
+struct snd_soc_dai da7210_dai = {
+	.name = "DA7210 IIS",
+	.id = 0,
+	/* playback capabilities */
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = DA7210_FORMATS,
+	},
+	/* capture capabilities */
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = DA7210_FORMATS,
+	},
+	.ops = &da7210_dai_ops,
+};
+EXPORT_SYMBOL_GPL(da7210_dai);
+
+/*
+ * Initialize the DA7210 driver
+ * register the mixer and dsp interfaces with the kernel
+ */
+static int da7210_init(struct da7210_priv *da7210)
+{
+	struct snd_soc_codec *codec = &da7210->codec;
+	int ret = 0;
+
+	if (da7210_codec) {
+		dev_err(codec->dev, "Another da7210 is registered\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data	= da7210;
+	codec->name		= "DA7210";
+	codec->owner		= THIS_MODULE;
+	codec->read		= da7210_read;
+	codec->write		= da7210_write;
+	codec->dai		= &da7210_dai;
+	codec->num_dai		= 1;
+	codec->hw_write		= (hw_write_t)i2c_master_send;
+	codec->reg_cache_size	= ARRAY_SIZE(da7210_reg);
+	codec->reg_cache	= kmemdup(da7210_reg,
+					  sizeof(da7210_reg), GFP_KERNEL);
+
+	if (!codec->reg_cache)
+		return -ENOMEM;
+
+	da7210_dai.dev = codec->dev;
+	da7210_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret) {
+		dev_err(codec->dev, "Failed to register CODEC: %d\n", ret);
+		goto init_err;
+	}
+
+	ret = snd_soc_register_dai(&da7210_dai);
+	if (ret) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		goto init_err;
+	}
+
+	/* FIXME
+	 *
+	 * This driver use fixed value here
+	 */
+
+	/*
+	 * ADC settings
+	 */
+
+	/* Enable Left & Right MIC PGA and Mic Bias */
+	da7210_write(codec, DA7210_MIC_L, DA7210_MIC_L_EN | DA7210_MICBIAS_EN);
+	da7210_write(codec, DA7210_MIC_R, DA7210_MIC_R_EN);
+
+	/* Enable Left and Right input PGA */
+	da7210_write(codec, DA7210_INMIX_L, DA7210_IN_L_EN);
+	da7210_write(codec, DA7210_INMIX_R, DA7210_IN_R_EN);
+
+	/* Enable Left and Right ADC */
+	da7210_write(codec, DA7210_ADC, DA7210_ADC_L_EN | DA7210_ADC_R_EN);
+
+	/*
+	 * DAC settings
+	 */
+
+	/* Enable Left and Right DAC */
+	da7210_write(codec, DA7210_DAC_SEL,
+		     DA7210_DAC_L_SRC_DAI_L | DA7210_DAC_L_EN |
+		     DA7210_DAC_R_SRC_DAI_R | DA7210_DAC_R_EN);
+
+	/* Enable Left and Right out PGA */
+	da7210_write(codec, DA7210_OUTMIX_L, DA7210_OUT_L_EN);
+	da7210_write(codec, DA7210_OUTMIX_R, DA7210_OUT_R_EN);
+
+	/* Enable Left and Right HeadPhone PGA */
+	da7210_write(codec, DA7210_HP_CFG,
+		     DA7210_HP_2CAP_MODE | DA7210_HP_SENSE_EN |
+		     DA7210_HP_L_EN | DA7210_HP_MODE | DA7210_HP_R_EN);
+
+	/* Diable PLL and bypass it */
+	da7210_write(codec, DA7210_PLL, DA7210_PLL_FS_48000);
+
+	/* Bypass PLL and set MCLK freq rang to 10-20MHz */
+	da7210_write(codec, DA7210_PLL_DIV3,
+		     DA7210_MCLK_RANGE_10_20_MHZ | DA7210_PLL_BYP);
+
+	/* Activate all enabled subsystem */
+	da7210_write(codec, DA7210_STARTUP1, DA7210_SC_MST_EN);
+
+	return ret;
+
+init_err:
+	kfree(codec->reg_cache);
+	codec->reg_cache = NULL;
+
+	return ret;
+
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int __devinit da7210_i2c_probe(struct i2c_client *i2c,
+			   	      const struct i2c_device_id *id)
+{
+	struct da7210_priv *da7210;
+	struct snd_soc_codec *codec;
+	int ret;
+
+	da7210 = kzalloc(sizeof(struct da7210_priv), GFP_KERNEL);
+	if (!da7210)
+		return -ENOMEM;
+
+	codec = &da7210->codec;
+	codec->dev = &i2c->dev;
+
+	i2c_set_clientdata(i2c, da7210);
+	codec->control_data = i2c;
+
+	ret = da7210_init(da7210);
+	if (ret < 0)
+		pr_err("Failed to initialise da7210 audio codec\n");
+
+	return ret;
+}
+
+static int __devexit da7210_i2c_remove(struct i2c_client *client)
+{
+	struct da7210_priv *da7210 = i2c_get_clientdata(client);
+
+	snd_soc_unregister_dai(&da7210_dai);
+	kfree(da7210->codec.reg_cache);
+	kfree(da7210);
+	da7210_codec = NULL;
+
+	return 0;
+}
+
+static const struct i2c_device_id da7210_i2c_id[] = {
+	{ "da7210", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
+
+/* I2C codec control layer */
+static struct i2c_driver da7210_i2c_driver = {
+	.driver = {
+		.name = "DA7210 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.probe = da7210_i2c_probe,
+	.remove =  __devexit_p(da7210_i2c_remove),
+	.id_table = da7210_i2c_id,
+};
+#endif
+
+static int da7210_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret;
+
+	if (!da7210_codec) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = da7210_codec;
+	codec = da7210_codec;
+
+	/* Register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0)
+		goto pcm_err;
+
+	dev_info(&pdev->dev, "DA7210 Audio Codec %s\n", DA7210_VERSION);
+
+pcm_err:
+	return ret;
+}
+
+static int da7210_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_da7210 = {
+	.probe =	da7210_probe,
+	.remove =	da7210_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_da7210);
+
+static int __init da7210_modinit(void)
+{
+	int ret = 0;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&da7210_i2c_driver);
+#endif
+	return ret;
+}
+module_init(da7210_modinit);
+
+static void __exit da7210_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&da7210_i2c_driver);
+#endif
+}
+module_exit(da7210_exit);
+
+MODULE_DESCRIPTION("ASoC DA7210 driver");
+MODULE_AUTHOR("David Chen, Kuninori Morimoto");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7210.h b/sound/soc/codecs/da7210.h
new file mode 100644
index 0000000..390d621
--- /dev/null
+++ b/sound/soc/codecs/da7210.h
@@ -0,0 +1,24 @@
+/*
+ * da7210.h  --  audio driver for da7210
+ *
+ * Copyright (c) 2009 Dialog Semiconductor
+ * Written by David Chen <Dajun.chen@diasemi.com>
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Cleanups by Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef _DA7210_H
+#define _DA7210_H
+
+extern struct snd_soc_dai da7210_dai;
+extern struct snd_soc_codec_device soc_codec_dev_da7210;
+
+#endif
+
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 2b4dc2b..e4b946a 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -765,9 +765,10 @@
 	struct snd_soc_codec *codec = socdev->card->codec;
 	struct aic3x_priv *aic3x = codec->private_data;
 	int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
-	u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
-	u16 pll_d = 1;
+	u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
+	u16 d, pll_d = 1;
 	u8 reg;
+	int clk;
 
 	/* select data word length */
 	data =
@@ -833,48 +834,70 @@
 	if (bypass_pll)
 		return 0;
 
-	/* Use PLL
-	 * find an apropriate setup for j, d, r and p by iterating over
-	 * p and r - j and d are calculated for each fraction.
-	 * Up to 128 values are probed, the closest one wins the game.
+	/* Use PLL, compute apropriate setup for j, d, r and p, the closest
+	 * one wins the game. Try with d==0 first, next with d!=0.
+	 * Constraints for j are according to the datasheet.
 	 * The sysclk is divided by 1000 to prevent integer overflows.
 	 */
+
 	codec_clk = (2048 * fsref) / (aic3x->sysclk / 1000);
 
 	for (r = 1; r <= 16; r++)
 		for (p = 1; p <= 8; p++) {
-			int clk, tmp = (codec_clk * pll_r * 10) / pll_p;
-			u8 j = tmp / 10000;
-			u16 d = tmp % 10000;
+			for (j = 4; j <= 55; j++) {
+				/* This is actually 1000*((j+(d/10000))*r)/p
+				 * The term had to be converted to get
+				 * rid of the division by 10000; d = 0 here
+				 */
+				int tmp_clk = (1000 * j * r) / p;
 
-			if (j > 63)
-				continue;
+				/* Check whether this values get closer than
+				 * the best ones we had before
+				 */
+				if (abs(codec_clk - tmp_clk) <
+					abs(codec_clk - last_clk)) {
+					pll_j = j; pll_d = 0;
+					pll_r = r; pll_p = p;
+					last_clk = tmp_clk;
+				}
 
-			if (d != 0 && aic3x->sysclk < 10000000)
-				continue;
-
-			/* This is actually 1000 * ((j + (d/10000)) * r) / p
-			 * The term had to be converted to get rid of the
-			 * division by 10000 */
-			clk = ((10000 * j * r) + (d * r)) / (10 * p);
-
-			/* check whether this values get closer than the best
-			 * ones we had before */
-			if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
-				pll_j = j; pll_d = d; pll_r = r; pll_p = p;
-				last_clk = clk;
+				/* Early exit for exact matches */
+				if (tmp_clk == codec_clk)
+					goto found;
 			}
-
-			/* Early exit for exact matches */
-			if (clk == codec_clk)
-				break;
 		}
 
+	/* try with d != 0 */
+	for (p = 1; p <= 8; p++) {
+		j = codec_clk * p / 1000;
+
+		if (j < 4 || j > 11)
+			continue;
+
+		/* do not use codec_clk here since we'd loose precision */
+		d = ((2048 * p * fsref) - j * aic3x->sysclk)
+			* 100 / (aic3x->sysclk/100);
+
+		clk = (10000 * j + d) / (10 * p);
+
+		/* check whether this values get closer than the best
+		 * ones we had before */
+		if (abs(codec_clk - clk) < abs(codec_clk - last_clk)) {
+			pll_j = j; pll_d = d; pll_r = 1; pll_p = p;
+			last_clk = clk;
+		}
+
+		/* Early exit for exact matches */
+		if (clk == codec_clk)
+			goto found;
+	}
+
 	if (last_clk == 0) {
 		printk(KERN_ERR "%s(): unable to setup PLL\n", __func__);
 		return -EINVAL;
 	}
 
+found:
 	data = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
 	aic3x_write(codec, AIC3X_PLL_PROGA_REG, data | (pll_p << PLLP_SHIFT));
 	aic3x_write(codec, AIC3X_OVRF_STATUS_AND_PLLR_REG, pll_r << PLLR_SHIFT);
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 9c8903d..f9f367d 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -30,6 +30,7 @@
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -58,11 +59,26 @@
 	DAC33_FLUSH,
 };
 
+enum dac33_fifo_modes {
+	DAC33_FIFO_BYPASS = 0,
+	DAC33_FIFO_MODE1,
+	DAC33_FIFO_MODE7,
+	DAC33_FIFO_LAST_MODE,
+};
+
+#define DAC33_NUM_SUPPLIES 3
+static const char *dac33_supply_names[DAC33_NUM_SUPPLIES] = {
+	"AVDD",
+	"DVDD",
+	"IOVDD",
+};
+
 struct tlv320dac33_priv {
 	struct mutex mutex;
 	struct workqueue_struct *dac33_wq;
 	struct work_struct work;
 	struct snd_soc_codec codec;
+	struct regulator_bulk_data supplies[DAC33_NUM_SUPPLIES];
 	int power_gpio;
 	int chip_power;
 	int irq;
@@ -73,8 +89,9 @@
 					 * this */
 	unsigned int nsample_max;	/* nsample should not be higher than
 					 * this */
-	unsigned int nsample_switch;	/* Use FIFO or bypass FIFO switch */
+	enum dac33_fifo_modes fifo_mode;/* FIFO mode selection */
 	unsigned int nsample;		/* burst read amount from host */
+	u8 burst_bclkdiv;		/* BCLK divider value in burst mode */
 
 	enum dac33_state state;
 };
@@ -297,28 +314,49 @@
 	dac33_write(codec, DAC33_PWR_CTRL, reg);
 }
 
-static void dac33_hard_power(struct snd_soc_codec *codec, int power)
+static int dac33_hard_power(struct snd_soc_codec *codec, int power)
 {
 	struct tlv320dac33_priv *dac33 = codec->private_data;
+	int ret;
 
 	mutex_lock(&dac33->mutex);
 	if (power) {
-		if (dac33->power_gpio >= 0) {
-			gpio_set_value(dac33->power_gpio, 1);
-			dac33->chip_power = 1;
-			/* Restore registers */
-			dac33_restore_regs(codec);
+		ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies),
+					  dac33->supplies);
+		if (ret != 0) {
+			dev_err(codec->dev,
+				"Failed to enable supplies: %d\n", ret);
+				goto exit;
 		}
+
+		if (dac33->power_gpio >= 0)
+			gpio_set_value(dac33->power_gpio, 1);
+
+		dac33->chip_power = 1;
+
+		/* Restore registers */
+		dac33_restore_regs(codec);
+
 		dac33_soft_power(codec, 1);
 	} else {
 		dac33_soft_power(codec, 0);
-		if (dac33->power_gpio >= 0) {
+		if (dac33->power_gpio >= 0)
 			gpio_set_value(dac33->power_gpio, 0);
-			dac33->chip_power = 0;
-		}
-	}
-	mutex_unlock(&dac33->mutex);
 
+		ret = regulator_bulk_disable(ARRAY_SIZE(dac33->supplies),
+					     dac33->supplies);
+		if (ret != 0) {
+			dev_err(codec->dev,
+				"Failed to disable supplies: %d\n", ret);
+			goto exit;
+		}
+
+		dac33->chip_power = 0;
+	}
+
+exit:
+	mutex_unlock(&dac33->mutex);
+	return ret;
 }
 
 static int dac33_get_nsample(struct snd_kcontrol *kcontrol,
@@ -351,39 +389,48 @@
 	return ret;
 }
 
-static int dac33_get_nsample_switch(struct snd_kcontrol *kcontrol,
+static int dac33_get_fifo_mode(struct snd_kcontrol *kcontrol,
 			 struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct tlv320dac33_priv *dac33 = codec->private_data;
 
-	ucontrol->value.integer.value[0] = dac33->nsample_switch;
+	ucontrol->value.integer.value[0] = dac33->fifo_mode;
 
 	return 0;
 }
 
-static int dac33_set_nsample_switch(struct snd_kcontrol *kcontrol,
+static int dac33_set_fifo_mode(struct snd_kcontrol *kcontrol,
 			 struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct tlv320dac33_priv *dac33 = codec->private_data;
 	int ret = 0;
 
-	if (dac33->nsample_switch == ucontrol->value.integer.value[0])
+	if (dac33->fifo_mode == ucontrol->value.integer.value[0])
 		return 0;
 	/* Do not allow changes while stream is running*/
 	if (codec->active)
 		return -EPERM;
 
 	if (ucontrol->value.integer.value[0] < 0 ||
-	    ucontrol->value.integer.value[0] > 1)
+	    ucontrol->value.integer.value[0] >= DAC33_FIFO_LAST_MODE)
 		ret = -EINVAL;
 	else
-		dac33->nsample_switch = ucontrol->value.integer.value[0];
+		dac33->fifo_mode = ucontrol->value.integer.value[0];
 
 	return ret;
 }
 
+/* Codec operation modes */
+static const char *dac33_fifo_mode_texts[] = {
+	"Bypass", "Mode 1", "Mode 7"
+};
+
+static const struct soc_enum dac33_fifo_mode_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dac33_fifo_mode_texts),
+			    dac33_fifo_mode_texts);
+
 /*
  * DACL/R digital volume control:
  * from 0 dB to -63.5 in 0.5 dB steps
@@ -406,8 +453,8 @@
 static const struct snd_kcontrol_new dac33_nsample_snd_controls[] = {
 	SOC_SINGLE_EXT("nSample", 0, 0, 5900, 0,
 		 dac33_get_nsample, dac33_set_nsample),
-	SOC_SINGLE_EXT("nSample Switch", 0, 0, 1, 0,
-		 dac33_get_nsample_switch, dac33_set_nsample_switch),
+	SOC_ENUM_EXT("FIFO Mode", dac33_fifo_mode_enum,
+		 dac33_get_fifo_mode, dac33_set_fifo_mode),
 };
 
 /* Analog bypass */
@@ -469,6 +516,8 @@
 static int dac33_set_bias_level(struct snd_soc_codec *codec,
 				enum snd_soc_bias_level level)
 {
+	int ret;
+
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 		dac33_soft_power(codec, 1);
@@ -476,12 +525,19 @@
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		if (codec->bias_level == SND_SOC_BIAS_OFF)
-			dac33_hard_power(codec, 1);
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			ret = dac33_hard_power(codec, 1);
+			if (ret != 0)
+				return ret;
+		}
+
 		dac33_soft_power(codec, 0);
 		break;
 	case SND_SOC_BIAS_OFF:
-		dac33_hard_power(codec, 0);
+		ret = dac33_hard_power(codec, 0);
+		if (ret != 0)
+			return ret;
+
 		break;
 	}
 	codec->bias_level = level;
@@ -489,6 +545,51 @@
 	return 0;
 }
 
+static inline void dac33_prefill_handler(struct tlv320dac33_priv *dac33)
+{
+	struct snd_soc_codec *codec;
+
+	codec = &dac33->codec;
+
+	switch (dac33->fifo_mode) {
+	case DAC33_FIFO_MODE1:
+		dac33_write16(codec, DAC33_NSAMPLE_MSB,
+				DAC33_THRREG(dac33->nsample));
+		dac33_write16(codec, DAC33_PREFILL_MSB,
+				DAC33_THRREG(dac33->alarm_threshold));
+		break;
+	case DAC33_FIFO_MODE7:
+		dac33_write16(codec, DAC33_PREFILL_MSB,
+				DAC33_THRREG(10));
+		break;
+	default:
+		dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
+							dac33->fifo_mode);
+		break;
+	}
+}
+
+static inline void dac33_playback_handler(struct tlv320dac33_priv *dac33)
+{
+	struct snd_soc_codec *codec;
+
+	codec = &dac33->codec;
+
+	switch (dac33->fifo_mode) {
+	case DAC33_FIFO_MODE1:
+		dac33_write16(codec, DAC33_NSAMPLE_MSB,
+				DAC33_THRREG(dac33->nsample));
+		break;
+	case DAC33_FIFO_MODE7:
+		/* At the moment we are not using interrupts in mode7 */
+		break;
+	default:
+		dev_warn(codec->dev, "Unhandled FIFO mode: %d\n",
+							dac33->fifo_mode);
+		break;
+	}
+}
+
 static void dac33_work(struct work_struct *work)
 {
 	struct snd_soc_codec *codec;
@@ -502,14 +603,10 @@
 	switch (dac33->state) {
 	case DAC33_PREFILL:
 		dac33->state = DAC33_PLAYBACK;
-		dac33_write16(codec, DAC33_NSAMPLE_MSB,
-				DAC33_THRREG(dac33->nsample));
-		dac33_write16(codec, DAC33_PREFILL_MSB,
-				DAC33_THRREG(dac33->alarm_threshold));
+		dac33_prefill_handler(dac33);
 		break;
 	case DAC33_PLAYBACK:
-		dac33_write16(codec, DAC33_NSAMPLE_MSB,
-				DAC33_THRREG(dac33->nsample));
+		dac33_playback_handler(dac33);
 		break;
 	case DAC33_IDLE:
 		break;
@@ -547,7 +644,7 @@
 	unsigned int pwr_ctrl;
 
 	/* Stop pending workqueue */
-	if (dac33->nsample_switch)
+	if (dac33->fifo_mode)
 		cancel_work_sync(&dac33->work);
 
 	mutex_lock(&dac33->mutex);
@@ -603,7 +700,7 @@
 }
 
 #define CALC_OSCSET(rate, refclk) ( \
-	((((rate * 10000) / refclk) * 4096) + 5000) / 10000)
+	((((rate * 10000) / refclk) * 4096) + 7000) / 10000)
 #define CALC_RATIOSET(rate, refclk) ( \
 	((((refclk  * 100000) / rate) * 16384) + 50000) / 100000)
 
@@ -619,7 +716,7 @@
 	struct snd_soc_codec *codec = socdev->card->codec;
 	struct tlv320dac33_priv *dac33 = codec->private_data;
 	unsigned int oscset, ratioset, pwr_ctrl, reg_tmp;
-	u8 aictrl_a, fifoctrl_a;
+	u8 aictrl_a, aictrl_b, fifoctrl_a;
 
 	switch (substream->runtime->rate) {
 	case 44100:
@@ -637,7 +734,10 @@
 
 	aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
 	aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK);
+	/* Read FIFO control A, and clear FIFO flush bit */
 	fifoctrl_a = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+	fifoctrl_a &= ~DAC33_FIFOFLUSH;
+
 	fifoctrl_a &= ~DAC33_WIDTH;
 	switch (substream->runtime->format) {
 	case SNDRV_PCM_FORMAT_S16_LE:
@@ -675,7 +775,8 @@
 
 	dac33_oscwait(codec);
 
-	if (dac33->nsample_switch) {
+	if (dac33->fifo_mode) {
+		/* Generic for all FIFO modes */
 		/* 50-51 : ASRC Control registers */
 		dac33_write(codec, DAC33_ASRC_CTRL_A, (1 << 4)); /* div=2 */
 		dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */
@@ -685,38 +786,101 @@
 
 		/* Set interrupts to high active */
 		dac33_write(codec, DAC33_INTP_CTRL_A, DAC33_INTPM_AHIGH);
-
-		dac33_write(codec, DAC33_FIFO_IRQ_MODE_B,
-			    DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL));
-		dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
 	} else {
+		/* FIFO bypass mode */
 		/* 50-51 : ASRC Control registers */
 		dac33_write(codec, DAC33_ASRC_CTRL_A, DAC33_SRCBYP);
 		dac33_write(codec, DAC33_ASRC_CTRL_B, 0); /* ??? */
 	}
 
-	if (dac33->nsample_switch)
+	/* Interrupt behaviour configuration */
+	switch (dac33->fifo_mode) {
+	case DAC33_FIFO_MODE1:
+		dac33_write(codec, DAC33_FIFO_IRQ_MODE_B,
+			    DAC33_ATM(DAC33_FIFO_IRQ_MODE_LEVEL));
+		dac33_write(codec, DAC33_FIFO_IRQ_MASK, DAC33_MAT);
+		break;
+	case DAC33_FIFO_MODE7:
+		/* Disable all interrupts */
+		dac33_write(codec, DAC33_FIFO_IRQ_MASK, 0);
+		break;
+	default:
+		/* in FIFO bypass mode, the interrupts are not used */
+		break;
+	}
+
+	aictrl_b = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+
+	switch (dac33->fifo_mode) {
+	case DAC33_FIFO_MODE1:
+		/*
+		 * For mode1:
+		 * Disable the FIFO bypass (Enable the use of FIFO)
+		 * Select nSample mode
+		 * BCLK is only running when data is needed by DAC33
+		 */
 		fifoctrl_a &= ~DAC33_FBYPAS;
-	else
+		fifoctrl_a &= ~DAC33_FAUTO;
+		aictrl_b &= ~DAC33_BCLKON;
+		break;
+	case DAC33_FIFO_MODE7:
+		/*
+		 * For mode1:
+		 * Disable the FIFO bypass (Enable the use of FIFO)
+		 * Select Threshold mode
+		 * BCLK is only running when data is needed by DAC33
+		 */
+		fifoctrl_a &= ~DAC33_FBYPAS;
+		fifoctrl_a |= DAC33_FAUTO;
+		aictrl_b &= ~DAC33_BCLKON;
+		break;
+	default:
+		/*
+		 * For FIFO bypass mode:
+		 * Enable the FIFO bypass (Disable the FIFO use)
+		 * Set the BCLK as continous
+		 */
 		fifoctrl_a |= DAC33_FBYPAS;
+		aictrl_b |= DAC33_BCLKON;
+		break;
+	}
+
 	dac33_write(codec, DAC33_FIFO_CTRL_A, fifoctrl_a);
-
 	dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
-	reg_tmp = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
-	if (dac33->nsample_switch)
-		reg_tmp &= ~DAC33_BCLKON;
+	dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b);
+
+	/*
+	 * BCLK divide ratio
+	 * 0: 1.5
+	 * 1: 1
+	 * 2: 2
+	 * ...
+	 * 254: 254
+	 * 255: 255
+	 */
+	if (dac33->fifo_mode)
+		dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C,
+							dac33->burst_bclkdiv);
 	else
-		reg_tmp |= DAC33_BCLKON;
-	dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, reg_tmp);
+		dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32);
 
-	if (dac33->nsample_switch) {
-		/* 20: BCLK divide ratio */
-		dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 3);
-
+	switch (dac33->fifo_mode) {
+	case DAC33_FIFO_MODE1:
 		dac33_write16(codec, DAC33_ATHR_MSB,
 			      DAC33_THRREG(dac33->alarm_threshold));
-	} else {
-		dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32);
+		break;
+	case DAC33_FIFO_MODE7:
+		/*
+		 * Configure the threshold levels, and leave 10 sample space
+		 * at the bottom, and also at the top of the FIFO
+		 */
+		dac33_write16(codec, DAC33_UTHR_MSB,
+			DAC33_THRREG(DAC33_BUFFER_SIZE_SAMPLES - 10));
+		dac33_write16(codec, DAC33_LTHR_MSB,
+			DAC33_THRREG(10));
+		break;
+	default:
+		break;
 	}
 
 	mutex_unlock(&dac33->mutex);
@@ -789,7 +953,7 @@
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		if (dac33->nsample_switch) {
+		if (dac33->fifo_mode) {
 			dac33->state = DAC33_PREFILL;
 			queue_work(dac33->dac33_wq, &dac33->work);
 		}
@@ -797,7 +961,7 @@
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		if (dac33->nsample_switch) {
+		if (dac33->fifo_mode) {
 			dac33->state = DAC33_FLUSH;
 			queue_work(dac33->dac33_wq, &dac33->work);
 		}
@@ -843,6 +1007,7 @@
 			     unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
+	struct tlv320dac33_priv *dac33 = codec->private_data;
 	u8 aictrl_a, aictrl_b;
 
 	aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
@@ -855,7 +1020,11 @@
 		break;
 	case SND_SOC_DAIFMT_CBS_CFS:
 		/* Codec Slave */
-		aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK);
+		if (dac33->fifo_mode) {
+			dev_err(codec->dev, "FIFO mode requires master mode\n");
+			return -EINVAL;
+		} else
+			aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK);
 		break;
 	default:
 		return -EINVAL;
@@ -959,6 +1128,9 @@
 	/* power on device */
 	dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
+	/* Bias level configuration has enabled regulator an extra time */
+	regulator_bulk_disable(ARRAY_SIZE(dac33->supplies), dac33->supplies);
+
 	return 0;
 
 pcm_err:
@@ -1033,13 +1205,13 @@
 };
 EXPORT_SYMBOL_GPL(dac33_dai);
 
-static int dac33_i2c_probe(struct i2c_client *client,
-			   const struct i2c_device_id *id)
+static int __devinit dac33_i2c_probe(struct i2c_client *client,
+				     const struct i2c_device_id *id)
 {
 	struct tlv320dac33_platform_data *pdata;
 	struct tlv320dac33_priv *dac33;
 	struct snd_soc_codec *codec;
-	int ret = 0;
+	int ret, i;
 
 	if (client->dev.platform_data == NULL) {
 		dev_err(&client->dev, "Platform data not set\n");
@@ -1080,10 +1252,11 @@
 	i2c_set_clientdata(client, dac33);
 
 	dac33->power_gpio = pdata->power_gpio;
+	dac33->burst_bclkdiv = pdata->burst_bclkdiv;
 	dac33->irq = client->irq;
 	dac33->nsample = NSAMPLE_MAX;
 	/* Disable FIFO use by default */
-	dac33->nsample_switch = 0;
+	dac33->fifo_mode = DAC33_FIFO_BYPASS;
 
 	tlv320dac33_codec = codec;
 
@@ -1130,6 +1303,24 @@
 		}
 	}
 
+	for (i = 0; i < ARRAY_SIZE(dac33->supplies); i++)
+		dac33->supplies[i].supply = dac33_supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(dac33->supplies),
+				 dac33->supplies);
+
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+		goto err_get;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(dac33->supplies),
+				    dac33->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+		goto err_enable;
+	}
+
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
@@ -1149,6 +1340,10 @@
 	return ret;
 
 error_codec:
+	regulator_bulk_disable(ARRAY_SIZE(dac33->supplies), dac33->supplies);
+err_enable:
+	regulator_bulk_free(ARRAY_SIZE(dac33->supplies), dac33->supplies);
+err_get:
 	if (dac33->irq >= 0) {
 		free_irq(dac33->irq, &dac33->codec);
 		destroy_workqueue(dac33->dac33_wq);
@@ -1165,7 +1360,7 @@
 	return ret;
 }
 
-static int dac33_i2c_remove(struct i2c_client *client)
+static int __devexit dac33_i2c_remove(struct i2c_client *client)
 {
 	struct tlv320dac33_priv *dac33;
 
@@ -1177,6 +1372,8 @@
 	if (dac33->irq >= 0)
 		free_irq(dac33->irq, &dac33->codec);
 
+	regulator_bulk_free(ARRAY_SIZE(dac33->supplies), dac33->supplies);
+
 	destroy_workqueue(dac33->dac33_wq);
 	snd_soc_unregister_dai(&dac33_dai);
 	snd_soc_unregister_codec(&dac33->codec);
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
index 6b650c1..958d49c 100644
--- a/sound/soc/codecs/tpa6130a2.c
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -25,6 +25,7 @@
 #include <linux/device.h>
 #include <linux/i2c.h>
 #include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
 #include <sound/tpa6130a2-plat.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
@@ -34,10 +35,22 @@
 
 static struct i2c_client *tpa6130a2_client;
 
+#define TPA6130A2_NUM_SUPPLIES 2
+static const char *tpa6130a2_supply_names[TPA6130A2_NUM_SUPPLIES] = {
+	"CPVSS",
+	"Vdd",
+};
+
+static const char *tpa6140a2_supply_names[TPA6130A2_NUM_SUPPLIES] = {
+	"HPVdd",
+	"AVdd",
+};
+
 /* This struct is used to save the context */
 struct tpa6130a2_data {
 	struct mutex mutex;
 	unsigned char regs[TPA6130A2_CACHEREGNUM];
+	struct regulator_bulk_data supplies[TPA6130A2_NUM_SUPPLIES];
 	int power_gpio;
 	unsigned char power_state;
 };
@@ -106,10 +119,11 @@
 		tpa6130a2_i2c_write(i, data->regs[i]);
 }
 
-static void tpa6130a2_power(int power)
+static int tpa6130a2_power(int power)
 {
 	struct	tpa6130a2_data *data;
 	u8	val;
+	int	ret;
 
 	BUG_ON(tpa6130a2_client == NULL);
 	data = i2c_get_clientdata(tpa6130a2_client);
@@ -117,11 +131,20 @@
 	mutex_lock(&data->mutex);
 	if (power) {
 		/* Power on */
-		if (data->power_gpio >= 0) {
+		if (data->power_gpio >= 0)
 			gpio_set_value(data->power_gpio, 1);
-			data->power_state = 1;
-			tpa6130a2_initialize();
+
+		ret = regulator_bulk_enable(ARRAY_SIZE(data->supplies),
+					    data->supplies);
+		if (ret != 0) {
+			dev_err(&tpa6130a2_client->dev,
+				"Failed to enable supplies: %d\n", ret);
+			goto exit;
 		}
+
+		data->power_state = 1;
+		tpa6130a2_initialize();
+
 		/* Clear SWS */
 		val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
 		val &= ~TPA6130A2_SWS;
@@ -131,13 +154,25 @@
 		val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
 		val |= TPA6130A2_SWS;
 		tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+
 		/* Power off */
-		if (data->power_gpio >= 0) {
+		if (data->power_gpio >= 0)
 			gpio_set_value(data->power_gpio, 0);
-			data->power_state = 0;
+
+		ret = regulator_bulk_disable(ARRAY_SIZE(data->supplies),
+					     data->supplies);
+		if (ret != 0) {
+			dev_err(&tpa6130a2_client->dev,
+				"Failed to disable supplies: %d\n", ret);
+			goto exit;
 		}
+
+		data->power_state = 0;
 	}
+
+exit:
 	mutex_unlock(&data->mutex);
+	return ret;
 }
 
 static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
@@ -237,12 +272,8 @@
  */
 static void tpa6130a2_channel_enable(u8 channel, int enable)
 {
-	struct	tpa6130a2_data *data;
 	u8	val;
 
-	BUG_ON(tpa6130a2_client == NULL);
-	data = i2c_get_clientdata(tpa6130a2_client);
-
 	if (enable) {
 		/* Enable channel */
 		/* Enable amplifier */
@@ -299,15 +330,17 @@
 static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w,
 		struct snd_kcontrol *kcontrol, int event)
 {
+	int ret = 0;
+
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		tpa6130a2_power(1);
+		ret = tpa6130a2_power(1);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		tpa6130a2_power(0);
+		ret = tpa6130a2_power(0);
 		break;
 	}
-	return 0;
+	return ret;
 }
 
 static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
@@ -346,13 +379,13 @@
 }
 EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
 
-static int tpa6130a2_probe(struct i2c_client *client,
-			   const struct i2c_device_id *id)
+static int __devinit tpa6130a2_probe(struct i2c_client *client,
+				     const struct i2c_device_id *id)
 {
 	struct device *dev;
 	struct tpa6130a2_data *data;
 	struct tpa6130a2_platform_data *pdata;
-	int ret;
+	int i, ret;
 
 	dev = &client->dev;
 
@@ -387,15 +420,38 @@
 		if (ret < 0) {
 			dev_err(dev, "Failed to request power GPIO (%d)\n",
 				data->power_gpio);
-			goto fail;
+			goto err_gpio;
 		}
 		gpio_direction_output(data->power_gpio, 0);
-	} else {
-		data->power_state = 1;
-		tpa6130a2_initialize();
 	}
 
-	tpa6130a2_power(1);
+	switch (pdata->id) {
+	case TPA6130A2:
+		for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
+			data->supplies[i].supply = tpa6130a2_supply_names[i];
+		break;
+	case TPA6140A2:
+		for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
+			data->supplies[i].supply = tpa6140a2_supply_names[i];;
+		break;
+	default:
+		dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
+			 pdata->id);
+		for (i = 0; i < ARRAY_SIZE(data->supplies); i++)
+			data->supplies[i].supply = tpa6130a2_supply_names[i];
+	}
+
+	ret = regulator_bulk_get(dev, ARRAY_SIZE(data->supplies),
+				 data->supplies);
+	if (ret != 0) {
+		dev_err(dev, "Failed to request supplies: %d\n", ret);
+		goto err_regulator;
+	}
+
+	ret = tpa6130a2_power(1);
+	if (ret != 0)
+		goto err_power;
+
 
 	/* Read version */
 	ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
@@ -404,10 +460,18 @@
 		dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
 
 	/* Disable the chip */
-	tpa6130a2_power(0);
+	ret = tpa6130a2_power(0);
+	if (ret != 0)
+		goto err_power;
 
 	return 0;
-fail:
+
+err_power:
+	regulator_bulk_free(ARRAY_SIZE(data->supplies), data->supplies);
+err_regulator:
+	if (data->power_gpio >= 0)
+		gpio_free(data->power_gpio);
+err_gpio:
 	kfree(data);
 	i2c_set_clientdata(tpa6130a2_client, NULL);
 	tpa6130a2_client = NULL;
@@ -415,7 +479,7 @@
 	return ret;
 }
 
-static int tpa6130a2_remove(struct i2c_client *client)
+static int __devexit tpa6130a2_remove(struct i2c_client *client)
 {
 	struct tpa6130a2_data *data = i2c_get_clientdata(client);
 
@@ -423,6 +487,9 @@
 
 	if (data->power_gpio >= 0)
 		gpio_free(data->power_gpio);
+
+	regulator_bulk_free(ARRAY_SIZE(data->supplies), data->supplies);
+
 	kfree(data);
 	tpa6130a2_client = NULL;
 
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 2a27f7b..6f5d4af 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -55,7 +55,7 @@
 	0x0c, /* REG_ATXR1PGA		(0xB)	*/
 	0x00, /* REG_AVTXL2PGA		(0xC)	*/
 	0x00, /* REG_AVTXR2PGA		(0xD)	*/
-	0x01, /* REG_AUDIO_IF		(0xE)	*/
+	0x00, /* REG_AUDIO_IF		(0xE)	*/
 	0x00, /* REG_VOICE_IF		(0xF)	*/
 	0x00, /* REG_ARXR1PGA		(0x10)	*/
 	0x00, /* REG_ARXL1PGA		(0x11)	*/
@@ -64,19 +64,19 @@
 	0x00, /* REG_VRXPGA		(0x14)	*/
 	0x00, /* REG_VSTPGA		(0x15)	*/
 	0x00, /* REG_VRX2ARXPGA		(0x16)	*/
-	0x0c, /* REG_AVDAC_CTL		(0x17)	*/
+	0x00, /* REG_AVDAC_CTL		(0x17)	*/
 	0x00, /* REG_ARX2VTXPGA		(0x18)	*/
 	0x00, /* REG_ARXL1_APGA_CTL	(0x19)	*/
 	0x00, /* REG_ARXR1_APGA_CTL	(0x1A)	*/
-	0x4b, /* REG_ARXL2_APGA_CTL	(0x1B)	*/
-	0x4b, /* REG_ARXR2_APGA_CTL	(0x1C)	*/
+	0x4a, /* REG_ARXL2_APGA_CTL	(0x1B)	*/
+	0x4a, /* REG_ARXR2_APGA_CTL	(0x1C)	*/
 	0x00, /* REG_ATX2ARXPGA		(0x1D)	*/
 	0x00, /* REG_BT_IF		(0x1E)	*/
 	0x00, /* REG_BTPGA		(0x1F)	*/
 	0x00, /* REG_BTSTPGA		(0x20)	*/
 	0x00, /* REG_EAR_CTL		(0x21)	*/
-	0x24, /* REG_HS_SEL		(0x22)	*/
-	0x0a, /* REG_HS_GAIN_SET	(0x23)	*/
+	0x00, /* REG_HS_SEL		(0x22)	*/
+	0x00, /* REG_HS_GAIN_SET	(0x23)	*/
 	0x00, /* REG_HS_POPN_SET	(0x24)	*/
 	0x00, /* REG_PREDL_CTL		(0x25)	*/
 	0x00, /* REG_PREDR_CTL		(0x26)	*/
@@ -99,7 +99,7 @@
 	0x00, /* REG_I2S_RX_SCRAMBLE_H	(0x37)	*/
 	0x00, /* REG_I2S_RX_SCRAMBLE_M	(0x38)	*/
 	0x00, /* REG_I2S_RX_SCRAMBLE_L	(0x39)	*/
-	0x16, /* REG_APLL_CTL		(0x3A)	*/
+	0x06, /* REG_APLL_CTL		(0x3A)	*/
 	0x00, /* REG_DTMF_CTL		(0x3B)	*/
 	0x00, /* REG_DTMF_PGA_CTL2	(0x3C)	*/
 	0x00, /* REG_DTMF_PGA_CTL1	(0x3D)	*/
@@ -1203,6 +1203,8 @@
 	SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event,
 			    SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
 
+	SND_SOC_DAPM_SUPPLY("AIF Enable", TWL4030_REG_AUDIO_IF, 0, 0, NULL, 0),
+
 	/* Output MIXER controls */
 	/* Earpiece */
 	SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
@@ -1337,6 +1339,11 @@
 	{"Digital L2 Playback Mixer", NULL, "APLL Enable"},
 	{"Digital Voice Playback Mixer", NULL, "APLL Enable"},
 
+	{"Digital R1 Playback Mixer", NULL, "AIF Enable"},
+	{"Digital L1 Playback Mixer", NULL, "AIF Enable"},
+	{"Digital R2 Playback Mixer", NULL, "AIF Enable"},
+	{"Digital L2 Playback Mixer", NULL, "AIF Enable"},
+
 	{"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"},
 	{"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"},
 	{"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"},
@@ -1455,6 +1462,11 @@
 	{"ADC Virtual Left2", NULL, "APLL Enable"},
 	{"ADC Virtual Right2", NULL, "APLL Enable"},
 
+	{"ADC Virtual Left1", NULL, "AIF Enable"},
+	{"ADC Virtual Right1", NULL, "AIF Enable"},
+	{"ADC Virtual Left2", NULL, "AIF Enable"},
+	{"ADC Virtual Right2", NULL, "AIF Enable"},
+
 	/* Analog bypass routes */
 	{"Right1 Analog Loopback", "Switch", "Analog Right"},
 	{"Left1 Analog Loopback", "Switch", "Analog Left"},
@@ -2152,8 +2164,6 @@
 	twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
-	kfree(codec->private_data);
-	kfree(codec);
 
 	return 0;
 }
@@ -2192,7 +2202,7 @@
 	codec->write = twl4030_write;
 	codec->set_bias_level = twl4030_set_bias_level;
 	codec->dai = twl4030_dai;
-	codec->num_dai = ARRAY_SIZE(twl4030_dai),
+	codec->num_dai = ARRAY_SIZE(twl4030_dai);
 	codec->reg_cache_size = sizeof(twl4030_reg);
 	codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
 					GFP_KERNEL);
@@ -2237,6 +2247,9 @@
 {
 	struct twl4030_priv *twl4030 = platform_get_drvdata(pdev);
 
+	snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+	snd_soc_unregister_codec(&twl4030->codec);
+	kfree(twl4030->codec.reg_cache);
 	kfree(twl4030);
 
 	twl4030_codec = NULL;
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index dd6396e..f206d24 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -25,7 +25,7 @@
 /* Register descriptions are here */
 #include <linux/mfd/twl4030-codec.h>
 
-/* Sgadow register used by the audio driver */
+/* Shadow register used by the audio driver */
 #define TWL4030_REG_SW_SHADOW		0x4A
 #define TWL4030_CACHEREGNUM	(TWL4030_REG_SW_SHADOW + 1)
 
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
new file mode 100644
index 0000000..217b026
--- /dev/null
+++ b/sound/soc/codecs/wm2000.c
@@ -0,0 +1,888 @@
+/*
+ * wm2000.c  --  WM2000 ALSA Soc Audio driver
+ *
+ * Copyright 2008-2010 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The download image for the WM2000 will be requested as
+ * 'wm2000_anc.bin' by default (overridable via platform data) at
+ * runtime and is expected to be in flat binary format.  This is
+ * generated by Wolfson configuration tools and includes
+ * system-specific callibration information.  If supplied as a
+ * sequence of ASCII-encoded hexidecimal bytes this can be converted
+ * into a flat binary with a command such as this on the command line:
+ *
+ * perl -e 'while (<>) { s/[\r\n]+// ; printf("%c", hex($_)); }'
+ *                 < file  > wm2000_anc.bin
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <sound/wm2000.h>
+
+#include "wm2000.h"
+
+enum wm2000_anc_mode {
+	ANC_ACTIVE = 0,
+	ANC_BYPASS = 1,
+	ANC_STANDBY = 2,
+	ANC_OFF = 3,
+};
+
+struct wm2000_priv {
+	struct i2c_client *i2c;
+
+	enum wm2000_anc_mode anc_mode;
+
+	unsigned int anc_active:1;
+	unsigned int anc_eng_ena:1;
+	unsigned int spk_ena:1;
+
+	unsigned int mclk_div:1;
+	unsigned int speech_clarity:1;
+
+	int anc_download_size;
+	char *anc_download;
+};
+
+static struct i2c_client *wm2000_i2c;
+
+static int wm2000_write(struct i2c_client *i2c, unsigned int reg,
+			unsigned int value)
+{
+	u8 data[3];
+	int ret;
+
+	data[0] = (reg >> 8) & 0xff;
+	data[1] = reg & 0xff;
+	data[2] = value & 0xff;
+
+	dev_vdbg(&i2c->dev, "write %x = %x\n", reg, value);
+
+	ret = i2c_master_send(i2c, data, 3);
+	if (ret == 3)
+		return 0;
+	if (ret < 0)
+		return ret;
+	else
+		return -EIO;
+}
+
+static unsigned int wm2000_read(struct i2c_client *i2c, unsigned int r)
+{
+	struct i2c_msg xfer[2];
+	u8 reg[2];
+	u8 data;
+	int ret;
+
+	/* Write register */
+	reg[0] = (r >> 8) & 0xff;
+	reg[1] = r & 0xff;
+	xfer[0].addr = i2c->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = sizeof(reg);
+	xfer[0].buf = &reg[0];
+
+	/* Read data */
+	xfer[1].addr = i2c->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = 1;
+	xfer[1].buf = &data;
+
+	ret = i2c_transfer(i2c->adapter, xfer, 2);
+	if (ret != 2) {
+		dev_err(&i2c->dev, "i2c_transfer() returned %d\n", ret);
+		return 0;
+	}
+
+	dev_vdbg(&i2c->dev, "read %x from %x\n", data, r);
+
+	return data;
+}
+
+static void wm2000_reset(struct wm2000_priv *wm2000)
+{
+	struct i2c_client *i2c = wm2000->i2c;
+
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR);
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR);
+	wm2000_write(i2c, WM2000_REG_ID1, 0);
+
+	wm2000->anc_mode = ANC_OFF;
+}
+
+static int wm2000_poll_bit(struct i2c_client *i2c,
+			   unsigned int reg, u8 mask, int timeout)
+{
+	int val;
+
+	val = wm2000_read(i2c, reg);
+
+	while (!(val & mask) && --timeout) {
+		msleep(1);
+		val = wm2000_read(i2c, reg);
+	}
+
+	if (timeout == 0)
+		return 0;
+	else
+		return 1;
+}
+
+static int wm2000_power_up(struct i2c_client *i2c, int analogue)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+	int ret, timeout;
+
+	BUG_ON(wm2000->anc_mode != ANC_OFF);
+
+	dev_dbg(&i2c->dev, "Beginning power up\n");
+
+	if (!wm2000->mclk_div) {
+		dev_dbg(&i2c->dev, "Disabling MCLK divider\n");
+		wm2000_write(i2c, WM2000_REG_SYS_CTL2,
+			     WM2000_MCLK_DIV2_ENA_CLR);
+	} else {
+		dev_dbg(&i2c->dev, "Enabling MCLK divider\n");
+		wm2000_write(i2c, WM2000_REG_SYS_CTL2,
+			     WM2000_MCLK_DIV2_ENA_SET);
+	}
+
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_CLR);
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_ENG_SET);
+
+	/* Wait for ANC engine to become ready */
+	if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
+			     WM2000_ANC_ENG_IDLE, 1)) {
+		dev_err(&i2c->dev, "ANC engine failed to reset\n");
+		return -ETIMEDOUT;
+	}
+
+	if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+			     WM2000_STATUS_BOOT_COMPLETE, 1)) {
+		dev_err(&i2c->dev, "ANC engine failed to initialise\n");
+		return -ETIMEDOUT;
+	}
+
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET);
+
+	/* Open code download of the data since it is the only bulk
+	 * write we do. */
+	dev_dbg(&i2c->dev, "Downloading %d bytes\n",
+		wm2000->anc_download_size - 2);
+
+	ret = i2c_master_send(i2c, wm2000->anc_download,
+			      wm2000->anc_download_size);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "i2c_transfer() failed: %d\n", ret);
+		return ret;
+	}
+	if (ret != wm2000->anc_download_size) {
+		dev_err(&i2c->dev, "i2c_transfer() failed, %d != %d\n",
+			ret, wm2000->anc_download_size);
+		return -EIO;
+	}
+
+	dev_dbg(&i2c->dev, "Download complete\n");
+
+	if (analogue) {
+		timeout = 248;
+		wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, timeout / 4);
+
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_ANA_SEQ_INCLUDE |
+			     WM2000_MODE_MOUSE_ENABLE |
+			     WM2000_MODE_THERMAL_ENABLE);
+	} else {
+		timeout = 10;
+
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_MOUSE_ENABLE |
+			     WM2000_MODE_THERMAL_ENABLE);
+	}
+
+	ret = wm2000_read(i2c, WM2000_REG_SPEECH_CLARITY);
+	if (wm2000->speech_clarity)
+		ret &= ~WM2000_SPEECH_CLARITY;
+	else
+		ret |= WM2000_SPEECH_CLARITY;
+	wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, ret);
+
+	wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33);
+	wm2000_write(i2c, WM2000_REG_SYS_START1, 0x02);
+
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR);
+
+	if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+			     WM2000_STATUS_MOUSE_ACTIVE, timeout)) {
+		dev_err(&i2c->dev, "Timed out waiting for device after %dms\n",
+			timeout * 10);
+		return -ETIMEDOUT;
+	}
+
+	dev_dbg(&i2c->dev, "ANC active\n");
+	if (analogue)
+		dev_dbg(&i2c->dev, "Analogue active\n");
+	wm2000->anc_mode = ANC_ACTIVE;
+
+	return 0;
+}
+
+static int wm2000_power_down(struct i2c_client *i2c, int analogue)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+	int timeout;
+
+	if (analogue) {
+		timeout = 248;
+		wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, timeout / 4);
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_ANA_SEQ_INCLUDE |
+			     WM2000_MODE_POWER_DOWN);
+	} else {
+		timeout = 10;
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_POWER_DOWN);
+	}
+
+	if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+			     WM2000_STATUS_POWER_DOWN_COMPLETE, timeout)) {
+		dev_err(&i2c->dev, "Timeout waiting for ANC power down\n");
+		return -ETIMEDOUT;
+	}
+
+	if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
+			     WM2000_ANC_ENG_IDLE, 1)) {
+		dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n");
+		return -ETIMEDOUT;
+	}
+
+	dev_dbg(&i2c->dev, "powered off\n");
+	wm2000->anc_mode = ANC_OFF;
+
+	return 0;
+}
+
+static int wm2000_enter_bypass(struct i2c_client *i2c, int analogue)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+	BUG_ON(wm2000->anc_mode != ANC_ACTIVE);
+
+	if (analogue) {
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_ANA_SEQ_INCLUDE |
+			     WM2000_MODE_THERMAL_ENABLE |
+			     WM2000_MODE_BYPASS_ENTRY);
+	} else {
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_THERMAL_ENABLE |
+			     WM2000_MODE_BYPASS_ENTRY);
+	}
+
+	if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+			     WM2000_STATUS_ANC_DISABLED, 10)) {
+		dev_err(&i2c->dev, "Timeout waiting for ANC disable\n");
+		return -ETIMEDOUT;
+	}
+
+	if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT,
+			     WM2000_ANC_ENG_IDLE, 1)) {
+		dev_err(&i2c->dev, "Timeout waiting for ANC engine idle\n");
+		return -ETIMEDOUT;
+	}
+
+	wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY);
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR);
+
+	wm2000->anc_mode = ANC_BYPASS;
+	dev_dbg(&i2c->dev, "bypass enabled\n");
+
+	return 0;
+}
+
+static int wm2000_exit_bypass(struct i2c_client *i2c, int analogue)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+	BUG_ON(wm2000->anc_mode != ANC_BYPASS);
+	
+	wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0);
+
+	if (analogue) {
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_ANA_SEQ_INCLUDE |
+			     WM2000_MODE_MOUSE_ENABLE |
+			     WM2000_MODE_THERMAL_ENABLE);
+	} else {
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_MOUSE_ENABLE |
+			     WM2000_MODE_THERMAL_ENABLE);
+	}
+
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET);
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR);
+
+	if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+			     WM2000_STATUS_MOUSE_ACTIVE, 10)) {
+		dev_err(&i2c->dev, "Timed out waiting for MOUSE\n");
+		return -ETIMEDOUT;
+	}
+
+	wm2000->anc_mode = ANC_ACTIVE;
+	dev_dbg(&i2c->dev, "MOUSE active\n");
+
+	return 0;
+}
+
+static int wm2000_enter_standby(struct i2c_client *i2c, int analogue)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+	int timeout;
+
+	BUG_ON(wm2000->anc_mode != ANC_ACTIVE);
+
+	if (analogue) {
+		timeout = 248;
+		wm2000_write(i2c, WM2000_REG_ANA_VMID_PD_TIME, timeout / 4);
+
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_ANA_SEQ_INCLUDE |
+			     WM2000_MODE_THERMAL_ENABLE |
+			     WM2000_MODE_STANDBY_ENTRY);
+	} else {
+		timeout = 10;
+
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_THERMAL_ENABLE |
+			     WM2000_MODE_STANDBY_ENTRY);
+	}
+
+	if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+			     WM2000_STATUS_ANC_DISABLED, timeout)) {
+		dev_err(&i2c->dev,
+			"Timed out waiting for ANC disable after 1ms\n");
+		return -ETIMEDOUT;
+	}
+
+	if (!wm2000_poll_bit(i2c, WM2000_REG_ANC_STAT, WM2000_ANC_ENG_IDLE,
+			     1)) {
+		dev_err(&i2c->dev,
+			"Timed out waiting for standby after %dms\n",
+			timeout * 10);
+		return -ETIMEDOUT;
+	}
+
+	wm2000_write(i2c, WM2000_REG_SYS_CTL1, WM2000_SYS_STBY);
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_CLR);
+
+	wm2000->anc_mode = ANC_STANDBY;
+	dev_dbg(&i2c->dev, "standby\n");
+	if (analogue)
+		dev_dbg(&i2c->dev, "Analogue disabled\n");
+
+	return 0;
+}
+
+static int wm2000_exit_standby(struct i2c_client *i2c, int analogue)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+	int timeout;
+
+	BUG_ON(wm2000->anc_mode != ANC_STANDBY);
+
+	wm2000_write(i2c, WM2000_REG_SYS_CTL1, 0);
+
+	if (analogue) {
+		timeout = 248;
+		wm2000_write(i2c, WM2000_REG_ANA_VMID_PU_TIME, timeout / 4);
+
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_ANA_SEQ_INCLUDE |
+			     WM2000_MODE_THERMAL_ENABLE |
+			     WM2000_MODE_MOUSE_ENABLE);
+	} else {
+		timeout = 10;
+
+		wm2000_write(i2c, WM2000_REG_SYS_MODE_CNTRL,
+			     WM2000_MODE_THERMAL_ENABLE |
+			     WM2000_MODE_MOUSE_ENABLE);
+	}
+
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_RAM_SET);
+	wm2000_write(i2c, WM2000_REG_SYS_CTL2, WM2000_ANC_INT_N_CLR);
+
+	if (!wm2000_poll_bit(i2c, WM2000_REG_SYS_STATUS,
+			     WM2000_STATUS_MOUSE_ACTIVE, timeout)) {
+		dev_err(&i2c->dev, "Timed out waiting for MOUSE after %dms\n",
+			timeout * 10);
+		return -ETIMEDOUT;
+	}
+
+	wm2000->anc_mode = ANC_ACTIVE;
+	dev_dbg(&i2c->dev, "MOUSE active\n");
+	if (analogue)
+		dev_dbg(&i2c->dev, "Analogue enabled\n");
+
+	return 0;
+}
+
+typedef int (*wm2000_mode_fn)(struct i2c_client *i2c, int analogue);
+
+static struct {
+	enum wm2000_anc_mode source;
+	enum wm2000_anc_mode dest;
+	int analogue;
+	wm2000_mode_fn step[2];
+} anc_transitions[] = {
+	{
+		.source = ANC_OFF,
+		.dest = ANC_ACTIVE,
+		.analogue = 1,
+		.step = {
+			wm2000_power_up,
+		},
+	},
+	{
+		.source = ANC_OFF,
+		.dest = ANC_STANDBY,
+		.step = {
+			wm2000_power_up,
+			wm2000_enter_standby,
+		},
+	},
+	{
+		.source = ANC_OFF,
+		.dest = ANC_BYPASS,
+		.analogue = 1,
+		.step = {
+			wm2000_power_up,
+			wm2000_enter_bypass,
+		},
+	},
+	{
+		.source = ANC_ACTIVE,
+		.dest = ANC_BYPASS,
+		.analogue = 1,
+		.step = {
+			wm2000_enter_bypass,
+		},
+	},
+	{
+		.source = ANC_ACTIVE,
+		.dest = ANC_STANDBY,
+		.analogue = 1,
+		.step = {
+			wm2000_enter_standby,
+		},
+	},
+	{
+		.source = ANC_ACTIVE,
+		.dest = ANC_OFF,
+		.analogue = 1,
+		.step = {
+			wm2000_power_down,
+		},
+	},
+	{
+		.source = ANC_BYPASS,
+		.dest = ANC_ACTIVE,
+		.analogue = 1,
+		.step = {
+			wm2000_exit_bypass,
+		},
+	},
+	{
+		.source = ANC_BYPASS,
+		.dest = ANC_STANDBY,
+		.analogue = 1,
+		.step = {
+			wm2000_exit_bypass,
+			wm2000_enter_standby,
+		},
+	},
+	{
+		.source = ANC_BYPASS,
+		.dest = ANC_OFF,
+		.step = {
+			wm2000_exit_bypass,
+			wm2000_power_down,
+		},
+	},
+	{
+		.source = ANC_STANDBY,
+		.dest = ANC_ACTIVE,
+		.analogue = 1,
+		.step = {
+			wm2000_exit_standby,
+		},
+	},
+	{
+		.source = ANC_STANDBY,
+		.dest = ANC_BYPASS,
+		.analogue = 1,
+		.step = {
+			wm2000_exit_standby,
+			wm2000_enter_bypass,
+		},
+	},
+	{
+		.source = ANC_STANDBY,
+		.dest = ANC_OFF,
+		.step = {
+			wm2000_exit_standby,
+			wm2000_power_down,
+		},
+	},
+};
+
+static int wm2000_anc_transition(struct wm2000_priv *wm2000,
+				 enum wm2000_anc_mode mode)
+{
+	struct i2c_client *i2c = wm2000->i2c;
+	int i, j;
+	int ret;
+
+	if (wm2000->anc_mode == mode)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(anc_transitions); i++)
+		if (anc_transitions[i].source == wm2000->anc_mode &&
+		    anc_transitions[i].dest == mode)
+			break;
+	if (i == ARRAY_SIZE(anc_transitions)) {
+		dev_err(&i2c->dev, "No transition for %d->%d\n",
+			wm2000->anc_mode, mode);
+		return -EINVAL;
+	}
+
+	for (j = 0; j < ARRAY_SIZE(anc_transitions[j].step); j++) {
+		if (!anc_transitions[i].step[j])
+			break;
+		ret = anc_transitions[i].step[j](i2c,
+						 anc_transitions[i].analogue);
+		if (ret != 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int wm2000_anc_set_mode(struct wm2000_priv *wm2000)
+{
+	struct i2c_client *i2c = wm2000->i2c;
+	enum wm2000_anc_mode mode;
+
+	if (wm2000->anc_eng_ena && wm2000->spk_ena)
+		if (wm2000->anc_active)
+			mode = ANC_ACTIVE;
+		else
+			mode = ANC_BYPASS;
+	else
+		mode = ANC_STANDBY;
+
+	dev_dbg(&i2c->dev, "Set mode %d (enabled %d, mute %d, active %d)\n",
+		mode, wm2000->anc_eng_ena, !wm2000->spk_ena,
+		wm2000->anc_active);
+
+	return wm2000_anc_transition(wm2000, mode);
+}
+
+static int wm2000_anc_mode_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&wm2000_i2c->dev);
+
+	ucontrol->value.enumerated.item[0] = wm2000->anc_active;
+
+	return 0;
+}
+
+static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&wm2000_i2c->dev);
+	int anc_active = ucontrol->value.enumerated.item[0];
+
+	if (anc_active > 1)
+		return -EINVAL;
+
+	wm2000->anc_active = anc_active;
+
+	return wm2000_anc_set_mode(wm2000);
+}
+
+static int wm2000_speaker_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&wm2000_i2c->dev);
+
+	ucontrol->value.enumerated.item[0] = wm2000->spk_ena;
+
+	return 0;
+}
+
+static int wm2000_speaker_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&wm2000_i2c->dev);
+	int val = ucontrol->value.enumerated.item[0];
+
+	if (val > 1)
+		return -EINVAL;
+
+	wm2000->spk_ena = val;
+
+	return wm2000_anc_set_mode(wm2000);
+}
+
+static const struct snd_kcontrol_new wm2000_controls[] = {
+	SOC_SINGLE_BOOL_EXT("WM2000 ANC Switch", 0,
+			    wm2000_anc_mode_get,
+			    wm2000_anc_mode_put),
+	SOC_SINGLE_BOOL_EXT("WM2000 Switch", 0,
+			    wm2000_speaker_get,
+			    wm2000_speaker_put),
+};
+
+static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&wm2000_i2c->dev);
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		wm2000->anc_eng_ena = 1;
+
+	if (SND_SOC_DAPM_EVENT_OFF(event))
+		wm2000->anc_eng_ena = 0;
+
+	return wm2000_anc_set_mode(wm2000);
+}
+
+static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = {
+/* Externally visible pins */
+SND_SOC_DAPM_OUTPUT("WM2000 SPKN"),
+SND_SOC_DAPM_OUTPUT("WM2000 SPKP"),
+
+SND_SOC_DAPM_INPUT("WM2000 LINN"),
+SND_SOC_DAPM_INPUT("WM2000 LINP"),
+
+SND_SOC_DAPM_PGA_E("ANC Engine", SND_SOC_NOPM, 0, 0, NULL, 0,
+		   wm2000_anc_power_event,
+		   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+};
+
+/* Target, Path, Source */
+static const struct snd_soc_dapm_route audio_map[] = {
+	{ "WM2000 SPKN", NULL, "ANC Engine" },
+	{ "WM2000 SPKP", NULL, "ANC Engine" },
+	{ "ANC Engine", NULL, "WM2000 LINN" },
+	{ "ANC Engine", NULL, "WM2000 LINP" },
+};
+
+/* Called from the machine driver */
+int wm2000_add_controls(struct snd_soc_codec *codec)
+{
+	int ret;
+
+	if (!wm2000_i2c) {
+		pr_err("WM2000 not yet probed\n");
+		return -ENODEV;
+	}
+
+	ret = snd_soc_dapm_new_controls(codec, wm2000_dapm_widgets,
+					ARRAY_SIZE(wm2000_dapm_widgets));
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+	if (ret < 0)
+		return ret;
+
+	return snd_soc_add_controls(codec, wm2000_controls,
+			ARRAY_SIZE(wm2000_controls));
+}
+EXPORT_SYMBOL_GPL(wm2000_add_controls);
+
+static int __devinit wm2000_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *i2c_id)
+{
+	struct wm2000_priv *wm2000;
+	struct wm2000_platform_data *pdata;
+	const char *filename;
+	const struct firmware *fw;
+	int reg, ret;
+	u16 id;
+
+	if (wm2000_i2c) {
+		dev_err(&i2c->dev, "Another WM2000 is already registered\n");
+		return -EINVAL;
+	}
+
+	wm2000 = kzalloc(sizeof(struct wm2000_priv), GFP_KERNEL);
+	if (wm2000 == NULL) {
+		dev_err(&i2c->dev, "Unable to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	/* Verify that this is a WM2000 */
+	reg = wm2000_read(i2c, WM2000_REG_ID1);
+	id = reg << 8;
+	reg = wm2000_read(i2c, WM2000_REG_ID2);
+	id |= reg & 0xff;
+
+	if (id != 0x2000) {
+		dev_err(&i2c->dev, "Device is not a WM2000 - ID %x\n", id);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	reg = wm2000_read(i2c, WM2000_REG_REVISON);
+	dev_info(&i2c->dev, "revision %c\n", reg + 'A');
+
+	filename = "wm2000_anc.bin";
+	pdata = dev_get_platdata(&i2c->dev);
+	if (pdata) {
+		wm2000->mclk_div = pdata->mclkdiv2;
+		wm2000->speech_clarity = !pdata->speech_enh_disable;
+
+		if (pdata->download_file)
+			filename = pdata->download_file;
+	}
+
+	ret = request_firmware(&fw, filename, &i2c->dev);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to acquire ANC data: %d\n", ret);
+		goto err;
+	}
+
+	/* Pre-cook the concatenation of the register address onto the image */
+	wm2000->anc_download_size = fw->size + 2;
+	wm2000->anc_download = kmalloc(wm2000->anc_download_size, GFP_KERNEL);
+	if (wm2000->anc_download == NULL) {
+		dev_err(&i2c->dev, "Out of memory\n");
+		ret = -ENOMEM;
+		goto err_fw;
+	}
+
+	wm2000->anc_download[0] = 0x80;
+	wm2000->anc_download[1] = 0x00;
+	memcpy(wm2000->anc_download + 2, fw->data, fw->size);
+
+	release_firmware(fw);
+
+	dev_set_drvdata(&i2c->dev, wm2000);
+	wm2000->anc_eng_ena = 1;
+	wm2000->i2c = i2c;
+
+	wm2000_reset(wm2000);
+
+	/* This will trigger a transition to standby mode by default */
+	wm2000_anc_set_mode(wm2000);	
+
+	wm2000_i2c = i2c;
+
+	return 0;
+
+err_fw:
+	release_firmware(fw);
+err:
+	kfree(wm2000);
+	return ret;
+}
+
+static __devexit int wm2000_i2c_remove(struct i2c_client *i2c)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+	wm2000_anc_transition(wm2000, ANC_OFF);
+
+	wm2000_i2c = NULL;
+	kfree(wm2000->anc_download);
+	kfree(wm2000);
+
+	return 0;
+}
+
+static void wm2000_i2c_shutdown(struct i2c_client *i2c)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+	wm2000_anc_transition(wm2000, ANC_OFF);
+}
+
+#ifdef CONFIG_PM
+static int wm2000_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+	return wm2000_anc_transition(wm2000, ANC_OFF);
+}
+
+static int wm2000_i2c_resume(struct i2c_client *i2c)
+{
+	struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
+
+	return wm2000_anc_set_mode(wm2000);
+}
+#else
+#define wm2000_i2c_suspend NULL
+#define wm2000_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm2000_i2c_id[] = {
+	{ "wm2000", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm2000_i2c_id);
+
+static struct i2c_driver wm2000_i2c_driver = {
+	.driver = {
+		.name = "wm2000",
+		.owner = THIS_MODULE,
+	},
+	.probe = wm2000_i2c_probe,
+	.remove = __devexit_p(wm2000_i2c_remove),
+	.suspend = wm2000_i2c_suspend,
+	.resume = wm2000_i2c_resume,
+	.shutdown = wm2000_i2c_shutdown,
+	.id_table = wm2000_i2c_id,
+};
+
+static int __init wm2000_init(void)
+{
+	return i2c_add_driver(&wm2000_i2c_driver);
+}
+module_init(wm2000_init);
+
+static void __exit wm2000_exit(void)
+{
+	i2c_del_driver(&wm2000_i2c_driver);
+}
+module_exit(wm2000_exit);
+
+MODULE_DESCRIPTION("ASoC WM2000 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm2000.h b/sound/soc/codecs/wm2000.h
new file mode 100644
index 0000000..c18e261
--- /dev/null
+++ b/sound/soc/codecs/wm2000.h
@@ -0,0 +1,79 @@
+/*
+ * wm2000.h  --  WM2000 Soc Audio driver
+ *
+ * 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 _WM2000_H
+#define _WM2000_H
+
+struct wm2000_setup_data {
+	unsigned short i2c_address;
+	int mclk_div;   /* Set to a non-zero value if MCLK_DIV_2 required */
+};
+
+extern int wm2000_add_controls(struct snd_soc_codec *codec);
+
+extern struct snd_soc_dai wm2000_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm2000;
+
+#define WM2000_REG_SYS_START	    0x8000
+#define WM2000_REG_SPEECH_CLARITY   0x8fef
+#define WM2000_REG_SYS_WATCHDOG     0x8ff6
+#define WM2000_REG_ANA_VMID_PD_TIME 0x8ff7
+#define WM2000_REG_ANA_VMID_PU_TIME 0x8ff8
+#define WM2000_REG_CAT_FLTR_INDX    0x8ff9
+#define WM2000_REG_CAT_GAIN_0       0x8ffa
+#define WM2000_REG_SYS_STATUS       0x8ffc
+#define WM2000_REG_SYS_MODE_CNTRL   0x8ffd
+#define WM2000_REG_SYS_START0       0x8ffe
+#define WM2000_REG_SYS_START1       0x8fff
+#define WM2000_REG_ID1              0xf000
+#define WM2000_REG_ID2              0xf001
+#define WM2000_REG_REVISON          0xf002
+#define WM2000_REG_SYS_CTL1         0xf003
+#define WM2000_REG_SYS_CTL2         0xf004
+#define WM2000_REG_ANC_STAT         0xf005
+#define WM2000_REG_IF_CTL           0xf006
+
+/* SPEECH_CLARITY */
+#define WM2000_SPEECH_CLARITY   0x01
+
+/* SYS_STATUS */
+#define WM2000_STATUS_MOUSE_ACTIVE              0x40
+#define WM2000_STATUS_CAT_FREQ_COMPLETE         0x20
+#define WM2000_STATUS_CAT_GAIN_COMPLETE         0x10
+#define WM2000_STATUS_THERMAL_SHUTDOWN_COMPLETE 0x08
+#define WM2000_STATUS_ANC_DISABLED              0x04
+#define WM2000_STATUS_POWER_DOWN_COMPLETE       0x02
+#define WM2000_STATUS_BOOT_COMPLETE             0x01
+
+/* SYS_MODE_CNTRL */
+#define WM2000_MODE_ANA_SEQ_INCLUDE 0x80
+#define WM2000_MODE_MOUSE_ENABLE    0x40
+#define WM2000_MODE_CAT_FREQ_ENABLE 0x20
+#define WM2000_MODE_CAT_GAIN_ENABLE 0x10
+#define WM2000_MODE_BYPASS_ENTRY    0x08
+#define WM2000_MODE_STANDBY_ENTRY   0x04
+#define WM2000_MODE_THERMAL_ENABLE  0x02
+#define WM2000_MODE_POWER_DOWN      0x01
+
+/* SYS_CTL1 */
+#define WM2000_SYS_STBY          0x01
+
+/* SYS_CTL2 */
+#define WM2000_MCLK_DIV2_ENA_CLR 0x80
+#define WM2000_MCLK_DIV2_ENA_SET 0x40
+#define WM2000_ANC_ENG_CLR       0x20
+#define WM2000_ANC_ENG_SET       0x10
+#define WM2000_ANC_INT_N_CLR     0x08
+#define WM2000_ANC_INT_N_SET     0x04
+#define WM2000_RAM_CLR           0x02
+#define WM2000_RAM_SET           0x01
+
+/* ANC_STAT */
+#define WM2000_ANC_ENG_IDLE      0x01
+
+#endif
diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c
index d8ffbd6..63a254e 100644
--- a/sound/soc/codecs/wm8727.c
+++ b/sound/soc/codecs/wm8727.c
@@ -44,23 +44,16 @@
 };
 EXPORT_SYMBOL_GPL(wm8727_dai);
 
+static struct snd_soc_codec *wm8727_codec;
+
 static int wm8727_soc_probe(struct platform_device *pdev)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct snd_soc_codec *codec;
 	int ret = 0;
 
-	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-	if (codec == NULL)
-		return -ENOMEM;
-	mutex_init(&codec->mutex);
-	codec->name = "WM8727";
-	codec->owner = THIS_MODULE;
-	codec->dai = &wm8727_dai;
-	codec->num_dai = 1;
-	socdev->card->codec = codec;
-	INIT_LIST_HEAD(&codec->dapm_widgets);
-	INIT_LIST_HEAD(&codec->dapm_paths);
+	BUG_ON(!wm8727_codec);
+
+	socdev->card->codec = wm8727_codec;
 
 	/* register pcms */
 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@@ -80,12 +73,9 @@
 static int wm8727_soc_remove(struct platform_device *pdev)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct snd_soc_codec *codec = socdev->card->codec;
 
-	if (codec == NULL)
-		return 0;
 	snd_soc_free_pcms(socdev);
-	kfree(codec);
+
 	return 0;
 }
 
@@ -98,13 +88,55 @@
 
 static __devinit int wm8727_platform_probe(struct platform_device *pdev)
 {
+	struct snd_soc_codec *codec;
+	int ret;
+
+	if (wm8727_codec) {
+		dev_err(&pdev->dev, "Another WM8727 is registered\n");
+		return -EBUSY;
+	}
+
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+	wm8727_codec = codec;
+
+	platform_set_drvdata(pdev, codec);
+
+	mutex_init(&codec->mutex);
+	codec->dev = &pdev->dev;
+	codec->name = "WM8727";
+	codec->owner = THIS_MODULE;
+	codec->dai = &wm8727_dai;
+	codec->num_dai = 1;
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
 	wm8727_dai.dev = &pdev->dev;
-	return snd_soc_register_dai(&wm8727_dai);
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to register CODEC: %d\n", ret);
+		goto err;
+	}
+
+	ret = snd_soc_register_dai(&wm8727_dai);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to register DAI: %d\n", ret);
+		goto err_codec;
+	}
+
+err_codec:
+	snd_soc_unregister_codec(codec);
+err:
+	kfree(codec);
+	return ret;
 }
 
 static int __devexit wm8727_platform_remove(struct platform_device *pdev)
 {
 	snd_soc_unregister_dai(&wm8727_dai);
+	snd_soc_unregister_codec(platform_get_drvdata(pdev));
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 3a49781..5a2619d 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -456,6 +456,9 @@
 
 	/* Sync reg_cache with the hardware */
 	for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
+		if (cache[i] == wm8731_reg[i])
+			continue;
+
 		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
 		data[1] = cache[i] & 0x00ff;
 		codec->hw_write(codec->control_data, data, 2);
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index d6850dacd..c2444e7 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1507,10 +1507,6 @@
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->card->codec;
 
-	/* we only need to suspend if we are a valid card */
-	if (!codec->card)
-		return 0;
-
 	wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
@@ -1523,10 +1519,6 @@
 	u8 data[2];
 	u16 *cache = codec->reg_cache;
 
-	/* we only need to resume if we are a valid card */
-	if (!codec->card)
-		return 0;
-
 	/* Sync reg_cache with the hardware */
 	for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) {
 		if (i + 1 == WM8753_RESET)
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index ab2c0da..44e7d9d 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -406,6 +406,8 @@
 
 	/* Sync reg_cache with the hardware */
 	for (i = 0; i < ARRAY_SIZE(wm8776_reg); i++) {
+		if (cache[i] == wm8776_reg[i])
+			continue;
 		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
 		data[1] = cache[i] & 0x00ff;
 		codec->hw_write(codec->control_data, data, 2);
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
new file mode 100644
index 0000000..593e47d
--- /dev/null
+++ b/sound/soc/codecs/wm8904.c
@@ -0,0 +1,2656 @@
+/*
+ * wm8904.c  --  WM8904 ALSA SoC Audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/wm8904.h>
+
+#include "wm8904.h"
+
+static struct snd_soc_codec *wm8904_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8904;
+
+enum wm8904_type {
+	WM8904,
+	WM8912,
+};
+
+#define WM8904_NUM_DCS_CHANNELS 4
+
+#define WM8904_NUM_SUPPLIES 5
+static const char *wm8904_supply_names[WM8904_NUM_SUPPLIES] = {
+	"DCVDD",
+	"DBVDD",
+	"AVDD",
+	"CPVDD",
+	"MICVDD",
+};
+
+/* codec private data */
+struct wm8904_priv {
+	struct snd_soc_codec codec;
+	u16 reg_cache[WM8904_MAX_REGISTER + 1];
+
+	enum wm8904_type devtype;
+
+	struct regulator_bulk_data supplies[WM8904_NUM_SUPPLIES];
+
+	struct wm8904_pdata *pdata;
+
+	int deemph;
+
+	/* Platform provided DRC configuration */
+	const char **drc_texts;
+	int drc_cfg;
+	struct soc_enum drc_enum;
+
+	/* Platform provided ReTune mobile configuration */
+	int num_retune_mobile_texts;
+	const char **retune_mobile_texts;
+	int retune_mobile_cfg;
+	struct soc_enum retune_mobile_enum;
+
+	/* FLL setup */
+	int fll_src;
+	int fll_fref;
+	int fll_fout;
+
+	/* Clocking configuration */
+	unsigned int mclk_rate;
+	int sysclk_src;
+	unsigned int sysclk_rate;
+
+	int tdm_width;
+	int tdm_slots;
+	int bclk;
+	int fs;
+
+	/* DC servo configuration - cached offset values */
+	int dcs_state[WM8904_NUM_DCS_CHANNELS];
+};
+
+static const u16 wm8904_reg[WM8904_MAX_REGISTER + 1] = {
+	0x8904,     /* R0   - SW Reset and ID */
+	0x0000,     /* R1   - Revision */
+	0x0000,     /* R2 */
+	0x0000,     /* R3 */
+	0x0018,     /* R4   - Bias Control 0 */
+	0x0000,     /* R5   - VMID Control 0 */
+	0x0000,     /* R6   - Mic Bias Control 0 */
+	0x0000,     /* R7   - Mic Bias Control 1 */
+	0x0001,     /* R8   - Analogue DAC 0 */
+	0x9696,     /* R9   - mic Filter Control */
+	0x0001,     /* R10  - Analogue ADC 0 */
+	0x0000,     /* R11 */
+	0x0000,     /* R12  - Power Management 0 */
+	0x0000,     /* R13 */
+	0x0000,     /* R14  - Power Management 2 */
+	0x0000,     /* R15  - Power Management 3 */
+	0x0000,     /* R16 */
+	0x0000,     /* R17 */
+	0x0000,     /* R18  - Power Management 6 */
+	0x0000,     /* R19 */
+	0x945E,     /* R20  - Clock Rates 0 */
+	0x0C05,     /* R21  - Clock Rates 1 */
+	0x0006,     /* R22  - Clock Rates 2 */
+	0x0000,     /* R23 */
+	0x0050,     /* R24  - Audio Interface 0 */
+	0x000A,     /* R25  - Audio Interface 1 */
+	0x00E4,     /* R26  - Audio Interface 2 */
+	0x0040,     /* R27  - Audio Interface 3 */
+	0x0000,     /* R28 */
+	0x0000,     /* R29 */
+	0x00C0,     /* R30  - DAC Digital Volume Left */
+	0x00C0,     /* R31  - DAC Digital Volume Right */
+	0x0000,     /* R32  - DAC Digital 0 */
+	0x0008,     /* R33  - DAC Digital 1 */
+	0x0000,     /* R34 */
+	0x0000,     /* R35 */
+	0x00C0,     /* R36  - ADC Digital Volume Left */
+	0x00C0,     /* R37  - ADC Digital Volume Right */
+	0x0010,     /* R38  - ADC Digital 0 */
+	0x0000,     /* R39  - Digital Microphone 0 */
+	0x01AF,     /* R40  - DRC 0 */
+	0x3248,     /* R41  - DRC 1 */
+	0x0000,     /* R42  - DRC 2 */
+	0x0000,     /* R43  - DRC 3 */
+	0x0085,     /* R44  - Analogue Left Input 0 */
+	0x0085,     /* R45  - Analogue Right Input 0 */
+	0x0044,     /* R46  - Analogue Left Input 1 */
+	0x0044,     /* R47  - Analogue Right Input 1 */
+	0x0000,     /* R48 */
+	0x0000,     /* R49 */
+	0x0000,     /* R50 */
+	0x0000,     /* R51 */
+	0x0000,     /* R52 */
+	0x0000,     /* R53 */
+	0x0000,     /* R54 */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x002D,     /* R57  - Analogue OUT1 Left */
+	0x002D,     /* R58  - Analogue OUT1 Right */
+	0x0039,     /* R59  - Analogue OUT2 Left */
+	0x0039,     /* R60  - Analogue OUT2 Right */
+	0x0000,     /* R61  - Analogue OUT12 ZC */
+	0x0000,     /* R62 */
+	0x0000,     /* R63 */
+	0x0000,     /* R64 */
+	0x0000,     /* R65 */
+	0x0000,     /* R66 */
+	0x0000,     /* R67  - DC Servo 0 */
+	0x0000,     /* R68  - DC Servo 1 */
+	0xAAAA,     /* R69  - DC Servo 2 */
+	0x0000,     /* R70 */
+	0xAAAA,     /* R71  - DC Servo 4 */
+	0xAAAA,     /* R72  - DC Servo 5 */
+	0x0000,     /* R73  - DC Servo 6 */
+	0x0000,     /* R74  - DC Servo 7 */
+	0x0000,     /* R75  - DC Servo 8 */
+	0x0000,     /* R76  - DC Servo 9 */
+	0x0000,     /* R77  - DC Servo Readback 0 */
+	0x0000,     /* R78 */
+	0x0000,     /* R79 */
+	0x0000,     /* R80 */
+	0x0000,     /* R81 */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84 */
+	0x0000,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87 */
+	0x0000,     /* R88 */
+	0x0000,     /* R89 */
+	0x0000,     /* R90  - Analogue HP 0 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92 */
+	0x0000,     /* R93 */
+	0x0000,     /* R94  - Analogue Lineout 0 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96 */
+	0x0000,     /* R97 */
+	0x0000,     /* R98  - Charge Pump 0 */
+	0x0000,     /* R99 */
+	0x0000,     /* R100 */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x0004,     /* R104 - Class W 0 */
+	0x0000,     /* R105 */
+	0x0000,     /* R106 */
+	0x0000,     /* R107 */
+	0x0000,     /* R108 - Write Sequencer 0 */
+	0x0000,     /* R109 - Write Sequencer 1 */
+	0x0000,     /* R110 - Write Sequencer 2 */
+	0x0000,     /* R111 - Write Sequencer 3 */
+	0x0000,     /* R112 - Write Sequencer 4 */
+	0x0000,     /* R113 */
+	0x0000,     /* R114 */
+	0x0000,     /* R115 */
+	0x0000,     /* R116 - FLL Control 1 */
+	0x0007,     /* R117 - FLL Control 2 */
+	0x0000,     /* R118 - FLL Control 3 */
+	0x2EE0,     /* R119 - FLL Control 4 */
+	0x0004,     /* R120 - FLL Control 5 */
+	0x0014,     /* R121 - GPIO Control 1 */
+	0x0010,     /* R122 - GPIO Control 2 */
+	0x0010,     /* R123 - GPIO Control 3 */
+	0x0000,     /* R124 - GPIO Control 4 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 - Digital Pulls */
+	0x0000,     /* R127 - Interrupt Status */
+	0xFFFF,     /* R128 - Interrupt Status Mask */
+	0x0000,     /* R129 - Interrupt Polarity */
+	0x0000,     /* R130 - Interrupt Debounce */
+	0x0000,     /* R131 */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 */
+	0x0000,     /* R134 - EQ1 */
+	0x000C,     /* R135 - EQ2 */
+	0x000C,     /* R136 - EQ3 */
+	0x000C,     /* R137 - EQ4 */
+	0x000C,     /* R138 - EQ5 */
+	0x000C,     /* R139 - EQ6 */
+	0x0FCA,     /* R140 - EQ7 */
+	0x0400,     /* R141 - EQ8 */
+	0x00D8,     /* R142 - EQ9 */
+	0x1EB5,     /* R143 - EQ10 */
+	0xF145,     /* R144 - EQ11 */
+	0x0B75,     /* R145 - EQ12 */
+	0x01C5,     /* R146 - EQ13 */
+	0x1C58,     /* R147 - EQ14 */
+	0xF373,     /* R148 - EQ15 */
+	0x0A54,     /* R149 - EQ16 */
+	0x0558,     /* R150 - EQ17 */
+	0x168E,     /* R151 - EQ18 */
+	0xF829,     /* R152 - EQ19 */
+	0x07AD,     /* R153 - EQ20 */
+	0x1103,     /* R154 - EQ21 */
+	0x0564,     /* R155 - EQ22 */
+	0x0559,     /* R156 - EQ23 */
+	0x4000,     /* R157 - EQ24 */
+	0x0000,     /* R158 */
+	0x0000,     /* R159 */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 - Control Interface Test 1 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 */
+	0x0000,     /* R164 */
+	0x0000,     /* R165 */
+	0x0000,     /* R166 */
+	0x0000,     /* R167 */
+	0x0000,     /* R168 */
+	0x0000,     /* R169 */
+	0x0000,     /* R170 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 */
+	0x0000,     /* R173 */
+	0x0000,     /* R174 */
+	0x0000,     /* R175 */
+	0x0000,     /* R176 */
+	0x0000,     /* R177 */
+	0x0000,     /* R178 */
+	0x0000,     /* R179 */
+	0x0000,     /* R180 */
+	0x0000,     /* R181 */
+	0x0000,     /* R182 */
+	0x0000,     /* R183 */
+	0x0000,     /* R184 */
+	0x0000,     /* R185 */
+	0x0000,     /* R186 */
+	0x0000,     /* R187 */
+	0x0000,     /* R188 */
+	0x0000,     /* R189 */
+	0x0000,     /* R190 */
+	0x0000,     /* R191 */
+	0x0000,     /* R192 */
+	0x0000,     /* R193 */
+	0x0000,     /* R194 */
+	0x0000,     /* R195 */
+	0x0000,     /* R196 */
+	0x0000,     /* R197 */
+	0x0000,     /* R198 */
+	0x0000,     /* R199 */
+	0x0000,     /* R200 */
+	0x0000,     /* R201 */
+	0x0000,     /* R202 */
+	0x0000,     /* R203 */
+	0x0000,     /* R204 - Analogue Output Bias 0 */
+	0x0000,     /* R205 */
+	0x0000,     /* R206 */
+	0x0000,     /* R207 */
+	0x0000,     /* R208 */
+	0x0000,     /* R209 */
+	0x0000,     /* R210 */
+	0x0000,     /* R211 */
+	0x0000,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 */
+	0x0000,     /* R216 */
+	0x0000,     /* R217 */
+	0x0000,     /* R218 */
+	0x0000,     /* R219 */
+	0x0000,     /* R220 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0000,     /* R223 */
+	0x0000,     /* R224 */
+	0x0000,     /* R225 */
+	0x0000,     /* R226 */
+	0x0000,     /* R227 */
+	0x0000,     /* R228 */
+	0x0000,     /* R229 */
+	0x0000,     /* R230 */
+	0x0000,     /* R231 */
+	0x0000,     /* R232 */
+	0x0000,     /* R233 */
+	0x0000,     /* R234 */
+	0x0000,     /* R235 */
+	0x0000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0000,     /* R238 */
+	0x0000,     /* R239 */
+	0x0000,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0000,     /* R243 */
+	0x0000,     /* R244 */
+	0x0000,     /* R245 */
+	0x0000,     /* R246 */
+	0x0000,     /* R247 - FLL NCO Test 0 */
+	0x0019,     /* R248 - FLL NCO Test 1 */
+};
+
+static struct {
+	int readable;
+	int writable;
+	int vol;
+} wm8904_access[] = {
+	{ 0xFFFF, 0xFFFF, 1 }, /* R0   - SW Reset and ID */
+	{ 0x0000, 0x0000, 0 }, /* R1   - Revision */
+	{ 0x0000, 0x0000, 0 }, /* R2 */
+	{ 0x0000, 0x0000, 0 }, /* R3 */
+	{ 0x001F, 0x001F, 0 }, /* R4   - Bias Control 0 */
+	{ 0x0047, 0x0047, 0 }, /* R5   - VMID Control 0 */
+	{ 0x007F, 0x007F, 0 }, /* R6   - Mic Bias Control 0 */
+	{ 0xC007, 0xC007, 0 }, /* R7   - Mic Bias Control 1 */
+	{ 0x001E, 0x001E, 0 }, /* R8   - Analogue DAC 0 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R9   - mic Filter Control */
+	{ 0x0001, 0x0001, 0 }, /* R10  - Analogue ADC 0 */
+	{ 0x0000, 0x0000, 0 }, /* R11 */
+	{ 0x0003, 0x0003, 0 }, /* R12  - Power Management 0 */
+	{ 0x0000, 0x0000, 0 }, /* R13 */
+	{ 0x0003, 0x0003, 0 }, /* R14  - Power Management 2 */
+	{ 0x0003, 0x0003, 0 }, /* R15  - Power Management 3 */
+	{ 0x0000, 0x0000, 0 }, /* R16 */
+	{ 0x0000, 0x0000, 0 }, /* R17 */
+	{ 0x000F, 0x000F, 0 }, /* R18  - Power Management 6 */
+	{ 0x0000, 0x0000, 0 }, /* R19 */
+	{ 0x7001, 0x7001, 0 }, /* R20  - Clock Rates 0 */
+	{ 0x3C07, 0x3C07, 0 }, /* R21  - Clock Rates 1 */
+	{ 0xD00F, 0xD00F, 0 }, /* R22  - Clock Rates 2 */
+	{ 0x0000, 0x0000, 0 }, /* R23 */
+	{ 0x1FFF, 0x1FFF, 0 }, /* R24  - Audio Interface 0 */
+	{ 0x3DDF, 0x3DDF, 0 }, /* R25  - Audio Interface 1 */
+	{ 0x0F1F, 0x0F1F, 0 }, /* R26  - Audio Interface 2 */
+	{ 0x0FFF, 0x0FFF, 0 }, /* R27  - Audio Interface 3 */
+	{ 0x0000, 0x0000, 0 }, /* R28 */
+	{ 0x0000, 0x0000, 0 }, /* R29 */
+	{ 0x00FF, 0x01FF, 0 }, /* R30  - DAC Digital Volume Left */
+	{ 0x00FF, 0x01FF, 0 }, /* R31  - DAC Digital Volume Right */
+	{ 0x0FFF, 0x0FFF, 0 }, /* R32  - DAC Digital 0 */
+	{ 0x1E4E, 0x1E4E, 0 }, /* R33  - DAC Digital 1 */
+	{ 0x0000, 0x0000, 0 }, /* R34 */
+	{ 0x0000, 0x0000, 0 }, /* R35 */
+	{ 0x00FF, 0x01FF, 0 }, /* R36  - ADC Digital Volume Left */
+	{ 0x00FF, 0x01FF, 0 }, /* R37  - ADC Digital Volume Right */
+	{ 0x0073, 0x0073, 0 }, /* R38  - ADC Digital 0 */
+	{ 0x1800, 0x1800, 0 }, /* R39  - Digital Microphone 0 */
+	{ 0xDFEF, 0xDFEF, 0 }, /* R40  - DRC 0 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R41  - DRC 1 */
+	{ 0x003F, 0x003F, 0 }, /* R42  - DRC 2 */
+	{ 0x07FF, 0x07FF, 0 }, /* R43  - DRC 3 */
+	{ 0x009F, 0x009F, 0 }, /* R44  - Analogue Left Input 0 */
+	{ 0x009F, 0x009F, 0 }, /* R45  - Analogue Right Input 0 */
+	{ 0x007F, 0x007F, 0 }, /* R46  - Analogue Left Input 1 */
+	{ 0x007F, 0x007F, 0 }, /* R47  - Analogue Right Input 1 */
+	{ 0x0000, 0x0000, 0 }, /* R48 */
+	{ 0x0000, 0x0000, 0 }, /* R49 */
+	{ 0x0000, 0x0000, 0 }, /* R50 */
+	{ 0x0000, 0x0000, 0 }, /* R51 */
+	{ 0x0000, 0x0000, 0 }, /* R52 */
+	{ 0x0000, 0x0000, 0 }, /* R53 */
+	{ 0x0000, 0x0000, 0 }, /* R54 */
+	{ 0x0000, 0x0000, 0 }, /* R55 */
+	{ 0x0000, 0x0000, 0 }, /* R56 */
+	{ 0x017F, 0x01FF, 0 }, /* R57  - Analogue OUT1 Left */
+	{ 0x017F, 0x01FF, 0 }, /* R58  - Analogue OUT1 Right */
+	{ 0x017F, 0x01FF, 0 }, /* R59  - Analogue OUT2 Left */
+	{ 0x017F, 0x01FF, 0 }, /* R60  - Analogue OUT2 Right */
+	{ 0x000F, 0x000F, 0 }, /* R61  - Analogue OUT12 ZC */
+	{ 0x0000, 0x0000, 0 }, /* R62 */
+	{ 0x0000, 0x0000, 0 }, /* R63 */
+	{ 0x0000, 0x0000, 0 }, /* R64 */
+	{ 0x0000, 0x0000, 0 }, /* R65 */
+	{ 0x0000, 0x0000, 0 }, /* R66 */
+	{ 0x000F, 0x000F, 0 }, /* R67  - DC Servo 0 */
+	{ 0xFFFF, 0xFFFF, 1 }, /* R68  - DC Servo 1 */
+	{ 0x0F0F, 0x0F0F, 0 }, /* R69  - DC Servo 2 */
+	{ 0x0000, 0x0000, 0 }, /* R70 */
+	{ 0x007F, 0x007F, 0 }, /* R71  - DC Servo 4 */
+	{ 0x007F, 0x007F, 0 }, /* R72  - DC Servo 5 */
+	{ 0x00FF, 0x00FF, 1 }, /* R73  - DC Servo 6 */
+	{ 0x00FF, 0x00FF, 1 }, /* R74  - DC Servo 7 */
+	{ 0x00FF, 0x00FF, 1 }, /* R75  - DC Servo 8 */
+	{ 0x00FF, 0x00FF, 1 }, /* R76  - DC Servo 9 */
+	{ 0x0FFF, 0x0000, 1 }, /* R77  - DC Servo Readback 0 */
+	{ 0x0000, 0x0000, 0 }, /* R78 */
+	{ 0x0000, 0x0000, 0 }, /* R79 */
+	{ 0x0000, 0x0000, 0 }, /* R80 */
+	{ 0x0000, 0x0000, 0 }, /* R81 */
+	{ 0x0000, 0x0000, 0 }, /* R82 */
+	{ 0x0000, 0x0000, 0 }, /* R83 */
+	{ 0x0000, 0x0000, 0 }, /* R84 */
+	{ 0x0000, 0x0000, 0 }, /* R85 */
+	{ 0x0000, 0x0000, 0 }, /* R86 */
+	{ 0x0000, 0x0000, 0 }, /* R87 */
+	{ 0x0000, 0x0000, 0 }, /* R88 */
+	{ 0x0000, 0x0000, 0 }, /* R89 */
+	{ 0x00FF, 0x00FF, 0 }, /* R90  - Analogue HP 0 */
+	{ 0x0000, 0x0000, 0 }, /* R91 */
+	{ 0x0000, 0x0000, 0 }, /* R92 */
+	{ 0x0000, 0x0000, 0 }, /* R93 */
+	{ 0x00FF, 0x00FF, 0 }, /* R94  - Analogue Lineout 0 */
+	{ 0x0000, 0x0000, 0 }, /* R95 */
+	{ 0x0000, 0x0000, 0 }, /* R96 */
+	{ 0x0000, 0x0000, 0 }, /* R97 */
+	{ 0x0001, 0x0001, 0 }, /* R98  - Charge Pump 0 */
+	{ 0x0000, 0x0000, 0 }, /* R99 */
+	{ 0x0000, 0x0000, 0 }, /* R100 */
+	{ 0x0000, 0x0000, 0 }, /* R101 */
+	{ 0x0000, 0x0000, 0 }, /* R102 */
+	{ 0x0000, 0x0000, 0 }, /* R103 */
+	{ 0x0001, 0x0001, 0 }, /* R104 - Class W 0 */
+	{ 0x0000, 0x0000, 0 }, /* R105 */
+	{ 0x0000, 0x0000, 0 }, /* R106 */
+	{ 0x0000, 0x0000, 0 }, /* R107 */
+	{ 0x011F, 0x011F, 0 }, /* R108 - Write Sequencer 0 */
+	{ 0x7FFF, 0x7FFF, 0 }, /* R109 - Write Sequencer 1 */
+	{ 0x4FFF, 0x4FFF, 0 }, /* R110 - Write Sequencer 2 */
+	{ 0x003F, 0x033F, 0 }, /* R111 - Write Sequencer 3 */
+	{ 0x03F1, 0x0000, 0 }, /* R112 - Write Sequencer 4 */
+	{ 0x0000, 0x0000, 0 }, /* R113 */
+	{ 0x0000, 0x0000, 0 }, /* R114 */
+	{ 0x0000, 0x0000, 0 }, /* R115 */
+	{ 0x0007, 0x0007, 0 }, /* R116 - FLL Control 1 */
+	{ 0x3F77, 0x3F77, 0 }, /* R117 - FLL Control 2 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R118 - FLL Control 3 */
+	{ 0x7FEF, 0x7FEF, 0 }, /* R119 - FLL Control 4 */
+	{ 0x001B, 0x001B, 0 }, /* R120 - FLL Control 5 */
+	{ 0x003F, 0x003F, 0 }, /* R121 - GPIO Control 1 */
+	{ 0x003F, 0x003F, 0 }, /* R122 - GPIO Control 2 */
+	{ 0x003F, 0x003F, 0 }, /* R123 - GPIO Control 3 */
+	{ 0x038F, 0x038F, 0 }, /* R124 - GPIO Control 4 */
+	{ 0x0000, 0x0000, 0 }, /* R125 */
+	{ 0x00FF, 0x00FF, 0 }, /* R126 - Digital Pulls */
+	{ 0x07FF, 0x03FF, 1 }, /* R127 - Interrupt Status */
+	{ 0x03FF, 0x03FF, 0 }, /* R128 - Interrupt Status Mask */
+	{ 0x03FF, 0x03FF, 0 }, /* R129 - Interrupt Polarity */
+	{ 0x03FF, 0x03FF, 0 }, /* R130 - Interrupt Debounce */
+	{ 0x0000, 0x0000, 0 }, /* R131 */
+	{ 0x0000, 0x0000, 0 }, /* R132 */
+	{ 0x0000, 0x0000, 0 }, /* R133 */
+	{ 0x0001, 0x0001, 0 }, /* R134 - EQ1 */
+	{ 0x001F, 0x001F, 0 }, /* R135 - EQ2 */
+	{ 0x001F, 0x001F, 0 }, /* R136 - EQ3 */
+	{ 0x001F, 0x001F, 0 }, /* R137 - EQ4 */
+	{ 0x001F, 0x001F, 0 }, /* R138 - EQ5 */
+	{ 0x001F, 0x001F, 0 }, /* R139 - EQ6 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R140 - EQ7 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R141 - EQ8 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R142 - EQ9 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R143 - EQ10 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R144 - EQ11 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R145 - EQ12 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R146 - EQ13 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R147 - EQ14 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R148 - EQ15 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R149 - EQ16 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R150 - EQ17 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R151wm8523_dai - EQ18 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R152 - EQ19 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R153 - EQ20 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R154 - EQ21 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R155 - EQ22 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R156 - EQ23 */
+	{ 0xFFFF, 0xFFFF, 0 }, /* R157 - EQ24 */
+	{ 0x0000, 0x0000, 0 }, /* R158 */
+	{ 0x0000, 0x0000, 0 }, /* R159 */
+	{ 0x0000, 0x0000, 0 }, /* R160 */
+	{ 0x0002, 0x0002, 0 }, /* R161 - Control Interface Test 1 */
+	{ 0x0000, 0x0000, 0 }, /* R162 */
+	{ 0x0000, 0x0000, 0 }, /* R163 */
+	{ 0x0000, 0x0000, 0 }, /* R164 */
+	{ 0x0000, 0x0000, 0 }, /* R165 */
+	{ 0x0000, 0x0000, 0 }, /* R166 */
+	{ 0x0000, 0x0000, 0 }, /* R167 */
+	{ 0x0000, 0x0000, 0 }, /* R168 */
+	{ 0x0000, 0x0000, 0 }, /* R169 */
+	{ 0x0000, 0x0000, 0 }, /* R170 */
+	{ 0x0000, 0x0000, 0 }, /* R171 */
+	{ 0x0000, 0x0000, 0 }, /* R172 */
+	{ 0x0000, 0x0000, 0 }, /* R173 */
+	{ 0x0000, 0x0000, 0 }, /* R174 */
+	{ 0x0000, 0x0000, 0 }, /* R175 */
+	{ 0x0000, 0x0000, 0 }, /* R176 */
+	{ 0x0000, 0x0000, 0 }, /* R177 */
+	{ 0x0000, 0x0000, 0 }, /* R178 */
+	{ 0x0000, 0x0000, 0 }, /* R179 */
+	{ 0x0000, 0x0000, 0 }, /* R180 */
+	{ 0x0000, 0x0000, 0 }, /* R181 */
+	{ 0x0000, 0x0000, 0 }, /* R182 */
+	{ 0x0000, 0x0000, 0 }, /* R183 */
+	{ 0x0000, 0x0000, 0 }, /* R184 */
+	{ 0x0000, 0x0000, 0 }, /* R185 */
+	{ 0x0000, 0x0000, 0 }, /* R186 */
+	{ 0x0000, 0x0000, 0 }, /* R187 */
+	{ 0x0000, 0x0000, 0 }, /* R188 */
+	{ 0x0000, 0x0000, 0 }, /* R189 */
+	{ 0x0000, 0x0000, 0 }, /* R190 */
+	{ 0x0000, 0x0000, 0 }, /* R191 */
+	{ 0x0000, 0x0000, 0 }, /* R192 */
+	{ 0x0000, 0x0000, 0 }, /* R193 */
+	{ 0x0000, 0x0000, 0 }, /* R194 */
+	{ 0x0000, 0x0000, 0 }, /* R195 */
+	{ 0x0000, 0x0000, 0 }, /* R196 */
+	{ 0x0000, 0x0000, 0 }, /* R197 */
+	{ 0x0000, 0x0000, 0 }, /* R198 */
+	{ 0x0000, 0x0000, 0 }, /* R199 */
+	{ 0x0000, 0x0000, 0 }, /* R200 */
+	{ 0x0000, 0x0000, 0 }, /* R201 */
+	{ 0x0000, 0x0000, 0 }, /* R202 */
+	{ 0x0000, 0x0000, 0 }, /* R203 */
+	{ 0x0070, 0x0070, 0 }, /* R204 - Analogue Output Bias 0 */
+	{ 0x0000, 0x0000, 0 }, /* R205 */
+	{ 0x0000, 0x0000, 0 }, /* R206 */
+	{ 0x0000, 0x0000, 0 }, /* R207 */
+	{ 0x0000, 0x0000, 0 }, /* R208 */
+	{ 0x0000, 0x0000, 0 }, /* R209 */
+	{ 0x0000, 0x0000, 0 }, /* R210 */
+	{ 0x0000, 0x0000, 0 }, /* R211 */
+	{ 0x0000, 0x0000, 0 }, /* R212 */
+	{ 0x0000, 0x0000, 0 }, /* R213 */
+	{ 0x0000, 0x0000, 0 }, /* R214 */
+	{ 0x0000, 0x0000, 0 }, /* R215 */
+	{ 0x0000, 0x0000, 0 }, /* R216 */
+	{ 0x0000, 0x0000, 0 }, /* R217 */
+	{ 0x0000, 0x0000, 0 }, /* R218 */
+	{ 0x0000, 0x0000, 0 }, /* R219 */
+	{ 0x0000, 0x0000, 0 }, /* R220 */
+	{ 0x0000, 0x0000, 0 }, /* R221 */
+	{ 0x0000, 0x0000, 0 }, /* R222 */
+	{ 0x0000, 0x0000, 0 }, /* R223 */
+	{ 0x0000, 0x0000, 0 }, /* R224 */
+	{ 0x0000, 0x0000, 0 }, /* R225 */
+	{ 0x0000, 0x0000, 0 }, /* R226 */
+	{ 0x0000, 0x0000, 0 }, /* R227 */
+	{ 0x0000, 0x0000, 0 }, /* R228 */
+	{ 0x0000, 0x0000, 0 }, /* R229 */
+	{ 0x0000, 0x0000, 0 }, /* R230 */
+	{ 0x0000, 0x0000, 0 }, /* R231 */
+	{ 0x0000, 0x0000, 0 }, /* R232 */
+	{ 0x0000, 0x0000, 0 }, /* R233 */
+	{ 0x0000, 0x0000, 0 }, /* R234 */
+	{ 0x0000, 0x0000, 0 }, /* R235 */
+	{ 0x0000, 0x0000, 0 }, /* R236 */
+	{ 0x0000, 0x0000, 0 }, /* R237 */
+	{ 0x0000, 0x0000, 0 }, /* R238 */
+	{ 0x0000, 0x0000, 0 }, /* R239 */
+	{ 0x0000, 0x0000, 0 }, /* R240 */
+	{ 0x0000, 0x0000, 0 }, /* R241 */
+	{ 0x0000, 0x0000, 0 }, /* R242 */
+	{ 0x0000, 0x0000, 0 }, /* R243 */
+	{ 0x0000, 0x0000, 0 }, /* R244 */
+	{ 0x0000, 0x0000, 0 }, /* R245 */
+	{ 0x0000, 0x0000, 0 }, /* R246 */
+	{ 0x0001, 0x0001, 0 }, /* R247 - FLL NCO Test 0 */
+	{ 0x003F, 0x003F, 0 }, /* R248 - FLL NCO Test 1 */
+};
+
+static int wm8904_volatile_register(unsigned int reg)
+{
+	return wm8904_access[reg].vol;
+}
+
+static int wm8904_reset(struct snd_soc_codec *codec)
+{
+	return snd_soc_write(codec, WM8904_SW_RESET_AND_ID, 0);
+}
+
+static int wm8904_configure_clocking(struct snd_soc_codec *codec)
+{
+	struct wm8904_priv *wm8904 = codec->private_data;
+	unsigned int clock0, clock2, rate;
+
+	/* Gate the clock while we're updating to avoid misclocking */
+	clock2 = snd_soc_read(codec, WM8904_CLOCK_RATES_2);
+	snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+			    WM8904_SYSCLK_SRC, 0);
+
+	/* This should be done on init() for bypass paths */
+	switch (wm8904->sysclk_src) {
+	case WM8904_CLK_MCLK:
+		dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8904->mclk_rate);
+
+		clock2 &= ~WM8904_SYSCLK_SRC;
+		rate = wm8904->mclk_rate;
+
+		/* Ensure the FLL is stopped */
+		snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+				    WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+		break;
+
+	case WM8904_CLK_FLL:
+		dev_dbg(codec->dev, "Using %dHz FLL clock\n",
+			wm8904->fll_fout);
+
+		clock2 |= WM8904_SYSCLK_SRC;
+		rate = wm8904->fll_fout;
+		break;
+
+	default:
+		dev_err(codec->dev, "System clock not configured\n");
+		return -EINVAL;
+	}
+
+	/* SYSCLK shouldn't be over 13.5MHz */
+	if (rate > 13500000) {
+		clock0 = WM8904_MCLK_DIV;
+		wm8904->sysclk_rate = rate / 2;
+	} else {
+		clock0 = 0;
+		wm8904->sysclk_rate = rate;
+	}
+
+	snd_soc_update_bits(codec, WM8904_CLOCK_RATES_0, WM8904_MCLK_DIV,
+			    clock0);
+
+	snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+			    WM8904_CLK_SYS_ENA | WM8904_SYSCLK_SRC, clock2);
+
+	dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm8904->sysclk_rate);
+
+	return 0;
+}
+
+static void wm8904_set_drc(struct snd_soc_codec *codec)
+{
+	struct wm8904_priv *wm8904 = codec->private_data;
+	struct wm8904_pdata *pdata = wm8904->pdata;
+	int save, i;
+
+	/* Save any enables; the configuration should clear them. */
+	save = snd_soc_read(codec, WM8904_DRC_0);
+
+	for (i = 0; i < WM8904_DRC_REGS; i++)
+		snd_soc_update_bits(codec, WM8904_DRC_0 + i, 0xffff,
+				    pdata->drc_cfgs[wm8904->drc_cfg].regs[i]);
+
+	/* Reenable the DRC */
+	snd_soc_update_bits(codec, WM8904_DRC_0,
+			    WM8904_DRC_ENA | WM8904_DRC_DAC_PATH, save);
+}
+
+static int wm8904_put_drc_enum(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8904_priv *wm8904 = codec->private_data;	
+	struct wm8904_pdata *pdata = wm8904->pdata;
+	int value = ucontrol->value.integer.value[0];
+
+	if (value >= pdata->num_drc_cfgs)
+		return -EINVAL;
+
+	wm8904->drc_cfg = value;
+
+	wm8904_set_drc(codec);
+
+	return 0;
+}
+
+static int wm8904_get_drc_enum(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8904_priv *wm8904 = codec->private_data;
+
+	ucontrol->value.enumerated.item[0] = wm8904->drc_cfg;
+
+	return 0;
+}
+
+static void wm8904_set_retune_mobile(struct snd_soc_codec *codec)
+{
+	struct wm8904_priv *wm8904 = codec->private_data;
+	struct wm8904_pdata *pdata = wm8904->pdata;
+	int best, best_val, save, i, cfg;
+
+	if (!pdata || !wm8904->num_retune_mobile_texts)
+		return;
+
+	/* Find the version of the currently selected configuration
+	 * with the nearest sample rate. */
+	cfg = wm8904->retune_mobile_cfg;
+	best = 0;
+	best_val = INT_MAX;
+	for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+		if (strcmp(pdata->retune_mobile_cfgs[i].name,
+			   wm8904->retune_mobile_texts[cfg]) == 0 &&
+		    abs(pdata->retune_mobile_cfgs[i].rate
+			- wm8904->fs) < best_val) {
+			best = i;
+			best_val = abs(pdata->retune_mobile_cfgs[i].rate
+				       - wm8904->fs);
+		}
+	}
+
+	dev_dbg(codec->dev, "ReTune Mobile %s/%dHz for %dHz sample rate\n",
+		pdata->retune_mobile_cfgs[best].name,
+		pdata->retune_mobile_cfgs[best].rate,
+		wm8904->fs);
+
+	/* The EQ will be disabled while reconfiguring it, remember the
+	 * current configuration. 
+	 */
+	save = snd_soc_read(codec, WM8904_EQ1);
+
+	for (i = 0; i < WM8904_EQ_REGS; i++)
+		snd_soc_update_bits(codec, WM8904_EQ1 + i, 0xffff,
+				pdata->retune_mobile_cfgs[best].regs[i]);
+
+	snd_soc_update_bits(codec, WM8904_EQ1, WM8904_EQ_ENA, save);
+}
+
+static int wm8904_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8904_priv *wm8904 = codec->private_data;	
+	struct wm8904_pdata *pdata = wm8904->pdata;
+	int value = ucontrol->value.integer.value[0];
+
+	if (value >= pdata->num_retune_mobile_cfgs)
+		return -EINVAL;
+
+	wm8904->retune_mobile_cfg = value;
+
+	wm8904_set_retune_mobile(codec);
+
+	return 0;
+}
+
+static int wm8904_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8904_priv *wm8904 = codec->private_data;
+
+	ucontrol->value.enumerated.item[0] = wm8904->retune_mobile_cfg;
+
+	return 0;
+}
+
+static int deemph_settings[] = { 0, 32000, 44100, 48000 };
+
+static int wm8904_set_deemph(struct snd_soc_codec *codec)
+{
+	struct wm8904_priv *wm8904 = codec->private_data;
+	int val, i, best;
+
+	/* If we're using deemphasis select the nearest available sample 
+	 * rate.
+	 */
+	if (wm8904->deemph) {
+		best = 1;
+		for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
+			if (abs(deemph_settings[i] - wm8904->fs) <
+			    abs(deemph_settings[best] - wm8904->fs))
+				best = i;
+		}
+
+		val = best << WM8904_DEEMPH_SHIFT;
+	} else {
+		val = 0;
+	}
+
+	dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+
+	return snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_1,
+				   WM8904_DEEMPH_MASK, val);
+}
+
+static int wm8904_get_deemph(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8904_priv *wm8904 = codec->private_data;
+
+	return wm8904->deemph;
+}
+
+static int wm8904_put_deemph(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8904_priv *wm8904 = codec->private_data;
+	int deemph = ucontrol->value.enumerated.item[0];
+
+	if (deemph > 1)
+		return -EINVAL;
+
+	wm8904->deemph = deemph;
+
+	return wm8904_set_deemph(codec);
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static const char *input_mode_text[] = {
+	"Single-Ended", "Differential Line", "Differential Mic"
+};
+
+static const struct soc_enum lin_mode =
+	SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 0, 3, input_mode_text);
+
+static const struct soc_enum rin_mode =
+	SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 0, 3, input_mode_text);
+
+static const char *hpf_mode_text[] = {
+	"Hi-fi", "Voice 1", "Voice 2", "Voice 3"
+};
+
+static const struct soc_enum hpf_mode =
+	SOC_ENUM_SINGLE(WM8904_ADC_DIGITAL_0, 5, 4, hpf_mode_text);
+
+static const struct snd_kcontrol_new wm8904_adc_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Digital Capture Volume", WM8904_ADC_DIGITAL_VOLUME_LEFT,
+		 WM8904_ADC_DIGITAL_VOLUME_RIGHT, 1, 119, 0, digital_tlv),
+
+SOC_ENUM("Left Caputure Mode", lin_mode),
+SOC_ENUM("Right Capture Mode", rin_mode),
+
+/* No TLV since it depends on mode */
+SOC_DOUBLE_R("Capture Volume", WM8904_ANALOGUE_LEFT_INPUT_0,
+	     WM8904_ANALOGUE_RIGHT_INPUT_0, 0, 31, 0),
+SOC_DOUBLE_R("Capture Switch", WM8904_ANALOGUE_LEFT_INPUT_0,
+	     WM8904_ANALOGUE_RIGHT_INPUT_0, 7, 1, 0),
+
+SOC_SINGLE("High Pass Filter Switch", WM8904_ADC_DIGITAL_0, 4, 1, 0),
+SOC_ENUM("High Pass Filter Mode", hpf_mode),
+
+SOC_SINGLE("ADC 128x OSR Switch", WM8904_ANALOGUE_ADC_0, 0, 1, 0),
+};
+
+static const char *drc_path_text[] = {
+	"ADC", "DAC"
+};
+
+static const struct soc_enum drc_path =
+	SOC_ENUM_SINGLE(WM8904_DRC_0, 14, 2, drc_path_text);
+
+static const struct snd_kcontrol_new wm8904_dac_snd_controls[] = {
+SOC_SINGLE_TLV("Digital Playback Boost Volume", 
+	       WM8904_AUDIO_INTERFACE_0, 9, 3, 0, dac_boost_tlv),
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8904_DAC_DIGITAL_VOLUME_LEFT,
+		 WM8904_DAC_DIGITAL_VOLUME_RIGHT, 1, 96, 0, digital_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Volume", WM8904_ANALOGUE_OUT1_LEFT,
+		 WM8904_ANALOGUE_OUT1_RIGHT, 0, 63, 0, out_tlv),
+SOC_DOUBLE_R("Headphone Switch", WM8904_ANALOGUE_OUT1_LEFT,
+	     WM8904_ANALOGUE_OUT1_RIGHT, 8, 1, 1),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8904_ANALOGUE_OUT1_LEFT,
+	     WM8904_ANALOGUE_OUT1_RIGHT, 6, 1, 0),
+
+SOC_DOUBLE_R_TLV("Line Output Volume", WM8904_ANALOGUE_OUT2_LEFT,
+		 WM8904_ANALOGUE_OUT2_RIGHT, 0, 63, 0, out_tlv),
+SOC_DOUBLE_R("Line Output Switch", WM8904_ANALOGUE_OUT2_LEFT,
+	     WM8904_ANALOGUE_OUT2_RIGHT, 8, 1, 1),
+SOC_DOUBLE_R("Line Output ZC Switch", WM8904_ANALOGUE_OUT2_LEFT,
+	     WM8904_ANALOGUE_OUT2_RIGHT, 6, 1, 0),
+
+SOC_SINGLE("EQ Switch", WM8904_EQ1, 0, 1, 0),
+SOC_SINGLE("DRC Switch", WM8904_DRC_0, 15, 1, 0),
+SOC_ENUM("DRC Path", drc_path),
+SOC_SINGLE("DAC OSRx2 Switch", WM8904_DAC_DIGITAL_1, 6, 1, 0),
+SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+		    wm8904_get_deemph, wm8904_put_deemph),
+};
+
+static const struct snd_kcontrol_new wm8904_snd_controls[] = {
+SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8904_DAC_DIGITAL_0, 4, 8, 15, 0,
+	       sidetone_tlv),
+};
+
+static const struct snd_kcontrol_new wm8904_eq_controls[] = {
+SOC_SINGLE_TLV("EQ1 Volume", WM8904_EQ2, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Volume", WM8904_EQ3, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Volume", WM8904_EQ4, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Volume", WM8904_EQ5, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ5 Volume", WM8904_EQ6, 0, 24, 0, eq_tlv),
+};
+
+static int cp_event(struct snd_soc_dapm_widget *w,
+		    struct snd_kcontrol *kcontrol, int event)
+{
+	BUG_ON(event != SND_SOC_DAPM_POST_PMU);
+
+	/* Maximum startup time */
+	udelay(500);
+
+	return 0;
+}
+
+static int sysclk_event(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct wm8904_priv *wm8904 = codec->private_data;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* If we're using the FLL then we only start it when
+		 * required; we assume that the configuration has been
+		 * done previously and all we need to do is kick it
+		 * off.
+		 */
+		switch (wm8904->sysclk_src) {
+		case WM8904_CLK_FLL:
+			snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+					    WM8904_FLL_OSC_ENA,
+					    WM8904_FLL_OSC_ENA);
+
+			snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+					    WM8904_FLL_ENA,
+					    WM8904_FLL_ENA);
+			break;
+
+		default:
+			break;
+		}
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+				    WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+		break;
+	}
+
+	return 0;
+}
+
+static int out_pga_event(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct wm8904_priv *wm8904 = codec->private_data;
+	int reg, val;
+	int dcs_mask;
+	int dcs_l, dcs_r;
+	int dcs_l_reg, dcs_r_reg;
+	int timeout;
+	int pwr_reg;
+
+	/* This code is shared between HP and LINEOUT; we do all our
+	 * power management in stereo pairs to avoid latency issues so
+	 * we reuse shift to identify which rather than strcmp() the
+	 * name. */
+	reg = w->shift;
+
+	switch (reg) {
+	case WM8904_ANALOGUE_HP_0:
+		pwr_reg = WM8904_POWER_MANAGEMENT_2;
+		dcs_mask = WM8904_DCS_ENA_CHAN_0 | WM8904_DCS_ENA_CHAN_1;
+		dcs_r_reg = WM8904_DC_SERVO_8;
+		dcs_l_reg = WM8904_DC_SERVO_9;
+		dcs_l = 0;
+		dcs_r = 1;
+		break;
+	case WM8904_ANALOGUE_LINEOUT_0:
+		pwr_reg = WM8904_POWER_MANAGEMENT_3;
+		dcs_mask = WM8904_DCS_ENA_CHAN_2 | WM8904_DCS_ENA_CHAN_3;
+		dcs_r_reg = WM8904_DC_SERVO_6;
+		dcs_l_reg = WM8904_DC_SERVO_7;
+		dcs_l = 2;
+		dcs_r = 3;
+		break;
+	default:
+		BUG();
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Power on the PGAs */
+		snd_soc_update_bits(codec, pwr_reg,
+				    WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA,
+				    WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA);
+
+		/* Power on the amplifier */
+		snd_soc_update_bits(codec, reg,
+				    WM8904_HPL_ENA | WM8904_HPR_ENA,
+				    WM8904_HPL_ENA | WM8904_HPR_ENA);
+
+
+		/* Enable the first stage */
+		snd_soc_update_bits(codec, reg,
+				    WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY,
+				    WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY);
+
+		/* Power up the DC servo */
+		snd_soc_update_bits(codec, WM8904_DC_SERVO_0,
+				    dcs_mask, dcs_mask);
+
+		/* Either calibrate the DC servo or restore cached state
+		 * if we have that.
+		 */
+		if (wm8904->dcs_state[dcs_l] || wm8904->dcs_state[dcs_r]) {
+			dev_dbg(codec->dev, "Restoring DC servo state\n");
+
+			snd_soc_write(codec, dcs_l_reg,
+				      wm8904->dcs_state[dcs_l]);
+			snd_soc_write(codec, dcs_r_reg,
+				      wm8904->dcs_state[dcs_r]);
+
+			snd_soc_write(codec, WM8904_DC_SERVO_1, dcs_mask);
+
+			timeout = 20;
+		} else {
+			dev_dbg(codec->dev, "Calibrating DC servo\n");
+
+			snd_soc_write(codec, WM8904_DC_SERVO_1,
+				dcs_mask << WM8904_DCS_TRIG_STARTUP_0_SHIFT);
+
+			timeout = 500;
+		}
+
+		/* Wait for DC servo to complete */
+		dcs_mask <<= WM8904_DCS_CAL_COMPLETE_SHIFT;
+		do {
+			val = snd_soc_read(codec, WM8904_DC_SERVO_READBACK_0);
+			if ((val & dcs_mask) == dcs_mask)
+				break;
+
+			msleep(1);
+		} while (--timeout);
+
+		if ((val & dcs_mask) != dcs_mask)
+			dev_warn(codec->dev, "DC servo timed out\n");
+		else
+			dev_dbg(codec->dev, "DC servo ready\n");
+
+		/* Enable the output stage */
+		snd_soc_update_bits(codec, reg,
+				    WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP,
+				    WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP);
+		break;
+
+	case SND_SOC_DAPM_POST_PMU:
+		/* Unshort the output itself */
+		snd_soc_update_bits(codec, reg,
+				    WM8904_HPL_RMV_SHORT |
+				    WM8904_HPR_RMV_SHORT,
+				    WM8904_HPL_RMV_SHORT |
+				    WM8904_HPR_RMV_SHORT);
+
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		/* Short the output */
+		snd_soc_update_bits(codec, reg,
+				    WM8904_HPL_RMV_SHORT |
+				    WM8904_HPR_RMV_SHORT, 0);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		/* Cache the DC servo configuration; this will be
+		 * invalidated if we change the configuration. */
+		wm8904->dcs_state[dcs_l] = snd_soc_read(codec, dcs_l_reg);
+		wm8904->dcs_state[dcs_r] = snd_soc_read(codec, dcs_r_reg);
+
+		snd_soc_update_bits(codec, WM8904_DC_SERVO_0,
+				    dcs_mask, 0);
+
+		/* Disable the amplifier input and output stages */
+		snd_soc_update_bits(codec, reg,
+				    WM8904_HPL_ENA | WM8904_HPR_ENA |
+				    WM8904_HPL_ENA_DLY | WM8904_HPR_ENA_DLY |
+				    WM8904_HPL_ENA_OUTP | WM8904_HPR_ENA_OUTP,
+				    0);
+
+		/* PGAs too */
+		snd_soc_update_bits(codec, pwr_reg,
+				    WM8904_HPL_PGA_ENA | WM8904_HPR_PGA_ENA,
+				    0);
+		break;
+	}
+
+	return 0;
+}
+
+static const char *lin_text[] = {
+	"IN1L", "IN2L", "IN3L"
+};
+
+static const struct soc_enum lin_enum =
+	SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 2, 3, lin_text);
+
+static const struct snd_kcontrol_new lin_mux =
+	SOC_DAPM_ENUM("Left Capture Mux", lin_enum);
+
+static const struct soc_enum lin_inv_enum =
+	SOC_ENUM_SINGLE(WM8904_ANALOGUE_LEFT_INPUT_1, 4, 3, lin_text);
+
+static const struct snd_kcontrol_new lin_inv_mux =
+	SOC_DAPM_ENUM("Left Capture Inveting Mux", lin_inv_enum);
+
+static const char *rin_text[] = {
+	"IN1R", "IN2R", "IN3R"
+};
+
+static const struct soc_enum rin_enum =
+	SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 2, 3, rin_text);
+
+static const struct snd_kcontrol_new rin_mux =
+	SOC_DAPM_ENUM("Right Capture Mux", rin_enum);
+
+static const struct soc_enum rin_inv_enum =
+	SOC_ENUM_SINGLE(WM8904_ANALOGUE_RIGHT_INPUT_1, 4, 3, rin_text);
+
+static const struct snd_kcontrol_new rin_inv_mux =
+	SOC_DAPM_ENUM("Right Capture Inveting Mux", rin_inv_enum);
+
+static const char *aif_text[] = {
+	"Left", "Right"
+};
+
+static const struct soc_enum aifoutl_enum =
+	SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 7, 2, aif_text);
+
+static const struct snd_kcontrol_new aifoutl_mux =
+	SOC_DAPM_ENUM("AIFOUTL Mux", aifoutl_enum);
+
+static const struct soc_enum aifoutr_enum =
+	SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 6, 2, aif_text);
+
+static const struct snd_kcontrol_new aifoutr_mux =
+	SOC_DAPM_ENUM("AIFOUTR Mux", aifoutr_enum);
+
+static const struct soc_enum aifinl_enum =
+	SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 5, 2, aif_text);
+
+static const struct snd_kcontrol_new aifinl_mux =
+	SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum);
+
+static const struct soc_enum aifinr_enum =
+	SOC_ENUM_SINGLE(WM8904_AUDIO_INTERFACE_0, 4, 2, aif_text);
+
+static const struct snd_kcontrol_new aifinr_mux =
+	SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum);
+
+static const struct snd_soc_dapm_widget wm8904_core_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", WM8904_CLOCK_RATES_2, 2, 0, sysclk_event,
+		    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8904_CLOCK_RATES_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("TOCLK", WM8904_CLOCK_RATES_2, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8904_adc_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8904_MIC_BIAS_CONTROL_0, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lin_mux),
+SND_SOC_DAPM_MUX("Left Capture Inverting Mux", SND_SOC_NOPM, 0, 0,
+		 &lin_inv_mux),
+SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rin_mux),
+SND_SOC_DAPM_MUX("Right Capture Inverting Mux", SND_SOC_NOPM, 0, 0,
+		 &rin_inv_mux),
+
+SND_SOC_DAPM_PGA("Left Capture PGA", WM8904_POWER_MANAGEMENT_0, 1, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("Right Capture PGA", WM8904_POWER_MANAGEMENT_0, 0, 0,
+		 NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8904_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8904_POWER_MANAGEMENT_6, 0, 0),
+
+SND_SOC_DAPM_MUX("AIFOUTL Mux", SND_SOC_NOPM, 0, 0, &aifoutl_mux),
+SND_SOC_DAPM_MUX("AIFOUTR Mux", SND_SOC_NOPM, 0, 0, &aifoutr_mux),
+
+SND_SOC_DAPM_AIF_OUT("AIFOUTL", "Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFOUTR", "Capture", 1, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8904_dac_dapm_widgets[] = {
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &aifinl_mux),
+SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &aifinr_mux),
+
+SND_SOC_DAPM_DAC("DACL", NULL, WM8904_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, WM8904_POWER_MANAGEMENT_6, 2, 0),
+
+SND_SOC_DAPM_SUPPLY("Charge pump", WM8904_CHARGE_PUMP_0, 0, 0, cp_event,
+		    SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("HPL PGA", SND_SOC_NOPM, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("HPR PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("LINEL PGA", SND_SOC_NOPM, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LINER PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM, WM8904_ANALOGUE_HP_0,
+		   0, NULL, 0, out_pga_event,
+		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_PGA_E("Line Output", SND_SOC_NOPM, WM8904_ANALOGUE_LINEOUT_0,
+		   0, NULL, 0, out_pga_event,
+		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+};
+
+static const char *out_mux_text[] = {
+	"DAC", "Bypass"
+};
+
+static const struct soc_enum hpl_enum =
+	SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 3, 2, out_mux_text);
+
+static const struct snd_kcontrol_new hpl_mux =
+	SOC_DAPM_ENUM("HPL Mux", hpl_enum);
+
+static const struct soc_enum hpr_enum =
+	SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 2, 2, out_mux_text);
+
+static const struct snd_kcontrol_new hpr_mux =
+	SOC_DAPM_ENUM("HPR Mux", hpr_enum);
+
+static const struct soc_enum linel_enum =
+	SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 1, 2, out_mux_text);
+
+static const struct snd_kcontrol_new linel_mux =
+	SOC_DAPM_ENUM("LINEL Mux", linel_enum);
+
+static const struct soc_enum liner_enum =
+	SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 0, 2, out_mux_text);
+
+static const struct snd_kcontrol_new liner_mux =
+	SOC_DAPM_ENUM("LINEL Mux", liner_enum);
+
+static const char *sidetone_text[] = {
+	"None", "Left", "Right"
+};
+
+static const struct soc_enum dacl_sidetone_enum =
+	SOC_ENUM_SINGLE(WM8904_DAC_DIGITAL_0, 2, 3, sidetone_text);
+
+static const struct snd_kcontrol_new dacl_sidetone_mux =
+	SOC_DAPM_ENUM("Left Sidetone Mux", dacl_sidetone_enum);
+
+static const struct soc_enum dacr_sidetone_enum =
+	SOC_ENUM_SINGLE(WM8904_DAC_DIGITAL_0, 0, 3, sidetone_text);
+
+static const struct snd_kcontrol_new dacr_sidetone_mux =
+	SOC_DAPM_ENUM("Right Sidetone Mux", dacr_sidetone_enum);
+
+static const struct snd_soc_dapm_widget wm8904_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("Class G", WM8904_CLASS_W_0, 0, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Left Bypass", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Bypass", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &dacl_sidetone_mux),
+SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &dacr_sidetone_mux),
+
+SND_SOC_DAPM_MUX("HPL Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
+SND_SOC_DAPM_MUX("HPR Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+SND_SOC_DAPM_MUX("LINEL Mux", SND_SOC_NOPM, 0, 0, &linel_mux),
+SND_SOC_DAPM_MUX("LINER Mux", SND_SOC_NOPM, 0, 0, &liner_mux),
+};
+
+static const struct snd_soc_dapm_route core_intercon[] = {
+	{ "CLK_DSP", NULL, "SYSCLK" },
+	{ "TOCLK", NULL, "SYSCLK" },
+};
+
+static const struct snd_soc_dapm_route adc_intercon[] = {
+	{ "Left Capture Mux", "IN1L", "IN1L" },
+	{ "Left Capture Mux", "IN2L", "IN2L" },
+	{ "Left Capture Mux", "IN3L", "IN3L" },
+
+	{ "Left Capture Inverting Mux", "IN1L", "IN1L" },
+	{ "Left Capture Inverting Mux", "IN2L", "IN2L" },
+	{ "Left Capture Inverting Mux", "IN3L", "IN3L" },
+
+	{ "Right Capture Mux", "IN1R", "IN1R" },
+	{ "Right Capture Mux", "IN2R", "IN2R" },
+	{ "Right Capture Mux", "IN3R", "IN3R" },
+
+	{ "Right Capture Inverting Mux", "IN1R", "IN1R" },
+	{ "Right Capture Inverting Mux", "IN2R", "IN2R" },
+	{ "Right Capture Inverting Mux", "IN3R", "IN3R" },
+
+	{ "Left Capture PGA", NULL, "Left Capture Mux" },
+	{ "Left Capture PGA", NULL, "Left Capture Inverting Mux" },
+
+	{ "Right Capture PGA", NULL, "Right Capture Mux" },
+	{ "Right Capture PGA", NULL, "Right Capture Inverting Mux" },
+
+	{ "AIFOUTL", "Left",  "ADCL" },
+	{ "AIFOUTL", "Right", "ADCR" },
+	{ "AIFOUTR", "Left",  "ADCL" },
+	{ "AIFOUTR", "Right", "ADCR" },
+
+	{ "ADCL", NULL, "CLK_DSP" },
+	{ "ADCL", NULL, "Left Capture PGA" },
+
+	{ "ADCR", NULL, "CLK_DSP" },
+	{ "ADCR", NULL, "Right Capture PGA" },
+};
+
+static const struct snd_soc_dapm_route dac_intercon[] = {
+	{ "DACL", "Right", "AIFINR" },
+	{ "DACL", "Left",  "AIFINL" },
+	{ "DACL", NULL, "CLK_DSP" },
+
+	{ "DACR", "Right", "AIFINR" },
+	{ "DACR", "Left",  "AIFINL" },
+	{ "DACR", NULL, "CLK_DSP" },
+
+	{ "Charge pump", NULL, "SYSCLK" },
+
+	{ "Headphone Output", NULL, "HPL PGA" },
+	{ "Headphone Output", NULL, "HPR PGA" },
+	{ "Headphone Output", NULL, "Charge pump" },
+	{ "Headphone Output", NULL, "TOCLK" },
+
+	{ "Line Output", NULL, "LINEL PGA" },
+	{ "Line Output", NULL, "LINER PGA" },
+	{ "Line Output", NULL, "Charge pump" },
+	{ "Line Output", NULL, "TOCLK" },
+
+	{ "HPOUTL", NULL, "Headphone Output" },
+	{ "HPOUTR", NULL, "Headphone Output" },
+
+	{ "LINEOUTL", NULL, "Line Output" },
+	{ "LINEOUTR", NULL, "Line Output" },
+};
+
+static const struct snd_soc_dapm_route wm8904_intercon[] = {
+	{ "Left Sidetone", "Left", "ADCL" },
+	{ "Left Sidetone", "Right", "ADCR" },
+	{ "DACL", NULL, "Left Sidetone" },
+	
+	{ "Right Sidetone", "Left", "ADCL" },
+	{ "Right Sidetone", "Right", "ADCR" },
+	{ "DACR", NULL, "Right Sidetone" },
+
+	{ "Left Bypass", NULL, "Class G" },
+	{ "Left Bypass", NULL, "Left Capture PGA" },
+
+	{ "Right Bypass", NULL, "Class G" },
+	{ "Right Bypass", NULL, "Right Capture PGA" },
+
+	{ "HPL Mux", "DAC", "DACL" },
+	{ "HPL Mux", "Bypass", "Left Bypass" },
+
+	{ "HPR Mux", "DAC", "DACR" },
+	{ "HPR Mux", "Bypass", "Right Bypass" },
+
+	{ "LINEL Mux", "DAC", "DACL" },
+	{ "LINEL Mux", "Bypass", "Left Bypass" },
+
+	{ "LINER Mux", "DAC", "DACR" },
+	{ "LINER Mux", "Bypass", "Right Bypass" },
+
+	{ "HPL PGA", NULL, "HPL Mux" },
+	{ "HPR PGA", NULL, "HPR Mux" },
+
+	{ "LINEL PGA", NULL, "LINEL Mux" },
+	{ "LINER PGA", NULL, "LINER Mux" },
+};
+
+static const struct snd_soc_dapm_route wm8912_intercon[] = {
+	{ "HPL PGA", NULL, "DACL" },
+	{ "HPR PGA", NULL, "DACR" },
+
+	{ "LINEL PGA", NULL, "DACL" },
+	{ "LINER PGA", NULL, "DACR" },
+};
+
+static int wm8904_add_widgets(struct snd_soc_codec *codec)
+{
+	struct wm8904_priv *wm8904 = codec->private_data;
+
+	snd_soc_dapm_new_controls(codec, wm8904_core_dapm_widgets,
+				  ARRAY_SIZE(wm8904_core_dapm_widgets));
+	snd_soc_dapm_add_routes(codec, core_intercon,
+				ARRAY_SIZE(core_intercon));
+
+	switch (wm8904->devtype) {
+	case WM8904:
+		snd_soc_add_controls(codec, wm8904_adc_snd_controls,
+				     ARRAY_SIZE(wm8904_adc_snd_controls));
+		snd_soc_add_controls(codec, wm8904_dac_snd_controls,
+				     ARRAY_SIZE(wm8904_dac_snd_controls));
+		snd_soc_add_controls(codec, wm8904_snd_controls,
+				     ARRAY_SIZE(wm8904_snd_controls));
+
+		snd_soc_dapm_new_controls(codec, wm8904_adc_dapm_widgets,
+					  ARRAY_SIZE(wm8904_adc_dapm_widgets));
+		snd_soc_dapm_new_controls(codec, wm8904_dac_dapm_widgets,
+					  ARRAY_SIZE(wm8904_dac_dapm_widgets));
+		snd_soc_dapm_new_controls(codec, wm8904_dapm_widgets,
+					  ARRAY_SIZE(wm8904_dapm_widgets));
+
+		snd_soc_dapm_add_routes(codec, core_intercon,
+					ARRAY_SIZE(core_intercon));
+		snd_soc_dapm_add_routes(codec, adc_intercon,
+					ARRAY_SIZE(adc_intercon));
+		snd_soc_dapm_add_routes(codec, dac_intercon,
+					ARRAY_SIZE(dac_intercon));
+		snd_soc_dapm_add_routes(codec, wm8904_intercon,
+					ARRAY_SIZE(wm8904_intercon));
+		break;
+
+	case WM8912:
+		snd_soc_add_controls(codec, wm8904_dac_snd_controls,
+				     ARRAY_SIZE(wm8904_dac_snd_controls));
+
+		snd_soc_dapm_new_controls(codec, wm8904_dac_dapm_widgets,
+					  ARRAY_SIZE(wm8904_dac_dapm_widgets));
+
+		snd_soc_dapm_add_routes(codec, dac_intercon,
+					ARRAY_SIZE(dac_intercon));
+		snd_soc_dapm_add_routes(codec, wm8912_intercon,
+					ARRAY_SIZE(wm8912_intercon));
+		break;
+	}
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+static struct {
+	int ratio;
+	unsigned int clk_sys_rate;
+} clk_sys_rates[] = {
+	{   64,  0 },
+	{  128,  1 },
+	{  192,  2 },
+	{  256,  3 },
+	{  384,  4 },
+	{  512,  5 },
+	{  786,  6 },
+	{ 1024,  7 },
+	{ 1408,  8 },
+	{ 1536,  9 },
+};
+
+static struct {
+	int rate;
+	int sample_rate;
+} sample_rates[] = {
+	{ 8000,  0  },
+	{ 11025, 1  },
+	{ 12000, 1  },
+	{ 16000, 2  },
+	{ 22050, 3  },
+	{ 24000, 3  },
+	{ 32000, 4  },
+	{ 44100, 5  },
+	{ 48000, 5  },
+};
+
+static struct {
+	int div; /* *10 due to .5s */
+	int bclk_div;
+} bclk_divs[] = {
+	{ 10,  0  },
+	{ 15,  1  },
+	{ 20,  2  },
+	{ 30,  3  },
+	{ 40,  4  },
+	{ 50,  5  },
+	{ 55,  6  },
+	{ 60,  7  },
+	{ 80,  8  },
+	{ 100, 9  },
+	{ 110, 10 },
+	{ 120, 11 },
+	{ 160, 12 },
+	{ 200, 13 },
+	{ 220, 14 },
+	{ 240, 16 },
+	{ 200, 17 },
+	{ 320, 18 },
+	{ 440, 19 },
+	{ 480, 20 },
+};
+
+
+static int wm8904_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 wm8904_priv *wm8904 = codec->private_data;
+	int ret, i, best, best_val, cur_val;
+	unsigned int aif1 = 0;
+	unsigned int aif2 = 0;
+	unsigned int aif3 = 0;
+	unsigned int clock1 = 0;
+	unsigned int dac_digital1 = 0;
+
+	/* What BCLK do we need? */
+	wm8904->fs = params_rate(params);
+	if (wm8904->tdm_slots) {
+		dev_dbg(codec->dev, "Configuring for %d %d bit TDM slots\n",
+			wm8904->tdm_slots, wm8904->tdm_width);
+		wm8904->bclk = snd_soc_calc_bclk(wm8904->fs,
+						 wm8904->tdm_width, 2,
+						 wm8904->tdm_slots);
+	} else {
+		wm8904->bclk = snd_soc_params_to_bclk(params);
+	}
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		aif1 |= 0x40;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		aif1 |= 0x80;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		aif1 |= 0xc0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+
+	dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8904->bclk);
+
+	ret = wm8904_configure_clocking(codec);
+	if (ret != 0)
+		return ret;
+
+	/* Select nearest CLK_SYS_RATE */
+	best = 0;
+	best_val = abs((wm8904->sysclk_rate / clk_sys_rates[0].ratio)
+		       - wm8904->fs);
+	for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) {
+		cur_val = abs((wm8904->sysclk_rate /
+			       clk_sys_rates[i].ratio) - wm8904->fs);;
+		if (cur_val < best_val) {
+			best = i;
+			best_val = cur_val;
+		}
+	}
+	dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+		clk_sys_rates[best].ratio);
+	clock1 |= (clk_sys_rates[best].clk_sys_rate
+		   << WM8904_CLK_SYS_RATE_SHIFT);
+
+	/* SAMPLE_RATE */
+	best = 0;
+	best_val = abs(wm8904->fs - sample_rates[0].rate);
+	for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+		/* Closest match */
+		cur_val = abs(wm8904->fs - sample_rates[i].rate);
+		if (cur_val < best_val) {
+			best = i;
+			best_val = cur_val;
+		}
+	}
+	dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+		sample_rates[best].rate);
+	clock1 |= (sample_rates[best].sample_rate
+		   << WM8904_SAMPLE_RATE_SHIFT);
+
+	/* Enable sloping stopband filter for low sample rates */
+	if (wm8904->fs <= 24000)
+		dac_digital1 |= WM8904_DAC_SB_FILT;
+
+	/* BCLK_DIV */
+	best = 0;
+	best_val = INT_MAX;
+	for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+		cur_val = ((wm8904->sysclk_rate * 10) / bclk_divs[i].div)
+			- wm8904->bclk;
+		if (cur_val < 0) /* Table is sorted */
+			break;
+		if (cur_val < best_val) {
+			best = i;
+			best_val = cur_val;
+		}
+	}
+	wm8904->bclk = (wm8904->sysclk_rate * 10) / bclk_divs[best].div;
+	dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+		bclk_divs[best].div, wm8904->bclk);
+	aif2 |= bclk_divs[best].bclk_div;
+
+	/* LRCLK is a simple fraction of BCLK */
+	dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8904->bclk / wm8904->fs);
+	aif3 |= wm8904->bclk / wm8904->fs;
+
+	/* Apply the settings */
+	snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_1,
+			    WM8904_DAC_SB_FILT, dac_digital1);
+	snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+			    WM8904_AIF_WL_MASK, aif1);
+	snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_2,
+			    WM8904_BCLK_DIV_MASK, aif2);
+	snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_3,
+			    WM8904_LRCLK_RATE_MASK, aif3);
+	snd_soc_update_bits(codec, WM8904_CLOCK_RATES_1,
+			    WM8904_SAMPLE_RATE_MASK |
+			    WM8904_CLK_SYS_RATE_MASK, clock1);
+
+	/* Update filters for the new settings */
+	wm8904_set_retune_mobile(codec);
+	wm8904_set_deemph(codec);
+
+	return 0;
+}
+
+
+static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+			     unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8904_priv *priv = codec->private_data;
+
+	switch (clk_id) {
+	case WM8904_CLK_MCLK:
+		priv->sysclk_src = clk_id;
+		priv->mclk_rate = freq;
+		break;
+
+	case WM8904_CLK_FLL:
+		priv->sysclk_src = clk_id;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+	wm8904_configure_clocking(codec);
+
+	return 0;
+}
+
+static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	unsigned int aif1 = 0;
+	unsigned int aif3 = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		aif3 |= WM8904_LRCLK_DIR;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		aif1 |= WM8904_BCLK_DIR;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		aif1 |= WM8904_BCLK_DIR;
+		aif3 |= WM8904_LRCLK_DIR;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_B:
+		aif1 |= WM8904_AIF_LRCLK_INV;
+	case SND_SOC_DAIFMT_DSP_A:
+		aif1 |= 0x3;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		aif1 |= 0x2;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		aif1 |= 0x1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+	case SND_SOC_DAIFMT_DSP_B:
+		/* frame inversion not valid for DSP modes */
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif1 |= WM8904_AIF_BCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_RIGHT_J:
+	case SND_SOC_DAIFMT_LEFT_J:
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_IF:
+			aif1 |= WM8904_AIF_BCLK_INV | WM8904_AIF_LRCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif1 |= WM8904_AIF_BCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_NB_IF:
+			aif1 |= WM8904_AIF_LRCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+			    WM8904_AIF_BCLK_INV | WM8904_AIF_LRCLK_INV |
+			    WM8904_AIF_FMT_MASK | WM8904_BCLK_DIR, aif1);
+	snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_3,
+			    WM8904_LRCLK_DIR, aif3);
+
+	return 0;
+}
+
+
+static int wm8904_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+			       unsigned int rx_mask, int slots, int slot_width)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8904_priv *wm8904 = codec->private_data;
+	int aif1 = 0;
+
+	/* Don't need to validate anything if we're turning off TDM */
+	if (slots == 0)
+		goto out;
+
+	/* Note that we allow configurations we can't handle ourselves - 
+	 * for example, we can generate clocks for slots 2 and up even if
+	 * we can't use those slots ourselves.
+	 */
+	aif1 |= WM8904_AIFADC_TDM | WM8904_AIFDAC_TDM;
+
+	switch (rx_mask) {
+	case 3:
+		break;
+	case 0xc:
+		aif1 |= WM8904_AIFADC_TDM_CHAN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+
+	switch (tx_mask) {
+	case 3:
+		break;
+	case 0xc:
+		aif1 |= WM8904_AIFDAC_TDM_CHAN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+out:
+	wm8904->tdm_width = slot_width;
+	wm8904->tdm_slots = slots / 2;
+
+	snd_soc_update_bits(codec, WM8904_AUDIO_INTERFACE_1,
+			    WM8904_AIFADC_TDM | WM8904_AIFADC_TDM_CHAN |
+			    WM8904_AIFDAC_TDM | WM8904_AIFDAC_TDM_CHAN, aif1);
+
+	return 0;
+}
+
+struct _fll_div {
+	u16 fll_fratio;
+	u16 fll_outdiv;
+	u16 fll_clk_ref_div;
+	u16 n;
+	u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static struct {
+	unsigned int min;
+	unsigned int max;
+	u16 fll_fratio;
+	int ratio;
+} fll_fratios[] = {
+	{       0,    64000, 4, 16 },
+	{   64000,   128000, 3,  8 },
+	{  128000,   256000, 2,  4 },
+	{  256000,  1000000, 1,  2 },
+	{ 1000000, 13500000, 0,  1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+		       unsigned int Fout)
+{
+	u64 Kpart;
+	unsigned int K, Ndiv, Nmod, target;
+	unsigned int div;
+	int i;
+
+	/* Fref must be <=13.5MHz */
+	div = 1;
+	fll_div->fll_clk_ref_div = 0;
+	while ((Fref / div) > 13500000) {
+		div *= 2;
+		fll_div->fll_clk_ref_div++;
+
+		if (div > 8) {
+			pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+			       Fref);
+			return -EINVAL;
+		}
+	}
+
+	pr_debug("Fref=%u Fout=%u\n", Fref, Fout);
+
+	/* Apply the division for our remaining calculations */
+	Fref /= div;
+
+	/* Fvco should be 90-100MHz; don't check the upper bound */
+	div = 4;
+	while (Fout * div < 90000000) {
+		div++;
+		if (div > 64) {
+			pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+			       Fout);
+			return -EINVAL;
+		}
+	}
+	target = Fout * div;
+	fll_div->fll_outdiv = div - 1;
+
+	pr_debug("Fvco=%dHz\n", target);
+
+	/* Find an appropraite FLL_FRATIO and factor it out of the target */
+	for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+		if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+			fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+			target /= fll_fratios[i].ratio;
+			break;
+		}
+	}
+	if (i == ARRAY_SIZE(fll_fratios)) {
+		pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+		return -EINVAL;
+	}
+
+	/* Now, calculate N.K */
+	Ndiv = target / Fref;
+
+	fll_div->n = Ndiv;
+	Nmod = target % Fref;
+	pr_debug("Nmod=%d\n", Nmod);
+
+	/* Calculate fractional part - scale up so we can round. */
+	Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, Fref);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	fll_div->k = K / 10;
+
+	pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n",
+		 fll_div->n, fll_div->k,
+		 fll_div->fll_fratio, fll_div->fll_outdiv,
+		 fll_div->fll_clk_ref_div);
+
+	return 0;
+}
+
+static int wm8904_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
+			  unsigned int Fref, unsigned int Fout)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8904_priv *wm8904 = codec->private_data;
+	struct _fll_div fll_div;
+	int ret, val;
+	int clock2, fll1;
+
+	/* Any change? */
+	if (source == wm8904->fll_src && Fref == wm8904->fll_fref &&
+	    Fout == wm8904->fll_fout)
+		return 0;
+
+	clock2 = snd_soc_read(codec, WM8904_CLOCK_RATES_2);
+
+	if (Fout == 0) {
+		dev_dbg(codec->dev, "FLL disabled\n");
+
+		wm8904->fll_fref = 0;
+		wm8904->fll_fout = 0;
+
+		/* Gate SYSCLK to avoid glitches */
+		snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+				    WM8904_CLK_SYS_ENA, 0);
+
+		snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+				    WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+
+		goto out;
+	}
+
+	/* Validate the FLL ID */
+	switch (source) {
+	case WM8904_FLL_MCLK:
+	case WM8904_FLL_LRCLK:
+	case WM8904_FLL_BCLK:
+		ret = fll_factors(&fll_div, Fref, Fout);
+		if (ret != 0)
+			return ret;
+		break;
+
+	case WM8904_FLL_FREE_RUNNING:
+		dev_dbg(codec->dev, "Using free running FLL\n");
+		/* Force 12MHz and output/4 for now */
+		Fout = 12000000;
+		Fref = 12000000;
+
+		memset(&fll_div, 0, sizeof(fll_div));
+		fll_div.fll_outdiv = 3;
+		break;
+
+	default:
+		dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+		return -EINVAL;
+	}
+
+	/* Save current state then disable the FLL and SYSCLK to avoid
+	 * misclocking */
+	fll1 = snd_soc_read(codec, WM8904_FLL_CONTROL_1);
+	snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+			    WM8904_CLK_SYS_ENA, 0);
+	snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+			    WM8904_FLL_OSC_ENA | WM8904_FLL_ENA, 0);
+
+	/* Unlock forced oscilator control to switch it on/off */
+	snd_soc_update_bits(codec, WM8904_CONTROL_INTERFACE_TEST_1,
+			    WM8904_USER_KEY, WM8904_USER_KEY);
+
+	if (fll_id == WM8904_FLL_FREE_RUNNING) {
+		val = WM8904_FLL_FRC_NCO;
+	} else {
+		val = 0;
+	}
+
+	snd_soc_update_bits(codec, WM8904_FLL_NCO_TEST_1, WM8904_FLL_FRC_NCO,
+			    val);
+	snd_soc_update_bits(codec, WM8904_CONTROL_INTERFACE_TEST_1,
+			    WM8904_USER_KEY, 0);
+
+	switch (fll_id) {
+	case WM8904_FLL_MCLK:
+		snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+				    WM8904_FLL_CLK_REF_SRC_MASK, 0);
+		break;
+
+	case WM8904_FLL_LRCLK:
+		snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+				    WM8904_FLL_CLK_REF_SRC_MASK, 1);
+		break;
+
+	case WM8904_FLL_BCLK:
+		snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+				    WM8904_FLL_CLK_REF_SRC_MASK, 2);
+		break;
+	}
+
+	if (fll_div.k)
+		val = WM8904_FLL_FRACN_ENA;
+	else
+		val = 0;
+	snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+			    WM8904_FLL_FRACN_ENA, val);
+
+	snd_soc_update_bits(codec, WM8904_FLL_CONTROL_2,
+			    WM8904_FLL_OUTDIV_MASK | WM8904_FLL_FRATIO_MASK,
+			    (fll_div.fll_outdiv << WM8904_FLL_OUTDIV_SHIFT) |
+			    (fll_div.fll_fratio << WM8904_FLL_FRATIO_SHIFT));
+
+	snd_soc_write(codec, WM8904_FLL_CONTROL_3, fll_div.k);
+
+	snd_soc_update_bits(codec, WM8904_FLL_CONTROL_4, WM8904_FLL_N_MASK,
+			    fll_div.n << WM8904_FLL_N_SHIFT);
+
+	snd_soc_update_bits(codec, WM8904_FLL_CONTROL_5,
+			    WM8904_FLL_CLK_REF_DIV_MASK,
+			    fll_div.fll_clk_ref_div 
+			    << WM8904_FLL_CLK_REF_DIV_SHIFT);
+
+	dev_dbg(codec->dev, "FLL configured for %dHz->%dHz\n", Fref, Fout);
+
+	wm8904->fll_fref = Fref;
+	wm8904->fll_fout = Fout;
+	wm8904->fll_src = source;
+
+	/* Enable the FLL if it was previously active */
+	snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+			    WM8904_FLL_OSC_ENA, fll1);
+	snd_soc_update_bits(codec, WM8904_FLL_CONTROL_1,
+			    WM8904_FLL_ENA, fll1);
+
+out:
+	/* Reenable SYSCLK if it was previously active */
+	snd_soc_update_bits(codec, WM8904_CLOCK_RATES_2,
+			    WM8904_CLK_SYS_ENA, clock2);
+
+	return 0;
+}
+
+static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	int val;
+
+	if (mute)
+		val = WM8904_DAC_MUTE;
+	else
+		val = 0;
+
+	snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_1, WM8904_DAC_MUTE, val);
+
+	return 0;
+}
+
+static void wm8904_sync_cache(struct snd_soc_codec *codec)
+{
+	struct wm8904_priv *wm8904 = codec->private_data;
+	int i;
+
+	if (!codec->cache_sync)
+		return;
+
+	codec->cache_only = 0;
+
+	/* Sync back cached values if they're different from the
+	 * hardware default.
+	 */
+	for (i = 1; i < ARRAY_SIZE(wm8904->reg_cache); i++) {
+		if (!wm8904_access[i].writable)
+			continue;
+
+		if (wm8904->reg_cache[i] == wm8904_reg[i])
+			continue;
+
+		snd_soc_write(codec, i, wm8904->reg_cache[i]);
+	}
+
+	codec->cache_sync = 0;
+}
+
+static int wm8904_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	struct wm8904_priv *wm8904 = codec->private_data;
+	int ret;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		/* VMID resistance 2*50k */
+		snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+				    WM8904_VMID_RES_MASK,
+				    0x1 << WM8904_VMID_RES_SHIFT);
+
+		/* Normal bias current */
+		snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+				    WM8904_ISEL_MASK, 2 << WM8904_ISEL_SHIFT);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies),
+						    wm8904->supplies);
+			if (ret != 0) {
+				dev_err(codec->dev,
+					"Failed to enable supplies: %d\n",
+					ret);
+				return ret;
+			}
+
+			wm8904_sync_cache(codec);
+
+			/* Enable bias */
+			snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+					    WM8904_BIAS_ENA, WM8904_BIAS_ENA);
+
+			/* Enable VMID, VMID buffering, 2*5k resistance */
+			snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+					    WM8904_VMID_ENA |
+					    WM8904_VMID_RES_MASK,
+					    WM8904_VMID_ENA |
+					    0x3 << WM8904_VMID_RES_SHIFT);
+
+			/* Let VMID ramp */
+			msleep(1);
+		}
+
+		/* Maintain VMID with 2*250k */
+		snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+				    WM8904_VMID_RES_MASK,
+				    0x2 << WM8904_VMID_RES_SHIFT);
+
+		/* Bias current *0.5 */
+		snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+				    WM8904_ISEL_MASK, 0);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		/* Turn off VMID */
+		snd_soc_update_bits(codec, WM8904_VMID_CONTROL_0,
+				    WM8904_VMID_RES_MASK | WM8904_VMID_ENA, 0);
+
+		/* Stop bias generation */
+		snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+				    WM8904_BIAS_ENA, 0);
+
+#ifdef CONFIG_REGULATOR
+		/* Post 2.6.34 we will be able to get a callback when
+		 * the regulators are disabled which we can use but
+		 * for now just assume that the power will be cut if
+		 * the regulator API is in use.
+		 */
+		codec->cache_sync = 1;
+#endif
+
+		regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies),
+				       wm8904->supplies);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8904_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8904_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_ops wm8904_dai_ops = {
+	.set_sysclk = wm8904_set_sysclk,
+	.set_fmt = wm8904_set_fmt,
+	.set_tdm_slot = wm8904_set_tdm_slot,
+	.set_pll = wm8904_set_fll,
+	.hw_params = wm8904_hw_params,
+	.digital_mute = wm8904_digital_mute,
+};
+
+struct snd_soc_dai wm8904_dai = {
+	.name = "WM8904",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = WM8904_RATES,
+		.formats = WM8904_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = WM8904_RATES,
+		.formats = WM8904_FORMATS,
+	},
+	.ops = &wm8904_dai_ops,
+	.symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8904_dai);
+
+#ifdef CONFIG_PM
+static int wm8904_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm8904_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int wm8904_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+#else
+#define wm8904_suspend NULL
+#define wm8904_resume NULL
+#endif
+
+static void wm8904_handle_retune_mobile_pdata(struct wm8904_priv *wm8904)
+{
+	struct snd_soc_codec *codec = &wm8904->codec;
+	struct wm8904_pdata *pdata = wm8904->pdata;
+	struct snd_kcontrol_new control =
+		SOC_ENUM_EXT("EQ Mode",
+			     wm8904->retune_mobile_enum,
+			     wm8904_get_retune_mobile_enum,
+			     wm8904_put_retune_mobile_enum);
+	int ret, i, j;
+	const char **t;
+
+	/* We need an array of texts for the enum API but the number
+	 * of texts is likely to be less than the number of
+	 * configurations due to the sample rate dependency of the
+	 * configurations. */
+	wm8904->num_retune_mobile_texts = 0;
+	wm8904->retune_mobile_texts = NULL;
+	for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+		for (j = 0; j < wm8904->num_retune_mobile_texts; j++) {
+			if (strcmp(pdata->retune_mobile_cfgs[i].name,
+				   wm8904->retune_mobile_texts[j]) == 0)
+				break;
+		}
+
+		if (j != wm8904->num_retune_mobile_texts)
+			continue;
+
+		/* Expand the array... */
+		t = krealloc(wm8904->retune_mobile_texts,
+			     sizeof(char *) * 
+			     (wm8904->num_retune_mobile_texts + 1),
+			     GFP_KERNEL);
+		if (t == NULL)
+			continue;
+
+		/* ...store the new entry... */
+		t[wm8904->num_retune_mobile_texts] = 
+			pdata->retune_mobile_cfgs[i].name;
+
+		/* ...and remember the new version. */
+		wm8904->num_retune_mobile_texts++;
+		wm8904->retune_mobile_texts = t;
+	}
+
+	dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+		wm8904->num_retune_mobile_texts);
+
+	wm8904->retune_mobile_enum.max = wm8904->num_retune_mobile_texts;
+	wm8904->retune_mobile_enum.texts = wm8904->retune_mobile_texts;
+
+	ret = snd_soc_add_controls(&wm8904->codec, &control, 1);
+	if (ret != 0)
+		dev_err(wm8904->codec.dev,
+			"Failed to add ReTune Mobile control: %d\n", ret);
+}
+
+static void wm8904_handle_pdata(struct wm8904_priv *wm8904)
+{
+	struct snd_soc_codec *codec = &wm8904->codec;
+	struct wm8904_pdata *pdata = wm8904->pdata;
+	int ret, i;
+
+	if (!pdata) {
+		snd_soc_add_controls(&wm8904->codec, wm8904_eq_controls,
+				     ARRAY_SIZE(wm8904_eq_controls));
+		return;
+	}
+
+	dev_dbg(codec->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
+
+	if (pdata->num_drc_cfgs) {
+		struct snd_kcontrol_new control =
+			SOC_ENUM_EXT("DRC Mode", wm8904->drc_enum,
+				     wm8904_get_drc_enum, wm8904_put_drc_enum);
+
+		/* We need an array of texts for the enum API */
+		wm8904->drc_texts = kmalloc(sizeof(char *)
+					    * pdata->num_drc_cfgs, GFP_KERNEL);
+		if (!wm8904->drc_texts) {
+			dev_err(wm8904->codec.dev,
+				"Failed to allocate %d DRC config texts\n",
+				pdata->num_drc_cfgs);
+			return;
+		}
+
+		for (i = 0; i < pdata->num_drc_cfgs; i++)
+			wm8904->drc_texts[i] = pdata->drc_cfgs[i].name;
+
+		wm8904->drc_enum.max = pdata->num_drc_cfgs;
+		wm8904->drc_enum.texts = wm8904->drc_texts;
+
+		ret = snd_soc_add_controls(&wm8904->codec, &control, 1);
+		if (ret != 0)
+			dev_err(wm8904->codec.dev,
+				"Failed to add DRC mode control: %d\n", ret);
+
+		wm8904_set_drc(codec);
+	}
+
+	dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
+		pdata->num_retune_mobile_cfgs);
+
+	if (pdata->num_retune_mobile_cfgs)
+		wm8904_handle_retune_mobile_pdata(wm8904);
+	else
+		snd_soc_add_controls(&wm8904->codec, wm8904_eq_controls,
+				     ARRAY_SIZE(wm8904_eq_controls));
+}
+
+static int wm8904_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8904_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8904_codec;
+	codec = wm8904_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	wm8904_handle_pdata(codec->private_data);
+
+	wm8904_add_widgets(codec);
+
+	return ret;
+
+pcm_err:
+	return ret;
+}
+
+static int wm8904_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8904 = {
+	.probe = 	wm8904_probe,
+	.remove = 	wm8904_remove,
+	.suspend = 	wm8904_suspend,
+	.resume =	wm8904_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8904);
+
+static int wm8904_register(struct wm8904_priv *wm8904,
+			   enum snd_soc_control_type control)
+{
+	int ret;
+	struct snd_soc_codec *codec = &wm8904->codec;
+	int i;
+
+	if (wm8904_codec) {
+		dev_err(codec->dev, "Another WM8904 is registered\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8904;
+	codec->name = "WM8904";
+	codec->owner = THIS_MODULE;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8904_set_bias_level;
+	codec->dai = &wm8904_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = WM8904_MAX_REGISTER;
+	codec->reg_cache = &wm8904->reg_cache;
+	codec->volatile_register = wm8904_volatile_register;
+	codec->cache_sync = 1;
+	codec->idle_bias_off = 1;
+
+	switch (wm8904->devtype) {
+	case WM8904:
+		break;
+	case WM8912:
+		memset(&wm8904_dai.capture, 0, sizeof(wm8904_dai.capture));
+		break;
+	default:
+		dev_err(codec->dev, "Unknown device type %d\n",
+			wm8904->devtype);
+		return -EINVAL;
+	}
+
+	memcpy(codec->reg_cache, wm8904_reg, sizeof(wm8904_reg));
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, control);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(wm8904->supplies); i++)
+		wm8904->supplies[i].supply = wm8904_supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8904->supplies),
+				 wm8904->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+		goto err;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies),
+				    wm8904->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+		goto err_get;
+	}
+
+	ret = snd_soc_read(codec, WM8904_SW_RESET_AND_ID);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to read ID register\n");
+		goto err_enable;
+	}
+	if (ret != wm8904_reg[WM8904_SW_RESET_AND_ID]) {
+		dev_err(codec->dev, "Device is not a WM8904, ID is %x\n", ret);
+		ret = -EINVAL;
+		goto err_enable;
+	}
+
+	ret = snd_soc_read(codec, WM8904_REVISION);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to read device revision: %d\n",
+			ret);
+		goto err_enable;
+	}
+	dev_info(codec->dev, "revision %c\n", ret + 'A');
+
+	ret = wm8904_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		goto err_enable;
+	}
+
+	wm8904_dai.dev = codec->dev;
+
+	/* Change some default settings - latch VU and enable ZC */
+	wm8904->reg_cache[WM8904_ADC_DIGITAL_VOLUME_LEFT] |= WM8904_ADC_VU;
+	wm8904->reg_cache[WM8904_ADC_DIGITAL_VOLUME_RIGHT] |= WM8904_ADC_VU;
+	wm8904->reg_cache[WM8904_DAC_DIGITAL_VOLUME_LEFT] |= WM8904_DAC_VU;
+	wm8904->reg_cache[WM8904_DAC_DIGITAL_VOLUME_RIGHT] |= WM8904_DAC_VU;
+	wm8904->reg_cache[WM8904_ANALOGUE_OUT1_LEFT] |= WM8904_HPOUT_VU |
+		WM8904_HPOUTLZC;
+	wm8904->reg_cache[WM8904_ANALOGUE_OUT1_RIGHT] |= WM8904_HPOUT_VU |
+		WM8904_HPOUTRZC;
+	wm8904->reg_cache[WM8904_ANALOGUE_OUT2_LEFT] |= WM8904_LINEOUT_VU |
+		WM8904_LINEOUTLZC;
+	wm8904->reg_cache[WM8904_ANALOGUE_OUT2_RIGHT] |= WM8904_LINEOUT_VU |
+		WM8904_LINEOUTRZC;
+	wm8904->reg_cache[WM8904_CLOCK_RATES_0] &= ~WM8904_SR_MODE;
+
+	/* Set Class W by default - this will be managed by the Class
+	 * G widget at runtime where bypass paths are available.
+	 */
+	wm8904->reg_cache[WM8904_CLASS_W_0] |= WM8904_CP_DYN_PWR;
+
+	/* Use normal bias source */
+	wm8904->reg_cache[WM8904_BIAS_CONTROL_0] &= ~WM8904_POBCTRL;
+
+	wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* Bias level configuration will have done an extra enable */
+	regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
+
+	wm8904_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_register_dai(&wm8904_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		return ret;
+	}
+
+	return 0;
+
+err_enable:
+	regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
+err_get:
+	regulator_bulk_free(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
+err:
+	kfree(wm8904);
+	return ret;
+}
+
+static void wm8904_unregister(struct wm8904_priv *wm8904)
+{
+	wm8904_set_bias_level(&wm8904->codec, SND_SOC_BIAS_OFF);
+	regulator_bulk_free(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
+	snd_soc_unregister_dai(&wm8904_dai);
+	snd_soc_unregister_codec(&wm8904->codec);
+	kfree(wm8904);
+	wm8904_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8904_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8904_priv *wm8904;
+	struct snd_soc_codec *codec;
+
+	wm8904 = kzalloc(sizeof(struct wm8904_priv), GFP_KERNEL);
+	if (wm8904 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8904->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	wm8904->devtype = id->driver_data;
+
+	i2c_set_clientdata(i2c, wm8904);
+	codec->control_data = i2c;
+	wm8904->pdata = i2c->dev.platform_data;
+
+	codec->dev = &i2c->dev;
+
+	return wm8904_register(wm8904, SND_SOC_I2C);
+}
+
+static __devexit int wm8904_i2c_remove(struct i2c_client *client)
+{
+	struct wm8904_priv *wm8904 = i2c_get_clientdata(client);
+	wm8904_unregister(wm8904);
+	return 0;
+}
+
+static const struct i2c_device_id wm8904_i2c_id[] = {
+	{ "wm8904", WM8904 },
+	{ "wm8912", WM8912 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id);
+
+static struct i2c_driver wm8904_i2c_driver = {
+	.driver = {
+		.name = "WM8904",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm8904_i2c_probe,
+	.remove =   __devexit_p(wm8904_i2c_remove),
+	.id_table = wm8904_i2c_id,
+};
+#endif
+
+static int __init wm8904_modinit(void)
+{
+	int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&wm8904_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8904 I2C driver: %d\n",
+		       ret);
+	}
+#endif
+	return 0;
+}
+module_init(wm8904_modinit);
+
+static void __exit wm8904_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8904_i2c_driver);
+#endif
+}
+module_exit(wm8904_exit);
+
+MODULE_DESCRIPTION("ASoC WM8904 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8904.h b/sound/soc/codecs/wm8904.h
new file mode 100644
index 0000000..b68886d
--- /dev/null
+++ b/sound/soc/codecs/wm8904.h
@@ -0,0 +1,1681 @@
+/*
+ * wm8904.h  --  WM8904 ASoC driver
+ *
+ * Copyright 2009 Wolfson Microelectronics, plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8904_H
+#define _WM8904_H
+
+#define WM8904_CLK_MCLK 1
+#define WM8904_CLK_FLL  2
+
+#define WM8904_FLL_MCLK          1
+#define WM8904_FLL_BCLK          2
+#define WM8904_FLL_LRCLK         3
+#define WM8904_FLL_FREE_RUNNING  4
+
+extern struct snd_soc_dai wm8904_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8904;
+
+/*
+ * Register values.
+ */
+#define WM8904_SW_RESET_AND_ID                  0x00
+#define WM8904_REVISION				0x01
+#define WM8904_BIAS_CONTROL_0                   0x04
+#define WM8904_VMID_CONTROL_0                   0x05
+#define WM8904_MIC_BIAS_CONTROL_0               0x06
+#define WM8904_MIC_BIAS_CONTROL_1               0x07
+#define WM8904_ANALOGUE_DAC_0                   0x08
+#define WM8904_MIC_FILTER_CONTROL               0x09
+#define WM8904_ANALOGUE_ADC_0                   0x0A
+#define WM8904_POWER_MANAGEMENT_0               0x0C
+#define WM8904_POWER_MANAGEMENT_2               0x0E
+#define WM8904_POWER_MANAGEMENT_3               0x0F
+#define WM8904_POWER_MANAGEMENT_6               0x12
+#define WM8904_CLOCK_RATES_0                    0x14
+#define WM8904_CLOCK_RATES_1                    0x15
+#define WM8904_CLOCK_RATES_2                    0x16
+#define WM8904_AUDIO_INTERFACE_0                0x18
+#define WM8904_AUDIO_INTERFACE_1                0x19
+#define WM8904_AUDIO_INTERFACE_2                0x1A
+#define WM8904_AUDIO_INTERFACE_3                0x1B
+#define WM8904_DAC_DIGITAL_VOLUME_LEFT          0x1E
+#define WM8904_DAC_DIGITAL_VOLUME_RIGHT         0x1F
+#define WM8904_DAC_DIGITAL_0                    0x20
+#define WM8904_DAC_DIGITAL_1                    0x21
+#define WM8904_ADC_DIGITAL_VOLUME_LEFT          0x24
+#define WM8904_ADC_DIGITAL_VOLUME_RIGHT         0x25
+#define WM8904_ADC_DIGITAL_0                    0x26
+#define WM8904_DIGITAL_MICROPHONE_0             0x27
+#define WM8904_DRC_0                            0x28
+#define WM8904_DRC_1                            0x29
+#define WM8904_DRC_2                            0x2A
+#define WM8904_DRC_3                            0x2B
+#define WM8904_ANALOGUE_LEFT_INPUT_0            0x2C
+#define WM8904_ANALOGUE_RIGHT_INPUT_0           0x2D
+#define WM8904_ANALOGUE_LEFT_INPUT_1            0x2E
+#define WM8904_ANALOGUE_RIGHT_INPUT_1           0x2F
+#define WM8904_ANALOGUE_OUT1_LEFT               0x39
+#define WM8904_ANALOGUE_OUT1_RIGHT              0x3A
+#define WM8904_ANALOGUE_OUT2_LEFT               0x3B
+#define WM8904_ANALOGUE_OUT2_RIGHT              0x3C
+#define WM8904_ANALOGUE_OUT12_ZC                0x3D
+#define WM8904_DC_SERVO_0                       0x43
+#define WM8904_DC_SERVO_1                       0x44
+#define WM8904_DC_SERVO_2                       0x45
+#define WM8904_DC_SERVO_4                       0x47
+#define WM8904_DC_SERVO_5                       0x48
+#define WM8904_DC_SERVO_6                       0x49
+#define WM8904_DC_SERVO_7                       0x4A
+#define WM8904_DC_SERVO_8                       0x4B
+#define WM8904_DC_SERVO_9                       0x4C
+#define WM8904_DC_SERVO_READBACK_0              0x4D
+#define WM8904_ANALOGUE_HP_0                    0x5A
+#define WM8904_ANALOGUE_LINEOUT_0               0x5E
+#define WM8904_CHARGE_PUMP_0                    0x62
+#define WM8904_CLASS_W_0                        0x68
+#define WM8904_WRITE_SEQUENCER_0                0x6C
+#define WM8904_WRITE_SEQUENCER_1                0x6D
+#define WM8904_WRITE_SEQUENCER_2                0x6E
+#define WM8904_WRITE_SEQUENCER_3                0x6F
+#define WM8904_WRITE_SEQUENCER_4                0x70
+#define WM8904_FLL_CONTROL_1                    0x74
+#define WM8904_FLL_CONTROL_2                    0x75
+#define WM8904_FLL_CONTROL_3                    0x76
+#define WM8904_FLL_CONTROL_4                    0x77
+#define WM8904_FLL_CONTROL_5                    0x78
+#define WM8904_GPIO_CONTROL_1                   0x79
+#define WM8904_GPIO_CONTROL_2                   0x7A
+#define WM8904_GPIO_CONTROL_3                   0x7B
+#define WM8904_GPIO_CONTROL_4                   0x7C
+#define WM8904_DIGITAL_PULLS                    0x7E
+#define WM8904_INTERRUPT_STATUS                 0x7F
+#define WM8904_INTERRUPT_STATUS_MASK            0x80
+#define WM8904_INTERRUPT_POLARITY               0x81
+#define WM8904_INTERRUPT_DEBOUNCE               0x82
+#define WM8904_EQ1                              0x86
+#define WM8904_EQ2                              0x87
+#define WM8904_EQ3                              0x88
+#define WM8904_EQ4                              0x89
+#define WM8904_EQ5                              0x8A
+#define WM8904_EQ6                              0x8B
+#define WM8904_EQ7                              0x8C
+#define WM8904_EQ8                              0x8D
+#define WM8904_EQ9                              0x8E
+#define WM8904_EQ10                             0x8F
+#define WM8904_EQ11                             0x90
+#define WM8904_EQ12                             0x91
+#define WM8904_EQ13                             0x92
+#define WM8904_EQ14                             0x93
+#define WM8904_EQ15                             0x94
+#define WM8904_EQ16                             0x95
+#define WM8904_EQ17                             0x96
+#define WM8904_EQ18                             0x97
+#define WM8904_EQ19                             0x98
+#define WM8904_EQ20                             0x99
+#define WM8904_EQ21                             0x9A
+#define WM8904_EQ22                             0x9B
+#define WM8904_EQ23                             0x9C
+#define WM8904_EQ24                             0x9D
+#define WM8904_CONTROL_INTERFACE_TEST_1         0xA1
+#define WM8904_ANALOGUE_OUTPUT_BIAS_0           0xCC
+#define WM8904_FLL_NCO_TEST_0                   0xF7
+#define WM8904_FLL_NCO_TEST_1                   0xF8
+
+#define WM8904_REGISTER_COUNT                   101
+#define WM8904_MAX_REGISTER                     0xF8
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - SW Reset and ID
+ */
+#define WM8904_SW_RST_DEV_ID1_MASK              0xFFFF  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8904_SW_RST_DEV_ID1_SHIFT                  0  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8904_SW_RST_DEV_ID1_WIDTH                 16  /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R1 (0x01) - Revision
+ */
+#define WM8904_REVISION_MASK              	0x000F  /* REVISION - [3:0] */
+#define WM8904_REVISION_SHIFT             	     0  /* REVISION - [3:0] */
+#define WM8904_REVISION_WIDTH             	    16  /* REVISION - [3:0] */
+
+/*
+ * R4 (0x04) - Bias Control 0
+ */
+#define WM8904_POBCTRL                          0x0010  /* POBCTRL */
+#define WM8904_POBCTRL_MASK                     0x0010  /* POBCTRL */
+#define WM8904_POBCTRL_SHIFT                         4  /* POBCTRL */
+#define WM8904_POBCTRL_WIDTH                         1  /* POBCTRL */
+#define WM8904_ISEL_MASK                        0x000C  /* ISEL - [3:2] */
+#define WM8904_ISEL_SHIFT                            2  /* ISEL - [3:2] */
+#define WM8904_ISEL_WIDTH                            2  /* ISEL - [3:2] */
+#define WM8904_STARTUP_BIAS_ENA                 0x0002  /* STARTUP_BIAS_ENA */
+#define WM8904_STARTUP_BIAS_ENA_MASK            0x0002  /* STARTUP_BIAS_ENA */
+#define WM8904_STARTUP_BIAS_ENA_SHIFT                1  /* STARTUP_BIAS_ENA */
+#define WM8904_STARTUP_BIAS_ENA_WIDTH                1  /* STARTUP_BIAS_ENA */
+#define WM8904_BIAS_ENA                         0x0001  /* BIAS_ENA */
+#define WM8904_BIAS_ENA_MASK                    0x0001  /* BIAS_ENA */
+#define WM8904_BIAS_ENA_SHIFT                        0  /* BIAS_ENA */
+#define WM8904_BIAS_ENA_WIDTH                        1  /* BIAS_ENA */
+
+/*
+ * R5 (0x05) - VMID Control 0
+ */
+#define WM8904_VMID_BUF_ENA                     0x0040  /* VMID_BUF_ENA */
+#define WM8904_VMID_BUF_ENA_MASK                0x0040  /* VMID_BUF_ENA */
+#define WM8904_VMID_BUF_ENA_SHIFT                    6  /* VMID_BUF_ENA */
+#define WM8904_VMID_BUF_ENA_WIDTH                    1  /* VMID_BUF_ENA */
+#define WM8904_VMID_RES_MASK                    0x0006  /* VMID_RES - [2:1] */
+#define WM8904_VMID_RES_SHIFT                        1  /* VMID_RES - [2:1] */
+#define WM8904_VMID_RES_WIDTH                        2  /* VMID_RES - [2:1] */
+#define WM8904_VMID_ENA                         0x0001  /* VMID_ENA */
+#define WM8904_VMID_ENA_MASK                    0x0001  /* VMID_ENA */
+#define WM8904_VMID_ENA_SHIFT                        0  /* VMID_ENA */
+#define WM8904_VMID_ENA_WIDTH                        1  /* VMID_ENA */
+
+/*
+ * R6 (0x06) - Mic Bias Control 0
+ */
+#define WM8904_MICDET_THR_MASK                  0x0070  /* MICDET_THR - [6:4] */
+#define WM8904_MICDET_THR_SHIFT                      4  /* MICDET_THR - [6:4] */
+#define WM8904_MICDET_THR_WIDTH                      3  /* MICDET_THR - [6:4] */
+#define WM8904_MICSHORT_THR_MASK                0x000C  /* MICSHORT_THR - [3:2] */
+#define WM8904_MICSHORT_THR_SHIFT                    2  /* MICSHORT_THR - [3:2] */
+#define WM8904_MICSHORT_THR_WIDTH                    2  /* MICSHORT_THR - [3:2] */
+#define WM8904_MICDET_ENA                       0x0002  /* MICDET_ENA */
+#define WM8904_MICDET_ENA_MASK                  0x0002  /* MICDET_ENA */
+#define WM8904_MICDET_ENA_SHIFT                      1  /* MICDET_ENA */
+#define WM8904_MICDET_ENA_WIDTH                      1  /* MICDET_ENA */
+#define WM8904_MICBIAS_ENA                      0x0001  /* MICBIAS_ENA */
+#define WM8904_MICBIAS_ENA_MASK                 0x0001  /* MICBIAS_ENA */
+#define WM8904_MICBIAS_ENA_SHIFT                     0  /* MICBIAS_ENA */
+#define WM8904_MICBIAS_ENA_WIDTH                     1  /* MICBIAS_ENA */
+
+/*
+ * R7 (0x07) - Mic Bias Control 1
+ */
+#define WM8904_MIC_DET_FILTER_ENA               0x8000  /* MIC_DET_FILTER_ENA */
+#define WM8904_MIC_DET_FILTER_ENA_MASK          0x8000  /* MIC_DET_FILTER_ENA */
+#define WM8904_MIC_DET_FILTER_ENA_SHIFT             15  /* MIC_DET_FILTER_ENA */
+#define WM8904_MIC_DET_FILTER_ENA_WIDTH              1  /* MIC_DET_FILTER_ENA */
+#define WM8904_MIC_SHORT_FILTER_ENA             0x4000  /* MIC_SHORT_FILTER_ENA */
+#define WM8904_MIC_SHORT_FILTER_ENA_MASK        0x4000  /* MIC_SHORT_FILTER_ENA */
+#define WM8904_MIC_SHORT_FILTER_ENA_SHIFT           14  /* MIC_SHORT_FILTER_ENA */
+#define WM8904_MIC_SHORT_FILTER_ENA_WIDTH            1  /* MIC_SHORT_FILTER_ENA */
+#define WM8904_MICBIAS_SEL_MASK                 0x0007  /* MICBIAS_SEL - [2:0] */
+#define WM8904_MICBIAS_SEL_SHIFT                     0  /* MICBIAS_SEL - [2:0] */
+#define WM8904_MICBIAS_SEL_WIDTH                     3  /* MICBIAS_SEL - [2:0] */
+
+/*
+ * R8 (0x08) - Analogue DAC 0
+ */
+#define WM8904_DAC_BIAS_SEL_MASK                0x0018  /* DAC_BIAS_SEL - [4:3] */
+#define WM8904_DAC_BIAS_SEL_SHIFT                    3  /* DAC_BIAS_SEL - [4:3] */
+#define WM8904_DAC_BIAS_SEL_WIDTH                    2  /* DAC_BIAS_SEL - [4:3] */
+#define WM8904_DAC_VMID_BIAS_SEL_MASK           0x0006  /* DAC_VMID_BIAS_SEL - [2:1] */
+#define WM8904_DAC_VMID_BIAS_SEL_SHIFT               1  /* DAC_VMID_BIAS_SEL - [2:1] */
+#define WM8904_DAC_VMID_BIAS_SEL_WIDTH               2  /* DAC_VMID_BIAS_SEL - [2:1] */
+
+/*
+ * R9 (0x09) - mic Filter Control
+ */
+#define WM8904_MIC_DET_SET_THRESHOLD_MASK       0xF000  /* MIC_DET_SET_THRESHOLD - [15:12] */
+#define WM8904_MIC_DET_SET_THRESHOLD_SHIFT          12  /* MIC_DET_SET_THRESHOLD - [15:12] */
+#define WM8904_MIC_DET_SET_THRESHOLD_WIDTH           4  /* MIC_DET_SET_THRESHOLD - [15:12] */
+#define WM8904_MIC_DET_RESET_THRESHOLD_MASK     0x0F00  /* MIC_DET_RESET_THRESHOLD - [11:8] */
+#define WM8904_MIC_DET_RESET_THRESHOLD_SHIFT         8  /* MIC_DET_RESET_THRESHOLD - [11:8] */
+#define WM8904_MIC_DET_RESET_THRESHOLD_WIDTH         4  /* MIC_DET_RESET_THRESHOLD - [11:8] */
+#define WM8904_MIC_SHORT_SET_THRESHOLD_MASK     0x00F0  /* MIC_SHORT_SET_THRESHOLD - [7:4] */
+#define WM8904_MIC_SHORT_SET_THRESHOLD_SHIFT         4  /* MIC_SHORT_SET_THRESHOLD - [7:4] */
+#define WM8904_MIC_SHORT_SET_THRESHOLD_WIDTH         4  /* MIC_SHORT_SET_THRESHOLD - [7:4] */
+#define WM8904_MIC_SHORT_RESET_THRESHOLD_MASK   0x000F  /* MIC_SHORT_RESET_THRESHOLD - [3:0] */
+#define WM8904_MIC_SHORT_RESET_THRESHOLD_SHIFT       0  /* MIC_SHORT_RESET_THRESHOLD - [3:0] */
+#define WM8904_MIC_SHORT_RESET_THRESHOLD_WIDTH       4  /* MIC_SHORT_RESET_THRESHOLD - [3:0] */
+
+/*
+ * R10 (0x0A) - Analogue ADC 0
+ */
+#define WM8904_ADC_OSR128                       0x0001  /* ADC_OSR128 */
+#define WM8904_ADC_OSR128_MASK                  0x0001  /* ADC_OSR128 */
+#define WM8904_ADC_OSR128_SHIFT                      0  /* ADC_OSR128 */
+#define WM8904_ADC_OSR128_WIDTH                      1  /* ADC_OSR128 */
+
+/*
+ * R12 (0x0C) - Power Management 0
+ */
+#define WM8904_INL_ENA                          0x0002  /* INL_ENA */
+#define WM8904_INL_ENA_MASK                     0x0002  /* INL_ENA */
+#define WM8904_INL_ENA_SHIFT                         1  /* INL_ENA */
+#define WM8904_INL_ENA_WIDTH                         1  /* INL_ENA */
+#define WM8904_INR_ENA                          0x0001  /* INR_ENA */
+#define WM8904_INR_ENA_MASK                     0x0001  /* INR_ENA */
+#define WM8904_INR_ENA_SHIFT                         0  /* INR_ENA */
+#define WM8904_INR_ENA_WIDTH                         1  /* INR_ENA */
+
+/*
+ * R14 (0x0E) - Power Management 2
+ */
+#define WM8904_HPL_PGA_ENA                      0x0002  /* HPL_PGA_ENA */
+#define WM8904_HPL_PGA_ENA_MASK                 0x0002  /* HPL_PGA_ENA */
+#define WM8904_HPL_PGA_ENA_SHIFT                     1  /* HPL_PGA_ENA */
+#define WM8904_HPL_PGA_ENA_WIDTH                     1  /* HPL_PGA_ENA */
+#define WM8904_HPR_PGA_ENA                      0x0001  /* HPR_PGA_ENA */
+#define WM8904_HPR_PGA_ENA_MASK                 0x0001  /* HPR_PGA_ENA */
+#define WM8904_HPR_PGA_ENA_SHIFT                     0  /* HPR_PGA_ENA */
+#define WM8904_HPR_PGA_ENA_WIDTH                     1  /* HPR_PGA_ENA */
+
+/*
+ * R15 (0x0F) - Power Management 3
+ */
+#define WM8904_LINEOUTL_PGA_ENA                 0x0002  /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTL_PGA_ENA_MASK            0x0002  /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTL_PGA_ENA_SHIFT                1  /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTL_PGA_ENA_WIDTH                1  /* LINEOUTL_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA                 0x0001  /* LINEOUTR_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA_MASK            0x0001  /* LINEOUTR_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA_SHIFT                0  /* LINEOUTR_PGA_ENA */
+#define WM8904_LINEOUTR_PGA_ENA_WIDTH                1  /* LINEOUTR_PGA_ENA */
+
+/*
+ * R18 (0x12) - Power Management 6
+ */
+#define WM8904_DACL_ENA                         0x0008  /* DACL_ENA */
+#define WM8904_DACL_ENA_MASK                    0x0008  /* DACL_ENA */
+#define WM8904_DACL_ENA_SHIFT                        3  /* DACL_ENA */
+#define WM8904_DACL_ENA_WIDTH                        1  /* DACL_ENA */
+#define WM8904_DACR_ENA                         0x0004  /* DACR_ENA */
+#define WM8904_DACR_ENA_MASK                    0x0004  /* DACR_ENA */
+#define WM8904_DACR_ENA_SHIFT                        2  /* DACR_ENA */
+#define WM8904_DACR_ENA_WIDTH                        1  /* DACR_ENA */
+#define WM8904_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8904_ADCL_ENA_MASK                    0x0002  /* ADCL_ENA */
+#define WM8904_ADCL_ENA_SHIFT                        1  /* ADCL_ENA */
+#define WM8904_ADCL_ENA_WIDTH                        1  /* ADCL_ENA */
+#define WM8904_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8904_ADCR_ENA_MASK                    0x0001  /* ADCR_ENA */
+#define WM8904_ADCR_ENA_SHIFT                        0  /* ADCR_ENA */
+#define WM8904_ADCR_ENA_WIDTH                        1  /* ADCR_ENA */
+
+/*
+ * R20 (0x14) - Clock Rates 0
+ */
+#define WM8904_TOCLK_RATE_DIV16                 0x4000  /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_DIV16_MASK            0x4000  /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_DIV16_SHIFT               14  /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_DIV16_WIDTH                1  /* TOCLK_RATE_DIV16 */
+#define WM8904_TOCLK_RATE_X4                    0x2000  /* TOCLK_RATE_X4 */
+#define WM8904_TOCLK_RATE_X4_MASK               0x2000  /* TOCLK_RATE_X4 */
+#define WM8904_TOCLK_RATE_X4_SHIFT                  13  /* TOCLK_RATE_X4 */
+#define WM8904_TOCLK_RATE_X4_WIDTH                   1  /* TOCLK_RATE_X4 */
+#define WM8904_SR_MODE                          0x1000  /* SR_MODE */
+#define WM8904_SR_MODE_MASK                     0x1000  /* SR_MODE */
+#define WM8904_SR_MODE_SHIFT                        12  /* SR_MODE */
+#define WM8904_SR_MODE_WIDTH                         1  /* SR_MODE */
+#define WM8904_MCLK_DIV                         0x0001  /* MCLK_DIV */
+#define WM8904_MCLK_DIV_MASK                    0x0001  /* MCLK_DIV */
+#define WM8904_MCLK_DIV_SHIFT                        0  /* MCLK_DIV */
+#define WM8904_MCLK_DIV_WIDTH                        1  /* MCLK_DIV */
+
+/*
+ * R21 (0x15) - Clock Rates 1
+ */
+#define WM8904_CLK_SYS_RATE_MASK                0x3C00  /* CLK_SYS_RATE - [13:10] */
+#define WM8904_CLK_SYS_RATE_SHIFT                   10  /* CLK_SYS_RATE - [13:10] */
+#define WM8904_CLK_SYS_RATE_WIDTH                    4  /* CLK_SYS_RATE - [13:10] */
+#define WM8904_SAMPLE_RATE_MASK                 0x0007  /* SAMPLE_RATE - [2:0] */
+#define WM8904_SAMPLE_RATE_SHIFT                     0  /* SAMPLE_RATE - [2:0] */
+#define WM8904_SAMPLE_RATE_WIDTH                     3  /* SAMPLE_RATE - [2:0] */
+
+/*
+ * R22 (0x16) - Clock Rates 2
+ */
+#define WM8904_MCLK_INV                         0x8000  /* MCLK_INV */
+#define WM8904_MCLK_INV_MASK                    0x8000  /* MCLK_INV */
+#define WM8904_MCLK_INV_SHIFT                       15  /* MCLK_INV */
+#define WM8904_MCLK_INV_WIDTH                        1  /* MCLK_INV */
+#define WM8904_SYSCLK_SRC                       0x4000  /* SYSCLK_SRC */
+#define WM8904_SYSCLK_SRC_MASK                  0x4000  /* SYSCLK_SRC */
+#define WM8904_SYSCLK_SRC_SHIFT                     14  /* SYSCLK_SRC */
+#define WM8904_SYSCLK_SRC_WIDTH                      1  /* SYSCLK_SRC */
+#define WM8904_TOCLK_RATE                       0x1000  /* TOCLK_RATE */
+#define WM8904_TOCLK_RATE_MASK                  0x1000  /* TOCLK_RATE */
+#define WM8904_TOCLK_RATE_SHIFT                     12  /* TOCLK_RATE */
+#define WM8904_TOCLK_RATE_WIDTH                      1  /* TOCLK_RATE */
+#define WM8904_OPCLK_ENA                        0x0008  /* OPCLK_ENA */
+#define WM8904_OPCLK_ENA_MASK                   0x0008  /* OPCLK_ENA */
+#define WM8904_OPCLK_ENA_SHIFT                       3  /* OPCLK_ENA */
+#define WM8904_OPCLK_ENA_WIDTH                       1  /* OPCLK_ENA */
+#define WM8904_CLK_SYS_ENA                      0x0004  /* CLK_SYS_ENA */
+#define WM8904_CLK_SYS_ENA_MASK                 0x0004  /* CLK_SYS_ENA */
+#define WM8904_CLK_SYS_ENA_SHIFT                     2  /* CLK_SYS_ENA */
+#define WM8904_CLK_SYS_ENA_WIDTH                     1  /* CLK_SYS_ENA */
+#define WM8904_CLK_DSP_ENA                      0x0002  /* CLK_DSP_ENA */
+#define WM8904_CLK_DSP_ENA_MASK                 0x0002  /* CLK_DSP_ENA */
+#define WM8904_CLK_DSP_ENA_SHIFT                     1  /* CLK_DSP_ENA */
+#define WM8904_CLK_DSP_ENA_WIDTH                     1  /* CLK_DSP_ENA */
+#define WM8904_TOCLK_ENA                        0x0001  /* TOCLK_ENA */
+#define WM8904_TOCLK_ENA_MASK                   0x0001  /* TOCLK_ENA */
+#define WM8904_TOCLK_ENA_SHIFT                       0  /* TOCLK_ENA */
+#define WM8904_TOCLK_ENA_WIDTH                       1  /* TOCLK_ENA */
+
+/*
+ * R24 (0x18) - Audio Interface 0
+ */
+#define WM8904_DACL_DATINV                      0x1000  /* DACL_DATINV */
+#define WM8904_DACL_DATINV_MASK                 0x1000  /* DACL_DATINV */
+#define WM8904_DACL_DATINV_SHIFT                    12  /* DACL_DATINV */
+#define WM8904_DACL_DATINV_WIDTH                     1  /* DACL_DATINV */
+#define WM8904_DACR_DATINV                      0x0800  /* DACR_DATINV */
+#define WM8904_DACR_DATINV_MASK                 0x0800  /* DACR_DATINV */
+#define WM8904_DACR_DATINV_SHIFT                    11  /* DACR_DATINV */
+#define WM8904_DACR_DATINV_WIDTH                     1  /* DACR_DATINV */
+#define WM8904_DAC_BOOST_MASK                   0x0600  /* DAC_BOOST - [10:9] */
+#define WM8904_DAC_BOOST_SHIFT                       9  /* DAC_BOOST - [10:9] */
+#define WM8904_DAC_BOOST_WIDTH                       2  /* DAC_BOOST - [10:9] */
+#define WM8904_LOOPBACK                         0x0100  /* LOOPBACK */
+#define WM8904_LOOPBACK_MASK                    0x0100  /* LOOPBACK */
+#define WM8904_LOOPBACK_SHIFT                        8  /* LOOPBACK */
+#define WM8904_LOOPBACK_WIDTH                        1  /* LOOPBACK */
+#define WM8904_AIFADCL_SRC                      0x0080  /* AIFADCL_SRC */
+#define WM8904_AIFADCL_SRC_MASK                 0x0080  /* AIFADCL_SRC */
+#define WM8904_AIFADCL_SRC_SHIFT                     7  /* AIFADCL_SRC */
+#define WM8904_AIFADCL_SRC_WIDTH                     1  /* AIFADCL_SRC */
+#define WM8904_AIFADCR_SRC                      0x0040  /* AIFADCR_SRC */
+#define WM8904_AIFADCR_SRC_MASK                 0x0040  /* AIFADCR_SRC */
+#define WM8904_AIFADCR_SRC_SHIFT                     6  /* AIFADCR_SRC */
+#define WM8904_AIFADCR_SRC_WIDTH                     1  /* AIFADCR_SRC */
+#define WM8904_AIFDACL_SRC                      0x0020  /* AIFDACL_SRC */
+#define WM8904_AIFDACL_SRC_MASK                 0x0020  /* AIFDACL_SRC */
+#define WM8904_AIFDACL_SRC_SHIFT                     5  /* AIFDACL_SRC */
+#define WM8904_AIFDACL_SRC_WIDTH                     1  /* AIFDACL_SRC */
+#define WM8904_AIFDACR_SRC                      0x0010  /* AIFDACR_SRC */
+#define WM8904_AIFDACR_SRC_MASK                 0x0010  /* AIFDACR_SRC */
+#define WM8904_AIFDACR_SRC_SHIFT                     4  /* AIFDACR_SRC */
+#define WM8904_AIFDACR_SRC_WIDTH                     1  /* AIFDACR_SRC */
+#define WM8904_ADC_COMP                         0x0008  /* ADC_COMP */
+#define WM8904_ADC_COMP_MASK                    0x0008  /* ADC_COMP */
+#define WM8904_ADC_COMP_SHIFT                        3  /* ADC_COMP */
+#define WM8904_ADC_COMP_WIDTH                        1  /* ADC_COMP */
+#define WM8904_ADC_COMPMODE                     0x0004  /* ADC_COMPMODE */
+#define WM8904_ADC_COMPMODE_MASK                0x0004  /* ADC_COMPMODE */
+#define WM8904_ADC_COMPMODE_SHIFT                    2  /* ADC_COMPMODE */
+#define WM8904_ADC_COMPMODE_WIDTH                    1  /* ADC_COMPMODE */
+#define WM8904_DAC_COMP                         0x0002  /* DAC_COMP */
+#define WM8904_DAC_COMP_MASK                    0x0002  /* DAC_COMP */
+#define WM8904_DAC_COMP_SHIFT                        1  /* DAC_COMP */
+#define WM8904_DAC_COMP_WIDTH                        1  /* DAC_COMP */
+#define WM8904_DAC_COMPMODE                     0x0001  /* DAC_COMPMODE */
+#define WM8904_DAC_COMPMODE_MASK                0x0001  /* DAC_COMPMODE */
+#define WM8904_DAC_COMPMODE_SHIFT                    0  /* DAC_COMPMODE */
+#define WM8904_DAC_COMPMODE_WIDTH                    1  /* DAC_COMPMODE */
+
+/*
+ * R25 (0x19) - Audio Interface 1
+ */
+#define WM8904_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_MASK                  0x2000  /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_SHIFT                     13  /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_WIDTH                      1  /* AIFDAC_TDM */
+#define WM8904_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFDAC_TDM_CHAN_MASK             0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFDAC_TDM_CHAN_SHIFT                12  /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFDAC_TDM_CHAN_WIDTH                 1  /* AIFDAC_TDM_CHAN */
+#define WM8904_AIFADC_TDM                       0x0800  /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_MASK                  0x0800  /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_SHIFT                     11  /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_WIDTH                      1  /* AIFADC_TDM */
+#define WM8904_AIFADC_TDM_CHAN                  0x0400  /* AIFADC_TDM_CHAN */
+#define WM8904_AIFADC_TDM_CHAN_MASK             0x0400  /* AIFADC_TDM_CHAN */
+#define WM8904_AIFADC_TDM_CHAN_SHIFT                10  /* AIFADC_TDM_CHAN */
+#define WM8904_AIFADC_TDM_CHAN_WIDTH                 1  /* AIFADC_TDM_CHAN */
+#define WM8904_AIF_TRIS                         0x0100  /* AIF_TRIS */
+#define WM8904_AIF_TRIS_MASK                    0x0100  /* AIF_TRIS */
+#define WM8904_AIF_TRIS_SHIFT                        8  /* AIF_TRIS */
+#define WM8904_AIF_TRIS_WIDTH                        1  /* AIF_TRIS */
+#define WM8904_AIF_BCLK_INV                     0x0080  /* AIF_BCLK_INV */
+#define WM8904_AIF_BCLK_INV_MASK                0x0080  /* AIF_BCLK_INV */
+#define WM8904_AIF_BCLK_INV_SHIFT                    7  /* AIF_BCLK_INV */
+#define WM8904_AIF_BCLK_INV_WIDTH                    1  /* AIF_BCLK_INV */
+#define WM8904_BCLK_DIR                         0x0040  /* BCLK_DIR */
+#define WM8904_BCLK_DIR_MASK                    0x0040  /* BCLK_DIR */
+#define WM8904_BCLK_DIR_SHIFT                        6  /* BCLK_DIR */
+#define WM8904_BCLK_DIR_WIDTH                        1  /* BCLK_DIR */
+#define WM8904_AIF_LRCLK_INV                    0x0010  /* AIF_LRCLK_INV */
+#define WM8904_AIF_LRCLK_INV_MASK               0x0010  /* AIF_LRCLK_INV */
+#define WM8904_AIF_LRCLK_INV_SHIFT                   4  /* AIF_LRCLK_INV */
+#define WM8904_AIF_LRCLK_INV_WIDTH                   1  /* AIF_LRCLK_INV */
+#define WM8904_AIF_WL_MASK                      0x000C  /* AIF_WL - [3:2] */
+#define WM8904_AIF_WL_SHIFT                          2  /* AIF_WL - [3:2] */
+#define WM8904_AIF_WL_WIDTH                          2  /* AIF_WL - [3:2] */
+#define WM8904_AIF_FMT_MASK                     0x0003  /* AIF_FMT - [1:0] */
+#define WM8904_AIF_FMT_SHIFT                         0  /* AIF_FMT - [1:0] */
+#define WM8904_AIF_FMT_WIDTH                         2  /* AIF_FMT - [1:0] */
+
+/*
+ * R26 (0x1A) - Audio Interface 2
+ */
+#define WM8904_OPCLK_DIV_MASK                   0x0F00  /* OPCLK_DIV - [11:8] */
+#define WM8904_OPCLK_DIV_SHIFT                       8  /* OPCLK_DIV - [11:8] */
+#define WM8904_OPCLK_DIV_WIDTH                       4  /* OPCLK_DIV - [11:8] */
+#define WM8904_BCLK_DIV_MASK                    0x001F  /* BCLK_DIV - [4:0] */
+#define WM8904_BCLK_DIV_SHIFT                        0  /* BCLK_DIV - [4:0] */
+#define WM8904_BCLK_DIV_WIDTH                        5  /* BCLK_DIV - [4:0] */
+
+/*
+ * R27 (0x1B) - Audio Interface 3
+ */
+#define WM8904_LRCLK_DIR                        0x0800  /* LRCLK_DIR */
+#define WM8904_LRCLK_DIR_MASK                   0x0800  /* LRCLK_DIR */
+#define WM8904_LRCLK_DIR_SHIFT                      11  /* LRCLK_DIR */
+#define WM8904_LRCLK_DIR_WIDTH                       1  /* LRCLK_DIR */
+#define WM8904_LRCLK_RATE_MASK                  0x07FF  /* LRCLK_RATE - [10:0] */
+#define WM8904_LRCLK_RATE_SHIFT                      0  /* LRCLK_RATE - [10:0] */
+#define WM8904_LRCLK_RATE_WIDTH                     11  /* LRCLK_RATE - [10:0] */
+
+/*
+ * R30 (0x1E) - DAC Digital Volume Left
+ */
+#define WM8904_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8904_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8904_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8904_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8904_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8904_DACL_VOL_SHIFT                        0  /* DACL_VOL - [7:0] */
+#define WM8904_DACL_VOL_WIDTH                        8  /* DACL_VOL - [7:0] */
+
+/*
+ * R31 (0x1F) - DAC Digital Volume Right
+ */
+#define WM8904_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8904_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8904_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8904_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8904_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8904_DACR_VOL_SHIFT                        0  /* DACR_VOL - [7:0] */
+#define WM8904_DACR_VOL_WIDTH                        8  /* DACR_VOL - [7:0] */
+
+/*
+ * R32 (0x20) - DAC Digital 0
+ */
+#define WM8904_ADCL_DAC_SVOL_MASK               0x0F00  /* ADCL_DAC_SVOL - [11:8] */
+#define WM8904_ADCL_DAC_SVOL_SHIFT                   8  /* ADCL_DAC_SVOL - [11:8] */
+#define WM8904_ADCL_DAC_SVOL_WIDTH                   4  /* ADCL_DAC_SVOL - [11:8] */
+#define WM8904_ADCR_DAC_SVOL_MASK               0x00F0  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8904_ADCR_DAC_SVOL_SHIFT                   4  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8904_ADCR_DAC_SVOL_WIDTH                   4  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8904_ADC_TO_DACL_MASK                 0x000C  /* ADC_TO_DACL - [3:2] */
+#define WM8904_ADC_TO_DACL_SHIFT                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8904_ADC_TO_DACL_WIDTH                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8904_ADC_TO_DACR_MASK                 0x0003  /* ADC_TO_DACR - [1:0] */
+#define WM8904_ADC_TO_DACR_SHIFT                     0  /* ADC_TO_DACR - [1:0] */
+#define WM8904_ADC_TO_DACR_WIDTH                     2  /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R33 (0x21) - DAC Digital 1
+ */
+#define WM8904_DAC_MONO                         0x1000  /* DAC_MONO */
+#define WM8904_DAC_MONO_MASK                    0x1000  /* DAC_MONO */
+#define WM8904_DAC_MONO_SHIFT                       12  /* DAC_MONO */
+#define WM8904_DAC_MONO_WIDTH                        1  /* DAC_MONO */
+#define WM8904_DAC_SB_FILT                      0x0800  /* DAC_SB_FILT */
+#define WM8904_DAC_SB_FILT_MASK                 0x0800  /* DAC_SB_FILT */
+#define WM8904_DAC_SB_FILT_SHIFT                    11  /* DAC_SB_FILT */
+#define WM8904_DAC_SB_FILT_WIDTH                     1  /* DAC_SB_FILT */
+#define WM8904_DAC_MUTERATE                     0x0400  /* DAC_MUTERATE */
+#define WM8904_DAC_MUTERATE_MASK                0x0400  /* DAC_MUTERATE */
+#define WM8904_DAC_MUTERATE_SHIFT                   10  /* DAC_MUTERATE */
+#define WM8904_DAC_MUTERATE_WIDTH                    1  /* DAC_MUTERATE */
+#define WM8904_DAC_UNMUTE_RAMP                  0x0200  /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_UNMUTE_RAMP_MASK             0x0200  /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_UNMUTE_RAMP_SHIFT                 9  /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_UNMUTE_RAMP_WIDTH                 1  /* DAC_UNMUTE_RAMP */
+#define WM8904_DAC_OSR128                       0x0040  /* DAC_OSR128 */
+#define WM8904_DAC_OSR128_MASK                  0x0040  /* DAC_OSR128 */
+#define WM8904_DAC_OSR128_SHIFT                      6  /* DAC_OSR128 */
+#define WM8904_DAC_OSR128_WIDTH                      1  /* DAC_OSR128 */
+#define WM8904_DAC_MUTE                         0x0008  /* DAC_MUTE */
+#define WM8904_DAC_MUTE_MASK                    0x0008  /* DAC_MUTE */
+#define WM8904_DAC_MUTE_SHIFT                        3  /* DAC_MUTE */
+#define WM8904_DAC_MUTE_WIDTH                        1  /* DAC_MUTE */
+#define WM8904_DEEMPH_MASK                      0x0006  /* DEEMPH - [2:1] */
+#define WM8904_DEEMPH_SHIFT                          1  /* DEEMPH - [2:1] */
+#define WM8904_DEEMPH_WIDTH                          2  /* DEEMPH - [2:1] */
+
+/*
+ * R36 (0x24) - ADC Digital Volume Left
+ */
+#define WM8904_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8904_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8904_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8904_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8904_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8904_ADCL_VOL_SHIFT                        0  /* ADCL_VOL - [7:0] */
+#define WM8904_ADCL_VOL_WIDTH                        8  /* ADCL_VOL - [7:0] */
+
+/*
+ * R37 (0x25) - ADC Digital Volume Right
+ */
+#define WM8904_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8904_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8904_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8904_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8904_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8904_ADCR_VOL_SHIFT                        0  /* ADCR_VOL - [7:0] */
+#define WM8904_ADCR_VOL_WIDTH                        8  /* ADCR_VOL - [7:0] */
+
+/*
+ * R38 (0x26) - ADC Digital 0
+ */
+#define WM8904_ADC_HPF_CUT_MASK                 0x0060  /* ADC_HPF_CUT - [6:5] */
+#define WM8904_ADC_HPF_CUT_SHIFT                     5  /* ADC_HPF_CUT - [6:5] */
+#define WM8904_ADC_HPF_CUT_WIDTH                     2  /* ADC_HPF_CUT - [6:5] */
+#define WM8904_ADC_HPF                          0x0010  /* ADC_HPF */
+#define WM8904_ADC_HPF_MASK                     0x0010  /* ADC_HPF */
+#define WM8904_ADC_HPF_SHIFT                         4  /* ADC_HPF */
+#define WM8904_ADC_HPF_WIDTH                         1  /* ADC_HPF */
+#define WM8904_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8904_ADCL_DATINV_MASK                 0x0002  /* ADCL_DATINV */
+#define WM8904_ADCL_DATINV_SHIFT                     1  /* ADCL_DATINV */
+#define WM8904_ADCL_DATINV_WIDTH                     1  /* ADCL_DATINV */
+#define WM8904_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8904_ADCR_DATINV_MASK                 0x0001  /* ADCR_DATINV */
+#define WM8904_ADCR_DATINV_SHIFT                     0  /* ADCR_DATINV */
+#define WM8904_ADCR_DATINV_WIDTH                     1  /* ADCR_DATINV */
+
+/*
+ * R39 (0x27) - Digital Microphone 0
+ */
+#define WM8904_DMIC_ENA                         0x1000  /* DMIC_ENA */
+#define WM8904_DMIC_ENA_MASK                    0x1000  /* DMIC_ENA */
+#define WM8904_DMIC_ENA_SHIFT                       12  /* DMIC_ENA */
+#define WM8904_DMIC_ENA_WIDTH                        1  /* DMIC_ENA */
+#define WM8904_DMIC_SRC                         0x0800  /* DMIC_SRC */
+#define WM8904_DMIC_SRC_MASK                    0x0800  /* DMIC_SRC */
+#define WM8904_DMIC_SRC_SHIFT                       11  /* DMIC_SRC */
+#define WM8904_DMIC_SRC_WIDTH                        1  /* DMIC_SRC */
+
+/*
+ * R40 (0x28) - DRC 0
+ */
+#define WM8904_DRC_ENA                          0x8000  /* DRC_ENA */
+#define WM8904_DRC_ENA_MASK                     0x8000  /* DRC_ENA */
+#define WM8904_DRC_ENA_SHIFT                        15  /* DRC_ENA */
+#define WM8904_DRC_ENA_WIDTH                         1  /* DRC_ENA */
+#define WM8904_DRC_DAC_PATH                     0x4000  /* DRC_DAC_PATH */
+#define WM8904_DRC_DAC_PATH_MASK                0x4000  /* DRC_DAC_PATH */
+#define WM8904_DRC_DAC_PATH_SHIFT                   14  /* DRC_DAC_PATH */
+#define WM8904_DRC_DAC_PATH_WIDTH                    1  /* DRC_DAC_PATH */
+#define WM8904_DRC_GS_HYST_LVL_MASK             0x1800  /* DRC_GS_HYST_LVL - [12:11] */
+#define WM8904_DRC_GS_HYST_LVL_SHIFT                11  /* DRC_GS_HYST_LVL - [12:11] */
+#define WM8904_DRC_GS_HYST_LVL_WIDTH                 2  /* DRC_GS_HYST_LVL - [12:11] */
+#define WM8904_DRC_STARTUP_GAIN_MASK            0x07C0  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8904_DRC_STARTUP_GAIN_SHIFT                6  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8904_DRC_STARTUP_GAIN_WIDTH                5  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM8904_DRC_FF_DELAY                     0x0020  /* DRC_FF_DELAY */
+#define WM8904_DRC_FF_DELAY_MASK                0x0020  /* DRC_FF_DELAY */
+#define WM8904_DRC_FF_DELAY_SHIFT                    5  /* DRC_FF_DELAY */
+#define WM8904_DRC_FF_DELAY_WIDTH                    1  /* DRC_FF_DELAY */
+#define WM8904_DRC_GS_ENA                       0x0008  /* DRC_GS_ENA */
+#define WM8904_DRC_GS_ENA_MASK                  0x0008  /* DRC_GS_ENA */
+#define WM8904_DRC_GS_ENA_SHIFT                      3  /* DRC_GS_ENA */
+#define WM8904_DRC_GS_ENA_WIDTH                      1  /* DRC_GS_ENA */
+#define WM8904_DRC_QR                           0x0004  /* DRC_QR */
+#define WM8904_DRC_QR_MASK                      0x0004  /* DRC_QR */
+#define WM8904_DRC_QR_SHIFT                          2  /* DRC_QR */
+#define WM8904_DRC_QR_WIDTH                          1  /* DRC_QR */
+#define WM8904_DRC_ANTICLIP                     0x0002  /* DRC_ANTICLIP */
+#define WM8904_DRC_ANTICLIP_MASK                0x0002  /* DRC_ANTICLIP */
+#define WM8904_DRC_ANTICLIP_SHIFT                    1  /* DRC_ANTICLIP */
+#define WM8904_DRC_ANTICLIP_WIDTH                    1  /* DRC_ANTICLIP */
+#define WM8904_DRC_GS_HYST                      0x0001  /* DRC_GS_HYST */
+#define WM8904_DRC_GS_HYST_MASK                 0x0001  /* DRC_GS_HYST */
+#define WM8904_DRC_GS_HYST_SHIFT                     0  /* DRC_GS_HYST */
+#define WM8904_DRC_GS_HYST_WIDTH                     1  /* DRC_GS_HYST */
+
+/*
+ * R41 (0x29) - DRC 1
+ */
+#define WM8904_DRC_ATK_MASK                     0xF000  /* DRC_ATK - [15:12] */
+#define WM8904_DRC_ATK_SHIFT                        12  /* DRC_ATK - [15:12] */
+#define WM8904_DRC_ATK_WIDTH                         4  /* DRC_ATK - [15:12] */
+#define WM8904_DRC_DCY_MASK                     0x0F00  /* DRC_DCY - [11:8] */
+#define WM8904_DRC_DCY_SHIFT                         8  /* DRC_DCY - [11:8] */
+#define WM8904_DRC_DCY_WIDTH                         4  /* DRC_DCY - [11:8] */
+#define WM8904_DRC_QR_THR_MASK                  0x00C0  /* DRC_QR_THR - [7:6] */
+#define WM8904_DRC_QR_THR_SHIFT                      6  /* DRC_QR_THR - [7:6] */
+#define WM8904_DRC_QR_THR_WIDTH                      2  /* DRC_QR_THR - [7:6] */
+#define WM8904_DRC_QR_DCY_MASK                  0x0030  /* DRC_QR_DCY - [5:4] */
+#define WM8904_DRC_QR_DCY_SHIFT                      4  /* DRC_QR_DCY - [5:4] */
+#define WM8904_DRC_QR_DCY_WIDTH                      2  /* DRC_QR_DCY - [5:4] */
+#define WM8904_DRC_MINGAIN_MASK                 0x000C  /* DRC_MINGAIN - [3:2] */
+#define WM8904_DRC_MINGAIN_SHIFT                     2  /* DRC_MINGAIN - [3:2] */
+#define WM8904_DRC_MINGAIN_WIDTH                     2  /* DRC_MINGAIN - [3:2] */
+#define WM8904_DRC_MAXGAIN_MASK                 0x0003  /* DRC_MAXGAIN - [1:0] */
+#define WM8904_DRC_MAXGAIN_SHIFT                     0  /* DRC_MAXGAIN - [1:0] */
+#define WM8904_DRC_MAXGAIN_WIDTH                     2  /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R42 (0x2A) - DRC 2
+ */
+#define WM8904_DRC_HI_COMP_MASK                 0x0038  /* DRC_HI_COMP - [5:3] */
+#define WM8904_DRC_HI_COMP_SHIFT                     3  /* DRC_HI_COMP - [5:3] */
+#define WM8904_DRC_HI_COMP_WIDTH                     3  /* DRC_HI_COMP - [5:3] */
+#define WM8904_DRC_LO_COMP_MASK                 0x0007  /* DRC_LO_COMP - [2:0] */
+#define WM8904_DRC_LO_COMP_SHIFT                     0  /* DRC_LO_COMP - [2:0] */
+#define WM8904_DRC_LO_COMP_WIDTH                     3  /* DRC_LO_COMP - [2:0] */
+
+/*
+ * R43 (0x2B) - DRC 3
+ */
+#define WM8904_DRC_KNEE_IP_MASK                 0x07E0  /* DRC_KNEE_IP - [10:5] */
+#define WM8904_DRC_KNEE_IP_SHIFT                     5  /* DRC_KNEE_IP - [10:5] */
+#define WM8904_DRC_KNEE_IP_WIDTH                     6  /* DRC_KNEE_IP - [10:5] */
+#define WM8904_DRC_KNEE_OP_MASK                 0x001F  /* DRC_KNEE_OP - [4:0] */
+#define WM8904_DRC_KNEE_OP_SHIFT                     0  /* DRC_KNEE_OP - [4:0] */
+#define WM8904_DRC_KNEE_OP_WIDTH                     5  /* DRC_KNEE_OP - [4:0] */
+
+/*
+ * R44 (0x2C) - Analogue Left Input 0
+ */
+#define WM8904_LINMUTE                          0x0080  /* LINMUTE */
+#define WM8904_LINMUTE_MASK                     0x0080  /* LINMUTE */
+#define WM8904_LINMUTE_SHIFT                         7  /* LINMUTE */
+#define WM8904_LINMUTE_WIDTH                         1  /* LINMUTE */
+#define WM8904_LIN_VOL_MASK                     0x001F  /* LIN_VOL - [4:0] */
+#define WM8904_LIN_VOL_SHIFT                         0  /* LIN_VOL - [4:0] */
+#define WM8904_LIN_VOL_WIDTH                         5  /* LIN_VOL - [4:0] */
+
+/*
+ * R45 (0x2D) - Analogue Right Input 0
+ */
+#define WM8904_RINMUTE                          0x0080  /* RINMUTE */
+#define WM8904_RINMUTE_MASK                     0x0080  /* RINMUTE */
+#define WM8904_RINMUTE_SHIFT                         7  /* RINMUTE */
+#define WM8904_RINMUTE_WIDTH                         1  /* RINMUTE */
+#define WM8904_RIN_VOL_MASK                     0x001F  /* RIN_VOL - [4:0] */
+#define WM8904_RIN_VOL_SHIFT                         0  /* RIN_VOL - [4:0] */
+#define WM8904_RIN_VOL_WIDTH                         5  /* RIN_VOL - [4:0] */
+
+/*
+ * R46 (0x2E) - Analogue Left Input 1
+ */
+#define WM8904_INL_CM_ENA                       0x0040  /* INL_CM_ENA */
+#define WM8904_INL_CM_ENA_MASK                  0x0040  /* INL_CM_ENA */
+#define WM8904_INL_CM_ENA_SHIFT                      6  /* INL_CM_ENA */
+#define WM8904_INL_CM_ENA_WIDTH                      1  /* INL_CM_ENA */
+#define WM8904_L_IP_SEL_N_MASK                  0x0030  /* L_IP_SEL_N - [5:4] */
+#define WM8904_L_IP_SEL_N_SHIFT                      4  /* L_IP_SEL_N - [5:4] */
+#define WM8904_L_IP_SEL_N_WIDTH                      2  /* L_IP_SEL_N - [5:4] */
+#define WM8904_L_IP_SEL_P_MASK                  0x000C  /* L_IP_SEL_P - [3:2] */
+#define WM8904_L_IP_SEL_P_SHIFT                      2  /* L_IP_SEL_P - [3:2] */
+#define WM8904_L_IP_SEL_P_WIDTH                      2  /* L_IP_SEL_P - [3:2] */
+#define WM8904_L_MODE_MASK                      0x0003  /* L_MODE - [1:0] */
+#define WM8904_L_MODE_SHIFT                          0  /* L_MODE - [1:0] */
+#define WM8904_L_MODE_WIDTH                          2  /* L_MODE - [1:0] */
+
+/*
+ * R47 (0x2F) - Analogue Right Input 1
+ */
+#define WM8904_INR_CM_ENA                       0x0040  /* INR_CM_ENA */
+#define WM8904_INR_CM_ENA_MASK                  0x0040  /* INR_CM_ENA */
+#define WM8904_INR_CM_ENA_SHIFT                      6  /* INR_CM_ENA */
+#define WM8904_INR_CM_ENA_WIDTH                      1  /* INR_CM_ENA */
+#define WM8904_R_IP_SEL_N_MASK                  0x0030  /* R_IP_SEL_N - [5:4] */
+#define WM8904_R_IP_SEL_N_SHIFT                      4  /* R_IP_SEL_N - [5:4] */
+#define WM8904_R_IP_SEL_N_WIDTH                      2  /* R_IP_SEL_N - [5:4] */
+#define WM8904_R_IP_SEL_P_MASK                  0x000C  /* R_IP_SEL_P - [3:2] */
+#define WM8904_R_IP_SEL_P_SHIFT                      2  /* R_IP_SEL_P - [3:2] */
+#define WM8904_R_IP_SEL_P_WIDTH                      2  /* R_IP_SEL_P - [3:2] */
+#define WM8904_R_MODE_MASK                      0x0003  /* R_MODE - [1:0] */
+#define WM8904_R_MODE_SHIFT                          0  /* R_MODE - [1:0] */
+#define WM8904_R_MODE_WIDTH                          2  /* R_MODE - [1:0] */
+
+/*
+ * R57 (0x39) - Analogue OUT1 Left
+ */
+#define WM8904_HPOUTL_MUTE                      0x0100  /* HPOUTL_MUTE */
+#define WM8904_HPOUTL_MUTE_MASK                 0x0100  /* HPOUTL_MUTE */
+#define WM8904_HPOUTL_MUTE_SHIFT                     8  /* HPOUTL_MUTE */
+#define WM8904_HPOUTL_MUTE_WIDTH                     1  /* HPOUTL_MUTE */
+#define WM8904_HPOUT_VU                         0x0080  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_MASK                    0x0080  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_SHIFT                        7  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_WIDTH                        1  /* HPOUT_VU */
+#define WM8904_HPOUTLZC                         0x0040  /* HPOUTLZC */
+#define WM8904_HPOUTLZC_MASK                    0x0040  /* HPOUTLZC */
+#define WM8904_HPOUTLZC_SHIFT                        6  /* HPOUTLZC */
+#define WM8904_HPOUTLZC_WIDTH                        1  /* HPOUTLZC */
+#define WM8904_HPOUTL_VOL_MASK                  0x003F  /* HPOUTL_VOL - [5:0] */
+#define WM8904_HPOUTL_VOL_SHIFT                      0  /* HPOUTL_VOL - [5:0] */
+#define WM8904_HPOUTL_VOL_WIDTH                      6  /* HPOUTL_VOL - [5:0] */
+
+/*
+ * R58 (0x3A) - Analogue OUT1 Right
+ */
+#define WM8904_HPOUTR_MUTE                      0x0100  /* HPOUTR_MUTE */
+#define WM8904_HPOUTR_MUTE_MASK                 0x0100  /* HPOUTR_MUTE */
+#define WM8904_HPOUTR_MUTE_SHIFT                     8  /* HPOUTR_MUTE */
+#define WM8904_HPOUTR_MUTE_WIDTH                     1  /* HPOUTR_MUTE */
+#define WM8904_HPOUT_VU                         0x0080  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_MASK                    0x0080  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_SHIFT                        7  /* HPOUT_VU */
+#define WM8904_HPOUT_VU_WIDTH                        1  /* HPOUT_VU */
+#define WM8904_HPOUTRZC                         0x0040  /* HPOUTRZC */
+#define WM8904_HPOUTRZC_MASK                    0x0040  /* HPOUTRZC */
+#define WM8904_HPOUTRZC_SHIFT                        6  /* HPOUTRZC */
+#define WM8904_HPOUTRZC_WIDTH                        1  /* HPOUTRZC */
+#define WM8904_HPOUTR_VOL_MASK                  0x003F  /* HPOUTR_VOL - [5:0] */
+#define WM8904_HPOUTR_VOL_SHIFT                      0  /* HPOUTR_VOL - [5:0] */
+#define WM8904_HPOUTR_VOL_WIDTH                      6  /* HPOUTR_VOL - [5:0] */
+
+/*
+ * R59 (0x3B) - Analogue OUT2 Left
+ */
+#define WM8904_LINEOUTL_MUTE                    0x0100  /* LINEOUTL_MUTE */
+#define WM8904_LINEOUTL_MUTE_MASK               0x0100  /* LINEOUTL_MUTE */
+#define WM8904_LINEOUTL_MUTE_SHIFT                   8  /* LINEOUTL_MUTE */
+#define WM8904_LINEOUTL_MUTE_WIDTH                   1  /* LINEOUTL_MUTE */
+#define WM8904_LINEOUT_VU                       0x0080  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_MASK                  0x0080  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_SHIFT                      7  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_WIDTH                      1  /* LINEOUT_VU */
+#define WM8904_LINEOUTLZC                       0x0040  /* LINEOUTLZC */
+#define WM8904_LINEOUTLZC_MASK                  0x0040  /* LINEOUTLZC */
+#define WM8904_LINEOUTLZC_SHIFT                      6  /* LINEOUTLZC */
+#define WM8904_LINEOUTLZC_WIDTH                      1  /* LINEOUTLZC */
+#define WM8904_LINEOUTL_VOL_MASK                0x003F  /* LINEOUTL_VOL - [5:0] */
+#define WM8904_LINEOUTL_VOL_SHIFT                    0  /* LINEOUTL_VOL - [5:0] */
+#define WM8904_LINEOUTL_VOL_WIDTH                    6  /* LINEOUTL_VOL - [5:0] */
+
+/*
+ * R60 (0x3C) - Analogue OUT2 Right
+ */
+#define WM8904_LINEOUTR_MUTE                    0x0100  /* LINEOUTR_MUTE */
+#define WM8904_LINEOUTR_MUTE_MASK               0x0100  /* LINEOUTR_MUTE */
+#define WM8904_LINEOUTR_MUTE_SHIFT                   8  /* LINEOUTR_MUTE */
+#define WM8904_LINEOUTR_MUTE_WIDTH                   1  /* LINEOUTR_MUTE */
+#define WM8904_LINEOUT_VU                       0x0080  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_MASK                  0x0080  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_SHIFT                      7  /* LINEOUT_VU */
+#define WM8904_LINEOUT_VU_WIDTH                      1  /* LINEOUT_VU */
+#define WM8904_LINEOUTRZC                       0x0040  /* LINEOUTRZC */
+#define WM8904_LINEOUTRZC_MASK                  0x0040  /* LINEOUTRZC */
+#define WM8904_LINEOUTRZC_SHIFT                      6  /* LINEOUTRZC */
+#define WM8904_LINEOUTRZC_WIDTH                      1  /* LINEOUTRZC */
+#define WM8904_LINEOUTR_VOL_MASK                0x003F  /* LINEOUTR_VOL - [5:0] */
+#define WM8904_LINEOUTR_VOL_SHIFT                    0  /* LINEOUTR_VOL - [5:0] */
+#define WM8904_LINEOUTR_VOL_WIDTH                    6  /* LINEOUTR_VOL - [5:0] */
+
+/*
+ * R61 (0x3D) - Analogue OUT12 ZC
+ */
+#define WM8904_HPL_BYP_ENA                      0x0008  /* HPL_BYP_ENA */
+#define WM8904_HPL_BYP_ENA_MASK                 0x0008  /* HPL_BYP_ENA */
+#define WM8904_HPL_BYP_ENA_SHIFT                     3  /* HPL_BYP_ENA */
+#define WM8904_HPL_BYP_ENA_WIDTH                     1  /* HPL_BYP_ENA */
+#define WM8904_HPR_BYP_ENA                      0x0004  /* HPR_BYP_ENA */
+#define WM8904_HPR_BYP_ENA_MASK                 0x0004  /* HPR_BYP_ENA */
+#define WM8904_HPR_BYP_ENA_SHIFT                     2  /* HPR_BYP_ENA */
+#define WM8904_HPR_BYP_ENA_WIDTH                     1  /* HPR_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA                 0x0002  /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA_MASK            0x0002  /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA_SHIFT                1  /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTL_BYP_ENA_WIDTH                1  /* LINEOUTL_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA                 0x0001  /* LINEOUTR_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA_MASK            0x0001  /* LINEOUTR_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA_SHIFT                0  /* LINEOUTR_BYP_ENA */
+#define WM8904_LINEOUTR_BYP_ENA_WIDTH                1  /* LINEOUTR_BYP_ENA */
+
+/*
+ * R67 (0x43) - DC Servo 0
+ */
+#define WM8904_DCS_ENA_CHAN_3                   0x0008  /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_3_MASK              0x0008  /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_3_SHIFT                  3  /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_3_WIDTH                  1  /* DCS_ENA_CHAN_3 */
+#define WM8904_DCS_ENA_CHAN_2                   0x0004  /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_2_MASK              0x0004  /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_2_SHIFT                  2  /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_2_WIDTH                  1  /* DCS_ENA_CHAN_2 */
+#define WM8904_DCS_ENA_CHAN_1                   0x0002  /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_1_MASK              0x0002  /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_1_SHIFT                  1  /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_1_WIDTH                  1  /* DCS_ENA_CHAN_1 */
+#define WM8904_DCS_ENA_CHAN_0                   0x0001  /* DCS_ENA_CHAN_0 */
+#define WM8904_DCS_ENA_CHAN_0_MASK              0x0001  /* DCS_ENA_CHAN_0 */
+#define WM8904_DCS_ENA_CHAN_0_SHIFT                  0  /* DCS_ENA_CHAN_0 */
+#define WM8904_DCS_ENA_CHAN_0_WIDTH                  1  /* DCS_ENA_CHAN_0 */
+
+/*
+ * R68 (0x44) - DC Servo 1
+ */
+#define WM8904_DCS_TRIG_SINGLE_3                0x8000  /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_3_MASK           0x8000  /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_3_SHIFT              15  /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_3_WIDTH               1  /* DCS_TRIG_SINGLE_3 */
+#define WM8904_DCS_TRIG_SINGLE_2                0x4000  /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_2_MASK           0x4000  /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_2_SHIFT              14  /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_2_WIDTH               1  /* DCS_TRIG_SINGLE_2 */
+#define WM8904_DCS_TRIG_SINGLE_1                0x2000  /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_1_MASK           0x2000  /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_1_SHIFT              13  /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_1_WIDTH               1  /* DCS_TRIG_SINGLE_1 */
+#define WM8904_DCS_TRIG_SINGLE_0                0x1000  /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SINGLE_0_MASK           0x1000  /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SINGLE_0_SHIFT              12  /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SINGLE_0_WIDTH               1  /* DCS_TRIG_SINGLE_0 */
+#define WM8904_DCS_TRIG_SERIES_3                0x0800  /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_3_MASK           0x0800  /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_3_SHIFT              11  /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_3_WIDTH               1  /* DCS_TRIG_SERIES_3 */
+#define WM8904_DCS_TRIG_SERIES_2                0x0400  /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_2_MASK           0x0400  /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_2_SHIFT              10  /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_2_WIDTH               1  /* DCS_TRIG_SERIES_2 */
+#define WM8904_DCS_TRIG_SERIES_1                0x0200  /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_1_MASK           0x0200  /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_1_SHIFT               9  /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_1_WIDTH               1  /* DCS_TRIG_SERIES_1 */
+#define WM8904_DCS_TRIG_SERIES_0                0x0100  /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_SERIES_0_MASK           0x0100  /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_SERIES_0_SHIFT               8  /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_SERIES_0_WIDTH               1  /* DCS_TRIG_SERIES_0 */
+#define WM8904_DCS_TRIG_STARTUP_3               0x0080  /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_3_MASK          0x0080  /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_3_SHIFT              7  /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_3_WIDTH              1  /* DCS_TRIG_STARTUP_3 */
+#define WM8904_DCS_TRIG_STARTUP_2               0x0040  /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_2_MASK          0x0040  /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_2_SHIFT              6  /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_2_WIDTH              1  /* DCS_TRIG_STARTUP_2 */
+#define WM8904_DCS_TRIG_STARTUP_1               0x0020  /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_1_MASK          0x0020  /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_1_SHIFT              5  /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_1_WIDTH              1  /* DCS_TRIG_STARTUP_1 */
+#define WM8904_DCS_TRIG_STARTUP_0               0x0010  /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_STARTUP_0_MASK          0x0010  /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_STARTUP_0_SHIFT              4  /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_STARTUP_0_WIDTH              1  /* DCS_TRIG_STARTUP_0 */
+#define WM8904_DCS_TRIG_DAC_WR_3                0x0008  /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_3_MASK           0x0008  /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_3_SHIFT               3  /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_3_WIDTH               1  /* DCS_TRIG_DAC_WR_3 */
+#define WM8904_DCS_TRIG_DAC_WR_2                0x0004  /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_2_MASK           0x0004  /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_2_SHIFT               2  /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_2_WIDTH               1  /* DCS_TRIG_DAC_WR_2 */
+#define WM8904_DCS_TRIG_DAC_WR_1                0x0002  /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_1_MASK           0x0002  /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_1_SHIFT               1  /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_1_WIDTH               1  /* DCS_TRIG_DAC_WR_1 */
+#define WM8904_DCS_TRIG_DAC_WR_0                0x0001  /* DCS_TRIG_DAC_WR_0 */
+#define WM8904_DCS_TRIG_DAC_WR_0_MASK           0x0001  /* DCS_TRIG_DAC_WR_0 */
+#define WM8904_DCS_TRIG_DAC_WR_0_SHIFT               0  /* DCS_TRIG_DAC_WR_0 */
+#define WM8904_DCS_TRIG_DAC_WR_0_WIDTH               1  /* DCS_TRIG_DAC_WR_0 */
+
+/*
+ * R69 (0x45) - DC Servo 2
+ */
+#define WM8904_DCS_TIMER_PERIOD_23_MASK         0x0F00  /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8904_DCS_TIMER_PERIOD_23_SHIFT             8  /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8904_DCS_TIMER_PERIOD_23_WIDTH             4  /* DCS_TIMER_PERIOD_23 - [11:8] */
+#define WM8904_DCS_TIMER_PERIOD_01_MASK         0x000F  /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8904_DCS_TIMER_PERIOD_01_SHIFT             0  /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8904_DCS_TIMER_PERIOD_01_WIDTH             4  /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R71 (0x47) - DC Servo 4
+ */
+#define WM8904_DCS_SERIES_NO_23_MASK            0x007F  /* DCS_SERIES_NO_23 - [6:0] */
+#define WM8904_DCS_SERIES_NO_23_SHIFT                0  /* DCS_SERIES_NO_23 - [6:0] */
+#define WM8904_DCS_SERIES_NO_23_WIDTH                7  /* DCS_SERIES_NO_23 - [6:0] */
+
+/*
+ * R72 (0x48) - DC Servo 5
+ */
+#define WM8904_DCS_SERIES_NO_01_MASK            0x007F  /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8904_DCS_SERIES_NO_01_SHIFT                0  /* DCS_SERIES_NO_01 - [6:0] */
+#define WM8904_DCS_SERIES_NO_01_WIDTH                7  /* DCS_SERIES_NO_01 - [6:0] */
+
+/*
+ * R73 (0x49) - DC Servo 6
+ */
+#define WM8904_DCS_DAC_WR_VAL_3_MASK            0x00FF  /* DCS_DAC_WR_VAL_3 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_3_SHIFT                0  /* DCS_DAC_WR_VAL_3 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_3_WIDTH                8  /* DCS_DAC_WR_VAL_3 - [7:0] */
+
+/*
+ * R74 (0x4A) - DC Servo 7
+ */
+#define WM8904_DCS_DAC_WR_VAL_2_MASK            0x00FF  /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_2_SHIFT                0  /* DCS_DAC_WR_VAL_2 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_2_WIDTH                8  /* DCS_DAC_WR_VAL_2 - [7:0] */
+
+/*
+ * R75 (0x4B) - DC Servo 8
+ */
+#define WM8904_DCS_DAC_WR_VAL_1_MASK            0x00FF  /* DCS_DAC_WR_VAL_1 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_1_SHIFT                0  /* DCS_DAC_WR_VAL_1 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_1_WIDTH                8  /* DCS_DAC_WR_VAL_1 - [7:0] */
+
+/*
+ * R76 (0x4C) - DC Servo 9
+ */
+#define WM8904_DCS_DAC_WR_VAL_0_MASK            0x00FF  /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_0_SHIFT                0  /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8904_DCS_DAC_WR_VAL_0_WIDTH                8  /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R77 (0x4D) - DC Servo Readback 0
+ */
+#define WM8904_DCS_CAL_COMPLETE_MASK            0x0F00  /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8904_DCS_CAL_COMPLETE_SHIFT                8  /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8904_DCS_CAL_COMPLETE_WIDTH                4  /* DCS_CAL_COMPLETE - [11:8] */
+#define WM8904_DCS_DAC_WR_COMPLETE_MASK         0x00F0  /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8904_DCS_DAC_WR_COMPLETE_SHIFT             4  /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8904_DCS_DAC_WR_COMPLETE_WIDTH             4  /* DCS_DAC_WR_COMPLETE - [7:4] */
+#define WM8904_DCS_STARTUP_COMPLETE_MASK        0x000F  /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8904_DCS_STARTUP_COMPLETE_SHIFT            0  /* DCS_STARTUP_COMPLETE - [3:0] */
+#define WM8904_DCS_STARTUP_COMPLETE_WIDTH            4  /* DCS_STARTUP_COMPLETE - [3:0] */
+
+/*
+ * R90 (0x5A) - Analogue HP 0
+ */
+#define WM8904_HPL_RMV_SHORT                    0x0080  /* HPL_RMV_SHORT */
+#define WM8904_HPL_RMV_SHORT_MASK               0x0080  /* HPL_RMV_SHORT */
+#define WM8904_HPL_RMV_SHORT_SHIFT                   7  /* HPL_RMV_SHORT */
+#define WM8904_HPL_RMV_SHORT_WIDTH                   1  /* HPL_RMV_SHORT */
+#define WM8904_HPL_ENA_OUTP                     0x0040  /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_OUTP_MASK                0x0040  /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_OUTP_SHIFT                    6  /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_OUTP_WIDTH                    1  /* HPL_ENA_OUTP */
+#define WM8904_HPL_ENA_DLY                      0x0020  /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA_DLY_MASK                 0x0020  /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA_DLY_SHIFT                     5  /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA_DLY_WIDTH                     1  /* HPL_ENA_DLY */
+#define WM8904_HPL_ENA                          0x0010  /* HPL_ENA */
+#define WM8904_HPL_ENA_MASK                     0x0010  /* HPL_ENA */
+#define WM8904_HPL_ENA_SHIFT                         4  /* HPL_ENA */
+#define WM8904_HPL_ENA_WIDTH                         1  /* HPL_ENA */
+#define WM8904_HPR_RMV_SHORT                    0x0008  /* HPR_RMV_SHORT */
+#define WM8904_HPR_RMV_SHORT_MASK               0x0008  /* HPR_RMV_SHORT */
+#define WM8904_HPR_RMV_SHORT_SHIFT                   3  /* HPR_RMV_SHORT */
+#define WM8904_HPR_RMV_SHORT_WIDTH                   1  /* HPR_RMV_SHORT */
+#define WM8904_HPR_ENA_OUTP                     0x0004  /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_OUTP_MASK                0x0004  /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_OUTP_SHIFT                    2  /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_OUTP_WIDTH                    1  /* HPR_ENA_OUTP */
+#define WM8904_HPR_ENA_DLY                      0x0002  /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA_DLY_MASK                 0x0002  /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA_DLY_SHIFT                     1  /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA_DLY_WIDTH                     1  /* HPR_ENA_DLY */
+#define WM8904_HPR_ENA                          0x0001  /* HPR_ENA */
+#define WM8904_HPR_ENA_MASK                     0x0001  /* HPR_ENA */
+#define WM8904_HPR_ENA_SHIFT                         0  /* HPR_ENA */
+#define WM8904_HPR_ENA_WIDTH                         1  /* HPR_ENA */
+
+/*
+ * R94 (0x5E) - Analogue Lineout 0
+ */
+#define WM8904_LINEOUTL_RMV_SHORT               0x0080  /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_RMV_SHORT_MASK          0x0080  /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_RMV_SHORT_SHIFT              7  /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_RMV_SHORT_WIDTH              1  /* LINEOUTL_RMV_SHORT */
+#define WM8904_LINEOUTL_ENA_OUTP                0x0040  /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_OUTP_MASK           0x0040  /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_OUTP_SHIFT               6  /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_OUTP_WIDTH               1  /* LINEOUTL_ENA_OUTP */
+#define WM8904_LINEOUTL_ENA_DLY                 0x0020  /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA_DLY_MASK            0x0020  /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA_DLY_SHIFT                5  /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA_DLY_WIDTH                1  /* LINEOUTL_ENA_DLY */
+#define WM8904_LINEOUTL_ENA                     0x0010  /* LINEOUTL_ENA */
+#define WM8904_LINEOUTL_ENA_MASK                0x0010  /* LINEOUTL_ENA */
+#define WM8904_LINEOUTL_ENA_SHIFT                    4  /* LINEOUTL_ENA */
+#define WM8904_LINEOUTL_ENA_WIDTH                    1  /* LINEOUTL_ENA */
+#define WM8904_LINEOUTR_RMV_SHORT               0x0008  /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_RMV_SHORT_MASK          0x0008  /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_RMV_SHORT_SHIFT              3  /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_RMV_SHORT_WIDTH              1  /* LINEOUTR_RMV_SHORT */
+#define WM8904_LINEOUTR_ENA_OUTP                0x0004  /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_OUTP_MASK           0x0004  /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_OUTP_SHIFT               2  /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_OUTP_WIDTH               1  /* LINEOUTR_ENA_OUTP */
+#define WM8904_LINEOUTR_ENA_DLY                 0x0002  /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA_DLY_MASK            0x0002  /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA_DLY_SHIFT                1  /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA_DLY_WIDTH                1  /* LINEOUTR_ENA_DLY */
+#define WM8904_LINEOUTR_ENA                     0x0001  /* LINEOUTR_ENA */
+#define WM8904_LINEOUTR_ENA_MASK                0x0001  /* LINEOUTR_ENA */
+#define WM8904_LINEOUTR_ENA_SHIFT                    0  /* LINEOUTR_ENA */
+#define WM8904_LINEOUTR_ENA_WIDTH                    1  /* LINEOUTR_ENA */
+
+/*
+ * R98 (0x62) - Charge Pump 0
+ */
+#define WM8904_CP_ENA                           0x0001  /* CP_ENA */
+#define WM8904_CP_ENA_MASK                      0x0001  /* CP_ENA */
+#define WM8904_CP_ENA_SHIFT                          0  /* CP_ENA */
+#define WM8904_CP_ENA_WIDTH                          1  /* CP_ENA */
+
+/*
+ * R104 (0x68) - Class W 0
+ */
+#define WM8904_CP_DYN_PWR                       0x0001  /* CP_DYN_PWR */
+#define WM8904_CP_DYN_PWR_MASK                  0x0001  /* CP_DYN_PWR */
+#define WM8904_CP_DYN_PWR_SHIFT                      0  /* CP_DYN_PWR */
+#define WM8904_CP_DYN_PWR_WIDTH                      1  /* CP_DYN_PWR */
+
+/*
+ * R108 (0x6C) - Write Sequencer 0
+ */
+#define WM8904_WSEQ_ENA                         0x0100  /* WSEQ_ENA */
+#define WM8904_WSEQ_ENA_MASK                    0x0100  /* WSEQ_ENA */
+#define WM8904_WSEQ_ENA_SHIFT                        8  /* WSEQ_ENA */
+#define WM8904_WSEQ_ENA_WIDTH                        1  /* WSEQ_ENA */
+#define WM8904_WSEQ_WRITE_INDEX_MASK            0x001F  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8904_WSEQ_WRITE_INDEX_SHIFT                0  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8904_WSEQ_WRITE_INDEX_WIDTH                5  /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R109 (0x6D) - Write Sequencer 1
+ */
+#define WM8904_WSEQ_DATA_WIDTH_MASK             0x7000  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8904_WSEQ_DATA_WIDTH_SHIFT                12  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8904_WSEQ_DATA_WIDTH_WIDTH                 3  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8904_WSEQ_DATA_START_MASK             0x0F00  /* WSEQ_DATA_START - [11:8] */
+#define WM8904_WSEQ_DATA_START_SHIFT                 8  /* WSEQ_DATA_START - [11:8] */
+#define WM8904_WSEQ_DATA_START_WIDTH                 4  /* WSEQ_DATA_START - [11:8] */
+#define WM8904_WSEQ_ADDR_MASK                   0x00FF  /* WSEQ_ADDR - [7:0] */
+#define WM8904_WSEQ_ADDR_SHIFT                       0  /* WSEQ_ADDR - [7:0] */
+#define WM8904_WSEQ_ADDR_WIDTH                       8  /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R110 (0x6E) - Write Sequencer 2
+ */
+#define WM8904_WSEQ_EOS                         0x4000  /* WSEQ_EOS */
+#define WM8904_WSEQ_EOS_MASK                    0x4000  /* WSEQ_EOS */
+#define WM8904_WSEQ_EOS_SHIFT                       14  /* WSEQ_EOS */
+#define WM8904_WSEQ_EOS_WIDTH                        1  /* WSEQ_EOS */
+#define WM8904_WSEQ_DELAY_MASK                  0x0F00  /* WSEQ_DELAY - [11:8] */
+#define WM8904_WSEQ_DELAY_SHIFT                      8  /* WSEQ_DELAY - [11:8] */
+#define WM8904_WSEQ_DELAY_WIDTH                      4  /* WSEQ_DELAY - [11:8] */
+#define WM8904_WSEQ_DATA_MASK                   0x00FF  /* WSEQ_DATA - [7:0] */
+#define WM8904_WSEQ_DATA_SHIFT                       0  /* WSEQ_DATA - [7:0] */
+#define WM8904_WSEQ_DATA_WIDTH                       8  /* WSEQ_DATA - [7:0] */
+
+/*
+ * R111 (0x6F) - Write Sequencer 3
+ */
+#define WM8904_WSEQ_ABORT                       0x0200  /* WSEQ_ABORT */
+#define WM8904_WSEQ_ABORT_MASK                  0x0200  /* WSEQ_ABORT */
+#define WM8904_WSEQ_ABORT_SHIFT                      9  /* WSEQ_ABORT */
+#define WM8904_WSEQ_ABORT_WIDTH                      1  /* WSEQ_ABORT */
+#define WM8904_WSEQ_START                       0x0100  /* WSEQ_START */
+#define WM8904_WSEQ_START_MASK                  0x0100  /* WSEQ_START */
+#define WM8904_WSEQ_START_SHIFT                      8  /* WSEQ_START */
+#define WM8904_WSEQ_START_WIDTH                      1  /* WSEQ_START */
+#define WM8904_WSEQ_START_INDEX_MASK            0x003F  /* WSEQ_START_INDEX - [5:0] */
+#define WM8904_WSEQ_START_INDEX_SHIFT                0  /* WSEQ_START_INDEX - [5:0] */
+#define WM8904_WSEQ_START_INDEX_WIDTH                6  /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R112 (0x70) - Write Sequencer 4
+ */
+#define WM8904_WSEQ_CURRENT_INDEX_MASK          0x03F0  /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8904_WSEQ_CURRENT_INDEX_SHIFT              4  /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8904_WSEQ_CURRENT_INDEX_WIDTH              6  /* WSEQ_CURRENT_INDEX - [9:4] */
+#define WM8904_WSEQ_BUSY                        0x0001  /* WSEQ_BUSY */
+#define WM8904_WSEQ_BUSY_MASK                   0x0001  /* WSEQ_BUSY */
+#define WM8904_WSEQ_BUSY_SHIFT                       0  /* WSEQ_BUSY */
+#define WM8904_WSEQ_BUSY_WIDTH                       1  /* WSEQ_BUSY */
+
+/*
+ * R116 (0x74) - FLL Control 1
+ */
+#define WM8904_FLL_FRACN_ENA                    0x0004  /* FLL_FRACN_ENA */
+#define WM8904_FLL_FRACN_ENA_MASK               0x0004  /* FLL_FRACN_ENA */
+#define WM8904_FLL_FRACN_ENA_SHIFT                   2  /* FLL_FRACN_ENA */
+#define WM8904_FLL_FRACN_ENA_WIDTH                   1  /* FLL_FRACN_ENA */
+#define WM8904_FLL_OSC_ENA                      0x0002  /* FLL_OSC_ENA */
+#define WM8904_FLL_OSC_ENA_MASK                 0x0002  /* FLL_OSC_ENA */
+#define WM8904_FLL_OSC_ENA_SHIFT                     1  /* FLL_OSC_ENA */
+#define WM8904_FLL_OSC_ENA_WIDTH                     1  /* FLL_OSC_ENA */
+#define WM8904_FLL_ENA                          0x0001  /* FLL_ENA */
+#define WM8904_FLL_ENA_MASK                     0x0001  /* FLL_ENA */
+#define WM8904_FLL_ENA_SHIFT                         0  /* FLL_ENA */
+#define WM8904_FLL_ENA_WIDTH                         1  /* FLL_ENA */
+
+/*
+ * R117 (0x75) - FLL Control 2
+ */
+#define WM8904_FLL_OUTDIV_MASK                  0x3F00  /* FLL_OUTDIV - [13:8] */
+#define WM8904_FLL_OUTDIV_SHIFT                      8  /* FLL_OUTDIV - [13:8] */
+#define WM8904_FLL_OUTDIV_WIDTH                      6  /* FLL_OUTDIV - [13:8] */
+#define WM8904_FLL_CTRL_RATE_MASK               0x0070  /* FLL_CTRL_RATE - [6:4] */
+#define WM8904_FLL_CTRL_RATE_SHIFT                   4  /* FLL_CTRL_RATE - [6:4] */
+#define WM8904_FLL_CTRL_RATE_WIDTH                   3  /* FLL_CTRL_RATE - [6:4] */
+#define WM8904_FLL_FRATIO_MASK                  0x0007  /* FLL_FRATIO - [2:0] */
+#define WM8904_FLL_FRATIO_SHIFT                      0  /* FLL_FRATIO - [2:0] */
+#define WM8904_FLL_FRATIO_WIDTH                      3  /* FLL_FRATIO - [2:0] */
+
+/*
+ * R118 (0x76) - FLL Control 3
+ */
+#define WM8904_FLL_K_MASK                       0xFFFF  /* FLL_K - [15:0] */
+#define WM8904_FLL_K_SHIFT                           0  /* FLL_K - [15:0] */
+#define WM8904_FLL_K_WIDTH                          16  /* FLL_K - [15:0] */
+
+/*
+ * R119 (0x77) - FLL Control 4
+ */
+#define WM8904_FLL_N_MASK                       0x7FE0  /* FLL_N - [14:5] */
+#define WM8904_FLL_N_SHIFT                           5  /* FLL_N - [14:5] */
+#define WM8904_FLL_N_WIDTH                          10  /* FLL_N - [14:5] */
+#define WM8904_FLL_GAIN_MASK                    0x000F  /* FLL_GAIN - [3:0] */
+#define WM8904_FLL_GAIN_SHIFT                        0  /* FLL_GAIN - [3:0] */
+#define WM8904_FLL_GAIN_WIDTH                        4  /* FLL_GAIN - [3:0] */
+
+/*
+ * R120 (0x78) - FLL Control 5
+ */
+#define WM8904_FLL_CLK_REF_DIV_MASK             0x0018  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8904_FLL_CLK_REF_DIV_SHIFT                 3  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8904_FLL_CLK_REF_DIV_WIDTH                 2  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8904_FLL_CLK_REF_SRC_MASK             0x0003  /* FLL_CLK_REF_SRC - [1:0] */
+#define WM8904_FLL_CLK_REF_SRC_SHIFT                 0  /* FLL_CLK_REF_SRC - [1:0] */
+#define WM8904_FLL_CLK_REF_SRC_WIDTH                 2  /* FLL_CLK_REF_SRC - [1:0] */
+
+/*
+ * R121 (0x79) - GPIO Control 1
+ */
+#define WM8904_GPIO1_PU                         0x0020  /* GPIO1_PU */
+#define WM8904_GPIO1_PU_MASK                    0x0020  /* GPIO1_PU */
+#define WM8904_GPIO1_PU_SHIFT                        5  /* GPIO1_PU */
+#define WM8904_GPIO1_PU_WIDTH                        1  /* GPIO1_PU */
+#define WM8904_GPIO1_PD                         0x0010  /* GPIO1_PD */
+#define WM8904_GPIO1_PD_MASK                    0x0010  /* GPIO1_PD */
+#define WM8904_GPIO1_PD_SHIFT                        4  /* GPIO1_PD */
+#define WM8904_GPIO1_PD_WIDTH                        1  /* GPIO1_PD */
+#define WM8904_GPIO1_SEL_MASK                   0x000F  /* GPIO1_SEL - [3:0] */
+#define WM8904_GPIO1_SEL_SHIFT                       0  /* GPIO1_SEL - [3:0] */
+#define WM8904_GPIO1_SEL_WIDTH                       4  /* GPIO1_SEL - [3:0] */
+
+/*
+ * R122 (0x7A) - GPIO Control 2
+ */
+#define WM8904_GPIO2_PU                         0x0020  /* GPIO2_PU */
+#define WM8904_GPIO2_PU_MASK                    0x0020  /* GPIO2_PU */
+#define WM8904_GPIO2_PU_SHIFT                        5  /* GPIO2_PU */
+#define WM8904_GPIO2_PU_WIDTH                        1  /* GPIO2_PU */
+#define WM8904_GPIO2_PD                         0x0010  /* GPIO2_PD */
+#define WM8904_GPIO2_PD_MASK                    0x0010  /* GPIO2_PD */
+#define WM8904_GPIO2_PD_SHIFT                        4  /* GPIO2_PD */
+#define WM8904_GPIO2_PD_WIDTH                        1  /* GPIO2_PD */
+#define WM8904_GPIO2_SEL_MASK                   0x000F  /* GPIO2_SEL - [3:0] */
+#define WM8904_GPIO2_SEL_SHIFT                       0  /* GPIO2_SEL - [3:0] */
+#define WM8904_GPIO2_SEL_WIDTH                       4  /* GPIO2_SEL - [3:0] */
+
+/*
+ * R123 (0x7B) - GPIO Control 3
+ */
+#define WM8904_GPIO3_PU                         0x0020  /* GPIO3_PU */
+#define WM8904_GPIO3_PU_MASK                    0x0020  /* GPIO3_PU */
+#define WM8904_GPIO3_PU_SHIFT                        5  /* GPIO3_PU */
+#define WM8904_GPIO3_PU_WIDTH                        1  /* GPIO3_PU */
+#define WM8904_GPIO3_PD                         0x0010  /* GPIO3_PD */
+#define WM8904_GPIO3_PD_MASK                    0x0010  /* GPIO3_PD */
+#define WM8904_GPIO3_PD_SHIFT                        4  /* GPIO3_PD */
+#define WM8904_GPIO3_PD_WIDTH                        1  /* GPIO3_PD */
+#define WM8904_GPIO3_SEL_MASK                   0x000F  /* GPIO3_SEL - [3:0] */
+#define WM8904_GPIO3_SEL_SHIFT                       0  /* GPIO3_SEL - [3:0] */
+#define WM8904_GPIO3_SEL_WIDTH                       4  /* GPIO3_SEL - [3:0] */
+
+/*
+ * R124 (0x7C) - GPIO Control 4
+ */
+#define WM8904_GPI7_ENA                         0x0200  /* GPI7_ENA */
+#define WM8904_GPI7_ENA_MASK                    0x0200  /* GPI7_ENA */
+#define WM8904_GPI7_ENA_SHIFT                        9  /* GPI7_ENA */
+#define WM8904_GPI7_ENA_WIDTH                        1  /* GPI7_ENA */
+#define WM8904_GPI8_ENA                         0x0100  /* GPI8_ENA */
+#define WM8904_GPI8_ENA_MASK                    0x0100  /* GPI8_ENA */
+#define WM8904_GPI8_ENA_SHIFT                        8  /* GPI8_ENA */
+#define WM8904_GPI8_ENA_WIDTH                        1  /* GPI8_ENA */
+#define WM8904_GPIO_BCLK_MODE_ENA               0x0080  /* GPIO_BCLK_MODE_ENA */
+#define WM8904_GPIO_BCLK_MODE_ENA_MASK          0x0080  /* GPIO_BCLK_MODE_ENA */
+#define WM8904_GPIO_BCLK_MODE_ENA_SHIFT              7  /* GPIO_BCLK_MODE_ENA */
+#define WM8904_GPIO_BCLK_MODE_ENA_WIDTH              1  /* GPIO_BCLK_MODE_ENA */
+#define WM8904_GPIO_BCLK_SEL_MASK               0x000F  /* GPIO_BCLK_SEL - [3:0] */
+#define WM8904_GPIO_BCLK_SEL_SHIFT                   0  /* GPIO_BCLK_SEL - [3:0] */
+#define WM8904_GPIO_BCLK_SEL_WIDTH                   4  /* GPIO_BCLK_SEL - [3:0] */
+
+/*
+ * R126 (0x7E) - Digital Pulls
+ */
+#define WM8904_MCLK_PU                          0x0080  /* MCLK_PU */
+#define WM8904_MCLK_PU_MASK                     0x0080  /* MCLK_PU */
+#define WM8904_MCLK_PU_SHIFT                         7  /* MCLK_PU */
+#define WM8904_MCLK_PU_WIDTH                         1  /* MCLK_PU */
+#define WM8904_MCLK_PD                          0x0040  /* MCLK_PD */
+#define WM8904_MCLK_PD_MASK                     0x0040  /* MCLK_PD */
+#define WM8904_MCLK_PD_SHIFT                         6  /* MCLK_PD */
+#define WM8904_MCLK_PD_WIDTH                         1  /* MCLK_PD */
+#define WM8904_DACDAT_PU                        0x0020  /* DACDAT_PU */
+#define WM8904_DACDAT_PU_MASK                   0x0020  /* DACDAT_PU */
+#define WM8904_DACDAT_PU_SHIFT                       5  /* DACDAT_PU */
+#define WM8904_DACDAT_PU_WIDTH                       1  /* DACDAT_PU */
+#define WM8904_DACDAT_PD                        0x0010  /* DACDAT_PD */
+#define WM8904_DACDAT_PD_MASK                   0x0010  /* DACDAT_PD */
+#define WM8904_DACDAT_PD_SHIFT                       4  /* DACDAT_PD */
+#define WM8904_DACDAT_PD_WIDTH                       1  /* DACDAT_PD */
+#define WM8904_LRCLK_PU                         0x0008  /* LRCLK_PU */
+#define WM8904_LRCLK_PU_MASK                    0x0008  /* LRCLK_PU */
+#define WM8904_LRCLK_PU_SHIFT                        3  /* LRCLK_PU */
+#define WM8904_LRCLK_PU_WIDTH                        1  /* LRCLK_PU */
+#define WM8904_LRCLK_PD                         0x0004  /* LRCLK_PD */
+#define WM8904_LRCLK_PD_MASK                    0x0004  /* LRCLK_PD */
+#define WM8904_LRCLK_PD_SHIFT                        2  /* LRCLK_PD */
+#define WM8904_LRCLK_PD_WIDTH                        1  /* LRCLK_PD */
+#define WM8904_BCLK_PU                          0x0002  /* BCLK_PU */
+#define WM8904_BCLK_PU_MASK                     0x0002  /* BCLK_PU */
+#define WM8904_BCLK_PU_SHIFT                         1  /* BCLK_PU */
+#define WM8904_BCLK_PU_WIDTH                         1  /* BCLK_PU */
+#define WM8904_BCLK_PD                          0x0001  /* BCLK_PD */
+#define WM8904_BCLK_PD_MASK                     0x0001  /* BCLK_PD */
+#define WM8904_BCLK_PD_SHIFT                         0  /* BCLK_PD */
+#define WM8904_BCLK_PD_WIDTH                         1  /* BCLK_PD */
+
+/*
+ * R127 (0x7F) - Interrupt Status
+ */
+#define WM8904_IRQ                              0x0400  /* IRQ */
+#define WM8904_IRQ_MASK                         0x0400  /* IRQ */
+#define WM8904_IRQ_SHIFT                            10  /* IRQ */
+#define WM8904_IRQ_WIDTH                             1  /* IRQ */
+#define WM8904_GPIO_BCLK_EINT                   0x0200  /* GPIO_BCLK_EINT */
+#define WM8904_GPIO_BCLK_EINT_MASK              0x0200  /* GPIO_BCLK_EINT */
+#define WM8904_GPIO_BCLK_EINT_SHIFT                  9  /* GPIO_BCLK_EINT */
+#define WM8904_GPIO_BCLK_EINT_WIDTH                  1  /* GPIO_BCLK_EINT */
+#define WM8904_WSEQ_EINT                        0x0100  /* WSEQ_EINT */
+#define WM8904_WSEQ_EINT_MASK                   0x0100  /* WSEQ_EINT */
+#define WM8904_WSEQ_EINT_SHIFT                       8  /* WSEQ_EINT */
+#define WM8904_WSEQ_EINT_WIDTH                       1  /* WSEQ_EINT */
+#define WM8904_GPIO3_EINT                       0x0080  /* GPIO3_EINT */
+#define WM8904_GPIO3_EINT_MASK                  0x0080  /* GPIO3_EINT */
+#define WM8904_GPIO3_EINT_SHIFT                      7  /* GPIO3_EINT */
+#define WM8904_GPIO3_EINT_WIDTH                      1  /* GPIO3_EINT */
+#define WM8904_GPIO2_EINT                       0x0040  /* GPIO2_EINT */
+#define WM8904_GPIO2_EINT_MASK                  0x0040  /* GPIO2_EINT */
+#define WM8904_GPIO2_EINT_SHIFT                      6  /* GPIO2_EINT */
+#define WM8904_GPIO2_EINT_WIDTH                      1  /* GPIO2_EINT */
+#define WM8904_GPIO1_EINT                       0x0020  /* GPIO1_EINT */
+#define WM8904_GPIO1_EINT_MASK                  0x0020  /* GPIO1_EINT */
+#define WM8904_GPIO1_EINT_SHIFT                      5  /* GPIO1_EINT */
+#define WM8904_GPIO1_EINT_WIDTH                      1  /* GPIO1_EINT */
+#define WM8904_GPI8_EINT                        0x0010  /* GPI8_EINT */
+#define WM8904_GPI8_EINT_MASK                   0x0010  /* GPI8_EINT */
+#define WM8904_GPI8_EINT_SHIFT                       4  /* GPI8_EINT */
+#define WM8904_GPI8_EINT_WIDTH                       1  /* GPI8_EINT */
+#define WM8904_GPI7_EINT                        0x0008  /* GPI7_EINT */
+#define WM8904_GPI7_EINT_MASK                   0x0008  /* GPI7_EINT */
+#define WM8904_GPI7_EINT_SHIFT                       3  /* GPI7_EINT */
+#define WM8904_GPI7_EINT_WIDTH                       1  /* GPI7_EINT */
+#define WM8904_FLL_LOCK_EINT                    0x0004  /* FLL_LOCK_EINT */
+#define WM8904_FLL_LOCK_EINT_MASK               0x0004  /* FLL_LOCK_EINT */
+#define WM8904_FLL_LOCK_EINT_SHIFT                   2  /* FLL_LOCK_EINT */
+#define WM8904_FLL_LOCK_EINT_WIDTH                   1  /* FLL_LOCK_EINT */
+#define WM8904_MIC_SHRT_EINT                    0x0002  /* MIC_SHRT_EINT */
+#define WM8904_MIC_SHRT_EINT_MASK               0x0002  /* MIC_SHRT_EINT */
+#define WM8904_MIC_SHRT_EINT_SHIFT                   1  /* MIC_SHRT_EINT */
+#define WM8904_MIC_SHRT_EINT_WIDTH                   1  /* MIC_SHRT_EINT */
+#define WM8904_MIC_DET_EINT                     0x0001  /* MIC_DET_EINT */
+#define WM8904_MIC_DET_EINT_MASK                0x0001  /* MIC_DET_EINT */
+#define WM8904_MIC_DET_EINT_SHIFT                    0  /* MIC_DET_EINT */
+#define WM8904_MIC_DET_EINT_WIDTH                    1  /* MIC_DET_EINT */
+
+/*
+ * R128 (0x80) - Interrupt Status Mask
+ */
+#define WM8904_IM_GPIO_BCLK_EINT                0x0200  /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_GPIO_BCLK_EINT_MASK           0x0200  /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_GPIO_BCLK_EINT_SHIFT               9  /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_GPIO_BCLK_EINT_WIDTH               1  /* IM_GPIO_BCLK_EINT */
+#define WM8904_IM_WSEQ_EINT                     0x0100  /* IM_WSEQ_EINT */
+#define WM8904_IM_WSEQ_EINT_MASK                0x0100  /* IM_WSEQ_EINT */
+#define WM8904_IM_WSEQ_EINT_SHIFT                    8  /* IM_WSEQ_EINT */
+#define WM8904_IM_WSEQ_EINT_WIDTH                    1  /* IM_WSEQ_EINT */
+#define WM8904_IM_GPIO3_EINT                    0x0080  /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO3_EINT_MASK               0x0080  /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO3_EINT_SHIFT                   7  /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO3_EINT_WIDTH                   1  /* IM_GPIO3_EINT */
+#define WM8904_IM_GPIO2_EINT                    0x0040  /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO2_EINT_MASK               0x0040  /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO2_EINT_SHIFT                   6  /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO2_EINT_WIDTH                   1  /* IM_GPIO2_EINT */
+#define WM8904_IM_GPIO1_EINT                    0x0020  /* IM_GPIO1_EINT */
+#define WM8904_IM_GPIO1_EINT_MASK               0x0020  /* IM_GPIO1_EINT */
+#define WM8904_IM_GPIO1_EINT_SHIFT                   5  /* IM_GPIO1_EINT */
+#define WM8904_IM_GPIO1_EINT_WIDTH                   1  /* IM_GPIO1_EINT */
+#define WM8904_IM_GPI8_EINT                     0x0010  /* IM_GPI8_EINT */
+#define WM8904_IM_GPI8_EINT_MASK                0x0010  /* IM_GPI8_EINT */
+#define WM8904_IM_GPI8_EINT_SHIFT                    4  /* IM_GPI8_EINT */
+#define WM8904_IM_GPI8_EINT_WIDTH                    1  /* IM_GPI8_EINT */
+#define WM8904_IM_GPI7_EINT                     0x0008  /* IM_GPI7_EINT */
+#define WM8904_IM_GPI7_EINT_MASK                0x0008  /* IM_GPI7_EINT */
+#define WM8904_IM_GPI7_EINT_SHIFT                    3  /* IM_GPI7_EINT */
+#define WM8904_IM_GPI7_EINT_WIDTH                    1  /* IM_GPI7_EINT */
+#define WM8904_IM_FLL_LOCK_EINT                 0x0004  /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_FLL_LOCK_EINT_MASK            0x0004  /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_FLL_LOCK_EINT_SHIFT                2  /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_FLL_LOCK_EINT_WIDTH                1  /* IM_FLL_LOCK_EINT */
+#define WM8904_IM_MIC_SHRT_EINT                 0x0002  /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_SHRT_EINT_MASK            0x0002  /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_SHRT_EINT_SHIFT                1  /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_SHRT_EINT_WIDTH                1  /* IM_MIC_SHRT_EINT */
+#define WM8904_IM_MIC_DET_EINT                  0x0001  /* IM_MIC_DET_EINT */
+#define WM8904_IM_MIC_DET_EINT_MASK             0x0001  /* IM_MIC_DET_EINT */
+#define WM8904_IM_MIC_DET_EINT_SHIFT                 0  /* IM_MIC_DET_EINT */
+#define WM8904_IM_MIC_DET_EINT_WIDTH                 1  /* IM_MIC_DET_EINT */
+
+/*
+ * R129 (0x81) - Interrupt Polarity
+ */
+#define WM8904_GPIO_BCLK_EINT_POL               0x0200  /* GPIO_BCLK_EINT_POL */
+#define WM8904_GPIO_BCLK_EINT_POL_MASK          0x0200  /* GPIO_BCLK_EINT_POL */
+#define WM8904_GPIO_BCLK_EINT_POL_SHIFT              9  /* GPIO_BCLK_EINT_POL */
+#define WM8904_GPIO_BCLK_EINT_POL_WIDTH              1  /* GPIO_BCLK_EINT_POL */
+#define WM8904_WSEQ_EINT_POL                    0x0100  /* WSEQ_EINT_POL */
+#define WM8904_WSEQ_EINT_POL_MASK               0x0100  /* WSEQ_EINT_POL */
+#define WM8904_WSEQ_EINT_POL_SHIFT                   8  /* WSEQ_EINT_POL */
+#define WM8904_WSEQ_EINT_POL_WIDTH                   1  /* WSEQ_EINT_POL */
+#define WM8904_GPIO3_EINT_POL                   0x0080  /* GPIO3_EINT_POL */
+#define WM8904_GPIO3_EINT_POL_MASK              0x0080  /* GPIO3_EINT_POL */
+#define WM8904_GPIO3_EINT_POL_SHIFT                  7  /* GPIO3_EINT_POL */
+#define WM8904_GPIO3_EINT_POL_WIDTH                  1  /* GPIO3_EINT_POL */
+#define WM8904_GPIO2_EINT_POL                   0x0040  /* GPIO2_EINT_POL */
+#define WM8904_GPIO2_EINT_POL_MASK              0x0040  /* GPIO2_EINT_POL */
+#define WM8904_GPIO2_EINT_POL_SHIFT                  6  /* GPIO2_EINT_POL */
+#define WM8904_GPIO2_EINT_POL_WIDTH                  1  /* GPIO2_EINT_POL */
+#define WM8904_GPIO1_EINT_POL                   0x0020  /* GPIO1_EINT_POL */
+#define WM8904_GPIO1_EINT_POL_MASK              0x0020  /* GPIO1_EINT_POL */
+#define WM8904_GPIO1_EINT_POL_SHIFT                  5  /* GPIO1_EINT_POL */
+#define WM8904_GPIO1_EINT_POL_WIDTH                  1  /* GPIO1_EINT_POL */
+#define WM8904_GPI8_EINT_POL                    0x0010  /* GPI8_EINT_POL */
+#define WM8904_GPI8_EINT_POL_MASK               0x0010  /* GPI8_EINT_POL */
+#define WM8904_GPI8_EINT_POL_SHIFT                   4  /* GPI8_EINT_POL */
+#define WM8904_GPI8_EINT_POL_WIDTH                   1  /* GPI8_EINT_POL */
+#define WM8904_GPI7_EINT_POL                    0x0008  /* GPI7_EINT_POL */
+#define WM8904_GPI7_EINT_POL_MASK               0x0008  /* GPI7_EINT_POL */
+#define WM8904_GPI7_EINT_POL_SHIFT                   3  /* GPI7_EINT_POL */
+#define WM8904_GPI7_EINT_POL_WIDTH                   1  /* GPI7_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL                0x0004  /* FLL_LOCK_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL_MASK           0x0004  /* FLL_LOCK_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL_SHIFT               2  /* FLL_LOCK_EINT_POL */
+#define WM8904_FLL_LOCK_EINT_POL_WIDTH               1  /* FLL_LOCK_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL                0x0002  /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL_MASK           0x0002  /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL_SHIFT               1  /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_SHRT_EINT_POL_WIDTH               1  /* MIC_SHRT_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL                 0x0001  /* MIC_DET_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL_MASK            0x0001  /* MIC_DET_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL_SHIFT                0  /* MIC_DET_EINT_POL */
+#define WM8904_MIC_DET_EINT_POL_WIDTH                1  /* MIC_DET_EINT_POL */
+
+/*
+ * R130 (0x82) - Interrupt Debounce
+ */
+#define WM8904_GPIO_BCLK_EINT_DB                0x0200  /* GPIO_BCLK_EINT_DB */
+#define WM8904_GPIO_BCLK_EINT_DB_MASK           0x0200  /* GPIO_BCLK_EINT_DB */
+#define WM8904_GPIO_BCLK_EINT_DB_SHIFT               9  /* GPIO_BCLK_EINT_DB */
+#define WM8904_GPIO_BCLK_EINT_DB_WIDTH               1  /* GPIO_BCLK_EINT_DB */
+#define WM8904_WSEQ_EINT_DB                     0x0100  /* WSEQ_EINT_DB */
+#define WM8904_WSEQ_EINT_DB_MASK                0x0100  /* WSEQ_EINT_DB */
+#define WM8904_WSEQ_EINT_DB_SHIFT                    8  /* WSEQ_EINT_DB */
+#define WM8904_WSEQ_EINT_DB_WIDTH                    1  /* WSEQ_EINT_DB */
+#define WM8904_GPIO3_EINT_DB                    0x0080  /* GPIO3_EINT_DB */
+#define WM8904_GPIO3_EINT_DB_MASK               0x0080  /* GPIO3_EINT_DB */
+#define WM8904_GPIO3_EINT_DB_SHIFT                   7  /* GPIO3_EINT_DB */
+#define WM8904_GPIO3_EINT_DB_WIDTH                   1  /* GPIO3_EINT_DB */
+#define WM8904_GPIO2_EINT_DB                    0x0040  /* GPIO2_EINT_DB */
+#define WM8904_GPIO2_EINT_DB_MASK               0x0040  /* GPIO2_EINT_DB */
+#define WM8904_GPIO2_EINT_DB_SHIFT                   6  /* GPIO2_EINT_DB */
+#define WM8904_GPIO2_EINT_DB_WIDTH                   1  /* GPIO2_EINT_DB */
+#define WM8904_GPIO1_EINT_DB                    0x0020  /* GPIO1_EINT_DB */
+#define WM8904_GPIO1_EINT_DB_MASK               0x0020  /* GPIO1_EINT_DB */
+#define WM8904_GPIO1_EINT_DB_SHIFT                   5  /* GPIO1_EINT_DB */
+#define WM8904_GPIO1_EINT_DB_WIDTH                   1  /* GPIO1_EINT_DB */
+#define WM8904_GPI8_EINT_DB                     0x0010  /* GPI8_EINT_DB */
+#define WM8904_GPI8_EINT_DB_MASK                0x0010  /* GPI8_EINT_DB */
+#define WM8904_GPI8_EINT_DB_SHIFT                    4  /* GPI8_EINT_DB */
+#define WM8904_GPI8_EINT_DB_WIDTH                    1  /* GPI8_EINT_DB */
+#define WM8904_GPI7_EINT_DB                     0x0008  /* GPI7_EINT_DB */
+#define WM8904_GPI7_EINT_DB_MASK                0x0008  /* GPI7_EINT_DB */
+#define WM8904_GPI7_EINT_DB_SHIFT                    3  /* GPI7_EINT_DB */
+#define WM8904_GPI7_EINT_DB_WIDTH                    1  /* GPI7_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB                 0x0004  /* FLL_LOCK_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB_MASK            0x0004  /* FLL_LOCK_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB_SHIFT                2  /* FLL_LOCK_EINT_DB */
+#define WM8904_FLL_LOCK_EINT_DB_WIDTH                1  /* FLL_LOCK_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB                 0x0002  /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB_MASK            0x0002  /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB_SHIFT                1  /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_SHRT_EINT_DB_WIDTH                1  /* MIC_SHRT_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB                  0x0001  /* MIC_DET_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB_MASK             0x0001  /* MIC_DET_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB_SHIFT                 0  /* MIC_DET_EINT_DB */
+#define WM8904_MIC_DET_EINT_DB_WIDTH                 1  /* MIC_DET_EINT_DB */
+
+/*
+ * R134 (0x86) - EQ1
+ */
+#define WM8904_EQ_ENA                           0x0001  /* EQ_ENA */
+#define WM8904_EQ_ENA_MASK                      0x0001  /* EQ_ENA */
+#define WM8904_EQ_ENA_SHIFT                          0  /* EQ_ENA */
+#define WM8904_EQ_ENA_WIDTH                          1  /* EQ_ENA */
+
+/*
+ * R135 (0x87) - EQ2
+ */
+#define WM8904_EQ_B1_GAIN_MASK                  0x001F  /* EQ_B1_GAIN - [4:0] */
+#define WM8904_EQ_B1_GAIN_SHIFT                      0  /* EQ_B1_GAIN - [4:0] */
+#define WM8904_EQ_B1_GAIN_WIDTH                      5  /* EQ_B1_GAIN - [4:0] */
+
+/*
+ * R136 (0x88) - EQ3
+ */
+#define WM8904_EQ_B2_GAIN_MASK                  0x001F  /* EQ_B2_GAIN - [4:0] */
+#define WM8904_EQ_B2_GAIN_SHIFT                      0  /* EQ_B2_GAIN - [4:0] */
+#define WM8904_EQ_B2_GAIN_WIDTH                      5  /* EQ_B2_GAIN - [4:0] */
+
+/*
+ * R137 (0x89) - EQ4
+ */
+#define WM8904_EQ_B3_GAIN_MASK                  0x001F  /* EQ_B3_GAIN - [4:0] */
+#define WM8904_EQ_B3_GAIN_SHIFT                      0  /* EQ_B3_GAIN - [4:0] */
+#define WM8904_EQ_B3_GAIN_WIDTH                      5  /* EQ_B3_GAIN - [4:0] */
+
+/*
+ * R138 (0x8A) - EQ5
+ */
+#define WM8904_EQ_B4_GAIN_MASK                  0x001F  /* EQ_B4_GAIN - [4:0] */
+#define WM8904_EQ_B4_GAIN_SHIFT                      0  /* EQ_B4_GAIN - [4:0] */
+#define WM8904_EQ_B4_GAIN_WIDTH                      5  /* EQ_B4_GAIN - [4:0] */
+
+/*
+ * R139 (0x8B) - EQ6
+ */
+#define WM8904_EQ_B5_GAIN_MASK                  0x001F  /* EQ_B5_GAIN - [4:0] */
+#define WM8904_EQ_B5_GAIN_SHIFT                      0  /* EQ_B5_GAIN - [4:0] */
+#define WM8904_EQ_B5_GAIN_WIDTH                      5  /* EQ_B5_GAIN - [4:0] */
+
+/*
+ * R140 (0x8C) - EQ7
+ */
+#define WM8904_EQ_B1_A_MASK                     0xFFFF  /* EQ_B1_A - [15:0] */
+#define WM8904_EQ_B1_A_SHIFT                         0  /* EQ_B1_A - [15:0] */
+#define WM8904_EQ_B1_A_WIDTH                        16  /* EQ_B1_A - [15:0] */
+
+/*
+ * R141 (0x8D) - EQ8
+ */
+#define WM8904_EQ_B1_B_MASK                     0xFFFF  /* EQ_B1_B - [15:0] */
+#define WM8904_EQ_B1_B_SHIFT                         0  /* EQ_B1_B - [15:0] */
+#define WM8904_EQ_B1_B_WIDTH                        16  /* EQ_B1_B - [15:0] */
+
+/*
+ * R142 (0x8E) - EQ9
+ */
+#define WM8904_EQ_B1_PG_MASK                    0xFFFF  /* EQ_B1_PG - [15:0] */
+#define WM8904_EQ_B1_PG_SHIFT                        0  /* EQ_B1_PG - [15:0] */
+#define WM8904_EQ_B1_PG_WIDTH                       16  /* EQ_B1_PG - [15:0] */
+
+/*
+ * R143 (0x8F) - EQ10
+ */
+#define WM8904_EQ_B2_A_MASK                     0xFFFF  /* EQ_B2_A - [15:0] */
+#define WM8904_EQ_B2_A_SHIFT                         0  /* EQ_B2_A - [15:0] */
+#define WM8904_EQ_B2_A_WIDTH                        16  /* EQ_B2_A - [15:0] */
+
+/*
+ * R144 (0x90) - EQ11
+ */
+#define WM8904_EQ_B2_B_MASK                     0xFFFF  /* EQ_B2_B - [15:0] */
+#define WM8904_EQ_B2_B_SHIFT                         0  /* EQ_B2_B - [15:0] */
+#define WM8904_EQ_B2_B_WIDTH                        16  /* EQ_B2_B - [15:0] */
+
+/*
+ * R145 (0x91) - EQ12
+ */
+#define WM8904_EQ_B2_C_MASK                     0xFFFF  /* EQ_B2_C - [15:0] */
+#define WM8904_EQ_B2_C_SHIFT                         0  /* EQ_B2_C - [15:0] */
+#define WM8904_EQ_B2_C_WIDTH                        16  /* EQ_B2_C - [15:0] */
+
+/*
+ * R146 (0x92) - EQ13
+ */
+#define WM8904_EQ_B2_PG_MASK                    0xFFFF  /* EQ_B2_PG - [15:0] */
+#define WM8904_EQ_B2_PG_SHIFT                        0  /* EQ_B2_PG - [15:0] */
+#define WM8904_EQ_B2_PG_WIDTH                       16  /* EQ_B2_PG - [15:0] */
+
+/*
+ * R147 (0x93) - EQ14
+ */
+#define WM8904_EQ_B3_A_MASK                     0xFFFF  /* EQ_B3_A - [15:0] */
+#define WM8904_EQ_B3_A_SHIFT                         0  /* EQ_B3_A - [15:0] */
+#define WM8904_EQ_B3_A_WIDTH                        16  /* EQ_B3_A - [15:0] */
+
+/*
+ * R148 (0x94) - EQ15
+ */
+#define WM8904_EQ_B3_B_MASK                     0xFFFF  /* EQ_B3_B - [15:0] */
+#define WM8904_EQ_B3_B_SHIFT                         0  /* EQ_B3_B - [15:0] */
+#define WM8904_EQ_B3_B_WIDTH                        16  /* EQ_B3_B - [15:0] */
+
+/*
+ * R149 (0x95) - EQ16
+ */
+#define WM8904_EQ_B3_C_MASK                     0xFFFF  /* EQ_B3_C - [15:0] */
+#define WM8904_EQ_B3_C_SHIFT                         0  /* EQ_B3_C - [15:0] */
+#define WM8904_EQ_B3_C_WIDTH                        16  /* EQ_B3_C - [15:0] */
+
+/*
+ * R150 (0x96) - EQ17
+ */
+#define WM8904_EQ_B3_PG_MASK                    0xFFFF  /* EQ_B3_PG - [15:0] */
+#define WM8904_EQ_B3_PG_SHIFT                        0  /* EQ_B3_PG - [15:0] */
+#define WM8904_EQ_B3_PG_WIDTH                       16  /* EQ_B3_PG - [15:0] */
+
+/*
+ * R151 (0x97) - EQ18
+ */
+#define WM8904_EQ_B4_A_MASK                     0xFFFF  /* EQ_B4_A - [15:0] */
+#define WM8904_EQ_B4_A_SHIFT                         0  /* EQ_B4_A - [15:0] */
+#define WM8904_EQ_B4_A_WIDTH                        16  /* EQ_B4_A - [15:0] */
+
+/*
+ * R152 (0x98) - EQ19
+ */
+#define WM8904_EQ_B4_B_MASK                     0xFFFF  /* EQ_B4_B - [15:0] */
+#define WM8904_EQ_B4_B_SHIFT                         0  /* EQ_B4_B - [15:0] */
+#define WM8904_EQ_B4_B_WIDTH                        16  /* EQ_B4_B - [15:0] */
+
+/*
+ * R153 (0x99) - EQ20
+ */
+#define WM8904_EQ_B4_C_MASK                     0xFFFF  /* EQ_B4_C - [15:0] */
+#define WM8904_EQ_B4_C_SHIFT                         0  /* EQ_B4_C - [15:0] */
+#define WM8904_EQ_B4_C_WIDTH                        16  /* EQ_B4_C - [15:0] */
+
+/*
+ * R154 (0x9A) - EQ21
+ */
+#define WM8904_EQ_B4_PG_MASK                    0xFFFF  /* EQ_B4_PG - [15:0] */
+#define WM8904_EQ_B4_PG_SHIFT                        0  /* EQ_B4_PG - [15:0] */
+#define WM8904_EQ_B4_PG_WIDTH                       16  /* EQ_B4_PG - [15:0] */
+
+/*
+ * R155 (0x9B) - EQ22
+ */
+#define WM8904_EQ_B5_A_MASK                     0xFFFF  /* EQ_B5_A - [15:0] */
+#define WM8904_EQ_B5_A_SHIFT                         0  /* EQ_B5_A - [15:0] */
+#define WM8904_EQ_B5_A_WIDTH                        16  /* EQ_B5_A - [15:0] */
+
+/*
+ * R156 (0x9C) - EQ23
+ */
+#define WM8904_EQ_B5_B_MASK                     0xFFFF  /* EQ_B5_B - [15:0] */
+#define WM8904_EQ_B5_B_SHIFT                         0  /* EQ_B5_B - [15:0] */
+#define WM8904_EQ_B5_B_WIDTH                        16  /* EQ_B5_B - [15:0] */
+
+/*
+ * R157 (0x9D) - EQ24
+ */
+#define WM8904_EQ_B5_PG_MASK                    0xFFFF  /* EQ_B5_PG - [15:0] */
+#define WM8904_EQ_B5_PG_SHIFT                        0  /* EQ_B5_PG - [15:0] */
+#define WM8904_EQ_B5_PG_WIDTH                       16  /* EQ_B5_PG - [15:0] */
+
+/*
+ * R161 (0xA1) - Control Interface Test 1
+ */
+#define WM8904_USER_KEY                         0x0002  /* USER_KEY */
+#define WM8904_USER_KEY_MASK                    0x0002  /* USER_KEY */
+#define WM8904_USER_KEY_SHIFT                        1  /* USER_KEY */
+#define WM8904_USER_KEY_WIDTH                        1  /* USER_KEY */
+
+/*
+ * R204 (0xCC) - Analogue Output Bias 0
+ */
+#define WM8904_PGA_BIAS_MASK                    0x0070  /* PGA_BIAS - [6:4] */
+#define WM8904_PGA_BIAS_SHIFT                        4  /* PGA_BIAS - [6:4] */
+#define WM8904_PGA_BIAS_WIDTH                        3  /* PGA_BIAS - [6:4] */
+
+/*
+ * R247 (0xF7) - FLL NCO Test 0
+ */
+#define WM8904_FLL_FRC_NCO                      0x0001  /* FLL_FRC_NCO */
+#define WM8904_FLL_FRC_NCO_MASK                 0x0001  /* FLL_FRC_NCO */
+#define WM8904_FLL_FRC_NCO_SHIFT                     0  /* FLL_FRC_NCO */
+#define WM8904_FLL_FRC_NCO_WIDTH                     1  /* FLL_FRC_NCO */
+
+/*
+ * R248 (0xF8) - FLL NCO Test 1
+ */
+#define WM8904_FLL_FRC_NCO_VAL_MASK             0x003F  /* FLL_FRC_NCO_VAL - [5:0] */
+#define WM8904_FLL_FRC_NCO_VAL_SHIFT                 0  /* FLL_FRC_NCO_VAL - [5:0] */
+#define WM8904_FLL_FRC_NCO_VAL_WIDTH                 6  /* FLL_FRC_NCO_VAL - [5:0] */
+
+#endif
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
new file mode 100644
index 0000000..615dab2
--- /dev/null
+++ b/sound/soc/codecs/wm8955.c
@@ -0,0 +1,1151 @@
+/*
+ * wm8955.c  --  WM8955 ALSA SoC Audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/wm8955.h>
+
+#include "wm8955.h"
+
+static struct snd_soc_codec *wm8955_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8955;
+
+#define WM8955_NUM_SUPPLIES 4
+static const char *wm8955_supply_names[WM8955_NUM_SUPPLIES] = {
+	"DCVDD",
+	"DBVDD",
+	"HPVDD",
+	"AVDD",
+};
+
+/* codec private data */
+struct wm8955_priv {
+	struct snd_soc_codec codec;
+	u16 reg_cache[WM8955_MAX_REGISTER + 1];
+
+	unsigned int mclk_rate;
+
+	int deemph;
+	int fs;
+
+	struct regulator_bulk_data supplies[WM8955_NUM_SUPPLIES];
+
+	struct wm8955_pdata *pdata;
+};
+
+static const u16 wm8955_reg[WM8955_MAX_REGISTER + 1] = {
+	0x0000,     /* R0 */
+	0x0000,     /* R1 */
+	0x0079,     /* R2  - LOUT1 volume */
+	0x0079,     /* R3  - ROUT1 volume */
+	0x0000,     /* R4 */
+	0x0008,     /* R5  - DAC Control */
+	0x0000,     /* R6 */
+	0x000A,     /* R7  - Audio Interface */
+	0x0000,     /* R8  - Sample Rate */
+	0x0000,     /* R9 */
+	0x00FF,     /* R10 - Left DAC volume */
+	0x00FF,     /* R11 - Right DAC volume */
+	0x000F,     /* R12 - Bass control */
+	0x000F,     /* R13 - Treble control */
+	0x0000,     /* R14 */
+	0x0000,     /* R15 - Reset */
+	0x0000,     /* R16 */
+	0x0000,     /* R17 */
+	0x0000,     /* R18 */
+	0x0000,     /* R19 */
+	0x0000,     /* R20 */
+	0x0000,     /* R21 */
+	0x0000,     /* R22 */
+	0x00C1,     /* R23 - Additional control (1) */
+	0x0000,     /* R24 - Additional control (2) */
+	0x0000,     /* R25 - Power Management (1) */
+	0x0000,     /* R26 - Power Management (2) */
+	0x0000,     /* R27 - Additional Control (3) */
+	0x0000,     /* R28 */
+	0x0000,     /* R29 */
+	0x0000,     /* R30 */
+	0x0000,     /* R31 */
+	0x0000,     /* R32 */
+	0x0000,     /* R33 */
+	0x0050,     /* R34 - Left out Mix (1) */
+	0x0050,     /* R35 - Left out Mix (2) */
+	0x0050,     /* R36 - Right out Mix (1) */
+	0x0050,     /* R37 - Right Out Mix (2) */
+	0x0050,     /* R38 - Mono out Mix (1) */
+	0x0050,     /* R39 - Mono out Mix (2) */
+	0x0079,     /* R40 - LOUT2 volume */
+	0x0079,     /* R41 - ROUT2 volume */
+	0x0079,     /* R42 - MONOOUT volume */
+	0x0000,     /* R43 - Clocking / PLL */
+	0x0103,     /* R44 - PLL Control 1 */
+	0x0024,     /* R45 - PLL Control 2 */
+	0x01BA,     /* R46 - PLL Control 3 */
+	0x0000,     /* R47 */
+	0x0000,     /* R48 */
+	0x0000,     /* R49 */
+	0x0000,     /* R50 */
+	0x0000,     /* R51 */
+	0x0000,     /* R52 */
+	0x0000,     /* R53 */
+	0x0000,     /* R54 */
+	0x0000,     /* R55 */
+	0x0000,     /* R56 */
+	0x0000,     /* R57 */
+	0x0000,     /* R58 */
+	0x0000,     /* R59 - PLL Control 4 */
+};
+
+static int wm8955_reset(struct snd_soc_codec *codec)
+{
+	return snd_soc_write(codec, WM8955_RESET, 0);
+}
+
+struct pll_factors {
+	int n;
+	int k;
+	int outdiv;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 22) * 10)
+
+static int wm8995_pll_factors(struct device *dev,
+			      int Fref, int Fout, struct pll_factors *pll)
+{
+	u64 Kpart;
+	unsigned int K, Ndiv, Nmod, target;
+
+	dev_dbg(dev, "Fref=%u Fout=%u\n", Fref, Fout);
+
+	/* The oscilator should run at should be 90-100MHz, and
+	 * there's a divide by 4 plus an optional divide by 2 in the
+	 * output path to generate the system clock.  The clock table
+	 * is sortd so we should always generate a suitable target. */
+	target = Fout * 4;
+	if (target < 90000000) {
+		pll->outdiv = 1;
+		target *= 2;
+	} else {
+		pll->outdiv = 0;
+	}
+
+	WARN_ON(target < 90000000 || target > 100000000);
+
+	dev_dbg(dev, "Fvco=%dHz\n", target);
+
+	/* Now, calculate N.K */
+	Ndiv = target / Fref;
+
+	pll->n = Ndiv;
+	Nmod = target % Fref;
+	dev_dbg(dev, "Nmod=%d\n", Nmod);
+
+	/* Calculate fractional part - scale up so we can round. */
+	Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, Fref);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	pll->k = K / 10;
+
+	dev_dbg(dev, "N=%x K=%x OUTDIV=%x\n", pll->n, pll->k, pll->outdiv);
+
+	return 0;
+}
+
+/* Lookup table specifiying SRATE (table 25 in datasheet); some of the
+ * output frequencies have been rounded to the standard frequencies
+ * they are intended to match where the error is slight. */
+static struct {
+	int mclk;
+	int fs;
+	int usb;
+	int sr;
+} clock_cfgs[] = {
+	{ 18432000,  8000, 0,  3, },
+	{ 18432000, 12000, 0,  9, },
+	{ 18432000, 16000, 0, 11, },
+	{ 18432000, 24000, 0, 29, },
+	{ 18432000, 32000, 0, 13, },
+	{ 18432000, 48000, 0,  1, },
+	{ 18432000, 96000, 0, 15, },
+
+	{ 16934400,  8018, 0, 19, },
+	{ 16934400, 11025, 0, 25, },
+	{ 16934400, 22050, 0, 27, },
+	{ 16934400, 44100, 0, 17, },
+	{ 16934400, 88200, 0, 31, },
+
+	{ 12000000,  8000, 1,  2, },
+	{ 12000000, 11025, 1, 25, },
+	{ 12000000, 12000, 1,  8, },
+	{ 12000000, 16000, 1, 10, },
+	{ 12000000, 22050, 1, 27, },
+	{ 12000000, 24000, 1, 28, },
+	{ 12000000, 32000, 1, 12, },
+	{ 12000000, 44100, 1, 17, },
+	{ 12000000, 48000, 1,  0, },
+	{ 12000000, 88200, 1, 31, },
+	{ 12000000, 96000, 1, 14, },
+
+	{ 12288000,  8000, 0,  2, },
+	{ 12288000, 12000, 0,  8, },
+	{ 12288000, 16000, 0, 10, },
+	{ 12288000, 24000, 0, 28, },
+	{ 12288000, 32000, 0, 12, },
+	{ 12288000, 48000, 0,  0, },
+	{ 12288000, 96000, 0, 14, },
+
+	{ 12289600,  8018, 0, 18, },
+	{ 12289600, 11025, 0, 24, },
+	{ 12289600, 22050, 0, 26, },
+	{ 11289600, 44100, 0, 16, },
+	{ 11289600, 88200, 0, 31, },
+};
+
+static int wm8955_configure_clocking(struct snd_soc_codec *codec)
+{
+	struct wm8955_priv *wm8955 = codec->private_data;
+	int i, ret, val;
+	int clocking = 0;
+	int srate = 0;
+	int sr = -1;
+	struct pll_factors pll;
+
+	/* If we're not running a sample rate currently just pick one */
+	if (wm8955->fs == 0)
+		wm8955->fs = 8000;
+
+	/* Can we generate an exact output? */
+	for (i = 0; i < ARRAY_SIZE(clock_cfgs); i++) {
+		if (wm8955->fs != clock_cfgs[i].fs)
+			continue;
+		sr = i;
+
+		if (wm8955->mclk_rate == clock_cfgs[i].mclk)
+			break;
+	}
+
+	/* We should never get here with an unsupported sample rate */
+	if (sr == -1) {
+		dev_err(codec->dev, "Sample rate %dHz unsupported\n",
+			wm8955->fs);
+		WARN_ON(sr == -1);
+		return -EINVAL;
+	}
+
+	if (i == ARRAY_SIZE(clock_cfgs)) {
+		/* If we can't generate the right clock from MCLK then
+		 * we should configure the PLL to supply us with an
+		 * appropriate clock.
+		 */
+		clocking |= WM8955_MCLKSEL;
+
+		/* Use the last divider configuration we saw for the
+		 * sample rate. */
+		ret = wm8995_pll_factors(codec->dev, wm8955->mclk_rate,
+					 clock_cfgs[sr].mclk, &pll);
+		if (ret != 0) {
+			dev_err(codec->dev,
+				"Unable to generate %dHz from %dHz MCLK\n",
+				wm8955->fs, wm8955->mclk_rate);
+			return -EINVAL;
+		}
+
+		snd_soc_update_bits(codec, WM8955_PLL_CONTROL_1,
+				    WM8955_N_MASK | WM8955_K_21_18_MASK,
+				    (pll.n << WM8955_N_SHIFT) |
+				    pll.k >> 18);
+		snd_soc_update_bits(codec, WM8955_PLL_CONTROL_2,
+				    WM8955_K_17_9_MASK,
+				    (pll.k >> 9) & WM8955_K_17_9_MASK);
+		snd_soc_update_bits(codec, WM8955_PLL_CONTROL_2,
+				    WM8955_K_8_0_MASK,
+				    pll.k & WM8955_K_8_0_MASK);
+		if (pll.k)
+			snd_soc_update_bits(codec, WM8955_PLL_CONTROL_4,
+					    WM8955_KEN, WM8955_KEN);
+		else
+			snd_soc_update_bits(codec, WM8955_PLL_CONTROL_4,
+					    WM8955_KEN, 0);
+
+		if (pll.outdiv)
+			val = WM8955_PLL_RB | WM8955_PLLOUTDIV2;
+		else
+			val = WM8955_PLL_RB;
+
+		/* Now start the PLL running */
+		snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+				    WM8955_PLL_RB | WM8955_PLLOUTDIV2, val);
+		snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+				    WM8955_PLLEN, WM8955_PLLEN);
+	}
+
+	srate = clock_cfgs[sr].usb | (clock_cfgs[sr].sr << WM8955_SR_SHIFT);
+
+	snd_soc_update_bits(codec, WM8955_SAMPLE_RATE,
+			    WM8955_USB | WM8955_SR_MASK, srate);
+	snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+			    WM8955_MCLKSEL, clocking);
+
+	return 0;
+}
+
+static int wm8955_sysclk(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	int ret = 0;
+
+	/* Always disable the clocks - if we're doing reconfiguration this
+	 * avoids misclocking.
+	 */
+	snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+			    WM8955_DIGENB, 0);
+	snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+			    WM8955_PLL_RB | WM8955_PLLEN, 0);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMD:
+		break;
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = wm8955_configure_clocking(codec);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int deemph_settings[] = { 0, 32000, 44100, 48000 };
+
+static int wm8955_set_deemph(struct snd_soc_codec *codec)
+{
+	struct wm8955_priv *wm8955 = codec->private_data;
+	int val, i, best;
+
+	/* If we're using deemphasis select the nearest available sample
+	 * rate.
+	 */
+	if (wm8955->deemph) {
+		best = 1;
+		for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) {
+			if (abs(deemph_settings[i] - wm8955->fs) <
+			    abs(deemph_settings[best] - wm8955->fs))
+				best = i;
+		}
+
+		val = best << WM8955_DEEMPH_SHIFT;
+	} else {
+		val = 0;
+	}
+
+	dev_dbg(codec->dev, "Set deemphasis %d\n", val);
+
+	return snd_soc_update_bits(codec, WM8955_DAC_CONTROL,
+				   WM8955_DEEMPH_MASK, val);
+}
+
+static int wm8955_get_deemph(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8955_priv *wm8955 = codec->private_data;
+
+	return wm8955->deemph;
+}
+
+static int wm8955_put_deemph(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8955_priv *wm8955 = codec->private_data;
+	int deemph = ucontrol->value.enumerated.item[0];
+
+	if (deemph > 1)
+		return -EINVAL;
+
+	wm8955->deemph = deemph;
+
+	return wm8955_set_deemph(codec);
+}
+
+static const char *bass_mode_text[] = {
+	"Linear", "Adaptive",
+};
+
+static const struct soc_enum bass_mode =
+	SOC_ENUM_SINGLE(WM8955_BASS_CONTROL, 7, 2, bass_mode_text);
+
+static const char *bass_cutoff_text[] = {
+	"Low", "High"
+};
+
+static const struct soc_enum bass_cutoff =
+	SOC_ENUM_SINGLE(WM8955_BASS_CONTROL, 6, 2, bass_cutoff_text);
+
+static const char *treble_cutoff_text[] = {
+	"High", "Low"
+};
+
+static const struct soc_enum treble_cutoff =
+	SOC_ENUM_SINGLE(WM8955_TREBLE_CONTROL, 6, 2, treble_cutoff_text);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(atten_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mono_tlv, -2100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(treble_tlv, -1200, 150, 1);
+
+static const struct snd_kcontrol_new wm8955_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8955_LEFT_DAC_VOLUME,
+		 WM8955_RIGHT_DAC_VOLUME, 0, 255, 0, digital_tlv),
+SOC_SINGLE_TLV("Playback Attenuation Volume", WM8955_DAC_CONTROL, 7, 1, 1,
+	       atten_tlv),
+SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0,
+		    wm8955_get_deemph, wm8955_put_deemph),
+
+SOC_ENUM("Bass Mode", bass_mode),
+SOC_ENUM("Bass Cutoff", bass_cutoff),
+SOC_SINGLE("Bass Volume", WM8955_BASS_CONTROL, 0, 15, 1),
+
+SOC_ENUM("Treble Cutoff", treble_cutoff),
+SOC_SINGLE_TLV("Treble Volume", WM8955_TREBLE_CONTROL, 0, 14, 1, treble_tlv),
+
+SOC_SINGLE_TLV("Left Bypass Volume", WM8955_LEFT_OUT_MIX_1, 4, 7, 1,
+	       bypass_tlv),
+SOC_SINGLE_TLV("Left Mono Volume", WM8955_LEFT_OUT_MIX_2, 4, 7, 1,
+	       bypass_tlv),
+
+SOC_SINGLE_TLV("Right Mono Volume", WM8955_RIGHT_OUT_MIX_1, 4, 7, 1,
+	       bypass_tlv),
+SOC_SINGLE_TLV("Right Bypass Volume", WM8955_RIGHT_OUT_MIX_2, 4, 7, 1,
+	       bypass_tlv),
+
+/* Not a stereo pair so they line up with the DAPM switches */
+SOC_SINGLE_TLV("Mono Left Bypass Volume", WM8955_MONO_OUT_MIX_1, 4, 7, 1,
+	       mono_tlv),
+SOC_SINGLE_TLV("Mono Right Bypass Volume", WM8955_MONO_OUT_MIX_2, 4, 7, 1,
+	       mono_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Volume", WM8955_LOUT1_VOLUME,
+		 WM8955_ROUT1_VOLUME, 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8955_LOUT1_VOLUME,
+	     WM8955_ROUT1_VOLUME, 7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Speaker Volume", WM8955_LOUT2_VOLUME,
+		 WM8955_ROUT2_VOLUME, 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8955_LOUT2_VOLUME,
+	     WM8955_ROUT2_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("Mono Volume", WM8955_MONOOUT_VOLUME, 0, 127, 0, out_tlv),
+SOC_SINGLE("Mono ZC Switch", WM8955_MONOOUT_VOLUME, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new lmixer[] = {
+SOC_DAPM_SINGLE("Playback Switch", WM8955_LEFT_OUT_MIX_1, 8, 1, 0),
+SOC_DAPM_SINGLE("Bypass Switch", WM8955_LEFT_OUT_MIX_1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8955_LEFT_OUT_MIX_2, 8, 1, 0),
+SOC_DAPM_SINGLE("Mono Switch", WM8955_LEFT_OUT_MIX_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new rmixer[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8955_RIGHT_OUT_MIX_1, 8, 1, 0),
+SOC_DAPM_SINGLE("Mono Switch", WM8955_RIGHT_OUT_MIX_1, 7, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", WM8955_RIGHT_OUT_MIX_2, 8, 1, 0),
+SOC_DAPM_SINGLE("Bypass Switch", WM8955_RIGHT_OUT_MIX_2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new mmixer[] = {
+SOC_DAPM_SINGLE("Left Playback Switch", WM8955_MONO_OUT_MIX_1, 8, 1, 0),
+SOC_DAPM_SINGLE("Left Bypass Switch", WM8955_MONO_OUT_MIX_1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Playback Switch", WM8955_MONO_OUT_MIX_2, 8, 1, 0),
+SOC_DAPM_SINGLE("Right Bypass Switch", WM8955_MONO_OUT_MIX_2, 7, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8955_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MONOIN-"),
+SND_SOC_DAPM_INPUT("MONOIN+"),
+SND_SOC_DAPM_INPUT("LINEINR"),
+SND_SOC_DAPM_INPUT("LINEINL"),
+
+SND_SOC_DAPM_PGA("Mono Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("SYSCLK", WM8955_POWER_MANAGEMENT_1, 0, 1, wm8955_sysclk,
+		    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("TSDEN", WM8955_ADDITIONAL_CONTROL_1, 8, 0, NULL, 0),
+
+SND_SOC_DAPM_DAC("DACL", "Playback", WM8955_POWER_MANAGEMENT_2, 8, 0),
+SND_SOC_DAPM_DAC("DACR", "Playback", WM8955_POWER_MANAGEMENT_2, 7, 0),
+
+SND_SOC_DAPM_PGA("LOUT1 PGA", WM8955_POWER_MANAGEMENT_2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ROUT1 PGA", WM8955_POWER_MANAGEMENT_2, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LOUT2 PGA", WM8955_POWER_MANAGEMENT_2, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ROUT2 PGA", WM8955_POWER_MANAGEMENT_2, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA("MOUT PGA", WM8955_POWER_MANAGEMENT_2, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("OUT3 PGA", WM8955_POWER_MANAGEMENT_2, 1, 0, NULL, 0),
+
+/* The names are chosen to make the control names nice */
+SND_SOC_DAPM_MIXER("Left", SND_SOC_NOPM, 0, 0,
+		   lmixer, ARRAY_SIZE(lmixer)),
+SND_SOC_DAPM_MIXER("Right", SND_SOC_NOPM, 0, 0,
+		   rmixer, ARRAY_SIZE(rmixer)),
+SND_SOC_DAPM_MIXER("Mono", SND_SOC_NOPM, 0, 0,
+		   mmixer, ARRAY_SIZE(mmixer)),
+
+SND_SOC_DAPM_OUTPUT("LOUT1"),
+SND_SOC_DAPM_OUTPUT("ROUT1"),
+SND_SOC_DAPM_OUTPUT("LOUT2"),
+SND_SOC_DAPM_OUTPUT("ROUT2"),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+};
+
+static const struct snd_soc_dapm_route wm8955_intercon[] = {
+	{ "DACL", NULL, "SYSCLK" },
+	{ "DACR", NULL, "SYSCLK" },
+
+	{ "Mono Input", NULL, "MONOIN-" },
+	{ "Mono Input", NULL, "MONOIN+" },
+
+	{ "Left", "Playback Switch", "DACL" },
+	{ "Left", "Right Playback Switch", "DACR" },
+	{ "Left", "Bypass Switch", "LINEINL" },
+	{ "Left", "Mono Switch", "Mono Input" },
+
+	{ "Right", "Playback Switch", "DACR" },
+	{ "Right", "Left Playback Switch", "DACL" },
+	{ "Right", "Bypass Switch", "LINEINR" },
+	{ "Right", "Mono Switch", "Mono Input" },
+
+	{ "Mono", "Left Playback Switch", "DACL" },
+	{ "Mono", "Right Playback Switch", "DACR" },
+	{ "Mono", "Left Bypass Switch", "LINEINL" },
+	{ "Mono", "Right Bypass Switch", "LINEINR" },
+
+	{ "LOUT1 PGA", NULL, "Left" },
+	{ "LOUT1", NULL, "TSDEN" },
+	{ "LOUT1", NULL, "LOUT1 PGA" },
+
+	{ "ROUT1 PGA", NULL, "Right" },
+	{ "ROUT1", NULL, "TSDEN" },
+	{ "ROUT1", NULL, "ROUT1 PGA" },
+
+	{ "LOUT2 PGA", NULL, "Left" },
+	{ "LOUT2", NULL, "TSDEN" },
+	{ "LOUT2", NULL, "LOUT2 PGA" },
+
+	{ "ROUT2 PGA", NULL, "Right" },
+	{ "ROUT2", NULL, "TSDEN" },
+	{ "ROUT2", NULL, "ROUT2 PGA" },
+
+	{ "MOUT PGA", NULL, "Mono" },
+	{ "MONOOUT", NULL, "MOUT PGA" },
+
+	/* OUT3 not currently implemented */
+	{ "OUT3", NULL, "OUT3 PGA" },
+};
+
+static int wm8955_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_add_controls(codec, wm8955_snd_controls,
+			     ARRAY_SIZE(wm8955_snd_controls));
+
+	snd_soc_dapm_new_controls(codec, wm8955_dapm_widgets,
+				  ARRAY_SIZE(wm8955_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, wm8955_intercon,
+				ARRAY_SIZE(wm8955_intercon));
+
+	return 0;
+}
+
+static int wm8955_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 wm8955_priv *wm8955 = codec->private_data;
+	int ret;
+	int wl;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		wl = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		wl = 0x4;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		wl = 0x8;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		wl = 0xc;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_soc_update_bits(codec, WM8955_AUDIO_INTERFACE,
+			    WM8955_WL_MASK, wl);
+
+	wm8955->fs = params_rate(params);
+	wm8955_set_deemph(codec);
+
+	/* If the chip is clocked then disable the clocks and force a
+	 * reconfiguration, otherwise DAPM will power up the
+	 * clocks for us later. */
+	ret = snd_soc_read(codec, WM8955_POWER_MANAGEMENT_1);
+	if (ret < 0)
+		return ret;
+	if (ret & WM8955_DIGENB) {
+		snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+				    WM8955_DIGENB, 0);
+		snd_soc_update_bits(codec, WM8955_CLOCKING_PLL,
+				    WM8955_PLL_RB | WM8955_PLLEN, 0);
+
+		wm8955_configure_clocking(codec);
+	}
+
+	return 0;
+}
+
+
+static int wm8955_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+			     unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8955_priv *priv = codec->private_data;
+	int div;
+
+	switch (clk_id) {
+	case WM8955_CLK_MCLK:
+		if (freq > 15000000) {
+			priv->mclk_rate = freq /= 2;
+			div = WM8955_MCLKDIV2;
+		} else {
+			priv->mclk_rate = freq;
+			div = 0;
+		}
+
+		snd_soc_update_bits(codec, WM8955_SAMPLE_RATE,
+				    WM8955_MCLKDIV2, div);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+	return 0;
+}
+
+static int wm8955_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 aif = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		aif |= WM8955_MS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_B:
+		aif |= WM8955_LRP;
+	case SND_SOC_DAIFMT_DSP_A:
+		aif |= 0x3;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		aif |= 0x2;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		aif |= 0x1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+	case SND_SOC_DAIFMT_DSP_B:
+		/* frame inversion not valid for DSP modes */
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif |= WM8955_BCLKINV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_RIGHT_J:
+	case SND_SOC_DAIFMT_LEFT_J:
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_IF:
+			aif |= WM8955_BCLKINV | WM8955_LRP;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif |= WM8955_BCLKINV;
+			break;
+		case SND_SOC_DAIFMT_NB_IF:
+			aif |= WM8955_LRP;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, WM8955_AUDIO_INTERFACE,
+			    WM8955_MS | WM8955_FORMAT_MASK | WM8955_BCLKINV |
+			    WM8955_LRP, aif);
+
+	return 0;
+}
+
+
+static int wm8955_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	int val;
+
+	if (mute)
+		val = WM8955_DACMU;
+	else
+		val = 0;
+
+	snd_soc_update_bits(codec, WM8955_DAC_CONTROL, WM8955_DACMU, val);
+
+	return 0;
+}
+
+static int wm8955_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	struct wm8955_priv *wm8955 = codec->private_data;
+	int ret, i;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		/* VMID resistance 2*50k */
+		snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+				    WM8955_VMIDSEL_MASK,
+				    0x1 << WM8955_VMIDSEL_SHIFT);
+
+		/* Default bias current */
+		snd_soc_update_bits(codec, WM8955_ADDITIONAL_CONTROL_1,
+				    WM8955_VSEL_MASK,
+				    0x2 << WM8955_VSEL_SHIFT);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies),
+						    wm8955->supplies);
+			if (ret != 0) {
+				dev_err(codec->dev,
+					"Failed to enable supplies: %d\n",
+					ret);
+				return ret;
+			}
+
+			/* Sync back cached values if they're
+			 * different from the hardware default.
+			 */
+			for (i = 0; i < ARRAY_SIZE(wm8955->reg_cache); i++) {
+				if (i == WM8955_RESET)
+					continue;
+
+				if (wm8955->reg_cache[i] == wm8955_reg[i])
+					continue;
+
+				snd_soc_write(codec, i, wm8955->reg_cache[i]);
+			}
+
+			/* Enable VREF and VMID */
+			snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+					    WM8955_VREF |
+					    WM8955_VMIDSEL_MASK,
+					    WM8955_VREF |
+					    0x3 << WM8955_VREF_SHIFT);
+
+			/* Let VMID ramp */
+			msleep(500);
+
+			/* High resistance VROI to maintain outputs */
+			snd_soc_update_bits(codec,
+					    WM8955_ADDITIONAL_CONTROL_3,
+					    WM8955_VROI, WM8955_VROI);
+		}
+
+		/* Maintain VMID with 2*250k */
+		snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+				    WM8955_VMIDSEL_MASK,
+				    0x2 << WM8955_VMIDSEL_SHIFT);
+
+		/* Minimum bias current */
+		snd_soc_update_bits(codec, WM8955_ADDITIONAL_CONTROL_1,
+				    WM8955_VSEL_MASK, 0);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		/* Low resistance VROI to help discharge */
+		snd_soc_update_bits(codec,
+				    WM8955_ADDITIONAL_CONTROL_3,
+				    WM8955_VROI, 0);
+
+		/* Turn off VMID and VREF */
+		snd_soc_update_bits(codec, WM8955_POWER_MANAGEMENT_1,
+				    WM8955_VREF |
+				    WM8955_VMIDSEL_MASK, 0);
+
+		regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies),
+				       wm8955->supplies);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8955_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8955_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_ops wm8955_dai_ops = {
+	.set_sysclk = wm8955_set_sysclk,
+	.set_fmt = wm8955_set_fmt,
+	.hw_params = wm8955_hw_params,
+	.digital_mute = wm8955_digital_mute,
+};
+
+struct snd_soc_dai wm8955_dai = {
+	.name = "WM8955",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = WM8955_RATES,
+		.formats = WM8955_FORMATS,
+	},
+	.ops = &wm8955_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8955_dai);
+
+#ifdef CONFIG_PM
+static int wm8955_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm8955_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int wm8955_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+#else
+#define wm8955_suspend NULL
+#define wm8955_resume NULL
+#endif
+
+static int wm8955_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8955_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8955_codec;
+	codec = wm8955_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	wm8955_add_widgets(codec);
+
+	return ret;
+
+pcm_err:
+	return ret;
+}
+
+static int wm8955_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8955 = {
+	.probe = 	wm8955_probe,
+	.remove = 	wm8955_remove,
+	.suspend = 	wm8955_suspend,
+	.resume =	wm8955_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8955);
+
+static int wm8955_register(struct wm8955_priv *wm8955,
+			   enum snd_soc_control_type control)
+{
+	int ret;
+	struct snd_soc_codec *codec = &wm8955->codec;
+	int i;
+
+	if (wm8955_codec) {
+		dev_err(codec->dev, "Another WM8955 is registered\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8955;
+	codec->name = "WM8955";
+	codec->owner = THIS_MODULE;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8955_set_bias_level;
+	codec->dai = &wm8955_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = WM8955_MAX_REGISTER;
+	codec->reg_cache = &wm8955->reg_cache;
+
+	memcpy(codec->reg_cache, wm8955_reg, sizeof(wm8955_reg));
+
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(wm8955->supplies); i++)
+		wm8955->supplies[i].supply = wm8955_supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8955->supplies),
+				 wm8955->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+		goto err;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies),
+				    wm8955->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+		goto err_get;
+	}
+
+	ret = wm8955_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+		goto err_enable;
+	}
+
+	wm8955_dai.dev = codec->dev;
+
+	/* Change some default settings - latch VU and enable ZC */
+	wm8955->reg_cache[WM8955_LEFT_DAC_VOLUME] |= WM8955_LDVU;
+	wm8955->reg_cache[WM8955_RIGHT_DAC_VOLUME] |= WM8955_RDVU;
+	wm8955->reg_cache[WM8955_LOUT1_VOLUME] |= WM8955_LO1VU | WM8955_LO1ZC;
+	wm8955->reg_cache[WM8955_ROUT1_VOLUME] |= WM8955_RO1VU | WM8955_RO1ZC;
+	wm8955->reg_cache[WM8955_LOUT2_VOLUME] |= WM8955_LO2VU | WM8955_LO2ZC;
+	wm8955->reg_cache[WM8955_ROUT2_VOLUME] |= WM8955_RO2VU | WM8955_RO2ZC;
+	wm8955->reg_cache[WM8955_MONOOUT_VOLUME] |= WM8955_MOZC;
+
+	/* Also enable adaptive bass boost by default */
+	wm8955->reg_cache[WM8955_BASS_CONTROL] |= WM8955_BB;
+
+	/* Set platform data values */
+	if (wm8955->pdata) {
+		if (wm8955->pdata->out2_speaker)
+			wm8955->reg_cache[WM8955_ADDITIONAL_CONTROL_2]
+				|= WM8955_ROUT2INV;
+
+		if (wm8955->pdata->monoin_diff)
+			wm8955->reg_cache[WM8955_MONO_OUT_MIX_1]
+				|= WM8955_DMEN;
+	}
+
+	wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* Bias level configuration will have done an extra enable */
+	regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
+
+	wm8955_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_register_dai(&wm8955_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		return ret;
+	}
+
+	return 0;
+
+err_enable:
+	regulator_bulk_disable(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
+err_get:
+	regulator_bulk_free(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
+err:
+	kfree(wm8955);
+	return ret;
+}
+
+static void wm8955_unregister(struct wm8955_priv *wm8955)
+{
+	wm8955_set_bias_level(&wm8955->codec, SND_SOC_BIAS_OFF);
+	regulator_bulk_free(ARRAY_SIZE(wm8955->supplies), wm8955->supplies);
+	snd_soc_unregister_dai(&wm8955_dai);
+	snd_soc_unregister_codec(&wm8955->codec);
+	kfree(wm8955);
+	wm8955_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8955_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8955_priv *wm8955;
+	struct snd_soc_codec *codec;
+
+	wm8955 = kzalloc(sizeof(struct wm8955_priv), GFP_KERNEL);
+	if (wm8955 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8955->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(i2c, wm8955);
+	codec->control_data = i2c;
+	wm8955->pdata = i2c->dev.platform_data;
+
+	codec->dev = &i2c->dev;
+
+	return wm8955_register(wm8955, SND_SOC_I2C);
+}
+
+static __devexit int wm8955_i2c_remove(struct i2c_client *client)
+{
+	struct wm8955_priv *wm8955 = i2c_get_clientdata(client);
+	wm8955_unregister(wm8955);
+	return 0;
+}
+
+static const struct i2c_device_id wm8955_i2c_id[] = {
+	{ "wm8955", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8955_i2c_id);
+
+static struct i2c_driver wm8955_i2c_driver = {
+	.driver = {
+		.name = "wm8955",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm8955_i2c_probe,
+	.remove =   __devexit_p(wm8955_i2c_remove),
+	.id_table = wm8955_i2c_id,
+};
+#endif
+
+static int __init wm8955_modinit(void)
+{
+	int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&wm8955_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8955 I2C driver: %d\n",
+		       ret);
+	}
+#endif
+	return 0;
+}
+module_init(wm8955_modinit);
+
+static void __exit wm8955_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8955_i2c_driver);
+#endif
+}
+module_exit(wm8955_exit);
+
+MODULE_DESCRIPTION("ASoC WM8955 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8955.h b/sound/soc/codecs/wm8955.h
new file mode 100644
index 0000000..ae349c8
--- /dev/null
+++ b/sound/soc/codecs/wm8955.h
@@ -0,0 +1,489 @@
+/*
+ * wm8955.h  --  WM8904 ASoC driver
+ *
+ * Copyright 2009 Wolfson Microelectronics, plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8955_H
+#define _WM8955_H
+
+#define WM8955_CLK_MCLK 1
+
+extern struct snd_soc_dai wm8955_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8955;
+
+/*
+ * Register values.
+ */
+#define WM8955_LOUT1_VOLUME                     0x02
+#define WM8955_ROUT1_VOLUME                     0x03
+#define WM8955_DAC_CONTROL                      0x05
+#define WM8955_AUDIO_INTERFACE                  0x07
+#define WM8955_SAMPLE_RATE                      0x08
+#define WM8955_LEFT_DAC_VOLUME                  0x0A
+#define WM8955_RIGHT_DAC_VOLUME                 0x0B
+#define WM8955_BASS_CONTROL                     0x0C
+#define WM8955_TREBLE_CONTROL                   0x0D
+#define WM8955_RESET                            0x0F
+#define WM8955_ADDITIONAL_CONTROL_1             0x17
+#define WM8955_ADDITIONAL_CONTROL_2             0x18
+#define WM8955_POWER_MANAGEMENT_1               0x19
+#define WM8955_POWER_MANAGEMENT_2               0x1A
+#define WM8955_ADDITIONAL_CONTROL_3             0x1B
+#define WM8955_LEFT_OUT_MIX_1                   0x22
+#define WM8955_LEFT_OUT_MIX_2                   0x23
+#define WM8955_RIGHT_OUT_MIX_1                  0x24
+#define WM8955_RIGHT_OUT_MIX_2                  0x25
+#define WM8955_MONO_OUT_MIX_1                   0x26
+#define WM8955_MONO_OUT_MIX_2                   0x27
+#define WM8955_LOUT2_VOLUME                     0x28
+#define WM8955_ROUT2_VOLUME                     0x29
+#define WM8955_MONOOUT_VOLUME                   0x2A
+#define WM8955_CLOCKING_PLL                     0x2B
+#define WM8955_PLL_CONTROL_1                    0x2C
+#define WM8955_PLL_CONTROL_2                    0x2D
+#define WM8955_PLL_CONTROL_3                    0x2E
+#define WM8955_PLL_CONTROL_4                    0x3B
+
+#define WM8955_REGISTER_COUNT                   29
+#define WM8955_MAX_REGISTER                     0x3B
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R2 (0x02) - LOUT1 volume
+ */
+#define WM8955_LO1VU                            0x0100  /* LO1VU */
+#define WM8955_LO1VU_MASK                       0x0100  /* LO1VU */
+#define WM8955_LO1VU_SHIFT                           8  /* LO1VU */
+#define WM8955_LO1VU_WIDTH                           1  /* LO1VU */
+#define WM8955_LO1ZC                            0x0080  /* LO1ZC */
+#define WM8955_LO1ZC_MASK                       0x0080  /* LO1ZC */
+#define WM8955_LO1ZC_SHIFT                           7  /* LO1ZC */
+#define WM8955_LO1ZC_WIDTH                           1  /* LO1ZC */
+#define WM8955_LOUTVOL_MASK                     0x007F  /* LOUTVOL - [6:0] */
+#define WM8955_LOUTVOL_SHIFT                         0  /* LOUTVOL - [6:0] */
+#define WM8955_LOUTVOL_WIDTH                         7  /* LOUTVOL - [6:0] */
+
+/*
+ * R3 (0x03) - ROUT1 volume
+ */
+#define WM8955_RO1VU                            0x0100  /* RO1VU */
+#define WM8955_RO1VU_MASK                       0x0100  /* RO1VU */
+#define WM8955_RO1VU_SHIFT                           8  /* RO1VU */
+#define WM8955_RO1VU_WIDTH                           1  /* RO1VU */
+#define WM8955_RO1ZC                            0x0080  /* RO1ZC */
+#define WM8955_RO1ZC_MASK                       0x0080  /* RO1ZC */
+#define WM8955_RO1ZC_SHIFT                           7  /* RO1ZC */
+#define WM8955_RO1ZC_WIDTH                           1  /* RO1ZC */
+#define WM8955_ROUTVOL_MASK                     0x007F  /* ROUTVOL - [6:0] */
+#define WM8955_ROUTVOL_SHIFT                         0  /* ROUTVOL - [6:0] */
+#define WM8955_ROUTVOL_WIDTH                         7  /* ROUTVOL - [6:0] */
+
+/*
+ * R5 (0x05) - DAC Control
+ */
+#define WM8955_DAT                              0x0080  /* DAT */
+#define WM8955_DAT_MASK                         0x0080  /* DAT */
+#define WM8955_DAT_SHIFT                             7  /* DAT */
+#define WM8955_DAT_WIDTH                             1  /* DAT */
+#define WM8955_DACMU                            0x0008  /* DACMU */
+#define WM8955_DACMU_MASK                       0x0008  /* DACMU */
+#define WM8955_DACMU_SHIFT                           3  /* DACMU */
+#define WM8955_DACMU_WIDTH                           1  /* DACMU */
+#define WM8955_DEEMPH_MASK                      0x0006  /* DEEMPH - [2:1] */
+#define WM8955_DEEMPH_SHIFT                          1  /* DEEMPH - [2:1] */
+#define WM8955_DEEMPH_WIDTH                          2  /* DEEMPH - [2:1] */
+
+/*
+ * R7 (0x07) - Audio Interface
+ */
+#define WM8955_BCLKINV                          0x0080  /* BCLKINV */
+#define WM8955_BCLKINV_MASK                     0x0080  /* BCLKINV */
+#define WM8955_BCLKINV_SHIFT                         7  /* BCLKINV */
+#define WM8955_BCLKINV_WIDTH                         1  /* BCLKINV */
+#define WM8955_MS                               0x0040  /* MS */
+#define WM8955_MS_MASK                          0x0040  /* MS */
+#define WM8955_MS_SHIFT                              6  /* MS */
+#define WM8955_MS_WIDTH                              1  /* MS */
+#define WM8955_LRSWAP                           0x0020  /* LRSWAP */
+#define WM8955_LRSWAP_MASK                      0x0020  /* LRSWAP */
+#define WM8955_LRSWAP_SHIFT                          5  /* LRSWAP */
+#define WM8955_LRSWAP_WIDTH                          1  /* LRSWAP */
+#define WM8955_LRP                              0x0010  /* LRP */
+#define WM8955_LRP_MASK                         0x0010  /* LRP */
+#define WM8955_LRP_SHIFT                             4  /* LRP */
+#define WM8955_LRP_WIDTH                             1  /* LRP */
+#define WM8955_WL_MASK                          0x000C  /* WL - [3:2] */
+#define WM8955_WL_SHIFT                              2  /* WL - [3:2] */
+#define WM8955_WL_WIDTH                              2  /* WL - [3:2] */
+#define WM8955_FORMAT_MASK                      0x0003  /* FORMAT - [1:0] */
+#define WM8955_FORMAT_SHIFT                          0  /* FORMAT - [1:0] */
+#define WM8955_FORMAT_WIDTH                          2  /* FORMAT - [1:0] */
+
+/*
+ * R8 (0x08) - Sample Rate
+ */
+#define WM8955_BCLKDIV2                         0x0080  /* BCLKDIV2 */
+#define WM8955_BCLKDIV2_MASK                    0x0080  /* BCLKDIV2 */
+#define WM8955_BCLKDIV2_SHIFT                        7  /* BCLKDIV2 */
+#define WM8955_BCLKDIV2_WIDTH                        1  /* BCLKDIV2 */
+#define WM8955_MCLKDIV2                         0x0040  /* MCLKDIV2 */
+#define WM8955_MCLKDIV2_MASK                    0x0040  /* MCLKDIV2 */
+#define WM8955_MCLKDIV2_SHIFT                        6  /* MCLKDIV2 */
+#define WM8955_MCLKDIV2_WIDTH                        1  /* MCLKDIV2 */
+#define WM8955_SR_MASK                          0x003E  /* SR - [5:1] */
+#define WM8955_SR_SHIFT                              1  /* SR - [5:1] */
+#define WM8955_SR_WIDTH                              5  /* SR - [5:1] */
+#define WM8955_USB                              0x0001  /* USB */
+#define WM8955_USB_MASK                         0x0001  /* USB */
+#define WM8955_USB_SHIFT                             0  /* USB */
+#define WM8955_USB_WIDTH                             1  /* USB */
+
+/*
+ * R10 (0x0A) - Left DAC volume
+ */
+#define WM8955_LDVU                             0x0100  /* LDVU */
+#define WM8955_LDVU_MASK                        0x0100  /* LDVU */
+#define WM8955_LDVU_SHIFT                            8  /* LDVU */
+#define WM8955_LDVU_WIDTH                            1  /* LDVU */
+#define WM8955_LDACVOL_MASK                     0x00FF  /* LDACVOL - [7:0] */
+#define WM8955_LDACVOL_SHIFT                         0  /* LDACVOL - [7:0] */
+#define WM8955_LDACVOL_WIDTH                         8  /* LDACVOL - [7:0] */
+
+/*
+ * R11 (0x0B) - Right DAC volume
+ */
+#define WM8955_RDVU                             0x0100  /* RDVU */
+#define WM8955_RDVU_MASK                        0x0100  /* RDVU */
+#define WM8955_RDVU_SHIFT                            8  /* RDVU */
+#define WM8955_RDVU_WIDTH                            1  /* RDVU */
+#define WM8955_RDACVOL_MASK                     0x00FF  /* RDACVOL - [7:0] */
+#define WM8955_RDACVOL_SHIFT                         0  /* RDACVOL - [7:0] */
+#define WM8955_RDACVOL_WIDTH                         8  /* RDACVOL - [7:0] */
+
+/*
+ * R12 (0x0C) - Bass control
+ */
+#define WM8955_BB                               0x0080  /* BB */
+#define WM8955_BB_MASK                          0x0080  /* BB */
+#define WM8955_BB_SHIFT                              7  /* BB */
+#define WM8955_BB_WIDTH                              1  /* BB */
+#define WM8955_BC                               0x0040  /* BC */
+#define WM8955_BC_MASK                          0x0040  /* BC */
+#define WM8955_BC_SHIFT                              6  /* BC */
+#define WM8955_BC_WIDTH                              1  /* BC */
+#define WM8955_BASS_MASK                        0x000F  /* BASS - [3:0] */
+#define WM8955_BASS_SHIFT                            0  /* BASS - [3:0] */
+#define WM8955_BASS_WIDTH                            4  /* BASS - [3:0] */
+
+/*
+ * R13 (0x0D) - Treble control
+ */
+#define WM8955_TC                               0x0040  /* TC */
+#define WM8955_TC_MASK                          0x0040  /* TC */
+#define WM8955_TC_SHIFT                              6  /* TC */
+#define WM8955_TC_WIDTH                              1  /* TC */
+#define WM8955_TRBL_MASK                        0x000F  /* TRBL - [3:0] */
+#define WM8955_TRBL_SHIFT                            0  /* TRBL - [3:0] */
+#define WM8955_TRBL_WIDTH                            4  /* TRBL - [3:0] */
+
+/*
+ * R15 (0x0F) - Reset
+ */
+#define WM8955_RESET_MASK                       0x01FF  /* RESET - [8:0] */
+#define WM8955_RESET_SHIFT                           0  /* RESET - [8:0] */
+#define WM8955_RESET_WIDTH                           9  /* RESET - [8:0] */
+
+/*
+ * R23 (0x17) - Additional control (1)
+ */
+#define WM8955_TSDEN                            0x0100  /* TSDEN */
+#define WM8955_TSDEN_MASK                       0x0100  /* TSDEN */
+#define WM8955_TSDEN_SHIFT                           8  /* TSDEN */
+#define WM8955_TSDEN_WIDTH                           1  /* TSDEN */
+#define WM8955_VSEL_MASK                        0x00C0  /* VSEL - [7:6] */
+#define WM8955_VSEL_SHIFT                            6  /* VSEL - [7:6] */
+#define WM8955_VSEL_WIDTH                            2  /* VSEL - [7:6] */
+#define WM8955_DMONOMIX_MASK                    0x0030  /* DMONOMIX - [5:4] */
+#define WM8955_DMONOMIX_SHIFT                        4  /* DMONOMIX - [5:4] */
+#define WM8955_DMONOMIX_WIDTH                        2  /* DMONOMIX - [5:4] */
+#define WM8955_DACINV                           0x0002  /* DACINV */
+#define WM8955_DACINV_MASK                      0x0002  /* DACINV */
+#define WM8955_DACINV_SHIFT                          1  /* DACINV */
+#define WM8955_DACINV_WIDTH                          1  /* DACINV */
+#define WM8955_TOEN                             0x0001  /* TOEN */
+#define WM8955_TOEN_MASK                        0x0001  /* TOEN */
+#define WM8955_TOEN_SHIFT                            0  /* TOEN */
+#define WM8955_TOEN_WIDTH                            1  /* TOEN */
+
+/*
+ * R24 (0x18) - Additional control (2)
+ */
+#define WM8955_OUT3SW_MASK                      0x0180  /* OUT3SW - [8:7] */
+#define WM8955_OUT3SW_SHIFT                          7  /* OUT3SW - [8:7] */
+#define WM8955_OUT3SW_WIDTH                          2  /* OUT3SW - [8:7] */
+#define WM8955_ROUT2INV                         0x0010  /* ROUT2INV */
+#define WM8955_ROUT2INV_MASK                    0x0010  /* ROUT2INV */
+#define WM8955_ROUT2INV_SHIFT                        4  /* ROUT2INV */
+#define WM8955_ROUT2INV_WIDTH                        1  /* ROUT2INV */
+#define WM8955_DACOSR                           0x0001  /* DACOSR */
+#define WM8955_DACOSR_MASK                      0x0001  /* DACOSR */
+#define WM8955_DACOSR_SHIFT                          0  /* DACOSR */
+#define WM8955_DACOSR_WIDTH                          1  /* DACOSR */
+
+/*
+ * R25 (0x19) - Power Management (1)
+ */
+#define WM8955_VMIDSEL_MASK                     0x0180  /* VMIDSEL - [8:7] */
+#define WM8955_VMIDSEL_SHIFT                         7  /* VMIDSEL - [8:7] */
+#define WM8955_VMIDSEL_WIDTH                         2  /* VMIDSEL - [8:7] */
+#define WM8955_VREF                             0x0040  /* VREF */
+#define WM8955_VREF_MASK                        0x0040  /* VREF */
+#define WM8955_VREF_SHIFT                            6  /* VREF */
+#define WM8955_VREF_WIDTH                            1  /* VREF */
+#define WM8955_DIGENB                           0x0001  /* DIGENB */
+#define WM8955_DIGENB_MASK                      0x0001  /* DIGENB */
+#define WM8955_DIGENB_SHIFT                          0  /* DIGENB */
+#define WM8955_DIGENB_WIDTH                          1  /* DIGENB */
+
+/*
+ * R26 (0x1A) - Power Management (2)
+ */
+#define WM8955_DACL                             0x0100  /* DACL */
+#define WM8955_DACL_MASK                        0x0100  /* DACL */
+#define WM8955_DACL_SHIFT                            8  /* DACL */
+#define WM8955_DACL_WIDTH                            1  /* DACL */
+#define WM8955_DACR                             0x0080  /* DACR */
+#define WM8955_DACR_MASK                        0x0080  /* DACR */
+#define WM8955_DACR_SHIFT                            7  /* DACR */
+#define WM8955_DACR_WIDTH                            1  /* DACR */
+#define WM8955_LOUT1                            0x0040  /* LOUT1 */
+#define WM8955_LOUT1_MASK                       0x0040  /* LOUT1 */
+#define WM8955_LOUT1_SHIFT                           6  /* LOUT1 */
+#define WM8955_LOUT1_WIDTH                           1  /* LOUT1 */
+#define WM8955_ROUT1                            0x0020  /* ROUT1 */
+#define WM8955_ROUT1_MASK                       0x0020  /* ROUT1 */
+#define WM8955_ROUT1_SHIFT                           5  /* ROUT1 */
+#define WM8955_ROUT1_WIDTH                           1  /* ROUT1 */
+#define WM8955_LOUT2                            0x0010  /* LOUT2 */
+#define WM8955_LOUT2_MASK                       0x0010  /* LOUT2 */
+#define WM8955_LOUT2_SHIFT                           4  /* LOUT2 */
+#define WM8955_LOUT2_WIDTH                           1  /* LOUT2 */
+#define WM8955_ROUT2                            0x0008  /* ROUT2 */
+#define WM8955_ROUT2_MASK                       0x0008  /* ROUT2 */
+#define WM8955_ROUT2_SHIFT                           3  /* ROUT2 */
+#define WM8955_ROUT2_WIDTH                           1  /* ROUT2 */
+#define WM8955_MONO                             0x0004  /* MONO */
+#define WM8955_MONO_MASK                        0x0004  /* MONO */
+#define WM8955_MONO_SHIFT                            2  /* MONO */
+#define WM8955_MONO_WIDTH                            1  /* MONO */
+#define WM8955_OUT3                             0x0002  /* OUT3 */
+#define WM8955_OUT3_MASK                        0x0002  /* OUT3 */
+#define WM8955_OUT3_SHIFT                            1  /* OUT3 */
+#define WM8955_OUT3_WIDTH                            1  /* OUT3 */
+
+/*
+ * R27 (0x1B) - Additional Control (3)
+ */
+#define WM8955_VROI                             0x0040  /* VROI */
+#define WM8955_VROI_MASK                        0x0040  /* VROI */
+#define WM8955_VROI_SHIFT                            6  /* VROI */
+#define WM8955_VROI_WIDTH                            1  /* VROI */
+
+/*
+ * R34 (0x22) - Left out Mix (1)
+ */
+#define WM8955_LD2LO                            0x0100  /* LD2LO */
+#define WM8955_LD2LO_MASK                       0x0100  /* LD2LO */
+#define WM8955_LD2LO_SHIFT                           8  /* LD2LO */
+#define WM8955_LD2LO_WIDTH                           1  /* LD2LO */
+#define WM8955_LI2LO                            0x0080  /* LI2LO */
+#define WM8955_LI2LO_MASK                       0x0080  /* LI2LO */
+#define WM8955_LI2LO_SHIFT                           7  /* LI2LO */
+#define WM8955_LI2LO_WIDTH                           1  /* LI2LO */
+#define WM8955_LI2LOVOL_MASK                    0x0070  /* LI2LOVOL - [6:4] */
+#define WM8955_LI2LOVOL_SHIFT                        4  /* LI2LOVOL - [6:4] */
+#define WM8955_LI2LOVOL_WIDTH                        3  /* LI2LOVOL - [6:4] */
+
+/*
+ * R35 (0x23) - Left out Mix (2)
+ */
+#define WM8955_RD2LO                            0x0100  /* RD2LO */
+#define WM8955_RD2LO_MASK                       0x0100  /* RD2LO */
+#define WM8955_RD2LO_SHIFT                           8  /* RD2LO */
+#define WM8955_RD2LO_WIDTH                           1  /* RD2LO */
+#define WM8955_RI2LO                            0x0080  /* RI2LO */
+#define WM8955_RI2LO_MASK                       0x0080  /* RI2LO */
+#define WM8955_RI2LO_SHIFT                           7  /* RI2LO */
+#define WM8955_RI2LO_WIDTH                           1  /* RI2LO */
+#define WM8955_RI2LOVOL_MASK                    0x0070  /* RI2LOVOL - [6:4] */
+#define WM8955_RI2LOVOL_SHIFT                        4  /* RI2LOVOL - [6:4] */
+#define WM8955_RI2LOVOL_WIDTH                        3  /* RI2LOVOL - [6:4] */
+
+/*
+ * R36 (0x24) - Right out Mix (1)
+ */
+#define WM8955_LD2RO                            0x0100  /* LD2RO */
+#define WM8955_LD2RO_MASK                       0x0100  /* LD2RO */
+#define WM8955_LD2RO_SHIFT                           8  /* LD2RO */
+#define WM8955_LD2RO_WIDTH                           1  /* LD2RO */
+#define WM8955_LI2RO                            0x0080  /* LI2RO */
+#define WM8955_LI2RO_MASK                       0x0080  /* LI2RO */
+#define WM8955_LI2RO_SHIFT                           7  /* LI2RO */
+#define WM8955_LI2RO_WIDTH                           1  /* LI2RO */
+#define WM8955_LI2ROVOL_MASK                    0x0070  /* LI2ROVOL - [6:4] */
+#define WM8955_LI2ROVOL_SHIFT                        4  /* LI2ROVOL - [6:4] */
+#define WM8955_LI2ROVOL_WIDTH                        3  /* LI2ROVOL - [6:4] */
+
+/*
+ * R37 (0x25) - Right Out Mix (2)
+ */
+#define WM8955_RD2RO                            0x0100  /* RD2RO */
+#define WM8955_RD2RO_MASK                       0x0100  /* RD2RO */
+#define WM8955_RD2RO_SHIFT                           8  /* RD2RO */
+#define WM8955_RD2RO_WIDTH                           1  /* RD2RO */
+#define WM8955_RI2RO                            0x0080  /* RI2RO */
+#define WM8955_RI2RO_MASK                       0x0080  /* RI2RO */
+#define WM8955_RI2RO_SHIFT                           7  /* RI2RO */
+#define WM8955_RI2RO_WIDTH                           1  /* RI2RO */
+#define WM8955_RI2ROVOL_MASK                    0x0070  /* RI2ROVOL - [6:4] */
+#define WM8955_RI2ROVOL_SHIFT                        4  /* RI2ROVOL - [6:4] */
+#define WM8955_RI2ROVOL_WIDTH                        3  /* RI2ROVOL - [6:4] */
+
+/*
+ * R38 (0x26) - Mono out Mix (1)
+ */
+#define WM8955_LD2MO                            0x0100  /* LD2MO */
+#define WM8955_LD2MO_MASK                       0x0100  /* LD2MO */
+#define WM8955_LD2MO_SHIFT                           8  /* LD2MO */
+#define WM8955_LD2MO_WIDTH                           1  /* LD2MO */
+#define WM8955_LI2MO                            0x0080  /* LI2MO */
+#define WM8955_LI2MO_MASK                       0x0080  /* LI2MO */
+#define WM8955_LI2MO_SHIFT                           7  /* LI2MO */
+#define WM8955_LI2MO_WIDTH                           1  /* LI2MO */
+#define WM8955_LI2MOVOL_MASK                    0x0070  /* LI2MOVOL - [6:4] */
+#define WM8955_LI2MOVOL_SHIFT                        4  /* LI2MOVOL - [6:4] */
+#define WM8955_LI2MOVOL_WIDTH                        3  /* LI2MOVOL - [6:4] */
+#define WM8955_DMEN                             0x0001  /* DMEN */
+#define WM8955_DMEN_MASK                        0x0001  /* DMEN */
+#define WM8955_DMEN_SHIFT                            0  /* DMEN */
+#define WM8955_DMEN_WIDTH                            1  /* DMEN */
+
+/*
+ * R39 (0x27) - Mono out Mix (2)
+ */
+#define WM8955_RD2MO                            0x0100  /* RD2MO */
+#define WM8955_RD2MO_MASK                       0x0100  /* RD2MO */
+#define WM8955_RD2MO_SHIFT                           8  /* RD2MO */
+#define WM8955_RD2MO_WIDTH                           1  /* RD2MO */
+#define WM8955_RI2MO                            0x0080  /* RI2MO */
+#define WM8955_RI2MO_MASK                       0x0080  /* RI2MO */
+#define WM8955_RI2MO_SHIFT                           7  /* RI2MO */
+#define WM8955_RI2MO_WIDTH                           1  /* RI2MO */
+#define WM8955_RI2MOVOL_MASK                    0x0070  /* RI2MOVOL - [6:4] */
+#define WM8955_RI2MOVOL_SHIFT                        4  /* RI2MOVOL - [6:4] */
+#define WM8955_RI2MOVOL_WIDTH                        3  /* RI2MOVOL - [6:4] */
+
+/*
+ * R40 (0x28) - LOUT2 volume
+ */
+#define WM8955_LO2VU                            0x0100  /* LO2VU */
+#define WM8955_LO2VU_MASK                       0x0100  /* LO2VU */
+#define WM8955_LO2VU_SHIFT                           8  /* LO2VU */
+#define WM8955_LO2VU_WIDTH                           1  /* LO2VU */
+#define WM8955_LO2ZC                            0x0080  /* LO2ZC */
+#define WM8955_LO2ZC_MASK                       0x0080  /* LO2ZC */
+#define WM8955_LO2ZC_SHIFT                           7  /* LO2ZC */
+#define WM8955_LO2ZC_WIDTH                           1  /* LO2ZC */
+#define WM8955_LOUT2VOL_MASK                    0x007F  /* LOUT2VOL - [6:0] */
+#define WM8955_LOUT2VOL_SHIFT                        0  /* LOUT2VOL - [6:0] */
+#define WM8955_LOUT2VOL_WIDTH                        7  /* LOUT2VOL - [6:0] */
+
+/*
+ * R41 (0x29) - ROUT2 volume
+ */
+#define WM8955_RO2VU                            0x0100  /* RO2VU */
+#define WM8955_RO2VU_MASK                       0x0100  /* RO2VU */
+#define WM8955_RO2VU_SHIFT                           8  /* RO2VU */
+#define WM8955_RO2VU_WIDTH                           1  /* RO2VU */
+#define WM8955_RO2ZC                            0x0080  /* RO2ZC */
+#define WM8955_RO2ZC_MASK                       0x0080  /* RO2ZC */
+#define WM8955_RO2ZC_SHIFT                           7  /* RO2ZC */
+#define WM8955_RO2ZC_WIDTH                           1  /* RO2ZC */
+#define WM8955_ROUT2VOL_MASK                    0x007F  /* ROUT2VOL - [6:0] */
+#define WM8955_ROUT2VOL_SHIFT                        0  /* ROUT2VOL - [6:0] */
+#define WM8955_ROUT2VOL_WIDTH                        7  /* ROUT2VOL - [6:0] */
+
+/*
+ * R42 (0x2A) - MONOOUT volume
+ */
+#define WM8955_MOZC                             0x0080  /* MOZC */
+#define WM8955_MOZC_MASK                        0x0080  /* MOZC */
+#define WM8955_MOZC_SHIFT                            7  /* MOZC */
+#define WM8955_MOZC_WIDTH                            1  /* MOZC */
+#define WM8955_MOUTVOL_MASK                     0x007F  /* MOUTVOL - [6:0] */
+#define WM8955_MOUTVOL_SHIFT                         0  /* MOUTVOL - [6:0] */
+#define WM8955_MOUTVOL_WIDTH                         7  /* MOUTVOL - [6:0] */
+
+/*
+ * R43 (0x2B) - Clocking / PLL
+ */
+#define WM8955_MCLKSEL                          0x0100  /* MCLKSEL */
+#define WM8955_MCLKSEL_MASK                     0x0100  /* MCLKSEL */
+#define WM8955_MCLKSEL_SHIFT                         8  /* MCLKSEL */
+#define WM8955_MCLKSEL_WIDTH                         1  /* MCLKSEL */
+#define WM8955_PLLOUTDIV2                       0x0020  /* PLLOUTDIV2 */
+#define WM8955_PLLOUTDIV2_MASK                  0x0020  /* PLLOUTDIV2 */
+#define WM8955_PLLOUTDIV2_SHIFT                      5  /* PLLOUTDIV2 */
+#define WM8955_PLLOUTDIV2_WIDTH                      1  /* PLLOUTDIV2 */
+#define WM8955_PLL_RB                           0x0010  /* PLL_RB */
+#define WM8955_PLL_RB_MASK                      0x0010  /* PLL_RB */
+#define WM8955_PLL_RB_SHIFT                          4  /* PLL_RB */
+#define WM8955_PLL_RB_WIDTH                          1  /* PLL_RB */
+#define WM8955_PLLEN                            0x0008  /* PLLEN */
+#define WM8955_PLLEN_MASK                       0x0008  /* PLLEN */
+#define WM8955_PLLEN_SHIFT                           3  /* PLLEN */
+#define WM8955_PLLEN_WIDTH                           1  /* PLLEN */
+
+/*
+ * R44 (0x2C) - PLL Control 1
+ */
+#define WM8955_N_MASK                           0x01E0  /* N - [8:5] */
+#define WM8955_N_SHIFT                               5  /* N - [8:5] */
+#define WM8955_N_WIDTH                               4  /* N - [8:5] */
+#define WM8955_K_21_18_MASK                     0x000F  /* K(21:18) - [3:0] */
+#define WM8955_K_21_18_SHIFT                         0  /* K(21:18) - [3:0] */
+#define WM8955_K_21_18_WIDTH                         4  /* K(21:18) - [3:0] */
+
+/*
+ * R45 (0x2D) - PLL Control 2
+ */
+#define WM8955_K_17_9_MASK                      0x01FF  /* K(17:9) - [8:0] */
+#define WM8955_K_17_9_SHIFT                          0  /* K(17:9) - [8:0] */
+#define WM8955_K_17_9_WIDTH                          9  /* K(17:9) - [8:0] */
+
+/*
+ * R46 (0x2E) - PLL Control 3
+ */
+#define WM8955_K_8_0_MASK                       0x01FF  /* K(8:0) - [8:0] */
+#define WM8955_K_8_0_SHIFT                           0  /* K(8:0) - [8:0] */
+#define WM8955_K_8_0_WIDTH                           9  /* K(8:0) - [8:0] */
+
+/*
+ * R59 (0x3B) - PLL Control 4
+ */
+#define WM8955_KEN                              0x0080  /* KEN */
+#define WM8955_KEN_MASK                         0x0080  /* KEN */
+#define WM8955_KEN_SHIFT                             7  /* KEN */
+#define WM8955_KEN_WIDTH                             1  /* KEN */
+
+#endif
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index a8007d5..d2342c5 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -1022,6 +1022,9 @@
 	int i;
 
 	for (i = 0; i < codec->reg_cache_size; i++) {
+		if (reg_cache[i] == wm8961_reg_defaults[i])
+			continue;
+
 		if (i == WM8961_SOFTWARE_RESET)
 			continue;
 
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 8812751..ee637af 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -170,6 +170,10 @@
 
 SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST,  8, 1, 0),
 SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
+
+/* DAC / ADC oversampling */
+SOC_SINGLE("DAC 128x Oversampling Switch", WM8974_DAC, 8, 1, 0),
+SOC_SINGLE("ADC 128x Oversampling Switch", WM8974_ADC, 8, 1, 0),
 };
 
 /* Speaker Output Mixer */
@@ -381,14 +385,6 @@
 		reg = snd_soc_read(codec, WM8974_CLOCK) & 0x11f;
 		snd_soc_write(codec, WM8974_CLOCK, reg | div);
 		break;
-	case WM8974_ADCCLK:
-		reg = snd_soc_read(codec, WM8974_ADC) & 0x1f7;
-		snd_soc_write(codec, WM8974_ADC, reg | div);
-		break;
-	case WM8974_DACCLK:
-		reg = snd_soc_read(codec, WM8974_DAC) & 0x1f7;
-		snd_soc_write(codec, WM8974_DAC, reg | div);
-		break;
 	case WM8974_BCLKDIV:
 		reg = snd_soc_read(codec, WM8974_CLOCK) & 0x1e3;
 		snd_soc_write(codec, WM8974_CLOCK, reg | div);
diff --git a/sound/soc/codecs/wm8974.h b/sound/soc/codecs/wm8974.h
index 98de956..896a7f0 100644
--- a/sound/soc/codecs/wm8974.h
+++ b/sound/soc/codecs/wm8974.h
@@ -57,17 +57,7 @@
 /* Clock divider Id's */
 #define WM8974_OPCLKDIV		0
 #define WM8974_MCLKDIV		1
-#define WM8974_ADCCLK		2
-#define WM8974_DACCLK		3
-#define WM8974_BCLKDIV		4
-
-/* DAC clock dividers */
-#define WM8974_DACCLK_F2	(1 << 3)
-#define WM8974_DACCLK_F4	(0 << 3)
-
-/* ADC clock dividers */
-#define WM8974_ADCCLK_F2	(1 << 3)
-#define WM8974_ADCCLK_F4	(0 << 3)
+#define WM8974_BCLKDIV		2
 
 /* PLL Out dividers */
 #define WM8974_OPCLKDIV_1	(0 << 4)
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
new file mode 100644
index 0000000..28bb59e
--- /dev/null
+++ b/sound/soc/codecs/wm8978.c
@@ -0,0 +1,1149 @@
+/*
+ * wm8978.c  --  WM8978 ALSA SoC Audio Codec driver
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2007 Carlos Munoz <carlos@kenati.com>
+ * Copyright 2006-2009 Wolfson Microelectronics PLC.
+ * Based on wm8974 and wm8990 by Liam Girdwood <lrg@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include "wm8978.h"
+
+static struct snd_soc_codec *wm8978_codec;
+
+/* wm8978 register cache. Note that register 0 is not included in the cache. */
+static const u16 wm8978_reg[WM8978_CACHEREGNUM] = {
+	0x0000, 0x0000, 0x0000, 0x0000,	/* 0x00...0x03 */
+	0x0050, 0x0000, 0x0140, 0x0000,	/* 0x04...0x07 */
+	0x0000, 0x0000, 0x0000, 0x00ff,	/* 0x08...0x0b */
+	0x00ff, 0x0000, 0x0100, 0x00ff,	/* 0x0c...0x0f */
+	0x00ff, 0x0000, 0x012c, 0x002c,	/* 0x10...0x13 */
+	0x002c, 0x002c, 0x002c, 0x0000,	/* 0x14...0x17 */
+	0x0032, 0x0000, 0x0000, 0x0000,	/* 0x18...0x1b */
+	0x0000, 0x0000, 0x0000, 0x0000,	/* 0x1c...0x1f */
+	0x0038, 0x000b, 0x0032, 0x0000,	/* 0x20...0x23 */
+	0x0008, 0x000c, 0x0093, 0x00e9,	/* 0x24...0x27 */
+	0x0000, 0x0000, 0x0000, 0x0000,	/* 0x28...0x2b */
+	0x0033, 0x0010, 0x0010, 0x0100,	/* 0x2c...0x2f */
+	0x0100, 0x0002, 0x0001, 0x0001,	/* 0x30...0x33 */
+	0x0039, 0x0039, 0x0039, 0x0039,	/* 0x34...0x37 */
+	0x0001,	0x0001,			/* 0x38...0x3b */
+};
+
+/* codec private data */
+struct wm8978_priv {
+	struct snd_soc_codec codec;
+	unsigned int f_pllout;
+	unsigned int f_mclk;
+	unsigned int f_256fs;
+	unsigned int f_opclk;
+	int mclk_idx;
+	enum wm8978_sysclk_src sysclk;
+	u16 reg_cache[WM8978_CACHEREGNUM];
+};
+
+static const char *wm8978_companding[] = {"Off", "NC", "u-law", "A-law"};
+static const char *wm8978_eqmode[] = {"Capture", "Playback"};
+static const char *wm8978_bw[] = {"Narrow", "Wide"};
+static const char *wm8978_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz"};
+static const char *wm8978_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz"};
+static const char *wm8978_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz"};
+static const char *wm8978_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz"};
+static const char *wm8978_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz"};
+static const char *wm8978_alc3[] = {"ALC", "Limiter"};
+static const char *wm8978_alc1[] = {"Off", "Right", "Left", "Both"};
+
+static const SOC_ENUM_SINGLE_DECL(adc_compand, WM8978_COMPANDING_CONTROL, 1,
+				  wm8978_companding);
+static const SOC_ENUM_SINGLE_DECL(dac_compand, WM8978_COMPANDING_CONTROL, 3,
+				  wm8978_companding);
+static const SOC_ENUM_SINGLE_DECL(eqmode, WM8978_EQ1, 8, wm8978_eqmode);
+static const SOC_ENUM_SINGLE_DECL(eq1, WM8978_EQ1, 5, wm8978_eq1);
+static const SOC_ENUM_SINGLE_DECL(eq2bw, WM8978_EQ2, 8, wm8978_bw);
+static const SOC_ENUM_SINGLE_DECL(eq2, WM8978_EQ2, 5, wm8978_eq2);
+static const SOC_ENUM_SINGLE_DECL(eq3bw, WM8978_EQ3, 8, wm8978_bw);
+static const SOC_ENUM_SINGLE_DECL(eq3, WM8978_EQ3, 5, wm8978_eq3);
+static const SOC_ENUM_SINGLE_DECL(eq4bw, WM8978_EQ4, 8, wm8978_bw);
+static const SOC_ENUM_SINGLE_DECL(eq4, WM8978_EQ4, 5, wm8978_eq4);
+static const SOC_ENUM_SINGLE_DECL(eq5, WM8978_EQ5, 5, wm8978_eq5);
+static const SOC_ENUM_SINGLE_DECL(alc3, WM8978_ALC_CONTROL_3, 8, wm8978_alc3);
+static const SOC_ENUM_SINGLE_DECL(alc1, WM8978_ALC_CONTROL_1, 7, wm8978_alc1);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1);
+
+static const struct snd_kcontrol_new wm8978_snd_controls[] = {
+
+	SOC_SINGLE("Digital Loopback Switch",
+		WM8978_COMPANDING_CONTROL, 0, 1, 0),
+
+	SOC_ENUM("ADC Companding", adc_compand),
+	SOC_ENUM("DAC Companding", dac_compand),
+
+	SOC_DOUBLE("DAC Inversion Switch", WM8978_DAC_CONTROL, 0, 1, 1, 0),
+
+	SOC_DOUBLE_R_TLV("PCM Volume",
+		WM8978_LEFT_DAC_DIGITAL_VOLUME, WM8978_RIGHT_DAC_DIGITAL_VOLUME,
+		0, 255, 0, digital_tlv),
+
+	SOC_SINGLE("High Pass Filter Switch", WM8978_ADC_CONTROL, 8, 1, 0),
+	SOC_SINGLE("High Pass Cut Off", WM8978_ADC_CONTROL, 4, 7, 0),
+	SOC_DOUBLE("ADC Inversion Switch", WM8978_ADC_CONTROL, 0, 1, 1, 0),
+
+	SOC_DOUBLE_R_TLV("ADC Volume",
+		WM8978_LEFT_ADC_DIGITAL_VOLUME, WM8978_RIGHT_ADC_DIGITAL_VOLUME,
+		0, 255, 0, digital_tlv),
+
+	SOC_ENUM("Equaliser Function", eqmode),
+	SOC_ENUM("EQ1 Cut Off", eq1),
+	SOC_SINGLE_TLV("EQ1 Volume", WM8978_EQ1,  0, 24, 1, eq_tlv),
+
+	SOC_ENUM("Equaliser EQ2 Bandwith", eq2bw),
+	SOC_ENUM("EQ2 Cut Off", eq2),
+	SOC_SINGLE_TLV("EQ2 Volume", WM8978_EQ2,  0, 24, 1, eq_tlv),
+
+	SOC_ENUM("Equaliser EQ3 Bandwith", eq3bw),
+	SOC_ENUM("EQ3 Cut Off", eq3),
+	SOC_SINGLE_TLV("EQ3 Volume", WM8978_EQ3,  0, 24, 1, eq_tlv),
+
+	SOC_ENUM("Equaliser EQ4 Bandwith", eq4bw),
+	SOC_ENUM("EQ4 Cut Off", eq4),
+	SOC_SINGLE_TLV("EQ4 Volume", WM8978_EQ4,  0, 24, 1, eq_tlv),
+
+	SOC_ENUM("EQ5 Cut Off", eq5),
+	SOC_SINGLE_TLV("EQ5 Volume", WM8978_EQ5, 0, 24, 1, eq_tlv),
+
+	SOC_SINGLE("DAC Playback Limiter Switch",
+		WM8978_DAC_LIMITER_1, 8, 1, 0),
+	SOC_SINGLE("DAC Playback Limiter Decay",
+		WM8978_DAC_LIMITER_1, 4, 15, 0),
+	SOC_SINGLE("DAC Playback Limiter Attack",
+		WM8978_DAC_LIMITER_1, 0, 15, 0),
+
+	SOC_SINGLE("DAC Playback Limiter Threshold",
+		WM8978_DAC_LIMITER_2, 4, 7, 0),
+	SOC_SINGLE("DAC Playback Limiter Boost",
+		WM8978_DAC_LIMITER_2, 0, 15, 0),
+
+	SOC_ENUM("ALC Enable Switch", alc1),
+	SOC_SINGLE("ALC Capture Min Gain", WM8978_ALC_CONTROL_1, 0, 7, 0),
+	SOC_SINGLE("ALC Capture Max Gain", WM8978_ALC_CONTROL_1, 3, 7, 0),
+
+	SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 7, 0),
+	SOC_SINGLE("ALC Capture Target", WM8978_ALC_CONTROL_2, 0, 15, 0),
+
+	SOC_ENUM("ALC Capture Mode", alc3),
+	SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 15, 0),
+	SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 15, 0),
+
+	SOC_SINGLE("ALC Capture Noise Gate Switch", WM8978_NOISE_GATE, 3, 1, 0),
+	SOC_SINGLE("ALC Capture Noise Gate Threshold",
+		WM8978_NOISE_GATE, 0, 7, 0),
+
+	SOC_DOUBLE_R("Capture PGA ZC Switch",
+		WM8978_LEFT_INP_PGA_CONTROL, WM8978_RIGHT_INP_PGA_CONTROL,
+		7, 1, 0),
+
+	/* OUT1 - Headphones */
+	SOC_DOUBLE_R("Headphone Playback ZC Switch",
+		WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL, 7, 1, 0),
+
+	SOC_DOUBLE_R_TLV("Headphone Playback Volume",
+		WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL,
+		0, 63, 0, spk_tlv),
+
+	/* OUT2 - Speakers */
+	SOC_DOUBLE_R("Speaker Playback ZC Switch",
+		WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 7, 1, 0),
+
+	SOC_DOUBLE_R_TLV("Speaker Playback Volume",
+		WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL,
+		0, 63, 0, spk_tlv),
+
+	/* OUT3/4 - Line Output */
+	SOC_DOUBLE_R("Line Playback Switch",
+		WM8978_OUT3_MIXER_CONTROL, WM8978_OUT4_MIXER_CONTROL, 6, 1, 1),
+
+	/* Mixer #3: Boost (Input) mixer */
+	SOC_DOUBLE_R("PGA Boost (+20dB)",
+		WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL,
+		8, 1, 0),
+	SOC_DOUBLE_R_TLV("L2/R2 Boost Volume",
+		WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL,
+		4, 7, 0, boost_tlv),
+	SOC_DOUBLE_R_TLV("Aux Boost Volume",
+		WM8978_LEFT_ADC_BOOST_CONTROL, WM8978_RIGHT_ADC_BOOST_CONTROL,
+		0, 7, 0, boost_tlv),
+
+	/* Input PGA volume */
+	SOC_DOUBLE_R_TLV("Input PGA Volume",
+		WM8978_LEFT_INP_PGA_CONTROL, WM8978_RIGHT_INP_PGA_CONTROL,
+		0, 63, 0, inpga_tlv),
+
+	/* Headphone */
+	SOC_DOUBLE_R("Headphone Switch",
+		WM8978_LOUT1_HP_CONTROL, WM8978_ROUT1_HP_CONTROL, 6, 1, 1),
+
+	/* Speaker */
+	SOC_DOUBLE_R("Speaker Switch",
+		WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 6, 1, 1),
+
+	/* DAC / ADC oversampling */
+	SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL, 8, 1, 0),
+	SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL, 8, 1, 0),
+};
+
+/* Mixer #1: Output (OUT1, OUT2) Mixer: mix AUX, Input mixer output and DAC */
+static const struct snd_kcontrol_new wm8978_left_out_mixer[] = {
+	SOC_DAPM_SINGLE("Line Bypass Switch", WM8978_LEFT_MIXER_CONTROL, 1, 1, 0),
+	SOC_DAPM_SINGLE("Aux Playback Switch", WM8978_LEFT_MIXER_CONTROL, 5, 1, 0),
+	SOC_DAPM_SINGLE("PCM Playback Switch", WM8978_LEFT_MIXER_CONTROL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8978_right_out_mixer[] = {
+	SOC_DAPM_SINGLE("Line Bypass Switch", WM8978_RIGHT_MIXER_CONTROL, 1, 1, 0),
+	SOC_DAPM_SINGLE("Aux Playback Switch", WM8978_RIGHT_MIXER_CONTROL, 5, 1, 0),
+	SOC_DAPM_SINGLE("PCM Playback Switch", WM8978_RIGHT_MIXER_CONTROL, 0, 1, 0),
+};
+
+/* OUT3/OUT4 Mixer not implemented */
+
+/* Mixer #2: Input PGA Mute */
+static const struct snd_kcontrol_new wm8978_left_input_mixer[] = {
+	SOC_DAPM_SINGLE("L2 Switch", WM8978_INPUT_CONTROL, 2, 1, 0),
+	SOC_DAPM_SINGLE("MicN Switch", WM8978_INPUT_CONTROL, 1, 1, 0),
+	SOC_DAPM_SINGLE("MicP Switch", WM8978_INPUT_CONTROL, 0, 1, 0),
+};
+static const struct snd_kcontrol_new wm8978_right_input_mixer[] = {
+	SOC_DAPM_SINGLE("R2 Switch", WM8978_INPUT_CONTROL, 6, 1, 0),
+	SOC_DAPM_SINGLE("MicN Switch", WM8978_INPUT_CONTROL, 5, 1, 0),
+	SOC_DAPM_SINGLE("MicP Switch", WM8978_INPUT_CONTROL, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8978_dapm_widgets[] = {
+	SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
+			 WM8978_POWER_MANAGEMENT_3, 0, 0),
+	SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
+			 WM8978_POWER_MANAGEMENT_3, 1, 0),
+	SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture",
+			 WM8978_POWER_MANAGEMENT_2, 0, 0),
+	SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture",
+			 WM8978_POWER_MANAGEMENT_2, 1, 0),
+
+	/* Mixer #1: OUT1,2 */
+	SOC_MIXER_ARRAY("Left Output Mixer", WM8978_POWER_MANAGEMENT_3,
+			2, 0, wm8978_left_out_mixer),
+	SOC_MIXER_ARRAY("Right Output Mixer", WM8978_POWER_MANAGEMENT_3,
+			3, 0, wm8978_right_out_mixer),
+
+	SOC_MIXER_ARRAY("Left Input Mixer", WM8978_POWER_MANAGEMENT_2,
+			2, 0, wm8978_left_input_mixer),
+	SOC_MIXER_ARRAY("Right Input Mixer", WM8978_POWER_MANAGEMENT_2,
+			3, 0, wm8978_right_input_mixer),
+
+	SND_SOC_DAPM_PGA("Left Boost Mixer", WM8978_POWER_MANAGEMENT_2,
+			 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Boost Mixer", WM8978_POWER_MANAGEMENT_2,
+			 5, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("Left Capture PGA", WM8978_LEFT_INP_PGA_CONTROL,
+			 6, 1, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Capture PGA", WM8978_RIGHT_INP_PGA_CONTROL,
+			 6, 1, NULL, 0),
+
+	SND_SOC_DAPM_PGA("Left Headphone Out", WM8978_POWER_MANAGEMENT_2,
+			 7, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Headphone Out", WM8978_POWER_MANAGEMENT_2,
+			 8, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("Left Speaker Out", WM8978_POWER_MANAGEMENT_3,
+			 6, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Speaker Out", WM8978_POWER_MANAGEMENT_3,
+			 5, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER("OUT4 VMID", WM8978_POWER_MANAGEMENT_3,
+			   8, 0, NULL, 0),
+
+	SND_SOC_DAPM_MICBIAS("Mic Bias", WM8978_POWER_MANAGEMENT_1, 4, 0),
+
+	SND_SOC_DAPM_INPUT("LMICN"),
+	SND_SOC_DAPM_INPUT("LMICP"),
+	SND_SOC_DAPM_INPUT("RMICN"),
+	SND_SOC_DAPM_INPUT("RMICP"),
+	SND_SOC_DAPM_INPUT("LAUX"),
+	SND_SOC_DAPM_INPUT("RAUX"),
+	SND_SOC_DAPM_INPUT("L2"),
+	SND_SOC_DAPM_INPUT("R2"),
+	SND_SOC_DAPM_OUTPUT("LHP"),
+	SND_SOC_DAPM_OUTPUT("RHP"),
+	SND_SOC_DAPM_OUTPUT("LSPK"),
+	SND_SOC_DAPM_OUTPUT("RSPK"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Output mixer */
+	{"Right Output Mixer", "PCM Playback Switch", "Right DAC"},
+	{"Right Output Mixer", "Aux Playback Switch", "RAUX"},
+	{"Right Output Mixer", "Line Bypass Switch", "Right Boost Mixer"},
+
+	{"Left Output Mixer", "PCM Playback Switch", "Left DAC"},
+	{"Left Output Mixer", "Aux Playback Switch", "LAUX"},
+	{"Left Output Mixer", "Line Bypass Switch", "Left Boost Mixer"},
+
+	/* Outputs */
+	{"Right Headphone Out", NULL, "Right Output Mixer"},
+	{"RHP", NULL, "Right Headphone Out"},
+
+	{"Left Headphone Out", NULL, "Left Output Mixer"},
+	{"LHP", NULL, "Left Headphone Out"},
+
+	{"Right Speaker Out", NULL, "Right Output Mixer"},
+	{"RSPK", NULL, "Right Speaker Out"},
+
+	{"Left Speaker Out", NULL, "Left Output Mixer"},
+	{"LSPK", NULL, "Left Speaker Out"},
+
+	/* Boost Mixer */
+	{"Right ADC", NULL, "Right Boost Mixer"},
+
+	{"Right Boost Mixer", NULL, "RAUX"},
+	{"Right Boost Mixer", NULL, "Right Capture PGA"},
+	{"Right Boost Mixer", NULL, "R2"},
+
+	{"Left ADC", NULL, "Left Boost Mixer"},
+
+	{"Left Boost Mixer", NULL, "LAUX"},
+	{"Left Boost Mixer", NULL, "Left Capture PGA"},
+	{"Left Boost Mixer", NULL, "L2"},
+
+	/* Input PGA */
+	{"Right Capture PGA", NULL, "Right Input Mixer"},
+	{"Left Capture PGA", NULL, "Left Input Mixer"},
+
+	{"Right Input Mixer", "R2 Switch", "R2"},
+	{"Right Input Mixer", "MicN Switch", "RMICN"},
+	{"Right Input Mixer", "MicP Switch", "RMICP"},
+
+	{"Left Input Mixer", "L2 Switch", "L2"},
+	{"Left Input Mixer", "MicN Switch", "LMICN"},
+	{"Left Input Mixer", "MicP Switch", "LMICP"},
+};
+
+static int wm8978_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8978_dapm_widgets,
+				  ARRAY_SIZE(wm8978_dapm_widgets));
+
+	/* set up the WM8978 audio map */
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	return 0;
+}
+
+/* PLL divisors */
+struct wm8978_pll_div {
+	u32 k;
+	u8 n;
+	u8 div2;
+};
+
+#define FIXED_PLL_SIZE (1 << 24)
+
+static void pll_factors(struct wm8978_pll_div *pll_div, unsigned int target,
+			unsigned int source)
+{
+	u64 k_part;
+	unsigned int k, n_div, n_mod;
+
+	n_div = target / source;
+	if (n_div < 6) {
+		source >>= 1;
+		pll_div->div2 = 1;
+		n_div = target / source;
+	} else {
+		pll_div->div2 = 0;
+	}
+
+	if (n_div < 6 || n_div > 12)
+		dev_warn(wm8978_codec->dev,
+			 "WM8978 N value exceeds recommended range! N = %u\n",
+			 n_div);
+
+	pll_div->n = n_div;
+	n_mod = target - source * n_div;
+	k_part = FIXED_PLL_SIZE * (long long)n_mod + source / 2;
+
+	do_div(k_part, source);
+
+	k = k_part & 0xFFFFFFFF;
+
+	pll_div->k = k;
+}
+
+/* MCLK dividers */
+static const int mclk_numerator[]	= {1, 3, 2, 3, 4, 6, 8, 12};
+static const int mclk_denominator[]	= {1, 2, 1, 1, 1, 1, 1, 1};
+
+/*
+ * find index >= idx, such that, for a given f_out,
+ * 3 * f_mclk / 4 <= f_PLLOUT < 13 * f_mclk / 4
+ * f_out can be f_256fs or f_opclk, currently only used for f_256fs. Can be
+ * generalised for f_opclk with suitable coefficient arrays, but currently
+ * the OPCLK divisor is calculated directly, not iteratively.
+ */
+static int wm8978_enum_mclk(unsigned int f_out, unsigned int f_mclk,
+			    unsigned int *f_pllout)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) {
+		unsigned int f_pllout_x4 = 4 * f_out * mclk_numerator[i] /
+			mclk_denominator[i];
+		if (3 * f_mclk <= f_pllout_x4 && f_pllout_x4 < 13 * f_mclk) {
+			*f_pllout = f_pllout_x4 / 4;
+			return i;
+		}
+	}
+
+	return -EINVAL;
+}
+
+/*
+ * Calculate internal frequencies and dividers, according to Figure 40
+ * "PLL and Clock Select Circuit" in WM8978 datasheet Rev. 2.6
+ */
+static int wm8978_configure_pll(struct snd_soc_codec *codec)
+{
+	struct wm8978_priv *wm8978 = codec->private_data;
+	struct wm8978_pll_div pll_div;
+	unsigned int f_opclk = wm8978->f_opclk, f_mclk = wm8978->f_mclk,
+		f_256fs = wm8978->f_256fs;
+	unsigned int f2;
+
+	if (!f_mclk)
+		return -EINVAL;
+
+	if (f_opclk) {
+		unsigned int opclk_div;
+		/* Cannot set up MCLK divider now, do later */
+		wm8978->mclk_idx = -1;
+
+		/*
+		 * The user needs OPCLK. Choose OPCLKDIV to put
+		 * 6 <= R = f2 / f1 < 13, 1 <= OPCLKDIV <= 4.
+		 * f_opclk = f_mclk * prescale * R / 4 / OPCLKDIV, where
+		 * prescale = 1, or prescale = 2. Prescale is calculated inside
+		 * pll_factors(). We have to select f_PLLOUT, such that
+		 * f_mclk * 3 / 4 <= f_PLLOUT < f_mclk * 13 / 4. Must be
+		 * f_mclk * 3 / 16 <= f_opclk < f_mclk * 13 / 4.
+		 */
+		if (16 * f_opclk < 3 * f_mclk || 4 * f_opclk >= 13 * f_mclk)
+			return -EINVAL;
+
+		if (4 * f_opclk < 3 * f_mclk)
+			/* Have to use OPCLKDIV */
+			opclk_div = (3 * f_mclk / 4 + f_opclk - 1) / f_opclk;
+		else
+			opclk_div = 1;
+
+		dev_dbg(codec->dev, "%s: OPCLKDIV=%d\n", __func__, opclk_div);
+
+		snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 0x30,
+				    (opclk_div - 1) << 4);
+
+		wm8978->f_pllout = f_opclk * opclk_div;
+	} else if (f_256fs) {
+		/*
+		 * Not using OPCLK, but PLL is used for the codec, choose R:
+		 * 6 <= R = f2 / f1 < 13, to put 1 <= MCLKDIV <= 12.
+		 * f_256fs = f_mclk * prescale * R / 4 / MCLKDIV, where
+		 * prescale = 1, or prescale = 2. Prescale is calculated inside
+		 * pll_factors(). We have to select f_PLLOUT, such that
+		 * f_mclk * 3 / 4 <= f_PLLOUT < f_mclk * 13 / 4. Must be
+		 * f_mclk * 3 / 48 <= f_256fs < f_mclk * 13 / 4. This means MCLK
+		 * must be 3.781MHz <= f_MCLK <= 32.768MHz
+		 */
+		int idx = wm8978_enum_mclk(f_256fs, f_mclk, &wm8978->f_pllout);
+		if (idx < 0)
+			return idx;
+
+		wm8978->mclk_idx = idx;
+
+		/* GPIO1 into default mode as input - before configuring PLL */
+		snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 0);
+	} else {
+		return -EINVAL;
+	}
+
+	f2 = wm8978->f_pllout * 4;
+
+	dev_dbg(codec->dev, "%s: f_MCLK=%uHz, f_PLLOUT=%uHz\n", __func__,
+		wm8978->f_mclk, wm8978->f_pllout);
+
+	pll_factors(&pll_div, f2, wm8978->f_mclk);
+
+	dev_dbg(codec->dev, "%s: calculated PLL N=0x%x, K=0x%x, div2=%d\n",
+		__func__, pll_div.n, pll_div.k, pll_div.div2);
+
+	/* Turn PLL off for configuration... */
+	snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0);
+
+	snd_soc_write(codec, WM8978_PLL_N, (pll_div.div2 << 4) | pll_div.n);
+	snd_soc_write(codec, WM8978_PLL_K1, pll_div.k >> 18);
+	snd_soc_write(codec, WM8978_PLL_K2, (pll_div.k >> 9) & 0x1ff);
+	snd_soc_write(codec, WM8978_PLL_K3, pll_div.k & 0x1ff);
+
+	/* ...and on again */
+	snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20);
+
+	if (f_opclk)
+		/* Output PLL (OPCLK) to GPIO1 */
+		snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 4);
+
+	return 0;
+}
+
+/*
+ * Configure WM8978 clock dividers.
+ */
+static int wm8978_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+				 int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8978_priv *wm8978 = codec->private_data;
+	int ret = 0;
+
+	switch (div_id) {
+	case WM8978_OPCLKRATE:
+		wm8978->f_opclk = div;
+
+		if (wm8978->f_mclk)
+			/*
+			 * We know the MCLK frequency, the user has requested
+			 * OPCLK, configure the PLL based on that and start it
+			 * and OPCLK immediately. We will configure PLL to match
+			 * user-requested OPCLK frquency as good as possible.
+			 * In fact, it is likely, that matching the sampling
+			 * rate, when it becomes known, is more important, and
+			 * we will not be reconfiguring PLL then, because we
+			 * must not interrupt OPCLK. But it should be fine,
+			 * because typically the user will request OPCLK to run
+			 * at 256fs or 512fs, and for these cases we will also
+			 * find an exact MCLK divider configuration - it will
+			 * be equal to or double the OPCLK divisor.
+			 */
+			ret = wm8978_configure_pll(codec);
+		break;
+	case WM8978_BCLKDIV:
+		if (div & ~0x1c)
+			return -EINVAL;
+		snd_soc_update_bits(codec, WM8978_CLOCKING, 0x1c, div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	dev_dbg(codec->dev, "%s: ID %d, value %u\n", __func__, div_id, div);
+
+	return ret;
+}
+
+/*
+ * @freq:	when .set_pll() us not used, freq is codec MCLK input frequency
+ */
+static int wm8978_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+				 unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8978_priv *wm8978 = codec->private_data;
+	int ret = 0;
+
+	dev_dbg(codec->dev, "%s: ID %d, freq %u\n", __func__, clk_id, freq);
+
+	if (freq) {
+		wm8978->f_mclk = freq;
+
+		/* Even if MCLK is used for system clock, might have to drive OPCLK */
+		if (wm8978->f_opclk)
+			ret = wm8978_configure_pll(codec);
+
+		/* Our sysclk is fixed to 256 * fs, will configure in .hw_params()  */
+
+		if (!ret)
+			wm8978->sysclk = clk_id;
+	}
+
+	if (wm8978->sysclk == WM8978_PLL && (!freq || clk_id == WM8978_MCLK)) {
+		/* Clock CODEC directly from MCLK */
+		snd_soc_update_bits(codec, WM8978_CLOCKING, 0x100, 0);
+
+		/* GPIO1 into default mode as input - before configuring PLL */
+		snd_soc_update_bits(codec, WM8978_GPIO_CONTROL, 7, 0);
+
+		/* Turn off PLL */
+		snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0);
+		wm8978->sysclk = WM8978_MCLK;
+		wm8978->f_pllout = 0;
+		wm8978->f_opclk = 0;
+	}
+
+	return ret;
+}
+
+/*
+ * Set ADC and Voice DAC format.
+ */
+static int wm8978_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	/*
+	 * BCLK polarity mask = 0x100, LRC clock polarity mask = 0x80,
+	 * Data Format mask = 0x18: all will be calculated anew
+	 */
+	u16 iface = snd_soc_read(codec, WM8978_AUDIO_INTERFACE) & ~0x198;
+	u16 clk = snd_soc_read(codec, WM8978_CLOCKING);
+
+	dev_dbg(codec->dev, "%s\n", __func__);
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		clk |= 1;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		clk &= ~1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= 0x10;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x8;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x18;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= 0x180;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= 0x100;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x80;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, WM8978_AUDIO_INTERFACE, iface);
+	snd_soc_write(codec, WM8978_CLOCKING, clk);
+
+	return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8978_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct wm8978_priv *wm8978 = codec->private_data;
+	/* Word length mask = 0x60 */
+	u16 iface_ctl = snd_soc_read(codec, WM8978_AUDIO_INTERFACE) & ~0x60;
+	/* Sampling rate mask = 0xe (for filters) */
+	u16 add_ctl = snd_soc_read(codec, WM8978_ADDITIONAL_CONTROL) & ~0xe;
+	u16 clking = snd_soc_read(codec, WM8978_CLOCKING);
+	enum wm8978_sysclk_src current_clk_id = clking & 0x100 ?
+		WM8978_PLL : WM8978_MCLK;
+	unsigned int f_sel, diff, diff_best = INT_MAX;
+	int i, best = 0;
+
+	if (!wm8978->f_mclk)
+		return -EINVAL;
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface_ctl |= 0x20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface_ctl |= 0x40;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface_ctl |= 0x60;
+		break;
+	}
+
+	/* filter coefficient */
+	switch (params_rate(params)) {
+	case 8000:
+		add_ctl |= 0x5 << 1;
+		break;
+	case 11025:
+		add_ctl |= 0x4 << 1;
+		break;
+	case 16000:
+		add_ctl |= 0x3 << 1;
+		break;
+	case 22050:
+		add_ctl |= 0x2 << 1;
+		break;
+	case 32000:
+		add_ctl |= 0x1 << 1;
+		break;
+	case 44100:
+	case 48000:
+		break;
+	}
+
+	/* Sampling rate is known now, can configure the MCLK divider */
+	wm8978->f_256fs = params_rate(params) * 256;
+
+	if (wm8978->sysclk == WM8978_MCLK) {
+		wm8978->mclk_idx = -1;
+		f_sel = wm8978->f_mclk;
+	} else {
+		if (!wm8978->f_pllout) {
+			/* We only enter here, if OPCLK is not used */
+			int ret = wm8978_configure_pll(codec);
+			if (ret < 0)
+				return ret;
+		}
+		f_sel = wm8978->f_pllout;
+	}
+
+	if (wm8978->mclk_idx < 0) {
+		/* Either MCLK is used directly, or OPCLK is used */
+		if (f_sel < wm8978->f_256fs || f_sel > 12 * wm8978->f_256fs)
+			return -EINVAL;
+
+		for (i = 0; i < ARRAY_SIZE(mclk_numerator); i++) {
+			diff = abs(wm8978->f_256fs * 3 -
+				   f_sel * 3 * mclk_denominator[i] / mclk_numerator[i]);
+
+			if (diff < diff_best) {
+				diff_best = diff;
+				best = i;
+			}
+
+			if (!diff)
+				break;
+		}
+	} else {
+		/* OPCLK not used, codec driven by PLL */
+		best = wm8978->mclk_idx;
+		diff = 0;
+	}
+
+	if (diff)
+		dev_warn(codec->dev, "Imprecise sampling rate: %uHz%s\n",
+			f_sel * mclk_denominator[best] / mclk_numerator[best] / 256,
+			wm8978->sysclk == WM8978_MCLK ?
+			", consider using PLL" : "");
+
+	dev_dbg(codec->dev, "%s: fmt %d, rate %u, MCLK divisor #%d\n", __func__,
+		params_format(params), params_rate(params), best);
+
+	/* MCLK divisor mask = 0xe0 */
+	snd_soc_update_bits(codec, WM8978_CLOCKING, 0xe0, best << 5);
+
+	snd_soc_write(codec, WM8978_AUDIO_INTERFACE, iface_ctl);
+	snd_soc_write(codec, WM8978_ADDITIONAL_CONTROL, add_ctl);
+
+	if (wm8978->sysclk != current_clk_id) {
+		if (wm8978->sysclk == WM8978_PLL)
+			/* Run CODEC from PLL instead of MCLK */
+			snd_soc_update_bits(codec, WM8978_CLOCKING,
+					    0x100, 0x100);
+		else
+			/* Clock CODEC directly from MCLK */
+			snd_soc_update_bits(codec, WM8978_CLOCKING, 0x100, 0);
+	}
+
+	return 0;
+}
+
+static int wm8978_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	dev_dbg(codec->dev, "%s: %d\n", __func__, mute);
+
+	if (mute)
+		snd_soc_update_bits(codec, WM8978_DAC_CONTROL, 0x40, 0x40);
+	else
+		snd_soc_update_bits(codec, WM8978_DAC_CONTROL, 0x40, 0);
+
+	return 0;
+}
+
+static int wm8978_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	u16 power1 = snd_soc_read(codec, WM8978_POWER_MANAGEMENT_1) & ~3;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+		power1 |= 1;  /* VMID 75k */
+		snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* bit 3: enable bias, bit 2: enable I/O tie off buffer */
+		power1 |= 0xc;
+
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* Initial cap charge at VMID 5k */
+			snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1,
+				      power1 | 0x3);
+			mdelay(100);
+		}
+
+		power1 |= 0x2;  /* VMID 500k */
+		snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, power1);
+		break;
+	case SND_SOC_BIAS_OFF:
+		/* Preserve PLL - OPCLK may be used by someone */
+		snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, ~0x20, 0);
+		snd_soc_write(codec, WM8978_POWER_MANAGEMENT_2, 0);
+		snd_soc_write(codec, WM8978_POWER_MANAGEMENT_3, 0);
+		break;
+	}
+
+	dev_dbg(codec->dev, "%s: %d, %x\n", __func__, level, power1);
+
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8978_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_ops wm8978_dai_ops = {
+	.hw_params	= wm8978_hw_params,
+	.digital_mute	= wm8978_mute,
+	.set_fmt	= wm8978_set_dai_fmt,
+	.set_clkdiv	= wm8978_set_dai_clkdiv,
+	.set_sysclk	= wm8978_set_dai_sysclk,
+};
+
+/* Also supports 12kHz */
+struct snd_soc_dai wm8978_dai = {
+	.name = "WM8978 HiFi",
+	.id = 1,
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = WM8978_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = WM8978_FORMATS,
+	},
+	.ops = &wm8978_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8978_dai);
+
+static int wm8978_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm8978_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	/* Also switch PLL off */
+	snd_soc_write(codec, WM8978_POWER_MANAGEMENT_1, 0);
+
+	return 0;
+}
+
+static int wm8978_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct wm8978_priv *wm8978 = codec->private_data;
+	int i;
+	u16 *cache = codec->reg_cache;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < ARRAY_SIZE(wm8978_reg); i++) {
+		if (i == WM8978_RESET)
+			continue;
+		if (cache[i] != wm8978_reg[i])
+			snd_soc_write(codec, i, cache[i]);
+	}
+
+	wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	if (wm8978->f_pllout)
+		/* Switch PLL on */
+		snd_soc_update_bits(codec, WM8978_POWER_MANAGEMENT_1, 0x20, 0x20);
+
+	return 0;
+}
+
+static int wm8978_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8978_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8978_codec;
+	codec = wm8978_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, wm8978_snd_controls,
+			     ARRAY_SIZE(wm8978_snd_controls));
+	wm8978_add_widgets(codec);
+
+pcm_err:
+	return ret;
+}
+
+/* power down chip */
+static int wm8978_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8978 = {
+	.probe		= wm8978_probe,
+	.remove		= wm8978_remove,
+	.suspend	= wm8978_suspend,
+	.resume		= wm8978_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8978);
+
+/*
+ * These registers contain an "update" bit - bit 8. This means, for example,
+ * that one can write new DAC digital volume for both channels, but only when
+ * the update bit is set, will also the volume be updated - simultaneously for
+ * both channels.
+ */
+static const int update_reg[] = {
+	WM8978_LEFT_DAC_DIGITAL_VOLUME,
+	WM8978_RIGHT_DAC_DIGITAL_VOLUME,
+	WM8978_LEFT_ADC_DIGITAL_VOLUME,
+	WM8978_RIGHT_ADC_DIGITAL_VOLUME,
+	WM8978_LEFT_INP_PGA_CONTROL,
+	WM8978_RIGHT_INP_PGA_CONTROL,
+	WM8978_LOUT1_HP_CONTROL,
+	WM8978_ROUT1_HP_CONTROL,
+	WM8978_LOUT2_SPK_CONTROL,
+	WM8978_ROUT2_SPK_CONTROL,
+};
+
+static __devinit int wm8978_register(struct wm8978_priv *wm8978)
+{
+	int ret, i;
+	struct snd_soc_codec *codec = &wm8978->codec;
+
+	if (wm8978_codec) {
+		dev_err(codec->dev, "Another WM8978 is registered\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Set default system clock to PLL, it is more precise, this is also the
+	 * default hardware setting
+	 */
+	wm8978->sysclk = WM8978_PLL;
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8978;
+	codec->name = "WM8978";
+	codec->owner = THIS_MODULE;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8978_set_bias_level;
+	codec->dai = &wm8978_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = WM8978_CACHEREGNUM;
+	codec->reg_cache = &wm8978->reg_cache;
+
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, SND_SOC_I2C);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	memcpy(codec->reg_cache, wm8978_reg, sizeof(wm8978_reg));
+
+	/*
+	 * Set the update bit in all registers, that have one. This way all
+	 * writes to those registers will also cause the update bit to be
+	 * written.
+	 */
+	for (i = 0; i < ARRAY_SIZE(update_reg); i++)
+		((u16 *)codec->reg_cache)[update_reg[i]] |= 0x100;
+
+	/* Reset the codec */
+	ret = snd_soc_write(codec, WM8978_RESET, 0);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		goto err;
+	}
+
+	wm8978_dai.dev = codec->dev;
+
+	wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	wm8978_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto err;
+	}
+
+	ret = snd_soc_register_dai(&wm8978_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		goto err_codec;
+	}
+
+	return 0;
+
+err_codec:
+	snd_soc_unregister_codec(codec);
+err:
+	kfree(wm8978);
+	return ret;
+}
+
+static __devexit void wm8978_unregister(struct wm8978_priv *wm8978)
+{
+	wm8978_set_bias_level(&wm8978->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dai(&wm8978_dai);
+	snd_soc_unregister_codec(&wm8978->codec);
+	kfree(wm8978);
+	wm8978_codec = NULL;
+}
+
+static __devinit int wm8978_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8978_priv *wm8978;
+	struct snd_soc_codec *codec;
+
+	wm8978 = kzalloc(sizeof(struct wm8978_priv), GFP_KERNEL);
+	if (wm8978 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8978->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(i2c, wm8978);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+
+	return wm8978_register(wm8978);
+}
+
+static __devexit int wm8978_i2c_remove(struct i2c_client *client)
+{
+	struct wm8978_priv *wm8978 = i2c_get_clientdata(client);
+	wm8978_unregister(wm8978);
+	return 0;
+}
+
+static const struct i2c_device_id wm8978_i2c_id[] = {
+	{ "wm8978", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id);
+
+static struct i2c_driver wm8978_i2c_driver = {
+	.driver = {
+		.name = "WM8978",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm8978_i2c_probe,
+	.remove =   __devexit_p(wm8978_i2c_remove),
+	.id_table = wm8978_i2c_id,
+};
+
+static int __init wm8978_modinit(void)
+{
+	return i2c_add_driver(&wm8978_i2c_driver);
+}
+module_init(wm8978_modinit);
+
+static void __exit wm8978_exit(void)
+{
+	i2c_del_driver(&wm8978_i2c_driver);
+}
+module_exit(wm8978_exit);
+
+MODULE_DESCRIPTION("ASoC WM8978 codec driver");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8978.h b/sound/soc/codecs/wm8978.h
new file mode 100644
index 0000000..56ec832
--- /dev/null
+++ b/sound/soc/codecs/wm8978.h
@@ -0,0 +1,86 @@
+/*
+ * wm8978.h		--  codec driver for WM8978
+ *
+ * Copyright 2009 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * 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 __WM8978_H__
+#define __WM8978_H__
+
+/*
+ * Register values.
+ */
+#define WM8978_RESET				0x00
+#define WM8978_POWER_MANAGEMENT_1		0x01
+#define WM8978_POWER_MANAGEMENT_2		0x02
+#define WM8978_POWER_MANAGEMENT_3		0x03
+#define WM8978_AUDIO_INTERFACE			0x04
+#define WM8978_COMPANDING_CONTROL		0x05
+#define WM8978_CLOCKING				0x06
+#define WM8978_ADDITIONAL_CONTROL		0x07
+#define WM8978_GPIO_CONTROL			0x08
+#define WM8978_JACK_DETECT_CONTROL_1		0x09
+#define WM8978_DAC_CONTROL			0x0A
+#define WM8978_LEFT_DAC_DIGITAL_VOLUME		0x0B
+#define WM8978_RIGHT_DAC_DIGITAL_VOLUME		0x0C
+#define WM8978_JACK_DETECT_CONTROL_2		0x0D
+#define WM8978_ADC_CONTROL			0x0E
+#define WM8978_LEFT_ADC_DIGITAL_VOLUME		0x0F
+#define WM8978_RIGHT_ADC_DIGITAL_VOLUME		0x10
+#define WM8978_EQ1				0x12
+#define WM8978_EQ2				0x13
+#define WM8978_EQ3				0x14
+#define WM8978_EQ4				0x15
+#define WM8978_EQ5				0x16
+#define WM8978_DAC_LIMITER_1			0x18
+#define WM8978_DAC_LIMITER_2			0x19
+#define WM8978_NOTCH_FILTER_1			0x1b
+#define WM8978_NOTCH_FILTER_2			0x1c
+#define WM8978_NOTCH_FILTER_3			0x1d
+#define WM8978_NOTCH_FILTER_4			0x1e
+#define WM8978_ALC_CONTROL_1			0x20
+#define WM8978_ALC_CONTROL_2			0x21
+#define WM8978_ALC_CONTROL_3			0x22
+#define WM8978_NOISE_GATE			0x23
+#define WM8978_PLL_N				0x24
+#define WM8978_PLL_K1				0x25
+#define WM8978_PLL_K2				0x26
+#define WM8978_PLL_K3				0x27
+#define WM8978_3D_CONTROL			0x29
+#define WM8978_BEEP_CONTROL			0x2b
+#define WM8978_INPUT_CONTROL			0x2c
+#define WM8978_LEFT_INP_PGA_CONTROL		0x2d
+#define WM8978_RIGHT_INP_PGA_CONTROL		0x2e
+#define WM8978_LEFT_ADC_BOOST_CONTROL		0x2f
+#define WM8978_RIGHT_ADC_BOOST_CONTROL		0x30
+#define WM8978_OUTPUT_CONTROL			0x31
+#define WM8978_LEFT_MIXER_CONTROL		0x32
+#define WM8978_RIGHT_MIXER_CONTROL		0x33
+#define WM8978_LOUT1_HP_CONTROL			0x34
+#define WM8978_ROUT1_HP_CONTROL			0x35
+#define WM8978_LOUT2_SPK_CONTROL		0x36
+#define WM8978_ROUT2_SPK_CONTROL		0x37
+#define WM8978_OUT3_MIXER_CONTROL		0x38
+#define WM8978_OUT4_MIXER_CONTROL		0x39
+
+#define WM8978_CACHEREGNUM			58
+
+/* Clock divider Id's */
+enum wm8978_clk_id {
+	WM8978_OPCLKRATE,
+	WM8978_BCLKDIV,
+};
+
+enum wm8978_sysclk_src {
+	WM8978_PLL,
+	WM8978_MCLK
+};
+
+extern struct snd_soc_dai wm8978_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8978;
+
+#endif	/* __WM8978_H__ */
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 341481e..a54dc77b 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1319,10 +1319,6 @@
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->card->codec;
 
-	/* we only need to suspend if we are a valid card */
-	if (!codec->card)
-		return 0;
-
 	wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
@@ -1335,10 +1331,6 @@
 	u8 data[2];
 	u16 *cache = codec->reg_cache;
 
-	/* we only need to resume if we are a valid card */
-	if (!codec->card)
-		return 0;
-
 	/* Sync reg_cache with the hardware */
 	for (i = 0; i < ARRAY_SIZE(wm8990_reg); i++) {
 		if (i + 1 == WM8990_RESET)
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 2981afa..bf022f6 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1,7 +1,7 @@
 /*
  * wm8993.c -- WM8993 ALSA SoC audio driver
  *
- * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2009, 2010 Wolfson Microelectronics plc
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
@@ -16,6 +16,7 @@
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
+#include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -29,6 +30,16 @@
 #include "wm8993.h"
 #include "wm_hubs.h"
 
+#define WM8993_NUM_SUPPLIES 6
+static const char *wm8993_supply_names[WM8993_NUM_SUPPLIES] = {
+	"DCVDD",
+	"DBVDD",
+	"AVDD1",
+	"AVDD2",
+	"CPVDD",
+	"SPKVDD",
+};
+
 static u16 wm8993_reg_defaults[WM8993_REGISTER_COUNT] = {
 	0x8993,     /* R0   - Software Reset */
 	0x0000,     /* R1   - Power Management (1) */
@@ -213,7 +224,9 @@
 };
 
 struct wm8993_priv {
+	struct wm_hubs_data hubs_data;
 	u16 reg_cache[WM8993_REGISTER_COUNT];
+	struct regulator_bulk_data supplies[WM8993_NUM_SUPPLIES];
 	struct wm8993_platform_data pdata;
 	struct snd_soc_codec codec;
 	int master;
@@ -227,36 +240,9 @@
 	int class_w_users;
 	unsigned int fll_fref;
 	unsigned int fll_fout;
+	int fll_src;
 };
 
-static unsigned int wm8993_read_hw(struct snd_soc_codec *codec, u8 reg)
-{
-	struct i2c_msg xfer[2];
-	u16 data;
-	int ret;
-	struct i2c_client *i2c = codec->control_data;
-
-	/* Write register */
-	xfer[0].addr = i2c->addr;
-	xfer[0].flags = 0;
-	xfer[0].len = 1;
-	xfer[0].buf = &reg;
-
-	/* Read data */
-	xfer[1].addr = i2c->addr;
-	xfer[1].flags = I2C_M_RD;
-	xfer[1].len = 2;
-	xfer[1].buf = (u8 *)&data;
-
-	ret = i2c_transfer(i2c->adapter, xfer, 2);
-	if (ret != 2) {
-		dev_err(codec->dev, "Failed to read 0x%x: %d\n", reg, ret);
-		return 0;
-	}
-
-	return (data >> 8) | ((data & 0xff) << 8);
-}
-
 static int wm8993_volatile(unsigned int reg)
 {
 	switch (reg) {
@@ -271,48 +257,6 @@
 	}
 }
 
-static unsigned int wm8993_read(struct snd_soc_codec *codec,
-				unsigned int reg)
-{
-	u16 *reg_cache = codec->reg_cache;
-
-	BUG_ON(reg > WM8993_MAX_REGISTER);
-
-	if (wm8993_volatile(reg))
-		return wm8993_read_hw(codec, reg);
-	else
-		return reg_cache[reg];
-}
-
-static int wm8993_write(struct snd_soc_codec *codec, unsigned int reg,
-			unsigned int value)
-{
-	u16 *reg_cache = codec->reg_cache;
-	u8 data[3];
-	int ret;
-
-	BUG_ON(reg > WM8993_MAX_REGISTER);
-
-	/* data is
-	 *   D15..D9 WM8993 register offset
-	 *   D8...D0 register data
-	 */
-	data[0] = reg;
-	data[1] = value >> 8;
-	data[2] = value & 0x00ff;
-
-	if (!wm8993_volatile(reg))
-		reg_cache[reg] = value;
-
-	ret = codec->hw_write(codec->control_data, data, 3);
-
-	if (ret == 3)
-		return 0;
-	if (ret < 0)
-		return ret;
-	return -EIO;
-}
-
 struct _fll_div {
 	u16 fll_fratio;
 	u16 fll_outdiv;
@@ -441,9 +385,9 @@
 		wm8993->fll_fref = 0;
 		wm8993->fll_fout = 0;
 
-		reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1);
+		reg1 = snd_soc_read(codec, WM8993_FLL_CONTROL_1);
 		reg1 &= ~WM8993_FLL_ENA;
-		wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+		snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
 
 		return 0;
 	}
@@ -452,7 +396,7 @@
 	if (ret != 0)
 		return ret;
 
-	reg5 = wm8993_read(codec, WM8993_FLL_CONTROL_5);
+	reg5 = snd_soc_read(codec, WM8993_FLL_CONTROL_5);
 	reg5 &= ~WM8993_FLL_CLK_SRC_MASK;
 
 	switch (fll_id) {
@@ -474,38 +418,39 @@
 
 	/* Any FLL configuration change requires that the FLL be
 	 * disabled first. */
-	reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1);
+	reg1 = snd_soc_read(codec, WM8993_FLL_CONTROL_1);
 	reg1 &= ~WM8993_FLL_ENA;
-	wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+	snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
 
 	/* Apply the configuration */
 	if (fll_div.k)
 		reg1 |= WM8993_FLL_FRAC_MASK;
 	else
 		reg1 &= ~WM8993_FLL_FRAC_MASK;
-	wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+	snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1);
 
-	wm8993_write(codec, WM8993_FLL_CONTROL_2,
-		     (fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) |
-		     (fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT));
-	wm8993_write(codec, WM8993_FLL_CONTROL_3, fll_div.k);
+	snd_soc_write(codec, WM8993_FLL_CONTROL_2,
+		      (fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) |
+		      (fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT));
+	snd_soc_write(codec, WM8993_FLL_CONTROL_3, fll_div.k);
 
-	reg4 = wm8993_read(codec, WM8993_FLL_CONTROL_4);
+	reg4 = snd_soc_read(codec, WM8993_FLL_CONTROL_4);
 	reg4 &= ~WM8993_FLL_N_MASK;
 	reg4 |= fll_div.n << WM8993_FLL_N_SHIFT;
-	wm8993_write(codec, WM8993_FLL_CONTROL_4, reg4);
+	snd_soc_write(codec, WM8993_FLL_CONTROL_4, reg4);
 
 	reg5 &= ~WM8993_FLL_CLK_REF_DIV_MASK;
 	reg5 |= fll_div.fll_clk_ref_div << WM8993_FLL_CLK_REF_DIV_SHIFT;
-	wm8993_write(codec, WM8993_FLL_CONTROL_5, reg5);
+	snd_soc_write(codec, WM8993_FLL_CONTROL_5, reg5);
 
 	/* Enable the FLL */
-	wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA);
+	snd_soc_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA);
 
 	dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
 
 	wm8993->fll_fref = Fref;
 	wm8993->fll_fout = Fout;
+	wm8993->fll_src = source;
 
 	return 0;
 }
@@ -520,7 +465,7 @@
 	case WM8993_SYSCLK_MCLK:
 		dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8993->mclk_rate);
 
-		reg = wm8993_read(codec, WM8993_CLOCKING_2);
+		reg = snd_soc_read(codec, WM8993_CLOCKING_2);
 		reg &= ~(WM8993_MCLK_DIV | WM8993_SYSCLK_SRC);
 		if (wm8993->mclk_rate > 13500000) {
 			reg |= WM8993_MCLK_DIV;
@@ -529,14 +474,14 @@
 			reg &= ~WM8993_MCLK_DIV;
 			wm8993->sysclk_rate = wm8993->mclk_rate;
 		}
-		wm8993_write(codec, WM8993_CLOCKING_2, reg);
+		snd_soc_write(codec, WM8993_CLOCKING_2, reg);
 		break;
 
 	case WM8993_SYSCLK_FLL:
 		dev_dbg(codec->dev, "Using %dHz FLL clock\n",
 			wm8993->fll_fout);
 
-		reg = wm8993_read(codec, WM8993_CLOCKING_2);
+		reg = snd_soc_read(codec, WM8993_CLOCKING_2);
 		reg |= WM8993_SYSCLK_SRC;
 		if (wm8993->fll_fout > 13500000) {
 			reg |= WM8993_MCLK_DIV;
@@ -545,7 +490,7 @@
 			reg &= ~WM8993_MCLK_DIV;
 			wm8993->sysclk_rate = wm8993->fll_fout;
 		}
-		wm8993_write(codec, WM8993_CLOCKING_2, reg);
+		snd_soc_write(codec, WM8993_CLOCKING_2, reg);
 		break;
 
 	default:
@@ -978,10 +923,33 @@
 	{ "Right Headphone Mux", "DAC", "DACR" },
 };
 
+static void wm8993_cache_restore(struct snd_soc_codec *codec)
+{
+	u16 *cache = codec->reg_cache;
+	int i;
+
+	if (!codec->cache_sync)
+		return;
+
+	/* Reenable hardware writes */
+	codec->cache_only = 0;
+
+	/* Restore the register settings */
+	for (i = 1; i < WM8993_MAX_REGISTER; i++) {
+		if (cache[i] == wm8993_reg_defaults[i])
+			continue;
+		snd_soc_write(codec, i, cache[i]);
+	}
+
+	/* We're in sync again */
+	codec->cache_sync = 0;
+}
+
 static int wm8993_set_bias_level(struct snd_soc_codec *codec,
 				 enum snd_soc_bias_level level)
 {
 	struct wm8993_priv *wm8993 = codec->private_data;
+	int ret;
 
 	switch (level) {
 	case SND_SOC_BIAS_ON:
@@ -995,6 +963,18 @@
 
 	case SND_SOC_BIAS_STANDBY:
 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies),
+						    wm8993->supplies);
+			if (ret != 0)
+				return ret;
+
+			wm8993_cache_restore(codec);
+
+			/* Tune DC servo configuration */
+			snd_soc_write(codec, 0x44, 3);
+			snd_soc_write(codec, 0x56, 3);
+			snd_soc_write(codec, 0x44, 0);
+
 			/* Bring up VMID with fast soft start */
 			snd_soc_update_bits(codec, WM8993_ANTIPOP2,
 					    WM8993_STARTUP_BIAS_ENA |
@@ -1042,6 +1022,18 @@
 		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
 				    WM8993_VMID_SEL_MASK | WM8993_BIAS_ENA,
 				    0);
+
+#ifdef CONFIG_REGULATOR
+               /* Post 2.6.34 we will be able to get a callback when
+                * the regulators are disabled which we can use but
+		* for now just assume that the power will be cut if
+		* the regulator API is in use.
+		*/
+		codec->cache_sync = 1;
+#endif
+
+		regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies),
+				       wm8993->supplies);
 		break;
 	}
 
@@ -1075,8 +1067,8 @@
 {
 	struct snd_soc_codec *codec = dai->codec;
 	struct wm8993_priv *wm8993 = codec->private_data;
-	unsigned int aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1);
-	unsigned int aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4);
+	unsigned int aif1 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_1);
+	unsigned int aif4 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_4);
 
 	aif1 &= ~(WM8993_BCLK_DIR | WM8993_AIF_BCLK_INV |
 		  WM8993_AIF_LRCLK_INV | WM8993_AIF_FMT_MASK);
@@ -1159,8 +1151,8 @@
 		return -EINVAL;
 	}
 
-	wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
-	wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+	snd_soc_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+	snd_soc_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
 
 	return 0;
 }
@@ -1174,16 +1166,16 @@
 	int ret, i, best, best_val, cur_val;
 	unsigned int clocking1, clocking3, aif1, aif4;
 
-	clocking1 = wm8993_read(codec, WM8993_CLOCKING_1);
+	clocking1 = snd_soc_read(codec, WM8993_CLOCKING_1);
 	clocking1 &= ~WM8993_BCLK_DIV_MASK;
 
-	clocking3 = wm8993_read(codec, WM8993_CLOCKING_3);
+	clocking3 = snd_soc_read(codec, WM8993_CLOCKING_3);
 	clocking3 &= ~(WM8993_CLK_SYS_RATE_MASK | WM8993_SAMPLE_RATE_MASK);
 
-	aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1);
+	aif1 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_1);
 	aif1 &= ~WM8993_AIF_WL_MASK;
 
-	aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4);
+	aif4 = snd_soc_read(codec, WM8993_AUDIO_INTERFACE_4);
 	aif4 &= ~WM8993_LRCLK_RATE_MASK;
 
 	/* What BCLK do we need? */
@@ -1276,14 +1268,14 @@
 	dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8993->bclk / wm8993->fs);
 	aif4 |= wm8993->bclk / wm8993->fs;
 
-	wm8993_write(codec, WM8993_CLOCKING_1, clocking1);
-	wm8993_write(codec, WM8993_CLOCKING_3, clocking3);
-	wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
-	wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+	snd_soc_write(codec, WM8993_CLOCKING_1, clocking1);
+	snd_soc_write(codec, WM8993_CLOCKING_3, clocking3);
+	snd_soc_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+	snd_soc_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
 
 	/* ReTune Mobile? */
 	if (wm8993->pdata.num_retune_configs) {
-		u16 eq1 = wm8993_read(codec, WM8993_EQ1);
+		u16 eq1 = snd_soc_read(codec, WM8993_EQ1);
 		struct wm8993_retune_mobile_setting *s;
 
 		best = 0;
@@ -1306,7 +1298,7 @@
 		snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, 0);
 
 		for (i = 1; i < ARRAY_SIZE(s->config); i++)
-			wm8993_write(codec, WM8993_EQ1 + i, s->config[i]);
+			snd_soc_write(codec, WM8993_EQ1 + i, s->config[i]);
 
 		snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, eq1);
 	}
@@ -1319,14 +1311,14 @@
 	struct snd_soc_codec *codec = codec_dai->codec;
 	unsigned int reg;
 
-	reg = wm8993_read(codec, WM8993_DAC_CTRL);
+	reg = snd_soc_read(codec, WM8993_DAC_CTRL);
 
 	if (mute)
 		reg |= WM8993_DAC_MUTE;
 	else
 		reg &= ~WM8993_DAC_MUTE;
 
-	wm8993_write(codec, WM8993_DAC_CTRL, reg);
+	snd_soc_write(codec, WM8993_DAC_CTRL, reg);
 
 	return 0;
 }
@@ -1480,9 +1472,66 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8993_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct wm8993_priv *wm8993 = codec->private_data;
+	int fll_fout = wm8993->fll_fout;
+	int fll_fref  = wm8993->fll_fref;
+	int ret;
+
+	/* Stop the FLL in an orderly fashion */
+	ret = wm8993_set_fll(codec->dai, 0, 0, 0, 0);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "Failed to stop FLL\n");
+		return ret;
+	}
+
+	wm8993->fll_fout = fll_fout;
+	wm8993->fll_fref = fll_fref;
+
+	wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int wm8993_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct wm8993_priv *wm8993 = codec->private_data;
+	int ret;
+
+	wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* Restart the FLL? */
+	if (wm8993->fll_fout) {
+		int fll_fout = wm8993->fll_fout;
+		int fll_fref  = wm8993->fll_fref;
+
+		wm8993->fll_fref = 0;
+		wm8993->fll_fout = 0;
+
+		ret = wm8993_set_fll(codec->dai, 0, wm8993->fll_src,
+				     fll_fref, fll_fout);
+		if (ret != 0)
+			dev_err(codec->dev, "Failed to restart FLL\n");
+	}
+
+	return 0;
+}
+#else
+#define wm8993_suspend NULL
+#define wm8993_resume NULL
+#endif
+
 struct snd_soc_codec_device soc_codec_dev_wm8993 = {
 	.probe = 	wm8993_probe,
 	.remove = 	wm8993_remove,
+	.suspend =	wm8993_suspend,
+	.resume =	wm8993_resume,
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8993);
 
@@ -1493,6 +1542,7 @@
 	struct snd_soc_codec *codec;
 	unsigned int val;
 	int ret;
+	int i;
 
 	if (wm8993_codec) {
 		dev_err(&i2c->dev, "A WM8993 is already registered\n");
@@ -1513,9 +1563,7 @@
 	INIT_LIST_HEAD(&codec->dapm_paths);
 
 	codec->name = "WM8993";
-	codec->read = wm8993_read;
-	codec->write = wm8993_write;
-	codec->hw_write = (hw_write_t)i2c_master_send;
+	codec->volatile_register = wm8993_volatile;
 	codec->reg_cache = wm8993->reg_cache;
 	codec->reg_cache_size = ARRAY_SIZE(wm8993->reg_cache);
 	codec->bias_level = SND_SOC_BIAS_OFF;
@@ -1524,25 +1572,53 @@
 	codec->num_dai = 1;
 	codec->private_data = wm8993;
 
+	wm8993->hubs_data.hp_startup_mode = 1;
+	wm8993->hubs_data.dcs_codes = -2;
+
 	memcpy(wm8993->reg_cache, wm8993_reg_defaults,
 	       sizeof(wm8993->reg_cache));
 
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
 	i2c_set_clientdata(i2c, wm8993);
 	codec->control_data = i2c;
 	wm8993_codec = codec;
 
 	codec->dev = &i2c->dev;
 
-	val = wm8993_read_hw(codec, WM8993_SOFTWARE_RESET);
-	if (val != wm8993_reg_defaults[WM8993_SOFTWARE_RESET]) {
-		dev_err(codec->dev, "Invalid ID register value %x\n", val);
-		ret = -EINVAL;
+	for (i = 0; i < ARRAY_SIZE(wm8993->supplies); i++)
+		wm8993->supplies[i].supply = wm8993_supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8993->supplies),
+				 wm8993->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
 		goto err;
 	}
 
-	ret = wm8993_write(codec, WM8993_SOFTWARE_RESET, 0xffff);
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8993->supplies),
+				    wm8993->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+		goto err_get;
+	}
+
+	val = snd_soc_read(codec, WM8993_SOFTWARE_RESET);
+	if (val != wm8993_reg_defaults[WM8993_SOFTWARE_RESET]) {
+		dev_err(codec->dev, "Invalid ID register value %x\n", val);
+		ret = -EINVAL;
+		goto err_enable;
+	}
+
+	ret = snd_soc_write(codec, WM8993_SOFTWARE_RESET, 0xffff);
 	if (ret != 0)
-		goto err;
+		goto err_enable;
+
+	codec->cache_only = 1;
 
 	/* By default we're using the output mixers */
 	wm8993->class_w_users = 2;
@@ -1572,7 +1648,7 @@
 			     
 	ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	if (ret != 0)
-		goto err;
+		goto err_enable;
 
 	wm8993_dai.dev = codec->dev;
 
@@ -1586,6 +1662,10 @@
 
 err_bias:
 	wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
+err_enable:
+	regulator_bulk_disable(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
+err_get:
+	regulator_bulk_free(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
 err:
 	wm8993_codec = NULL;
 	kfree(wm8993);
@@ -1600,6 +1680,7 @@
 	snd_soc_unregister_dai(&wm8993_dai);
 
 	wm8993_set_bias_level(&wm8993->codec, SND_SOC_BIAS_OFF);
+	regulator_bulk_free(ARRAY_SIZE(wm8993->supplies), wm8993->supplies);
 	kfree(wm8993);
 
 	return 0;
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
new file mode 100644
index 0000000..29f3771
--- /dev/null
+++ b/sound/soc/codecs/wm8994.c
@@ -0,0 +1,3867 @@
+/*
+ * wm8994.c  --  WM8994 ALSA SoC Audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/wm8994/core.h>
+#include <linux/mfd/wm8994/registers.h>
+#include <linux/mfd/wm8994/pdata.h>
+#include <linux/mfd/wm8994/gpio.h>
+
+#include "wm8994.h"
+#include "wm_hubs.h"
+
+static struct snd_soc_codec *wm8994_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8994;
+
+struct fll_config {
+	int src;
+	int in;
+	int out;
+};
+
+#define WM8994_NUM_DRC 3
+#define WM8994_NUM_EQ  3
+
+static int wm8994_drc_base[] = {
+	WM8994_AIF1_DRC1_1,
+	WM8994_AIF1_DRC2_1,
+	WM8994_AIF2_DRC_1,
+};
+
+static int wm8994_retune_mobile_base[] = {
+	WM8994_AIF1_DAC1_EQ_GAINS_1,
+	WM8994_AIF1_DAC2_EQ_GAINS_1,
+	WM8994_AIF2_EQ_GAINS_1,
+};
+
+#define WM8994_REG_CACHE_SIZE  0x621
+
+/* codec private data */
+struct wm8994_priv {
+	struct wm_hubs_data hubs;
+	struct snd_soc_codec codec;
+	u16 reg_cache[WM8994_REG_CACHE_SIZE + 1];
+	int sysclk[2];
+	int sysclk_rate[2];
+	int mclk[2];
+	int aifclk[2];
+	struct fll_config fll[2], fll_suspend[2];
+
+	int dac_rates[2];
+	int lrclk_shared[2];
+
+	/* Platform dependant DRC configuration */
+	const char **drc_texts;
+	int drc_cfg[WM8994_NUM_DRC];
+	struct soc_enum drc_enum;
+
+	/* Platform dependant ReTune mobile configuration */
+	int num_retune_mobile_texts;
+	const char **retune_mobile_texts;
+	int retune_mobile_cfg[WM8994_NUM_EQ];
+	struct soc_enum retune_mobile_enum;
+
+	struct wm8994_pdata *pdata;
+};
+
+static struct {
+	unsigned short  readable;   /* Mask of readable bits */
+	unsigned short  writable;   /* Mask of writable bits */
+	unsigned short  vol;        /* Mask of volatile bits */
+} access_masks[] = {
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R0     - Software Reset */
+	{ 0x3B37, 0x3B37, 0x0000 }, /* R1     - Power Management (1) */
+	{ 0x6BF0, 0x6BF0, 0x0000 }, /* R2     - Power Management (2) */
+	{ 0x3FF0, 0x3FF0, 0x0000 }, /* R3     - Power Management (3) */
+	{ 0x3F3F, 0x3F3F, 0x0000 }, /* R4     - Power Management (4) */
+	{ 0x3F0F, 0x3F0F, 0x0000 }, /* R5     - Power Management (5) */
+	{ 0x003F, 0x003F, 0x0000 }, /* R6     - Power Management (6) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R7 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R8 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R9 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R10 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R11 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R12 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R13 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R14 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R15 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R16 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R17 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R18 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R19 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R20 */
+	{ 0x01C0, 0x01C0, 0x0000 }, /* R21    - Input Mixer (1) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R22 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R23 */
+	{ 0x00DF, 0x01DF, 0x0000 }, /* R24    - Left Line Input 1&2 Volume */
+	{ 0x00DF, 0x01DF, 0x0000 }, /* R25    - Left Line Input 3&4 Volume */
+	{ 0x00DF, 0x01DF, 0x0000 }, /* R26    - Right Line Input 1&2 Volume */
+	{ 0x00DF, 0x01DF, 0x0000 }, /* R27    - Right Line Input 3&4 Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R28    - Left Output Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R29    - Right Output Volume */
+	{ 0x0077, 0x0077, 0x0000 }, /* R30    - Line Outputs Volume */
+	{ 0x0030, 0x0030, 0x0000 }, /* R31    - HPOUT2 Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R32    - Left OPGA Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R33    - Right OPGA Volume */
+	{ 0x007F, 0x007F, 0x0000 }, /* R34    - SPKMIXL Attenuation */
+	{ 0x017F, 0x017F, 0x0000 }, /* R35    - SPKMIXR Attenuation */
+	{ 0x003F, 0x003F, 0x0000 }, /* R36    - SPKOUT Mixers */
+	{ 0x003F, 0x003F, 0x0000 }, /* R37    - ClassD */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R38    - Speaker Volume Left */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R39    - Speaker Volume Right */
+	{ 0x00FF, 0x00FF, 0x0000 }, /* R40    - Input Mixer (2) */
+	{ 0x01B7, 0x01B7, 0x0000 }, /* R41    - Input Mixer (3) */
+	{ 0x01B7, 0x01B7, 0x0000 }, /* R42    - Input Mixer (4) */
+	{ 0x01C7, 0x01C7, 0x0000 }, /* R43    - Input Mixer (5) */
+	{ 0x01C7, 0x01C7, 0x0000 }, /* R44    - Input Mixer (6) */
+	{ 0x01FF, 0x01FF, 0x0000 }, /* R45    - Output Mixer (1) */
+	{ 0x01FF, 0x01FF, 0x0000 }, /* R46    - Output Mixer (2) */
+	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R47    - Output Mixer (3) */
+	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R48    - Output Mixer (4) */
+	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R49    - Output Mixer (5) */
+	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R50    - Output Mixer (6) */
+	{ 0x0038, 0x0038, 0x0000 }, /* R51    - HPOUT2 Mixer */
+	{ 0x0077, 0x0077, 0x0000 }, /* R52    - Line Mixer (1) */
+	{ 0x0077, 0x0077, 0x0000 }, /* R53    - Line Mixer (2) */
+	{ 0x03FF, 0x03FF, 0x0000 }, /* R54    - Speaker Mixer */
+	{ 0x00C1, 0x00C1, 0x0000 }, /* R55    - Additional Control */
+	{ 0x00F0, 0x00F0, 0x0000 }, /* R56    - AntiPOP (1) */
+	{ 0x01EF, 0x01EF, 0x0000 }, /* R57    - AntiPOP (2) */
+	{ 0x00FF, 0x00FF, 0x0000 }, /* R58    - MICBIAS */
+	{ 0x000F, 0x000F, 0x0000 }, /* R59    - LDO 1 */
+	{ 0x0007, 0x0007, 0x0000 }, /* R60    - LDO 2 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R61 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R62 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R63 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R64 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R65 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R66 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R67 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R68 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R69 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R70 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R71 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R72 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R73 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R74 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R75 */
+	{ 0x8000, 0x8000, 0x0000 }, /* R76    - Charge Pump (1) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R77 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R78 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R79 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R80 */
+	{ 0x0301, 0x0301, 0x0000 }, /* R81    - Class W (1) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R82 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R83 */
+	{ 0x333F, 0x333F, 0x0000 }, /* R84    - DC Servo (1) */
+	{ 0x0FEF, 0x0FEF, 0x0000 }, /* R85    - DC Servo (2) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R86 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R87    - DC Servo (4) */
+	{ 0x0333, 0x0000, 0x0000 }, /* R88    - DC Servo Readback */
+	{ 0x0000, 0x0000, 0x0000 }, /* R89 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R90 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R91 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R92 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R93 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R94 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R95 */
+	{ 0x00EE, 0x00EE, 0x0000 }, /* R96    - Analogue HP (1) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R97 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R98 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R99 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R100 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R101 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R102 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R103 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R104 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R105 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R106 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R107 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R108 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R109 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R110 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R111 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R112 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R113 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R114 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R115 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R116 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R117 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R118 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R119 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R120 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R121 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R122 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R123 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R124 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R125 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R126 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R127 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R128 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R129 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R130 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R131 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R132 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R133 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R134 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R135 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R136 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R137 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R138 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R139 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R140 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R141 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R142 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R143 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R144 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R145 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R146 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R147 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R148 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R149 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R150 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R151 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R152 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R153 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R154 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R155 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R156 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R157 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R158 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R159 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R160 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R161 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R162 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R163 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R164 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R165 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R166 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R167 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R168 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R169 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R170 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R171 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R172 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R173 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R174 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R175 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R176 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R177 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R178 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R179 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R180 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R181 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R182 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R183 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R184 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R185 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R186 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R187 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R188 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R189 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R190 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R191 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R192 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R193 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R194 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R195 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R196 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R197 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R198 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R199 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R200 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R201 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R202 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R203 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R204 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R205 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R206 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R207 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R208 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R209 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R210 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R211 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R212 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R213 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R214 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R215 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R216 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R217 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R218 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R219 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R220 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R221 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R222 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R223 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R224 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R225 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R226 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R227 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R228 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R229 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R230 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R231 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R232 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R233 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R234 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R235 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R236 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R237 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R238 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R239 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R240 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R241 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R242 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R243 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R244 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R245 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R246 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R247 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R248 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R249 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R250 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R251 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R252 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R253 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R254 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R255 */
+	{ 0x000F, 0x0000, 0x0000 }, /* R256   - Chip Revision */
+	{ 0x0074, 0x0074, 0x0000 }, /* R257   - Control Interface */
+	{ 0x0000, 0x0000, 0x0000 }, /* R258 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R259 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R260 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R261 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R262 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R263 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R264 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R265 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R266 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R267 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R268 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R269 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R270 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R271 */
+	{ 0x807F, 0x837F, 0x0000 }, /* R272   - Write Sequencer Ctrl (1) */
+	{ 0x017F, 0x0000, 0x0000 }, /* R273   - Write Sequencer Ctrl (2) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R274 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R275 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R276 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R277 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R278 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R279 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R280 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R281 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R282 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R283 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R284 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R285 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R286 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R287 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R288 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R289 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R290 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R291 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R292 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R293 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R294 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R295 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R296 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R297 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R298 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R299 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R300 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R301 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R302 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R303 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R304 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R305 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R306 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R307 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R308 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R309 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R310 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R311 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R312 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R313 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R314 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R315 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R316 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R317 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R318 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R319 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R320 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R321 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R322 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R323 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R324 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R325 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R326 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R327 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R328 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R329 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R330 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R331 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R332 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R333 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R334 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R335 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R336 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R337 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R338 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R339 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R340 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R341 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R342 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R343 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R344 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R345 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R346 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R347 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R348 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R349 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R350 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R351 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R352 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R353 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R354 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R355 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R356 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R357 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R358 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R359 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R360 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R361 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R362 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R363 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R364 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R365 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R366 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R367 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R368 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R369 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R370 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R371 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R372 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R373 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R374 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R375 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R376 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R377 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R378 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R379 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R380 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R381 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R382 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R383 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R384 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R385 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R386 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R387 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R388 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R389 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R390 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R391 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R392 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R393 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R394 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R395 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R396 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R397 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R398 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R399 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R400 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R401 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R402 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R403 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R404 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R405 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R406 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R407 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R408 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R409 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R410 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R411 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R412 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R413 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R414 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R415 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R416 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R417 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R418 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R419 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R420 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R421 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R422 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R423 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R424 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R425 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R426 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R427 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R428 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R429 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R430 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R431 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R432 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R433 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R434 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R435 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R436 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R437 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R438 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R439 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R440 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R441 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R442 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R443 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R444 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R445 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R446 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R447 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R448 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R449 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R450 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R451 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R452 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R453 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R454 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R455 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R456 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R457 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R458 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R459 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R460 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R461 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R462 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R463 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R464 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R465 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R466 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R467 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R468 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R469 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R470 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R471 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R472 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R473 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R474 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R475 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R476 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R477 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R478 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R479 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R480 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R481 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R482 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R483 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R484 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R485 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R486 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R487 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R488 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R489 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R490 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R491 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R492 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R493 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R494 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R495 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R496 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R497 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R498 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R499 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R500 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R501 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R502 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R503 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R504 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R505 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R506 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R507 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R508 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R509 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R510 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R511 */
+	{ 0x001F, 0x001F, 0x0000 }, /* R512   - AIF1 Clocking (1) */
+	{ 0x003F, 0x003F, 0x0000 }, /* R513   - AIF1 Clocking (2) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R514 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R515 */
+	{ 0x001F, 0x001F, 0x0000 }, /* R516   - AIF2 Clocking (1) */
+	{ 0x003F, 0x003F, 0x0000 }, /* R517   - AIF2 Clocking (2) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R518 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R519 */
+	{ 0x001F, 0x001F, 0x0000 }, /* R520   - Clocking (1) */
+	{ 0x0777, 0x0777, 0x0000 }, /* R521   - Clocking (2) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R522 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R523 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R524 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R525 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R526 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R527 */
+	{ 0x00FF, 0x00FF, 0x0000 }, /* R528   - AIF1 Rate */
+	{ 0x00FF, 0x00FF, 0x0000 }, /* R529   - AIF2 Rate */
+	{ 0x000F, 0x0000, 0x0000 }, /* R530   - Rate Status */
+	{ 0x0000, 0x0000, 0x0000 }, /* R531 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R532 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R533 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R534 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R535 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R536 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R537 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R538 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R539 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R540 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R541 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R542 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R543 */
+	{ 0x0007, 0x0007, 0x0000 }, /* R544   - FLL1 Control (1) */
+	{ 0x3F77, 0x3F77, 0x0000 }, /* R545   - FLL1 Control (2) */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R546   - FLL1 Control (3) */
+	{ 0x7FEF, 0x7FEF, 0x0000 }, /* R547   - FLL1 Control (4) */
+	{ 0x1FDB, 0x1FDB, 0x0000 }, /* R548   - FLL1 Control (5) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R549 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R550 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R551 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R552 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R553 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R554 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R555 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R556 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R557 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R558 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R559 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R560 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R561 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R562 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R563 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R564 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R565 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R566 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R567 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R568 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R569 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R570 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R571 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R572 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R573 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R574 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R575 */
+	{ 0x0007, 0x0007, 0x0000 }, /* R576   - FLL2 Control (1) */
+	{ 0x3F77, 0x3F77, 0x0000 }, /* R577   - FLL2 Control (2) */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R578   - FLL2 Control (3) */
+	{ 0x7FEF, 0x7FEF, 0x0000 }, /* R579   - FLL2 Control (4) */
+	{ 0x1FDB, 0x1FDB, 0x0000 }, /* R580   - FLL2 Control (5) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R581 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R582 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R583 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R584 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R585 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R586 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R587 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R588 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R589 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R590 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R591 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R592 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R593 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R594 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R595 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R596 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R597 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R598 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R599 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R600 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R601 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R602 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R603 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R604 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R605 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R606 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R607 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R608 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R609 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R610 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R611 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R612 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R613 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R614 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R615 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R616 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R617 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R618 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R619 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R620 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R621 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R622 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R623 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R624 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R625 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R626 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R627 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R628 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R629 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R630 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R631 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R632 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R633 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R634 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R635 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R636 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R637 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R638 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R639 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R640 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R641 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R642 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R643 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R644 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R645 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R646 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R647 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R648 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R649 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R650 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R651 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R652 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R653 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R654 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R655 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R656 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R657 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R658 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R659 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R660 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R661 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R662 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R663 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R664 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R665 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R666 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R667 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R668 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R669 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R670 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R671 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R672 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R673 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R674 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R675 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R676 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R677 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R678 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R679 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R680 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R681 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R682 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R683 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R684 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R685 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R686 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R687 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R688 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R689 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R690 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R691 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R692 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R693 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R694 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R695 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R696 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R697 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R698 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R699 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R700 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R701 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R702 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R703 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R704 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R705 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R706 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R707 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R708 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R709 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R710 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R711 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R712 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R713 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R714 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R715 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R716 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R717 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R718 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R719 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R720 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R721 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R722 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R723 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R724 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R725 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R726 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R727 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R728 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R729 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R730 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R731 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R732 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R733 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R734 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R735 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R736 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R737 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R738 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R739 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R740 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R741 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R742 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R743 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R744 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R745 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R746 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R747 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R748 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R749 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R750 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R751 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R752 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R753 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R754 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R755 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R756 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R757 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R758 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R759 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R760 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R761 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R762 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R763 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R764 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R765 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R766 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R767 */
+	{ 0xE1F8, 0xE1F8, 0x0000 }, /* R768   - AIF1 Control (1) */
+	{ 0xCD1F, 0xCD1F, 0x0000 }, /* R769   - AIF1 Control (2) */
+	{ 0xF000, 0xF000, 0x0000 }, /* R770   - AIF1 Master/Slave */
+	{ 0x01F0, 0x01F0, 0x0000 }, /* R771   - AIF1 BCLK */
+	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R772   - AIF1ADC LRCLK */
+	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R773   - AIF1DAC LRCLK */
+	{ 0x0003, 0x0003, 0x0000 }, /* R774   - AIF1DAC Data */
+	{ 0x0003, 0x0003, 0x0000 }, /* R775   - AIF1ADC Data */
+	{ 0x0000, 0x0000, 0x0000 }, /* R776 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R777 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R778 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R779 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R780 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R781 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R782 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R783 */
+	{ 0xF1F8, 0xF1F8, 0x0000 }, /* R784   - AIF2 Control (1) */
+	{ 0xFD1F, 0xFD1F, 0x0000 }, /* R785   - AIF2 Control (2) */
+	{ 0xF000, 0xF000, 0x0000 }, /* R786   - AIF2 Master/Slave */
+	{ 0x01F0, 0x01F0, 0x0000 }, /* R787   - AIF2 BCLK */
+	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R788   - AIF2ADC LRCLK */
+	{ 0x0FFF, 0x0FFF, 0x0000 }, /* R789   - AIF2DAC LRCLK */
+	{ 0x0003, 0x0003, 0x0000 }, /* R790   - AIF2DAC Data */
+	{ 0x0003, 0x0003, 0x0000 }, /* R791   - AIF2ADC Data */
+	{ 0x0000, 0x0000, 0x0000 }, /* R792 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R793 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R794 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R795 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R796 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R797 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R798 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R799 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R800 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R801 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R802 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R803 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R804 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R805 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R806 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R807 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R808 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R809 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R810 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R811 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R812 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R813 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R814 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R815 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R816 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R817 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R818 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R819 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R820 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R821 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R822 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R823 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R824 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R825 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R826 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R827 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R828 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R829 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R830 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R831 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R832 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R833 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R834 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R835 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R836 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R837 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R838 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R839 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R840 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R841 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R842 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R843 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R844 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R845 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R846 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R847 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R848 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R849 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R850 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R851 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R852 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R853 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R854 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R855 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R856 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R857 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R858 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R859 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R860 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R861 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R862 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R863 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R864 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R865 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R866 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R867 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R868 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R869 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R870 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R871 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R872 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R873 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R874 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R875 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R876 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R877 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R878 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R879 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R880 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R881 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R882 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R883 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R884 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R885 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R886 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R887 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R888 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R889 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R890 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R891 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R892 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R893 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R894 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R895 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R896 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R897 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R898 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R899 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R900 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R901 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R902 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R903 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R904 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R905 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R906 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R907 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R908 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R909 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R910 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R911 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R912 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R913 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R914 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R915 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R916 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R917 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R918 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R919 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R920 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R921 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R922 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R923 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R924 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R925 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R926 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R927 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R928 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R929 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R930 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R931 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R932 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R933 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R934 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R935 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R936 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R937 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R938 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R939 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R940 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R941 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R942 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R943 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R944 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R945 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R946 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R947 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R948 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R949 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R950 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R951 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R952 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R953 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R954 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R955 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R956 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R957 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R958 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R959 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R960 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R961 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R962 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R963 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R964 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R965 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R966 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R967 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R968 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R969 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R970 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R971 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R972 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R973 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R974 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R975 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R976 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R977 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R978 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R979 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R980 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R981 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R982 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R983 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R984 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R985 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R986 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R987 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R988 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R989 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R990 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R991 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R992 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R993 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R994 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R995 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R996 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R997 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R998 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R999 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1000 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1001 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1002 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1003 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1004 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1005 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1006 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1007 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1008 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1009 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1010 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1011 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1012 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1013 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1014 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1015 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1016 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1017 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1018 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1019 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1020 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1021 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1022 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1023 */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1024  - AIF1 ADC1 Left Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1025  - AIF1 ADC1 Right Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1026  - AIF1 DAC1 Left Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1027  - AIF1 DAC1 Right Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1028  - AIF1 ADC2 Left Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1029  - AIF1 ADC2 Right Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1030  - AIF1 DAC2 Left Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1031  - AIF1 DAC2 Right Volume */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1032 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1033 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1034 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1035 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1036 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1037 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1038 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1039 */
+	{ 0xF800, 0xF800, 0x0000 }, /* R1040  - AIF1 ADC1 Filters */
+	{ 0x7800, 0x7800, 0x0000 }, /* R1041  - AIF1 ADC2 Filters */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1042 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1043 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1044 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1045 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1046 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1047 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1048 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1049 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1050 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1051 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1052 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1053 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1054 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1055 */
+	{ 0x02B6, 0x02B6, 0x0000 }, /* R1056  - AIF1 DAC1 Filters (1) */
+	{ 0x3F00, 0x3F00, 0x0000 }, /* R1057  - AIF1 DAC1 Filters (2) */
+	{ 0x02B6, 0x02B6, 0x0000 }, /* R1058  - AIF1 DAC2 Filters (1) */
+	{ 0x3F00, 0x3F00, 0x0000 }, /* R1059  - AIF1 DAC2 Filters (2) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1060 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1061 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1062 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1063 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1064 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1065 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1066 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1067 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1068 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1069 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1070 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1071 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1072 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1073 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1074 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1075 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1076 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1077 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1078 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1079 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1080 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1081 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1082 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1083 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1084 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1085 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1086 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1087 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1088  - AIF1 DRC1 (1) */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R1089  - AIF1 DRC1 (2) */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1090  - AIF1 DRC1 (3) */
+	{ 0x07FF, 0x07FF, 0x0000 }, /* R1091  - AIF1 DRC1 (4) */
+	{ 0x03FF, 0x03FF, 0x0000 }, /* R1092  - AIF1 DRC1 (5) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1093 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1094 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1095 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1096 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1097 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1098 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1099 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1100 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1101 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1102 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1103 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1104  - AIF1 DRC2 (1) */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R1105  - AIF1 DRC2 (2) */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1106  - AIF1 DRC2 (3) */
+	{ 0x07FF, 0x07FF, 0x0000 }, /* R1107  - AIF1 DRC2 (4) */
+	{ 0x03FF, 0x03FF, 0x0000 }, /* R1108  - AIF1 DRC2 (5) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1109 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1110 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1111 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1112 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1113 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1114 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1115 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1116 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1117 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1118 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1119 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1120 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1121 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1122 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1123 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1124 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1125 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1126 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1127 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1128 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1129 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1130 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1131 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1132 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1133 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1134 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1135 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1136 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1137 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1138 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1139 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1140 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1141 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1142 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1143 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1144 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1145 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1146 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1147 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1148 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1149 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1150 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1151 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1152  - AIF1 DAC1 EQ Gains (1) */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R1153  - AIF1 DAC1 EQ Gains (2) */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1154  - AIF1 DAC1 EQ Band 1 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1155  - AIF1 DAC1 EQ Band 1 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1156  - AIF1 DAC1 EQ Band 1 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1157  - AIF1 DAC1 EQ Band 2 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1158  - AIF1 DAC1 EQ Band 2 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1159  - AIF1 DAC1 EQ Band 2 C */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1160  - AIF1 DAC1 EQ Band 2 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1161  - AIF1 DAC1 EQ Band 3 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1162  - AIF1 DAC1 EQ Band 3 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1163  - AIF1 DAC1 EQ Band 3 C */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1164  - AIF1 DAC1 EQ Band 3 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1165  - AIF1 DAC1 EQ Band 4 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1166  - AIF1 DAC1 EQ Band 4 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1167  - AIF1 DAC1 EQ Band 4 C */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1168  - AIF1 DAC1 EQ Band 4 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1169  - AIF1 DAC1 EQ Band 5 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1170  - AIF1 DAC1 EQ Band 5 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1171  - AIF1 DAC1 EQ Band 5 PG */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1172 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1173 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1174 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1175 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1176 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1177 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1178 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1179 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1180 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1181 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1182 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1183 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1184  - AIF1 DAC2 EQ Gains (1) */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R1185  - AIF1 DAC2 EQ Gains (2) */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1186  - AIF1 DAC2 EQ Band 1 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1187  - AIF1 DAC2 EQ Band 1 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1188  - AIF1 DAC2 EQ Band 1 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1189  - AIF1 DAC2 EQ Band 2 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1190  - AIF1 DAC2 EQ Band 2 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1191  - AIF1 DAC2 EQ Band 2 C */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1192  - AIF1 DAC2 EQ Band 2 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1193  - AIF1 DAC2 EQ Band 3 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1194  - AIF1 DAC2 EQ Band 3 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1195  - AIF1 DAC2 EQ Band 3 C */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1196  - AIF1 DAC2 EQ Band 3 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1197  - AIF1 DAC2 EQ Band 4 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1198  - AIF1 DAC2 EQ Band 4 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1199  - AIF1 DAC2 EQ Band 4 C */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1200  - AIF1 DAC2 EQ Band 4 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1201  - AIF1 DAC2 EQ Band 5 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1202  - AIF1 DAC2 EQ Band 5 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1203  - AIF1 DAC2 EQ Band 5 PG */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1204 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1205 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1206 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1207 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1208 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1209 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1210 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1211 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1212 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1213 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1214 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1215 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1216 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1217 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1218 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1219 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1220 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1221 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1222 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1223 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1224 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1225 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1226 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1227 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1228 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1229 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1230 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1231 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1232 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1233 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1234 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1235 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1236 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1237 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1238 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1239 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1240 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1241 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1242 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1243 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1244 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1245 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1246 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1247 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1248 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1249 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1250 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1251 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1252 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1253 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1254 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1255 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1256 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1257 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1258 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1259 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1260 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1261 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1262 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1263 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1264 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1265 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1266 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1267 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1268 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1269 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1270 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1271 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1272 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1273 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1274 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1275 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1276 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1277 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1278 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1279 */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1280  - AIF2 ADC Left Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1281  - AIF2 ADC Right Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1282  - AIF2 DAC Left Volume */
+	{ 0x00FF, 0x01FF, 0x0000 }, /* R1283  - AIF2 DAC Right Volume */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1284 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1285 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1286 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1287 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1288 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1289 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1290 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1291 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1292 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1293 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1294 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1295 */
+	{ 0xF800, 0xF800, 0x0000 }, /* R1296  - AIF2 ADC Filters */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1297 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1298 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1299 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1300 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1301 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1302 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1303 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1304 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1305 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1306 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1307 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1308 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1309 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1310 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1311 */
+	{ 0x02B6, 0x02B6, 0x0000 }, /* R1312  - AIF2 DAC Filters (1) */
+	{ 0x3F00, 0x3F00, 0x0000 }, /* R1313  - AIF2 DAC Filters (2) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1314 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1315 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1316 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1317 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1318 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1319 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1320 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1321 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1322 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1323 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1324 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1325 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1326 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1327 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1328 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1329 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1330 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1331 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1332 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1333 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1334 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1335 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1336 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1337 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1338 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1339 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1340 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1341 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1342 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1343 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1344  - AIF2 DRC (1) */
+	{ 0x1FFF, 0x1FFF, 0x0000 }, /* R1345  - AIF2 DRC (2) */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1346  - AIF2 DRC (3) */
+	{ 0x07FF, 0x07FF, 0x0000 }, /* R1347  - AIF2 DRC (4) */
+	{ 0x03FF, 0x03FF, 0x0000 }, /* R1348  - AIF2 DRC (5) */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1349 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1350 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1351 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1352 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1353 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1354 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1355 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1356 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1357 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1358 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1359 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1360 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1361 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1362 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1363 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1364 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1365 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1366 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1367 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1368 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1369 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1370 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1371 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1372 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1373 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1374 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1375 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1376 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1377 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1378 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1379 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1380 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1381 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1382 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1383 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1384 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1385 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1386 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1387 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1388 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1389 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1390 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1391 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1392 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1393 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1394 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1395 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1396 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1397 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1398 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1399 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1400 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1401 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1402 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1403 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1404 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1405 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1406 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1407 */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1408  - AIF2 EQ Gains (1) */
+	{ 0xFFC0, 0xFFC0, 0x0000 }, /* R1409  - AIF2 EQ Gains (2) */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1410  - AIF2 EQ Band 1 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1411  - AIF2 EQ Band 1 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1412  - AIF2 EQ Band 1 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1413  - AIF2 EQ Band 2 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1414  - AIF2 EQ Band 2 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1415  - AIF2 EQ Band 2 C */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1416  - AIF2 EQ Band 2 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1417  - AIF2 EQ Band 3 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1418  - AIF2 EQ Band 3 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1419  - AIF2 EQ Band 3 C */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1420  - AIF2 EQ Band 3 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1421  - AIF2 EQ Band 4 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1422  - AIF2 EQ Band 4 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1423  - AIF2 EQ Band 4 C */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1424  - AIF2 EQ Band 4 PG */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1425  - AIF2 EQ Band 5 A */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1426  - AIF2 EQ Band 5 B */
+	{ 0xFFFF, 0xFFFF, 0x0000 }, /* R1427  - AIF2 EQ Band 5 PG */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1428 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1429 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1430 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1431 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1432 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1433 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1434 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1435 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1436 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1437 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1438 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1439 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1440 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1441 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1442 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1443 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1444 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1445 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1446 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1447 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1448 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1449 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1450 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1451 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1452 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1453 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1454 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1455 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1456 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1457 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1458 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1459 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1460 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1461 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1462 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1463 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1464 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1465 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1466 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1467 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1468 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1469 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1470 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1471 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1472 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1473 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1474 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1475 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1476 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1477 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1478 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1479 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1480 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1481 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1482 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1483 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1484 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1485 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1486 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1487 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1488 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1489 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1490 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1491 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1492 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1493 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1494 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1495 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1496 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1497 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1498 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1499 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1500 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1501 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1502 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1503 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1504 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1505 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1506 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1507 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1508 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1509 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1510 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1511 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1512 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1513 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1514 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1515 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1516 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1517 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1518 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1519 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1520 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1521 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1522 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1523 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1524 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1525 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1526 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1527 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1528 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1529 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1530 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1531 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1532 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1533 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1534 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1535 */
+	{ 0x01EF, 0x01EF, 0x0000 }, /* R1536  - DAC1 Mixer Volumes */
+	{ 0x0037, 0x0037, 0x0000 }, /* R1537  - DAC1 Left Mixer Routing */
+	{ 0x0037, 0x0037, 0x0000 }, /* R1538  - DAC1 Right Mixer Routing */
+	{ 0x01EF, 0x01EF, 0x0000 }, /* R1539  - DAC2 Mixer Volumes */
+	{ 0x0037, 0x0037, 0x0000 }, /* R1540  - DAC2 Left Mixer Routing */
+	{ 0x0037, 0x0037, 0x0000 }, /* R1541  - DAC2 Right Mixer Routing */
+	{ 0x0003, 0x0003, 0x0000 }, /* R1542  - AIF1 ADC1 Left Mixer Routing */
+	{ 0x0003, 0x0003, 0x0000 }, /* R1543  - AIF1 ADC1 Right Mixer Routing */
+	{ 0x0003, 0x0003, 0x0000 }, /* R1544  - AIF1 ADC2 Left Mixer Routing */
+	{ 0x0003, 0x0003, 0x0000 }, /* R1545  - AIF1 ADC2 Right mixer Routing */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1546 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1547 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1548 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1549 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1550 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1551 */
+	{ 0x02FF, 0x03FF, 0x0000 }, /* R1552  - DAC1 Left Volume */
+	{ 0x02FF, 0x03FF, 0x0000 }, /* R1553  - DAC1 Right Volume */
+	{ 0x02FF, 0x03FF, 0x0000 }, /* R1554  - DAC2 Left Volume */
+	{ 0x02FF, 0x03FF, 0x0000 }, /* R1555  - DAC2 Right Volume */
+	{ 0x0003, 0x0003, 0x0000 }, /* R1556  - DAC Softmute */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1557 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1558 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1559 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1560 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1561 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1562 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1563 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1564 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1565 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1566 */
+	{ 0x0000, 0x0000, 0x0000 }, /* R1567 */
+	{ 0x0003, 0x0003, 0x0000 }, /* R1568  - Oversampling */
+	{ 0x03C3, 0x03C3, 0x0000 }, /* R1569  - Sidetone */
+};
+
+static int wm8994_readable(unsigned int reg)
+{
+	if (reg >= ARRAY_SIZE(access_masks))
+		return 0;
+	return access_masks[reg].readable != 0;
+}
+
+static int wm8994_volatile(unsigned int reg)
+{
+	if (reg >= WM8994_REG_CACHE_SIZE)
+		return 1;
+
+	switch (reg) {
+	case WM8994_SOFTWARE_RESET:
+	case WM8994_CHIP_REVISION:
+	case WM8994_DC_SERVO_1:
+	case WM8994_DC_SERVO_READBACK:
+	case WM8994_RATE_STATUS:
+	case WM8994_LDO_1:
+	case WM8994_LDO_2:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static int wm8994_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	struct wm8994_priv *wm8994 = codec->private_data;
+
+	BUG_ON(reg > WM8994_MAX_REGISTER);
+
+	if (!wm8994_volatile(reg))
+		wm8994->reg_cache[reg] = value;
+
+	return wm8994_reg_write(codec->control_data, reg, value);
+}
+
+static unsigned int wm8994_read(struct snd_soc_codec *codec,
+				unsigned int reg)
+{
+	u16 *reg_cache = codec->reg_cache;
+
+	BUG_ON(reg > WM8994_MAX_REGISTER);
+
+	if (wm8994_volatile(reg))
+		return wm8994_reg_read(codec->control_data, reg);
+	else
+		return reg_cache[reg];
+}
+
+static int configure_aif_clock(struct snd_soc_codec *codec, int aif)
+{
+	struct wm8994_priv *wm8994 = codec->private_data;
+	int rate;
+	int reg1 = 0;
+	int offset;
+
+	if (aif)
+		offset = 4;
+	else
+		offset = 0;
+
+	switch (wm8994->sysclk[aif]) {
+	case WM8994_SYSCLK_MCLK1:
+		rate = wm8994->mclk[0];
+		break;
+
+	case WM8994_SYSCLK_MCLK2:
+		reg1 |= 0x8;
+		rate = wm8994->mclk[1];
+		break;
+
+	case WM8994_SYSCLK_FLL1:
+		reg1 |= 0x10;
+		rate = wm8994->fll[0].out;
+		break;
+
+	case WM8994_SYSCLK_FLL2:
+		reg1 |= 0x18;
+		rate = wm8994->fll[1].out;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	if (rate >= 13500000) {
+		rate /= 2;
+		reg1 |= WM8994_AIF1CLK_DIV;
+
+		dev_dbg(codec->dev, "Dividing AIF%d clock to %dHz\n",
+			aif + 1, rate);
+	}
+	wm8994->aifclk[aif] = rate;
+
+	snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1 + offset,
+			    WM8994_AIF1CLK_SRC_MASK | WM8994_AIF1CLK_DIV,
+			    reg1);
+
+	return 0;
+}
+
+static int configure_clock(struct snd_soc_codec *codec)
+{
+	struct wm8994_priv *wm8994 = codec->private_data;
+	int old, new;
+
+	/* Bring up the AIF clocks first */
+	configure_aif_clock(codec, 0);
+	configure_aif_clock(codec, 1);
+
+	/* Then switch CLK_SYS over to the higher of them; a change
+	 * can only happen as a result of a clocking change which can
+	 * only be made outside of DAPM so we can safely redo the
+	 * clocking.
+	 */
+
+	/* If they're equal it doesn't matter which is used */
+	if (wm8994->aifclk[0] == wm8994->aifclk[1])
+		return 0;
+
+	if (wm8994->aifclk[0] < wm8994->aifclk[1])
+		new = WM8994_SYSCLK_SRC;
+	else
+		new = 0;
+
+	old = snd_soc_read(codec, WM8994_CLOCKING_1) & WM8994_SYSCLK_SRC;
+
+	/* If there's no change then we're done. */
+	if (old == new)
+		return 0;
+
+	snd_soc_update_bits(codec, WM8994_CLOCKING_1, WM8994_SYSCLK_SRC, new);
+
+	snd_soc_dapm_sync(codec);
+
+	return 0;
+}
+
+static int check_clk_sys(struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink)
+{
+	int reg = snd_soc_read(source->codec, WM8994_CLOCKING_1);
+	const char *clk;
+
+	/* Check what we're currently using for CLK_SYS */
+	if (reg & WM8994_SYSCLK_SRC)
+		clk = "AIF2CLK";
+	else
+		clk = "AIF1CLK";
+
+	return strcmp(source->name, clk) == 0;
+}
+
+static const char *sidetone_hpf_text[] = {
+	"2.7kHz", "1.35kHz", "675Hz", "370Hz", "180Hz", "90Hz", "45Hz"
+};
+
+static const struct soc_enum sidetone_hpf =
+	SOC_ENUM_SINGLE(WM8994_SIDETONE, 7, 7, sidetone_hpf_text);
+
+static const DECLARE_TLV_DB_SCALE(aif_tlv, 0, 600, 0);
+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);
+
+#define WM8994_DRC_SWITCH(xname, reg, shift) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+	.put = wm8994_put_drc_sw, \
+	.private_value =  SOC_SINGLE_VALUE(reg, shift, 1, 0) }
+
+static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int mask, ret;
+
+	/* Can't enable both ADC and DAC paths simultaneously */
+	if (mc->shift == WM8994_AIF1DAC1_DRC_ENA_SHIFT)
+		mask = WM8994_AIF1ADC1L_DRC_ENA_MASK |
+			WM8994_AIF1ADC1R_DRC_ENA_MASK;
+	else
+		mask = WM8994_AIF1DAC1_DRC_ENA_MASK;
+
+	ret = snd_soc_read(codec, mc->reg);
+	if (ret < 0)
+		return ret;
+	if (ret & mask)
+		return -EINVAL;
+
+	return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+
+
+static void wm8994_set_drc(struct snd_soc_codec *codec, int drc)
+{
+	struct wm8994_priv *wm8994 = codec->private_data;
+	struct wm8994_pdata *pdata = wm8994->pdata;
+	int base = wm8994_drc_base[drc];
+	int cfg = wm8994->drc_cfg[drc];
+	int save, i;
+
+	/* Save any enables; the configuration should clear them. */
+	save = snd_soc_read(codec, base);
+	save &= WM8994_AIF1DAC1_DRC_ENA | WM8994_AIF1ADC1L_DRC_ENA |
+		WM8994_AIF1ADC1R_DRC_ENA;
+
+	for (i = 0; i < WM8994_DRC_REGS; i++)
+		snd_soc_update_bits(codec, base + i, 0xffff,
+				    pdata->drc_cfgs[cfg].regs[i]);
+
+	snd_soc_update_bits(codec, base, WM8994_AIF1DAC1_DRC_ENA |
+			     WM8994_AIF1ADC1L_DRC_ENA |
+			     WM8994_AIF1ADC1R_DRC_ENA, save);
+}
+
+/* Icky as hell but saves code duplication */
+static int wm8994_get_drc(const char *name)
+{
+	if (strcmp(name, "AIF1DRC1 Mode") == 0)
+		return 0;
+	if (strcmp(name, "AIF1DRC2 Mode") == 0)
+		return 1;
+	if (strcmp(name, "AIF2DRC Mode") == 0)
+		return 2;
+	return -EINVAL;
+}
+
+static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8994_priv *wm8994 = codec->private_data;	
+	struct wm8994_pdata *pdata = wm8994->pdata;
+	int drc = wm8994_get_drc(kcontrol->id.name);
+	int value = ucontrol->value.integer.value[0];
+
+	if (drc < 0)
+		return drc;
+
+	if (value >= pdata->num_drc_cfgs)
+		return -EINVAL;
+
+	wm8994->drc_cfg[drc] = value;
+
+	wm8994_set_drc(codec, drc);
+
+	return 0;
+}
+
+static int wm8994_get_drc_enum(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8994_priv *wm8994 = codec->private_data;
+	int drc = wm8994_get_drc(kcontrol->id.name);
+
+	ucontrol->value.enumerated.item[0] = wm8994->drc_cfg[drc];
+
+	return 0;
+}
+
+static void wm8994_set_retune_mobile(struct snd_soc_codec *codec, int block)
+{
+	struct wm8994_priv *wm8994 = codec->private_data;
+	struct wm8994_pdata *pdata = wm8994->pdata;
+	int base = wm8994_retune_mobile_base[block];
+	int iface, best, best_val, save, i, cfg;
+
+	if (!pdata || !wm8994->num_retune_mobile_texts)
+		return;
+
+	switch (block) {
+	case 0:
+	case 1:
+		iface = 0;
+		break;
+	case 2:
+		iface = 1;
+		break;
+	default:
+		return;
+	}
+
+	/* Find the version of the currently selected configuration
+	 * with the nearest sample rate. */
+	cfg = wm8994->retune_mobile_cfg[block];
+	best = 0;
+	best_val = INT_MAX;
+	for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+		if (strcmp(pdata->retune_mobile_cfgs[i].name,
+			   wm8994->retune_mobile_texts[cfg]) == 0 &&
+		    abs(pdata->retune_mobile_cfgs[i].rate
+			- wm8994->dac_rates[iface]) < best_val) {
+			best = i;
+			best_val = abs(pdata->retune_mobile_cfgs[i].rate
+				       - wm8994->dac_rates[iface]);
+		}
+	}
+
+	dev_dbg(codec->dev, "ReTune Mobile %d %s/%dHz for %dHz sample rate\n",
+		block,
+		pdata->retune_mobile_cfgs[best].name,
+		pdata->retune_mobile_cfgs[best].rate,
+		wm8994->dac_rates[iface]);
+
+	/* The EQ will be disabled while reconfiguring it, remember the
+	 * current configuration. 
+	 */
+	save = snd_soc_read(codec, base);
+	save &= WM8994_AIF1DAC1_EQ_ENA;
+
+	for (i = 0; i < WM8994_EQ_REGS; i++)
+		snd_soc_update_bits(codec, base + i, 0xffff,
+				pdata->retune_mobile_cfgs[best].regs[i]);
+
+	snd_soc_update_bits(codec, base, WM8994_AIF1DAC1_EQ_ENA, save);
+}
+
+/* Icky as hell but saves code duplication */
+static int wm8994_get_retune_mobile_block(const char *name)
+{
+	if (strcmp(name, "AIF1.1 EQ Mode") == 0)
+		return 0;
+	if (strcmp(name, "AIF1.2 EQ Mode") == 0)
+		return 1;
+	if (strcmp(name, "AIF2 EQ Mode") == 0)
+		return 2;
+	return -EINVAL;
+}
+
+static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8994_priv *wm8994 = codec->private_data;	
+	struct wm8994_pdata *pdata = wm8994->pdata;
+	int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
+	int value = ucontrol->value.integer.value[0];
+
+	if (block < 0)
+		return block;
+
+	if (value >= pdata->num_retune_mobile_cfgs)
+		return -EINVAL;
+
+	wm8994->retune_mobile_cfg[block] = value;
+
+	wm8994_set_retune_mobile(codec, block);
+
+	return 0;
+}
+
+static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct wm8994_priv *wm8994 = codec->private_data;
+	int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
+
+	ucontrol->value.enumerated.item[0] = wm8994->retune_mobile_cfg[block];
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new wm8994_snd_controls[] = {
+SOC_DOUBLE_R_TLV("AIF1ADC1 Volume", WM8994_AIF1_ADC1_LEFT_VOLUME,
+		 WM8994_AIF1_ADC1_RIGHT_VOLUME,
+		 1, 119, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF1ADC2 Volume", WM8994_AIF1_ADC2_LEFT_VOLUME,
+		 WM8994_AIF1_ADC2_RIGHT_VOLUME,
+		 1, 119, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF2ADC Volume", WM8994_AIF2_ADC_LEFT_VOLUME,
+		 WM8994_AIF2_ADC_RIGHT_VOLUME,
+		 1, 119, 0, digital_tlv),
+
+SOC_DOUBLE_R_TLV("AIF1DAC1 Volume", WM8994_AIF1_DAC1_LEFT_VOLUME,
+		 WM8994_AIF1_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF1DAC2 Volume", WM8994_AIF1_DAC2_LEFT_VOLUME,
+		 WM8994_AIF1_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("AIF2DAC Volume", WM8994_AIF2_DAC_LEFT_VOLUME,
+		 WM8994_AIF2_DAC_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+
+SOC_SINGLE_TLV("AIF1 Boost Volume", WM8994_AIF1_CONTROL_2, 10, 3, 0, aif_tlv),
+SOC_SINGLE_TLV("AIF2 Boost Volume", WM8994_AIF2_CONTROL_2, 10, 3, 0, aif_tlv),
+
+SOC_SINGLE("AIF1DAC1 EQ Switch", WM8994_AIF1_DAC1_EQ_GAINS_1, 0, 1, 0),
+SOC_SINGLE("AIF1DAC2 EQ Switch", WM8994_AIF1_DAC2_EQ_GAINS_1, 0, 1, 0),
+SOC_SINGLE("AIF2 EQ Switch", WM8994_AIF2_EQ_GAINS_1, 0, 1, 0),
+
+WM8994_DRC_SWITCH("AIF1DAC1 DRC Switch", WM8994_AIF1_DRC1_1, 2),
+WM8994_DRC_SWITCH("AIF1ADC1L DRC Switch", WM8994_AIF1_DRC1_1, 1),
+WM8994_DRC_SWITCH("AIF1ADC1R DRC Switch", WM8994_AIF1_DRC1_1, 0),
+
+WM8994_DRC_SWITCH("AIF1DAC2 DRC Switch", WM8994_AIF1_DRC2_1, 2),
+WM8994_DRC_SWITCH("AIF1ADC2L DRC Switch", WM8994_AIF1_DRC2_1, 1),
+WM8994_DRC_SWITCH("AIF1ADC2R DRC Switch", WM8994_AIF1_DRC2_1, 0),
+
+WM8994_DRC_SWITCH("AIF2DAC DRC Switch", WM8994_AIF2_DRC_1, 2),
+WM8994_DRC_SWITCH("AIF2ADCL DRC Switch", WM8994_AIF2_DRC_1, 1),
+WM8994_DRC_SWITCH("AIF2ADCR DRC Switch", WM8994_AIF2_DRC_1, 0),
+
+SOC_SINGLE_TLV("DAC1 Right Sidetone Volume", WM8994_DAC1_MIXER_VOLUMES,
+	       5, 12, 0, st_tlv),
+SOC_SINGLE_TLV("DAC1 Left Sidetone Volume", WM8994_DAC1_MIXER_VOLUMES,
+	       0, 12, 0, st_tlv),
+SOC_SINGLE_TLV("DAC2 Right Sidetone Volume", WM8994_DAC2_MIXER_VOLUMES,
+	       5, 12, 0, st_tlv),
+SOC_SINGLE_TLV("DAC2 Left Sidetone Volume", WM8994_DAC2_MIXER_VOLUMES,
+	       0, 12, 0, st_tlv),
+SOC_ENUM("Sidetone HPF Mux", sidetone_hpf),
+SOC_SINGLE("Sidetone HPF Switch", WM8994_SIDETONE, 6, 1, 0),
+
+SOC_DOUBLE_R_TLV("DAC1 Volume", WM8994_DAC1_LEFT_VOLUME,
+		 WM8994_DAC1_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R("DAC1 Switch", WM8994_DAC1_LEFT_VOLUME,
+	     WM8994_DAC1_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_DOUBLE_R_TLV("DAC2 Volume", WM8994_DAC2_LEFT_VOLUME,
+		 WM8994_DAC2_RIGHT_VOLUME, 1, 96, 0, digital_tlv),
+SOC_DOUBLE_R("DAC2 Switch", WM8994_DAC2_LEFT_VOLUME,
+	     WM8994_DAC2_RIGHT_VOLUME, 9, 1, 1),
+
+SOC_SINGLE_TLV("SPKL DAC2 Volume", WM8994_SPKMIXL_ATTENUATION,
+	       6, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKL DAC1 Volume", WM8994_SPKMIXL_ATTENUATION,
+	       2, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("SPKR DAC2 Volume", WM8994_SPKMIXR_ATTENUATION,
+	       6, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKR DAC1 Volume", WM8994_SPKMIXR_ATTENUATION,
+	       2, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("AIF1DAC1 3D Stereo Volume", WM8994_AIF1_DAC1_FILTERS_2,
+	       10, 15, 0, wm8994_3d_tlv),
+SOC_SINGLE("AIF1DAC1 3D Stereo Switch", WM8994_AIF1_DAC2_FILTERS_2,
+	   8, 1, 0),
+SOC_SINGLE_TLV("AIF1DAC2 3D Stereo Volume", WM8994_AIF1_DAC2_FILTERS_2,
+	       10, 15, 0, wm8994_3d_tlv),
+SOC_SINGLE("AIF1DAC2 3D Stereo Switch", WM8994_AIF1_DAC2_FILTERS_2,
+	   8, 1, 0),
+SOC_SINGLE_TLV("AIF2DAC 3D Stereo Volume", WM8994_AIF1_DAC1_FILTERS_2,
+	       10, 15, 0, wm8994_3d_tlv),
+SOC_SINGLE("AIF2DAC 3D Stereo Switch", WM8994_AIF1_DAC2_FILTERS_2,
+	   8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8994_eq_controls[] = {
+SOC_SINGLE_TLV("AIF1DAC1 EQ1 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 11, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ2 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 6, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ3 Volume", WM8994_AIF1_DAC1_EQ_GAINS_1, 1, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ4 Volume", WM8994_AIF1_DAC1_EQ_GAINS_2, 11, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC1 EQ5 Volume", WM8994_AIF1_DAC1_EQ_GAINS_2, 6, 31, 0,
+	       eq_tlv),
+
+SOC_SINGLE_TLV("AIF1DAC2 EQ1 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 11, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ2 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 6, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ3 Volume", WM8994_AIF1_DAC2_EQ_GAINS_1, 1, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ4 Volume", WM8994_AIF1_DAC2_EQ_GAINS_2, 11, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF1DAC2 EQ5 Volume", WM8994_AIF1_DAC2_EQ_GAINS_2, 6, 31, 0,
+	       eq_tlv),
+
+SOC_SINGLE_TLV("AIF2 EQ1 Volume", WM8994_AIF2_EQ_GAINS_1, 11, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ2 Volume", WM8994_AIF2_EQ_GAINS_1, 6, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ3 Volume", WM8994_AIF2_EQ_GAINS_1, 1, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ4 Volume", WM8994_AIF2_EQ_GAINS_2, 11, 31, 0,
+	       eq_tlv),
+SOC_SINGLE_TLV("AIF2 EQ5 Volume", WM8994_AIF2_EQ_GAINS_2, 6, 31, 0,
+	       eq_tlv),
+};
+
+static int clk_sys_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:
+		return configure_clock(codec);
+
+	case SND_SOC_DAPM_POST_PMD:
+		configure_clock(codec);
+		break;
+	}
+
+	return 0;
+}
+
+static void wm8994_update_class_w(struct snd_soc_codec *codec)
+{
+	int enable = 1;
+	int source = 0;  /* GCC flow analysis can't track enable */
+	int reg, reg_r;
+
+	/* Only support direct DAC->headphone paths */
+	reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_1);
+	if (!(reg & WM8994_DAC1L_TO_HPOUT1L)) {
+		dev_dbg(codec->dev, "HPL connected to output mixer\n");
+		enable = 0;
+	}
+
+	reg = snd_soc_read(codec, WM8994_OUTPUT_MIXER_2);
+	if (!(reg & WM8994_DAC1R_TO_HPOUT1R)) {
+		dev_dbg(codec->dev, "HPR connected to output mixer\n");
+		enable = 0;
+	}
+
+	/* We also need the same setting for L/R and only one path */
+	reg = snd_soc_read(codec, WM8994_DAC1_LEFT_MIXER_ROUTING);
+	switch (reg) {
+	case WM8994_AIF2DACL_TO_DAC1L:
+		dev_dbg(codec->dev, "Class W source AIF2DAC\n");
+		source = 2 << WM8994_CP_DYN_SRC_SEL_SHIFT;
+		break;
+	case WM8994_AIF1DAC2L_TO_DAC1L:
+		dev_dbg(codec->dev, "Class W source AIF1DAC2\n");
+		source = 1 << WM8994_CP_DYN_SRC_SEL_SHIFT;
+		break;
+	case WM8994_AIF1DAC1L_TO_DAC1L:
+		dev_dbg(codec->dev, "Class W source AIF1DAC1\n");
+		source = 0 << WM8994_CP_DYN_SRC_SEL_SHIFT;
+		break;
+	default:
+		dev_dbg(codec->dev, "DAC mixer setting: %x\n", reg);
+		enable = 0;
+		break;
+	}
+
+	reg_r = snd_soc_read(codec, WM8994_DAC1_RIGHT_MIXER_ROUTING);
+	if (reg_r != reg) {
+		dev_dbg(codec->dev, "Left and right DAC mixers different\n");
+		enable = 0;
+	}
+
+	if (enable) {
+		dev_dbg(codec->dev, "Class W enabled\n");
+		snd_soc_update_bits(codec, WM8994_CLASS_W_1,
+				    WM8994_CP_DYN_PWR |
+				    WM8994_CP_DYN_SRC_SEL_MASK,
+				    source | WM8994_CP_DYN_PWR);
+		
+	} else {
+		dev_dbg(codec->dev, "Class W disabled\n");
+		snd_soc_update_bits(codec, WM8994_CLASS_W_1,
+				    WM8994_CP_DYN_PWR, 0);
+	}
+}
+
+static const char *hp_mux_text[] = {
+	"Mixer",
+	"DAC",
+};
+
+#define WM8994_HP_ENUM(xname, xenum) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_enum_double, \
+ 	.get = snd_soc_dapm_get_enum_double, \
+ 	.put = wm8994_put_hp_enum, \
+  	.private_value = (unsigned long)&xenum }
+
+static int wm8994_put_hp_enum(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_codec *codec = w->codec;
+	int ret;
+
+	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+	wm8994_update_class_w(codec);
+
+	return ret;
+}
+
+static const struct soc_enum hpl_enum =
+	SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_1, 8, 2, hp_mux_text);
+
+static const struct snd_kcontrol_new hpl_mux =
+	WM8994_HP_ENUM("Left Headphone Mux", hpl_enum);
+
+static const struct soc_enum hpr_enum =
+	SOC_ENUM_SINGLE(WM8994_OUTPUT_MIXER_2, 8, 2, hp_mux_text);
+
+static const struct snd_kcontrol_new hpr_mux =
+	WM8994_HP_ENUM("Right Headphone Mux", hpr_enum);
+
+static const char *adc_mux_text[] = {
+	"ADC",
+	"DMIC",
+};
+
+static const struct soc_enum adc_enum =
+	SOC_ENUM_SINGLE(0, 0, 2, adc_mux_text);
+
+static const struct snd_kcontrol_new adcl_mux =
+	SOC_DAPM_ENUM_VIRT("ADCL Mux", adc_enum);
+
+static const struct snd_kcontrol_new adcr_mux =
+	SOC_DAPM_ENUM_VIRT("ADCR Mux", adc_enum);
+
+static const struct snd_kcontrol_new left_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DAC2 Switch", WM8994_SPEAKER_MIXER, 9, 1, 0),
+SOC_DAPM_SINGLE("Input Switch", WM8994_SPEAKER_MIXER, 7, 1, 0),
+SOC_DAPM_SINGLE("IN1LP Switch", WM8994_SPEAKER_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8994_SPEAKER_MIXER, 3, 1, 0),
+SOC_DAPM_SINGLE("DAC1 Switch", WM8994_SPEAKER_MIXER, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_mixer[] = {
+SOC_DAPM_SINGLE("DAC2 Switch", WM8994_SPEAKER_MIXER, 8, 1, 0),
+SOC_DAPM_SINGLE("Input Switch", WM8994_SPEAKER_MIXER, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1RP Switch", WM8994_SPEAKER_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8994_SPEAKER_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("DAC1 Switch", WM8994_SPEAKER_MIXER, 0, 1, 0),
+};
+
+/* Debugging; dump chip status after DAPM transitions */
+static int post_ev(struct snd_soc_dapm_widget *w,
+	    struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	dev_dbg(codec->dev, "SRC status: %x\n",
+		snd_soc_read(codec,
+			     WM8994_RATE_STATUS));
+	return 0;
+}
+
+static const struct snd_kcontrol_new aif1adc1l_mix[] = {
+SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8994_AIF1_ADC1_LEFT_MIXER_ROUTING,
+		1, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC1_LEFT_MIXER_ROUTING,
+		0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif1adc1r_mix[] = {
+SOC_DAPM_SINGLE("ADC/DMIC Switch", WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING,
+		1, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_AIF1_ADC1_RIGHT_MIXER_ROUTING,
+		0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif2dac2l_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+		5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+		4, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+		2, 1, 0),
+SOC_DAPM_SINGLE("AIF1.2 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+		1, 1, 0),
+SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_LEFT_MIXER_ROUTING,
+		0, 1, 0),
+};
+
+static const struct snd_kcontrol_new aif2dac2r_mix[] = {
+SOC_DAPM_SINGLE("Right Sidetone Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+		5, 1, 0),
+SOC_DAPM_SINGLE("Left Sidetone Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+		4, 1, 0),
+SOC_DAPM_SINGLE("AIF2 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+		2, 1, 0),
+SOC_DAPM_SINGLE("AIF1.2 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+		1, 1, 0),
+SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
+		0, 1, 0),
+};
+
+#define WM8994_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_dapm_get_volsw, .put = wm8994_put_class_w, \
+	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_codec *codec = w->codec;
+	int ret;
+
+	ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
+
+	wm8994_update_class_w(codec);
+
+	return ret;
+}
+
+static const struct snd_kcontrol_new dac1l_mix[] = {
+WM8994_CLASS_W_SWITCH("Right Sidetone Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+		      5, 1, 0),
+WM8994_CLASS_W_SWITCH("Left Sidetone Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+		      4, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF2 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+		      2, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.2 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+		      1, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.1 Switch", WM8994_DAC1_LEFT_MIXER_ROUTING,
+		      0, 1, 0),
+};
+
+static const struct snd_kcontrol_new dac1r_mix[] = {
+WM8994_CLASS_W_SWITCH("Right Sidetone Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+		      5, 1, 0),
+WM8994_CLASS_W_SWITCH("Left Sidetone Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+		      4, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF2 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+		      2, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.2 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+		      1, 1, 0),
+WM8994_CLASS_W_SWITCH("AIF1.1 Switch", WM8994_DAC1_RIGHT_MIXER_ROUTING,
+		      0, 1, 0),
+};
+
+static const char *sidetone_text[] = {
+	"ADC/DMIC1", "DMIC2",
+};
+
+static const struct soc_enum sidetone1_enum =
+	SOC_ENUM_SINGLE(WM8994_SIDETONE, 0, 2, sidetone_text);
+
+static const struct snd_kcontrol_new sidetone1_mux =
+	SOC_DAPM_ENUM("Left Sidetone Mux", sidetone1_enum);
+
+static const struct soc_enum sidetone2_enum =
+	SOC_ENUM_SINGLE(WM8994_SIDETONE, 1, 2, sidetone_text);
+
+static const struct snd_kcontrol_new sidetone2_mux =
+	SOC_DAPM_ENUM("Right Sidetone Mux", sidetone2_enum);
+
+static const char *aif1dac_text[] = {
+	"AIF1DACDAT", "AIF3DACDAT",
+};
+
+static const struct soc_enum aif1dac_enum =
+	SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 0, 2, aif1dac_text);
+
+static const struct snd_kcontrol_new aif1dac_mux =
+	SOC_DAPM_ENUM("AIF1DAC Mux", aif1dac_enum);
+
+static const char *aif2dac_text[] = {
+	"AIF2DACDAT", "AIF3DACDAT",
+};
+
+static const struct soc_enum aif2dac_enum =
+	SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 1, 2, aif2dac_text);
+
+static const struct snd_kcontrol_new aif2dac_mux =
+	SOC_DAPM_ENUM("AIF2DAC Mux", aif2dac_enum);
+
+static const char *aif2adc_text[] = {
+	"AIF2ADCDAT", "AIF3DACDAT",
+};
+
+static const struct soc_enum aif2adc_enum =
+	SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 2, 2, aif2adc_text);
+
+static const struct snd_kcontrol_new aif2adc_mux =
+	SOC_DAPM_ENUM("AIF2ADC Mux", aif2adc_enum);
+
+static const char *aif3adc_text[] = {
+	"AIF1ADCDAT", "AIF2ADCDAT", "AIF2DACDAT",
+};
+
+static const struct soc_enum aif3adc_enum =
+	SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 3, 3, aif3adc_text);
+
+static const struct snd_kcontrol_new aif3adc_mux =
+	SOC_DAPM_ENUM("AIF3ADC Mux", aif3adc_enum);
+
+static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("DMIC1DAT"),
+SND_SOC_DAPM_INPUT("DMIC2DAT"),
+
+SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
+		    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_SUPPLY("DSP1CLK", WM8994_CLOCKING_1, 3, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSP2CLK", WM8994_CLOCKING_1, 2, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("DSPINTCLK", WM8994_CLOCKING_1, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", "AIF1 Capture",
+		     0, WM8994_POWER_MANAGEMENT_4, 9, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", "AIF1 Capture",
+		     0, WM8994_POWER_MANAGEMENT_4, 8, 0),
+SND_SOC_DAPM_AIF_IN("AIF1DAC1L", NULL, 0,
+		    WM8994_POWER_MANAGEMENT_5, 9, 0),
+SND_SOC_DAPM_AIF_IN("AIF1DAC1R", NULL, 0,
+		    WM8994_POWER_MANAGEMENT_5, 8, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1ADC2L", "AIF1 Capture",
+		     0, WM8994_POWER_MANAGEMENT_4, 11, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1ADC2R", "AIF1 Capture",
+		     0, WM8994_POWER_MANAGEMENT_4, 10, 0),
+SND_SOC_DAPM_AIF_IN("AIF1DAC2L", NULL, 0,
+		    WM8994_POWER_MANAGEMENT_5, 11, 0),
+SND_SOC_DAPM_AIF_IN("AIF1DAC2R", NULL, 0,
+		    WM8994_POWER_MANAGEMENT_5, 10, 0),
+
+SND_SOC_DAPM_MIXER("AIF1ADC1L Mixer", SND_SOC_NOPM, 0, 0,
+		   aif1adc1l_mix, ARRAY_SIZE(aif1adc1l_mix)),
+SND_SOC_DAPM_MIXER("AIF1ADC1R Mixer", SND_SOC_NOPM, 0, 0,
+		   aif1adc1r_mix, ARRAY_SIZE(aif1adc1r_mix)),
+
+SND_SOC_DAPM_MIXER("AIF2DAC2L Mixer", SND_SOC_NOPM, 0, 0,
+		   aif2dac2l_mix, ARRAY_SIZE(aif2dac2l_mix)),
+SND_SOC_DAPM_MIXER("AIF2DAC2R Mixer", SND_SOC_NOPM, 0, 0,
+		   aif2dac2r_mix, ARRAY_SIZE(aif2dac2r_mix)),
+
+SND_SOC_DAPM_MUX("Left Sidetone", SND_SOC_NOPM, 0, 0, &sidetone1_mux),
+SND_SOC_DAPM_MUX("Right Sidetone", SND_SOC_NOPM, 0, 0, &sidetone2_mux),
+
+SND_SOC_DAPM_MIXER("DAC1L Mixer", SND_SOC_NOPM, 0, 0,
+		   dac1l_mix, ARRAY_SIZE(dac1l_mix)),
+SND_SOC_DAPM_MIXER("DAC1R Mixer", SND_SOC_NOPM, 0, 0,
+		   dac1r_mix, ARRAY_SIZE(dac1r_mix)),
+
+SND_SOC_DAPM_AIF_OUT("AIF2ADCL", NULL, 0,
+		     WM8994_POWER_MANAGEMENT_4, 13, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2ADCR", NULL, 0,
+		     WM8994_POWER_MANAGEMENT_4, 12, 0),
+SND_SOC_DAPM_AIF_IN("AIF2DACL", NULL, 0,
+		    WM8994_POWER_MANAGEMENT_5, 13, 0),
+SND_SOC_DAPM_AIF_IN("AIF2DACR", NULL, 0,
+		    WM8994_POWER_MANAGEMENT_5, 12, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIF2DACDAT", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("AIF1DAC Mux", SND_SOC_NOPM, 0, 0, &aif1dac_mux),
+SND_SOC_DAPM_MUX("AIF2DAC Mux", SND_SOC_NOPM, 0, 0, &aif2dac_mux),
+SND_SOC_DAPM_MUX("AIF2ADC Mux", SND_SOC_NOPM, 0, 0, &aif2adc_mux),
+SND_SOC_DAPM_MUX("AIF3ADC Mux", SND_SOC_NOPM, 0, 0, &aif3adc_mux),
+
+SND_SOC_DAPM_AIF_IN("AIF3DACDAT", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIF3ADCDAT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_SUPPLY("TOCLK", WM8994_CLOCKING_1, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("DMIC2L", NULL, WM8994_POWER_MANAGEMENT_4, 5, 0),
+SND_SOC_DAPM_ADC("DMIC2R", NULL, WM8994_POWER_MANAGEMENT_4, 4, 0),
+SND_SOC_DAPM_ADC("DMIC1L", NULL, WM8994_POWER_MANAGEMENT_4, 3, 0),
+SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0),
+
+/* Power is done with the muxes since the ADC power also controls the
+ * downsampling chain, the chip will automatically manage the analogue
+ * specific portions.
+ */
+SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux),
+SND_SOC_DAPM_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux),
+
+SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0),
+SND_SOC_DAPM_DAC("DAC2R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0),
+SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0),
+SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+
+SND_SOC_DAPM_MIXER("SPKL", WM8994_POWER_MANAGEMENT_3, 8, 0,
+		   left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
+SND_SOC_DAPM_MIXER("SPKR", WM8994_POWER_MANAGEMENT_3, 9, 0,
+		   right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
+
+SND_SOC_DAPM_POST("Debug log", post_ev),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+
+	{ "CLK_SYS", NULL, "AIF1CLK", check_clk_sys },
+	{ "CLK_SYS", NULL, "AIF2CLK", check_clk_sys },
+
+	{ "DSP1CLK", NULL, "CLK_SYS" },
+	{ "DSP2CLK", NULL, "CLK_SYS" },
+	{ "DSPINTCLK", NULL, "CLK_SYS" },
+
+	{ "AIF1ADC1L", NULL, "AIF1CLK" },
+	{ "AIF1ADC1L", NULL, "DSP1CLK" },
+	{ "AIF1ADC1R", NULL, "AIF1CLK" },
+	{ "AIF1ADC1R", NULL, "DSP1CLK" },
+	{ "AIF1ADC1R", NULL, "DSPINTCLK" },
+
+	{ "AIF1DAC1L", NULL, "AIF1CLK" },
+	{ "AIF1DAC1L", NULL, "DSP1CLK" },
+	{ "AIF1DAC1R", NULL, "AIF1CLK" },
+	{ "AIF1DAC1R", NULL, "DSP1CLK" },
+	{ "AIF1DAC1R", NULL, "DSPINTCLK" },
+
+	{ "AIF1ADC2L", NULL, "AIF1CLK" },
+	{ "AIF1ADC2L", NULL, "DSP1CLK" },
+	{ "AIF1ADC2R", NULL, "AIF1CLK" },
+	{ "AIF1ADC2R", NULL, "DSP1CLK" },
+	{ "AIF1ADC2R", NULL, "DSPINTCLK" },
+
+	{ "AIF1DAC2L", NULL, "AIF1CLK" },
+	{ "AIF1DAC2L", NULL, "DSP1CLK" },
+	{ "AIF1DAC2R", NULL, "AIF1CLK" },
+	{ "AIF1DAC2R", NULL, "DSP1CLK" },
+	{ "AIF1DAC2R", NULL, "DSPINTCLK" },
+
+	{ "AIF2ADCL", NULL, "AIF2CLK" },
+	{ "AIF2ADCL", NULL, "DSP2CLK" },
+	{ "AIF2ADCR", NULL, "AIF2CLK" },
+	{ "AIF2ADCR", NULL, "DSP2CLK" },
+	{ "AIF2ADCR", NULL, "DSPINTCLK" },
+
+	{ "AIF2DACL", NULL, "AIF2CLK" },
+	{ "AIF2DACL", NULL, "DSP2CLK" },
+	{ "AIF2DACR", NULL, "AIF2CLK" },
+	{ "AIF2DACR", NULL, "DSP2CLK" },
+	{ "AIF2DACR", NULL, "DSPINTCLK" },
+
+	{ "DMIC1L", NULL, "DMIC1DAT" },
+	{ "DMIC1L", NULL, "CLK_SYS" },
+	{ "DMIC1R", NULL, "DMIC1DAT" },
+	{ "DMIC1R", NULL, "CLK_SYS" },
+	{ "DMIC2L", NULL, "DMIC2DAT" },
+	{ "DMIC2L", NULL, "CLK_SYS" },
+	{ "DMIC2R", NULL, "DMIC2DAT" },
+	{ "DMIC2R", NULL, "CLK_SYS" },
+
+	{ "ADCL", NULL, "AIF1CLK" },
+	{ "ADCL", NULL, "DSP1CLK" },
+	{ "ADCL", NULL, "DSPINTCLK" },
+
+	{ "ADCR", NULL, "AIF1CLK" },
+	{ "ADCR", NULL, "DSP1CLK" },
+	{ "ADCR", NULL, "DSPINTCLK" },
+
+	{ "ADCL Mux", "ADC", "ADCL" },
+	{ "ADCL Mux", "DMIC", "DMIC1L" },
+	{ "ADCR Mux", "ADC", "ADCR" },
+	{ "ADCR Mux", "DMIC", "DMIC1R" },
+
+	{ "DAC1L", NULL, "AIF1CLK" },
+	{ "DAC1L", NULL, "DSP1CLK" },
+	{ "DAC1L", NULL, "DSPINTCLK" },
+
+	{ "DAC1R", NULL, "AIF1CLK" },
+	{ "DAC1R", NULL, "DSP1CLK" },
+	{ "DAC1R", NULL, "DSPINTCLK" },
+
+	{ "DAC2L", NULL, "AIF2CLK" },
+	{ "DAC2L", NULL, "DSP2CLK" },
+	{ "DAC2L", NULL, "DSPINTCLK" },
+
+	{ "DAC2R", NULL, "AIF2DACR" },
+	{ "DAC2R", NULL, "AIF2CLK" },
+	{ "DAC2R", NULL, "DSP2CLK" },
+	{ "DAC2R", NULL, "DSPINTCLK" },
+
+	{ "TOCLK", NULL, "CLK_SYS" },
+
+	/* AIF1 outputs */
+	{ "AIF1ADC1L", NULL, "AIF1ADC1L Mixer" },
+	{ "AIF1ADC1L Mixer", "ADC/DMIC Switch", "ADCL Mux" },
+	{ "AIF1ADC1L Mixer", "AIF2 Switch", "AIF2DACL" },
+
+	{ "AIF1ADC1R", NULL, "AIF1ADC1R Mixer" },
+	{ "AIF1ADC1R Mixer", "ADC/DMIC Switch", "ADCR Mux" },
+	{ "AIF1ADC1R Mixer", "AIF2 Switch", "AIF2DACR" },
+
+	/* Pin level routing for AIF3 */
+	{ "AIF1DAC1L", NULL, "AIF1DAC Mux" },
+	{ "AIF1DAC1R", NULL, "AIF1DAC Mux" },
+	{ "AIF1DAC2L", NULL, "AIF1DAC Mux" },
+	{ "AIF1DAC2R", NULL, "AIF1DAC Mux" },
+
+	{ "AIF2DACL", NULL, "AIF2DAC Mux" },
+	{ "AIF2DACR", NULL, "AIF2DAC Mux" },
+
+	{ "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" },
+	{ "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
+	{ "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" },
+	{ "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" },
+	{ "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" },
+	{ "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" },
+	{ "AIF2ADC Mux", "AIF3DACDAT", "AIF3ADCDAT" },
+
+	/* DAC1 inputs */
+	{ "DAC1L", NULL, "DAC1L Mixer" },
+	{ "DAC1L Mixer", "AIF2 Switch", "AIF2DACL" },
+	{ "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" },
+	{ "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" },
+	{ "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+	{ "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+	{ "DAC1R", NULL, "DAC1R Mixer" },
+	{ "DAC1R Mixer", "AIF2 Switch", "AIF2DACR" },
+	{ "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" },
+	{ "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" },
+	{ "DAC1R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+	{ "DAC1R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+	/* DAC2/AIF2 outputs  */
+	{ "AIF2ADCL", NULL, "AIF2DAC2L Mixer" },
+	{ "DAC2L", NULL, "AIF2DAC2L Mixer" },
+	{ "AIF2DAC2L Mixer", "AIF2 Switch", "AIF2DACL" },
+	{ "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" },
+	{ "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" },
+	{ "AIF2DAC2L Mixer", "Left Sidetone Switch", "Left Sidetone" },
+	{ "AIF2DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+	{ "AIF2ADCR", NULL, "AIF2DAC2R Mixer" },
+	{ "DAC2R", NULL, "AIF2DAC2R Mixer" },
+	{ "AIF2DAC2R Mixer", "AIF2 Switch", "AIF2DACR" },
+	{ "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" },
+	{ "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" },
+	{ "AIF2DAC2R Mixer", "Left Sidetone Switch", "Left Sidetone" },
+	{ "AIF2DAC2R Mixer", "Right Sidetone Switch", "Right Sidetone" },
+
+	{ "AIF2ADCDAT", NULL, "AIF2ADC Mux" },
+
+	/* AIF3 output */
+	{ "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC1L" },
+	{ "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC1R" },
+	{ "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC2L" },
+	{ "AIF3ADCDAT", "AIF1ADCDAT", "AIF1ADC2R" },
+	{ "AIF3ADCDAT", "AIF2ADCDAT", "AIF2ADCL" },
+	{ "AIF3ADCDAT", "AIF2ADCDAT", "AIF2ADCR" },
+	{ "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" },
+	{ "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" },
+
+	/* Sidetone */
+	{ "Left Sidetone", "ADC/DMIC1", "ADCL Mux" },
+	{ "Left Sidetone", "DMIC2", "DMIC2L" },
+	{ "Right Sidetone", "ADC/DMIC1", "ADCR Mux" },
+	{ "Right Sidetone", "DMIC2", "DMIC2R" },
+
+	/* Output stages */
+	{ "Left Output Mixer", "DAC Switch", "DAC1L" },
+	{ "Right Output Mixer", "DAC Switch", "DAC1R" },
+
+	{ "SPKL", "DAC1 Switch", "DAC1L" },
+	{ "SPKL", "DAC2 Switch", "DAC2L" },
+
+	{ "SPKR", "DAC1 Switch", "DAC1R" },
+	{ "SPKR", "DAC2 Switch", "DAC2R" },
+
+	{ "Left Headphone Mux", "DAC", "DAC1L" },
+	{ "Right Headphone Mux", "DAC", "DAC1R" },
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+struct fll_div {
+	u16 outdiv;
+	u16 n;
+	u16 k;
+	u16 clk_ref_div;
+	u16 fll_fratio;
+};
+
+static int wm8994_get_fll_config(struct fll_div *fll,
+				 int freq_in, int freq_out)
+{
+	u64 Kpart;
+	unsigned int K, Ndiv, Nmod;
+
+	pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out);
+
+	/* Scale the input frequency down to <= 13.5MHz */
+	fll->clk_ref_div = 0;
+	while (freq_in > 13500000) {
+		fll->clk_ref_div++;
+		freq_in /= 2;
+
+		if (fll->clk_ref_div > 3)
+			return -EINVAL;
+	}
+	pr_debug("CLK_REF_DIV=%d, Fref=%dHz\n", fll->clk_ref_div, freq_in);
+
+	/* Scale the output to give 90MHz<=Fvco<=100MHz */
+	fll->outdiv = 3;
+	while (freq_out * (fll->outdiv + 1) < 90000000) {
+		fll->outdiv++;
+		if (fll->outdiv > 63)
+			return -EINVAL;
+	}
+	freq_out *= fll->outdiv + 1;
+	pr_debug("OUTDIV=%d, Fvco=%dHz\n", fll->outdiv, freq_out);
+
+	if (freq_in > 1000000) {
+		fll->fll_fratio = 0;
+	} else {
+		fll->fll_fratio = 3;
+		freq_in *= 8;
+	}
+	pr_debug("FLL_FRATIO=%d, Fref=%dHz\n", fll->fll_fratio, freq_in);
+
+	/* Now, calculate N.K */
+	Ndiv = freq_out / freq_in;
+
+	fll->n = Ndiv;
+	Nmod = freq_out % freq_in;
+	pr_debug("Nmod=%d\n", Nmod);
+
+	/* Calculate fractional part - scale up so we can round. */
+	Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, freq_in);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	fll->k = K / 10;
+
+	pr_debug("N=%x K=%x\n", fll->n, fll->k);
+
+	return 0;
+}
+
+static int wm8994_set_fll(struct snd_soc_dai *dai, int id, int src,
+			  unsigned int freq_in, unsigned int freq_out)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8994_priv *wm8994 = codec->private_data;
+	int reg_offset, ret;
+	struct fll_div fll;
+	u16 reg, aif1, aif2;
+
+	aif1 = snd_soc_read(codec, WM8994_AIF1_CLOCKING_1)
+		& WM8994_AIF1CLK_ENA;
+
+	aif2 = snd_soc_read(codec, WM8994_AIF2_CLOCKING_1)
+		& WM8994_AIF2CLK_ENA;
+
+	switch (id) {
+	case WM8994_FLL1:
+		reg_offset = 0;
+		id = 0;
+		break;
+	case WM8994_FLL2:
+		reg_offset = 0x20;
+		id = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Are we changing anything? */
+	if (wm8994->fll[id].src == src &&
+	    wm8994->fll[id].in == freq_in && wm8994->fll[id].out == freq_out)
+		return 0;
+
+	/* If we're stopping the FLL redo the old config - no
+	 * registers will actually be written but we avoid GCC flow
+	 * analysis bugs spewing warnings.
+	 */
+	if (freq_out)
+		ret = wm8994_get_fll_config(&fll, freq_in, freq_out);
+	else
+		ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in,
+					    wm8994->fll[id].out);
+	if (ret < 0)
+		return ret;
+
+	/* Gate the AIF clocks while we reclock */
+	snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
+			    WM8994_AIF1CLK_ENA, 0);
+	snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
+			    WM8994_AIF2CLK_ENA, 0);
+
+	/* We always need to disable the FLL while reconfiguring */
+	snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
+			    WM8994_FLL1_ENA, 0);
+
+	reg = (fll.outdiv << WM8994_FLL1_OUTDIV_SHIFT) |
+		(fll.fll_fratio << WM8994_FLL1_FRATIO_SHIFT);
+	snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_2 + reg_offset,
+			    WM8994_FLL1_OUTDIV_MASK |
+			    WM8994_FLL1_FRATIO_MASK, reg);
+
+	snd_soc_write(codec, WM8994_FLL1_CONTROL_3 + reg_offset, fll.k);
+
+	snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_4 + reg_offset,
+			    WM8994_FLL1_N_MASK,
+				    fll.n << WM8994_FLL1_N_SHIFT);
+
+	snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset,
+			    WM8994_FLL1_REFCLK_DIV_MASK,
+			    fll.clk_ref_div << WM8994_FLL1_REFCLK_DIV_SHIFT);
+
+	/* Enable (with fractional mode if required) */
+	if (freq_out) {
+		if (fll.k)
+			reg = WM8994_FLL1_ENA | WM8994_FLL1_FRAC;
+		else
+			reg = WM8994_FLL1_ENA;
+		snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_1 + reg_offset,
+				    WM8994_FLL1_ENA | WM8994_FLL1_FRAC,
+				    reg);
+	}
+
+	wm8994->fll[id].in = freq_in;
+	wm8994->fll[id].out = freq_out;
+
+	/* Enable any gated AIF clocks */
+	snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1,
+			    WM8994_AIF1CLK_ENA, aif1);
+	snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1,
+			    WM8994_AIF2CLK_ENA, aif2);
+
+	configure_clock(codec);
+
+	return 0;
+}
+
+static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8994_priv *wm8994 = codec->private_data;
+
+	switch (dai->id) {
+	case 1:
+	case 2:
+		break;
+
+	default:
+		/* AIF3 shares clocking with AIF1/2 */
+		return -EINVAL;
+	}
+
+	switch (clk_id) {
+	case WM8994_SYSCLK_MCLK1:
+		wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK1;
+		wm8994->mclk[0] = freq;
+		dev_dbg(dai->dev, "AIF%d using MCLK1 at %uHz\n",
+			dai->id, freq);
+		break;
+
+	case WM8994_SYSCLK_MCLK2:
+		/* TODO: Set GPIO AF */
+		wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_MCLK2;
+		wm8994->mclk[1] = freq;
+		dev_dbg(dai->dev, "AIF%d using MCLK2 at %uHz\n",
+			dai->id, freq);
+		break;
+
+	case WM8994_SYSCLK_FLL1:
+		wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_FLL1;
+		dev_dbg(dai->dev, "AIF%d using FLL1\n", dai->id);
+		break;
+
+	case WM8994_SYSCLK_FLL2:
+		wm8994->sysclk[dai->id - 1] = WM8994_SYSCLK_FLL2;
+		dev_dbg(dai->dev, "AIF%d using FLL2\n", dai->id);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	configure_clock(codec);
+
+	return 0;
+}
+
+static int wm8994_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:
+		/* VMID=2x40k */
+		snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
+				    WM8994_VMID_SEL_MASK, 0x2);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* Tweak DC servo configuration for improved
+			 * performance. */
+			snd_soc_write(codec, 0x102, 0x3);
+			snd_soc_write(codec, 0x56, 0x3);
+			snd_soc_write(codec, 0x102, 0);
+
+			/* Discharge LINEOUT1 & 2 */
+			snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
+					    WM8994_LINEOUT1_DISCH |
+					    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:
+		/* 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);
+
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	int ms_reg;
+	int aif1_reg;
+	int ms = 0;
+	int aif1 = 0;
+
+	switch (dai->id) {
+	case 1:
+		ms_reg = WM8994_AIF1_MASTER_SLAVE;
+		aif1_reg = WM8994_AIF1_CONTROL_1;
+		break;
+	case 2:
+		ms_reg = WM8994_AIF2_MASTER_SLAVE;
+		aif1_reg = WM8994_AIF2_CONTROL_1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		ms = WM8994_AIF1_MSTR;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_B:
+		aif1 |= WM8994_AIF1_LRCLK_INV;
+	case SND_SOC_DAIFMT_DSP_A:
+		aif1 |= 0x18;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		aif1 |= 0x10;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		aif1 |= 0x8;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+	case SND_SOC_DAIFMT_DSP_B:
+		/* frame inversion not valid for DSP modes */
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif1 |= WM8994_AIF1_BCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_RIGHT_J:
+	case SND_SOC_DAIFMT_LEFT_J:
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_IF:
+			aif1 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif1 |= WM8994_AIF1_BCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_NB_IF:
+			aif1 |= WM8994_AIF1_LRCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, aif1_reg,
+			    WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV |
+			    WM8994_AIF1_FMT_MASK,
+			    aif1);
+	snd_soc_update_bits(codec, ms_reg, WM8994_AIF1_MSTR,
+			    ms);
+
+	return 0;
+}
+
+static struct {
+	int val, rate;
+} srs[] = {
+	{ 0,   8000 },
+	{ 1,  11025 },
+	{ 2,  12000 },
+	{ 3,  16000 },
+	{ 4,  22050 },
+	{ 5,  24000 },
+	{ 6,  32000 },
+	{ 7,  44100 },
+	{ 8,  48000 },
+	{ 9,  88200 },
+	{ 10, 96000 },
+};
+
+static int fs_ratios[] = {
+	64, 128, 192, 256, 348, 512, 768, 1024, 1408, 1536
+};
+
+static int bclk_divs[] = {
+	10, 15, 20, 30, 40, 50, 60, 80, 110, 120, 160, 220, 240, 320, 440, 480,
+	640, 880, 960, 1280, 1760, 1920
+};
+
+static int wm8994_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 wm8994_priv *wm8994 = codec->private_data;
+	int aif1_reg;
+	int bclk_reg;
+	int lrclk_reg;
+	int rate_reg;
+	int aif1 = 0;
+	int bclk = 0;
+	int lrclk = 0;
+	int rate_val = 0;
+	int id = dai->id - 1;
+
+	int i, cur_val, best_val, bclk_rate, best;
+
+	switch (dai->id) {
+	case 1:
+		aif1_reg = WM8994_AIF1_CONTROL_1;
+		bclk_reg = WM8994_AIF1_BCLK;
+		rate_reg = WM8994_AIF1_RATE;
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+		    wm8994->lrclk_shared[0])
+			lrclk_reg = WM8994_AIF1DAC_LRCLK;
+		else
+			lrclk_reg = WM8994_AIF1ADC_LRCLK;
+		break;
+	case 2:
+		aif1_reg = WM8994_AIF2_CONTROL_1;
+		bclk_reg = WM8994_AIF2_BCLK;
+		rate_reg = WM8994_AIF2_RATE;
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+		    wm8994->lrclk_shared[1])
+			lrclk_reg = WM8994_AIF2DAC_LRCLK;
+		else
+			lrclk_reg = WM8994_AIF2ADC_LRCLK;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	bclk_rate = params_rate(params) * 2;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bclk_rate *= 16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		bclk_rate *= 20;
+		aif1 |= 0x20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bclk_rate *= 24;
+		aif1 |= 0x40;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bclk_rate *= 32;
+		aif1 |= 0x60;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Try to find an appropriate sample rate; look for an exact match. */
+	for (i = 0; i < ARRAY_SIZE(srs); i++)
+		if (srs[i].rate == params_rate(params))
+			break;
+	if (i == ARRAY_SIZE(srs))
+		return -EINVAL;
+	rate_val |= srs[i].val << WM8994_AIF1_SR_SHIFT;
+
+	dev_dbg(dai->dev, "Sample rate is %dHz\n", srs[i].rate);
+	dev_dbg(dai->dev, "AIF%dCLK is %dHz, target BCLK %dHz\n",
+		dai->id, wm8994->aifclk[id], bclk_rate);
+
+	if (wm8994->aifclk[id] == 0) {
+		dev_err(dai->dev, "AIF%dCLK not configured\n", dai->id);
+		return -EINVAL;
+	}
+
+	/* AIFCLK/fs ratio; look for a close match in either direction */
+	best = 0;
+	best_val = abs((fs_ratios[0] * params_rate(params))
+		       - wm8994->aifclk[id]);
+	for (i = 1; i < ARRAY_SIZE(fs_ratios); i++) {
+		cur_val = abs((fs_ratios[i] * params_rate(params))
+			      - wm8994->aifclk[id]);
+		if (cur_val >= best_val)
+			continue;
+		best = i;
+		best_val = cur_val;
+	}
+	dev_dbg(dai->dev, "Selected AIF%dCLK/fs = %d\n",
+		dai->id, fs_ratios[best]);
+	rate_val |= best;
+
+	/* We may not get quite the right frequency if using
+	 * approximate clocks so look for the closest match that is
+	 * higher than the target (we need to ensure that there enough
+	 * BCLKs to clock out the samples).
+	 */
+	best = 0;
+	for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+		cur_val = (wm8994->aifclk[id] * 10 / bclk_divs[i]) - bclk_rate;
+		if (cur_val < 0) /* BCLK table is sorted */
+			break;
+		best = i;
+	}
+	bclk_rate = wm8994->aifclk[id] * 10 / bclk_divs[best];
+	dev_dbg(dai->dev, "Using BCLK_DIV %d for actual BCLK %dHz\n",
+		bclk_divs[best], bclk_rate);
+	bclk |= best << WM8994_AIF1_BCLK_DIV_SHIFT;
+
+	lrclk = bclk_rate / params_rate(params);
+	dev_dbg(dai->dev, "Using LRCLK rate %d for actual LRCLK %dHz\n",
+		lrclk, bclk_rate / lrclk);
+
+	snd_soc_update_bits(codec, aif1_reg, WM8994_AIF1_WL_MASK, aif1);
+	snd_soc_update_bits(codec, bclk_reg, WM8994_AIF1_BCLK_DIV_MASK, bclk);
+	snd_soc_update_bits(codec, lrclk_reg, WM8994_AIF1DAC_RATE_MASK,
+			    lrclk);
+	snd_soc_update_bits(codec, rate_reg, WM8994_AIF1_SR_MASK |
+			    WM8994_AIF1CLK_RATE_MASK, rate_val);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		switch (dai->id) {
+		case 1:
+			wm8994->dac_rates[0] = params_rate(params);
+			wm8994_set_retune_mobile(codec, 0);
+			wm8994_set_retune_mobile(codec, 1);
+			break;
+		case 2:
+			wm8994->dac_rates[1] = params_rate(params);
+			wm8994_set_retune_mobile(codec, 2);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int wm8994_aif_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	int mute_reg;
+	int reg;
+
+	switch (codec_dai->id) {
+	case 1:
+		mute_reg = WM8994_AIF1_DAC1_FILTERS_1;
+		break;
+	case 2:
+		mute_reg = WM8994_AIF2_DAC_FILTERS_1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (mute)
+		reg = WM8994_AIF1DAC1_MUTE;
+	else
+		reg = 0;
+
+	snd_soc_update_bits(codec, mute_reg, WM8994_AIF1DAC1_MUTE, reg);
+
+	return 0;
+}
+
+#define WM8994_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8994_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
+	.set_sysclk	= wm8994_set_dai_sysclk,
+	.set_fmt	= wm8994_set_dai_fmt,
+	.hw_params	= wm8994_hw_params,
+	.digital_mute	= wm8994_aif_mute,
+	.set_pll	= wm8994_set_fll,
+};
+
+static struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
+	.set_sysclk	= wm8994_set_dai_sysclk,
+	.set_fmt	= wm8994_set_dai_fmt,
+	.hw_params	= wm8994_hw_params,
+	.digital_mute   = wm8994_aif_mute,
+	.set_pll	= wm8994_set_fll,
+};
+
+struct snd_soc_dai wm8994_dai[] = {
+	{
+		.name = "WM8994 AIF1",
+		.id = 1,
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = WM8994_RATES,
+			.formats = WM8994_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = WM8994_RATES,
+			.formats = WM8994_FORMATS,
+		 },
+		.ops = &wm8994_aif1_dai_ops,
+	},
+	{
+		.name = "WM8994 AIF2",
+		.id = 2,
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = WM8994_RATES,
+			.formats = WM8994_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIF2 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = WM8994_RATES,
+			.formats = WM8994_FORMATS,
+		},
+		.ops = &wm8994_aif2_dai_ops,
+	},
+	{
+		.name = "WM8994 AIF3",
+		.playback = {
+			.stream_name = "AIF3 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = WM8994_RATES,
+			.formats = WM8994_FORMATS,
+		},
+		.playback = {
+			.stream_name = "AIF3 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = WM8994_RATES,
+			.formats = WM8994_FORMATS,
+		},
+	}
+};
+EXPORT_SYMBOL_GPL(wm8994_dai);
+
+#ifdef CONFIG_PM
+static int wm8994_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct wm8994_priv *wm8994 = codec->private_data;
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
+		memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i],
+		       sizeof(struct fll_config));
+		ret = wm8994_set_fll(&codec->dai[0], i + 1, 0, 0, 0);
+		if (ret < 0)
+			dev_warn(codec->dev, "Failed to stop FLL%d: %d\n",
+				 i + 1, ret);
+	}
+
+	wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int wm8994_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct wm8994_priv *wm8994 = codec->private_data;
+	u16 *reg_cache = codec->reg_cache;
+	int i, ret;
+
+	/* Restore the registers */
+	for (i = 1; i < ARRAY_SIZE(wm8994->reg_cache); i++) {
+		switch (i) {
+		case WM8994_LDO_1:
+		case WM8994_LDO_2:
+		case WM8994_SOFTWARE_RESET:
+			/* Handled by other MFD drivers */
+			continue;
+		default:
+			break;
+		}
+
+		if (!access_masks[i].writable)
+			continue;
+
+		wm8994_reg_write(codec->control_data, i, reg_cache[i]);
+	}
+
+	wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
+		ret = wm8994_set_fll(&codec->dai[0], i + 1,
+				     wm8994->fll_suspend[i].src,
+				     wm8994->fll_suspend[i].in,
+				     wm8994->fll_suspend[i].out);
+		if (ret < 0)
+			dev_warn(codec->dev, "Failed to restore FLL%d: %d\n",
+				 i + 1, ret);
+	}
+
+	return 0;
+}
+#else
+#define wm8994_suspend NULL
+#define wm8994_resume NULL
+#endif
+
+static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994)
+{
+	struct snd_soc_codec *codec = &wm8994->codec;
+	struct wm8994_pdata *pdata = wm8994->pdata;
+	struct snd_kcontrol_new controls[] = {
+		SOC_ENUM_EXT("AIF1.1 EQ Mode",
+			     wm8994->retune_mobile_enum,
+			     wm8994_get_retune_mobile_enum,
+			     wm8994_put_retune_mobile_enum),
+		SOC_ENUM_EXT("AIF1.2 EQ Mode",
+			     wm8994->retune_mobile_enum,
+			     wm8994_get_retune_mobile_enum,
+			     wm8994_put_retune_mobile_enum),
+		SOC_ENUM_EXT("AIF2 EQ Mode",
+			     wm8994->retune_mobile_enum,
+			     wm8994_get_retune_mobile_enum,
+			     wm8994_put_retune_mobile_enum),
+	};
+	int ret, i, j;
+	const char **t;
+
+	/* We need an array of texts for the enum API but the number
+	 * of texts is likely to be less than the number of
+	 * configurations due to the sample rate dependency of the
+	 * configurations. */
+	wm8994->num_retune_mobile_texts = 0;
+	wm8994->retune_mobile_texts = NULL;
+	for (i = 0; i < pdata->num_retune_mobile_cfgs; i++) {
+		for (j = 0; j < wm8994->num_retune_mobile_texts; j++) {
+			if (strcmp(pdata->retune_mobile_cfgs[i].name,
+				   wm8994->retune_mobile_texts[j]) == 0)
+				break;
+		}
+
+		if (j != wm8994->num_retune_mobile_texts)
+			continue;
+
+		/* Expand the array... */
+		t = krealloc(wm8994->retune_mobile_texts,
+			     sizeof(char *) * 
+			     (wm8994->num_retune_mobile_texts + 1),
+			     GFP_KERNEL);
+		if (t == NULL)
+			continue;
+
+		/* ...store the new entry... */
+		t[wm8994->num_retune_mobile_texts] = 
+			pdata->retune_mobile_cfgs[i].name;
+
+		/* ...and remember the new version. */
+		wm8994->num_retune_mobile_texts++;
+		wm8994->retune_mobile_texts = t;
+	}
+
+	dev_dbg(codec->dev, "Allocated %d unique ReTune Mobile names\n",
+		wm8994->num_retune_mobile_texts);
+
+	wm8994->retune_mobile_enum.max = wm8994->num_retune_mobile_texts;
+	wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts;
+
+	ret = snd_soc_add_controls(&wm8994->codec, controls,
+				   ARRAY_SIZE(controls));
+	if (ret != 0)
+		dev_err(wm8994->codec.dev,
+			"Failed to add ReTune Mobile controls: %d\n", ret);
+}
+
+static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
+{
+	struct snd_soc_codec *codec = &wm8994->codec;
+	struct wm8994_pdata *pdata = wm8994->pdata;
+	int ret, i;
+
+	if (!pdata)
+		return;
+
+	wm_hubs_handle_analogue_pdata(codec, pdata->lineout1_diff,
+				      pdata->lineout2_diff,
+				      pdata->lineout1fb,
+				      pdata->lineout2fb,
+				      pdata->jd_scthr,
+				      pdata->jd_thr,
+				      pdata->micbias1_lvl,
+				      pdata->micbias2_lvl);
+
+	dev_dbg(codec->dev, "%d DRC configurations\n", pdata->num_drc_cfgs);
+
+	if (pdata->num_drc_cfgs) {
+		struct snd_kcontrol_new controls[] = {
+			SOC_ENUM_EXT("AIF1DRC1 Mode", wm8994->drc_enum,
+				     wm8994_get_drc_enum, wm8994_put_drc_enum),
+			SOC_ENUM_EXT("AIF1DRC2 Mode", wm8994->drc_enum,
+				     wm8994_get_drc_enum, wm8994_put_drc_enum),
+			SOC_ENUM_EXT("AIF2DRC Mode", wm8994->drc_enum,
+				     wm8994_get_drc_enum, wm8994_put_drc_enum),
+		};
+
+		/* We need an array of texts for the enum API */
+		wm8994->drc_texts = kmalloc(sizeof(char *)
+					    * pdata->num_drc_cfgs, GFP_KERNEL);
+		if (!wm8994->drc_texts) {
+			dev_err(wm8994->codec.dev,
+				"Failed to allocate %d DRC config texts\n",
+				pdata->num_drc_cfgs);
+			return;
+		}
+
+		for (i = 0; i < pdata->num_drc_cfgs; i++)
+			wm8994->drc_texts[i] = pdata->drc_cfgs[i].name;
+
+		wm8994->drc_enum.max = pdata->num_drc_cfgs;
+		wm8994->drc_enum.texts = wm8994->drc_texts;
+
+		ret = snd_soc_add_controls(&wm8994->codec, controls,
+					   ARRAY_SIZE(controls));
+		if (ret != 0)
+			dev_err(wm8994->codec.dev,
+				"Failed to add DRC mode controls: %d\n", ret);
+
+		for (i = 0; i < WM8994_NUM_DRC; i++)
+			wm8994_set_drc(codec, i);
+	}
+
+	dev_dbg(codec->dev, "%d ReTune Mobile configurations\n",
+		pdata->num_retune_mobile_cfgs);
+
+	if (pdata->num_retune_mobile_cfgs)
+		wm8994_handle_retune_mobile_pdata(wm8994);
+	else
+		snd_soc_add_controls(&wm8994->codec, wm8994_eq_controls,
+				     ARRAY_SIZE(wm8994_eq_controls));
+}
+
+static int wm8994_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8994_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8994_codec;
+	codec = wm8994_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		return ret;
+	}
+
+	wm8994_handle_pdata(codec->private_data);
+
+	wm_hubs_add_analogue_controls(codec);
+	snd_soc_add_controls(codec, wm8994_snd_controls,
+			     ARRAY_SIZE(wm8994_snd_controls));
+	snd_soc_dapm_new_controls(codec, wm8994_dapm_widgets,
+				  ARRAY_SIZE(wm8994_dapm_widgets));
+	wm_hubs_add_analogue_routes(codec, 0, 0);
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	return 0;
+}
+
+static int wm8994_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8994 = {
+	.probe = 	wm8994_probe,
+	.remove = 	wm8994_remove,
+	.suspend = 	wm8994_suspend,
+	.resume =	wm8994_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8994);
+
+static int wm8994_codec_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct wm8994_priv *wm8994;
+	struct snd_soc_codec *codec;
+	int i;
+	u16 rev;
+
+	if (wm8994_codec) {
+		dev_err(&pdev->dev, "Another WM8994 is registered\n");
+		return -EINVAL;
+	}
+
+	wm8994 = kzalloc(sizeof(struct wm8994_priv), GFP_KERNEL);
+	if (!wm8994) {
+		dev_err(&pdev->dev, "Failed to allocate private data\n");
+		return -ENOMEM;
+	}
+
+	codec = &wm8994->codec;
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8994;
+	codec->control_data = dev_get_drvdata(pdev->dev.parent);
+	codec->name = "WM8994";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8994_read;
+	codec->write = wm8994_write;
+	codec->readable_register = wm8994_readable;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8994_set_bias_level;
+	codec->dai = &wm8994_dai[0];
+	codec->num_dai = 3;
+	codec->reg_cache_size = WM8994_MAX_REGISTER;
+	codec->reg_cache = &wm8994->reg_cache;
+	codec->dev = &pdev->dev;
+
+	wm8994->pdata = pdev->dev.parent->platform_data;
+
+	/* Fill the cache with physical values we inherited; don't reset */
+	ret = wm8994_bulk_read(codec->control_data, 0,
+			       ARRAY_SIZE(wm8994->reg_cache) - 1,
+			       codec->reg_cache);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to fill register cache: %d\n",
+			ret);
+		goto err;
+	}
+
+	/* Clear the cached values for unreadable/volatile registers to
+	 * avoid potential confusion.
+	 */
+	for (i = 0; i < ARRAY_SIZE(wm8994->reg_cache); i++)
+		if (wm8994_volatile(i) || !wm8994_readable(i))
+			wm8994->reg_cache[i] = 0;
+
+	/* Set revision-specific configuration */
+	rev = snd_soc_read(codec, WM8994_CHIP_REVISION);
+	switch (rev) {
+	case 2:
+	case 3:
+		wm8994->hubs.dcs_codes = -5;
+		wm8994->hubs.hp_startup_mode = 1;
+		break;
+	default:
+		break;
+	}
+			   
+
+	/* Remember if AIFnLRCLK is configured as a GPIO.  This should be
+	 * configured on init - if a system wants to do this dynamically
+	 * at runtime we can deal with that then.
+	 */
+	ret = wm8994_reg_read(codec->control_data, WM8994_GPIO_1);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to read GPIO1 state: %d\n", ret);
+		goto err;
+	}
+	if ((ret & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
+		wm8994->lrclk_shared[0] = 1;
+		wm8994_dai[0].symmetric_rates = 1;
+	} else {
+		wm8994->lrclk_shared[0] = 0;
+	}
+
+	ret = wm8994_reg_read(codec->control_data, WM8994_GPIO_6);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to read GPIO6 state: %d\n", ret);
+		goto err;
+	}
+	if ((ret & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
+		wm8994->lrclk_shared[1] = 1;
+		wm8994_dai[1].symmetric_rates = 1;
+	} else {
+		wm8994->lrclk_shared[1] = 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(wm8994_dai); i++)
+		wm8994_dai[i].dev = codec->dev;
+
+	wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	wm8994_codec = codec;
+
+	/* Latch volume updates (right only; we always do left then right). */
+	snd_soc_update_bits(codec, WM8994_AIF1_DAC1_RIGHT_VOLUME,
+			    WM8994_AIF1DAC1_VU, WM8994_AIF1DAC1_VU);
+	snd_soc_update_bits(codec, WM8994_AIF1_DAC2_RIGHT_VOLUME,
+			    WM8994_AIF1DAC2_VU, WM8994_AIF1DAC2_VU);
+	snd_soc_update_bits(codec, WM8994_AIF2_DAC_RIGHT_VOLUME,
+			    WM8994_AIF2DAC_VU, WM8994_AIF2DAC_VU);
+	snd_soc_update_bits(codec, WM8994_AIF1_ADC1_RIGHT_VOLUME,
+			    WM8994_AIF1ADC1_VU, WM8994_AIF1ADC1_VU);
+	snd_soc_update_bits(codec, WM8994_AIF1_ADC2_RIGHT_VOLUME,
+			    WM8994_AIF1ADC2_VU, WM8994_AIF1ADC2_VU);
+	snd_soc_update_bits(codec, WM8994_AIF2_ADC_RIGHT_VOLUME,
+			    WM8994_AIF2ADC_VU, WM8994_AIF1ADC2_VU);
+	snd_soc_update_bits(codec, WM8994_DAC1_RIGHT_VOLUME,
+			    WM8994_DAC1_VU, WM8994_DAC1_VU);
+	snd_soc_update_bits(codec, WM8994_DAC2_RIGHT_VOLUME,
+			    WM8994_DAC2_VU, WM8994_DAC2_VU);
+
+	/* Set the low bit of the 3D stereo depth so TLV matches */
+	snd_soc_update_bits(codec, WM8994_AIF1_DAC1_FILTERS_2,
+			    1 << WM8994_AIF1DAC1_3D_GAIN_SHIFT,
+			    1 << WM8994_AIF1DAC1_3D_GAIN_SHIFT);
+	snd_soc_update_bits(codec, WM8994_AIF1_DAC2_FILTERS_2,
+			    1 << WM8994_AIF1DAC2_3D_GAIN_SHIFT,
+			    1 << WM8994_AIF1DAC2_3D_GAIN_SHIFT);
+	snd_soc_update_bits(codec, WM8994_AIF2_DAC_FILTERS_2,
+			    1 << WM8994_AIF2DAC_3D_GAIN_SHIFT,
+			    1 << WM8994_AIF2DAC_3D_GAIN_SHIFT);
+
+	wm8994_update_class_w(codec);
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto err;
+	}
+
+	ret = snd_soc_register_dais(wm8994_dai, ARRAY_SIZE(wm8994_dai));
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+		goto err_codec;
+	}
+
+	platform_set_drvdata(pdev, wm8994);
+
+	return 0;
+
+err_codec:
+	snd_soc_unregister_codec(codec);
+err:
+	kfree(wm8994);
+	return ret;
+}
+
+static int __devexit wm8994_codec_remove(struct platform_device *pdev)
+{
+	struct wm8994_priv *wm8994 = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = &wm8994->codec;
+
+	wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dais(wm8994_dai, ARRAY_SIZE(wm8994_dai));
+	snd_soc_unregister_codec(&wm8994->codec);
+	kfree(wm8994);
+	wm8994_codec = NULL;
+
+	return 0;
+}
+
+static struct platform_driver wm8994_codec_driver = {
+	.driver = {
+		   .name = "wm8994-codec",
+		   .owner = THIS_MODULE,
+		   },
+	.probe = wm8994_codec_probe,
+	.remove = __devexit_p(wm8994_codec_remove),
+};
+
+static __init int wm8994_init(void)
+{
+	return platform_driver_register(&wm8994_codec_driver);
+}
+module_init(wm8994_init);
+
+static __exit void wm8994_exit(void)
+{
+	platform_driver_unregister(&wm8994_codec_driver);
+}
+module_exit(wm8994_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8994 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8994-codec");
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
new file mode 100644
index 0000000..0a5e142
--- /dev/null
+++ b/sound/soc/codecs/wm8994.h
@@ -0,0 +1,26 @@
+/*
+ * wm8994.h  --  WM8994 Soc Audio driver
+ *
+ * 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 _WM8994_H
+#define _WM8994_H
+
+#include <sound/soc.h>
+
+extern struct snd_soc_codec_device soc_codec_dev_wm8994;
+extern struct snd_soc_dai wm8994_dai[];
+
+/* Sources for AIF1/2 SYSCLK - use with set_dai_sysclk() */
+#define WM8994_SYSCLK_MCLK1 1
+#define WM8994_SYSCLK_MCLK2 2
+#define WM8994_SYSCLK_FLL1  3
+#define WM8994_SYSCLK_FLL2  4
+
+#define WM8994_FLL1 1
+#define WM8994_FLL2 2
+
+#endif
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index c58aab3..ceb86b4 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -23,13 +23,12 @@
 #include <sound/ac97_codec.h>
 #include <sound/initval.h>
 #include <sound/pcm_params.h>
+#include <sound/tlv.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 
 #include "wm9713.h"
 
-#define WM9713_VERSION "0.15"
-
 struct wm9713_priv {
 	u32 pll_in; /* PLL input frequency */
 };
@@ -115,15 +114,27 @@
 SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
 };
 
+static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(misc_tlv, -1500, 300, 0);
+static unsigned int mic_tlv[] = {
+	TLV_DB_RANGE_HEAD(2),
+	0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0),
+	3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
 static const struct snd_kcontrol_new wm9713_snd_ac97_controls[] = {
-SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
+SOC_DOUBLE_TLV("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1, out_tlv),
 SOC_DOUBLE("Speaker Playback Switch", AC97_MASTER, 15, 7, 1, 1),
-SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
+SOC_DOUBLE_TLV("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1,
+	       out_tlv),
 SOC_DOUBLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 7, 1, 1),
-SOC_DOUBLE("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1),
-SOC_DOUBLE("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1),
-SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
-SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
+SOC_DOUBLE_TLV("Line In Volume", AC97_PC_BEEP, 8, 0, 31, 1, main_tlv),
+SOC_DOUBLE_TLV("PCM Playback Volume", AC97_PHONE, 8, 0, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic 1 Preamp Volume", AC97_3D_CONTROL, 10, 3, 0, mic_tlv),
+SOC_SINGLE_TLV("Mic 2 Preamp Volume", AC97_3D_CONTROL, 12, 3, 0, mic_tlv),
 
 SOC_SINGLE("Mic Boost (+20dB) Switch", AC97_LINE, 5, 1, 0),
 SOC_SINGLE("Mic Headphone Mixer Volume", AC97_LINE, 0, 7, 1),
@@ -133,7 +144,7 @@
 SOC_DOUBLE("Capture Volume", AC97_CD, 8, 0, 31, 0),
 SOC_SINGLE("Capture ZC Switch", AC97_CD, 7, 1, 0),
 
-SOC_SINGLE("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1),
+SOC_SINGLE_TLV("Capture to Headphone Volume", AC97_VIDEO, 11, 7, 1, misc_tlv),
 SOC_SINGLE("Capture to Mono Boost (+20dB) Switch", AC97_VIDEO, 8, 1, 0),
 SOC_SINGLE("Capture ADC Boost (+20dB) Switch", AC97_VIDEO, 6, 1, 0),
 
@@ -154,28 +165,43 @@
 
 SOC_SINGLE("Out4 Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
 SOC_SINGLE("Out4 Playback ZC Switch", AC97_MASTER_MONO, 14, 1, 0),
-SOC_SINGLE("Out4 Playback Volume", AC97_MASTER_MONO, 8, 63, 1),
+SOC_SINGLE_TLV("Out4 Playback Volume", AC97_MASTER_MONO, 8, 31, 1, out_tlv),
 
 SOC_SINGLE("Out3 Playback Switch", AC97_MASTER_MONO, 7, 1, 1),
 SOC_SINGLE("Out3 Playback ZC Switch", AC97_MASTER_MONO, 6, 1, 0),
-SOC_SINGLE("Out3 Playback Volume", AC97_MASTER_MONO, 0, 63, 1),
+SOC_SINGLE_TLV("Out3 Playback Volume", AC97_MASTER_MONO, 0, 31, 1, out_tlv),
 
-SOC_SINGLE("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1),
+SOC_SINGLE_TLV("Mono Capture Volume", AC97_MASTER_TONE, 8, 31, 1, main_tlv),
 SOC_SINGLE("Mono Playback Switch", AC97_MASTER_TONE, 7, 1, 1),
 SOC_SINGLE("Mono Playback ZC Switch", AC97_MASTER_TONE, 6, 1, 0),
-SOC_SINGLE("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1),
+SOC_SINGLE_TLV("Mono Playback Volume", AC97_MASTER_TONE, 0, 31, 1, out_tlv),
 
-SOC_SINGLE("Beep Playback Headphone Volume", AC97_AUX, 12, 7, 1),
-SOC_SINGLE("Beep Playback Speaker Volume", AC97_AUX, 8, 7, 1),
-SOC_SINGLE("Beep Playback Mono Volume", AC97_AUX, 4, 7, 1),
+SOC_SINGLE_TLV("Headphone Mixer Beep Playback Volume", AC97_AUX, 12, 7, 1,
+	       misc_tlv),
+SOC_SINGLE_TLV("Speaker Mixer Beep Playback Volume", AC97_AUX, 8, 7, 1,
+	       misc_tlv),
+SOC_SINGLE_TLV("Mono Mixer Beep Playback Volume", AC97_AUX, 4, 7, 1, misc_tlv),
 
-SOC_SINGLE("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1),
+SOC_SINGLE_TLV("Voice Playback Headphone Volume", AC97_PCM, 12, 7, 1,
+	       misc_tlv),
 SOC_SINGLE("Voice Playback Master Volume", AC97_PCM, 8, 7, 1),
 SOC_SINGLE("Voice Playback Mono Volume", AC97_PCM, 4, 7, 1),
 
+SOC_SINGLE_TLV("Headphone Mixer Aux Playback Volume", AC97_REC_SEL, 12, 7, 1,
+	       misc_tlv),
+
+SOC_SINGLE_TLV("Speaker Mixer Voice Playback Volume", AC97_PCM, 8, 7, 1,
+	       misc_tlv),
+SOC_SINGLE_TLV("Speaker Mixer Aux Playback Volume", AC97_REC_SEL, 8, 7, 1,
+	       misc_tlv),
+
+SOC_SINGLE_TLV("Mono Mixer Voice Playback Volume", AC97_PCM, 4, 7, 1,
+	       misc_tlv),
+SOC_SINGLE_TLV("Mono Mixer Aux Playback Volume", AC97_REC_SEL, 4, 7, 1,
+	       misc_tlv),
+
 SOC_SINGLE("Aux Playback Headphone Volume", AC97_REC_SEL, 12, 7, 1),
 SOC_SINGLE("Aux Playback Master Volume", AC97_REC_SEL, 8, 7, 1),
-SOC_SINGLE("Aux Playback Mono Volume", AC97_REC_SEL, 4, 7, 1),
 
 SOC_ENUM("Bass Control", wm9713_enum[16]),
 SOC_SINGLE("Bass Cut-off Switch", AC97_GENERAL_PURPOSE, 12, 1, 1),
@@ -1186,8 +1212,6 @@
 	struct snd_soc_codec *codec;
 	int ret = 0, reg;
 
-	printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION);
-
 	socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec),
 				      GFP_KERNEL);
 	if (socdev->card->codec == NULL)
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index d73c305..0ad9f5d 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -68,24 +68,77 @@
 	int count = 0;
 
 	dev_dbg(codec->dev, "Waiting for DC servo...\n");
+
 	do {
 		count++;
 		msleep(1);
 		reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0);
-		dev_dbg(codec->dev, "DC servo status: %x\n", reg);
-	} while ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
-		 != WM8993_DCS_CAL_COMPLETE_MASK && count < 1000);
+		dev_dbg(codec->dev, "DC servo: %x\n", reg);
+	} while (reg & WM8993_DCS_DATAPATH_BUSY);
 
-	if ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
-	    != WM8993_DCS_CAL_COMPLETE_MASK)
+	if (reg & WM8993_DCS_DATAPATH_BUSY)
 		dev_err(codec->dev, "Timed out waiting for DC Servo\n");
 }
 
 /*
+ * Startup calibration of the DC servo
+ */
+static void calibrate_dc_servo(struct snd_soc_codec *codec)
+{
+	struct wm_hubs_data *hubs = codec->private_data;
+	u16 reg, dcs_cfg;
+
+	/* Set for 32 series updates */
+	snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+			    WM8993_DCS_SERIES_NO_01_MASK,
+			    32 << WM8993_DCS_SERIES_NO_01_SHIFT);
+
+	/* Enable the DC servo.  Write all bits to avoid triggering startup
+	 * or write calibration.
+	 */
+	snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
+			    0xFFFF,
+			    WM8993_DCS_ENA_CHAN_0 |
+			    WM8993_DCS_ENA_CHAN_1 |
+			    WM8993_DCS_TRIG_SERIES_1 |
+			    WM8993_DCS_TRIG_SERIES_0);
+
+	wait_for_dc_servo(codec);
+
+	/* Apply correction to DC servo result */
+	if (hubs->dcs_codes) {
+		dev_dbg(codec->dev, "Applying %d code DC servo correction\n",
+			hubs->dcs_codes);
+
+		/* HPOUT1L */
+		reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_1) &
+			WM8993_DCS_INTEG_CHAN_0_MASK;;
+		reg += hubs->dcs_codes;
+		dcs_cfg = reg << WM8993_DCS_DAC_WR_VAL_1_SHIFT;
+
+		/* HPOUT1R */
+		reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_2) &
+			WM8993_DCS_INTEG_CHAN_1_MASK;
+		reg += hubs->dcs_codes;
+		dcs_cfg |= reg;
+
+		/* Do it */
+		snd_soc_write(codec, WM8993_DC_SERVO_3, dcs_cfg);
+		snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
+				    WM8993_DCS_TRIG_DAC_WR_0 |
+				    WM8993_DCS_TRIG_DAC_WR_1,
+				    WM8993_DCS_TRIG_DAC_WR_0 |
+				    WM8993_DCS_TRIG_DAC_WR_1);
+
+		wait_for_dc_servo(codec);
+	}
+}
+
+/*
  * Update the DC servo calibration on gain changes
  */
 static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
-			      struct snd_ctl_elem_value *ucontrol)
+			       struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	int ret;
@@ -251,6 +304,47 @@
 	       line_tlv),
 };
 
+static int hp_supply_event(struct snd_soc_dapm_widget *w,
+			   struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct wm_hubs_data *hubs = codec->private_data;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		switch (hubs->hp_startup_mode) {
+		case 0:
+			break;
+		case 1:
+			/* Enable the headphone amp */
+			snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+					    WM8993_HPOUT1L_ENA |
+					    WM8993_HPOUT1R_ENA,
+					    WM8993_HPOUT1L_ENA |
+					    WM8993_HPOUT1R_ENA);
+
+			/* Enable the second stage */
+			snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+					    WM8993_HPOUT1L_DLY |
+					    WM8993_HPOUT1R_DLY,
+					    WM8993_HPOUT1L_DLY |
+					    WM8993_HPOUT1R_DLY);
+			break;
+		default:
+			dev_err(codec->dev, "Unknown HP startup mode %d\n",
+				hubs->hp_startup_mode);
+			break;
+		}
+
+	case SND_SOC_DAPM_PRE_PMD:
+		snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+				    WM8993_CP_ENA, 0);
+		break;
+	}
+
+	return 0;
+}
+
 static int hp_event(struct snd_soc_dapm_widget *w,
 		    struct snd_kcontrol *kcontrol, int event)
 {
@@ -271,14 +365,11 @@
 		reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
 		snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
 
-		/* Start the DC servo */
-		snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
-				    0xFFFF,
-				    WM8993_DCS_ENA_CHAN_0 |
-				    WM8993_DCS_ENA_CHAN_1 |
-				    WM8993_DCS_TRIG_STARTUP_1 |
-				    WM8993_DCS_TRIG_STARTUP_0);
-		wait_for_dc_servo(codec);
+		/* Smallest supported update interval */
+		snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+				    WM8993_DCS_TIMER_PERIOD_01_MASK, 1);
+
+		calibrate_dc_servo(codec);
 
 		reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
 			WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
@@ -286,23 +377,19 @@
 		break;
 
 	case SND_SOC_DAPM_PRE_PMD:
-		reg &= ~(WM8993_HPOUT1L_RMV_SHORT |
-			 WM8993_HPOUT1L_DLY |
-			 WM8993_HPOUT1L_OUTP |
-			 WM8993_HPOUT1R_RMV_SHORT |
-			 WM8993_HPOUT1R_DLY |
-			 WM8993_HPOUT1R_OUTP);
+		snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+				    WM8993_HPOUT1L_DLY |
+				    WM8993_HPOUT1R_DLY |
+				    WM8993_HPOUT1L_RMV_SHORT |
+				    WM8993_HPOUT1R_RMV_SHORT, 0);
 
-		snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
-				    0xffff, 0);
+		snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+				    WM8993_HPOUT1L_OUTP |
+				    WM8993_HPOUT1R_OUTP, 0);
 
-		snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
 		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
 				    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
 				    0);
-
-		snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
-				    WM8993_CP_ENA, 0);
 		break;
 	}
 
@@ -473,6 +560,8 @@
 SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
 SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
 
+SND_SOC_DAPM_SUPPLY("Headphone Supply", SND_SOC_NOPM, 0, 0, hp_supply_event, 
+		    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
 SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0,
 		   NULL, 0,
 		   hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -626,6 +715,7 @@
 	{ "Headphone PGA", NULL, "Left Headphone Mux" },
 	{ "Headphone PGA", NULL, "Right Headphone Mux" },
 	{ "Headphone PGA", NULL, "CLK_SYS" },
+	{ "Headphone PGA", NULL, "Headphone Supply" },
 
 	{ "HPOUT1L", NULL, "Headphone PGA" },
 	{ "HPOUT1R", NULL, "Headphone PGA" },
@@ -753,6 +843,12 @@
 				    WM8993_LINEOUT2_MODE,
 				    WM8993_LINEOUT2_MODE);
 
+	/* If the line outputs are differential then we aren't presenting
+	 * VMID as an output and can disable it.
+	 */
+	if (lineout1_diff && lineout2_diff)
+		codec->idle_bias_off = 1;
+
 	if (lineout1fb)
 		snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
 				    WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index 36d3fba..420104f 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -18,6 +18,12 @@
 
 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 hp_startup_mode;
+};
+
 extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
 extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int);
 extern int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *,
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 0a302e1..ab6518d 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -767,14 +767,26 @@
 	int ret = 0;
 
 	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (!dev->clk_active) {
+			clk_enable(dev->clk);
+			dev->clk_active = 1;
+		}
 		davinci_mcasp_start(dev, substream->stream);
 		break;
 
-	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
+		davinci_mcasp_stop(dev, substream->stream);
+		if (dev->clk_active) {
+			clk_disable(dev->clk);
+			dev->clk_active = 0;
+		}
+
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		davinci_mcasp_stop(dev, substream->stream);
 		break;
@@ -866,6 +878,7 @@
 	}
 
 	clk_enable(dev->clk);
+	dev->clk_active = 1;
 
 	dev->base = (void __iomem *)IO_ADDRESS(mem->start);
 	dev->op_mode = pdata->op_mode;
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
index 582c924..e755b51 100644
--- a/sound/soc/davinci/davinci-mcasp.h
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -44,6 +44,7 @@
 	int sample_rate;
 	struct clk *clk;
 	unsigned int codec_fmt;
+	u8 clk_active;
 
 	/* McASP specific data */
 	int	tdm_slots;
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index ad4d7f4..80c7fdf 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -49,7 +49,7 @@
 static struct snd_pcm_hardware pcm_hardware_playback = {
 	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
 		 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
-		 SNDRV_PCM_INFO_PAUSE),
+		 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
 	.formats = (SNDRV_PCM_FMTBIT_S16_LE),
 	.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
 		  SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 |
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
index a700562e..c7d0fd9 100644
--- a/sound/soc/imx/Kconfig
+++ b/sound/soc/imx/Kconfig
@@ -1,21 +1,13 @@
-config SND_MX1_MX2_SOC
-	tristate "SoC Audio for Freecale i.MX1x i.MX2x CPUs"
-	depends on ARCH_MX2 || ARCH_MX1
+config SND_IMX_SOC
+	tristate "SoC Audio for Freescale i.MX CPUs"
+	depends on ARCH_MXC && BROKEN
 	select SND_PCM
+	select FIQ
+	select SND_SOC_AC97_BUS
 	help
 	  Say Y or M if you want to add support for codecs attached to
-	  the MX1 or MX2 SSI interface.
+	  the i.MX SSI interface.
 
 config SND_MXC_SOC_SSI
 	tristate
 
-config SND_SOC_MX27VIS_WM8974
-	tristate "SoC Audio support for MX27 - WM8974 Visstrim_sm10 board"
-	depends on SND_MX1_MX2_SOC && MACH_MX27 && MACH_IMX27_VISSTRIM_M10
-	select SND_MXC_SOC_SSI
-	select SND_SOC_WM8974
-	help
-	  Say Y if you want to add support for SoC audio on Visstrim SM10
-	  board with WM8974.
-
-
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
index c2ffd2c..9f8bb92 100644
--- a/sound/soc/imx/Makefile
+++ b/sound/soc/imx/Makefile
@@ -1,10 +1,12 @@
 # i.MX Platform Support
-snd-soc-mx1_mx2-objs := mx1_mx2-pcm.o
-snd-soc-mxc-ssi-objs := mxc-ssi.o
+snd-soc-imx-objs := imx-ssi.o imx-pcm-fiq.o
 
-obj-$(CONFIG_SND_MX1_MX2_SOC) += snd-soc-mx1_mx2.o
-obj-$(CONFIG_SND_MXC_SOC_SSI) += snd-soc-mxc-ssi.o
+ifdef CONFIG_MACH_MX27
+snd-soc-imx-objs += imx-pcm-dma-mx2.o
+endif
+
+obj-$(CONFIG_SND_IMX_SOC) += snd-soc-imx.o
 
 # i.MX Machine Support
-snd-soc-mx27vis-wm8974-objs := mx27vis_wm8974.o
-obj-$(CONFIG_SND_SOC_MX27VIS_WM8974) += snd-soc-mx27vis-wm8974.o
+snd-soc-phycore-ac97-objs := phycore-ac97.o
+obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c
new file mode 100644
index 0000000..19452e4
--- /dev/null
+++ b/sound/soc/imx/imx-pcm-dma-mx2.c
@@ -0,0 +1,313 @@
+/*
+ * imx-pcm-dma-mx2.c  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This code is based on code copyrighted by Freescale,
+ * Liam Girdwood, Javier Martin and probably others.
+ *
+ *  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.
+ */
+#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 <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/dma-mx1-mx2.h>
+
+#include "imx-ssi.h"
+
+struct imx_pcm_runtime_data {
+	int sg_count;
+	struct scatterlist *sg_list;
+	int period;
+	int periods;
+	unsigned long dma_addr;
+	int dma;
+	struct snd_pcm_substream *substream;
+	unsigned long offset;
+	unsigned long size;
+	unsigned long period_cnt;
+	void *buf;
+	int period_time;
+};
+
+/* Called by the DMA framework when a period has elapsed */
+static void imx_ssi_dma_progression(int channel, void *data,
+					struct scatterlist *sg)
+{
+	struct snd_pcm_substream *substream = data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+	if (!sg)
+		return;
+
+	runtime = iprtd->substream->runtime;
+
+	iprtd->offset = sg->dma_address - runtime->dma_addr;
+
+	snd_pcm_period_elapsed(iprtd->substream);
+}
+
+static void imx_ssi_dma_callback(int channel, void *data)
+{
+	pr_err("%s shouldn't be called\n", __func__);
+}
+
+static void snd_imx_dma_err_callback(int channel, void *data, int err)
+{
+	pr_err("DMA error callback called\n");
+
+	pr_err("DMA timeout on channel %d -%s%s%s%s\n",
+		 channel,
+		 err & IMX_DMA_ERR_BURST ?    " burst" : "",
+		 err & IMX_DMA_ERR_REQUEST ?  " request" : "",
+		 err & IMX_DMA_ERR_TRANSFER ? " transfer" : "",
+		 err & IMX_DMA_ERR_BUFFER ?   " buffer" : "");
+}
+
+static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct imx_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+	int ret;
+
+	iprtd->dma = imx_dma_request_by_prio(DRV_NAME, DMA_PRIO_HIGH);
+	if (iprtd->dma < 0) {
+		pr_err("Failed to claim the audio DMA\n");
+		return -ENODEV;
+	}
+
+	ret = imx_dma_setup_handlers(iprtd->dma,
+				imx_ssi_dma_callback,
+				snd_imx_dma_err_callback, substream);
+	if (ret)
+		goto out;
+
+	ret = imx_dma_setup_progression_handler(iprtd->dma,
+			imx_ssi_dma_progression);
+	if (ret) {
+		pr_err("Failed to setup the DMA handler\n");
+		goto out;
+	}
+
+	ret = imx_dma_config_channel(iprtd->dma,
+			IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+			IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+			dma_params->dma, 1);
+	if (ret < 0) {
+		pr_err("Cannot configure DMA channel: %d\n", ret);
+		goto out;
+	}
+
+	imx_dma_config_burstlen(iprtd->dma, dma_params->burstsize * 2);
+
+	return 0;
+out:
+	imx_dma_free(iprtd->dma);
+	return ret;
+}
+
+static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+	int i;
+	unsigned long dma_addr;
+
+	imx_ssi_dma_alloc(substream);
+
+	iprtd->size = params_buffer_bytes(params);
+	iprtd->periods = params_periods(params);
+	iprtd->period = 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);
+
+	if (iprtd->sg_count != iprtd->periods) {
+		kfree(iprtd->sg_list);
+
+		iprtd->sg_list = kcalloc(iprtd->periods + 1,
+				sizeof(struct scatterlist), GFP_KERNEL);
+		if (!iprtd->sg_list)
+			return -ENOMEM;
+		iprtd->sg_count = iprtd->periods + 1;
+	}
+
+	sg_init_table(iprtd->sg_list, iprtd->sg_count);
+	dma_addr = runtime->dma_addr;
+
+	for (i = 0; i < iprtd->periods; i++) {
+		iprtd->sg_list[i].page_link = 0;
+		iprtd->sg_list[i].offset = 0;
+		iprtd->sg_list[i].dma_address = dma_addr;
+		iprtd->sg_list[i].length = iprtd->period;
+		dma_addr += iprtd->period;
+	}
+
+	/* close the loop */
+	iprtd->sg_list[iprtd->sg_count - 1].offset = 0;
+	iprtd->sg_list[iprtd->sg_count - 1].length = 0;
+	iprtd->sg_list[iprtd->sg_count - 1].page_link =
+			((unsigned long) iprtd->sg_list | 0x01) & ~0x02;
+	return 0;
+}
+
+static int snd_imx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+	if (iprtd->dma >= 0) {
+		imx_dma_free(iprtd->dma);
+		iprtd->dma = -EINVAL;
+	}
+
+	kfree(iprtd->sg_list);
+	iprtd->sg_list = NULL;
+
+	return 0;
+}
+
+static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct imx_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
+	struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+	int err;
+
+	iprtd->substream = substream;
+	iprtd->buf = (unsigned int *)substream->dma_buffer.area;
+	iprtd->period_cnt = 0;
+
+	pr_debug("%s: buf: %p period: %d periods: %d\n",
+			__func__, iprtd->buf, iprtd->period, iprtd->periods);
+
+	err = imx_dma_setup_sg(iprtd->dma, iprtd->sg_list, iprtd->sg_count,
+			IMX_DMA_LENGTH_LOOP, dma_params->dma_addr,
+			substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			DMA_MODE_WRITE : DMA_MODE_READ);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_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:
+		imx_dma_enable(iprtd->dma);
+
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		imx_dma_disable(iprtd->dma);
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+	return bytes_to_frames(substream->runtime, iprtd->offset);
+}
+
+static struct snd_pcm_hardware snd_imx_hardware = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_RESUME,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	.rate_min = 8000,
+	.channels_min = 2,
+	.channels_max = 2,
+	.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
+	.period_bytes_min = 128,
+	.period_bytes_max = 16 * 1024,
+	.periods_min = 2,
+	.periods_max = 255,
+	.fifo_size = 0,
+};
+
+static int snd_imx_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd;
+	int ret;
+
+	iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
+	runtime->private_data = iprtd;
+
+	ret = snd_pcm_hw_constraint_integer(substream->runtime,
+			SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
+	return 0;
+}
+
+static struct snd_pcm_ops imx_pcm_ops = {
+	.open		= snd_imx_open,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_imx_pcm_hw_params,
+	.hw_free	= snd_imx_pcm_hw_free,
+	.prepare	= snd_imx_pcm_prepare,
+	.trigger	= snd_imx_pcm_trigger,
+	.pointer	= snd_imx_pcm_pointer,
+	.mmap		= snd_imx_pcm_mmap,
+};
+
+static struct snd_soc_platform imx_soc_platform_dma = {
+	.name		= "imx-audio",
+	.pcm_ops 	= &imx_pcm_ops,
+	.pcm_new	= imx_pcm_new,
+	.pcm_free	= imx_pcm_free,
+};
+
+struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev,
+		struct imx_ssi *ssi)
+{
+	ssi->dma_params_tx.burstsize = DMA_TXFIFO_BURST;
+	ssi->dma_params_rx.burstsize = DMA_RXFIFO_BURST;
+
+	return &imx_soc_platform_dma;
+}
+
diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c
new file mode 100644
index 0000000..d9cb984
--- /dev/null
+++ b/sound/soc/imx/imx-pcm-fiq.c
@@ -0,0 +1,297 @@
+/*
+ * imx-pcm-fiq.c  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This code is based on code copyrighted by Freescale,
+ * Liam Girdwood, Javier Martin and probably others.
+ *
+ *  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.
+ */
+#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 <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/fiq.h>
+
+#include <mach/ssi.h>
+
+#include "imx-ssi.h"
+
+struct imx_pcm_runtime_data {
+	int period;
+	int periods;
+	unsigned long offset;
+	unsigned long last_offset;
+	unsigned long size;
+	struct timer_list timer;
+	int poll_time;
+};
+
+static inline void imx_ssi_set_next_poll(struct imx_pcm_runtime_data *iprtd)
+{
+	iprtd->timer.expires = jiffies + iprtd->poll_time;
+}
+
+static void imx_ssi_timer_callback(unsigned long data)
+{
+	struct snd_pcm_substream *substream = (void *)data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+	struct pt_regs regs;
+	unsigned long delta;
+
+	get_fiq_regs(&regs);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		iprtd->offset = regs.ARM_r8 & 0xffff;
+	else
+		iprtd->offset = regs.ARM_r9 & 0xffff;
+
+	/* How much data have we transferred since the last period report? */
+	if (iprtd->offset >= iprtd->last_offset)
+		delta = iprtd->offset - iprtd->last_offset;
+	else
+		delta = runtime->buffer_size + iprtd->offset
+			- iprtd->last_offset;
+
+	/* If we've transferred at least a period then report it and
+	 * reset our poll time */
+	if (delta >= runtime->period_size) {
+		snd_pcm_period_elapsed(substream);
+		iprtd->last_offset = iprtd->offset;
+
+		imx_ssi_set_next_poll(iprtd);
+	}
+
+	/* Restart the timer; if we didn't report we'll run on the next tick */
+	add_timer(&iprtd->timer);
+
+}
+
+static struct fiq_handler fh = {
+	.name		= DRV_NAME,
+};
+
+static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+	iprtd->size = params_buffer_bytes(params);
+	iprtd->periods = params_periods(params);
+	iprtd->period = params_period_bytes(params) ;
+	iprtd->offset = 0;
+	iprtd->last_offset = 0;
+	iprtd->poll_time = HZ / (params_rate(params) / params_period_size(params));
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	return 0;
+}
+
+static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+	struct pt_regs regs;
+
+	get_fiq_regs(&regs);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		regs.ARM_r8 = (iprtd->period * iprtd->periods - 1) << 16;
+	else
+		regs.ARM_r9 = (iprtd->period * iprtd->periods - 1) << 16;
+
+	set_fiq_regs(&regs);
+
+	return 0;
+}
+
+static int fiq_enable;
+static int imx_pcm_fiq;
+
+static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_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:
+		imx_ssi_set_next_poll(iprtd);
+		add_timer(&iprtd->timer);
+		if (++fiq_enable == 1)
+			enable_fiq(imx_pcm_fiq);
+
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		del_timer(&iprtd->timer);
+		if (--fiq_enable == 0)
+			disable_fiq(imx_pcm_fiq);
+
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+	return bytes_to_frames(substream->runtime, iprtd->offset);
+}
+
+static struct snd_pcm_hardware snd_imx_hardware = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_PAUSE |
+		SNDRV_PCM_INFO_RESUME,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	.rate_min = 8000,
+	.channels_min = 2,
+	.channels_max = 2,
+	.buffer_bytes_max = IMX_SSI_DMABUF_SIZE,
+	.period_bytes_min = 128,
+	.period_bytes_max = 16 * 1024,
+	.periods_min = 2,
+	.periods_max = 255,
+	.fifo_size = 0,
+};
+
+static int snd_imx_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd;
+	int ret;
+
+	iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL);
+	runtime->private_data = iprtd;
+
+	init_timer(&iprtd->timer);
+	iprtd->timer.data = (unsigned long)substream;
+	iprtd->timer.function = imx_ssi_timer_callback;
+
+	ret = snd_pcm_hw_constraint_integer(substream->runtime,
+			SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
+	return 0;
+}
+
+static int snd_imx_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct imx_pcm_runtime_data *iprtd = runtime->private_data;
+
+	del_timer_sync(&iprtd->timer);
+	kfree(iprtd);
+
+	return 0;
+}
+
+static struct snd_pcm_ops imx_pcm_ops = {
+	.open		= snd_imx_open,
+	.close		= snd_imx_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= snd_imx_pcm_hw_params,
+	.prepare	= snd_imx_pcm_prepare,
+	.trigger	= snd_imx_pcm_trigger,
+	.pointer	= snd_imx_pcm_pointer,
+	.mmap		= snd_imx_pcm_mmap,
+};
+
+static int imx_pcm_fiq_new(struct snd_card *card, struct snd_soc_dai *dai,
+	struct snd_pcm *pcm)
+{
+	int ret;
+
+	ret = imx_pcm_new(card, dai, pcm);
+	if (ret)
+		return ret;
+
+	if (dai->playback.channels_min) {
+		struct snd_pcm_substream *substream =
+			pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+		struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+		imx_ssi_fiq_tx_buffer = (unsigned long)buf->area;
+	}
+
+	if (dai->capture.channels_min) {
+		struct snd_pcm_substream *substream =
+			pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+		struct snd_dma_buffer *buf = &substream->dma_buffer;
+
+		imx_ssi_fiq_rx_buffer = (unsigned long)buf->area;
+	}
+
+	set_fiq_handler(&imx_ssi_fiq_start,
+		&imx_ssi_fiq_end - &imx_ssi_fiq_start);
+
+	return 0;
+}
+
+static struct snd_soc_platform imx_soc_platform_fiq = {
+	.pcm_ops 	= &imx_pcm_ops,
+	.pcm_new	= imx_pcm_fiq_new,
+	.pcm_free	= imx_pcm_free,
+};
+
+struct snd_soc_platform *imx_ssi_fiq_init(struct platform_device *pdev,
+		struct imx_ssi *ssi)
+{
+	int ret = 0;
+
+	ret = claim_fiq(&fh);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to claim fiq: %d", ret);
+		return ERR_PTR(ret);
+	}
+
+	mxc_set_irq_fiq(ssi->irq, 1);
+
+	imx_pcm_fiq = ssi->irq;
+
+	imx_ssi_fiq_base = (unsigned long)ssi->base;
+
+	ssi->dma_params_tx.burstsize = 4;
+	ssi->dma_params_rx.burstsize = 6;
+
+	return &imx_soc_platform_fiq;
+}
+
+void imx_ssi_fiq_exit(struct platform_device *pdev,
+		struct imx_ssi *ssi)
+{
+	mxc_set_irq_fiq(ssi->irq, 0);
+	release_fiq(&fh);
+}
+
diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c
new file mode 100644
index 0000000..56f46a7
--- /dev/null
+++ b/sound/soc/imx/imx-ssi.c
@@ -0,0 +1,758 @@
+/*
+ * imx-ssi.c  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * This code is based on code copyrighted by Freescale,
+ * Liam Girdwood, Javier Martin and probably others.
+ *
+ *  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.
+ *
+ *
+ * The i.MX SSI core has some nasty limitations in AC97 mode. While most
+ * sane processor vendors have a FIFO per AC97 slot, the i.MX has only
+ * one FIFO which combines all valid receive slots. We cannot even select
+ * which slots we want to receive. The WM9712 with which this driver
+ * was developped with always sends GPIO status data in slot 12 which
+ * we receive in our (PCM-) data stream. The only chance we have is to
+ * manually skip this data in the FIQ handler. With sampling rates different
+ * from 48000Hz not every frame has valid receive data, so the ratio
+ * between pcm data and GPIO status data changes. Our FIQ handler is not
+ * able to handle this, hence this driver only works with 48000Hz sampling
+ * rate.
+ * Reading and writing AC97 registers is another challange. The core
+ * provides us status bits when the read register is updated with *another*
+ * value. When we read the same register two times (and the register still
+ * contains the same value) these status bits are not set. We work
+ * around this by not polling these bits but only wait a fixed delay.
+ * 
+ */
+
+#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 <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <mach/ssi.h>
+#include <mach/hardware.h>
+
+#include "imx-ssi.h"
+
+#define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV)
+
+/*
+ * SSI Network Mode or TDM slots configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+	struct imx_ssi *ssi = cpu_dai->private_data;
+	u32 sccr;
+
+	sccr = readl(ssi->base + SSI_STCCR);
+	sccr &= ~SSI_STCCR_DC_MASK;
+	sccr |= SSI_STCCR_DC(slots - 1);
+	writel(sccr, ssi->base + SSI_STCCR);
+
+	sccr = readl(ssi->base + SSI_SRCCR);
+	sccr &= ~SSI_STCCR_DC_MASK;
+	sccr |= SSI_STCCR_DC(slots - 1);
+	writel(sccr, ssi->base + SSI_SRCCR);
+
+	writel(tx_mask, ssi->base + SSI_STMSK);
+	writel(rx_mask, ssi->base + SSI_SRMSK);
+
+	return 0;
+}
+
+/*
+ * SSI DAI format configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ * Note: We don't use the I2S modes but instead manually configure the
+ * SSI for I2S because the I2S mode is only a register preset.
+ */
+static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+	struct imx_ssi *ssi = cpu_dai->private_data;
+	u32 strcr = 0, scr;
+
+	scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET);
+
+	/* DAI mode */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		/* data on rising edge of bclk, frame low 1clk before data */
+		strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
+		scr |= SSI_SCR_NET;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		/* data on rising edge of bclk, frame high with data */
+		strcr |= SSI_STCR_TXBIT0;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		/* data on rising edge of bclk, frame high with data */
+		strcr |= SSI_STCR_TFSL;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		/* data on rising edge of bclk, frame high 1clk before data */
+		strcr |= SSI_STCR_TFSL | SSI_STCR_TEFS;
+		break;
+	}
+
+	/* DAI clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_IB_IF:
+		strcr |= SSI_STCR_TFSI;
+		strcr &= ~SSI_STCR_TSCKP;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		strcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
+		break;
+	case SND_SOC_DAIFMT_NB_NF:
+		strcr &= ~SSI_STCR_TFSI;
+		strcr |= SSI_STCR_TSCKP;
+		break;
+	}
+
+	/* DAI clock master masks */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	default:
+		/* Master mode not implemented, needs handling of clocks. */
+		return -EINVAL;
+	}
+
+	strcr |= SSI_STCR_TFEN0;
+
+	writel(strcr, ssi->base + SSI_STCR);
+	writel(strcr, ssi->base + SSI_SRCR);
+	writel(scr, ssi->base + SSI_SCR);
+
+	return 0;
+}
+
+/*
+ * SSI system clock configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+				  int clk_id, unsigned int freq, int dir)
+{
+	struct imx_ssi *ssi = cpu_dai->private_data;
+	u32 scr;
+
+	scr = readl(ssi->base + SSI_SCR);
+
+	switch (clk_id) {
+	case IMX_SSP_SYS_CLK:
+		if (dir == SND_SOC_CLOCK_OUT)
+			scr |= SSI_SCR_SYS_CLK_EN;
+		else
+			scr &= ~SSI_SCR_SYS_CLK_EN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(scr, ssi->base + SSI_SCR);
+
+	return 0;
+}
+
+/*
+ * SSI Clock dividers
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+				  int div_id, int div)
+{
+	struct imx_ssi *ssi = cpu_dai->private_data;
+	u32 stccr, srccr;
+
+	stccr = readl(ssi->base + SSI_STCCR);
+	srccr = readl(ssi->base + SSI_SRCCR);
+
+	switch (div_id) {
+	case IMX_SSI_TX_DIV_2:
+		stccr &= ~SSI_STCCR_DIV2;
+		stccr |= div;
+		break;
+	case IMX_SSI_TX_DIV_PSR:
+		stccr &= ~SSI_STCCR_PSR;
+		stccr |= div;
+		break;
+	case IMX_SSI_TX_DIV_PM:
+		stccr &= ~0xff;
+		stccr |= SSI_STCCR_PM(div);
+		break;
+	case IMX_SSI_RX_DIV_2:
+		stccr &= ~SSI_STCCR_DIV2;
+		stccr |= div;
+		break;
+	case IMX_SSI_RX_DIV_PSR:
+		stccr &= ~SSI_STCCR_PSR;
+		stccr |= div;
+		break;
+	case IMX_SSI_RX_DIV_PM:
+		stccr &= ~0xff;
+		stccr |= SSI_STCCR_PM(div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	writel(stccr, ssi->base + SSI_STCCR);
+	writel(srccr, ssi->base + SSI_SRCCR);
+
+	return 0;
+}
+
+/*
+ * Should only be called when port is inactive (i.e. SSIEN = 0),
+ * although can be called multiple times by upper layers.
+ */
+static int imx_ssi_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *cpu_dai)
+{
+	struct imx_ssi *ssi = cpu_dai->private_data;
+	u32 reg, sccr;
+
+	/* Tx/Rx config */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		reg = SSI_STCCR;
+		cpu_dai->dma_data = &ssi->dma_params_tx;
+	} else {
+		reg = SSI_SRCCR;
+		cpu_dai->dma_data = &ssi->dma_params_rx;
+	}
+
+	sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK;
+
+	/* DAI data (word) size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		sccr |= SSI_SRCCR_WL(16);
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		sccr |= SSI_SRCCR_WL(20);
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		sccr |= SSI_SRCCR_WL(24);
+		break;
+	}
+
+	writel(sccr, ssi->base + reg);
+
+	return 0;
+}
+
+static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+		struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct imx_ssi *ssi = cpu_dai->private_data;
+	unsigned int sier_bits, sier;
+	unsigned int scr;
+
+	scr = readl(ssi->base + SSI_SCR);
+	sier = readl(ssi->base + SSI_SIER);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (ssi->flags & IMX_SSI_DMA)
+			sier_bits = SSI_SIER_TDMAE;
+		else
+			sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN;
+	} else {
+		if (ssi->flags & IMX_SSI_DMA)
+			sier_bits = SSI_SIER_RDMAE;
+		else
+			sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN;
+	}
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			scr |= SSI_SCR_TE;
+		else
+			scr |= SSI_SCR_RE;
+		sier |= sier_bits;
+
+		if (++ssi->enabled == 1)
+			scr |= SSI_SCR_SSIEN;
+
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			scr &= ~SSI_SCR_TE;
+		else
+			scr &= ~SSI_SCR_RE;
+		sier &= ~sier_bits;
+
+		if (--ssi->enabled == 0)
+			scr &= ~SSI_SCR_SSIEN;
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (!(ssi->flags & IMX_SSI_USE_AC97))
+		/* rx/tx are always enabled to access ac97 registers */
+		writel(scr, ssi->base + SSI_SCR);
+
+	writel(sier, ssi->base + SSI_SIER);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
+	.hw_params	= imx_ssi_hw_params,
+	.set_fmt	= imx_ssi_set_dai_fmt,
+	.set_clkdiv	= imx_ssi_set_dai_clkdiv,
+	.set_sysclk	= imx_ssi_set_dai_sysclk,
+	.set_tdm_slot	= imx_ssi_set_dai_tdm_slot,
+	.trigger	= imx_ssi_trigger,
+};
+
+static struct snd_soc_dai imx_ssi_dai = {
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &imx_ssi_pcm_dai_ops,
+};
+
+int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
+		struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int ret;
+
+	ret = dma_mmap_coherent(NULL, 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,
+			runtime->dma_addr,
+			runtime->dma_bytes);
+	return ret;
+}
+
+static int imx_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 = IMX_SSI_DMABUF_SIZE;
+
+	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 imx_pcm_dmamask = DMA_BIT_MASK(32);
+
+int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+	struct snd_pcm *pcm)
+{
+
+	int ret = 0;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &imx_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+	if (dai->playback.channels_min) {
+		ret = imx_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto out;
+	}
+
+	if (dai->capture.channels_min) {
+		ret = imx_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto out;
+	}
+
+out:
+	return ret;
+}
+
+void imx_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;
+	}
+}
+
+struct snd_soc_platform imx_soc_platform = {
+	.name		= "imx-audio",
+};
+EXPORT_SYMBOL_GPL(imx_soc_platform);
+
+static struct snd_soc_dai imx_ac97_dai = {
+	.name = "AC97",
+	.ac97_control = 1,
+	.playback = {
+		.stream_name = "AC97 Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "AC97 Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &imx_ssi_pcm_dai_ops,
+};
+
+static void setup_channel_to_ac97(struct imx_ssi *imx_ssi)
+{
+	void __iomem *base = imx_ssi->base;
+
+	writel(0x0, base + SSI_SCR);
+	writel(0x0, base + SSI_STCR);
+	writel(0x0, base + SSI_SRCR);
+
+	writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR);
+
+	writel(SSI_SFCSR_RFWM0(8) |
+		SSI_SFCSR_TFWM0(8) |
+		SSI_SFCSR_RFWM1(8) |
+		SSI_SFCSR_TFWM1(8), base + SSI_SFCSR);
+
+	writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_STCCR);
+	writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_SRCCR);
+
+	writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN, base + SSI_SCR);
+	writel(SSI_SOR_WAIT(3), base + SSI_SOR);
+
+	writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN |
+			SSI_SCR_TE | SSI_SCR_RE,
+			base + SSI_SCR);
+
+	writel(SSI_SACNT_DEFAULT, base + SSI_SACNT);
+	writel(0xff, base + SSI_SACCDIS);
+	writel(0x300, base + SSI_SACCEN);
+}
+
+static struct imx_ssi *ac97_ssi;
+
+static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+		unsigned short val)
+{
+	struct imx_ssi *imx_ssi = ac97_ssi;
+	void __iomem *base = imx_ssi->base;
+	unsigned int lreg;
+	unsigned int lval;
+
+	if (reg > 0x7f)
+		return;
+
+	pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
+
+	lreg = reg <<  12;
+	writel(lreg, base + SSI_SACADD);
+
+	lval = val << 4;
+	writel(lval , base + SSI_SACDAT);
+
+	writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT);
+	udelay(100);
+}
+
+static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97,
+		unsigned short reg)
+{
+	struct imx_ssi *imx_ssi = ac97_ssi;
+	void __iomem *base = imx_ssi->base;
+
+	unsigned short val = -1;
+	unsigned int lreg;
+
+	lreg = (reg & 0x7f) <<  12 ;
+	writel(lreg, base + SSI_SACADD);
+	writel(SSI_SACNT_DEFAULT | SSI_SACNT_RD, base + SSI_SACNT);
+
+	udelay(100);
+
+	val = (readl(base + SSI_SACDAT) >> 4) & 0xffff;
+
+	pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val);
+
+	return val;
+}
+
+static void imx_ssi_ac97_reset(struct snd_ac97 *ac97)
+{
+	struct imx_ssi *imx_ssi = ac97_ssi;
+
+	if (imx_ssi->ac97_reset)
+		imx_ssi->ac97_reset(ac97);
+}
+
+static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+	struct imx_ssi *imx_ssi = ac97_ssi;
+
+	if (imx_ssi->ac97_warm_reset)
+		imx_ssi->ac97_warm_reset(ac97);
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read		= imx_ssi_ac97_read,
+	.write		= imx_ssi_ac97_write,
+	.reset		= imx_ssi_ac97_reset,
+	.warm_reset	= imx_ssi_ac97_warm_reset
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+struct snd_soc_dai imx_ssi_pcm_dai[2];
+EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
+
+static int imx_ssi_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct imx_ssi *ssi;
+	struct imx_ssi_platform_data *pdata = pdev->dev.platform_data;
+	struct snd_soc_platform *platform;
+	int ret = 0;
+	unsigned int val;
+	struct snd_soc_dai *dai = &imx_ssi_pcm_dai[pdev->id];
+
+	if (dai->id >= ARRAY_SIZE(imx_ssi_pcm_dai))
+		return -EINVAL;
+
+	ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);
+	if (!ssi)
+		return -ENOMEM;
+
+	if (pdata) {
+		ssi->ac97_reset = pdata->ac97_reset;
+		ssi->ac97_warm_reset = pdata->ac97_warm_reset;
+		ssi->flags = pdata->flags;
+	}
+
+	ssi->irq = platform_get_irq(pdev, 0);
+
+	ssi->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(ssi->clk)) {
+		ret = PTR_ERR(ssi->clk);
+		dev_err(&pdev->dev, "Cannot get the clock: %d\n",
+			ret);
+		goto failed_clk;
+	}
+	clk_enable(ssi->clk);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		goto failed_get_resource;
+	}
+
+	if (!request_mem_region(res->start, resource_size(res), DRV_NAME)) {
+		dev_err(&pdev->dev, "request_mem_region failed\n");
+		ret = -EBUSY;
+		goto failed_get_resource;
+	}
+
+	ssi->base = ioremap(res->start, resource_size(res));
+	if (!ssi->base) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENODEV;
+		goto failed_ioremap;
+	}
+
+	if (ssi->flags & IMX_SSI_USE_AC97) {
+		if (ac97_ssi) {
+			ret = -EBUSY;
+			goto failed_ac97;
+		}
+		ac97_ssi = ssi;
+		setup_channel_to_ac97(ssi);
+		memcpy(dai, &imx_ac97_dai, sizeof(imx_ac97_dai));
+	} else
+		memcpy(dai, &imx_ssi_dai, sizeof(imx_ssi_dai));
+
+	writel(0x0, ssi->base + SSI_SIER);
+
+	ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0;
+	ssi->dma_params_tx.dma_addr = res->start + SSI_STX0;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0");
+	if (res)
+		ssi->dma_params_tx.dma = res->start;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0");
+	if (res)
+		ssi->dma_params_rx.dma = res->start;
+
+	dai->id = pdev->id;
+	dai->dev = &pdev->dev;
+	dai->name = kasprintf(GFP_KERNEL, "imx-ssi.%d", pdev->id);
+	dai->private_data = ssi;
+
+	if ((cpu_is_mx27() || cpu_is_mx21()) &&
+			!(ssi->flags & IMX_SSI_USE_AC97)) {
+		ssi->flags |= IMX_SSI_DMA;
+		platform = imx_ssi_dma_mx2_init(pdev, ssi);
+	} else
+		platform = imx_ssi_fiq_init(pdev, ssi);
+
+	imx_soc_platform.pcm_ops = platform->pcm_ops;
+	imx_soc_platform.pcm_new = platform->pcm_new;
+	imx_soc_platform.pcm_free = platform->pcm_free;
+
+	val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.burstsize) |
+		SSI_SFCSR_RFWM0(ssi->dma_params_rx.burstsize);
+	writel(val, ssi->base + SSI_SFCSR);
+
+	ret = snd_soc_register_dai(dai);
+	if (ret) {
+		dev_err(&pdev->dev, "register DAI failed\n");
+		goto failed_register;
+	}
+
+	platform_set_drvdata(pdev, ssi);
+
+	return 0;
+
+failed_register:
+failed_ac97:
+	iounmap(ssi->base);
+failed_ioremap:
+	release_mem_region(res->start, resource_size(res));
+failed_get_resource:
+	clk_disable(ssi->clk);
+	clk_put(ssi->clk);
+failed_clk:
+	kfree(ssi);
+
+	return ret;
+}
+
+static int __devexit imx_ssi_remove(struct platform_device *pdev)
+{
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	struct imx_ssi *ssi = platform_get_drvdata(pdev);
+	struct snd_soc_dai *dai = &imx_ssi_pcm_dai[pdev->id];
+
+	snd_soc_unregister_dai(dai);
+
+	if (ssi->flags & IMX_SSI_USE_AC97)
+		ac97_ssi = NULL;
+
+	if (!(ssi->flags & IMX_SSI_DMA))
+		imx_ssi_fiq_exit(pdev, ssi);
+
+	iounmap(ssi->base);
+	release_mem_region(res->start, resource_size(res));
+	clk_disable(ssi->clk);
+	clk_put(ssi->clk);
+	kfree(ssi);
+
+	return 0;
+}
+
+static struct platform_driver imx_ssi_driver = {
+	.probe = imx_ssi_probe,
+	.remove = __devexit_p(imx_ssi_remove),
+
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init imx_ssi_init(void)
+{
+	int ret;
+
+	ret = snd_soc_register_platform(&imx_soc_platform);
+	if (ret) {
+		pr_err("failed to register soc platform: %d\n", ret);
+		return ret;
+	}
+
+	ret = platform_driver_register(&imx_ssi_driver);
+	if (ret) {
+		snd_soc_unregister_platform(&imx_soc_platform);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void __exit imx_ssi_exit(void)
+{
+	platform_driver_unregister(&imx_ssi_driver);
+	snd_soc_unregister_platform(&imx_soc_platform);
+}
+
+module_init(imx_ssi_init);
+module_exit(imx_ssi_exit);
+
+/* Module information */
+MODULE_AUTHOR("Sascha Hauer, <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h
new file mode 100644
index 0000000..55f26eb
--- /dev/null
+++ b/sound/soc/imx/imx-ssi.h
@@ -0,0 +1,237 @@
+/*
+ * 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 _IMX_SSI_H
+#define _IMX_SSI_H
+
+#define SSI_STX0	0x00
+#define SSI_STX1	0x04
+#define SSI_SRX0	0x08
+#define SSI_SRX1	0x0c
+
+#define SSI_SCR		0x10
+#define SSI_SCR_CLK_IST		(1 << 9)
+#define SSI_SCR_CLK_IST_SHIFT	9
+#define SSI_SCR_TCH_EN		(1 << 8)
+#define SSI_SCR_SYS_CLK_EN	(1 << 7)
+#define SSI_SCR_I2S_MODE_NORM	(0 << 5)
+#define SSI_SCR_I2S_MODE_MSTR	(1 << 5)
+#define SSI_SCR_I2S_MODE_SLAVE	(2 << 5)
+#define SSI_I2S_MODE_MASK	(3 << 5)
+#define SSI_SCR_SYN		(1 << 4)
+#define SSI_SCR_NET		(1 << 3)
+#define SSI_SCR_RE		(1 << 2)
+#define SSI_SCR_TE		(1 << 1)
+#define SSI_SCR_SSIEN		(1 << 0)
+
+#define SSI_SISR	0x14
+#define SSI_SISR_MASK		((1 << 19) - 1)
+#define SSI_SISR_CMDAU		(1 << 18)
+#define SSI_SISR_CMDDU		(1 << 17)
+#define SSI_SISR_RXT		(1 << 16)
+#define SSI_SISR_RDR1		(1 << 15)
+#define SSI_SISR_RDR0		(1 << 14)
+#define SSI_SISR_TDE1		(1 << 13)
+#define SSI_SISR_TDE0		(1 << 12)
+#define SSI_SISR_ROE1		(1 << 11)
+#define SSI_SISR_ROE0		(1 << 10)
+#define SSI_SISR_TUE1		(1 << 9)
+#define SSI_SISR_TUE0		(1 << 8)
+#define SSI_SISR_TFS		(1 << 7)
+#define SSI_SISR_RFS		(1 << 6)
+#define SSI_SISR_TLS		(1 << 5)
+#define SSI_SISR_RLS		(1 << 4)
+#define SSI_SISR_RFF1		(1 << 3)
+#define SSI_SISR_RFF0		(1 << 2)
+#define SSI_SISR_TFE1		(1 << 1)
+#define SSI_SISR_TFE0		(1 << 0)
+
+#define SSI_SIER	0x18
+#define SSI_SIER_RDMAE		(1 << 22)
+#define SSI_SIER_RIE		(1 << 21)
+#define SSI_SIER_TDMAE		(1 << 20)
+#define SSI_SIER_TIE		(1 << 19)
+#define SSI_SIER_CMDAU_EN	(1 << 18)
+#define SSI_SIER_CMDDU_EN	(1 << 17)
+#define SSI_SIER_RXT_EN		(1 << 16)
+#define SSI_SIER_RDR1_EN	(1 << 15)
+#define SSI_SIER_RDR0_EN	(1 << 14)
+#define SSI_SIER_TDE1_EN	(1 << 13)
+#define SSI_SIER_TDE0_EN	(1 << 12)
+#define SSI_SIER_ROE1_EN	(1 << 11)
+#define SSI_SIER_ROE0_EN	(1 << 10)
+#define SSI_SIER_TUE1_EN	(1 << 9)
+#define SSI_SIER_TUE0_EN	(1 << 8)
+#define SSI_SIER_TFS_EN		(1 << 7)
+#define SSI_SIER_RFS_EN		(1 << 6)
+#define SSI_SIER_TLS_EN		(1 << 5)
+#define SSI_SIER_RLS_EN		(1 << 4)
+#define SSI_SIER_RFF1_EN	(1 << 3)
+#define SSI_SIER_RFF0_EN	(1 << 2)
+#define SSI_SIER_TFE1_EN	(1 << 1)
+#define SSI_SIER_TFE0_EN	(1 << 0)
+
+#define SSI_STCR	0x1c
+#define SSI_STCR_TXBIT0		(1 << 9)
+#define SSI_STCR_TFEN1		(1 << 8)
+#define SSI_STCR_TFEN0		(1 << 7)
+#define SSI_FIFO_ENABLE_0_SHIFT 7
+#define SSI_STCR_TFDIR		(1 << 6)
+#define SSI_STCR_TXDIR		(1 << 5)
+#define SSI_STCR_TSHFD		(1 << 4)
+#define SSI_STCR_TSCKP		(1 << 3)
+#define SSI_STCR_TFSI		(1 << 2)
+#define SSI_STCR_TFSL		(1 << 1)
+#define SSI_STCR_TEFS		(1 << 0)
+
+#define SSI_SRCR	0x20
+#define SSI_SRCR_RXBIT0		(1 << 9)
+#define SSI_SRCR_RFEN1		(1 << 8)
+#define SSI_SRCR_RFEN0		(1 << 7)
+#define SSI_FIFO_ENABLE_0_SHIFT 7
+#define SSI_SRCR_RFDIR		(1 << 6)
+#define SSI_SRCR_RXDIR		(1 << 5)
+#define SSI_SRCR_RSHFD		(1 << 4)
+#define SSI_SRCR_RSCKP		(1 << 3)
+#define SSI_SRCR_RFSI		(1 << 2)
+#define SSI_SRCR_RFSL		(1 << 1)
+#define SSI_SRCR_REFS		(1 << 0)
+
+#define SSI_SRCCR		0x28
+#define SSI_SRCCR_DIV2		(1 << 18)
+#define SSI_SRCCR_PSR		(1 << 17)
+#define SSI_SRCCR_WL(x)		((((x) - 2) >> 1) << 13)
+#define SSI_SRCCR_DC(x)		(((x) & 0x1f) << 8)
+#define SSI_SRCCR_PM(x)		(((x) & 0xff) << 0)
+#define SSI_SRCCR_WL_MASK	(0xf << 13)
+#define SSI_SRCCR_DC_MASK	(0x1f << 8)
+#define SSI_SRCCR_PM_MASK	(0xff << 0)
+
+#define SSI_STCCR		0x24
+#define SSI_STCCR_DIV2		(1 << 18)
+#define SSI_STCCR_PSR		(1 << 17)
+#define SSI_STCCR_WL(x)		((((x) - 2) >> 1) << 13)
+#define SSI_STCCR_DC(x)		(((x) & 0x1f) << 8)
+#define SSI_STCCR_PM(x)		(((x) & 0xff) << 0)
+#define SSI_STCCR_WL_MASK	(0xf << 13)
+#define SSI_STCCR_DC_MASK	(0x1f << 8)
+#define SSI_STCCR_PM_MASK	(0xff << 0)
+
+#define SSI_SFCSR	0x2c
+#define SSI_SFCSR_RFCNT1(x)	(((x) & 0xf) << 28)
+#define SSI_RX_FIFO_1_COUNT_SHIFT 28
+#define SSI_SFCSR_TFCNT1(x)	(((x) & 0xf) << 24)
+#define SSI_TX_FIFO_1_COUNT_SHIFT 24
+#define SSI_SFCSR_RFWM1(x)	(((x) & 0xf) << 20)
+#define SSI_SFCSR_TFWM1(x)	(((x) & 0xf) << 16)
+#define SSI_SFCSR_RFCNT0(x)	(((x) & 0xf) << 12)
+#define SSI_RX_FIFO_0_COUNT_SHIFT 12
+#define SSI_SFCSR_TFCNT0(x)	(((x) & 0xf) <<  8)
+#define SSI_TX_FIFO_0_COUNT_SHIFT 8
+#define SSI_SFCSR_RFWM0(x)	(((x) & 0xf) <<  4)
+#define SSI_SFCSR_TFWM0(x)	(((x) & 0xf) <<  0)
+#define SSI_SFCSR_RFWM0_MASK	(0xf <<  4)
+#define SSI_SFCSR_TFWM0_MASK	(0xf <<  0)
+
+#define SSI_STR		0x30
+#define SSI_STR_TEST		(1 << 15)
+#define SSI_STR_RCK2TCK		(1 << 14)
+#define SSI_STR_RFS2TFS		(1 << 13)
+#define SSI_STR_RXSTATE(x)	(((x) & 0xf) << 8)
+#define SSI_STR_TXD2RXD		(1 <<  7)
+#define SSI_STR_TCK2RCK		(1 <<  6)
+#define SSI_STR_TFS2RFS		(1 <<  5)
+#define SSI_STR_TXSTATE(x)	(((x) & 0xf) << 0)
+
+#define SSI_SOR		0x34
+#define SSI_SOR_CLKOFF		(1 << 6)
+#define SSI_SOR_RX_CLR		(1 << 5)
+#define SSI_SOR_TX_CLR		(1 << 4)
+#define SSI_SOR_INIT		(1 << 3)
+#define SSI_SOR_WAIT(x)		(((x) & 0x3) << 1)
+#define SSI_SOR_WAIT_MASK	(0x3 << 1)
+#define SSI_SOR_SYNRST		(1 << 0)
+
+#define SSI_SACNT	0x38
+#define SSI_SACNT_FRDIV(x)	(((x) & 0x3f) << 5)
+#define SSI_SACNT_WR		(1 << 4)
+#define SSI_SACNT_RD		(1 << 3)
+#define SSI_SACNT_TIF		(1 << 2)
+#define SSI_SACNT_FV		(1 << 1)
+#define SSI_SACNT_AC97EN	(1 << 0)
+
+#define SSI_SACADD	0x3c
+#define SSI_SACDAT	0x40
+#define SSI_SATAG	0x44
+#define SSI_STMSK	0x48
+#define SSI_SRMSK	0x4c
+#define SSI_SACCST	0x50
+#define SSI_SACCEN	0x54
+#define SSI_SACCDIS	0x58
+
+/* SSI clock sources */
+#define IMX_SSP_SYS_CLK		0
+
+/* SSI audio dividers */
+#define IMX_SSI_TX_DIV_2	0
+#define IMX_SSI_TX_DIV_PSR	1
+#define IMX_SSI_TX_DIV_PM	2
+#define IMX_SSI_RX_DIV_2	3
+#define IMX_SSI_RX_DIV_PSR	4
+#define IMX_SSI_RX_DIV_PM	5
+
+extern struct snd_soc_dai imx_ssi_pcm_dai[2];
+extern struct snd_soc_platform imx_soc_platform;
+
+#define DRV_NAME "imx-ssi"
+
+struct imx_pcm_dma_params {
+	int dma;
+	unsigned long dma_addr;
+	int burstsize;
+};
+
+struct imx_ssi {
+	struct platform_device *ac97_dev;
+
+	struct snd_soc_device imx_ac97;
+	struct clk *clk;
+	void __iomem *base;
+	int irq;
+	int fiq_enable;
+	unsigned int offset;
+
+	unsigned int flags;
+
+	void (*ac97_reset) (struct snd_ac97 *ac97);
+	void (*ac97_warm_reset)(struct snd_ac97 *ac97);
+
+	struct imx_pcm_dma_params	dma_params_rx;
+	struct imx_pcm_dma_params	dma_params_tx;
+
+	int enabled;
+};
+
+struct snd_soc_platform *imx_ssi_fiq_init(struct platform_device *pdev,
+		struct imx_ssi *ssi);
+void imx_ssi_fiq_exit(struct platform_device *pdev, struct imx_ssi *ssi);
+struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev,
+		struct imx_ssi *ssi);
+
+int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma);
+int imx_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+	struct snd_pcm *pcm);
+void imx_pcm_free(struct snd_pcm *pcm);
+
+/*
+ * Do not change this as the FIQ handler depends on this size
+ */
+#define IMX_SSI_DMABUF_SIZE	(64 * 1024)
+
+#define DMA_RXFIFO_BURST      0x4
+#define DMA_TXFIFO_BURST      0x6
+
+#endif /* _IMX_SSI_H */
diff --git a/sound/soc/imx/mx1_mx2-pcm.c b/sound/soc/imx/mx1_mx2-pcm.c
deleted file mode 100644
index bffffcd..0000000
--- a/sound/soc/imx/mx1_mx2-pcm.c
+++ /dev/null
@@ -1,488 +0,0 @@
-/*
- * mx1_mx2-pcm.c -- ALSA SoC interface for Freescale i.MX1x, i.MX2x CPUs
- *
- * Copyright 2009 Vista Silicon S.L.
- * Author: Javier Martin
- *         javier.martin@vista-silicon.com
- *
- * Based on mxc-pcm.c by Liam Girdwood.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- *
- */
-
-#include <linux/module.h>
-#include <linux/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/dma.h>
-#include <mach/hardware.h>
-#include <mach/dma-mx1-mx2.h>
-
-#include "mx1_mx2-pcm.h"
-
-
-static const struct snd_pcm_hardware mx1_mx2_pcm_hardware = {
-	.info			= (SNDRV_PCM_INFO_INTERLEAVED |
-				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				   SNDRV_PCM_INFO_MMAP |
-				   SNDRV_PCM_INFO_MMAP_VALID),
-	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
-	.buffer_bytes_max	= 32 * 1024,
-	.period_bytes_min	= 64,
-	.period_bytes_max	= 8 * 1024,
-	.periods_min		= 2,
-	.periods_max		= 255,
-	.fifo_size		= 0,
-};
-
-struct mx1_mx2_runtime_data {
-	int dma_ch;
-	int active;
-	unsigned int period;
-	unsigned int periods;
-	int tx_spin;
-	spinlock_t dma_lock;
-	struct mx1_mx2_pcm_dma_params *dma_params;
-};
-
-
-/**
-  * This function stops the current dma transfer for playback
-  * and clears the dma pointers.
-  *
-  * @param	substream	pointer to the structure of the current stream.
-  *
-  */
-static int audio_stop_dma(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mx1_mx2_runtime_data *prtd = runtime->private_data;
-	unsigned long flags;
-
-	spin_lock_irqsave(&prtd->dma_lock, flags);
-
-	pr_debug("%s\n", __func__);
-
-	prtd->active = 0;
-	prtd->period = 0;
-	prtd->periods = 0;
-
-	/* this stops the dma channel and clears the buffer ptrs */
-
-	imx_dma_disable(prtd->dma_ch);
-
-	spin_unlock_irqrestore(&prtd->dma_lock, flags);
-
-	return 0;
-}
-
-/**
-  * This function is called whenever a new audio block needs to be
-  * transferred to the codec. The function receives the address and the size
-  * of the new block and start a new DMA transfer.
-  *
-  * @param	substream	pointer to the structure of the current stream.
-  *
-  */
-static int dma_new_period(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime =  substream->runtime;
-	struct mx1_mx2_runtime_data *prtd = runtime->private_data;
-	unsigned int dma_size;
-	unsigned int offset;
-	int ret = 0;
-	dma_addr_t mem_addr;
-	unsigned int dev_addr;
-
-	if (prtd->active) {
-		dma_size = frames_to_bytes(runtime, runtime->period_size);
-		offset = dma_size * prtd->period;
-
-		pr_debug("%s: period (%d) out of (%d)\n", __func__,
-			prtd->period,
-			runtime->periods);
-		pr_debug("period_size %d frames\n offset %d bytes\n",
-			(unsigned int)runtime->period_size,
-			offset);
-		pr_debug("dma_size %d bytes\n", dma_size);
-
-		snd_BUG_ON(dma_size > mx1_mx2_pcm_hardware.period_bytes_max);
-
-		mem_addr = (dma_addr_t)(runtime->dma_addr + offset);
-		dev_addr = prtd->dma_params->per_address;
-		pr_debug("%s: mem_addr is %x\n dev_addr is %x\n",
-				 __func__, mem_addr, dev_addr);
-
-		ret = imx_dma_setup_single(prtd->dma_ch, mem_addr,
-					dma_size, dev_addr,
-					prtd->dma_params->transfer_type);
-		if (ret < 0) {
-			printk(KERN_ERR "Error %d configuring DMA\n", ret);
-			return ret;
-		}
-		imx_dma_enable(prtd->dma_ch);
-
-		pr_debug("%s: transfer enabled\nmem_addr = %x\n",
-			__func__, (unsigned int) mem_addr);
-		pr_debug("dev_addr = %x\ndma_size = %d\n",
-			(unsigned int) dev_addr, dma_size);
-
-		prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
-		prtd->period++;
-		prtd->period %= runtime->periods;
-    }
-	return ret;
-}
-
-
-/**
-  * This is a callback which will be called
-  * when a TX transfer finishes. The call occurs
-  * in interrupt context.
-  *
-  * @param	dat	pointer to the structure of the current stream.
-  *
-  */
-static void audio_dma_irq(int channel, void *data)
-{
-	struct snd_pcm_substream *substream;
-	struct snd_pcm_runtime *runtime;
-	struct mx1_mx2_runtime_data *prtd;
-	unsigned int dma_size;
-	unsigned int previous_period;
-	unsigned int offset;
-
-	substream = data;
-	runtime = substream->runtime;
-	prtd = runtime->private_data;
-	previous_period  = prtd->periods;
-	dma_size = frames_to_bytes(runtime, runtime->period_size);
-	offset = dma_size * previous_period;
-
-	prtd->tx_spin = 0;
-	prtd->periods++;
-	prtd->periods %= runtime->periods;
-
-	pr_debug("%s: irq per %d offset %x\n", __func__, prtd->periods, offset);
-
-	/*
-	  * If we are getting a callback for an active stream then we inform
-	  * the PCM middle layer we've finished a period
-	  */
-	if (prtd->active)
-		snd_pcm_period_elapsed(substream);
-
-	/*
-	  * Trig next DMA transfer
-	  */
-	dma_new_period(substream);
-}
-
-/**
-  * This function configures the hardware to allow audio
-  * playback operations. It is called by ALSA framework.
-  *
-  * @param	substream	pointer to the structure of the current stream.
-  *
-  * @return              0 on success, -1 otherwise.
-  */
-static int
-snd_mx1_mx2_prepare(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime =  substream->runtime;
-	struct mx1_mx2_runtime_data *prtd = runtime->private_data;
-
-	prtd->period = 0;
-	prtd->periods = 0;
-
-	return 0;
-}
-
-static int mx1_mx2_pcm_hw_params(struct snd_pcm_substream *substream,
-	struct snd_pcm_hw_params *hw_params)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	int ret;
-
-	ret = snd_pcm_lib_malloc_pages(substream,
-					params_buffer_bytes(hw_params));
-	if (ret < 0) {
-		printk(KERN_ERR "%s: Error %d failed to malloc pcm pages \n",
-		__func__, ret);
-		return ret;
-	}
-
-	pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_addr 0x(%x)\n",
-		__func__, (unsigned int)runtime->dma_addr);
-	pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_area 0x(%x)\n",
-		__func__, (unsigned int)runtime->dma_area);
-	pr_debug("%s: snd_imx1_mx2_audio_hw_params runtime->dma_bytes 0x(%x)\n",
-		__func__, (unsigned int)runtime->dma_bytes);
-
-	return ret;
-}
-
-static int mx1_mx2_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mx1_mx2_runtime_data *prtd = runtime->private_data;
-
-	imx_dma_free(prtd->dma_ch);
-
-	snd_pcm_lib_free_pages(substream);
-
-	return 0;
-}
-
-static int mx1_mx2_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	struct mx1_mx2_runtime_data *prtd = substream->runtime->private_data;
-	int ret = 0;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		prtd->tx_spin = 0;
-		/* requested stream startup */
-		prtd->active = 1;
-		pr_debug("%s: starting dma_new_period\n", __func__);
-		ret = dma_new_period(substream);
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-		/* requested stream shutdown */
-		pr_debug("%s: stopping dma transfer\n", __func__);
-		ret = audio_stop_dma(substream);
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-
-	return ret;
-}
-
-static snd_pcm_uframes_t
-mx1_mx2_pcm_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mx1_mx2_runtime_data *prtd = runtime->private_data;
-	unsigned int offset = 0;
-
-	/* tx_spin value is used here to check if a transfer is active */
-	if (prtd->tx_spin) {
-		offset = (runtime->period_size * (prtd->periods)) +
-						(runtime->period_size >> 1);
-		if (offset >= runtime->buffer_size)
-			offset = runtime->period_size >> 1;
-	} else {
-		offset = (runtime->period_size * (prtd->periods));
-		if (offset >= runtime->buffer_size)
-			offset = 0;
-	}
-	pr_debug("%s: pointer offset %x\n", __func__, offset);
-
-	return offset;
-}
-
-static int mx1_mx2_pcm_open(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mx1_mx2_runtime_data *prtd;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct mx1_mx2_pcm_dma_params *dma_data = rtd->dai->cpu_dai->dma_data;
-	int ret;
-
-	snd_soc_set_runtime_hwparams(substream, &mx1_mx2_pcm_hardware);
-
-	ret = snd_pcm_hw_constraint_integer(runtime,
-					SNDRV_PCM_HW_PARAM_PERIODS);
-	if (ret < 0)
-		return ret;
-
-	prtd = kzalloc(sizeof(struct mx1_mx2_runtime_data), GFP_KERNEL);
-	if (prtd == NULL) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	runtime->private_data = prtd;
-
-	if (!dma_data)
-		return -ENODEV;
-
-	prtd->dma_params = dma_data;
-
-	pr_debug("%s: Requesting dma channel (%s)\n", __func__,
-						prtd->dma_params->name);
-	ret = imx_dma_request_by_prio(prtd->dma_params->name, DMA_PRIO_HIGH);
-	if (ret < 0) {
-		printk(KERN_ERR "Error %d requesting dma channel\n", ret);
-		return ret;
-	}
-	prtd->dma_ch = ret;
-	imx_dma_config_burstlen(prtd->dma_ch,
-				prtd->dma_params->watermark_level);
-
-	ret = imx_dma_config_channel(prtd->dma_ch,
-			prtd->dma_params->per_config,
-			prtd->dma_params->mem_config,
-			prtd->dma_params->event_id, 0);
-
-	if (ret) {
-		pr_debug(KERN_ERR "Error %d configuring dma channel %d\n",
-			ret, prtd->dma_ch);
-		return ret;
-	}
-
-	pr_debug("%s: Setting tx dma callback function\n", __func__);
-	ret = imx_dma_setup_handlers(prtd->dma_ch,
-				audio_dma_irq, NULL,
-				(void *)substream);
-	if (ret < 0) {
-		printk(KERN_ERR "Error %d setting dma callback function\n", ret);
-		return ret;
-	}
-	return 0;
-
- out:
-	return ret;
-}
-
-static int mx1_mx2_pcm_close(struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct mx1_mx2_runtime_data *prtd = runtime->private_data;
-
-	kfree(prtd);
-
-	return 0;
-}
-
-static int mx1_mx2_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 mx1_mx2_pcm_ops = {
-	.open		= mx1_mx2_pcm_open,
-	.close		= mx1_mx2_pcm_close,
-	.ioctl		= snd_pcm_lib_ioctl,
-	.hw_params	= mx1_mx2_pcm_hw_params,
-	.hw_free	= mx1_mx2_pcm_hw_free,
-	.prepare	= snd_mx1_mx2_prepare,
-	.trigger	= mx1_mx2_pcm_trigger,
-	.pointer	= mx1_mx2_pcm_pointer,
-	.mmap		= mx1_mx2_pcm_mmap,
-};
-
-static u64 mx1_mx2_pcm_dmamask = 0xffffffff;
-
-static int mx1_mx2_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 = mx1_mx2_pcm_hardware.buffer_bytes_max;
-	buf->dev.type = SNDRV_DMA_TYPE_DEV;
-	buf->dev.dev = pcm->card->dev;
-	buf->private_data = NULL;
-
-	/* Reserve uncached-buffered memory area for DMA */
-	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
-					   &buf->addr, GFP_KERNEL);
-
-	pr_debug("%s: preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
-		__func__, (void *) buf->area, (void *) buf->addr, size);
-
-	if (!buf->area)
-		return -ENOMEM;
-
-	buf->bytes = size;
-	return 0;
-}
-
-static void mx1_mx2_pcm_free_dma_buffers(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 int mx1_mx2_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-	struct snd_pcm *pcm)
-{
-	int ret = 0;
-
-	if (!card->dev->dma_mask)
-		card->dev->dma_mask = &mx1_mx2_pcm_dmamask;
-	if (!card->dev->coherent_dma_mask)
-		card->dev->coherent_dma_mask = 0xffffffff;
-
-	if (dai->playback.channels_min) {
-		ret = mx1_mx2_pcm_preallocate_dma_buffer(pcm,
-			SNDRV_PCM_STREAM_PLAYBACK);
-		pr_debug("%s: preallocate playback buffer\n", __func__);
-		if (ret)
-			goto out;
-	}
-
-	if (dai->capture.channels_min) {
-		ret = mx1_mx2_pcm_preallocate_dma_buffer(pcm,
-			SNDRV_PCM_STREAM_CAPTURE);
-		pr_debug("%s: preallocate capture buffer\n", __func__);
-		if (ret)
-			goto out;
-	}
- out:
-	return ret;
-}
-
-struct snd_soc_platform mx1_mx2_soc_platform = {
-	.name		= "mx1_mx2-audio",
-	.pcm_ops 	= &mx1_mx2_pcm_ops,
-	.pcm_new	= mx1_mx2_pcm_new,
-	.pcm_free	= mx1_mx2_pcm_free_dma_buffers,
-};
-EXPORT_SYMBOL_GPL(mx1_mx2_soc_platform);
-
-static int __init mx1_mx2_soc_platform_init(void)
-{
-	return snd_soc_register_platform(&mx1_mx2_soc_platform);
-}
-module_init(mx1_mx2_soc_platform_init);
-
-static void __exit mx1_mx2_soc_platform_exit(void)
-{
-	snd_soc_unregister_platform(&mx1_mx2_soc_platform);
-}
-module_exit(mx1_mx2_soc_platform_exit);
-
-MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com");
-MODULE_DESCRIPTION("Freescale i.MX2x, i.MX1x PCM DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/mx1_mx2-pcm.h b/sound/soc/imx/mx1_mx2-pcm.h
deleted file mode 100644
index 2e52810..0000000
--- a/sound/soc/imx/mx1_mx2-pcm.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * mx1_mx2-pcm.h :- ASoC platform header for Freescale i.MX1x, i.MX2x
- *
- * 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 _MX1_MX2_PCM_H
-#define _MX1_MX2_PCM_H
-
-/* DMA information for mx1_mx2 platforms */
-struct mx1_mx2_pcm_dma_params {
-	char *name;			/* stream identifier */
-	unsigned int transfer_type;	/* READ or WRITE DMA transfer */
-	dma_addr_t per_address;		/* physical address of SSI fifo */
-	int event_id;			/* fixed DMA number for SSI fifo */
-	int watermark_level;		/* SSI fifo watermark level */
-	int per_config;			/* DMA Config flags for peripheral */
-	int mem_config;			/* DMA Config flags for RAM */
- };
-
-/* platform data */
-extern struct snd_soc_platform mx1_mx2_soc_platform;
-
-#endif
diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c
deleted file mode 100644
index 07d2a24..0000000
--- a/sound/soc/imx/mx27vis_wm8974.c
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * mx27vis_wm8974.c  --  SoC audio for mx27vis
- *
- * Copyright 2009 Vista Silicon S.L.
- * Author: Javier Martin
- *         javier.martin@vista-silicon.com
- *
- *  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.
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/i2c.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-
-
-#include "../codecs/wm8974.h"
-#include "mx1_mx2-pcm.h"
-#include "mxc-ssi.h"
-#include <mach/gpio.h>
-#include <mach/iomux.h>
-
-#define IGNORED_ARG 0
-
-
-static struct snd_soc_card mx27vis;
-
-/**
-  * This function connects SSI1 (HPCR1) as slave to
-  * SSI1 external signals (PPCR1)
-  * As slave, HPCR1 must set TFSDIR and TCLKDIR as inputs from
-  * port 4
-  */
-void audmux_connect_1_4(void)
-{
-	pr_debug("AUDMUX: normal operation mode\n");
-	/* Reset HPCR1 and PPCR1 */
-
-	DAM_HPCR1 = 0x00000000;
-	DAM_PPCR1 = 0x00000000;
-
-	/* set to synchronous */
-	DAM_HPCR1 |= AUDMUX_HPCR_SYN;
-	DAM_PPCR1 |= AUDMUX_PPCR_SYN;
-
-
-	/* set Rx sources 1 <--> 4 */
-	DAM_HPCR1 |= AUDMUX_HPCR_RXDSEL(3); /* port 4 */
-	DAM_PPCR1 |= AUDMUX_PPCR_RXDSEL(0); /* port 1 */
-
-	/* set Tx frame and Clock direction and source  4 --> 1 output */
-	DAM_HPCR1 |= AUDMUX_HPCR_TFSDIR | AUDMUX_HPCR_TCLKDIR;
-	DAM_HPCR1 |= AUDMUX_HPCR_TFCSEL(3); /* TxDS and TxCclk from port 4 */
-
-	return;
-}
-
-static int mx27vis_hifi_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->dai->codec_dai;
-	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-	unsigned int pll_out = 0, bclk = 0, fmt = 0, mclk = 0;
-	int ret = 0;
-
-	/*
-	 * The WM8974 is better at generating accurate audio clocks than the
-	 * MX27 SSI controller, so we will use it as master when we can.
-	 */
-	switch (params_rate(params)) {
-	case 8000:
-		fmt = SND_SOC_DAIFMT_CBM_CFM;
-		mclk = WM8974_MCLKDIV_12;
-		pll_out = 24576000;
-		break;
-	case 16000:
-		fmt = SND_SOC_DAIFMT_CBM_CFM;
-		pll_out = 12288000;
-		break;
-	case 48000:
-		fmt = SND_SOC_DAIFMT_CBM_CFM;
-		bclk = WM8974_BCLKDIV_4;
-		pll_out = 12288000;
-		break;
-	case 96000:
-		fmt = SND_SOC_DAIFMT_CBM_CFM;
-		bclk = WM8974_BCLKDIV_2;
-		pll_out = 12288000;
-		break;
-	case 11025:
-		fmt = SND_SOC_DAIFMT_CBM_CFM;
-		bclk = WM8974_BCLKDIV_16;
-		pll_out = 11289600;
-		break;
-	case 22050:
-		fmt = SND_SOC_DAIFMT_CBM_CFM;
-		bclk = WM8974_BCLKDIV_8;
-		pll_out = 11289600;
-		break;
-	case 44100:
-		fmt = SND_SOC_DAIFMT_CBM_CFM;
-		bclk = WM8974_BCLKDIV_4;
-		mclk = WM8974_MCLKDIV_2;
-		pll_out = 11289600;
-		break;
-	case 88200:
-		fmt = SND_SOC_DAIFMT_CBM_CFM;
-		bclk = WM8974_BCLKDIV_2;
-		pll_out = 11289600;
-		break;
-	}
-
-	/* set codec DAI configuration */
-	ret = codec_dai->ops->set_fmt(codec_dai,
-		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
-		SND_SOC_DAIFMT_SYNC | fmt);
-	if (ret < 0) {
-		printk(KERN_ERR "Error from codec DAI configuration\n");
-		return ret;
-	}
-
-	/* set cpu DAI configuration */
-	ret = cpu_dai->ops->set_fmt(cpu_dai,
-		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
-		SND_SOC_DAIFMT_SYNC | fmt);
-	if (ret < 0) {
-		printk(KERN_ERR "Error from cpu DAI configuration\n");
-		return ret;
-	}
-
-	/* Put DC field of STCCR to 1 (not zero) */
-	ret = cpu_dai->ops->set_tdm_slot(cpu_dai, 0, 2);
-
-	/* set the SSI system clock as input */
-	ret = cpu_dai->ops->set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
-		SND_SOC_CLOCK_IN);
-	if (ret < 0) {
-		printk(KERN_ERR "Error when setting system SSI clk\n");
-		return ret;
-	}
-
-	/* set codec BCLK division for sample rate */
-	ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_BCLKDIV, bclk);
-	if (ret < 0) {
-		printk(KERN_ERR "Error when setting BCLK division\n");
-		return ret;
-	}
-
-
-	/* codec PLL input is 25 MHz */
-	ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG,
-					25000000, pll_out);
-	if (ret < 0) {
-		printk(KERN_ERR "Error when setting PLL input\n");
-		return ret;
-	}
-
-	/*set codec MCLK division for sample rate */
-	ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_MCLKDIV, mclk);
-	if (ret < 0) {
-		printk(KERN_ERR "Error when setting MCLK division\n");
-		return ret;
-	}
-
-	return 0;
-}
-
-static int mx27vis_hifi_hw_free(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
-
-	/* disable the PLL */
-	return codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG,
-				       0, 0);
-}
-
-/*
- * mx27vis WM8974 HiFi DAI opserations.
- */
-static struct snd_soc_ops mx27vis_hifi_ops = {
-	.hw_params = mx27vis_hifi_hw_params,
-	.hw_free = mx27vis_hifi_hw_free,
-};
-
-
-static int mx27vis_suspend(struct platform_device *pdev, pm_message_t state)
-{
-	return 0;
-}
-
-static int mx27vis_resume(struct platform_device *pdev)
-{
-	return 0;
-}
-
-static int mx27vis_probe(struct platform_device *pdev)
-{
-	int ret = 0;
-
-	ret = get_ssi_clk(0, &pdev->dev);
-
-	if (ret < 0) {
-		printk(KERN_ERR "%s: cant get ssi clock\n", __func__);
-		return ret;
-	}
-
-
-	return 0;
-}
-
-static int mx27vis_remove(struct platform_device *pdev)
-{
-	put_ssi_clk(0);
-	return 0;
-}
-
-static struct snd_soc_dai_link mx27vis_dai[] = {
-{ /* Hifi Playback*/
-	.name = "WM8974",
-	.stream_name = "WM8974 HiFi",
-	.cpu_dai = &imx_ssi_pcm_dai[0],
-	.codec_dai = &wm8974_dai,
-	.ops = &mx27vis_hifi_ops,
-},
-};
-
-static struct snd_soc_card mx27vis = {
-	.name = "mx27vis",
-	.platform = &mx1_mx2_soc_platform,
-	.probe = mx27vis_probe,
-	.remove = mx27vis_remove,
-	.suspend_pre = mx27vis_suspend,
-	.resume_post = mx27vis_resume,
-	.dai_link = mx27vis_dai,
-	.num_links = ARRAY_SIZE(mx27vis_dai),
-};
-
-static struct snd_soc_device mx27vis_snd_devdata = {
-	.card = &mx27vis,
-	.codec_dev = &soc_codec_dev_wm8974,
-};
-
-static struct platform_device *mx27vis_snd_device;
-
-/* Temporal definition of board specific behaviour */
-void gpio_ssi_active(int ssi_num)
-{
-	int ret = 0;
-
-	unsigned int ssi1_pins[] = {
-		PC20_PF_SSI1_FS,
-		PC21_PF_SSI1_RXD,
-		PC22_PF_SSI1_TXD,
-		PC23_PF_SSI1_CLK,
-	};
-	unsigned int ssi2_pins[] = {
-		PC24_PF_SSI2_FS,
-		PC25_PF_SSI2_RXD,
-		PC26_PF_SSI2_TXD,
-		PC27_PF_SSI2_CLK,
-	};
-	if (ssi_num == 0)
-		ret = mxc_gpio_setup_multiple_pins(ssi1_pins,
-				ARRAY_SIZE(ssi1_pins), "USB OTG");
-	else
-		ret = mxc_gpio_setup_multiple_pins(ssi2_pins,
-				ARRAY_SIZE(ssi2_pins), "USB OTG");
-	if (ret)
-		printk(KERN_ERR "Error requesting ssi %x pins\n", ssi_num);
-}
-
-
-static int __init mx27vis_init(void)
-{
-	int ret;
-
-	mx27vis_snd_device = platform_device_alloc("soc-audio", -1);
-	if (!mx27vis_snd_device)
-		return -ENOMEM;
-
-	platform_set_drvdata(mx27vis_snd_device, &mx27vis_snd_devdata);
-	mx27vis_snd_devdata.dev = &mx27vis_snd_device->dev;
-	ret = platform_device_add(mx27vis_snd_device);
-
-	if (ret) {
-		printk(KERN_ERR "ASoC: Platform device allocation failed\n");
-		platform_device_put(mx27vis_snd_device);
-	}
-
-	/* WM8974 uses SSI1 (HPCR1) via AUDMUX port 4 for audio (PPCR1) */
-	gpio_ssi_active(0);
-	audmux_connect_1_4();
-
-	return ret;
-}
-
-static void __exit mx27vis_exit(void)
-{
-	/* We should call some "ssi_gpio_inactive()" properly */
-}
-
-module_init(mx27vis_init);
-module_exit(mx27vis_exit);
-
-
-MODULE_AUTHOR("Javier Martin, javier.martin@vista-silicon.com");
-MODULE_DESCRIPTION("ALSA SoC WM8974 mx27vis");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/mxc-ssi.c b/sound/soc/imx/mxc-ssi.c
deleted file mode 100644
index ccdefe6..0000000
--- a/sound/soc/imx/mxc-ssi.c
+++ /dev/null
@@ -1,860 +0,0 @@
-/*
- * mxc-ssi.c  --  SSI driver for Freescale IMX
- *
- * Copyright 2006 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- *  Based on mxc-alsa-mc13783 (C) 2006 Freescale.
- *
- *  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.
- *
- * TODO:
- *   Need to rework SSI register defs when new defs go into mainline.
- *   Add support for TDM and FIFO 1.
- *   Add support for i.mx3x DMA interface.
- *
- */
-
-
-#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 <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <mach/dma-mx1-mx2.h>
-#include <asm/mach-types.h>
-
-#include "mxc-ssi.h"
-#include "mx1_mx2-pcm.h"
-
-#define SSI1_PORT	0
-#define SSI2_PORT	1
-
-static int ssi_active[2] = {0, 0};
-
-/* DMA information for mx1_mx2 platforms */
-static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out0 = {
-	.name			= "SSI1 PCM Stereo out 0",
-	.transfer_type = DMA_MODE_WRITE,
-	.per_address = SSI1_BASE_ADDR + STX0,
-	.event_id = DMA_REQ_SSI1_TX0,
-	.watermark_level = TXFIFO_WATERMARK,
-	.per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
-	.mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out1 = {
-	.name			= "SSI1 PCM Stereo out 1",
-	.transfer_type = DMA_MODE_WRITE,
-	.per_address = SSI1_BASE_ADDR + STX1,
-	.event_id = DMA_REQ_SSI1_TX1,
-	.watermark_level = TXFIFO_WATERMARK,
-	.per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
-	.mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in0 = {
-	.name			= "SSI1 PCM Stereo in 0",
-	.transfer_type = DMA_MODE_READ,
-	.per_address = SSI1_BASE_ADDR + SRX0,
-	.event_id = DMA_REQ_SSI1_RX0,
-	.watermark_level = RXFIFO_WATERMARK,
-	.per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
-	.mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in1 = {
-	.name			= "SSI1 PCM Stereo in 1",
-	.transfer_type = DMA_MODE_READ,
-	.per_address = SSI1_BASE_ADDR + SRX1,
-	.event_id = DMA_REQ_SSI1_RX1,
-	.watermark_level = RXFIFO_WATERMARK,
-	.per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
-	.mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out0 = {
-	.name			= "SSI2 PCM Stereo out 0",
-	.transfer_type = DMA_MODE_WRITE,
-	.per_address = SSI2_BASE_ADDR + STX0,
-	.event_id = DMA_REQ_SSI2_TX0,
-	.watermark_level = TXFIFO_WATERMARK,
-	.per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
-	.mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out1 = {
-	.name			= "SSI2 PCM Stereo out 1",
-	.transfer_type = DMA_MODE_WRITE,
-	.per_address = SSI2_BASE_ADDR + STX1,
-	.event_id = DMA_REQ_SSI2_TX1,
-	.watermark_level = TXFIFO_WATERMARK,
-	.per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
-	.mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in0 = {
-	.name			= "SSI2 PCM Stereo in 0",
-	.transfer_type = DMA_MODE_READ,
-	.per_address = SSI2_BASE_ADDR + SRX0,
-	.event_id = DMA_REQ_SSI2_RX0,
-	.watermark_level = RXFIFO_WATERMARK,
-	.per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
-	.mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in1 = {
-	.name			= "SSI2 PCM Stereo in 1",
-	.transfer_type = DMA_MODE_READ,
-	.per_address = SSI2_BASE_ADDR + SRX1,
-	.event_id = DMA_REQ_SSI2_RX1,
-	.watermark_level = RXFIFO_WATERMARK,
-	.per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
-	.mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
-};
-
-static struct clk *ssi_clk0, *ssi_clk1;
-
-int get_ssi_clk(int ssi, struct device *dev)
-{
-	switch (ssi) {
-	case 0:
-		ssi_clk0 = clk_get(dev, "ssi1");
-		if (IS_ERR(ssi_clk0))
-			return PTR_ERR(ssi_clk0);
-		return 0;
-	case 1:
-		ssi_clk1 = clk_get(dev, "ssi2");
-		if (IS_ERR(ssi_clk1))
-			return PTR_ERR(ssi_clk1);
-		return 0;
-	default:
-		return -EINVAL;
-	}
-}
-EXPORT_SYMBOL(get_ssi_clk);
-
-void put_ssi_clk(int ssi)
-{
-	switch (ssi) {
-	case 0:
-		clk_put(ssi_clk0);
-		ssi_clk0 = NULL;
-		break;
-	case 1:
-		clk_put(ssi_clk1);
-		ssi_clk1 = NULL;
-		break;
-	}
-}
-EXPORT_SYMBOL(put_ssi_clk);
-
-/*
- * SSI system clock configuration.
- * Should only be called when port is inactive (i.e. SSIEN = 0).
- */
-static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
-	int clk_id, unsigned int freq, int dir)
-{
-	u32 scr;
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		scr = SSI1_SCR;
-		pr_debug("%s: SCR for SSI1 is %x\n", __func__, scr);
-	} else {
-		scr = SSI2_SCR;
-		pr_debug("%s: SCR for SSI2 is %x\n", __func__, scr);
-	}
-
-	if (scr & SSI_SCR_SSIEN) {
-		printk(KERN_WARNING "Warning ssi already enabled\n");
-		return 0;
-	}
-
-	switch (clk_id) {
-	case IMX_SSP_SYS_CLK:
-		if (dir == SND_SOC_CLOCK_OUT) {
-			scr |= SSI_SCR_SYS_CLK_EN;
-			pr_debug("%s: clk of is output\n", __func__);
-		} else {
-			scr &= ~SSI_SCR_SYS_CLK_EN;
-			pr_debug("%s: clk of is input\n", __func__);
-		}
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		pr_debug("%s: writeback of SSI1_SCR\n", __func__);
-		SSI1_SCR = scr;
-	} else {
-		pr_debug("%s: writeback of SSI2_SCR\n", __func__);
-		SSI2_SCR = scr;
-	}
-
-	return 0;
-}
-
-/*
- * SSI Clock dividers
- * Should only be called when port is inactive (i.e. SSIEN = 0).
- */
-static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
-	int div_id, int div)
-{
-	u32 stccr, srccr;
-
-	pr_debug("%s\n", __func__);
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		if (SSI1_SCR & SSI_SCR_SSIEN)
-			return 0;
-		srccr = SSI1_STCCR;
-		stccr = SSI1_STCCR;
-	} else {
-		if (SSI2_SCR & SSI_SCR_SSIEN)
-			return 0;
-		srccr = SSI2_STCCR;
-		stccr = SSI2_STCCR;
-	}
-
-	switch (div_id) {
-	case IMX_SSI_TX_DIV_2:
-		stccr &= ~SSI_STCCR_DIV2;
-		stccr |= div;
-		break;
-	case IMX_SSI_TX_DIV_PSR:
-		stccr &= ~SSI_STCCR_PSR;
-		stccr |= div;
-		break;
-	case IMX_SSI_TX_DIV_PM:
-		stccr &= ~0xff;
-		stccr |= SSI_STCCR_PM(div);
-		break;
-	case IMX_SSI_RX_DIV_2:
-		stccr &= ~SSI_STCCR_DIV2;
-		stccr |= div;
-		break;
-	case IMX_SSI_RX_DIV_PSR:
-		stccr &= ~SSI_STCCR_PSR;
-		stccr |= div;
-		break;
-	case IMX_SSI_RX_DIV_PM:
-		stccr &= ~0xff;
-		stccr |= SSI_STCCR_PM(div);
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		SSI1_STCCR = stccr;
-		SSI1_SRCCR = srccr;
-	} else {
-		SSI2_STCCR = stccr;
-		SSI2_SRCCR = srccr;
-	}
-	return 0;
-}
-
-/*
- * SSI Network Mode or TDM slots configuration.
- * Should only be called when port is inactive (i.e. SSIEN = 0).
- */
-static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
-	unsigned int mask, int slots)
-{
-	u32 stmsk, srmsk, stccr;
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		if (SSI1_SCR & SSI_SCR_SSIEN) {
-			printk(KERN_WARNING "Warning ssi already enabled\n");
-			return 0;
-		}
-		stccr = SSI1_STCCR;
-	} else {
-		if (SSI2_SCR & SSI_SCR_SSIEN) {
-			printk(KERN_WARNING "Warning ssi already enabled\n");
-			return 0;
-		}
-		stccr = SSI2_STCCR;
-	}
-
-	stmsk = srmsk = mask;
-	stccr &= ~SSI_STCCR_DC_MASK;
-	stccr |= SSI_STCCR_DC(slots - 1);
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		SSI1_STMSK = stmsk;
-		SSI1_SRMSK = srmsk;
-		SSI1_SRCCR = SSI1_STCCR = stccr;
-	} else {
-		SSI2_STMSK = stmsk;
-		SSI2_SRMSK = srmsk;
-		SSI2_SRCCR = SSI2_STCCR = stccr;
-	}
-
-	return 0;
-}
-
-/*
- * SSI DAI format configuration.
- * Should only be called when port is inactive (i.e. SSIEN = 0).
- * Note: We don't use the I2S modes but instead manually configure the
- * SSI for I2S.
- */
-static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai,
-		unsigned int fmt)
-{
-	u32 stcr = 0, srcr = 0, scr;
-
-	/*
-	 * This is done to avoid this function to modify
-	 * previous set values in stcr
-	 */
-	stcr = SSI1_STCR;
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
-		scr = SSI1_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
-	else
-		scr = SSI2_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
-
-	if (scr & SSI_SCR_SSIEN) {
-		printk(KERN_WARNING "Warning ssi already enabled\n");
-		return 0;
-	}
-
-	/* DAI mode */
-	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-	case SND_SOC_DAIFMT_I2S:
-		/* data on rising edge of bclk, frame low 1clk before data */
-		stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
-		srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0;
-		break;
-	case SND_SOC_DAIFMT_LEFT_J:
-		/* data on rising edge of bclk, frame high with data */
-		stcr |= SSI_STCR_TXBIT0;
-		srcr |= SSI_SRCR_RXBIT0;
-		break;
-	case SND_SOC_DAIFMT_DSP_B:
-		/* data on rising edge of bclk, frame high with data */
-		stcr |= SSI_STCR_TFSL;
-		srcr |= SSI_SRCR_RFSL;
-		break;
-	case SND_SOC_DAIFMT_DSP_A:
-		/* data on rising edge of bclk, frame high 1clk before data */
-		stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS;
-		srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS;
-		break;
-	}
-
-	/* DAI clock inversion */
-	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-	case SND_SOC_DAIFMT_IB_IF:
-		stcr |= SSI_STCR_TFSI;
-		stcr &= ~SSI_STCR_TSCKP;
-		srcr |= SSI_SRCR_RFSI;
-		srcr &= ~SSI_SRCR_RSCKP;
-		break;
-	case SND_SOC_DAIFMT_IB_NF:
-		stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
-		srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI);
-		break;
-	case SND_SOC_DAIFMT_NB_IF:
-		stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
-		srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP;
-		break;
-	case SND_SOC_DAIFMT_NB_NF:
-		stcr &= ~SSI_STCR_TFSI;
-		stcr |= SSI_STCR_TSCKP;
-		srcr &= ~SSI_SRCR_RFSI;
-		srcr |= SSI_SRCR_RSCKP;
-		break;
-	}
-
-	/* DAI clock master masks */
-	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
-	case SND_SOC_DAIFMT_CBS_CFS:
-		stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
-		srcr |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR;
-		break;
-	case SND_SOC_DAIFMT_CBM_CFS:
-		stcr |= SSI_STCR_TFDIR;
-		srcr |= SSI_SRCR_RFDIR;
-		break;
-	case SND_SOC_DAIFMT_CBS_CFM:
-		stcr |= SSI_STCR_TXDIR;
-		srcr |= SSI_SRCR_RXDIR;
-		break;
-	}
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		SSI1_STCR = stcr;
-		SSI1_SRCR = srcr;
-		SSI1_SCR = scr;
-	} else {
-		SSI2_STCR = stcr;
-		SSI2_SRCR = srcr;
-		SSI2_SCR = scr;
-	}
-
-	return 0;
-}
-
-static int imx_ssi_startup(struct snd_pcm_substream *substream,
-			struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		/* set up TX DMA params */
-		switch (cpu_dai->id) {
-		case IMX_DAI_SSI0:
-			cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out0;
-			break;
-		case IMX_DAI_SSI1:
-			cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out1;
-			break;
-		case IMX_DAI_SSI2:
-			cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out0;
-			break;
-		case IMX_DAI_SSI3:
-			cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out1;
-		}
-		pr_debug("%s: (playback)\n", __func__);
-	} else {
-		/* set up RX DMA params */
-		switch (cpu_dai->id) {
-		case IMX_DAI_SSI0:
-			cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in0;
-			break;
-		case IMX_DAI_SSI1:
-			cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in1;
-			break;
-		case IMX_DAI_SSI2:
-			cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in0;
-			break;
-		case IMX_DAI_SSI3:
-			cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in1;
-		}
-		pr_debug("%s: (capture)\n", __func__);
-	}
-
-	/*
-	 * we cant really change any SSI values after SSI is enabled
-	 * need to fix in software for max flexibility - lrg
-	 */
-	if (cpu_dai->active) {
-		printk(KERN_WARNING "Warning ssi already enabled\n");
-		return 0;
-	}
-
-	/* reset the SSI port - Sect 45.4.4 */
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-
-		if (!ssi_clk0)
-			return -EINVAL;
-
-		if (ssi_active[SSI1_PORT]++) {
-			pr_debug("%s: exit before reset\n", __func__);
-			return 0;
-		}
-
-		/* SSI1 Reset */
-		SSI1_SCR = 0;
-
-		SSI1_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
-			SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
-			SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
-			SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
-	} else {
-
-		if (!ssi_clk1)
-			return -EINVAL;
-
-		if (ssi_active[SSI2_PORT]++) {
-			pr_debug("%s: exit before reset\n", __func__);
-			return 0;
-		}
-
-		/* SSI2 Reset */
-		SSI2_SCR = 0;
-
-		SSI2_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
-			SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
-			SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
-			SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
-	}
-
-	return 0;
-}
-
-int imx_ssi_hw_tx_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->dai->cpu_dai;
-	u32 stccr, stcr, sier;
-
-	pr_debug("%s\n", __func__);
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		stccr = SSI1_STCCR & ~SSI_STCCR_WL_MASK;
-		stcr = SSI1_STCR;
-		sier = SSI1_SIER;
-	} else {
-		stccr = SSI2_STCCR & ~SSI_STCCR_WL_MASK;
-		stcr = SSI2_STCR;
-		sier = SSI2_SIER;
-	}
-
-	/* DAI data (word) size */
-	switch (params_format(params)) {
-	case SNDRV_PCM_FORMAT_S16_LE:
-		stccr |= SSI_STCCR_WL(16);
-		break;
-	case SNDRV_PCM_FORMAT_S20_3LE:
-		stccr |= SSI_STCCR_WL(20);
-		break;
-	case SNDRV_PCM_FORMAT_S24_LE:
-		stccr |= SSI_STCCR_WL(24);
-		break;
-	}
-
-	/* enable interrupts */
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
-		stcr |= SSI_STCR_TFEN0;
-	else
-		stcr |= SSI_STCR_TFEN1;
-	sier |= SSI_SIER_TDMAE;
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		SSI1_STCR = stcr;
-		SSI1_STCCR = stccr;
-		SSI1_SIER = sier;
-	} else {
-		SSI2_STCR = stcr;
-		SSI2_STCCR = stccr;
-		SSI2_SIER = sier;
-	}
-
-	return 0;
-}
-
-int imx_ssi_hw_rx_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->dai->cpu_dai;
-	u32 srccr, srcr, sier;
-
-	pr_debug("%s\n", __func__);
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		srccr = SSI1_SRCCR & ~SSI_SRCCR_WL_MASK;
-		srcr = SSI1_SRCR;
-		sier = SSI1_SIER;
-	} else {
-		srccr = SSI2_SRCCR & ~SSI_SRCCR_WL_MASK;
-		srcr = SSI2_SRCR;
-		sier = SSI2_SIER;
-	}
-
-	/* DAI data (word) size */
-	switch (params_format(params)) {
-	case SNDRV_PCM_FORMAT_S16_LE:
-		srccr |= SSI_SRCCR_WL(16);
-		break;
-	case SNDRV_PCM_FORMAT_S20_3LE:
-		srccr |= SSI_SRCCR_WL(20);
-		break;
-	case SNDRV_PCM_FORMAT_S24_LE:
-		srccr |= SSI_SRCCR_WL(24);
-		break;
-	}
-
-	/* enable interrupts */
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
-		srcr |= SSI_SRCR_RFEN0;
-	else
-		srcr |= SSI_SRCR_RFEN1;
-	sier |= SSI_SIER_RDMAE;
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		SSI1_SRCR = srcr;
-		SSI1_SRCCR = srccr;
-		SSI1_SIER = sier;
-	} else {
-		SSI2_SRCR = srcr;
-		SSI2_SRCCR = srccr;
-		SSI2_SIER = sier;
-	}
-
-	return 0;
-}
-
-/*
- * Should only be called when port is inactive (i.e. SSIEN = 0),
- * although can be called multiple times by upper layers.
- */
-int imx_ssi_hw_params(struct snd_pcm_substream *substream,
-				struct snd_pcm_hw_params *params,
-				struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-
-	int ret;
-
-	/* cant change any parameters when SSI is running */
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		if (SSI1_SCR & SSI_SCR_SSIEN) {
-			printk(KERN_WARNING "Warning ssi already enabled\n");
-			return 0;
-		}
-	} else {
-		if (SSI2_SCR & SSI_SCR_SSIEN) {
-			printk(KERN_WARNING "Warning ssi already enabled\n");
-			return 0;
-		}
-	}
-
-	/*
-	 * Configure both tx and rx params with the same settings. This is
-	 * really a harware restriction because SSI must be disabled until
-	 * we can change those values. If there is an active audio stream in
-	 * one direction, enabling the other direction with different
-	 * settings would mean disturbing the running one.
-	 */
-	ret = imx_ssi_hw_tx_params(substream, params);
-	if (ret < 0)
-		return ret;
-	return imx_ssi_hw_rx_params(substream, params);
-}
-
-int imx_ssi_prepare(struct snd_pcm_substream *substream,
-			struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-	int ret;
-
-	pr_debug("%s\n", __func__);
-
-	/* Enable clks here to follow SSI recommended init sequence */
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
-		ret = clk_enable(ssi_clk0);
-		if (ret < 0)
-			printk(KERN_ERR "Unable to enable ssi_clk0\n");
-	} else {
-		ret = clk_enable(ssi_clk1);
-		if (ret < 0)
-			printk(KERN_ERR "Unable to enable ssi_clk1\n");
-	}
-
-	return 0;
-}
-
-static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
-			struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-	u32 scr;
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
-		scr = SSI1_SCR;
-	else
-		scr = SSI2_SCR;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			scr |= SSI_SCR_TE | SSI_SCR_SSIEN;
-		else
-			scr |= SSI_SCR_RE | SSI_SCR_SSIEN;
-		break;
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			scr &= ~SSI_SCR_TE;
-		else
-			scr &= ~SSI_SCR_RE;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
-		SSI1_SCR = scr;
-	else
-		SSI2_SCR = scr;
-
-	return 0;
-}
-
-static void imx_ssi_shutdown(struct snd_pcm_substream *substream,
-			struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-
-	/* shutdown SSI if neither Tx or Rx is active */
-	if (!cpu_dai->active) {
-
-		if (cpu_dai->id == IMX_DAI_SSI0 ||
-			cpu_dai->id == IMX_DAI_SSI2) {
-
-			if (--ssi_active[SSI1_PORT] > 1)
-				return;
-
-			SSI1_SCR = 0;
-			clk_disable(ssi_clk0);
-		} else {
-			if (--ssi_active[SSI2_PORT])
-				return;
-			SSI2_SCR = 0;
-			clk_disable(ssi_clk1);
-		}
-	}
-}
-
-#ifdef CONFIG_PM
-static int imx_ssi_suspend(struct platform_device *dev,
-	struct snd_soc_dai *dai)
-{
-	return 0;
-}
-
-static int imx_ssi_resume(struct platform_device *pdev,
-	struct snd_soc_dai *dai)
-{
-	return 0;
-}
-
-#else
-#define imx_ssi_suspend	NULL
-#define imx_ssi_resume	NULL
-#endif
-
-#define IMX_SSI_RATES \
-	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
-	SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
-	SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
-	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
-	SNDRV_PCM_RATE_96000)
-
-#define IMX_SSI_BITS \
-	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
-	SNDRV_PCM_FMTBIT_S24_LE)
-
-static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
-	.startup = imx_ssi_startup,
-	.shutdown = imx_ssi_shutdown,
-	.trigger = imx_ssi_trigger,
-	.prepare = imx_ssi_prepare,
-	.hw_params = imx_ssi_hw_params,
-	.set_sysclk = imx_ssi_set_dai_sysclk,
-	.set_clkdiv = imx_ssi_set_dai_clkdiv,
-	.set_fmt = imx_ssi_set_dai_fmt,
-	.set_tdm_slot = imx_ssi_set_dai_tdm_slot,
-};
-
-struct snd_soc_dai imx_ssi_pcm_dai[] = {
-{
-	.name = "imx-i2s-1-0",
-	.id = IMX_DAI_SSI0,
-	.suspend = imx_ssi_suspend,
-	.resume = imx_ssi_resume,
-	.playback = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.formats = IMX_SSI_BITS,
-		.rates = IMX_SSI_RATES,},
-	.capture = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.formats = IMX_SSI_BITS,
-		.rates = IMX_SSI_RATES,},
-	.ops = &imx_ssi_pcm_dai_ops,
-},
-{
-	.name = "imx-i2s-2-0",
-	.id = IMX_DAI_SSI1,
-	.playback = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.formats = IMX_SSI_BITS,
-		.rates = IMX_SSI_RATES,},
-	.capture = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.formats = IMX_SSI_BITS,
-		.rates = IMX_SSI_RATES,},
-	.ops = &imx_ssi_pcm_dai_ops,
-},
-{
-	.name = "imx-i2s-1-1",
-	.id = IMX_DAI_SSI2,
-	.suspend = imx_ssi_suspend,
-	.resume = imx_ssi_resume,
-	.playback = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.formats = IMX_SSI_BITS,
-		.rates = IMX_SSI_RATES,},
-	.capture = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.formats = IMX_SSI_BITS,
-		.rates = IMX_SSI_RATES,},
-	.ops = &imx_ssi_pcm_dai_ops,
-},
-{
-	.name = "imx-i2s-2-1",
-	.id = IMX_DAI_SSI3,
-	.playback = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.formats = IMX_SSI_BITS,
-		.rates = IMX_SSI_RATES,},
-	.capture = {
-		.channels_min = 1,
-		.channels_max = 2,
-		.formats = IMX_SSI_BITS,
-		.rates = IMX_SSI_RATES,},
-	.ops = &imx_ssi_pcm_dai_ops,
-},
-};
-EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
-
-static int __init imx_ssi_init(void)
-{
-	return snd_soc_register_dais(imx_ssi_pcm_dai,
-				ARRAY_SIZE(imx_ssi_pcm_dai));
-}
-
-static void __exit imx_ssi_exit(void)
-{
-	snd_soc_unregister_dais(imx_ssi_pcm_dai,
-				ARRAY_SIZE(imx_ssi_pcm_dai));
-}
-
-module_init(imx_ssi_init);
-module_exit(imx_ssi_exit);
-MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com");
-MODULE_DESCRIPTION("i.MX ASoC I2S driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/mxc-ssi.h b/sound/soc/imx/mxc-ssi.h
deleted file mode 100644
index 12bbdc9..0000000
--- a/sound/soc/imx/mxc-ssi.h
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * 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 _IMX_SSI_H
-#define _IMX_SSI_H
-
-#include <mach/hardware.h>
-
-/* SSI regs definition - MOVE to /arch/arm/plat-mxc/include/mach/ when stable */
-#define SSI1_IO_BASE_ADDR	IO_ADDRESS(SSI1_BASE_ADDR)
-#define SSI2_IO_BASE_ADDR	IO_ADDRESS(SSI2_BASE_ADDR)
-
-#define STX0   0x00
-#define STX1   0x04
-#define SRX0   0x08
-#define SRX1   0x0c
-#define SCR    0x10
-#define SISR   0x14
-#define SIER   0x18
-#define STCR   0x1c
-#define SRCR   0x20
-#define STCCR  0x24
-#define SRCCR  0x28
-#define SFCSR  0x2c
-#define STR    0x30
-#define SOR    0x34
-#define SACNT  0x38
-#define SACADD 0x3c
-#define SACDAT 0x40
-#define SATAG  0x44
-#define STMSK  0x48
-#define SRMSK  0x4c
-
-#define SSI1_STX0	(*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX0)))
-#define SSI1_STX1   (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STX1)))
-#define SSI1_SRX0   (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX0)))
-#define SSI1_SRX1   (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRX1)))
-#define SSI1_SCR    (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SCR)))
-#define SSI1_SISR   (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SISR)))
-#define SSI1_SIER   (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SIER)))
-#define SSI1_STCR   (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCR)))
-#define SSI1_SRCR   (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCR)))
-#define SSI1_STCCR  (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STCCR)))
-#define SSI1_SRCCR  (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRCCR)))
-#define SSI1_SFCSR  (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SFCSR)))
-#define SSI1_STR    (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STR)))
-#define SSI1_SOR    (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SOR)))
-#define SSI1_SACNT  (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACNT)))
-#define SSI1_SACADD (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACADD)))
-#define SSI1_SACDAT (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SACDAT)))
-#define SSI1_SATAG  (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SATAG)))
-#define SSI1_STMSK  (*((volatile u32 *)(SSI1_IO_BASE_ADDR + STMSK)))
-#define SSI1_SRMSK  (*((volatile u32 *)(SSI1_IO_BASE_ADDR + SRMSK)))
-
-
-#define SSI2_STX0	(*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX0)))
-#define SSI2_STX1   (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STX1)))
-#define SSI2_SRX0   (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX0)))
-#define SSI2_SRX1   (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRX1)))
-#define SSI2_SCR    (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SCR)))
-#define SSI2_SISR   (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SISR)))
-#define SSI2_SIER   (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SIER)))
-#define SSI2_STCR   (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCR)))
-#define SSI2_SRCR   (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCR)))
-#define SSI2_STCCR  (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STCCR)))
-#define SSI2_SRCCR  (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRCCR)))
-#define SSI2_SFCSR  (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SFCSR)))
-#define SSI2_STR    (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STR)))
-#define SSI2_SOR    (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SOR)))
-#define SSI2_SACNT  (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACNT)))
-#define SSI2_SACADD (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACADD)))
-#define SSI2_SACDAT (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SACDAT)))
-#define SSI2_SATAG  (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SATAG)))
-#define SSI2_STMSK  (*((volatile u32 *)(SSI2_IO_BASE_ADDR + STMSK)))
-#define SSI2_SRMSK  (*((volatile u32 *)(SSI2_IO_BASE_ADDR + SRMSK)))
-
-#define SSI_SCR_CLK_IST        (1 << 9)
-#define SSI_SCR_TCH_EN         (1 << 8)
-#define SSI_SCR_SYS_CLK_EN     (1 << 7)
-#define SSI_SCR_I2S_MODE_NORM  (0 << 5)
-#define SSI_SCR_I2S_MODE_MSTR  (1 << 5)
-#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
-#define SSI_SCR_SYN            (1 << 4)
-#define SSI_SCR_NET            (1 << 3)
-#define SSI_SCR_RE             (1 << 2)
-#define SSI_SCR_TE             (1 << 1)
-#define SSI_SCR_SSIEN          (1 << 0)
-
-#define SSI_SISR_CMDAU         (1 << 18)
-#define SSI_SISR_CMDDU         (1 << 17)
-#define SSI_SISR_RXT           (1 << 16)
-#define SSI_SISR_RDR1          (1 << 15)
-#define SSI_SISR_RDR0          (1 << 14)
-#define SSI_SISR_TDE1          (1 << 13)
-#define SSI_SISR_TDE0          (1 << 12)
-#define SSI_SISR_ROE1          (1 << 11)
-#define SSI_SISR_ROE0          (1 << 10)
-#define SSI_SISR_TUE1          (1 << 9)
-#define SSI_SISR_TUE0          (1 << 8)
-#define SSI_SISR_TFS           (1 << 7)
-#define SSI_SISR_RFS           (1 << 6)
-#define SSI_SISR_TLS           (1 << 5)
-#define SSI_SISR_RLS           (1 << 4)
-#define SSI_SISR_RFF1          (1 << 3)
-#define SSI_SISR_RFF0          (1 << 2)
-#define SSI_SISR_TFE1          (1 << 1)
-#define SSI_SISR_TFE0          (1 << 0)
-
-#define SSI_SIER_RDMAE         (1 << 22)
-#define SSI_SIER_RIE           (1 << 21)
-#define SSI_SIER_TDMAE         (1 << 20)
-#define SSI_SIER_TIE           (1 << 19)
-#define SSI_SIER_CMDAU_EN      (1 << 18)
-#define SSI_SIER_CMDDU_EN      (1 << 17)
-#define SSI_SIER_RXT_EN        (1 << 16)
-#define SSI_SIER_RDR1_EN       (1 << 15)
-#define SSI_SIER_RDR0_EN       (1 << 14)
-#define SSI_SIER_TDE1_EN       (1 << 13)
-#define SSI_SIER_TDE0_EN       (1 << 12)
-#define SSI_SIER_ROE1_EN       (1 << 11)
-#define SSI_SIER_ROE0_EN       (1 << 10)
-#define SSI_SIER_TUE1_EN       (1 << 9)
-#define SSI_SIER_TUE0_EN       (1 << 8)
-#define SSI_SIER_TFS_EN        (1 << 7)
-#define SSI_SIER_RFS_EN        (1 << 6)
-#define SSI_SIER_TLS_EN        (1 << 5)
-#define SSI_SIER_RLS_EN        (1 << 4)
-#define SSI_SIER_RFF1_EN       (1 << 3)
-#define SSI_SIER_RFF0_EN       (1 << 2)
-#define SSI_SIER_TFE1_EN       (1 << 1)
-#define SSI_SIER_TFE0_EN       (1 << 0)
-
-#define SSI_STCR_TXBIT0        (1 << 9)
-#define SSI_STCR_TFEN1         (1 << 8)
-#define SSI_STCR_TFEN0         (1 << 7)
-#define SSI_STCR_TFDIR         (1 << 6)
-#define SSI_STCR_TXDIR         (1 << 5)
-#define SSI_STCR_TSHFD         (1 << 4)
-#define SSI_STCR_TSCKP         (1 << 3)
-#define SSI_STCR_TFSI          (1 << 2)
-#define SSI_STCR_TFSL          (1 << 1)
-#define SSI_STCR_TEFS          (1 << 0)
-
-#define SSI_SRCR_RXBIT0        (1 << 9)
-#define SSI_SRCR_RFEN1         (1 << 8)
-#define SSI_SRCR_RFEN0         (1 << 7)
-#define SSI_SRCR_RFDIR         (1 << 6)
-#define SSI_SRCR_RXDIR         (1 << 5)
-#define SSI_SRCR_RSHFD         (1 << 4)
-#define SSI_SRCR_RSCKP         (1 << 3)
-#define SSI_SRCR_RFSI          (1 << 2)
-#define SSI_SRCR_RFSL          (1 << 1)
-#define SSI_SRCR_REFS          (1 << 0)
-
-#define SSI_STCCR_DIV2         (1 << 18)
-#define SSI_STCCR_PSR          (1 << 15)
-#define SSI_STCCR_WL(x)        ((((x) - 2) >> 1) << 13)
-#define SSI_STCCR_DC(x)        (((x) & 0x1f) << 8)
-#define SSI_STCCR_PM(x)        (((x) & 0xff) << 0)
-#define SSI_STCCR_WL_MASK        (0xf << 13)
-#define SSI_STCCR_DC_MASK        (0x1f << 8)
-#define SSI_STCCR_PM_MASK        (0xff << 0)
-
-#define SSI_SRCCR_DIV2         (1 << 18)
-#define SSI_SRCCR_PSR          (1 << 15)
-#define SSI_SRCCR_WL(x)        ((((x) - 2) >> 1) << 13)
-#define SSI_SRCCR_DC(x)        (((x) & 0x1f) << 8)
-#define SSI_SRCCR_PM(x)        (((x) & 0xff) << 0)
-#define SSI_SRCCR_WL_MASK        (0xf << 13)
-#define SSI_SRCCR_DC_MASK        (0x1f << 8)
-#define SSI_SRCCR_PM_MASK        (0xff << 0)
-
-
-#define SSI_SFCSR_RFCNT1(x)   (((x) & 0xf) << 28)
-#define SSI_SFCSR_TFCNT1(x)   (((x) & 0xf) << 24)
-#define SSI_SFCSR_RFWM1(x)    (((x) & 0xf) << 20)
-#define SSI_SFCSR_TFWM1(x)    (((x) & 0xf) << 16)
-#define SSI_SFCSR_RFCNT0(x)   (((x) & 0xf) << 12)
-#define SSI_SFCSR_TFCNT0(x)   (((x) & 0xf) <<  8)
-#define SSI_SFCSR_RFWM0(x)    (((x) & 0xf) <<  4)
-#define SSI_SFCSR_TFWM0(x)    (((x) & 0xf) <<  0)
-
-#define SSI_STR_TEST          (1 << 15)
-#define SSI_STR_RCK2TCK       (1 << 14)
-#define SSI_STR_RFS2TFS       (1 << 13)
-#define SSI_STR_RXSTATE(x)    (((x) & 0xf) << 8)
-#define SSI_STR_TXD2RXD       (1 <<  7)
-#define SSI_STR_TCK2RCK       (1 <<  6)
-#define SSI_STR_TFS2RFS       (1 <<  5)
-#define SSI_STR_TXSTATE(x)    (((x) & 0xf) << 0)
-
-#define SSI_SOR_CLKOFF        (1 << 6)
-#define SSI_SOR_RX_CLR        (1 << 5)
-#define SSI_SOR_TX_CLR        (1 << 4)
-#define SSI_SOR_INIT          (1 << 3)
-#define SSI_SOR_WAIT(x)       (((x) & 0x3) << 1)
-#define SSI_SOR_SYNRST        (1 << 0)
-
-#define SSI_SACNT_FRDIV(x)    (((x) & 0x3f) << 5)
-#define SSI_SACNT_WR          (x << 4)
-#define SSI_SACNT_RD          (x << 3)
-#define SSI_SACNT_TIF         (x << 2)
-#define SSI_SACNT_FV          (x << 1)
-#define SSI_SACNT_AC97EN      (x << 0)
-
-/* Watermarks for FIFO's */
-#define TXFIFO_WATERMARK				0x4
-#define RXFIFO_WATERMARK				0x4
-
-/* i.MX DAI SSP ID's */
-#define IMX_DAI_SSI0			0 /* SSI1 FIFO 0 */
-#define IMX_DAI_SSI1			1 /* SSI1 FIFO 1 */
-#define IMX_DAI_SSI2			2 /* SSI2 FIFO 0 */
-#define IMX_DAI_SSI3			3 /* SSI2 FIFO 1 */
-
-/* SSI clock sources */
-#define IMX_SSP_SYS_CLK		0
-
-/* SSI audio dividers */
-#define IMX_SSI_TX_DIV_2			0
-#define IMX_SSI_TX_DIV_PSR			1
-#define IMX_SSI_TX_DIV_PM			2
-#define IMX_SSI_RX_DIV_2			3
-#define IMX_SSI_RX_DIV_PSR			4
-#define IMX_SSI_RX_DIV_PM			5
-
-
-/* SSI Div 2 */
-#define IMX_SSI_DIV_2_OFF		(~SSI_STCCR_DIV2)
-#define IMX_SSI_DIV_2_ON		SSI_STCCR_DIV2
-
-extern struct snd_soc_dai imx_ssi_pcm_dai[4];
-extern int get_ssi_clk(int ssi, struct device *dev);
-extern void put_ssi_clk(int ssi);
-#endif
diff --git a/sound/soc/imx/phycore-ac97.c b/sound/soc/imx/phycore-ac97.c
new file mode 100644
index 0000000..a8307d5
--- /dev/null
+++ b/sound/soc/imx/phycore-ac97.c
@@ -0,0 +1,90 @@
+/*
+ * phycore-ac97.c  --  SoC audio for imx_phycore in AC97 mode
+ *
+ * Copyright 2009 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ *
+ *  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-types.h>
+
+#include "../codecs/wm9712.h"
+#include "imx-ssi.h"
+
+static struct snd_soc_card imx_phycore;
+
+static struct snd_soc_ops imx_phycore_hifi_ops = {
+};
+
+static struct snd_soc_dai_link imx_phycore_dai_ac97[] = {
+	{
+		.name		= "HiFi",
+		.stream_name	= "HiFi",
+		.codec_dai	= &wm9712_dai[WM9712_DAI_AC97_HIFI],
+		.ops		= &imx_phycore_hifi_ops,
+	},
+};
+
+static struct snd_soc_card imx_phycore = {
+	.name		= "PhyCORE-audio",
+	.platform	= &imx_soc_platform,
+	.dai_link	= imx_phycore_dai_ac97,
+	.num_links	= ARRAY_SIZE(imx_phycore_dai_ac97),
+};
+
+static struct snd_soc_device imx_phycore_snd_devdata = {
+	.card		= &imx_phycore,
+	.codec_dev	= &soc_codec_dev_wm9712,
+};
+
+static struct platform_device *imx_phycore_snd_device;
+
+static int __init imx_phycore_init(void)
+{
+	int ret;
+
+	if (!machine_is_pcm043() && !machine_is_pca100())
+		/* return happy. We might run on a totally different machine */
+		return 0;
+
+	imx_phycore_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!imx_phycore_snd_device)
+		return -ENOMEM;
+
+	imx_phycore_dai_ac97[0].cpu_dai = &imx_ssi_pcm_dai[0];
+
+	platform_set_drvdata(imx_phycore_snd_device, &imx_phycore_snd_devdata);
+	imx_phycore_snd_devdata.dev = &imx_phycore_snd_device->dev;
+	ret = platform_device_add(imx_phycore_snd_device);
+
+	if (ret) {
+		printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+		platform_device_put(imx_phycore_snd_device);
+	}
+
+	return ret;
+}
+
+static void __exit imx_phycore_exit(void)
+{
+	platform_device_unregister(imx_phycore_snd_device);
+}
+
+late_initcall(imx_phycore_init);
+module_exit(imx_phycore_exit);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("PhyCORE ALSA SoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 61952aa..f11963c 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -6,6 +6,9 @@
 	tristate
 	select OMAP_MCBSP
 
+config SND_OMAP_SOC_MCPDM
+	tristate
+
 config SND_OMAP_SOC_N810
 	tristate "SoC Audio support for Nokia N810"
 	depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
@@ -94,12 +97,14 @@
 	  Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
 
 config SND_OMAP_SOC_OMAP3_BEAGLE
-	tristate "SoC Audio support for OMAP3 Beagle"
-	depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_BEAGLE
+	tristate "SoC Audio support for OMAP3 Beagle and Devkit8000"
+	depends on TWL4030_CORE && SND_OMAP_SOC
+	depends on (MACH_OMAP3_BEAGLE || MACH_DEVKIT8000)
 	select SND_OMAP_SOC_MCBSP
 	select SND_SOC_TWL4030
 	help
-	  Say Y if you want to add support for SoC audio on the Beagleboard.
+	  Say Y if you want to add support for SoC audio on the Beagleboard or
+	  the clone Devkit8000.
 
 config SND_OMAP_SOC_ZOOM2
 	tristate "SoC Audio support for Zoom2"
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 19283e5..0bc00ca 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -1,9 +1,11 @@
 # OMAP Platform Support
 snd-soc-omap-objs := omap-pcm.o
 snd-soc-omap-mcbsp-objs := omap-mcbsp.o
+snd-soc-omap-mcpdm-objs := omap-mcpdm.o mcpdm.o
 
 obj-$(CONFIG_SND_OMAP_SOC) += snd-soc-omap.o
 obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o
+obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o
 
 # OMAP Machine Support
 snd-soc-n810-objs := n810.o
diff --git a/sound/soc/omap/mcpdm.c b/sound/soc/omap/mcpdm.c
new file mode 100644
index 0000000..ad8df6c
--- /dev/null
+++ b/sound/soc/omap/mcpdm.c
@@ -0,0 +1,484 @@
+/*
+ * mcpdm.c  --  McPDM interface driver
+ *
+ * Author: Jorge Eduardo Candelaria <x0107209@ti.com>
+ * Copyright (C) 2009 - Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include "mcpdm.h"
+
+static struct omap_mcpdm *mcpdm;
+
+static inline void omap_mcpdm_write(u16 reg, u32 val)
+{
+       __raw_writel(val, mcpdm->io_base + reg);
+}
+
+static inline int omap_mcpdm_read(u16 reg)
+{
+       return __raw_readl(mcpdm->io_base + reg);
+}
+
+static void omap_mcpdm_reg_dump(void)
+{
+       dev_dbg(mcpdm->dev, "***********************\n");
+       dev_dbg(mcpdm->dev, "IRQSTATUS_RAW:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_IRQSTATUS_RAW));
+       dev_dbg(mcpdm->dev, "IRQSTATUS:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_IRQSTATUS));
+       dev_dbg(mcpdm->dev, "IRQENABLE_SET:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_IRQENABLE_SET));
+       dev_dbg(mcpdm->dev, "IRQENABLE_CLR:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_IRQENABLE_CLR));
+       dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n",
+                       omap_mcpdm_read(MCPDM_IRQWAKE_EN));
+       dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n",
+                       omap_mcpdm_read(MCPDM_DMAENABLE_SET));
+       dev_dbg(mcpdm->dev, "DMAENABLE_CLR:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_DMAENABLE_CLR));
+       dev_dbg(mcpdm->dev, "DMAWAKEEN:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_DMAWAKEEN));
+       dev_dbg(mcpdm->dev, "CTRL:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_CTRL));
+       dev_dbg(mcpdm->dev, "DN_DATA:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_DN_DATA));
+       dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n",
+                       omap_mcpdm_read(MCPDM_UP_DATA));
+       dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n",
+                       omap_mcpdm_read(MCPDM_FIFO_CTRL_DN));
+       dev_dbg(mcpdm->dev, "FIFO_CTRL_UP:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_FIFO_CTRL_UP));
+       dev_dbg(mcpdm->dev, "DN_OFFSET:  0x%04x\n",
+                       omap_mcpdm_read(MCPDM_DN_OFFSET));
+       dev_dbg(mcpdm->dev, "***********************\n");
+}
+
+/*
+ * Takes the McPDM module in and out of reset state.
+ * Uplink and downlink can be reset individually.
+ */
+static void omap_mcpdm_reset_capture(int reset)
+{
+       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
+
+       if (reset)
+               ctrl |= SW_UP_RST;
+       else
+               ctrl &= ~SW_UP_RST;
+
+       omap_mcpdm_write(MCPDM_CTRL, ctrl);
+}
+
+static void omap_mcpdm_reset_playback(int reset)
+{
+       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
+
+       if (reset)
+               ctrl |= SW_DN_RST;
+       else
+               ctrl &= ~SW_DN_RST;
+
+       omap_mcpdm_write(MCPDM_CTRL, ctrl);
+}
+
+/*
+ * Enables the transfer through the PDM interface to/from the Phoenix
+ * codec by enabling the corresponding UP or DN channels.
+ */
+void omap_mcpdm_start(int stream)
+{
+       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
+
+       if (stream)
+               ctrl |= mcpdm->up_channels;
+       else
+               ctrl |= mcpdm->dn_channels;
+
+       omap_mcpdm_write(MCPDM_CTRL, ctrl);
+}
+
+/*
+ * Disables the transfer through the PDM interface to/from the Phoenix
+ * codec by disabling the corresponding UP or DN channels.
+ */
+void omap_mcpdm_stop(int stream)
+{
+       int ctrl = omap_mcpdm_read(MCPDM_CTRL);
+
+       if (stream)
+               ctrl &= ~mcpdm->up_channels;
+       else
+               ctrl &= ~mcpdm->dn_channels;
+
+       omap_mcpdm_write(MCPDM_CTRL, ctrl);
+}
+
+/*
+ * Configures McPDM uplink for audio recording.
+ * This function should be called before omap_mcpdm_start.
+ */
+int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink)
+{
+       int irq_mask = 0;
+       int ctrl;
+
+       if (!uplink)
+               return -EINVAL;
+
+       mcpdm->uplink = uplink;
+
+       /* Enable irq request generation */
+       irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
+       omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
+
+       /* Configure uplink threshold */
+       if (uplink->threshold > UP_THRES_MAX)
+               uplink->threshold = UP_THRES_MAX;
+
+       omap_mcpdm_write(MCPDM_FIFO_CTRL_UP, uplink->threshold);
+
+       /* Configure DMA controller */
+       omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_UP_ENABLE);
+
+       /* Set pdm out format */
+       ctrl = omap_mcpdm_read(MCPDM_CTRL);
+       ctrl &= ~PDMOUTFORMAT;
+       ctrl |= uplink->format & PDMOUTFORMAT;
+
+       /* Uplink channels */
+       mcpdm->up_channels = uplink->channels & (PDM_UP_MASK | PDM_STATUS_MASK);
+
+       omap_mcpdm_write(MCPDM_CTRL, ctrl);
+
+       return 0;
+}
+
+/*
+ * Configures McPDM downlink for audio playback.
+ * This function should be called before omap_mcpdm_start.
+ */
+int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink)
+{
+       int irq_mask = 0;
+       int ctrl;
+
+       if (!downlink)
+               return -EINVAL;
+
+       mcpdm->downlink = downlink;
+
+       /* Enable irq request generation */
+       irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
+       omap_mcpdm_write(MCPDM_IRQENABLE_SET, irq_mask);
+
+       /* Configure uplink threshold */
+       if (downlink->threshold > DN_THRES_MAX)
+               downlink->threshold = DN_THRES_MAX;
+
+       omap_mcpdm_write(MCPDM_FIFO_CTRL_DN, downlink->threshold);
+
+       /* Enable DMA request generation */
+       omap_mcpdm_write(MCPDM_DMAENABLE_SET, DMA_DN_ENABLE);
+
+       /* Set pdm out format */
+       ctrl = omap_mcpdm_read(MCPDM_CTRL);
+       ctrl &= ~PDMOUTFORMAT;
+       ctrl |= downlink->format & PDMOUTFORMAT;
+
+       /* Downlink channels */
+       mcpdm->dn_channels = downlink->channels & (PDM_DN_MASK | PDM_CMD_MASK);
+
+       omap_mcpdm_write(MCPDM_CTRL, ctrl);
+
+       return 0;
+}
+
+/*
+ * Cleans McPDM uplink configuration.
+ * This function should be called when the stream is closed.
+ */
+int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink)
+{
+       int irq_mask = 0;
+
+       if (!uplink)
+               return -EINVAL;
+
+       /* Disable irq request generation */
+       irq_mask |= uplink->irq_mask & MCPDM_UPLINK_IRQ_MASK;
+       omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
+
+       /* Disable DMA request generation */
+       omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_UP_ENABLE);
+
+       /* Clear Downlink channels */
+       mcpdm->up_channels = 0;
+
+       mcpdm->uplink = NULL;
+
+       return 0;
+}
+
+/*
+ * Cleans McPDM downlink configuration.
+ * This function should be called when the stream is closed.
+ */
+int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink)
+{
+       int irq_mask = 0;
+
+       if (!downlink)
+               return -EINVAL;
+
+       /* Disable irq request generation */
+       irq_mask |= downlink->irq_mask & MCPDM_DOWNLINK_IRQ_MASK;
+       omap_mcpdm_write(MCPDM_IRQENABLE_CLR, irq_mask);
+
+       /* Disable DMA request generation */
+       omap_mcpdm_write(MCPDM_DMAENABLE_CLR, DMA_DN_ENABLE);
+
+       /* clear Downlink channels */
+       mcpdm->dn_channels = 0;
+
+       mcpdm->downlink = NULL;
+
+       return 0;
+}
+
+static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id)
+{
+       struct omap_mcpdm *mcpdm_irq = dev_id;
+       int irq_status;
+
+       irq_status = omap_mcpdm_read(MCPDM_IRQSTATUS);
+
+       /* Acknowledge irq event */
+       omap_mcpdm_write(MCPDM_IRQSTATUS, irq_status);
+
+       if (irq & MCPDM_DN_IRQ_FULL) {
+               dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
+               omap_mcpdm_reset_playback(1);
+               omap_mcpdm_playback_open(mcpdm_irq->downlink);
+               omap_mcpdm_reset_playback(0);
+       }
+
+       if (irq & MCPDM_DN_IRQ_EMPTY) {
+               dev_err(mcpdm_irq->dev, "DN FIFO error %x\n", irq_status);
+               omap_mcpdm_reset_playback(1);
+               omap_mcpdm_playback_open(mcpdm_irq->downlink);
+               omap_mcpdm_reset_playback(0);
+       }
+
+       if (irq & MCPDM_DN_IRQ) {
+               dev_dbg(mcpdm_irq->dev, "DN write request\n");
+       }
+
+       if (irq & MCPDM_UP_IRQ_FULL) {
+               dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
+               omap_mcpdm_reset_capture(1);
+               omap_mcpdm_capture_open(mcpdm_irq->uplink);
+               omap_mcpdm_reset_capture(0);
+       }
+
+       if (irq & MCPDM_UP_IRQ_EMPTY) {
+               dev_err(mcpdm_irq->dev, "UP FIFO error %x\n", irq_status);
+               omap_mcpdm_reset_capture(1);
+               omap_mcpdm_capture_open(mcpdm_irq->uplink);
+               omap_mcpdm_reset_capture(0);
+       }
+
+       if (irq & MCPDM_UP_IRQ) {
+               dev_dbg(mcpdm_irq->dev, "UP write request\n");
+       }
+
+       return IRQ_HANDLED;
+}
+
+int omap_mcpdm_request(void)
+{
+       int ret;
+
+       clk_enable(mcpdm->clk);
+
+       spin_lock(&mcpdm->lock);
+
+       if (!mcpdm->free) {
+               dev_err(mcpdm->dev, "McPDM interface is in use\n");
+               spin_unlock(&mcpdm->lock);
+               ret = -EBUSY;
+               goto err;
+       }
+       mcpdm->free = 0;
+
+       spin_unlock(&mcpdm->lock);
+
+       /* Disable lines while request is ongoing */
+       omap_mcpdm_write(MCPDM_CTRL, 0x00);
+
+       ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler,
+                               0, "McPDM", (void *)mcpdm);
+       if (ret) {
+               dev_err(mcpdm->dev, "Request for McPDM IRQ failed\n");
+               goto err;
+       }
+
+       return 0;
+
+err:
+       clk_disable(mcpdm->clk);
+       return ret;
+}
+
+void omap_mcpdm_free(void)
+{
+       spin_lock(&mcpdm->lock);
+       if (mcpdm->free) {
+               dev_err(mcpdm->dev, "McPDM interface is already free\n");
+               spin_unlock(&mcpdm->lock);
+               return;
+       }
+       mcpdm->free = 1;
+       spin_unlock(&mcpdm->lock);
+
+       clk_disable(mcpdm->clk);
+
+       free_irq(mcpdm->irq, (void *)mcpdm);
+}
+
+/* Enable/disable DC offset cancelation for the analog
+ * headset path (PDM channels 1 and 2).
+ */
+int omap_mcpdm_set_offset(int offset1, int offset2)
+{
+       int offset;
+
+       if ((offset1 > DN_OFST_MAX) || (offset2 > DN_OFST_MAX))
+               return -EINVAL;
+
+       offset = (offset1 << DN_OFST_RX1) | (offset2 << DN_OFST_RX2);
+
+       /* offset cancellation for channel 1 */
+       if (offset1)
+               offset |= DN_OFST_RX1_EN;
+       else
+               offset &= ~DN_OFST_RX1_EN;
+
+       /* offset cancellation for channel 2 */
+       if (offset2)
+               offset |= DN_OFST_RX2_EN;
+       else
+               offset &= ~DN_OFST_RX2_EN;
+
+       omap_mcpdm_write(MCPDM_DN_OFFSET, offset);
+
+       return 0;
+}
+
+static int __devinit omap_mcpdm_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       int ret = 0;
+
+       mcpdm = kzalloc(sizeof(struct omap_mcpdm), GFP_KERNEL);
+       if (!mcpdm) {
+               ret = -ENOMEM;
+               goto exit;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (res == NULL) {
+               dev_err(&pdev->dev, "no resource\n");
+               goto err_resource;
+       }
+
+       spin_lock_init(&mcpdm->lock);
+       mcpdm->free = 1;
+       mcpdm->io_base = ioremap(res->start, resource_size(res));
+       if (!mcpdm->io_base) {
+               ret = -ENOMEM;
+               goto err_resource;
+       }
+
+       mcpdm->irq = platform_get_irq(pdev, 0);
+
+       mcpdm->clk = clk_get(&pdev->dev, "pdm_ck");
+       if (IS_ERR(mcpdm->clk)) {
+               ret = PTR_ERR(mcpdm->clk);
+               dev_err(&pdev->dev, "unable to get pdm_ck: %d\n", ret);
+               goto err_clk;
+       }
+
+       mcpdm->dev = &pdev->dev;
+       platform_set_drvdata(pdev, mcpdm);
+
+       return 0;
+
+err_clk:
+       iounmap(mcpdm->io_base);
+err_resource:
+       kfree(mcpdm);
+exit:
+       return ret;
+}
+
+static int __devexit omap_mcpdm_remove(struct platform_device *pdev)
+{
+       struct omap_mcpdm *mcpdm_ptr = platform_get_drvdata(pdev);
+
+       platform_set_drvdata(pdev, NULL);
+
+       clk_put(mcpdm_ptr->clk);
+
+       iounmap(mcpdm_ptr->io_base);
+
+       mcpdm_ptr->clk = NULL;
+       mcpdm_ptr->free = 0;
+       mcpdm_ptr->dev = NULL;
+
+       kfree(mcpdm_ptr);
+
+       return 0;
+}
+
+static struct platform_driver omap_mcpdm_driver = {
+       .probe = omap_mcpdm_probe,
+       .remove = __devexit_p(omap_mcpdm_remove),
+       .driver = {
+               .name = "omap-mcpdm",
+       },
+};
+
+static struct platform_device *omap_mcpdm_device;
+
+static int __init omap_mcpdm_init(void)
+{
+       return platform_driver_register(&omap_mcpdm_driver);
+}
+arch_initcall(omap_mcpdm_init);
diff --git a/sound/soc/omap/mcpdm.h b/sound/soc/omap/mcpdm.h
new file mode 100644
index 0000000..7bb326e
--- /dev/null
+++ b/sound/soc/omap/mcpdm.h
@@ -0,0 +1,151 @@
+/*
+ * mcpdm.h -- Defines for McPDM driver
+ *
+ * Author: Jorge Eduardo Candelaria <x0107209@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+/* McPDM registers */
+
+#define MCPDM_REVISION         0x00
+#define MCPDM_SYSCONFIG                0x10
+#define MCPDM_IRQSTATUS_RAW    0x24
+#define MCPDM_IRQSTATUS                0x28
+#define MCPDM_IRQENABLE_SET    0x2C
+#define MCPDM_IRQENABLE_CLR    0x30
+#define MCPDM_IRQWAKE_EN       0x34
+#define MCPDM_DMAENABLE_SET    0x38
+#define MCPDM_DMAENABLE_CLR    0x3C
+#define MCPDM_DMAWAKEEN                0x40
+#define MCPDM_CTRL             0x44
+#define MCPDM_DN_DATA          0x48
+#define MCPDM_UP_DATA          0x4C
+#define MCPDM_FIFO_CTRL_DN     0x50
+#define MCPDM_FIFO_CTRL_UP     0x54
+#define MCPDM_DN_OFFSET                0x58
+
+/*
+ * MCPDM_IRQ bit fields
+ * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR
+ */
+
+#define MCPDM_DN_IRQ                   (1 << 0)
+#define MCPDM_DN_IRQ_EMPTY             (1 << 1)
+#define MCPDM_DN_IRQ_ALMST_EMPTY       (1 << 2)
+#define MCPDM_DN_IRQ_FULL              (1 << 3)
+
+#define MCPDM_UP_IRQ                   (1 << 8)
+#define MCPDM_UP_IRQ_EMPTY             (1 << 9)
+#define MCPDM_UP_IRQ_ALMST_FULL                (1 << 10)
+#define MCPDM_UP_IRQ_FULL              (1 << 11)
+
+#define MCPDM_DOWNLINK_IRQ_MASK                0x00F
+#define MCPDM_UPLINK_IRQ_MASK          0xF00
+
+/*
+ * MCPDM_DMAENABLE bit fields
+ */
+
+#define DMA_DN_ENABLE          0x1
+#define DMA_UP_ENABLE          0x2
+
+/*
+ * MCPDM_CTRL bit fields
+ */
+
+#define PDM_UP1_EN             0x0001
+#define PDM_UP2_EN             0x0002
+#define PDM_UP3_EN             0x0004
+#define PDM_DN1_EN             0x0008
+#define PDM_DN2_EN             0x0010
+#define PDM_DN3_EN             0x0020
+#define PDM_DN4_EN             0x0040
+#define PDM_DN5_EN             0x0080
+#define PDMOUTFORMAT           0x0100
+#define CMD_INT                        0x0200
+#define STATUS_INT             0x0400
+#define SW_UP_RST              0x0800
+#define SW_DN_RST              0x1000
+#define PDM_UP_MASK            0x007
+#define PDM_DN_MASK            0x0F8
+#define PDM_CMD_MASK           0x200
+#define PDM_STATUS_MASK                0x400
+
+
+#define PDMOUTFORMAT_LJUST     (0 << 8)
+#define PDMOUTFORMAT_RJUST     (1 << 8)
+
+/*
+ * MCPDM_FIFO_CTRL bit fields
+ */
+
+#define UP_THRES_MAX           0xF
+#define DN_THRES_MAX           0xF
+
+/*
+ * MCPDM_DN_OFFSET bit fields
+ */
+
+#define DN_OFST_RX1_EN         0x0001
+#define DN_OFST_RX2_EN         0x0100
+
+#define DN_OFST_RX1            1
+#define DN_OFST_RX2            9
+#define DN_OFST_MAX            0x1F
+
+#define MCPDM_UPLINK           1
+#define MCPDM_DOWNLINK         2
+
+struct omap_mcpdm_link {
+       int irq_mask;
+       int threshold;
+       int format;
+       int channels;
+};
+
+struct omap_mcpdm_platform_data {
+       unsigned long phys_base;
+       u16 irq;
+};
+
+struct omap_mcpdm {
+       struct device *dev;
+       unsigned long phys_base;
+       void __iomem *io_base;
+       u8 free;
+       int irq;
+
+       spinlock_t lock;
+       struct omap_mcpdm_platform_data *pdata;
+       struct clk *clk;
+       struct omap_mcpdm_link *downlink;
+       struct omap_mcpdm_link *uplink;
+       struct completion irq_completion;
+
+       int dn_channels;
+       int up_channels;
+};
+
+extern void omap_mcpdm_start(int stream);
+extern void omap_mcpdm_stop(int stream);
+extern int omap_mcpdm_capture_open(struct omap_mcpdm_link *uplink);
+extern int omap_mcpdm_playback_open(struct omap_mcpdm_link *downlink);
+extern int omap_mcpdm_capture_close(struct omap_mcpdm_link *uplink);
+extern int omap_mcpdm_playback_close(struct omap_mcpdm_link *downlink);
+extern int omap_mcpdm_request(void);
+extern void omap_mcpdm_free(void);
+extern int omap_mcpdm_set_offset(int offset1, int offset2);
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 6bbbd2a..d297256 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -287,6 +287,8 @@
 	omap_mcbsp_dai_dma_params[id][substream->stream].dma_req = dma;
 	omap_mcbsp_dai_dma_params[id][substream->stream].port_addr = port;
 	omap_mcbsp_dai_dma_params[id][substream->stream].sync_mode = sync_mode;
+	omap_mcbsp_dai_dma_params[id][substream->stream].data_type =
+							OMAP_DMA_DATA_TYPE_S16;
 	cpu_dai->dma_data = &omap_mcbsp_dai_dma_params[id][substream->stream];
 
 	if (mcbsp_data->configured) {
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c
new file mode 100644
index 0000000..25f19e4
--- /dev/null
+++ b/sound/soc/omap/omap-mcpdm.c
@@ -0,0 +1,251 @@
+/*
+ * omap-mcpdm.c  --  OMAP ALSA SoC DAI driver using McPDM port
+ *
+ * Copyright (C) 2009 Texas Instruments
+ *
+ * Author: Misael Lopez Cruz <x0052729@ti.com>
+ * Contact: Jorge Eduardo Candelaria <x0107209@ti.com>
+ *          Margarita Olaya <magi.olaya@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <plat/control.h>
+#include <plat/dma.h>
+#include <plat/mcbsp.h>
+#include "mcpdm.h"
+#include "omap-mcpdm.h"
+#include "omap-pcm.h"
+
+struct omap_mcpdm_data {
+	struct omap_mcpdm_link *links;
+	int active;
+};
+
+static struct omap_mcpdm_link omap_mcpdm_links[] = {
+	/* downlink */
+	{
+		.irq_mask = MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL,
+		.threshold = 1,
+		.format = PDMOUTFORMAT_LJUST,
+	},
+	/* uplink */
+	{
+		.irq_mask = MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL,
+		.threshold = 1,
+		.format = PDMOUTFORMAT_LJUST,
+	},
+};
+
+static struct omap_mcpdm_data mcpdm_data = {
+	.links = omap_mcpdm_links,
+	.active = 0,
+};
+
+/*
+ * Stream DMA parameters
+ */
+static struct omap_pcm_dma_data omap_mcpdm_dai_dma_params[] = {
+	{
+		.name = "Audio playback",
+		.dma_req = OMAP44XX_DMA_MCPDM_DL,
+		.data_type = OMAP_DMA_DATA_TYPE_S32,
+		.sync_mode = OMAP_DMA_SYNC_PACKET,
+		.packet_size = 16,
+		.port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_DN_DATA,
+	},
+	{
+		.name = "Audio capture",
+		.dma_req = OMAP44XX_DMA_MCPDM_UP,
+		.data_type = OMAP_DMA_DATA_TYPE_S32,
+		.sync_mode = OMAP_DMA_SYNC_PACKET,
+		.packet_size = 16,
+		.port_addr = OMAP44XX_MCPDM_L3_BASE + MCPDM_UP_DATA,
+	},
+};
+
+static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	int err = 0;
+
+	if (!cpu_dai->active)
+		err = omap_mcpdm_request();
+
+	return err;
+}
+
+static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream,
+				    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	if (!cpu_dai->active)
+		omap_mcpdm_free();
+}
+
+static int omap_mcpdm_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data;
+	int stream = substream->stream;
+	int err = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (!mcpdm_priv->active++)
+			omap_mcpdm_start(stream);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (!--mcpdm_priv->active)
+			omap_mcpdm_stop(stream);
+		break;
+	default:
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
+static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream,
+				    struct snd_pcm_hw_params *params,
+				    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data;
+	struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links;
+	int stream = substream->stream;
+	int channels, err, link_mask = 0;
+
+	cpu_dai->dma_data = &omap_mcpdm_dai_dma_params[stream];
+
+	channels = params_channels(params);
+	switch (channels) {
+	case 4:
+		if (stream == SNDRV_PCM_STREAM_CAPTURE)
+			/* up to 2 channels for capture */
+			return -EINVAL;
+		link_mask |= 1 << 3;
+	case 3:
+		if (stream == SNDRV_PCM_STREAM_CAPTURE)
+			/* up to 2 channels for capture */
+			return -EINVAL;
+		link_mask |= 1 << 2;
+	case 2:
+		link_mask |= 1 << 1;
+	case 1:
+		link_mask |= 1 << 0;
+		break;
+	default:
+		/* unsupported number of channels */
+		return -EINVAL;
+	}
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		mcpdm_links[stream].channels = link_mask << 3;
+		err = omap_mcpdm_playback_open(&mcpdm_links[stream]);
+	} else {
+		mcpdm_links[stream].channels = link_mask << 0;
+		err = omap_mcpdm_capture_open(&mcpdm_links[stream]);
+	}
+
+	return err;
+}
+
+static int omap_mcpdm_dai_hw_free(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct omap_mcpdm_data *mcpdm_priv = cpu_dai->private_data;
+	struct omap_mcpdm_link *mcpdm_links = mcpdm_priv->links;
+	int stream = substream->stream;
+	int err;
+
+	if (substream->stream ==  SNDRV_PCM_STREAM_PLAYBACK)
+		err = omap_mcpdm_playback_close(&mcpdm_links[stream]);
+	else
+		err = omap_mcpdm_capture_close(&mcpdm_links[stream]);
+
+	return err;
+}
+
+static struct snd_soc_dai_ops omap_mcpdm_dai_ops = {
+	.startup	= omap_mcpdm_dai_startup,
+	.shutdown	= omap_mcpdm_dai_shutdown,
+	.trigger	= omap_mcpdm_dai_trigger,
+	.hw_params	= omap_mcpdm_dai_hw_params,
+	.hw_free	= omap_mcpdm_dai_hw_free,
+};
+
+#define OMAP_MCPDM_RATES	(SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
+#define OMAP_MCPDM_FORMATS	(SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai omap_mcpdm_dai = {
+	.name = "omap-mcpdm",
+	.id = -1,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 4,
+		.rates = OMAP_MCPDM_RATES,
+		.formats = OMAP_MCPDM_FORMATS,
+	},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = OMAP_MCPDM_RATES,
+		.formats = OMAP_MCPDM_FORMATS,
+	},
+	.ops = &omap_mcpdm_dai_ops,
+	.private_data = &mcpdm_data,
+};
+EXPORT_SYMBOL_GPL(omap_mcpdm_dai);
+
+static int __init snd_omap_mcpdm_init(void)
+{
+	return snd_soc_register_dai(&omap_mcpdm_dai);
+}
+module_init(snd_omap_mcpdm_init);
+
+static void __exit snd_omap_mcpdm_exit(void)
+{
+	snd_soc_unregister_dai(&omap_mcpdm_dai);
+}
+module_exit(snd_omap_mcpdm_exit);
+
+MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
+MODULE_DESCRIPTION("OMAP PDM SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/omap-mcpdm.h b/sound/soc/omap/omap-mcpdm.h
new file mode 100644
index 0000000..73b80d5
--- /dev/null
+++ b/sound/soc/omap/omap-mcpdm.h
@@ -0,0 +1,29 @@
+/*
+ * omap-mcpdm.h
+ *
+ * Copyright (C) 2009 Texas Instruments
+ *
+ * Contact: Misael Lopez Cruz <x0052729@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __OMAP_MCPDM_H__
+#define __OMAP_MCPDM_H__
+
+extern struct snd_soc_dai omap_mcpdm_dai;
+
+#endif	/* End of __OMAP_MCPDM_H__ */
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 9db2770..825db38 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -37,7 +37,8 @@
 				  SNDRV_PCM_INFO_INTERLEAVED |
 				  SNDRV_PCM_INFO_PAUSE |
 				  SNDRV_PCM_INFO_RESUME,
-	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE |
+				  SNDRV_PCM_FMTBIT_S32_LE,
 	.period_bytes_min	= 32,
 	.period_bytes_max	= 64 * 1024,
 	.periods_min		= 2,
@@ -149,6 +150,7 @@
 	struct omap_runtime_data *prtd = runtime->private_data;
 	struct omap_pcm_dma_data *dma_data = prtd->dma_data;
 	struct omap_dma_channel_params dma_params;
+	int bytes;
 
 	/* return if this is a bufferless transfer e.g.
 	 * codec <--> BT codec or GSM modem -- lg FIXME */
@@ -156,11 +158,7 @@
 		return 0;
 
 	memset(&dma_params, 0, sizeof(dma_params));
-	/*
-	 * Note: Regardless of interface data formats supported by OMAP McBSP
-	 * or EAC blocks, internal representation is always fixed 16-bit/sample
-	 */
-	dma_params.data_type			= OMAP_DMA_DATA_TYPE_S16;
+	dma_params.data_type			= dma_data->data_type;
 	dma_params.trigger			= dma_data->dma_req;
 	dma_params.sync_mode			= dma_data->sync_mode;
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
@@ -170,6 +168,7 @@
 		dma_params.src_start		= runtime->dma_addr;
 		dma_params.dst_start		= dma_data->port_addr;
 		dma_params.dst_port		= OMAP_DMA_PORT_MPUI;
+		dma_params.dst_fi		= dma_data->packet_size;
 	} else {
 		dma_params.src_amode		= OMAP_DMA_AMODE_CONSTANT;
 		dma_params.dst_amode		= OMAP_DMA_AMODE_POST_INC;
@@ -177,6 +176,7 @@
 		dma_params.src_start		= dma_data->port_addr;
 		dma_params.dst_start		= runtime->dma_addr;
 		dma_params.src_port		= OMAP_DMA_PORT_MPUI;
+		dma_params.src_fi		= dma_data->packet_size;
 	}
 	/*
 	 * Set DMA transfer frame size equal to ALSA period size and frame
@@ -184,7 +184,8 @@
 	 * we can transfer the whole ALSA buffer with single DMA transfer but
 	 * still can get an interrupt at each period bounary
 	 */
-	dma_params.elem_count	= snd_pcm_lib_period_bytes(substream) / 2;
+	bytes = snd_pcm_lib_period_bytes(substream);
+	dma_params.elem_count	= bytes >> dma_data->data_type;
 	dma_params.frame_count	= runtime->periods;
 	omap_set_dma_params(prtd->dma_ch, &dma_params);
 
diff --git a/sound/soc/omap/omap-pcm.h b/sound/soc/omap/omap-pcm.h
index 38a821d..b19975d 100644
--- a/sound/soc/omap/omap-pcm.h
+++ b/sound/soc/omap/omap-pcm.h
@@ -29,8 +29,10 @@
 	char		*name;		/* stream identifier */
 	int		dma_req;	/* DMA request line */
 	unsigned long	port_addr;	/* transmit/receive register */
-	int		sync_mode;	/* DMA sync mode */
 	void (*set_threshold)(struct snd_pcm_substream *substream);
+	int		data_type;	/* data type 8,16,32 */
+	int		sync_mode;	/* DMA sync mode */
+	int		packet_size;	/* packet size only in PACKET mode */
 };
 
 extern struct snd_soc_platform omap_soc_platform;
diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c
index d88ad5c..240e097 100644
--- a/sound/soc/omap/omap3beagle.c
+++ b/sound/soc/omap/omap3beagle.c
@@ -117,11 +117,11 @@
 {
 	int ret;
 
-	if (!machine_is_omap3_beagle()) {
-		pr_debug("Not OMAP3 Beagle!\n");
+	if (!(machine_is_omap3_beagle() || machine_is_devkit8000())) {
+		pr_debug("Not OMAP3 Beagle or Devkit8000!\n");
 		return -ENODEV;
 	}
-	pr_info("OMAP3 Beagle SoC init\n");
+	pr_info("OMAP3 Beagle/Devkit8000 SoC init\n");
 
 	omap3beagle_snd_device = platform_device_alloc("soc-audio", -1);
 	if (!omap3beagle_snd_device) {
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c
index 68980c1..de10f76 100644
--- a/sound/soc/omap/omap3pandora.c
+++ b/sound/soc/omap/omap3pandora.c
@@ -23,6 +23,7 @@
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
 #include <linux/delay.h>
+#include <linux/regulator/consumer.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -40,6 +41,8 @@
 
 #define PREFIX "ASoC omap3pandora: "
 
+static struct regulator *omap3pandora_dac_reg;
+
 static int omap3pandora_cmn_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params, unsigned int fmt)
 {
@@ -106,17 +109,33 @@
 					  SND_SOC_DAIFMT_CBS_CFS);
 }
 
+static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *k, int event)
+{
+	/*
+	 * The PCM1773 DAC datasheet requires 1ms delay between switching
+	 * VCC power on/off and /PD pin high/low
+	 */
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		regulator_enable(omap3pandora_dac_reg);
+		mdelay(1);
+		gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1);
+	} else {
+		gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
+		mdelay(1);
+		regulator_disable(omap3pandora_dac_reg);
+	}
+
+	return 0;
+}
+
 static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *k, int event)
 {
-	if (SND_SOC_DAPM_EVENT_ON(event)) {
-		gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1);
+	if (SND_SOC_DAPM_EVENT_ON(event))
 		gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1);
-	} else {
+	else
 		gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0);
-		mdelay(1);
-		gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0);
-	}
 
 	return 0;
 }
@@ -130,7 +149,9 @@
  *  |P| <--- TWL4030 <--------- Line In and MICs
  */
 static const struct snd_soc_dapm_widget omap3pandora_out_dapm_widgets[] = {
-	SND_SOC_DAPM_DAC("PCM DAC", "HiFi Playback", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM,
+			   0, 0, omap3pandora_dac_event,
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 	SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM,
 			   0, 0, NULL, 0, omap3pandora_hp_event,
 			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -306,8 +327,18 @@
 		goto fail2;
 	}
 
+	omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc");
+	if (IS_ERR(omap3pandora_dac_reg)) {
+		pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n",
+			dev_name(&omap3pandora_snd_device->dev),
+			PTR_ERR(omap3pandora_dac_reg));
+		goto fail3;
+	}
+
 	return 0;
 
+fail3:
+	platform_device_del(omap3pandora_snd_device);
 fail2:
 	platform_device_put(omap3pandora_snd_device);
 fail1:
@@ -320,6 +351,7 @@
 
 static void __exit omap3pandora_soc_exit(void)
 {
+	regulator_put(omap3pandora_dac_reg);
 	platform_device_unregister(omap3pandora_snd_device);
 	gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO);
 	gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO);
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 3bd7712..e69397f 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -135,10 +135,11 @@
 	struct ssp_priv *priv = cpu_dai->private_data;
 
 	if (!cpu_dai->active)
-		return 0;
+		clk_enable(priv->dev.ssp->clk);
 
 	ssp_save_state(&priv->dev, &priv->state);
 	clk_disable(priv->dev.ssp->clk);
+
 	return 0;
 }
 
@@ -146,12 +147,13 @@
 {
 	struct ssp_priv *priv = cpu_dai->private_data;
 
-	if (!cpu_dai->active)
-		return 0;
-
 	clk_enable(priv->dev.ssp->clk);
 	ssp_restore_state(&priv->dev, &priv->state);
-	ssp_enable(&priv->dev);
+
+	if (cpu_dai->active)
+		ssp_enable(&priv->dev);
+	else
+		clk_disable(priv->dev.ssp->clk);
 
 	return 0;
 }
diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c
index acfce1c..7e3f416 100644
--- a/sound/soc/pxa/raumfeld.c
+++ b/sound/soc/pxa/raumfeld.c
@@ -41,7 +41,9 @@
 };
 
 #define MAX9485_MCLK_FREQ_112896 0x22
-#define	MAX9485_MCLK_FREQ_122880 0x23
+#define MAX9485_MCLK_FREQ_122880 0x23
+#define MAX9485_MCLK_FREQ_225792 0x32
+#define MAX9485_MCLK_FREQ_245760 0x33
 
 static void set_max9485_clk(char clk)
 {
@@ -71,9 +73,17 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 
-	set_max9485_clk(MAX9485_MCLK_FREQ_112896);
+	/* set freq to 0 to enable all possible codec sample rates */
+	return snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0);
+}
 
-	return snd_soc_dai_set_sysclk(codec_dai, 0, 11289600, 0);
+static void raumfeld_cs4270_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+	/* set freq to 0 to enable all possible codec sample rates */
+	snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0);
 }
 
 static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream,
@@ -86,20 +96,24 @@
 	int ret = 0;
 
 	switch (params_rate(params)) {
-	case 8000:
-	case 16000:
-	case 48000:
-	case 96000:
-		set_max9485_clk(MAX9485_MCLK_FREQ_122880);
-		clk = 12288000;
-		break;
-	case 11025:
-	case 22050:
 	case 44100:
-	case 88200:
 		set_max9485_clk(MAX9485_MCLK_FREQ_112896);
 		clk = 11289600;
 		break;
+	case 48000:
+		set_max9485_clk(MAX9485_MCLK_FREQ_122880);
+		clk = 12288000;
+		break;
+	case 88200:
+		set_max9485_clk(MAX9485_MCLK_FREQ_225792);
+		clk = 22579200;
+		break;
+	case 96000:
+		set_max9485_clk(MAX9485_MCLK_FREQ_245760);
+		clk = 24576000;
+		break;
+	default:
+		return -EINVAL;
 	}
 
 	fmt = SND_SOC_DAIFMT_I2S |
@@ -128,7 +142,7 @@
 	if (ret < 0)
 		return ret;
 
-	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1);
+	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1);
 	if (ret < 0)
 		return ret;
 
@@ -137,6 +151,7 @@
 
 static struct snd_soc_ops raumfeld_cs4270_ops = {
 	.startup = raumfeld_cs4270_startup,
+	.shutdown = raumfeld_cs4270_shutdown,
 	.hw_params = raumfeld_cs4270_hw_params,
 };
 
@@ -181,20 +196,24 @@
 	int fmt, ret = 0, clk = 0;
 
 	switch (params_rate(params)) {
-	case 8000:
-	case 16000:
-	case 48000:
-	case 96000:
-		set_max9485_clk(MAX9485_MCLK_FREQ_122880);
-		clk = 12288000;
-		break;
-	case 11025:
-	case 22050:
 	case 44100:
-	case 88200:
 		set_max9485_clk(MAX9485_MCLK_FREQ_112896);
 		clk = 11289600;
 		break;
+	case 48000:
+		set_max9485_clk(MAX9485_MCLK_FREQ_122880);
+		clk = 12288000;
+		break;
+	case 88200:
+		set_max9485_clk(MAX9485_MCLK_FREQ_225792);
+		clk = 22579200;
+		break;
+	case 96000:
+		set_max9485_clk(MAX9485_MCLK_FREQ_245760);
+		clk = 24576000;
+		break;
+	default:
+		return -EINVAL;
 	}
 
 	fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF;
@@ -217,7 +236,7 @@
 	if (ret < 0)
 		return ret;
 
-	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, 0, 1);
+	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1);
 	if (ret < 0)
 		return ret;
 
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index b489f1a..15fe57e 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -27,12 +27,10 @@
 config SND_S3C_SOC_PCM
 	tristate
 
-config SND_S3C2443_SOC_AC97
+config SND_S3C_SOC_AC97
 	tristate
-	select S3C2410_DMA
-	select AC97_BUS
 	select SND_SOC_AC97_BUS
-	
+
 config SND_S3C24XX_SOC_NEO1973_WM8753
 	tristate "SoC I2S Audio support for NEO1973 - WM8753"
 	depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA01
@@ -71,8 +69,10 @@
 config SND_S3C24XX_SOC_SMDK2443_WM9710
 	tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
 	depends on SND_S3C24XX_SOC && MACH_SMDK2443
-	select SND_S3C2443_SOC_AC97
+	select S3C2410_DMA
+	select AC97_BUS
 	select SND_SOC_AC97_CODEC
+	select SND_S3C_SOC_AC97
 	help
 	  Say Y if you want to add support for SoC audio on smdk2443
 	  with the WM9710.
@@ -80,8 +80,10 @@
 config SND_S3C24XX_SOC_LN2440SBC_ALC650
 	tristate "SoC AC97 Audio support for LN2440SBC - ALC650"
 	depends on SND_S3C24XX_SOC && ARCH_S3C2410
-	select SND_S3C2443_SOC_AC97
+	select S3C2410_DMA
+	select AC97_BUS
 	select SND_SOC_AC97_CODEC
+	select SND_S3C_SOC_AC97
 	help
 	  Say Y if you want to add support for SoC audio on ln2440sbc
 	  with the ALC650.
@@ -111,3 +113,11 @@
 	select SND_S3C24XX_SOC_I2S
 	select SND_SOC_TLV320AIC3X
 	select SND_S3C24XX_SOC_SIMTEC
+
+config SND_SOC_SMDK_WM9713
+	tristate "SoC AC97 Audio support for SMDK with WM9713"
+	depends on SND_S3C24XX_SOC && MACH_SMDK6410
+	select SND_SOC_WM9713
+	select SND_S3C_SOC_AC97
+	help
+	  Sat Y if you want to add support for SoC audio on the SMDK.
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index b744657..df071a3 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -3,13 +3,13 @@
 snd-soc-s3c24xx-i2s-objs := s3c24xx-i2s.o
 snd-soc-s3c2412-i2s-objs := s3c2412-i2s.o
 snd-soc-s3c64xx-i2s-objs := s3c64xx-i2s.o
-snd-soc-s3c2443-ac97-objs := s3c2443-ac97.o
+snd-soc-s3c-ac97-objs := s3c-ac97.o
 snd-soc-s3c-i2s-v2-objs := s3c-i2s-v2.o
 snd-soc-s3c-pcm-objs := s3c-pcm.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC) += snd-soc-s3c24xx.o
 obj-$(CONFIG_SND_S3C24XX_SOC_I2S) += snd-soc-s3c24xx-i2s.o
-obj-$(CONFIG_SND_S3C2443_SOC_AC97) += snd-soc-s3c2443-ac97.o
+obj-$(CONFIG_SND_S3C_SOC_AC97) += snd-soc-s3c-ac97.o
 obj-$(CONFIG_SND_S3C2412_SOC_I2S) += snd-soc-s3c2412-i2s.o
 obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += snd-soc-s3c64xx-i2s.o
 obj-$(CONFIG_SND_S3C_I2SV2_SOC) += snd-soc-s3c-i2s-v2.o
@@ -26,6 +26,7 @@
 snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
 snd-soc-s3c24xx-simtec-tlv320aic23-objs := s3c24xx_simtec_tlv320aic23.o
 snd-soc-smdk64xx-wm8580-objs := smdk64xx_wm8580.o
+snd-soc-smdk-wm9713-objs := smdk_wm9713.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -37,4 +38,4 @@
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_HERMES) += snd-soc-s3c24xx-simtec-hermes.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC_TLV320AIC23) += snd-soc-s3c24xx-simtec-tlv320aic23.o
 obj-$(CONFIG_SND_S3C64XX_SOC_WM8580) += snd-soc-smdk64xx-wm8580.o
-
+obj-$(CONFIG_SND_SOC_SMDK_WM9713) += snd-soc-smdk-wm9713.o
diff --git a/sound/soc/s3c24xx/ln2440sbc_alc650.c b/sound/soc/s3c24xx/ln2440sbc_alc650.c
index d00d359..ffa954f 100644
--- a/sound/soc/s3c24xx/ln2440sbc_alc650.c
+++ b/sound/soc/s3c24xx/ln2440sbc_alc650.c
@@ -25,7 +25,7 @@
 
 #include "../codecs/ac97.h"
 #include "s3c-dma.h"
-#include "s3c24xx-ac97.h"
+#include "s3c-ac97.h"
 
 static struct snd_soc_card ln2440sbc;
 
@@ -33,7 +33,7 @@
 {
 	.name = "AC97",
 	.stream_name = "AC97 HiFi",
-	.cpu_dai = &s3c2443_ac97_dai[0],
+	.cpu_dai = &s3c_ac97_dai[S3C_AC97_DAI_PCM],
 	.codec_dai = &ac97_dai,
 },
 };
diff --git a/sound/soc/s3c24xx/s3c-ac97.c b/sound/soc/s3c24xx/s3c-ac97.c
new file mode 100644
index 0000000..ee8ed9d7
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-ac97.c
@@ -0,0 +1,518 @@
+/* sound/soc/s3c24xx/s3c-ac97.c
+ *
+ * ALSA SoC Audio Layer - S3C AC97 Controller driver
+ * 	Evolved from s3c2443-ac97.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co. Ltd
+ * 	Author: Jaswinder Singh <jassi.brar@samsung.com>
+ * 	Credits: Graeme Gregory, Sean Choi
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <sound/soc.h>
+
+#include <plat/regs-ac97.h>
+#include <mach/dma.h>
+#include <plat/audio.h>
+
+#include "s3c-dma.h"
+#include "s3c-ac97.h"
+
+#define AC_CMD_ADDR(x) (x << 16)
+#define AC_CMD_DATA(x) (x & 0xffff)
+
+struct s3c_ac97_info {
+	unsigned           state;
+	struct clk         *ac97_clk;
+	void __iomem	   *regs;
+	struct mutex       lock;
+	struct completion  done;
+};
+static struct s3c_ac97_info s3c_ac97;
+
+static struct s3c2410_dma_client s3c_dma_client_out = {
+	.name = "AC97 PCMOut"
+};
+
+static struct s3c2410_dma_client s3c_dma_client_in = {
+	.name = "AC97 PCMIn"
+};
+
+static struct s3c2410_dma_client s3c_dma_client_micin = {
+	.name = "AC97 MicIn"
+};
+
+static struct s3c_dma_params s3c_ac97_pcm_out = {
+	.client		= &s3c_dma_client_out,
+	.dma_size	= 4,
+};
+
+static struct s3c_dma_params s3c_ac97_pcm_in = {
+	.client		= &s3c_dma_client_in,
+	.dma_size	= 4,
+};
+
+static struct s3c_dma_params s3c_ac97_mic_in = {
+	.client		= &s3c_dma_client_micin,
+	.dma_size	= 4,
+};
+
+static void s3c_ac97_activate(struct snd_ac97 *ac97)
+{
+	u32 ac_glbctrl, stat;
+
+	stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
+	if (stat == S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE)
+		return; /* Return if already active */
+
+	INIT_COMPLETION(s3c_ac97.done);
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
+		printk(KERN_ERR "AC97: Unable to activate!");
+}
+
+static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
+	unsigned short reg)
+{
+	u32 ac_glbctrl, ac_codec_cmd;
+	u32 stat, addr, data;
+
+	mutex_lock(&s3c_ac97.lock);
+
+	s3c_ac97_activate(ac97);
+
+	INIT_COMPLETION(s3c_ac97.done);
+
+	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
+	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	udelay(50);
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
+		printk(KERN_ERR "AC97: Unable to read!");
+
+	stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
+	addr = (stat >> 16) & 0x7f;
+	data = (stat & 0xffff);
+
+	if (addr != reg)
+		printk(KERN_ERR "s3c-ac97: req addr = %02x, rep addr = %02x\n", reg, addr);
+
+	mutex_unlock(&s3c_ac97.lock);
+
+	return (unsigned short)data;
+}
+
+static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+	unsigned short val)
+{
+	u32 ac_glbctrl, ac_codec_cmd;
+
+	mutex_lock(&s3c_ac97.lock);
+
+	s3c_ac97_activate(ac97);
+
+	INIT_COMPLETION(s3c_ac97.done);
+
+	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
+	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	udelay(50);
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
+		printk(KERN_ERR "AC97: Unable to write!");
+
+	ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+	ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
+	writel(ac_codec_cmd, s3c_ac97.regs + S3C_AC97_CODEC_CMD);
+
+	mutex_unlock(&s3c_ac97.lock);
+}
+
+static void s3c_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+	writel(S3C_AC97_GLBCTRL_COLDRESET,
+			s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+}
+
+static void s3c_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+	u32 stat;
+
+	stat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT) & 0x7;
+	if (stat == S3C_AC97_GLBSTAT_MAINSTATE_ACTIVE)
+		return; /* Return if already active */
+
+	writel(S3C_AC97_GLBCTRL_WARMRESET, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	writel(0, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	msleep(1);
+
+	s3c_ac97_activate(ac97);
+}
+
+static irqreturn_t s3c_ac97_irq(int irq, void *dev_id)
+{
+	u32 ac_glbctrl, ac_glbstat;
+
+	ac_glbstat = readl(s3c_ac97.regs + S3C_AC97_GLBSTAT);
+
+	if (ac_glbstat & S3C_AC97_GLBSTAT_CODECREADY) {
+
+		ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
+		writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+		complete(&s3c_ac97.done);
+	}
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl |= (1<<30); /* Clear interrupt */
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	return IRQ_HANDLED;
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read       = s3c_ac97_read,
+	.write      = s3c_ac97_write,
+	.warm_reset = s3c_ac97_warm_reset,
+	.reset      = s3c_ac97_cold_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int s3c_ac97_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params,
+				  struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		cpu_dai->dma_data = &s3c_ac97_pcm_out;
+	else
+		cpu_dai->dma_data = &s3c_ac97_pcm_in;
+
+	return 0;
+}
+
+static int s3c_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	u32 ac_glbctrl;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int channel = ((struct s3c_dma_params *)
+		  rtd->dai->cpu_dai->dma_data)->channel;
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
+	else
+		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
+		else
+			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		break;
+	}
+
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
+	return 0;
+}
+
+static int s3c_ac97_hw_mic_params(struct snd_pcm_substream *substream,
+				      struct snd_pcm_hw_params *params,
+				      struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return -ENODEV;
+	else
+		cpu_dai->dma_data = &s3c_ac97_mic_in;
+
+	return 0;
+}
+
+static int s3c_ac97_mic_trigger(struct snd_pcm_substream *substream,
+				    int cmd, struct snd_soc_dai *dai)
+{
+	u32 ac_glbctrl;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int channel = ((struct s3c_dma_params *)
+		  rtd->dai->cpu_dai->dma_data)->channel;
+
+	ac_glbctrl = readl(s3c_ac97.regs + S3C_AC97_GLBCTRL);
+	ac_glbctrl &= ~S3C_AC97_GLBCTRL_MICINTM_MASK;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ac_glbctrl |= S3C_AC97_GLBCTRL_MICINTM_DMA;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		break;
+	}
+
+	writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
+
+	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops s3c_ac97_dai_ops = {
+	.hw_params	= s3c_ac97_hw_params,
+	.trigger	= s3c_ac97_trigger,
+};
+
+static struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
+	.hw_params	= s3c_ac97_hw_mic_params,
+	.trigger	= s3c_ac97_mic_trigger,
+};
+
+struct snd_soc_dai s3c_ac97_dai[] = {
+	[S3C_AC97_DAI_PCM] = {
+		.name =	"s3c-ac97",
+		.id = S3C_AC97_DAI_PCM,
+		.ac97_control = 1,
+		.playback = {
+			.stream_name = "AC97 Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.capture = {
+			.stream_name = "AC97 Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.ops = &s3c_ac97_dai_ops,
+	},
+	[S3C_AC97_DAI_MIC] = {
+		.name = "s3c-ac97-mic",
+		.id = S3C_AC97_DAI_MIC,
+		.ac97_control = 1,
+		.capture = {
+			.stream_name = "AC97 Mic Capture",
+			.channels_min = 1,
+			.channels_max = 1,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.ops = &s3c_ac97_mic_dai_ops,
+	},
+};
+EXPORT_SYMBOL_GPL(s3c_ac97_dai);
+
+static __devinit int s3c_ac97_probe(struct platform_device *pdev)
+{
+	struct resource *mem_res, *dmatx_res, *dmarx_res, *dmamic_res, *irq_res;
+	struct s3c_audio_pdata *ac97_pdata;
+	int ret;
+
+	ac97_pdata = pdev->dev.platform_data;
+	if (!ac97_pdata || !ac97_pdata->cfg_gpio) {
+		dev_err(&pdev->dev, "cfg_gpio callback not provided!\n");
+		return -EINVAL;
+	}
+
+	/* Check for availability of necessary resource */
+	dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!dmatx_res) {
+		dev_err(&pdev->dev, "Unable to get AC97-TX dma resource\n");
+		return -ENXIO;
+	}
+
+	dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (!dmarx_res) {
+		dev_err(&pdev->dev, "Unable to get AC97-RX dma resource\n");
+		return -ENXIO;
+	}
+
+	dmamic_res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
+	if (!dmamic_res) {
+		dev_err(&pdev->dev, "Unable to get AC97-MIC dma resource\n");
+		return -ENXIO;
+	}
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem_res) {
+		dev_err(&pdev->dev, "Unable to get register resource\n");
+		return -ENXIO;
+	}
+
+	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq_res) {
+		dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
+		return -ENXIO;
+	}
+
+	if (!request_mem_region(mem_res->start,
+				resource_size(mem_res), "s3c-ac97")) {
+		dev_err(&pdev->dev, "Unable to request register region\n");
+		return -EBUSY;
+	}
+
+	s3c_ac97_pcm_out.channel = dmatx_res->start;
+	s3c_ac97_pcm_out.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
+	s3c_ac97_pcm_in.channel = dmarx_res->start;
+	s3c_ac97_pcm_in.dma_addr = mem_res->start + S3C_AC97_PCM_DATA;
+	s3c_ac97_mic_in.channel = dmamic_res->start;
+	s3c_ac97_mic_in.dma_addr = mem_res->start + S3C_AC97_MIC_DATA;
+
+	init_completion(&s3c_ac97.done);
+	mutex_init(&s3c_ac97.lock);
+
+	s3c_ac97.regs = ioremap(mem_res->start, resource_size(mem_res));
+	if (s3c_ac97.regs == NULL) {
+		dev_err(&pdev->dev, "Unable to ioremap register region\n");
+		ret = -ENXIO;
+		goto err1;
+	}
+
+	s3c_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
+	if (IS_ERR(s3c_ac97.ac97_clk)) {
+		dev_err(&pdev->dev, "s3c-ac97 failed to get ac97_clock\n");
+		ret = -ENODEV;
+		goto err2;
+	}
+	clk_enable(s3c_ac97.ac97_clk);
+
+	if (ac97_pdata->cfg_gpio(pdev)) {
+		dev_err(&pdev->dev, "Unable to configure gpio\n");
+		ret = -EINVAL;
+		goto err3;
+	}
+
+	ret = request_irq(irq_res->start, s3c_ac97_irq,
+					IRQF_DISABLED, "AC97", NULL);
+	if (ret < 0) {
+		printk(KERN_ERR "s3c-ac97: interrupt request failed.\n");
+		goto err4;
+	}
+
+	s3c_ac97_dai[S3C_AC97_DAI_PCM].dev = &pdev->dev;
+	s3c_ac97_dai[S3C_AC97_DAI_MIC].dev = &pdev->dev;
+
+	ret = snd_soc_register_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
+	if (ret)
+		goto err5;
+
+	return 0;
+
+err5:
+	free_irq(irq_res->start, NULL);
+err4:
+err3:
+	clk_disable(s3c_ac97.ac97_clk);
+	clk_put(s3c_ac97.ac97_clk);
+err2:
+	iounmap(s3c_ac97.regs);
+err1:
+	release_mem_region(mem_res->start, resource_size(mem_res));
+
+	return ret;
+}
+
+static __devexit int s3c_ac97_remove(struct platform_device *pdev)
+{
+	struct resource *mem_res, *irq_res;
+
+	snd_soc_unregister_dais(s3c_ac97_dai, ARRAY_SIZE(s3c_ac97_dai));
+
+	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (irq_res)
+		free_irq(irq_res->start, NULL);
+
+	clk_disable(s3c_ac97.ac97_clk);
+	clk_put(s3c_ac97.ac97_clk);
+
+	iounmap(s3c_ac97.regs);
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (mem_res)
+		release_mem_region(mem_res->start, resource_size(mem_res));
+
+	return 0;
+}
+
+static struct platform_driver s3c_ac97_driver = {
+	.probe  = s3c_ac97_probe,
+	.remove = s3c_ac97_remove,
+	.driver = {
+		.name = "s3c-ac97",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init s3c_ac97_init(void)
+{
+	return platform_driver_register(&s3c_ac97_driver);
+}
+module_init(s3c_ac97_init);
+
+static void __exit s3c_ac97_exit(void)
+{
+	platform_driver_unregister(&s3c_ac97_driver);
+}
+module_exit(s3c_ac97_exit);
+
+MODULE_AUTHOR("Jaswinder Singh, <jassi.brar@samsung.com>");
+MODULE_DESCRIPTION("AC97 driver for the Samsung SoC");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c-ac97.h b/sound/soc/s3c24xx/s3c-ac97.h
new file mode 100644
index 0000000..2781983
--- /dev/null
+++ b/sound/soc/s3c24xx/s3c-ac97.h
@@ -0,0 +1,23 @@
+/* sound/soc/s3c24xx/s3c-ac97.h
+ *
+ * ALSA SoC Audio Layer - S3C AC97 Controller driver
+ * 	Evolved from s3c2443-ac97.h
+ *
+ * Copyright (c) 2010 Samsung Electronics Co. Ltd
+ * 	Author: Jaswinder Singh <jassi.brar@samsung.com>
+ * 	Credits: Graeme Gregory, Sean Choi
+ *
+ * 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 __S3C_AC97_H_
+#define __S3C_AC97_H_
+
+#define S3C_AC97_DAI_PCM 0
+#define S3C_AC97_DAI_MIC 1
+
+extern struct snd_soc_dai s3c_ac97_dai[];
+
+#endif /* __S3C_AC97_H_ */
diff --git a/sound/soc/s3c24xx/s3c-pcm.c b/sound/soc/s3c24xx/s3c-pcm.c
index 9e61a7c..a98f40c 100644
--- a/sound/soc/s3c24xx/s3c-pcm.c
+++ b/sound/soc/s3c24xx/s3c-pcm.c
@@ -229,8 +229,7 @@
 
 	spin_unlock_irqrestore(&pcm->lock, flags);
 
-	dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs \
-				SCLK_DIV=%d SYNC_DIV=%d\n",
+	dev_dbg(pcm->dev, "PCMSOURCE_CLK-%lu SCLK=%ufs SCLK_DIV=%d SYNC_DIV=%d\n",
 				clk_get_rate(clk), pcm->sclk_per_fs,
 				sclk_div, sync_div);
 
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
deleted file mode 100644
index 0191e3a..0000000
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * s3c2443-ac97.c  --  ALSA Soc Audio Layer
- *
- * (c) 2007 Wolfson Microelectronics PLC.
- * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- *  Copyright (C) 2005, Sean Choi <sh428.choi@samsung.com>
- *  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.
- */
-
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/wait.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/clk.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/ac97_codec.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-
-#include <mach/hardware.h>
-#include <plat/regs-ac97.h>
-#include <mach/regs-gpio.h>
-#include <mach/regs-clock.h>
-#include <asm/dma.h>
-#include <mach/dma.h>
-
-#include "s3c-dma.h"
-#include "s3c24xx-ac97.h"
-
-struct s3c24xx_ac97_info {
-	void __iomem	*regs;
-	struct clk	*ac97_clk;
-};
-static struct s3c24xx_ac97_info s3c24xx_ac97;
-
-static DECLARE_COMPLETION(ac97_completion);
-static u32 codec_ready;
-static DEFINE_MUTEX(ac97_mutex);
-
-static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97,
-	unsigned short reg)
-{
-	u32 ac_glbctrl;
-	u32 ac_codec_cmd;
-	u32 stat, addr, data;
-
-	mutex_lock(&ac97_mutex);
-
-	codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
-	ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
-	ac_codec_cmd = S3C_AC97_CODEC_CMD_READ | AC_CMD_ADDR(reg);
-	writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
-
-	udelay(50);
-
-	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-
-	wait_for_completion(&ac97_completion);
-
-	stat = readl(s3c24xx_ac97.regs + S3C_AC97_STAT);
-	addr = (stat >> 16) & 0x7f;
-	data = (stat & 0xffff);
-
-	if (addr != reg)
-		printk(KERN_ERR "s3c24xx-ac97: req addr = %02x,"
-				" rep addr = %02x\n", reg, addr);
-
-	mutex_unlock(&ac97_mutex);
-
-	return (unsigned short)data;
-}
-
-static void s3c2443_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
-	unsigned short val)
-{
-	u32 ac_glbctrl;
-	u32 ac_codec_cmd;
-
-	mutex_lock(&ac97_mutex);
-
-	codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
-	ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
-	ac_codec_cmd = AC_CMD_ADDR(reg) | AC_CMD_DATA(val);
-	writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
-
-	udelay(50);
-
-	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	ac_glbctrl |= S3C_AC97_GLBCTRL_CODECREADYIE;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-
-	wait_for_completion(&ac97_completion);
-
-	ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
-	ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
-	writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
-
-	mutex_unlock(&ac97_mutex);
-
-}
-
-static void s3c2443_ac97_warm_reset(struct snd_ac97 *ac97)
-{
-	u32 ac_glbctrl;
-
-	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	ac_glbctrl = S3C_AC97_GLBCTRL_WARMRESET;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	msleep(1);
-
-	ac_glbctrl = 0;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	msleep(1);
-}
-
-static void s3c2443_ac97_cold_reset(struct snd_ac97 *ac97)
-{
-	u32 ac_glbctrl;
-
-	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	msleep(1);
-
-	ac_glbctrl = 0;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	msleep(1);
-
-	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	msleep(1);
-
-	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	msleep(1);
-
-	ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA |
-		S3C_AC97_GLBCTRL_PCMINTM_DMA | S3C_AC97_GLBCTRL_MICINTM_DMA;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-}
-
-static irqreturn_t s3c2443_ac97_irq(int irq, void *dev_id)
-{
-	int status;
-	u32 ac_glbctrl;
-
-	status = readl(s3c24xx_ac97.regs + S3C_AC97_GLBSTAT) & codec_ready;
-
-	if (status) {
-		ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-		ac_glbctrl &= ~S3C_AC97_GLBCTRL_CODECREADYIE;
-		writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-		complete(&ac97_completion);
-	}
-	return IRQ_HANDLED;
-}
-
-struct snd_ac97_bus_ops soc_ac97_ops = {
-	.read	= s3c2443_ac97_read,
-	.write	= s3c2443_ac97_write,
-	.warm_reset	= s3c2443_ac97_warm_reset,
-	.reset	= s3c2443_ac97_cold_reset,
-};
-
-static struct s3c2410_dma_client s3c2443_dma_client_out = {
-	.name = "AC97 PCM Stereo out"
-};
-
-static struct s3c2410_dma_client s3c2443_dma_client_in = {
-	.name = "AC97 PCM Stereo in"
-};
-
-static struct s3c2410_dma_client s3c2443_dma_client_micin = {
-	.name = "AC97 Mic Mono in"
-};
-
-static struct s3c_dma_params s3c2443_ac97_pcm_stereo_out = {
-	.client		= &s3c2443_dma_client_out,
-	.channel	= DMACH_PCM_OUT,
-	.dma_addr	= S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
-	.dma_size	= 4,
-};
-
-static struct s3c_dma_params s3c2443_ac97_pcm_stereo_in = {
-	.client		= &s3c2443_dma_client_in,
-	.channel	= DMACH_PCM_IN,
-	.dma_addr	= S3C2440_PA_AC97 + S3C_AC97_PCM_DATA,
-	.dma_size	= 4,
-};
-
-static struct s3c_dma_params s3c2443_ac97_mic_mono_in = {
-	.client		= &s3c2443_dma_client_micin,
-	.channel	= DMACH_MIC_IN,
-	.dma_addr	= S3C2440_PA_AC97 + S3C_AC97_MIC_DATA,
-	.dma_size	= 4,
-};
-
-static int s3c2443_ac97_probe(struct platform_device *pdev,
-			      struct snd_soc_dai *dai)
-{
-	int ret;
-	u32 ac_glbctrl;
-
-	s3c24xx_ac97.regs = ioremap(S3C2440_PA_AC97, 0x100);
-	if (s3c24xx_ac97.regs == NULL)
-		return -ENXIO;
-
-	s3c24xx_ac97.ac97_clk = clk_get(&pdev->dev, "ac97");
-	if (s3c24xx_ac97.ac97_clk == NULL) {
-		printk(KERN_ERR "s3c2443-ac97 failed to get ac97_clock\n");
-		iounmap(s3c24xx_ac97.regs);
-		return -ENODEV;
-	}
-	clk_enable(s3c24xx_ac97.ac97_clk);
-
-	s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2443_GPE0_AC_nRESET);
-	s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2443_GPE1_AC_SYNC);
-	s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2443_GPE2_AC_BITCLK);
-	s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2443_GPE3_AC_SDI);
-	s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2443_GPE4_AC_SDO);
-
-	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	ac_glbctrl = S3C_AC97_GLBCTRL_COLDRESET;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	msleep(1);
-
-	ac_glbctrl = 0;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	msleep(1);
-
-	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	ac_glbctrl = S3C_AC97_GLBCTRL_ACLINKON;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	msleep(1);
-
-	ac_glbctrl |= S3C_AC97_GLBCTRL_TRANSFERDATAENABLE;
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-
-	ret = request_irq(IRQ_S3C244x_AC97, s3c2443_ac97_irq,
-		IRQF_DISABLED, "AC97", NULL);
-	if (ret < 0) {
-		printk(KERN_ERR "s3c24xx-ac97: interrupt request failed.\n");
-		clk_disable(s3c24xx_ac97.ac97_clk);
-		clk_put(s3c24xx_ac97.ac97_clk);
-		iounmap(s3c24xx_ac97.regs);
-	}
-	return ret;
-}
-
-static void s3c2443_ac97_remove(struct platform_device *pdev,
-				struct snd_soc_dai *dai)
-{
-	free_irq(IRQ_S3C244x_AC97, NULL);
-	clk_disable(s3c24xx_ac97.ac97_clk);
-	clk_put(s3c24xx_ac97.ac97_clk);
-	iounmap(s3c24xx_ac97.regs);
-}
-
-static int s3c2443_ac97_hw_params(struct snd_pcm_substream *substream,
-				  struct snd_pcm_hw_params *params,
-				  struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_out;
-	else
-		cpu_dai->dma_data = &s3c2443_ac97_pcm_stereo_in;
-
-	return 0;
-}
-
-static int s3c2443_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
-				struct snd_soc_dai *dai)
-{
-	u32 ac_glbctrl;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	int channel = ((struct s3c_dma_params *)
-		  rtd->dai->cpu_dai->dma_data)->channel;
-
-	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
-		else
-			ac_glbctrl |= S3C_AC97_GLBCTRL_PCMOUTTM_DMA;
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-			ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
-		else
-			ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMOUTTM_MASK;
-		break;
-	}
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-
-	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
-
-	return 0;
-}
-
-static int s3c2443_ac97_hw_mic_params(struct snd_pcm_substream *substream,
-				      struct snd_pcm_hw_params *params,
-				      struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		return -ENODEV;
-	else
-		cpu_dai->dma_data = &s3c2443_ac97_mic_mono_in;
-
-	return 0;
-}
-
-static int s3c2443_ac97_mic_trigger(struct snd_pcm_substream *substream,
-				    int cmd, struct snd_soc_dai *dai)
-{
-	u32 ac_glbctrl;
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	int channel = ((struct s3c_dma_params *)
-		  rtd->dai->cpu_dai->dma_data)->channel;
-
-	ac_glbctrl = readl(s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		ac_glbctrl |= S3C_AC97_GLBCTRL_PCMINTM_DMA;
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		ac_glbctrl &= ~S3C_AC97_GLBCTRL_PCMINTM_MASK;
-	}
-	writel(ac_glbctrl, s3c24xx_ac97.regs + S3C_AC97_GLBCTRL);
-
-	s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STARTED);
-
-	return 0;
-}
-
-#define s3c2443_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
-		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-
-static struct snd_soc_dai_ops s3c2443_ac97_dai_ops = {
-	.hw_params	= s3c2443_ac97_hw_params,
-	.trigger	= s3c2443_ac97_trigger,
-};
-
-static struct snd_soc_dai_ops s3c2443_ac97_mic_dai_ops = {
-	.hw_params	= s3c2443_ac97_hw_mic_params,
-	.trigger	= s3c2443_ac97_mic_trigger,
-};
-
-struct snd_soc_dai s3c2443_ac97_dai[] = {
-{
-	.name = "s3c2443-ac97",
-	.id = 0,
-	.ac97_control = 1,
-	.probe = s3c2443_ac97_probe,
-	.remove = s3c2443_ac97_remove,
-	.playback = {
-		.stream_name = "AC97 Playback",
-		.channels_min = 2,
-		.channels_max = 2,
-		.rates = s3c2443_AC97_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
-	.capture = {
-		.stream_name = "AC97 Capture",
-		.channels_min = 2,
-		.channels_max = 2,
-		.rates = s3c2443_AC97_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
-	.ops = &s3c2443_ac97_dai_ops,
-},
-{
-	.name = "pxa2xx-ac97-mic",
-	.id = 1,
-	.ac97_control = 1,
-	.capture = {
-		.stream_name = "AC97 Mic Capture",
-		.channels_min = 1,
-		.channels_max = 1,
-		.rates = s3c2443_AC97_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
-	.ops = &s3c2443_ac97_mic_dai_ops,
-},
-};
-EXPORT_SYMBOL_GPL(s3c2443_ac97_dai);
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
-
-static int __init s3c2443_ac97_init(void)
-{
-	return snd_soc_register_dais(s3c2443_ac97_dai,
-				     ARRAY_SIZE(s3c2443_ac97_dai));
-}
-module_init(s3c2443_ac97_init);
-
-static void __exit s3c2443_ac97_exit(void)
-{
-	snd_soc_unregister_dais(s3c2443_ac97_dai,
-				ARRAY_SIZE(s3c2443_ac97_dai));
-}
-module_exit(s3c2443_ac97_exit);
-
-
-MODULE_AUTHOR("Graeme Gregory");
-MODULE_DESCRIPTION("AC97 driver for the Samsung s3c2443 chip");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/s3c24xx-ac97.h b/sound/soc/s3c24xx/s3c24xx-ac97.h
deleted file mode 100644
index e96f941..0000000
--- a/sound/soc/s3c24xx/s3c24xx-ac97.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * s3c24xx-ac97.c  --  ALSA Soc Audio Layer
- *
- * (c) 2007 Wolfson Microelectronics PLC.
- * Author: Graeme Gregory
- *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- *  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.
- *
- *  Revision history
- *    10th Nov 2006   Initial version.
- */
-
-#ifndef S3C24XXAC97_H_
-#define S3C24XXAC97_H_
-
-#define AC_CMD_ADDR(x) (x << 16)
-#define AC_CMD_DATA(x) (x & 0xffff)
-
-extern struct snd_soc_dai s3c2443_ac97_dai[];
-
-#endif /*S3C24XXAC97_H_*/
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c
index cc7edb5..93ed3aa 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.c
@@ -15,16 +15,10 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/device.h>
-#include <linux/delay.h>
 #include <linux/clk.h>
-#include <linux/kernel.h>
 #include <linux/gpio.h>
 #include <linux/io.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
 
 #include <plat/regs-s3c2412-iis.h>
@@ -38,6 +32,11 @@
 #include "s3c-dma.h"
 #include "s3c64xx-i2s.h"
 
+/* The value should be set to maximum of the total number
+ * of I2Sv3 controllers that any supported SoC has.
+ */
+#define MAX_I2SV3	2
+
 static struct s3c2410_dma_client s3c64xx_dma_client_out = {
 	.name		= "I2S PCM Stereo out"
 };
@@ -46,37 +45,12 @@
 	.name		= "I2S PCM Stereo in"
 };
 
-static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_out[2] = {
-	[0] = {
-		.channel	= DMACH_I2S0_OUT,
-		.client		= &s3c64xx_dma_client_out,
-		.dma_addr	= S3C64XX_PA_IIS0 + S3C2412_IISTXD,
-		.dma_size	= 4,
-	},
-	[1] = {
-		.channel	= DMACH_I2S1_OUT,
-		.client		= &s3c64xx_dma_client_out,
-		.dma_addr	= S3C64XX_PA_IIS1 + S3C2412_IISTXD,
-		.dma_size	= 4,
-	},
-};
+static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_out[MAX_I2SV3];
+static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[MAX_I2SV3];
+static struct s3c_i2sv2_info s3c64xx_i2s[MAX_I2SV3];
 
-static struct s3c_dma_params s3c64xx_i2s_pcm_stereo_in[2] = {
-	[0] = {
-		.channel	= DMACH_I2S0_IN,
-		.client		= &s3c64xx_dma_client_in,
-		.dma_addr	= S3C64XX_PA_IIS0 + S3C2412_IISRXD,
-		.dma_size	= 4,
-	},
-	[1] = {
-		.channel	= DMACH_I2S1_IN,
-		.client		= &s3c64xx_dma_client_in,
-		.dma_addr	= S3C64XX_PA_IIS1 + S3C2412_IISRXD,
-		.dma_size	= 4,
-	},
-};
-
-static struct s3c_i2sv2_info s3c64xx_i2s[2];
+struct snd_soc_dai s3c64xx_i2s_dai[MAX_I2SV3];
+EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai);
 
 static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
 {
@@ -169,55 +143,13 @@
 	.set_sysclk	= s3c64xx_i2s_set_sysclk,	
 };
 
-struct snd_soc_dai s3c64xx_i2s_dai[] = {
-	{
-		.name		= "s3c64xx-i2s",
-		.id		= 0,
-		.probe		= s3c64xx_i2s_probe,
-		.playback = {
-			.channels_min	= 2,
-			.channels_max	= 2,
-			.rates		= S3C64XX_I2S_RATES,
-			.formats	= S3C64XX_I2S_FMTS,
-		},
-		.capture = {
-			 .channels_min	= 2,
-			 .channels_max	= 2,
-			 .rates		= S3C64XX_I2S_RATES,
-			 .formats	= S3C64XX_I2S_FMTS,
-		 },
-		.ops = &s3c64xx_i2s_dai_ops,
-		.symmetric_rates = 1,
-	},
-	{
-		.name		= "s3c64xx-i2s",
-		.id		= 1,
-		.probe		= s3c64xx_i2s_probe,
-		.playback = {
-			.channels_min	= 2,
-			.channels_max	= 2,
-			.rates		= S3C64XX_I2S_RATES,
-			.formats	= S3C64XX_I2S_FMTS,
-		},
-		.capture = {
-			 .channels_min	= 2,
-			 .channels_max	= 2,
-			 .rates		= S3C64XX_I2S_RATES,
-			 .formats	= S3C64XX_I2S_FMTS,
-		 },
-		.ops = &s3c64xx_i2s_dai_ops,
-		.symmetric_rates = 1,
-	},
-};
-EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai);
-
 static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev)
 {
 	struct s3c_i2sv2_info *i2s;
 	struct snd_soc_dai *dai;
 	int ret;
 
-	if (pdev->id >= ARRAY_SIZE(s3c64xx_i2s)) {
+	if (pdev->id >= MAX_I2SV3) {
 		dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
 		return -EINVAL;
 	}
@@ -225,10 +157,40 @@
 	i2s = &s3c64xx_i2s[pdev->id];
 	dai = &s3c64xx_i2s_dai[pdev->id];
 	dai->dev = &pdev->dev;
+	dai->name = "s3c64xx-i2s";
+	dai->id = pdev->id;
+	dai->symmetric_rates = 1;
+	dai->playback.channels_min = 2;
+	dai->playback.channels_max = 2;
+	dai->playback.rates = S3C64XX_I2S_RATES;
+	dai->playback.formats = S3C64XX_I2S_FMTS;
+	dai->capture.channels_min = 2;
+	dai->capture.channels_max = 2;
+	dai->capture.rates = S3C64XX_I2S_RATES;
+	dai->capture.formats = S3C64XX_I2S_FMTS;
+	dai->probe = s3c64xx_i2s_probe;
+	dai->ops = &s3c64xx_i2s_dai_ops;
 
 	i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id];
 	i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id];
 
+	if (pdev->id == 0) {
+		i2s->dma_capture->channel = DMACH_I2S0_IN;
+		i2s->dma_capture->dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISRXD;
+		i2s->dma_playback->channel = DMACH_I2S0_OUT;
+		i2s->dma_playback->dma_addr = S3C64XX_PA_IIS0 + S3C2412_IISTXD;
+	} else {
+		i2s->dma_capture->channel = DMACH_I2S1_IN;
+		i2s->dma_capture->dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISRXD;
+		i2s->dma_playback->channel = DMACH_I2S1_OUT;
+		i2s->dma_playback->dma_addr = S3C64XX_PA_IIS1 + S3C2412_IISTXD;
+	}
+
+	i2s->dma_capture->client = &s3c64xx_dma_client_in;
+	i2s->dma_capture->dma_size = 4;
+	i2s->dma_playback->client = &s3c64xx_dma_client_out;
+	i2s->dma_playback->dma_size = 4;
+
 	i2s->iis_cclk = clk_get(&pdev->dev, "audio-bus");
 	if (IS_ERR(i2s->iis_cclk)) {
 		dev_err(&pdev->dev, "failed to get audio-bus\n");
diff --git a/sound/soc/s3c24xx/smdk2443_wm9710.c b/sound/soc/s3c24xx/smdk2443_wm9710.c
index 12b783b1..3622588 100644
--- a/sound/soc/s3c24xx/smdk2443_wm9710.c
+++ b/sound/soc/s3c24xx/smdk2443_wm9710.c
@@ -21,7 +21,7 @@
 
 #include "../codecs/ac97.h"
 #include "s3c-dma.h"
-#include "s3c24xx-ac97.h"
+#include "s3c-ac97.h"
 
 static struct snd_soc_card smdk2443;
 
@@ -29,7 +29,7 @@
 {
 	.name = "AC97",
 	.stream_name = "AC97 HiFi",
-	.cpu_dai = &s3c2443_ac97_dai[0],
+	.cpu_dai = &s3c_ac97_dai[S3C_AC97_DAI_PCM],
 	.codec_dai = &ac97_dai,
 },
 };
diff --git a/sound/soc/s3c24xx/smdk_wm9713.c b/sound/soc/s3c24xx/smdk_wm9713.c
new file mode 100644
index 0000000..24fd39f
--- /dev/null
+++ b/sound/soc/s3c24xx/smdk_wm9713.c
@@ -0,0 +1,94 @@
+/*
+ * smdk_wm9713.c  --  SoC audio for SMDK
+ *
+ * Copyright 2010 Samsung Electronics Co. Ltd.
+ * Author: Jaswinder Singh Brar <jassi.brar@samsung.com>
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/soc.h>
+
+#include "../codecs/wm9713.h"
+#include "s3c-dma.h"
+#include "s3c-ac97.h"
+
+static struct snd_soc_card smdk;
+
+/*
+ * Default CFG switch settings to use this driver:
+ *
+ *   SMDK6410: Set CFG1 1-3 On, CFG2 1-4 Off
+ */
+
+/*
+ Playback (HeadPhone):-
+	$ amixer sset 'Headphone' unmute
+	$ amixer sset 'Right Headphone Out Mux' 'Headphone'
+	$ amixer sset 'Left Headphone Out Mux' 'Headphone'
+	$ amixer sset 'Right HP Mixer PCM' unmute
+	$ amixer sset 'Left HP Mixer PCM' unmute
+
+ Capture (LineIn):-
+	$ amixer sset 'Right Capture Source' 'Line'
+	$ amixer sset 'Left Capture Source' 'Line'
+*/
+
+static struct snd_soc_dai_link smdk_dai = {
+	.name = "AC97",
+	.stream_name = "AC97 PCM",
+	.cpu_dai = &s3c_ac97_dai[S3C_AC97_DAI_PCM],
+	.codec_dai = &wm9713_dai[WM9713_DAI_AC97_HIFI],
+};
+
+static struct snd_soc_card smdk = {
+	.name = "SMDK",
+	.platform = &s3c24xx_soc_platform,
+	.dai_link = &smdk_dai,
+	.num_links = 1,
+};
+
+static struct snd_soc_device smdk_snd_ac97_devdata = {
+	.card = &smdk,
+	.codec_dev = &soc_codec_dev_wm9713,
+};
+
+static struct platform_device *smdk_snd_ac97_device;
+
+static int __init smdk_init(void)
+{
+	int ret;
+
+	smdk_snd_ac97_device = platform_device_alloc("soc-audio", -1);
+	if (!smdk_snd_ac97_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(smdk_snd_ac97_device,
+			     &smdk_snd_ac97_devdata);
+	smdk_snd_ac97_devdata.dev = &smdk_snd_ac97_device->dev;
+
+	ret = platform_device_add(smdk_snd_ac97_device);
+	if (ret)
+		platform_device_put(smdk_snd_ac97_device);
+
+	return ret;
+}
+
+static void __exit smdk_exit(void)
+{
+	platform_device_unregister(smdk_snd_ac97_device);
+}
+
+module_init(smdk_init);
+module_exit(smdk_exit);
+
+/* Module information */
+MODULE_AUTHOR("Jaswinder Singh Brar, jassi.brar@samsung.com");
+MODULE_DESCRIPTION("ALSA SoC SMDK+WM9713");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 9e69765..1066749 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -26,6 +26,13 @@
 	help
 	  This option enables FSI sound support
 
+config SND_SOC_SH4_SIU
+	tristate
+	depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK
+	select DMA_ENGINE
+	select DMADEVICES
+	select SH_DMAE
+
 ##
 ## Boards
 ##
@@ -47,4 +54,20 @@
 	  This option enables generic sound support for the
 	  FSI - AK4642 unit
 
+config SND_FSI_DA7210
+	bool "FSI-DA7210 sound support"
+	depends on SND_SOC_SH4_FSI
+	select SND_SOC_DA7210
+	help
+	  This option enables generic sound support for the
+	  FSI - DA7210 unit
+
+config SND_SIU_MIGOR
+	tristate "SIU sound support on Migo-R"
+	depends on SH_MIGOR
+	select SND_SOC_SH4_SIU
+	select SND_SOC_WM8978
+	help
+	  This option enables sound support for the SH7722 Migo-R board
+
 endmenu
diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile
index a699787..8a5a192 100644
--- a/sound/soc/sh/Makefile
+++ b/sound/soc/sh/Makefile
@@ -6,13 +6,19 @@
 snd-soc-hac-objs	:= hac.o
 snd-soc-ssi-objs	:= ssi.o
 snd-soc-fsi-objs	:= fsi.o
+snd-soc-siu-objs	:= siu_pcm.o siu_dai.o
 obj-$(CONFIG_SND_SOC_SH4_HAC)	+= snd-soc-hac.o
 obj-$(CONFIG_SND_SOC_SH4_SSI)	+= snd-soc-ssi.o
 obj-$(CONFIG_SND_SOC_SH4_FSI)	+= snd-soc-fsi.o
+obj-$(CONFIG_SND_SOC_SH4_SIU)	+= snd-soc-siu.o
 
 ## boards
 snd-soc-sh7760-ac97-objs	:= sh7760-ac97.o
 snd-soc-fsi-ak4642-objs		:= fsi-ak4642.o
+snd-soc-fsi-da7210-objs		:= fsi-da7210.o
+snd-soc-migor-objs		:= migor.o
 
 obj-$(CONFIG_SND_SH7760_AC97)	+= snd-soc-sh7760-ac97.o
 obj-$(CONFIG_SND_FSI_AK4642)	+= snd-soc-fsi-ak4642.o
+obj-$(CONFIG_SND_FSI_DA7210)	+= snd-soc-fsi-da7210.o
+obj-$(CONFIG_SND_SIU_MIGOR)	+= snd-soc-migor.o
diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c
new file mode 100644
index 0000000..33b4d17
--- /dev/null
+++ b/sound/soc/sh/fsi-da7210.c
@@ -0,0 +1,83 @@
+/*
+ * fsi-da7210.c
+ *
+ * Copyright (C) 2009 Renesas Solutions Corp.
+ * Kuninori Morimoto <morimoto.kuninori@renesas.com>
+ *
+ *  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.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <sound/sh_fsi.h>
+#include "../codecs/da7210.h"
+
+static int fsi_da7210_init(struct snd_soc_codec *codec)
+{
+	return snd_soc_dai_set_fmt(&da7210_dai,
+				   SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				   SND_SOC_DAIFMT_CBM_CFM);
+}
+
+static struct snd_soc_dai_link fsi_da7210_dai = {
+	.name		= "DA7210",
+	.stream_name	= "DA7210",
+	.cpu_dai	= &fsi_soc_dai[1], /* FSI B */
+	.codec_dai	= &da7210_dai,
+	.init		= fsi_da7210_init,
+};
+
+static struct snd_soc_card fsi_soc_card = {
+	.name		= "FSI",
+	.platform	= &fsi_soc_platform,
+	.dai_link	= &fsi_da7210_dai,
+	.num_links	= 1,
+};
+
+static struct snd_soc_device fsi_da7210_snd_devdata = {
+	.card		= &fsi_soc_card,
+	.codec_dev	= &soc_codec_dev_da7210,
+};
+
+static struct platform_device *fsi_da7210_snd_device;
+
+static int __init fsi_da7210_sound_init(void)
+{
+	int ret;
+
+	fsi_da7210_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!fsi_da7210_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(fsi_da7210_snd_device, &fsi_da7210_snd_devdata);
+	fsi_da7210_snd_devdata.dev = &fsi_da7210_snd_device->dev;
+	ret = platform_device_add(fsi_da7210_snd_device);
+	if (ret)
+		platform_device_put(fsi_da7210_snd_device);
+
+	return ret;
+}
+
+static void __exit fsi_da7210_sound_exit(void)
+{
+	platform_device_unregister(fsi_da7210_snd_device);
+}
+
+module_init(fsi_da7210_sound_init);
+module_exit(fsi_da7210_sound_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("ALSA SoC FSI DA2710");
+MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 42813b8..993abb7 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -67,6 +67,7 @@
 /* DOFF_ST */
 #define ERR_OVER	0x00000010
 #define ERR_UNDER	0x00000001
+#define ST_ERR		(ERR_OVER | ERR_UNDER)
 
 /* CLK_RST */
 #define B_CLK		0x00000010
@@ -92,6 +93,7 @@
 struct fsi_priv {
 	void __iomem *base;
 	struct snd_pcm_substream *substream;
+	struct fsi_master *master;
 
 	int fifo_max;
 	int chan;
@@ -108,10 +110,9 @@
 	struct fsi_priv fsia;
 	struct fsi_priv fsib;
 	struct sh_fsi_platform_info *info;
+	spinlock_t lock;
 };
 
-static struct fsi_master *master;
-
 /************************************************************************
 
 
@@ -119,35 +120,35 @@
 
 
 ************************************************************************/
-static int __fsi_reg_write(u32 reg, u32 data)
+static void __fsi_reg_write(u32 reg, u32 data)
 {
 	/* valid data area is 24bit */
 	data &= 0x00ffffff;
 
-	return ctrl_outl(data, reg);
+	__raw_writel(data, reg);
 }
 
 static u32 __fsi_reg_read(u32 reg)
 {
-	return ctrl_inl(reg);
+	return __raw_readl(reg);
 }
 
-static int __fsi_reg_mask_set(u32 reg, u32 mask, u32 data)
+static void __fsi_reg_mask_set(u32 reg, u32 mask, u32 data)
 {
 	u32 val = __fsi_reg_read(reg);
 
 	val &= ~mask;
 	val |= data & mask;
 
-	return __fsi_reg_write(reg, val);
+	__fsi_reg_write(reg, val);
 }
 
-static int fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data)
+static void fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data)
 {
 	if (reg > REG_END)
-		return -1;
+		return;
 
-	return __fsi_reg_write((u32)(fsi->base + reg), data);
+	__fsi_reg_write((u32)(fsi->base + reg), data);
 }
 
 static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg)
@@ -158,39 +159,55 @@
 	return __fsi_reg_read((u32)(fsi->base + reg));
 }
 
-static int fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)
+static void fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data)
 {
 	if (reg > REG_END)
-		return -1;
+		return;
 
-	return __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);
+	__fsi_reg_mask_set((u32)(fsi->base + reg), mask, data);
 }
 
-static int fsi_master_write(u32 reg, u32 data)
+static void fsi_master_write(struct fsi_master *master, u32 reg, u32 data)
 {
+	unsigned long flags;
+
 	if ((reg < MREG_START) ||
 	    (reg > MREG_END))
-		return -1;
+		return;
 
-	return __fsi_reg_write((u32)(master->base + reg), data);
+	spin_lock_irqsave(&master->lock, flags);
+	__fsi_reg_write((u32)(master->base + reg), data);
+	spin_unlock_irqrestore(&master->lock, flags);
 }
 
-static u32 fsi_master_read(u32 reg)
+static u32 fsi_master_read(struct fsi_master *master, u32 reg)
 {
+	u32 ret;
+	unsigned long flags;
+
 	if ((reg < MREG_START) ||
 	    (reg > MREG_END))
 		return 0;
 
-	return __fsi_reg_read((u32)(master->base + reg));
+	spin_lock_irqsave(&master->lock, flags);
+	ret = __fsi_reg_read((u32)(master->base + reg));
+	spin_unlock_irqrestore(&master->lock, flags);
+
+	return ret;
 }
 
-static int fsi_master_mask_set(u32 reg, u32 mask, u32 data)
+static void fsi_master_mask_set(struct fsi_master *master,
+			       u32 reg, u32 mask, u32 data)
 {
+	unsigned long flags;
+
 	if ((reg < MREG_START) ||
 	    (reg > MREG_END))
-		return -1;
+		return;
 
-	return __fsi_reg_mask_set((u32)(master->base + reg), mask, data);
+	spin_lock_irqsave(&master->lock, flags);
+	__fsi_reg_mask_set((u32)(master->base + reg), mask, data);
+	spin_unlock_irqrestore(&master->lock, flags);
 }
 
 /************************************************************************
@@ -200,43 +217,35 @@
 
 
 ************************************************************************/
-static struct fsi_priv *fsi_get(struct snd_pcm_substream *substream)
+static struct fsi_master *fsi_get_master(struct fsi_priv *fsi)
 {
-	struct snd_soc_pcm_runtime *rtd;
-	struct fsi_priv *fsi = NULL;
-
-	if (!substream || !master)
-		return NULL;
-
-	rtd = substream->private_data;
-	switch (rtd->dai->cpu_dai->id) {
-	case 0:
-		fsi = &master->fsia;
-		break;
-	case 1:
-		fsi = &master->fsib;
-		break;
-	}
-
-	return fsi;
+	return fsi->master;
 }
 
 static int fsi_is_port_a(struct fsi_priv *fsi)
 {
-	/* return
-	 * 1 : port a
-	 * 0 : port b
-	 */
+	return fsi->master->base == fsi->base;
+}
 
-	if (fsi == &master->fsia)
-		return 1;
+static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai_link *machine = rtd->dai;
 
-	return 0;
+	return  machine->cpu_dai;
+}
+
+static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_dai *dai = fsi_get_dai(substream);
+
+	return dai->private_data;
 }
 
 static u32 fsi_get_info_flags(struct fsi_priv *fsi)
 {
 	int is_porta = fsi_is_port_a(fsi);
+	struct fsi_master *master = fsi_get_master(fsi);
 
 	return is_porta ? master->info->porta_flags :
 		master->info->portb_flags;
@@ -314,27 +323,30 @@
 static void fsi_irq_enable(struct fsi_priv *fsi, int is_play)
 {
 	u32 data = fsi_port_ab_io_bit(fsi, is_play);
+	struct fsi_master *master = fsi_get_master(fsi);
 
-	fsi_master_mask_set(IMSK,  data, data);
-	fsi_master_mask_set(IEMSK, data, data);
+	fsi_master_mask_set(master, IMSK,  data, data);
+	fsi_master_mask_set(master, IEMSK, data, data);
 }
 
 static void fsi_irq_disable(struct fsi_priv *fsi, int is_play)
 {
 	u32 data = fsi_port_ab_io_bit(fsi, is_play);
+	struct fsi_master *master = fsi_get_master(fsi);
 
-	fsi_master_mask_set(IMSK,  data, 0);
-	fsi_master_mask_set(IEMSK, data, 0);
+	fsi_master_mask_set(master, IMSK,  data, 0);
+	fsi_master_mask_set(master, IEMSK, data, 0);
 }
 
 static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable)
 {
 	u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4);
+	struct fsi_master *master = fsi_get_master(fsi);
 
 	if (enable)
-		fsi_master_mask_set(CLK_RST, val, val);
+		fsi_master_mask_set(master, CLK_RST, val, val);
 	else
-		fsi_master_mask_set(CLK_RST, val, 0);
+		fsi_master_mask_set(master, CLK_RST, val, 0);
 }
 
 static void fsi_irq_init(struct fsi_priv *fsi, int is_play)
@@ -355,43 +367,46 @@
 	fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR);
 
 	/* clear interrupt factor */
-	fsi_master_mask_set(INT_ST, data, 0);
+	fsi_master_mask_set(fsi_get_master(fsi), INT_ST, data, 0);
 }
 
-static void fsi_soft_all_reset(void)
+static void fsi_soft_all_reset(struct fsi_master *master)
 {
-	u32 status = fsi_master_read(SOFT_RST);
+	u32 status = fsi_master_read(master, SOFT_RST);
 
 	/* port AB reset */
 	status &= 0x000000ff;
-	fsi_master_write(SOFT_RST, status);
+	fsi_master_write(master, SOFT_RST, status);
 	mdelay(10);
 
 	/* soft reset */
 	status &= 0x000000f0;
-	fsi_master_write(SOFT_RST, status);
+	fsi_master_write(master, SOFT_RST, status);
 	status |= 0x00000001;
-	fsi_master_write(SOFT_RST, status);
+	fsi_master_write(master, SOFT_RST, status);
 	mdelay(10);
 }
 
 /* playback interrupt */
-static int fsi_data_push(struct fsi_priv *fsi)
+static int fsi_data_push(struct fsi_priv *fsi, int startup)
 {
 	struct snd_pcm_runtime *runtime;
 	struct snd_pcm_substream *substream = NULL;
+	u32 status;
 	int send;
 	int fifo_free;
 	int width;
 	u8 *start;
-	int i;
+	int i, over_period;
 
 	if (!fsi			||
 	    !fsi->substream		||
 	    !fsi->substream->runtime)
 		return -EINVAL;
 
-	runtime = fsi->substream->runtime;
+	over_period	= 0;
+	substream	= fsi->substream;
+	runtime		= substream->runtime;
 
 	/* FSI FIFO has limit.
 	 * So, this driver can not send periods data at a time
@@ -399,7 +414,7 @@
 	if (fsi->byte_offset >=
 	    fsi->period_len * (fsi->periods + 1)) {
 
-		substream = fsi->substream;
+		over_period = 1;
 		fsi->periods = (fsi->periods + 1) % runtime->periods;
 
 		if (0 == fsi->periods)
@@ -438,30 +453,44 @@
 
 	fsi->byte_offset += send * width;
 
+	status = fsi_reg_read(fsi, DOFF_ST);
+	if (!startup) {
+		struct snd_soc_dai *dai = fsi_get_dai(substream);
+
+		if (status & ERR_OVER)
+			dev_err(dai->dev, "over run\n");
+		if (status & ERR_UNDER)
+			dev_err(dai->dev, "under run\n");
+	}
+	fsi_reg_write(fsi, DOFF_ST, 0);
+
 	fsi_irq_enable(fsi, 1);
 
-	if (substream)
+	if (over_period)
 		snd_pcm_period_elapsed(substream);
 
 	return 0;
 }
 
-static int fsi_data_pop(struct fsi_priv *fsi)
+static int fsi_data_pop(struct fsi_priv *fsi, int startup)
 {
 	struct snd_pcm_runtime *runtime;
 	struct snd_pcm_substream *substream = NULL;
+	u32 status;
 	int free;
 	int fifo_fill;
 	int width;
 	u8 *start;
-	int i;
+	int i, over_period;
 
 	if (!fsi			||
 	    !fsi->substream		||
 	    !fsi->substream->runtime)
 		return -EINVAL;
 
-	runtime = fsi->substream->runtime;
+	over_period	= 0;
+	substream	= fsi->substream;
+	runtime		= substream->runtime;
 
 	/* FSI FIFO has limit.
 	 * So, this driver can not send periods data at a time
@@ -469,7 +498,7 @@
 	if (fsi->byte_offset >=
 	    fsi->period_len * (fsi->periods + 1)) {
 
-		substream = fsi->substream;
+		over_period = 1;
 		fsi->periods = (fsi->periods + 1) % runtime->periods;
 
 		if (0 == fsi->periods)
@@ -507,9 +536,20 @@
 
 	fsi->byte_offset += fifo_fill * width;
 
+	status = fsi_reg_read(fsi, DIFF_ST);
+	if (!startup) {
+		struct snd_soc_dai *dai = fsi_get_dai(substream);
+
+		if (status & ERR_OVER)
+			dev_err(dai->dev, "over run\n");
+		if (status & ERR_UNDER)
+			dev_err(dai->dev, "under run\n");
+	}
+	fsi_reg_write(fsi, DIFF_ST, 0);
+
 	fsi_irq_enable(fsi, 0);
 
-	if (substream)
+	if (over_period)
 		snd_pcm_period_elapsed(substream);
 
 	return 0;
@@ -517,23 +557,24 @@
 
 static irqreturn_t fsi_interrupt(int irq, void *data)
 {
-	u32 status = fsi_master_read(SOFT_RST) & ~0x00000010;
-	u32 int_st = fsi_master_read(INT_ST);
+	struct fsi_master *master = data;
+	u32 status = fsi_master_read(master, SOFT_RST) & ~0x00000010;
+	u32 int_st = fsi_master_read(master, INT_ST);
 
 	/* clear irq status */
-	fsi_master_write(SOFT_RST, status);
-	fsi_master_write(SOFT_RST, status | 0x00000010);
+	fsi_master_write(master, SOFT_RST, status);
+	fsi_master_write(master, SOFT_RST, status | 0x00000010);
 
 	if (int_st & INT_A_OUT)
-		fsi_data_push(&master->fsia);
+		fsi_data_push(&master->fsia, 0);
 	if (int_st & INT_B_OUT)
-		fsi_data_push(&master->fsib);
+		fsi_data_push(&master->fsib, 0);
 	if (int_st & INT_A_IN)
-		fsi_data_pop(&master->fsia);
+		fsi_data_pop(&master->fsia, 0);
 	if (int_st & INT_B_IN)
-		fsi_data_pop(&master->fsib);
+		fsi_data_pop(&master->fsib, 0);
 
-	fsi_master_write(INT_ST, 0x0000000);
+	fsi_master_write(master, INT_ST, 0x0000000);
 
 	return IRQ_HANDLED;
 }
@@ -548,7 +589,7 @@
 static int fsi_dai_startup(struct snd_pcm_substream *substream,
 			   struct snd_soc_dai *dai)
 {
-	struct fsi_priv *fsi = fsi_get(substream);
+	struct fsi_priv *fsi = fsi_get_priv(substream);
 	const char *msg;
 	u32 flags = fsi_get_info_flags(fsi);
 	u32 fmt;
@@ -667,7 +708,7 @@
 static void fsi_dai_shutdown(struct snd_pcm_substream *substream,
 			     struct snd_soc_dai *dai)
 {
-	struct fsi_priv *fsi = fsi_get(substream);
+	struct fsi_priv *fsi = fsi_get_priv(substream);
 	int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 
 	fsi_irq_disable(fsi, is_play);
@@ -679,7 +720,7 @@
 static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
 			   struct snd_soc_dai *dai)
 {
-	struct fsi_priv *fsi = fsi_get(substream);
+	struct fsi_priv *fsi = fsi_get_priv(substream);
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 	int ret = 0;
@@ -689,7 +730,7 @@
 		fsi_stream_push(fsi, substream,
 				frames_to_bytes(runtime, runtime->buffer_size),
 				frames_to_bytes(runtime, runtime->period_size));
-		ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
+		ret = is_play ? fsi_data_push(fsi, 1) : fsi_data_pop(fsi, 1);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 		fsi_irq_disable(fsi, is_play);
@@ -760,7 +801,7 @@
 static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct fsi_priv *fsi = fsi_get(substream);
+	struct fsi_priv *fsi = fsi_get_priv(substream);
 	long location;
 
 	location = (fsi->byte_offset - 1);
@@ -870,10 +911,16 @@
 ************************************************************************/
 static int fsi_probe(struct platform_device *pdev)
 {
+	struct fsi_master *master;
 	struct resource *res;
 	unsigned int irq;
 	int ret;
 
+	if (0 != pdev->id) {
+		dev_err(&pdev->dev, "current fsi support id 0 only now\n");
+		return -ENODEV;
+	}
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	irq = platform_get_irq(pdev, 0);
 	if (!res || (int)irq <= 0) {
@@ -899,15 +946,20 @@
 	master->irq		= irq;
 	master->info		= pdev->dev.platform_data;
 	master->fsia.base	= master->base;
+	master->fsia.master	= master;
 	master->fsib.base	= master->base + 0x40;
+	master->fsib.master	= master;
+	spin_lock_init(&master->lock);
 
 	pm_runtime_enable(&pdev->dev);
 	pm_runtime_resume(&pdev->dev);
 
 	fsi_soc_dai[0].dev		= &pdev->dev;
+	fsi_soc_dai[0].private_data	= &master->fsia;
 	fsi_soc_dai[1].dev		= &pdev->dev;
+	fsi_soc_dai[1].private_data	= &master->fsib;
 
-	fsi_soft_all_reset();
+	fsi_soft_all_reset(master);
 
 	ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master);
 	if (ret) {
@@ -937,6 +989,10 @@
 
 static int fsi_remove(struct platform_device *pdev)
 {
+	struct fsi_master *master;
+
+	master = fsi_get_master(fsi_soc_dai[0].private_data);
+
 	snd_soc_unregister_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai));
 	snd_soc_unregister_platform(&fsi_soc_platform);
 
@@ -946,7 +1002,12 @@
 
 	iounmap(master->base);
 	kfree(master);
-	master = NULL;
+
+	fsi_soc_dai[0].dev		= NULL;
+	fsi_soc_dai[0].private_data	= NULL;
+	fsi_soc_dai[1].dev		= NULL;
+	fsi_soc_dai[1].private_data	= NULL;
+
 	return 0;
 }
 
diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c
new file mode 100644
index 0000000..b823a5c
--- /dev/null
+++ b/sound/soc/sh/migor.c
@@ -0,0 +1,218 @@
+/*
+ * ALSA SoC driver for Migo-R
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+
+#include <asm/clock.h>
+
+#include <cpu/sh7722.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm8978.h"
+#include "siu.h"
+
+/* Default 8000Hz sampling frequency */
+static unsigned long codec_freq = 8000 * 512;
+
+static unsigned int use_count;
+
+/* External clock, sourced from the codec at the SIUMCKB pin */
+static unsigned long siumckb_recalc(struct clk *clk)
+{
+	return codec_freq;
+}
+
+static struct clk_ops siumckb_clk_ops = {
+	.recalc = siumckb_recalc,
+};
+
+static struct clk siumckb_clk = {
+	.name		= "siumckb_clk",
+	.id		= -1,
+	.ops		= &siumckb_clk_ops,
+	.rate		= 0, /* initialised at run-time */
+};
+
+static int migor_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->dai->codec_dai;
+	int ret;
+	unsigned int rate = params_rate(params);
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 13000000,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_OPCLKRATE, rate * 512);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_NB_IF |
+				  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(rtd->dai->cpu_dai, SND_SOC_DAIFMT_NB_IF |
+				  SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	codec_freq = rate * 512;
+	/*
+	 * This propagates the parent frequency change to children and
+	 * recalculates the frequency table
+	 */
+	clk_set_rate(&siumckb_clk, codec_freq);
+	dev_dbg(codec_dai->dev, "%s: configure %luHz\n", __func__, codec_freq);
+
+	ret = snd_soc_dai_set_sysclk(rtd->dai->cpu_dai, SIU_CLKB_EXT,
+				     codec_freq / 2, SND_SOC_CLOCK_IN);
+
+	if (!ret)
+		use_count++;
+
+	return ret;
+}
+
+static int migor_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+	if (use_count) {
+		use_count--;
+
+		if (!use_count)
+			snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 0,
+					       SND_SOC_CLOCK_IN);
+	} else {
+		dev_dbg(codec_dai->dev, "Unbalanced hw_free!\n");
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops migor_dai_ops = {
+	.hw_params = migor_hw_params,
+	.hw_free = migor_hw_free,
+};
+
+static const struct snd_soc_dapm_widget migor_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone", NULL),
+	SND_SOC_DAPM_MIC("Onboard Microphone", NULL),
+	SND_SOC_DAPM_MIC("External Microphone", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Headphone output connected to LHP/RHP, enable OUT4 for VMID */
+	{ "Headphone", NULL,  "OUT4 VMID" },
+	{ "OUT4 VMID", NULL,  "LHP" },
+	{ "OUT4 VMID", NULL,  "RHP" },
+
+	/* On-board microphone */
+	{ "RMICN", NULL, "Mic Bias" },
+	{ "RMICP", NULL, "Mic Bias" },
+	{ "Mic Bias", NULL, "Onboard Microphone" },
+
+	/* External microphone */
+	{ "LMICN", NULL, "Mic Bias" },
+	{ "LMICP", NULL, "Mic Bias" },
+	{ "Mic Bias", NULL, "External Microphone" },
+};
+
+static int migor_dai_init(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, migor_dapm_widgets,
+				  ARRAY_SIZE(migor_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	return 0;
+}
+
+/* migor digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link migor_dai = {
+	.name = "wm8978",
+	.stream_name = "WM8978",
+	.cpu_dai = &siu_i2s_dai,
+	.codec_dai = &wm8978_dai,
+	.ops = &migor_dai_ops,
+	.init = migor_dai_init,
+};
+
+/* migor audio machine driver */
+static struct snd_soc_card snd_soc_migor = {
+	.name = "Migo-R",
+	.platform = &siu_platform,
+	.dai_link = &migor_dai,
+	.num_links = 1,
+};
+
+/* migor audio subsystem */
+static struct snd_soc_device migor_snd_devdata = {
+	.card = &snd_soc_migor,
+	.codec_dev = &soc_codec_dev_wm8978,
+};
+
+static struct platform_device *migor_snd_device;
+
+static int __init migor_init(void)
+{
+	int ret;
+
+	ret = clk_register(&siumckb_clk);
+	if (ret < 0)
+		return ret;
+
+	/* Port number used on this machine: port B */
+	migor_snd_device = platform_device_alloc("soc-audio", 1);
+	if (!migor_snd_device) {
+		ret = -ENOMEM;
+		goto epdevalloc;
+	}
+
+	platform_set_drvdata(migor_snd_device, &migor_snd_devdata);
+
+	migor_snd_devdata.dev = &migor_snd_device->dev;
+
+	ret = platform_device_add(migor_snd_device);
+	if (ret)
+		goto epdevadd;
+
+	return 0;
+
+epdevadd:
+	platform_device_put(migor_snd_device);
+epdevalloc:
+	clk_unregister(&siumckb_clk);
+	return ret;
+}
+
+static void __exit migor_exit(void)
+{
+	clk_unregister(&siumckb_clk);
+	platform_device_unregister(migor_snd_device);
+}
+
+module_init(migor_init);
+module_exit(migor_exit);
+
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
+MODULE_DESCRIPTION("ALSA SoC Migor");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h
new file mode 100644
index 0000000..9cc04ab
--- /dev/null
+++ b/sound/soc/sh/siu.h
@@ -0,0 +1,193 @@
+/*
+ * siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef SIU_H
+#define SIU_H
+
+/* Common kernel and user-space firmware-building defines and types */
+
+#define YRAM0_SIZE		(0x0040 / 4)		/* 16 */
+#define YRAM1_SIZE		(0x0080 / 4)		/* 32 */
+#define YRAM2_SIZE		(0x0040 / 4)		/* 16 */
+#define YRAM3_SIZE		(0x0080 / 4)		/* 32 */
+#define YRAM4_SIZE		(0x0080 / 4)		/* 32 */
+#define YRAM_DEF_SIZE		(YRAM0_SIZE + YRAM1_SIZE + YRAM2_SIZE + \
+				 YRAM3_SIZE + YRAM4_SIZE)
+#define YRAM_FIR_SIZE		(0x0400 / 4)		/* 256 */
+#define YRAM_IIR_SIZE		(0x0200 / 4)		/* 128 */
+
+#define XRAM0_SIZE		(0x0400 / 4)		/* 256 */
+#define XRAM1_SIZE		(0x0200 / 4)		/* 128 */
+#define XRAM2_SIZE		(0x0200 / 4)		/* 128 */
+
+/* PRAM program array size */
+#define PRAM0_SIZE		(0x0100 / 4)		/* 64 */
+#define PRAM1_SIZE		((0x2000 - 0x0100) / 4)	/* 1984 */
+
+#include <linux/types.h>
+
+struct siu_spb_param {
+	__u32	ab1a;	/* input FIFO address */
+	__u32	ab0a;	/* output FIFO address */
+	__u32	dir;	/* 0=the ather except CPUOUTPUT, 1=CPUINPUT */
+	__u32	event;	/* SPB program starting conditions */
+	__u32	stfifo;	/* STFIFO register setting value */
+	__u32	trdat;	/* TRDAT register setting value */
+};
+
+struct siu_firmware {
+	__u32			yram_fir_coeff[YRAM_FIR_SIZE];
+	__u32			pram0[PRAM0_SIZE];
+	__u32			pram1[PRAM1_SIZE];
+	__u32			yram0[YRAM0_SIZE];
+	__u32			yram1[YRAM1_SIZE];
+	__u32			yram2[YRAM2_SIZE];
+	__u32			yram3[YRAM3_SIZE];
+	__u32			yram4[YRAM4_SIZE];
+	__u32			spbpar_num;
+	struct siu_spb_param	spbpar[32];
+};
+
+#ifdef __KERNEL__
+
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <asm/dma-sh.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc-dai.h>
+
+#define SIU_PERIOD_BYTES_MAX	8192		/* DMA transfer/period size */
+#define SIU_PERIOD_BYTES_MIN	256		/* DMA transfer/period size */
+#define SIU_PERIODS_MAX		64		/* Max periods in buffer */
+#define SIU_PERIODS_MIN		4		/* Min periods in buffer */
+#define SIU_BUFFER_BYTES_MAX	(SIU_PERIOD_BYTES_MAX * SIU_PERIODS_MAX)
+
+/* SIU ports: only one can be used at a time */
+enum {
+	SIU_PORT_A,
+	SIU_PORT_B,
+	SIU_PORT_NUM,
+};
+
+/* SIU clock configuration */
+enum {
+	SIU_CLKA_PLL,
+	SIU_CLKA_EXT,
+	SIU_CLKB_PLL,
+	SIU_CLKB_EXT
+};
+
+struct siu_info {
+	int			port_id;
+	u32 __iomem		*pram;
+	u32 __iomem		*xram;
+	u32 __iomem		*yram;
+	u32 __iomem		*reg;
+	struct siu_firmware	fw;
+};
+
+struct siu_stream {
+	struct tasklet_struct		tasklet;
+	struct snd_pcm_substream	*substream;
+	snd_pcm_format_t		format;
+	size_t				buf_bytes;
+	size_t				period_bytes;
+	int				cur_period;	/* Period currently in dma */
+	u32				volume;
+	snd_pcm_sframes_t		xfer_cnt;	/* Number of frames */
+	u8				rw_flg;		/* transfer status */
+	/* DMA status */
+	struct dma_chan			*chan;		/* DMA channel */
+	struct dma_async_tx_descriptor	*tx_desc;
+	dma_cookie_t			cookie;
+	struct sh_dmae_slave		param;
+};
+
+struct siu_port {
+	unsigned long		play_cap;	/* Used to track full duplex */
+	struct snd_pcm		*pcm;
+	struct siu_stream	playback;
+	struct siu_stream	capture;
+	u32			stfifo;		/* STFIFO value from firmware */
+	u32			trdat;		/* TRDAT value from firmware */
+};
+
+extern struct siu_port *siu_ports[SIU_PORT_NUM];
+
+static inline struct siu_port *siu_port_info(struct snd_pcm_substream *substream)
+{
+	struct platform_device *pdev =
+		to_platform_device(substream->pcm->card->dev);
+	return siu_ports[pdev->id];
+}
+
+/* Register access */
+static inline void siu_write32(u32 __iomem *addr, u32 val)
+{
+	__raw_writel(val, addr);
+}
+
+static inline u32 siu_read32(u32 __iomem *addr)
+{
+	return __raw_readl(addr);
+}
+
+/* SIU registers */
+#define SIU_IFCTL	(0x000 / sizeof(u32))
+#define SIU_SRCTL	(0x004 / sizeof(u32))
+#define SIU_SFORM	(0x008 / sizeof(u32))
+#define SIU_CKCTL	(0x00c / sizeof(u32))
+#define SIU_TRDAT	(0x010 / sizeof(u32))
+#define SIU_STFIFO	(0x014 / sizeof(u32))
+#define SIU_DPAK	(0x01c / sizeof(u32))
+#define SIU_CKREV	(0x020 / sizeof(u32))
+#define SIU_EVNTC	(0x028 / sizeof(u32))
+#define SIU_SBCTL	(0x040 / sizeof(u32))
+#define SIU_SBPSET	(0x044 / sizeof(u32))
+#define SIU_SBFSTS	(0x068 / sizeof(u32))
+#define SIU_SBDVCA	(0x06c / sizeof(u32))
+#define SIU_SBDVCB	(0x070 / sizeof(u32))
+#define SIU_SBACTIV	(0x074 / sizeof(u32))
+#define SIU_DMAIA	(0x090 / sizeof(u32))
+#define SIU_DMAIB	(0x094 / sizeof(u32))
+#define SIU_DMAOA	(0x098 / sizeof(u32))
+#define SIU_DMAOB	(0x09c / sizeof(u32))
+#define SIU_DMAML	(0x0a0 / sizeof(u32))
+#define SIU_SPSTS	(0x0cc / sizeof(u32))
+#define SIU_SPCTL	(0x0d0 / sizeof(u32))
+#define SIU_BRGASEL	(0x100 / sizeof(u32))
+#define SIU_BRRA	(0x104 / sizeof(u32))
+#define SIU_BRGBSEL	(0x108 / sizeof(u32))
+#define SIU_BRRB	(0x10c / sizeof(u32))
+
+extern struct snd_soc_platform siu_platform;
+extern struct snd_soc_dai siu_i2s_dai;
+
+int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card);
+void siu_free_port(struct siu_port *port_info);
+
+#endif
+
+#endif /* SIU_H */
diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c
new file mode 100644
index 0000000..5452d19
--- /dev/null
+++ b/sound/soc/sh/siu_dai.c
@@ -0,0 +1,847 @@
+/*
+ * siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral.
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+
+#include <asm/clock.h>
+#include <asm/siu.h>
+
+#include <sound/control.h>
+#include <sound/soc-dai.h>
+
+#include "siu.h"
+
+/* Board specifics */
+#if defined(CONFIG_CPU_SUBTYPE_SH7722)
+# define SIU_MAX_VOLUME		0x1000
+#else
+# define SIU_MAX_VOLUME		0x7fff
+#endif
+
+#define PRAM_SIZE	0x2000
+#define XRAM_SIZE	0x800
+#define YRAM_SIZE	0x800
+
+#define XRAM_OFFSET	0x4000
+#define YRAM_OFFSET	0x6000
+#define REG_OFFSET	0xc000
+
+#define PLAYBACK_ENABLED	1
+#define CAPTURE_ENABLED		2
+
+#define VOLUME_CAPTURE		0
+#define VOLUME_PLAYBACK		1
+#define DFLT_VOLUME_LEVEL	0x08000800
+
+/*
+ * SPDIF is only available on port A and on some SIU implementations it is only
+ * available for input. Due to the lack of hardware to test it, SPDIF is left
+ * disabled in this driver version
+ */
+struct format_flag {
+	u32	i2s;
+	u32	pcm;
+	u32	spdif;
+	u32	mask;
+};
+
+struct port_flag {
+	struct format_flag	playback;
+	struct format_flag	capture;
+};
+
+static struct port_flag siu_flags[SIU_PORT_NUM] = {
+	[SIU_PORT_A] = {
+		.playback = {
+			.i2s	= 0x50000000,
+			.pcm	= 0x40000000,
+			.spdif	= 0x80000000,	/* not on all SIU versions */
+			.mask	= 0xd0000000,
+		},
+		.capture = {
+			.i2s	= 0x05000000,
+			.pcm	= 0x04000000,
+			.spdif	= 0x08000000,
+			.mask	= 0x0d000000,
+		},
+	},
+	[SIU_PORT_B] = {
+		.playback = {
+			.i2s	= 0x00500000,
+			.pcm	= 0x00400000,
+			.spdif	= 0,		/* impossible - turn off */
+			.mask	= 0x00500000,
+		},
+		.capture = {
+			.i2s	= 0x00050000,
+			.pcm	= 0x00040000,
+			.spdif	= 0,		/* impossible - turn off */
+			.mask	= 0x00050000,
+		},
+	},
+};
+
+static void siu_dai_start(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+
+	dev_dbg(port_info->pcm->card->dev, "%s\n", __func__);
+
+	/* Turn on SIU clock */
+	pm_runtime_get_sync(siu_i2s_dai.dev);
+
+	/* Issue software reset to siu */
+	siu_write32(base + SIU_SRCTL, 0);
+
+	/* Wait for the reset to take effect */
+	udelay(1);
+
+	port_info->stfifo = 0;
+	port_info->trdat = 0;
+
+	/* portA, portB, SIU operate */
+	siu_write32(base + SIU_SRCTL, 0x301);
+
+	/* portA=256fs, portB=256fs */
+	siu_write32(base + SIU_CKCTL, 0x40400000);
+
+	/* portA's BRG does not divide SIUCKA */
+	siu_write32(base + SIU_BRGASEL, 0);
+	siu_write32(base + SIU_BRRA, 0);
+
+	/* portB's BRG divides SIUCKB by half */
+	siu_write32(base + SIU_BRGBSEL, 1);
+	siu_write32(base + SIU_BRRB, 0);
+
+	siu_write32(base + SIU_IFCTL, 0x44440000);
+
+	/* portA: 32 bit/fs, master; portB: 32 bit/fs, master */
+	siu_write32(base + SIU_SFORM, 0x0c0c0000);
+
+	/*
+	 * Volume levels: looks like the DSP firmware implements volume controls
+	 * differently from what's described in the datasheet
+	 */
+	siu_write32(base + SIU_SBDVCA, port_info->playback.volume);
+	siu_write32(base + SIU_SBDVCB, port_info->capture.volume);
+}
+
+static void siu_dai_stop(void)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+
+	/* SIU software reset */
+	siu_write32(base + SIU_SRCTL, 0);
+
+	/* Turn off SIU clock */
+	pm_runtime_put_sync(siu_i2s_dai.dev);
+}
+
+static void siu_dai_spbAselect(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct siu_firmware *fw = &info->fw;
+	u32 *ydef = fw->yram0;
+	u32 idx;
+
+	/* path A use */
+	if (!info->port_id)
+		idx = 1;		/* portA */
+	else
+		idx = 2;		/* portB */
+
+	ydef[0] = (fw->spbpar[idx].ab1a << 16) |
+		(fw->spbpar[idx].ab0a << 8) |
+		(fw->spbpar[idx].dir << 7) | 3;
+	ydef[1] = fw->yram0[1];	/* 0x03000300 */
+	ydef[2] = (16 / 2) << 24;
+	ydef[3] = fw->yram0[3];	/* 0 */
+	ydef[4] = fw->yram0[4];	/* 0 */
+	ydef[7] = fw->spbpar[idx].event;
+	port_info->stfifo |= fw->spbpar[idx].stfifo;
+	port_info->trdat |= fw->spbpar[idx].trdat;
+}
+
+static void siu_dai_spbBselect(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct siu_firmware *fw = &info->fw;
+	u32 *ydef = fw->yram0;
+	u32 idx;
+
+	/* path B use */
+	if (!info->port_id)
+		idx = 7;		/* portA */
+	else
+		idx = 8;		/* portB */
+
+	ydef[5] = (fw->spbpar[idx].ab1a << 16) |
+		(fw->spbpar[idx].ab0a << 8) | 1;
+	ydef[6] = fw->spbpar[idx].event;
+	port_info->stfifo |= fw->spbpar[idx].stfifo;
+	port_info->trdat |= fw->spbpar[idx].trdat;
+}
+
+static void siu_dai_open(struct siu_stream *siu_stream)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+	u32 srctl, ifctl;
+
+	srctl = siu_read32(base + SIU_SRCTL);
+	ifctl = siu_read32(base + SIU_IFCTL);
+
+	switch (info->port_id) {
+	case SIU_PORT_A:
+		/* portA operates */
+		srctl |= 0x200;
+		ifctl &= ~0xc2;
+		break;
+	case SIU_PORT_B:
+		/* portB operates */
+		srctl |= 0x100;
+		ifctl &= ~0x31;
+		break;
+	}
+
+	siu_write32(base + SIU_SRCTL, srctl);
+	/* Unmute and configure portA */
+	siu_write32(base + SIU_IFCTL, ifctl);
+}
+
+/*
+ * At the moment only fixed Left-upper, Left-lower, Right-upper, Right-lower
+ * packing is supported
+ */
+static void siu_dai_pcmdatapack(struct siu_stream *siu_stream)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+	u32 dpak;
+
+	dpak = siu_read32(base + SIU_DPAK);
+
+	switch (info->port_id) {
+	case SIU_PORT_A:
+		dpak &= ~0xc0000000;
+		break;
+	case SIU_PORT_B:
+		dpak &= ~0x00c00000;
+		break;
+	}
+
+	siu_write32(base + SIU_DPAK, dpak);
+}
+
+static int siu_dai_spbstart(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+	struct siu_firmware *fw = &info->fw;
+	u32 *ydef = fw->yram0;
+	int cnt;
+	u32 __iomem *add;
+	u32 *ptr;
+
+	/* Load SPB Program in PRAM */
+	ptr = fw->pram0;
+	add = info->pram;
+	for (cnt = 0; cnt < PRAM0_SIZE; cnt++, add++, ptr++)
+		siu_write32(add, *ptr);
+
+	ptr = fw->pram1;
+	add = info->pram + (0x0100 / sizeof(u32));
+	for (cnt = 0; cnt < PRAM1_SIZE; cnt++, add++, ptr++)
+		siu_write32(add, *ptr);
+
+	/* XRAM initialization */
+	add = info->xram;
+	for (cnt = 0; cnt < XRAM0_SIZE + XRAM1_SIZE + XRAM2_SIZE; cnt++, add++)
+		siu_write32(add, 0);
+
+	/* YRAM variable area initialization */
+	add = info->yram;
+	for (cnt = 0; cnt < YRAM_DEF_SIZE; cnt++, add++)
+		siu_write32(add, ydef[cnt]);
+
+	/* YRAM FIR coefficient area initialization */
+	add = info->yram + (0x0200 / sizeof(u32));
+	for (cnt = 0; cnt < YRAM_FIR_SIZE; cnt++, add++)
+		siu_write32(add, fw->yram_fir_coeff[cnt]);
+
+	/* YRAM IIR coefficient area initialization */
+	add = info->yram + (0x0600 / sizeof(u32));
+	for (cnt = 0; cnt < YRAM_IIR_SIZE; cnt++, add++)
+		siu_write32(add, 0);
+
+	siu_write32(base + SIU_TRDAT, port_info->trdat);
+	port_info->trdat = 0x0;
+
+
+	/* SPB start condition: software */
+	siu_write32(base + SIU_SBACTIV, 0);
+	/* Start SPB */
+	siu_write32(base + SIU_SBCTL, 0xc0000000);
+	/* Wait for program to halt */
+	cnt = 0x10000;
+	while (--cnt && siu_read32(base + SIU_SBCTL) != 0x80000000)
+		cpu_relax();
+
+	if (!cnt)
+		return -EBUSY;
+
+	/* SPB program start address setting */
+	siu_write32(base + SIU_SBPSET, 0x00400000);
+	/* SPB hardware start(FIFOCTL source) */
+	siu_write32(base + SIU_SBACTIV, 0xc0000000);
+
+	return 0;
+}
+
+static void siu_dai_spbstop(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+
+	siu_write32(base + SIU_SBACTIV, 0);
+	/* SPB stop */
+	siu_write32(base + SIU_SBCTL, 0);
+
+	port_info->stfifo = 0;
+}
+
+/*		API functions		*/
+
+/* Playback and capture hardware properties are identical */
+static struct snd_pcm_hardware siu_dai_pcm_hw = {
+	.info			= SNDRV_PCM_INFO_INTERLEAVED,
+	.formats		= SNDRV_PCM_FMTBIT_S16,
+	.rates			= SNDRV_PCM_RATE_8000_48000,
+	.rate_min		= 8000,
+	.rate_max		= 48000,
+	.channels_min		= 2,
+	.channels_max		= 2,
+	.buffer_bytes_max	= SIU_BUFFER_BYTES_MAX,
+	.period_bytes_min	= SIU_PERIOD_BYTES_MIN,
+	.period_bytes_max	= SIU_PERIOD_BYTES_MAX,
+	.periods_min		= SIU_PERIODS_MIN,
+	.periods_max		= SIU_PERIODS_MAX,
+};
+
+static int siu_dai_info_volume(struct snd_kcontrol *kctrl,
+			       struct snd_ctl_elem_info *uinfo)
+{
+	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
+
+	dev_dbg(port_info->pcm->card->dev, "%s\n", __func__);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = SIU_MAX_VOLUME;
+
+	return 0;
+}
+
+static int siu_dai_get_volume(struct snd_kcontrol *kctrl,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
+	struct device *dev = port_info->pcm->card->dev;
+	u32 vol;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	switch (kctrl->private_value) {
+	case VOLUME_PLAYBACK:
+		/* Playback is always on port 0 */
+		vol = port_info->playback.volume;
+		ucontrol->value.integer.value[0] = vol & 0xffff;
+		ucontrol->value.integer.value[1] = vol >> 16 & 0xffff;
+		break;
+	case VOLUME_CAPTURE:
+		/* Capture is always on port 1 */
+		vol = port_info->capture.volume;
+		ucontrol->value.integer.value[0] = vol & 0xffff;
+		ucontrol->value.integer.value[1] = vol >> 16 & 0xffff;
+		break;
+	default:
+		dev_err(dev, "%s() invalid private_value=%ld\n",
+			__func__, kctrl->private_value);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int siu_dai_put_volume(struct snd_kcontrol *kctrl,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct siu_port *port_info = snd_kcontrol_chip(kctrl);
+	struct device *dev = port_info->pcm->card->dev;
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+	u32 new_vol;
+	u32 cur_vol;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	if (ucontrol->value.integer.value[0] < 0 ||
+	    ucontrol->value.integer.value[0] > SIU_MAX_VOLUME ||
+	    ucontrol->value.integer.value[1] < 0 ||
+	    ucontrol->value.integer.value[1] > SIU_MAX_VOLUME)
+		return -EINVAL;
+
+	new_vol = ucontrol->value.integer.value[0] |
+		ucontrol->value.integer.value[1] << 16;
+
+	/* See comment above - DSP firmware implementation */
+	switch (kctrl->private_value) {
+	case VOLUME_PLAYBACK:
+		/* Playback is always on port 0 */
+		cur_vol = port_info->playback.volume;
+		siu_write32(base + SIU_SBDVCA, new_vol);
+		port_info->playback.volume = new_vol;
+		break;
+	case VOLUME_CAPTURE:
+		/* Capture is always on port 1 */
+		cur_vol = port_info->capture.volume;
+		siu_write32(base + SIU_SBDVCB, new_vol);
+		port_info->capture.volume = new_vol;
+		break;
+	default:
+		dev_err(dev, "%s() invalid private_value=%ld\n",
+			__func__, kctrl->private_value);
+		return -EINVAL;
+	}
+
+	if (cur_vol != new_vol)
+		return 1;
+
+	return 0;
+}
+
+static struct snd_kcontrol_new playback_controls = {
+	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name		= "PCM Playback Volume",
+	.index		= 0,
+	.info		= siu_dai_info_volume,
+	.get		= siu_dai_get_volume,
+	.put		= siu_dai_put_volume,
+	.private_value	= VOLUME_PLAYBACK,
+};
+
+static struct snd_kcontrol_new capture_controls = {
+	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name		= "PCM Capture Volume",
+	.index		= 0,
+	.info		= siu_dai_info_volume,
+	.get		= siu_dai_get_volume,
+	.put		= siu_dai_put_volume,
+	.private_value	= VOLUME_CAPTURE,
+};
+
+int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card)
+{
+	struct device *dev = card->dev;
+	struct snd_kcontrol *kctrl;
+	int ret;
+
+	*port_info = kzalloc(sizeof(**port_info), GFP_KERNEL);
+	if (!*port_info)
+		return -ENOMEM;
+
+	dev_dbg(dev, "%s: port #%d@%p\n", __func__, port, *port_info);
+
+	(*port_info)->playback.volume = DFLT_VOLUME_LEVEL;
+	(*port_info)->capture.volume = DFLT_VOLUME_LEVEL;
+
+	/*
+	 * Add mixer support. The SPB is used to change the volume. Both
+	 * ports use the same SPB. Therefore, we only register one
+	 * control instance since it will be used by both channels.
+	 * In error case we continue without controls.
+	 */
+	kctrl = snd_ctl_new1(&playback_controls, *port_info);
+	ret = snd_ctl_add(card, kctrl);
+	if (ret < 0)
+		dev_err(dev,
+			"failed to add playback controls %p port=%d err=%d\n",
+			kctrl, port, ret);
+
+	kctrl = snd_ctl_new1(&capture_controls, *port_info);
+	ret = snd_ctl_add(card, kctrl);
+	if (ret < 0)
+		dev_err(dev,
+			"failed to add capture controls %p port=%d err=%d\n",
+			kctrl, port, ret);
+
+	return 0;
+}
+
+void siu_free_port(struct siu_port *port_info)
+{
+	kfree(port_info);
+}
+
+static int siu_dai_startup(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct snd_pcm_runtime *rt = substream->runtime;
+	struct siu_port	*port_info = siu_port_info(substream);
+	int ret;
+
+	dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__,
+		info->port_id, port_info);
+
+	snd_soc_set_runtime_hwparams(substream, &siu_dai_pcm_hw);
+
+	ret = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS);
+	if (unlikely(ret < 0))
+		return ret;
+
+	siu_dai_start(port_info);
+
+	return 0;
+}
+
+static void siu_dai_shutdown(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct siu_port	*port_info = siu_port_info(substream);
+
+	dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__,
+		info->port_id, port_info);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		port_info->play_cap &= ~PLAYBACK_ENABLED;
+	else
+		port_info->play_cap &= ~CAPTURE_ENABLED;
+
+	/* Stop the siu if the other stream is not using it */
+	if (!port_info->play_cap) {
+		/* during stmread or stmwrite ? */
+		BUG_ON(port_info->playback.rw_flg || port_info->capture.rw_flg);
+		siu_dai_spbstop(port_info);
+		siu_dai_stop();
+	}
+}
+
+/* PCM part of siu_dai_playback_prepare() / siu_dai_capture_prepare() */
+static int siu_dai_prepare(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct snd_pcm_runtime *rt = substream->runtime;
+	struct siu_port *port_info = siu_port_info(substream);
+	struct siu_stream *siu_stream;
+	int self, ret;
+
+	dev_dbg(substream->pcm->card->dev,
+		"%s: port %d, active streams %lx, %d channels\n",
+		__func__, info->port_id, port_info->play_cap, rt->channels);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		self = PLAYBACK_ENABLED;
+		siu_stream = &port_info->playback;
+	} else {
+		self = CAPTURE_ENABLED;
+		siu_stream = &port_info->capture;
+	}
+
+	/* Set up the siu if not already done */
+	if (!port_info->play_cap) {
+		siu_stream->rw_flg = 0;	/* stream-data transfer flag */
+
+		siu_dai_spbAselect(port_info);
+		siu_dai_spbBselect(port_info);
+
+		siu_dai_open(siu_stream);
+
+		siu_dai_pcmdatapack(siu_stream);
+
+		ret = siu_dai_spbstart(port_info);
+		if (ret < 0)
+			goto fail;
+	}
+
+	port_info->play_cap |= self;
+
+fail:
+	return ret;
+}
+
+/*
+ * SIU can set bus format to I2S / PCM / SPDIF independently for playback and
+ * capture, however, the current API sets the bus format globally for a DAI.
+ */
+static int siu_dai_set_fmt(struct snd_soc_dai *dai,
+			   unsigned int fmt)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+	u32 ifctl;
+
+	dev_dbg(dai->dev, "%s: fmt 0x%x on port %d\n",
+		__func__, fmt, info->port_id);
+
+	if (info->port_id < 0)
+		return -ENODEV;
+
+	/* Here select between I2S / PCM / SPDIF */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		ifctl = siu_flags[info->port_id].playback.i2s |
+			siu_flags[info->port_id].capture.i2s;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		ifctl = siu_flags[info->port_id].playback.pcm |
+			siu_flags[info->port_id].capture.pcm;
+		break;
+	/* SPDIF disabled - see comment at the top */
+	default:
+		return -EINVAL;
+	}
+
+	ifctl |= ~(siu_flags[info->port_id].playback.mask |
+		   siu_flags[info->port_id].capture.mask) &
+		siu_read32(base + SIU_IFCTL);
+	siu_write32(base + SIU_IFCTL, ifctl);
+
+	return 0;
+}
+
+static int siu_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+			      unsigned int freq, int dir)
+{
+	struct clk *siu_clk, *parent_clk;
+	char *siu_name, *parent_name;
+	int ret;
+
+	if (dir != SND_SOC_CLOCK_IN)
+		return -EINVAL;
+
+	dev_dbg(dai->dev, "%s: using clock %d\n", __func__, clk_id);
+
+	switch (clk_id) {
+	case SIU_CLKA_PLL:
+		siu_name = "siua_clk";
+		parent_name = "pll_clk";
+		break;
+	case SIU_CLKA_EXT:
+		siu_name = "siua_clk";
+		parent_name = "siumcka_clk";
+		break;
+	case SIU_CLKB_PLL:
+		siu_name = "siub_clk";
+		parent_name = "pll_clk";
+		break;
+	case SIU_CLKB_EXT:
+		siu_name = "siub_clk";
+		parent_name = "siumckb_clk";
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	siu_clk = clk_get(siu_i2s_dai.dev, siu_name);
+	if (IS_ERR(siu_clk))
+		return PTR_ERR(siu_clk);
+
+	parent_clk = clk_get(siu_i2s_dai.dev, parent_name);
+	if (!IS_ERR(parent_clk)) {
+		ret = clk_set_parent(siu_clk, parent_clk);
+		if (!ret)
+			clk_set_rate(siu_clk, freq);
+		clk_put(parent_clk);
+	}
+
+	clk_put(siu_clk);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops siu_dai_ops = {
+	.startup	= siu_dai_startup,
+	.shutdown	= siu_dai_shutdown,
+	.prepare	= siu_dai_prepare,
+	.set_sysclk	= siu_dai_set_sysclk,
+	.set_fmt	= siu_dai_set_fmt,
+};
+
+struct snd_soc_dai siu_i2s_dai = {
+	.name = "sh-siu",
+	.id = 0,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.formats = SNDRV_PCM_FMTBIT_S16,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+	},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.formats = SNDRV_PCM_FMTBIT_S16,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+	 },
+	.ops = &siu_dai_ops,
+};
+EXPORT_SYMBOL_GPL(siu_i2s_dai);
+
+static int __devinit siu_probe(struct platform_device *pdev)
+{
+	const struct firmware *fw_entry;
+	struct resource *res, *region;
+	struct siu_info *info;
+	int ret;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev);
+	if (ret)
+		goto ereqfw;
+
+	/*
+	 * Loaded firmware is "const" - read only, but we have to modify it in
+	 * snd_siu_sh7343_spbAselect() and snd_siu_sh7343_spbBselect()
+	 */
+	memcpy(&info->fw, fw_entry->data, fw_entry->size);
+
+	release_firmware(fw_entry);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		ret = -ENODEV;
+		goto egetres;
+	}
+
+	region = request_mem_region(res->start, resource_size(res),
+				    pdev->name);
+	if (!region) {
+		dev_err(&pdev->dev, "SIU region already claimed\n");
+		ret = -EBUSY;
+		goto ereqmemreg;
+	}
+
+	ret = -ENOMEM;
+	info->pram = ioremap(res->start, PRAM_SIZE);
+	if (!info->pram)
+		goto emappram;
+	info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE);
+	if (!info->xram)
+		goto emapxram;
+	info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE);
+	if (!info->yram)
+		goto emapyram;
+	info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) -
+			    REG_OFFSET);
+	if (!info->reg)
+		goto emapreg;
+
+	siu_i2s_dai.dev = &pdev->dev;
+	siu_i2s_dai.private_data = info;
+
+	ret = snd_soc_register_dais(&siu_i2s_dai, 1);
+	if (ret < 0)
+		goto edaiinit;
+
+	ret = snd_soc_register_platform(&siu_platform);
+	if (ret < 0)
+		goto esocregp;
+
+	pm_runtime_enable(&pdev->dev);
+
+	return ret;
+
+esocregp:
+	snd_soc_unregister_dais(&siu_i2s_dai, 1);
+edaiinit:
+	iounmap(info->reg);
+emapreg:
+	iounmap(info->yram);
+emapyram:
+	iounmap(info->xram);
+emapxram:
+	iounmap(info->pram);
+emappram:
+	release_mem_region(res->start, resource_size(res));
+ereqmemreg:
+egetres:
+ereqfw:
+	kfree(info);
+
+	return ret;
+}
+
+static int __devexit siu_remove(struct platform_device *pdev)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct resource *res;
+
+	pm_runtime_disable(&pdev->dev);
+
+	snd_soc_unregister_platform(&siu_platform);
+	snd_soc_unregister_dais(&siu_i2s_dai, 1);
+
+	iounmap(info->reg);
+	iounmap(info->yram);
+	iounmap(info->xram);
+	iounmap(info->pram);
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res)
+		release_mem_region(res->start, resource_size(res));
+	kfree(info);
+
+	return 0;
+}
+
+static struct platform_driver siu_driver = {
+	.driver 	= {
+		.name	= "sh_siu",
+	},
+	.probe		= siu_probe,
+	.remove		= __devexit_p(siu_remove),
+};
+
+static int __init siu_init(void)
+{
+	return platform_driver_register(&siu_driver);
+}
+
+static void __exit siu_exit(void)
+{
+	platform_driver_unregister(&siu_driver);
+}
+
+module_init(siu_init)
+module_exit(siu_exit)
+
+MODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>");
+MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c
new file mode 100644
index 0000000..c5efc30
--- /dev/null
+++ b/sound/soc/sh/siu_pcm.c
@@ -0,0 +1,616 @@
+/*
+ * siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral.
+ *
+ * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dai.h>
+
+#include <asm/dma-sh.h>
+#include <asm/siu.h>
+
+#include "siu.h"
+
+#define GET_MAX_PERIODS(buf_bytes, period_bytes) \
+				((buf_bytes) / (period_bytes))
+#define PERIOD_OFFSET(buf_addr, period_num, period_bytes) \
+				((buf_addr) + ((period_num) * (period_bytes)))
+
+#define RWF_STM_RD		0x01		/* Read in progress */
+#define RWF_STM_WT		0x02		/* Write in progress */
+
+struct siu_port *siu_ports[SIU_PORT_NUM];
+
+/* transfersize is number of u32 dma transfers per period */
+static int siu_pcm_stmwrite_stop(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+	struct siu_stream *siu_stream = &port_info->playback;
+	u32 stfifo;
+
+	if (!siu_stream->rw_flg)
+		return -EPERM;
+
+	/* output FIFO disable */
+	stfifo = siu_read32(base + SIU_STFIFO);
+	siu_write32(base + SIU_STFIFO, stfifo & ~0x0c180c18);
+	pr_debug("%s: STFIFO %x -> %x\n", __func__,
+		 stfifo, stfifo & ~0x0c180c18);
+
+	/* during stmwrite clear */
+	siu_stream->rw_flg = 0;
+
+	return 0;
+}
+
+static int siu_pcm_stmwrite_start(struct siu_port *port_info)
+{
+	struct siu_stream *siu_stream = &port_info->playback;
+
+	if (siu_stream->rw_flg)
+		return -EPERM;
+
+	/* Current period in buffer */
+	port_info->playback.cur_period = 0;
+
+	/* during stmwrite flag set */
+	siu_stream->rw_flg = RWF_STM_WT;
+
+	/* DMA transfer start */
+	tasklet_schedule(&siu_stream->tasklet);
+
+	return 0;
+}
+
+static void siu_dma_tx_complete(void *arg)
+{
+	struct siu_stream *siu_stream = arg;
+
+	if (!siu_stream->rw_flg)
+		return;
+
+	/* Update completed period count */
+	if (++siu_stream->cur_period >=
+	    GET_MAX_PERIODS(siu_stream->buf_bytes,
+			    siu_stream->period_bytes))
+		siu_stream->cur_period = 0;
+
+	pr_debug("%s: done period #%d (%u/%u bytes), cookie %d\n",
+		__func__, siu_stream->cur_period,
+		siu_stream->cur_period * siu_stream->period_bytes,
+		siu_stream->buf_bytes, siu_stream->cookie);
+
+	tasklet_schedule(&siu_stream->tasklet);
+
+	/* Notify alsa: a period is done */
+	snd_pcm_period_elapsed(siu_stream->substream);
+}
+
+static int siu_pcm_wr_set(struct siu_port *port_info,
+			  dma_addr_t buff, u32 size)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+	struct siu_stream *siu_stream = &port_info->playback;
+	struct snd_pcm_substream *substream = siu_stream->substream;
+	struct device *dev = substream->pcm->card->dev;
+	struct dma_async_tx_descriptor *desc;
+	dma_cookie_t cookie;
+	struct scatterlist sg;
+	u32 stfifo;
+
+	sg_init_table(&sg, 1);
+	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)),
+		    size, offset_in_page(buff));
+	sg_dma_address(&sg) = buff;
+
+	desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan,
+		&sg, 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(dev, "Failed to allocate a dma descriptor\n");
+		return -ENOMEM;
+	}
+
+	desc->callback = siu_dma_tx_complete;
+	desc->callback_param = siu_stream;
+	cookie = desc->tx_submit(desc);
+	if (cookie < 0) {
+		dev_err(dev, "Failed to submit a dma transfer\n");
+		return cookie;
+	}
+
+	siu_stream->tx_desc = desc;
+	siu_stream->cookie = cookie;
+
+	dma_async_issue_pending(siu_stream->chan);
+
+	/* only output FIFO enable */
+	stfifo = siu_read32(base + SIU_STFIFO);
+	siu_write32(base + SIU_STFIFO, stfifo | (port_info->stfifo & 0x0c180c18));
+	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
+		stfifo, stfifo | (port_info->stfifo & 0x0c180c18));
+
+	return 0;
+}
+
+static int siu_pcm_rd_set(struct siu_port *port_info,
+			  dma_addr_t buff, size_t size)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+	struct siu_stream *siu_stream = &port_info->capture;
+	struct snd_pcm_substream *substream = siu_stream->substream;
+	struct device *dev = substream->pcm->card->dev;
+	struct dma_async_tx_descriptor *desc;
+	dma_cookie_t cookie;
+	struct scatterlist sg;
+	u32 stfifo;
+
+	dev_dbg(dev, "%s: %u@%llx\n", __func__, size, (unsigned long long)buff);
+
+	sg_init_table(&sg, 1);
+	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)),
+		    size, offset_in_page(buff));
+	sg_dma_address(&sg) = buff;
+
+	desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan,
+		&sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(dev, "Failed to allocate dma descriptor\n");
+		return -ENOMEM;
+	}
+
+	desc->callback = siu_dma_tx_complete;
+	desc->callback_param = siu_stream;
+	cookie = desc->tx_submit(desc);
+	if (cookie < 0) {
+		dev_err(dev, "Failed to submit dma descriptor\n");
+		return cookie;
+	}
+
+	siu_stream->tx_desc = desc;
+	siu_stream->cookie = cookie;
+
+	dma_async_issue_pending(siu_stream->chan);
+
+	/* only input FIFO enable */
+	stfifo = siu_read32(base + SIU_STFIFO);
+	siu_write32(base + SIU_STFIFO, siu_read32(base + SIU_STFIFO) |
+		    (port_info->stfifo & 0x13071307));
+	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
+		stfifo, stfifo | (port_info->stfifo & 0x13071307));
+
+	return 0;
+}
+
+static void siu_io_tasklet(unsigned long data)
+{
+	struct siu_stream *siu_stream = (struct siu_stream *)data;
+	struct snd_pcm_substream *substream = siu_stream->substream;
+	struct device *dev = substream->pcm->card->dev;
+	struct snd_pcm_runtime *rt = substream->runtime;
+	struct siu_port *port_info = siu_port_info(substream);
+
+	dev_dbg(dev, "%s: flags %x\n", __func__, siu_stream->rw_flg);
+
+	if (!siu_stream->rw_flg) {
+		dev_dbg(dev, "%s: stream inactive\n", __func__);
+		return;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		dma_addr_t buff;
+		size_t count;
+		u8 *virt;
+
+		buff = (dma_addr_t)PERIOD_OFFSET(rt->dma_addr,
+						siu_stream->cur_period,
+						siu_stream->period_bytes);
+		virt = PERIOD_OFFSET(rt->dma_area,
+				     siu_stream->cur_period,
+				     siu_stream->period_bytes);
+		count = siu_stream->period_bytes;
+
+		/* DMA transfer start */
+		siu_pcm_rd_set(port_info, buff, count);
+	} else {
+		siu_pcm_wr_set(port_info,
+			       (dma_addr_t)PERIOD_OFFSET(rt->dma_addr,
+						siu_stream->cur_period,
+						siu_stream->period_bytes),
+			       siu_stream->period_bytes);
+	}
+}
+
+/* Capture */
+static int siu_pcm_stmread_start(struct siu_port *port_info)
+{
+	struct siu_stream *siu_stream = &port_info->capture;
+
+	if (siu_stream->xfer_cnt > 0x1000000)
+		return -EINVAL;
+	if (siu_stream->rw_flg)
+		return -EPERM;
+
+	/* Current period in buffer */
+	siu_stream->cur_period = 0;
+
+	/* during stmread flag set */
+	siu_stream->rw_flg = RWF_STM_RD;
+
+	tasklet_schedule(&siu_stream->tasklet);
+
+	return 0;
+}
+
+static int siu_pcm_stmread_stop(struct siu_port *port_info)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+	struct siu_stream *siu_stream = &port_info->capture;
+	struct device *dev = siu_stream->substream->pcm->card->dev;
+	u32 stfifo;
+
+	if (!siu_stream->rw_flg)
+		return -EPERM;
+
+	/* input FIFO disable */
+	stfifo = siu_read32(base + SIU_STFIFO);
+	siu_write32(base + SIU_STFIFO, stfifo & ~0x13071307);
+	dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__,
+		stfifo, stfifo & ~0x13071307);
+
+	/* during stmread flag clear */
+	siu_stream->rw_flg = 0;
+
+	return 0;
+}
+
+static int siu_pcm_hw_params(struct snd_pcm_substream *ss,
+			     struct snd_pcm_hw_params *hw_params)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct device *dev = ss->pcm->card->dev;
+	int ret;
+
+	dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
+
+	ret = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params));
+	if (ret < 0)
+		dev_err(dev, "snd_pcm_lib_malloc_pages() failed\n");
+
+	return ret;
+}
+
+static int siu_pcm_hw_free(struct snd_pcm_substream *ss)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct siu_port	*port_info = siu_port_info(ss);
+	struct device *dev = ss->pcm->card->dev;
+	struct siu_stream *siu_stream;
+
+	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		siu_stream = &port_info->playback;
+	else
+		siu_stream = &port_info->capture;
+
+	dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
+
+	return snd_pcm_lib_free_pages(ss);
+}
+
+static bool filter(struct dma_chan *chan, void *slave)
+{
+	struct sh_dmae_slave *param = slave;
+
+	pr_debug("%s: slave ID %d\n", __func__, param->slave_id);
+
+	if (unlikely(param->dma_dev != chan->device->dev))
+		return false;
+
+	chan->private = param;
+	return true;
+}
+
+static int siu_pcm_open(struct snd_pcm_substream *ss)
+{
+	/* Playback / Capture */
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct siu_port *port_info = siu_port_info(ss);
+	struct siu_stream *siu_stream;
+	u32 port = info->port_id;
+	struct siu_platform *pdata = siu_i2s_dai.dev->platform_data;
+	struct device *dev = ss->pcm->card->dev;
+	dma_cap_mask_t mask;
+	struct sh_dmae_slave *param;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	dev_dbg(dev, "%s, port=%d@%p\n", __func__, port, port_info);
+
+	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		siu_stream = &port_info->playback;
+		param = &siu_stream->param;
+		param->slave_id = port ? SHDMA_SLAVE_SIUB_TX :
+			SHDMA_SLAVE_SIUA_TX;
+	} else {
+		siu_stream = &port_info->capture;
+		param = &siu_stream->param;
+		param->slave_id = port ? SHDMA_SLAVE_SIUB_RX :
+			SHDMA_SLAVE_SIUA_RX;
+	}
+
+	param->dma_dev = pdata->dma_dev;
+	/* Get DMA channel */
+	siu_stream->chan = dma_request_channel(mask, filter, param);
+	if (!siu_stream->chan) {
+		dev_err(dev, "DMA channel allocation failed!\n");
+		return -EBUSY;
+	}
+
+	siu_stream->substream = ss;
+
+	return 0;
+}
+
+static int siu_pcm_close(struct snd_pcm_substream *ss)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct device *dev = ss->pcm->card->dev;
+	struct siu_port *port_info = siu_port_info(ss);
+	struct siu_stream *siu_stream;
+
+	dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id);
+
+	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		siu_stream = &port_info->playback;
+	else
+		siu_stream = &port_info->capture;
+
+	dma_release_channel(siu_stream->chan);
+	siu_stream->chan = NULL;
+
+	siu_stream->substream = NULL;
+
+	return 0;
+}
+
+static int siu_pcm_prepare(struct snd_pcm_substream *ss)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct siu_port *port_info = siu_port_info(ss);
+	struct device *dev = ss->pcm->card->dev;
+	struct snd_pcm_runtime 	*rt = ss->runtime;
+	struct siu_stream *siu_stream;
+	snd_pcm_sframes_t xfer_cnt;
+
+	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		siu_stream = &port_info->playback;
+	else
+		siu_stream = &port_info->capture;
+
+	rt = siu_stream->substream->runtime;
+
+	siu_stream->buf_bytes = snd_pcm_lib_buffer_bytes(ss);
+	siu_stream->period_bytes = snd_pcm_lib_period_bytes(ss);
+
+	dev_dbg(dev, "%s: port=%d, %d channels, period=%u bytes\n", __func__,
+		info->port_id, rt->channels, siu_stream->period_bytes);
+
+	/* We only support buffers that are multiples of the period */
+	if (siu_stream->buf_bytes % siu_stream->period_bytes) {
+		dev_err(dev, "%s() - buffer=%d not multiple of period=%d\n",
+		       __func__, siu_stream->buf_bytes,
+		       siu_stream->period_bytes);
+		return -EINVAL;
+	}
+
+	xfer_cnt = bytes_to_frames(rt, siu_stream->period_bytes);
+	if (!xfer_cnt || xfer_cnt > 0x1000000)
+		return -EINVAL;
+
+	siu_stream->format = rt->format;
+	siu_stream->xfer_cnt = xfer_cnt;
+
+	dev_dbg(dev, "port=%d buf=%lx buf_bytes=%d period_bytes=%d "
+		"format=%d channels=%d xfer_cnt=%d\n", info->port_id,
+		(unsigned long)rt->dma_addr, siu_stream->buf_bytes,
+		siu_stream->period_bytes,
+		siu_stream->format, rt->channels, (int)xfer_cnt);
+
+	return 0;
+}
+
+static int siu_pcm_trigger(struct snd_pcm_substream *ss, int cmd)
+{
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct device *dev = ss->pcm->card->dev;
+	struct siu_port *port_info = siu_port_info(ss);
+	int ret;
+
+	dev_dbg(dev, "%s: port=%d@%p, cmd=%d\n", __func__,
+		info->port_id, port_info, cmd);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			ret = siu_pcm_stmwrite_start(port_info);
+		else
+			ret = siu_pcm_stmread_start(port_info);
+
+		if (ret < 0)
+			dev_warn(dev, "%s: start failed on port=%d\n",
+				 __func__, info->port_id);
+
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			siu_pcm_stmwrite_stop(port_info);
+		else
+			siu_pcm_stmread_stop(port_info);
+		ret = 0;
+
+		break;
+	default:
+		dev_err(dev, "%s() unsupported cmd=%d\n", __func__, cmd);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/*
+ * So far only resolution of one period is supported, subject to extending the
+ * dmangine API
+ */
+static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss)
+{
+	struct device *dev = ss->pcm->card->dev;
+	struct siu_info *info = siu_i2s_dai.private_data;
+	u32 __iomem *base = info->reg;
+	struct siu_port *port_info = siu_port_info(ss);
+	struct snd_pcm_runtime *rt = ss->runtime;
+	size_t ptr;
+	struct siu_stream *siu_stream;
+
+	if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		siu_stream = &port_info->playback;
+	else
+		siu_stream = &port_info->capture;
+
+	/*
+	 * ptr is the offset into the buffer where the dma is currently at. We
+	 * check if the dma buffer has just wrapped.
+	 */
+	ptr = PERIOD_OFFSET(rt->dma_addr,
+			    siu_stream->cur_period,
+			    siu_stream->period_bytes) - rt->dma_addr;
+
+	dev_dbg(dev,
+		"%s: port=%d, events %x, FSTS %x, xferred %u/%u, cookie %d\n",
+		__func__, info->port_id, siu_read32(base + SIU_EVNTC),
+		siu_read32(base + SIU_SBFSTS), ptr, siu_stream->buf_bytes,
+		siu_stream->cookie);
+
+	if (ptr >= siu_stream->buf_bytes)
+		ptr = 0;
+
+	return bytes_to_frames(ss->runtime, ptr);
+}
+
+static int siu_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+		       struct snd_pcm *pcm)
+{
+	/* card->dev == socdev->dev, see snd_soc_new_pcms() */
+	struct siu_info *info = siu_i2s_dai.private_data;
+	struct platform_device *pdev = to_platform_device(card->dev);
+	int ret;
+	int i;
+
+	/* pdev->id selects between SIUA and SIUB */
+	if (pdev->id < 0 || pdev->id >= SIU_PORT_NUM)
+		return -EINVAL;
+
+	info->port_id = pdev->id;
+
+	/*
+	 * While the siu has 2 ports, only one port can be on at a time (only 1
+	 * SPB). So far all the boards using the siu had only one of the ports
+	 * wired to a codec. To simplify things, we only register one port with
+	 * alsa. In case both ports are needed, it should be changed here
+	 */
+	for (i = pdev->id; i < pdev->id + 1; i++) {
+		struct siu_port **port_info = &siu_ports[i];
+
+		ret = siu_init_port(i, port_info, card);
+		if (ret < 0)
+			return ret;
+
+		ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
+				SNDRV_DMA_TYPE_DEV, NULL,
+				SIU_BUFFER_BYTES_MAX, SIU_BUFFER_BYTES_MAX);
+		if (ret < 0) {
+			dev_err(card->dev,
+			       "snd_pcm_lib_preallocate_pages_for_all() err=%d",
+				ret);
+			goto fail;
+		}
+
+		(*port_info)->pcm = pcm;
+
+		/* IO tasklets */
+		tasklet_init(&(*port_info)->playback.tasklet, siu_io_tasklet,
+			     (unsigned long)&(*port_info)->playback);
+		tasklet_init(&(*port_info)->capture.tasklet, siu_io_tasklet,
+			     (unsigned long)&(*port_info)->capture);
+	}
+
+	dev_info(card->dev, "SuperH SIU driver initialized.\n");
+	return 0;
+
+fail:
+	siu_free_port(siu_ports[pdev->id]);
+	dev_err(card->dev, "SIU: failed to initialize.\n");
+	return ret;
+}
+
+static void siu_pcm_free(struct snd_pcm *pcm)
+{
+	struct platform_device *pdev = to_platform_device(pcm->card->dev);
+	struct siu_port *port_info = siu_ports[pdev->id];
+
+	tasklet_kill(&port_info->capture.tasklet);
+	tasklet_kill(&port_info->playback.tasklet);
+
+	siu_free_port(port_info);
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+
+	dev_dbg(pcm->card->dev, "%s\n", __func__);
+}
+
+static struct snd_pcm_ops siu_pcm_ops = {
+	.open		= siu_pcm_open,
+	.close		= siu_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= siu_pcm_hw_params,
+	.hw_free	= siu_pcm_hw_free,
+	.prepare	= siu_pcm_prepare,
+	.trigger	= siu_pcm_trigger,
+	.pointer	= siu_pcm_pointer_dma,
+};
+
+struct snd_soc_platform siu_platform = {
+	.name		= "siu-audio",
+	.pcm_ops 	= &siu_pcm_ops,
+	.pcm_new	= siu_pcm_new,
+	.pcm_free	= siu_pcm_free,
+};
+EXPORT_SYMBOL_GPL(siu_platform);
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index d2505e8..5869dc3 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -15,6 +15,74 @@
 #include <linux/spi/spi.h>
 #include <sound/soc.h>
 
+static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
+				     unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= codec->reg_cache_size)
+		return -1;
+	return cache[reg];
+}
+
+static int snd_soc_4_12_write(struct snd_soc_codec *codec, unsigned int reg,
+			     unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	u8 data[2];
+	int ret;
+
+	BUG_ON(codec->volatile_register);
+
+	data[0] = (reg << 4) | ((value >> 8) & 0x000f);
+	data[1] = value & 0x00ff;
+
+	if (reg < codec->reg_cache_size)
+		cache[reg] = value;
+
+	if (codec->cache_only) {
+		codec->cache_sync = 1;
+		return 0;
+	}
+
+	ret = codec->hw_write(codec->control_data, data, 2);
+	if (ret == 2)
+		return 0;
+	if (ret < 0)
+		return ret;
+	else
+		return -EIO;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int snd_soc_4_12_spi_write(void *control_data, const char *data,
+				 int len)
+{
+	struct spi_device *spi = control_data;
+	struct spi_transfer t;
+	struct spi_message m;
+	u8 msg[2];
+
+	if (len <= 0)
+		return 0;
+
+	msg[0] = data[1];
+	msg[1] = data[0];
+
+	spi_message_init(&m);
+	memset(&t, 0, (sizeof t));
+
+	t.tx_buf = &msg[0];
+	t.len = len;
+
+	spi_message_add_tail(&t, &m);
+	spi_sync(spi, &m);
+
+	return len;
+}
+#else
+#define snd_soc_4_12_spi_write NULL
+#endif
+
 static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
 				     unsigned int reg)
 {
@@ -38,6 +106,12 @@
 
 	if (reg < codec->reg_cache_size)
 		cache[reg] = value;
+
+	if (codec->cache_only) {
+		codec->cache_sync = 1;
+		return 0;
+	}
+
 	ret = codec->hw_write(codec->control_data, data, 2);
 	if (ret == 2)
 		return 0;
@@ -91,6 +165,11 @@
 	if (reg < codec->reg_cache_size)
 		cache[reg] = value;
 
+	if (codec->cache_only) {
+		codec->cache_sync = 1;
+		return 0;
+	}
+
 	if (codec->hw_write(codec->control_data, data, 2) == 2)
 		return 0;
 	else
@@ -119,6 +198,11 @@
 	if (!snd_soc_codec_volatile_register(codec, reg))
 		reg_cache[reg] = value;
 
+	if (codec->cache_only) {
+		codec->cache_sync = 1;
+		return 0;
+	}
+
 	if (codec->hw_write(codec->control_data, data, 3) == 3)
 		return 0;
 	else
@@ -131,10 +215,14 @@
 	u16 *cache = codec->reg_cache;
 
 	if (reg >= codec->reg_cache_size ||
-	    snd_soc_codec_volatile_register(codec, reg))
+	    snd_soc_codec_volatile_register(codec, reg)) {
+		if (codec->cache_only)
+			return -EINVAL;
+
 		return codec->hw_read(codec, reg);
-	else
+	} else {
 		return cache[reg];
+	}
 }
 
 #if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
@@ -171,6 +259,114 @@
 #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)
+{
+	struct i2c_msg xfer[2];
+	u16 reg = r;
+	u8 data;
+	int ret;
+	struct i2c_client *client = codec->control_data;
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 2;
+	xfer[0].buf = (u8 *)&reg;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = 1;
+	xfer[1].buf = &data;
+
+	ret = i2c_transfer(client->adapter, xfer, 2);
+	if (ret != 2) {
+		dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+		return 0;
+	}
+
+	return data;
+}
+#else
+#define snd_soc_16_8_read_i2c NULL
+#endif
+
+static unsigned int snd_soc_16_8_read(struct snd_soc_codec *codec,
+				     unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+
+	reg &= 0xff;
+	if (reg >= codec->reg_cache_size)
+		return -1;
+	return cache[reg];
+}
+
+static int snd_soc_16_8_write(struct snd_soc_codec *codec, unsigned int reg,
+			     unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	u8 data[3];
+	int ret;
+
+	BUG_ON(codec->volatile_register);
+
+	data[0] = (reg >> 8) & 0xff;
+	data[1] = reg & 0xff;
+	data[2] = value;
+
+	reg &= 0xff;
+	if (reg < codec->reg_cache_size)
+		cache[reg] = value;
+
+	if (codec->cache_only) {
+		codec->cache_sync = 1;
+		return 0;
+	}
+
+	ret = codec->hw_write(codec->control_data, data, 3);
+	if (ret == 3)
+		return 0;
+	if (ret < 0)
+		return ret;
+	else
+		return -EIO;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int snd_soc_16_8_spi_write(void *control_data, const char *data,
+				 int len)
+{
+	struct spi_device *spi = control_data;
+	struct spi_transfer t;
+	struct spi_message m;
+	u8 msg[3];
+
+	if (len <= 0)
+		return 0;
+
+	msg[0] = data[0];
+	msg[1] = data[1];
+	msg[2] = data[2];
+
+	spi_message_init(&m);
+	memset(&t, 0, (sizeof t));
+
+	t.tx_buf = &msg[0];
+	t.len = len;
+
+	spi_message_add_tail(&t, &m);
+	spi_sync(spi, &m);
+
+	return len;
+}
+#else
+#define snd_soc_16_8_spi_write NULL
+#endif
+
+
 static struct {
 	int addr_bits;
 	int data_bits;
@@ -180,9 +376,14 @@
 	unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
 } io_types[] = {
 	{
+		.addr_bits = 4, .data_bits = 12,
+		.write = snd_soc_4_12_write, .read = snd_soc_4_12_read,
+		.spi_write = snd_soc_4_12_spi_write,
+	},
+	{
 		.addr_bits = 7, .data_bits = 9,
 		.write = snd_soc_7_9_write, .read = snd_soc_7_9_read,
-		.spi_write = snd_soc_7_9_spi_write 
+		.spi_write = snd_soc_7_9_spi_write,
 	},
 	{
 		.addr_bits = 8, .data_bits = 8,
@@ -193,6 +394,12 @@
 		.write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
 		.i2c_read = snd_soc_8_16_read_i2c,
 	},
+	{
+		.addr_bits = 16, .data_bits = 8,
+		.write = snd_soc_16_8_write, .read = snd_soc_16_8_read,
+		.i2c_read = snd_soc_16_8_read_i2c,
+		.spi_write = snd_soc_16_8_spi_write,
+	},
 };
 
 /**
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 0a6440c..c8b0556 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -130,6 +130,29 @@
 
 static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
 
+static ssize_t pmdown_time_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct snd_soc_device *socdev = dev_get_drvdata(dev);
+	struct snd_soc_card *card = socdev->card;
+
+	return sprintf(buf, "%ld\n", card->pmdown_time);
+}
+
+static ssize_t pmdown_time_set(struct device *dev,
+			       struct device_attribute *attr,
+			       const char *buf, size_t count)
+{
+	struct snd_soc_device *socdev = dev_get_drvdata(dev);
+	struct snd_soc_card *card = socdev->card;
+
+	strict_strtol(buf, 10, &card->pmdown_time);
+
+	return count;
+}
+
+static DEVICE_ATTR(pmdown_time, 0644, pmdown_time_show, pmdown_time_set);
+
 #ifdef CONFIG_DEBUG_FS
 static int codec_reg_open_file(struct inode *inode, struct file *file)
 {
@@ -404,24 +427,24 @@
 	if (!runtime->hw.rates) {
 		printk(KERN_ERR "asoc: %s <-> %s No matching rates\n",
 			codec_dai->name, cpu_dai->name);
-		goto machine_err;
+		goto config_err;
 	}
 	if (!runtime->hw.formats) {
 		printk(KERN_ERR "asoc: %s <-> %s No matching formats\n",
 			codec_dai->name, cpu_dai->name);
-		goto machine_err;
+		goto config_err;
 	}
 	if (!runtime->hw.channels_min || !runtime->hw.channels_max) {
 		printk(KERN_ERR "asoc: %s <-> %s No matching channels\n",
 			codec_dai->name, cpu_dai->name);
-		goto machine_err;
+		goto config_err;
 	}
 
 	/* Symmetry only applies if we've already got an active stream. */
 	if (cpu_dai->active || codec_dai->active) {
 		ret = soc_pcm_apply_symmetry(substream);
 		if (ret != 0)
-			goto machine_err;
+			goto config_err;
 	}
 
 	pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
@@ -441,10 +464,14 @@
 	mutex_unlock(&pcm_mutex);
 	return 0;
 
-machine_err:
+config_err:
 	if (machine->ops && machine->ops->shutdown)
 		machine->ops->shutdown(substream);
 
+machine_err:
+	if (codec_dai->ops->shutdown)
+		codec_dai->ops->shutdown(substream, codec_dai);
+
 codec_dai_err:
 	if (platform->pcm_ops->close)
 		platform->pcm_ops->close(substream);
@@ -542,7 +569,7 @@
 		/* start delayed pop wq here for playback streams */
 		codec_dai->pop_wait = 1;
 		schedule_delayed_work(&card->delayed_work,
-			msecs_to_jiffies(pmdown_time));
+			msecs_to_jiffies(card->pmdown_time));
 	} else {
 		/* capture streams can be powered down now */
 		snd_soc_dapm_stream_event(codec,
@@ -940,6 +967,12 @@
 	struct snd_soc_card *card = socdev->card;
 	struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai;
 
+	/* If the initialization of this soc device failed, there is no codec
+	 * associated with it. Just bail out in this case.
+	 */
+	if (!card->codec)
+		return 0;
+
 	/* AC97 devices might have other drivers hanging off them so
 	 * need to resume immediately.  Other drivers don't have that
 	 * problem and may take a substantial amount of time to resume
@@ -1039,6 +1072,8 @@
 	dev_dbg(card->dev, "All components present, instantiating\n");
 
 	/* Found everything, bring it up */
+	card->pmdown_time = pmdown_time;
+
 	if (card->probe) {
 		ret = card->probe(pdev);
 		if (ret < 0)
@@ -1122,6 +1157,10 @@
 	if (ret < 0)
 		printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n");
 
+	ret = device_create_file(card->socdev->dev, &dev_attr_pmdown_time);
+	if (ret < 0)
+		printk(KERN_WARNING "asoc: failed to add pmdown_time sysfs\n");
+
 	ret = device_create_file(card->socdev->dev, &dev_attr_codec_reg);
 	if (ret < 0)
 		printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
@@ -1276,8 +1315,8 @@
 	codec_dai->codec = card->codec;
 
 	/* check client and interface hw capabilities */
-	sprintf(new_name, "%s %s-%d", dai_link->stream_name, codec_dai->name,
-		num);
+	snprintf(new_name, sizeof(new_name), "%s %s-%d",
+		 dai_link->stream_name, codec_dai->name, num);
 
 	if (codec_dai->playback.channels_min)
 		playback = 1;
@@ -1368,6 +1407,7 @@
 
 	codec->ac97->bus->ops = ops;
 	codec->ac97->num = num;
+	codec->dev = &codec->ac97->dev;
 	mutex_unlock(&codec->mutex);
 	return 0;
 }
@@ -1427,9 +1467,9 @@
  *
  * Returns 1 for change else 0.
  */
-static int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
-				unsigned short reg, unsigned int mask,
-				unsigned int value)
+int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
+			       unsigned short reg, unsigned int mask,
+			       unsigned int value)
 {
 	int change;
 
@@ -1439,6 +1479,7 @@
 
 	return change;
 }
+EXPORT_SYMBOL_GPL(snd_soc_update_bits_locked);
 
 /**
  * snd_soc_test_bits - test register for change
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 0d294ef..6c33510 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -44,13 +44,6 @@
 #include <sound/soc-dapm.h>
 #include <sound/initval.h>
 
-/* debug */
-#ifdef DEBUG
-#define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
-#else
-#define dump_dapm(codec, action)
-#endif
-
 /* dapm power sequences - make this per codec in the future */
 static int dapm_up_seq[] = {
 	[snd_soc_dapm_pre] = 0,
@@ -739,6 +732,8 @@
 			    struct snd_soc_dapm_widget *b,
 			    int sort[])
 {
+	if (a->codec != b->codec)
+		return (unsigned long)a - (unsigned long)b;
 	if (sort[a->id] != sort[b->id])
 		return sort[a->id] - sort[b->id];
 	if (a->reg != b->reg)
@@ -1017,13 +1012,28 @@
 			sys_power = 0;
 			break;
 		case SND_SOC_DAPM_STREAM_NOP:
-			sys_power = codec->bias_level != SND_SOC_BIAS_STANDBY;
+			switch (codec->bias_level) {
+				case SND_SOC_BIAS_STANDBY:
+				case SND_SOC_BIAS_OFF:
+					sys_power = 0;
+					break;
+				default:
+					sys_power = 1;
+					break;
+			}
 			break;
 		default:
 			break;
 		}
 	}
 
+	if (sys_power && codec->bias_level == SND_SOC_BIAS_OFF) {
+		ret = snd_soc_dapm_set_bias_level(socdev,
+						  SND_SOC_BIAS_STANDBY);
+		if (ret != 0)
+			pr_err("Failed to turn on bias: %d\n", ret);
+	}
+
 	/* If we're changing to all on or all off then prepare */
 	if ((sys_power && codec->bias_level == SND_SOC_BIAS_STANDBY) ||
 	    (!sys_power && codec->bias_level == SND_SOC_BIAS_ON)) {
@@ -1047,6 +1057,14 @@
 			pr_err("Failed to apply standby bias: %d\n", ret);
 	}
 
+	/* If we're in standby and can support bias off then do that */
+	if (codec->bias_level == SND_SOC_BIAS_STANDBY &&
+	    codec->idle_bias_off) {
+		ret = snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_OFF);
+		if (ret != 0)
+			pr_err("Failed to turn off bias: %d\n", ret);
+	}
+
 	/* If we just powered up then move to active bias */
 	if (codec->bias_level == SND_SOC_BIAS_PREPARE && sys_power) {
 		ret = snd_soc_dapm_set_bias_level(socdev,
@@ -1061,66 +1079,6 @@
 	return 0;
 }
 
-#ifdef DEBUG
-static void dbg_dump_dapm(struct snd_soc_codec* codec, const char *action)
-{
-	struct snd_soc_dapm_widget *w;
-	struct snd_soc_dapm_path *p = NULL;
-	int in, out;
-
-	printk("DAPM %s %s\n", codec->name, action);
-
-	list_for_each_entry(w, &codec->dapm_widgets, list) {
-
-		/* only display widgets that effect routing */
-		switch (w->id) {
-		case snd_soc_dapm_pre:
-		case snd_soc_dapm_post:
-		case snd_soc_dapm_vmid:
-			continue;
-		case snd_soc_dapm_mux:
-		case snd_soc_dapm_value_mux:
-		case snd_soc_dapm_output:
-		case snd_soc_dapm_input:
-		case snd_soc_dapm_switch:
-		case snd_soc_dapm_hp:
-		case snd_soc_dapm_mic:
-		case snd_soc_dapm_spk:
-		case snd_soc_dapm_line:
-		case snd_soc_dapm_micbias:
-		case snd_soc_dapm_dac:
-		case snd_soc_dapm_adc:
-		case snd_soc_dapm_pga:
-		case snd_soc_dapm_mixer:
-		case snd_soc_dapm_mixer_named_ctl:
-		case snd_soc_dapm_supply:
-		case snd_soc_dapm_aif_in:
-		case snd_soc_dapm_aif_out:
-			if (w->name) {
-				in = is_connected_input_ep(w);
-				dapm_clear_walk(w->codec);
-				out = is_connected_output_ep(w);
-				dapm_clear_walk(w->codec);
-				printk("%s: %s  in %d out %d\n", w->name,
-					w->power ? "On":"Off",in, out);
-
-				list_for_each_entry(p, &w->sources, list_sink) {
-					if (p->connect)
-						printk(" in  %s %s\n", p->name ? p->name : "static",
-							p->source->name);
-				}
-				list_for_each_entry(p, &w->sinks, list_source) {
-					if (p->connect)
-						printk(" out %s %s\n", p->name ? p->name : "static",
-							p->sink->name);
-				}
-			}
-		break;
-		}
-	}
-}
-#endif
-
 #ifdef CONFIG_DEBUG_FS
 static int dapm_widget_power_open_file(struct inode *inode, struct file *file)
 {
@@ -1147,9 +1105,16 @@
 	out = is_connected_output_ep(w);
 	dapm_clear_walk(w->codec);
 
-	ret = snprintf(buf, PAGE_SIZE, "%s: %s  in %d out %d\n",
+	ret = snprintf(buf, PAGE_SIZE, "%s: %s  in %d out %d",
 		       w->name, w->power ? "On" : "Off", in, out);
 
+	if (w->reg >= 0)
+		ret += snprintf(buf + ret, PAGE_SIZE - ret,
+				" - R%d(0x%x) bit %d",
+				w->reg, w->reg, w->shift);
+
+	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
+
 	if (w->sname)
 		ret += snprintf(buf + ret, PAGE_SIZE - ret, " stream %s %s\n",
 				w->sname,
@@ -1245,18 +1210,15 @@
 			path->connect = 0; /* old connection must be powered down */
 	}
 
-	if (found) {
+	if (found)
 		dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
-		dump_dapm(widget->codec, "mux power update");
-	}
 
 	return 0;
 }
 
 /* test and update the power status of a mixer or switch widget */
 static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
-				   struct snd_kcontrol *kcontrol, int reg,
-				   int val_mask, int val, int invert)
+				   struct snd_kcontrol *kcontrol, int connect)
 {
 	struct snd_soc_dapm_path *path;
 	int found = 0;
@@ -1266,9 +1228,6 @@
 	    widget->id != snd_soc_dapm_switch)
 		return -ENODEV;
 
-	if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
-		return 0;
-
 	/* find dapm widget path assoc with kcontrol */
 	list_for_each_entry(path, &widget->codec->dapm_paths, list) {
 		if (path->kcontrol != kcontrol)
@@ -1276,19 +1235,12 @@
 
 		/* found, now check type */
 		found = 1;
-		if (val)
-			/* new connection */
-			path->connect = invert ? 0:1;
-		else
-			/* old connection must be powered down */
-			path->connect = invert ? 1:0;
+		path->connect = connect;
 		break;
 	}
 
-	if (found) {
+	if (found)
 		dapm_power_widgets(widget->codec, SND_SOC_DAPM_STREAM_NOP);
-		dump_dapm(widget->codec, "mixer power update");
-	}
 
 	return 0;
 }
@@ -1404,9 +1356,7 @@
  */
 int snd_soc_dapm_sync(struct snd_soc_codec *codec)
 {
-	int ret = dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
-	dump_dapm(codec, "sync");
-	return ret;
+	return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP);
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
@@ -1688,6 +1638,7 @@
 	unsigned int mask = (1 << fls(max)) - 1;
 	unsigned int invert = mc->invert;
 	unsigned int val, val2, val_mask;
+	int connect;
 	int ret;
 
 	val = (ucontrol->value.integer.value[0] & mask);
@@ -1714,7 +1665,17 @@
 		return 1;
 	}
 
-	dapm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
+	if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) {
+		if (val)
+			/* new connection */
+			connect = invert ? 0:1;
+		else
+			/* old connection must be powered down */
+			connect = invert ? 1:0;
+
+		dapm_mixer_update_power(widget, kcontrol, connect);
+	}
+
 	if (widget->event) {
 		if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
 			ret = widget->event(widget, kcontrol,
@@ -2152,7 +2113,6 @@
 
 	dapm_power_widgets(codec, event);
 	mutex_unlock(&codec->mutex);
-	dump_dapm(codec, __func__);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index ddc584b..4b91d8c 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -705,7 +705,7 @@
 		fprintf(stdout, "\t[w]     toggle display weighted/count[E]r. \t(%d)\n", display_weighted ? 1 : 0);
 
 	fprintf(stdout,
-		"\t[K]     hide kernel_symbols symbols.             \t(%s)\n",
+		"\t[K]     hide kernel_symbols symbols.     \t(%s)\n",
 		hide_kernel_symbols ? "yes" : "no");
 	fprintf(stdout,
 		"\t[U]     hide user symbols.               \t(%s)\n",
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index bb0fd6d..8a9e6ba 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -295,10 +295,10 @@
 	al->thread = self;
 	al->addr = addr;
 
-	if (cpumode & PERF_RECORD_MISC_KERNEL) {
+	if (cpumode == PERF_RECORD_MISC_KERNEL) {
 		al->level = 'k';
 		mg = &session->kmaps;
-	} else if (cpumode & PERF_RECORD_MISC_USER)
+	} else if (cpumode == PERF_RECORD_MISC_USER)
 		al->level = '.';
 	else {
 		al->level = 'H';
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 29465d4..fde17b0 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -272,6 +272,7 @@
 	int ret;
 
 	pp->probes[0] = buf = zalloc(MAX_CMDLEN);
+	pp->found = 1;
 	if (!buf)
 		die("Failed to allocate memory by zalloc.");
 	if (pp->offset) {
@@ -294,6 +295,7 @@
 error:
 		free(pp->probes[0]);
 		pp->probes[0] = NULL;
+		pp->found = 0;
 	}
 	return ret;
 }
@@ -455,6 +457,7 @@
 	struct strlist *rawlist;
 	struct str_node *ent;
 
+	memset(&pp, 0, sizeof(pp));
 	fd = open_kprobe_events(O_RDONLY, 0);
 	rawlist = get_trace_kprobe_event_rawlist(fd);
 	close(fd);