Merge branch 'for-2.6.32' into for-2.6.33
diff --git a/arch/arm/mach-omap2/board-3430sdp.c b/arch/arm/mach-omap2/board-3430sdp.c
index 0acb556..87e6965 100644
--- a/arch/arm/mach-omap2/board-3430sdp.c
+++ b/arch/arm/mach-omap2/board-3430sdp.c
@@ -410,6 +410,14 @@
 	.consumer_supplies	= &sdp3430_vdvi_supply,
 };
 
+static struct twl4030_codec_audio_data sdp3430_audio = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data sdp3430_codec = {
+	.audio = &sdp3430_audio,
+};
+
 static struct twl4030_platform_data sdp3430_twldata = {
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_end	= TWL4030_IRQ_END,
@@ -420,6 +428,7 @@
 	.madc		= &sdp3430_madc_data,
 	.keypad		= &sdp3430_kp_data,
 	.usb		= &sdp3430_usb_data,
+	.codec		= &sdp3430_codec,
 
 	.vaux1		= &sdp3430_vaux1,
 	.vaux2		= &sdp3430_vaux2,
diff --git a/arch/arm/mach-omap2/board-omap3beagle.c b/arch/arm/mach-omap2/board-omap3beagle.c
index 70df6b4..2161d85 100644
--- a/arch/arm/mach-omap2/board-omap3beagle.c
+++ b/arch/arm/mach-omap2/board-omap3beagle.c
@@ -254,6 +254,14 @@
 	.usb_mode	= T2_USB_MODE_ULPI,
 };
 
+static struct twl4030_codec_audio_data beagle_audio_data = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data beagle_codec_data = {
+	.audio = &beagle_audio_data,
+};
+
 static struct twl4030_platform_data beagle_twldata = {
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_end	= TWL4030_IRQ_END,
@@ -261,6 +269,7 @@
 	/* platform_data for children goes here */
 	.usb		= &beagle_usb_data,
 	.gpio		= &beagle_gpio_data,
+	.codec		= &beagle_codec_data,
 	.vmmc1		= &beagle_vmmc1,
 	.vsim		= &beagle_vsim,
 	.vdac		= &beagle_vdac,
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c
index 4c4d7f8d..c0d9736 100644
--- a/arch/arm/mach-omap2/board-omap3evm.c
+++ b/arch/arm/mach-omap2/board-omap3evm.c
@@ -194,6 +194,14 @@
 	.irq_line	= 1,
 };
 
+static struct twl4030_codec_audio_data omap3evm_audio_data = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data omap3evm_codec_data = {
+	.audio = &omap3evm_audio_data,
+};
+
 static struct twl4030_platform_data omap3evm_twldata = {
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_end	= TWL4030_IRQ_END,
@@ -203,6 +211,7 @@
 	.madc		= &omap3evm_madc_data,
 	.usb		= &omap3evm_usb_data,
 	.gpio		= &omap3evm_gpio_data,
+	.codec		= &omap3evm_codec_data,
 };
 
 static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = {
diff --git a/arch/arm/mach-omap2/board-omap3pandora.c b/arch/arm/mach-omap2/board-omap3pandora.c
index 5326e0d..74f7574 100644
--- a/arch/arm/mach-omap2/board-omap3pandora.c
+++ b/arch/arm/mach-omap2/board-omap3pandora.c
@@ -281,11 +281,20 @@
 	.usb_mode	= T2_USB_MODE_ULPI,
 };
 
+static struct twl4030_codec_audio_data omap3pandora_audio_data = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data omap3pandora_codec_data = {
+	.audio = &omap3pandora_audio_data,
+};
+
 static struct twl4030_platform_data omap3pandora_twldata = {
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_end	= TWL4030_IRQ_END,
 	.gpio		= &omap3pandora_gpio_data,
 	.usb		= &omap3pandora_usb_data,
+	.codec		= &omap3pandora_codec_data,
 	.vmmc1		= &pandora_vmmc1,
 	.vmmc2		= &pandora_vmmc2,
 	.keypad		= &pandora_kp_data,
diff --git a/arch/arm/mach-omap2/board-overo.c b/arch/arm/mach-omap2/board-overo.c
index 9917d2f..dc55008 100644
--- a/arch/arm/mach-omap2/board-overo.c
+++ b/arch/arm/mach-omap2/board-overo.c
@@ -329,6 +329,14 @@
 	.consumer_supplies	= &overo_vmmc1_supply,
 };
 
+static struct twl4030_codec_audio_data overo_audio_data = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data overo_codec_data = {
+	.audio = &overo_audio_data,
+};
+
 /* mmc2 (WLAN) and Bluetooth don't use twl4030 regulators */
 
 static struct twl4030_platform_data overo_twldata = {
@@ -336,6 +344,7 @@
 	.irq_end	= TWL4030_IRQ_END,
 	.gpio		= &overo_gpio_data,
 	.usb		= &overo_usb_data,
+	.codec		= &overo_codec_data,
 	.vmmc1		= &overo_vmmc1,
 };
 
diff --git a/arch/arm/mach-omap2/board-zoom2.c b/arch/arm/mach-omap2/board-zoom2.c
index ea00486..e04a4f0 100644
--- a/arch/arm/mach-omap2/board-zoom2.c
+++ b/arch/arm/mach-omap2/board-zoom2.c
@@ -231,6 +231,14 @@
 	.irq_line	= 1,
 };
 
+static struct twl4030_codec_audio_data zoom2_audio_data = {
+	.audio_mclk = 26000000,
+};
+
+static struct twl4030_codec_data zoom2_codec_data = {
+	.audio = &zoom2_audio_data,
+};
+
 static struct twl4030_platform_data zoom2_twldata = {
 	.irq_base	= TWL4030_IRQ_BASE,
 	.irq_end	= TWL4030_IRQ_END,
@@ -241,6 +249,7 @@
 	.usb		= &zoom2_usb_data,
 	.gpio		= &zoom2_gpio_data,
 	.keypad		= &zoom2_kp_twl4030_data,
+	.codec		= &zoom2_codec_data,
 	.vmmc1          = &zoom2_vmmc1,
 	.vmmc2          = &zoom2_vmmc2,
 	.vsim           = &zoom2_vsim,
diff --git a/arch/arm/plat-s3c/include/plat/audio.h b/arch/arm/plat-s3c/include/plat/audio.h
deleted file mode 100644
index de0e8da..0000000
--- a/arch/arm/plat-s3c/include/plat/audio.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* arch/arm/mach-s3c2410/include/mach/audio.h
- *
- * Copyright (c) 2004-2005 Simtec Electronics
- *	http://www.simtec.co.uk/products/SWLINUX/
- *	Ben Dooks <ben@simtec.co.uk>
- *
- * S3C24XX - Audio platfrom_device info
- *
- * 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_ARCH_AUDIO_H
-#define __ASM_ARCH_AUDIO_H __FILE__
-
-/* struct s3c24xx_iis_ops
- *
- * called from the s3c24xx audio core to deal with the architecture
- * or the codec's setup and control.
- *
- * the pointer to itself is passed through in case the caller wants to
- * embed this in an larger structure for easy reference to it's context.
-*/
-
-struct s3c24xx_iis_ops {
-	struct module *owner;
-
-	int	(*startup)(struct s3c24xx_iis_ops *me);
-	void	(*shutdown)(struct s3c24xx_iis_ops *me);
-	int	(*suspend)(struct s3c24xx_iis_ops *me);
-	int	(*resume)(struct s3c24xx_iis_ops *me);
-
-	int	(*open)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
-	int	(*close)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm);
-	int	(*prepare)(struct s3c24xx_iis_ops *me, struct snd_pcm_substream *strm, struct snd_pcm_runtime *rt);
-};
-
-struct s3c24xx_platdata_iis {
-	const char		*codec_clk;
-	struct s3c24xx_iis_ops	*ops;
-	int			(*match_dev)(struct device *dev);
-};
-
-#endif /* __ASM_ARCH_AUDIO_H */
diff --git a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
index 07659da..abf2fbc 100644
--- a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
+++ b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
@@ -67,6 +67,8 @@
 #define S3C2412_IISMOD_BCLK_MASK	(3 << 1)
 #define S3C2412_IISMOD_8BIT		(1 << 0)
 
+#define S3C64XX_IISMOD_CDCLKCON		(1 << 12)
+
 #define S3C2412_IISPSR_PSREN		(1 << 15)
 
 #define S3C2412_IISFIC_TXFLUSH		(1 << 15)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 570be13..08f2d07 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -121,6 +121,12 @@
 	  and load scripts controling which resources are switched off/on
 	  or reset when a sleep, wakeup or warm reset event occurs.
 
+config TWL4030_CODEC
+	bool
+	depends on TWL4030_CORE
+	select MFD_CORE
+	default n
+
 config MFD_TMIO
 	bool
 	default n
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f3b277b..af0fc90 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -26,6 +26,7 @@
 
 obj-$(CONFIG_TWL4030_CORE)	+= twl4030-core.o twl4030-irq.o
 obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
+obj-$(CONFIG_TWL4030_CODEC)	+= twl4030-codec.o
 
 obj-$(CONFIG_MFD_MC13783)	+= mc13783-core.o
 
diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c
new file mode 100644
index 0000000..9710307
--- /dev/null
+++ b/drivers/mfd/twl4030-codec.c
@@ -0,0 +1,241 @@
+/*
+ * MFD driver for twl4030 codec submodule
+ *
+ * Author:	Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * 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/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/twl4030-codec.h>
+
+#define TWL4030_CODEC_CELLS	2
+
+static struct platform_device *twl4030_codec_dev;
+
+struct twl4030_codec_resource {
+	int request_count;
+	u8 reg;
+	u8 mask;
+};
+
+struct twl4030_codec {
+	struct mutex mutex;
+	struct twl4030_codec_resource resource[TWL4030_CODEC_RES_MAX];
+	struct mfd_cell cells[TWL4030_CODEC_CELLS];
+};
+
+/*
+ * Modify the resource, the function returns the content of the register
+ * after the modification.
+ */
+static int twl4030_codec_set_resource(enum twl4030_codec_res id, int enable)
+{
+	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+	u8 val;
+
+	twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+			codec->resource[id].reg);
+
+	if (enable)
+		val |= codec->resource[id].mask;
+	else
+		val &= ~codec->resource[id].mask;
+
+	twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+					val, codec->resource[id].reg);
+
+	return val;
+}
+
+static inline int twl4030_codec_get_resource(enum twl4030_codec_res id)
+{
+	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+	u8 val;
+
+	twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &val,
+			codec->resource[id].reg);
+
+	return val;
+}
+
+/*
+ * Enable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_enable_resource(enum twl4030_codec_res id)
+{
+	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+	int val;
+
+	if (id >= TWL4030_CODEC_RES_MAX) {
+		dev_err(&twl4030_codec_dev->dev,
+				"Invalid resource ID (%u)\n", id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&codec->mutex);
+	if (!codec->resource[id].request_count)
+		/* Resource was disabled, enable it */
+		val = twl4030_codec_set_resource(id, 1);
+	else
+		val = twl4030_codec_get_resource(id);
+
+	codec->resource[id].request_count++;
+	mutex_unlock(&codec->mutex);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_enable_resource);
+
+/*
+ * Disable the resource.
+ * The function returns with error or the content of the register
+ */
+int twl4030_codec_disable_resource(unsigned id)
+{
+	struct twl4030_codec *codec = platform_get_drvdata(twl4030_codec_dev);
+	int val;
+
+	if (id >= TWL4030_CODEC_RES_MAX) {
+		dev_err(&twl4030_codec_dev->dev,
+				"Invalid resource ID (%u)\n", id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&codec->mutex);
+	if (!codec->resource[id].request_count) {
+		dev_err(&twl4030_codec_dev->dev,
+			"Resource has been disabled already (%u)\n", id);
+		mutex_unlock(&codec->mutex);
+		return -EPERM;
+	}
+	codec->resource[id].request_count--;
+
+	if (!codec->resource[id].request_count)
+		/* Resource can be disabled now */
+		val = twl4030_codec_set_resource(id, 0);
+	else
+		val = twl4030_codec_get_resource(id);
+
+	mutex_unlock(&codec->mutex);
+
+	return val;
+}
+EXPORT_SYMBOL_GPL(twl4030_codec_disable_resource);
+
+static int __devinit twl4030_codec_probe(struct platform_device *pdev)
+{
+	struct twl4030_codec *codec;
+	struct twl4030_codec_data *pdata = pdev->dev.platform_data;
+	struct mfd_cell *cell = NULL;
+	int ret, childs = 0;
+
+	codec = kzalloc(sizeof(struct twl4030_codec), GFP_KERNEL);
+	if (!codec)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, codec);
+
+	twl4030_codec_dev = pdev;
+	mutex_init(&codec->mutex);
+
+	/* Codec power */
+	codec->resource[TWL4030_CODEC_RES_POWER].reg = TWL4030_REG_CODEC_MODE;
+	codec->resource[TWL4030_CODEC_RES_POWER].mask = TWL4030_CODECPDZ;
+
+	/* PLL */
+	codec->resource[TWL4030_CODEC_RES_APLL].reg = TWL4030_REG_APLL_CTL;
+	codec->resource[TWL4030_CODEC_RES_APLL].mask = TWL4030_APLL_EN;
+
+	if (pdata->audio) {
+		cell = &codec->cells[childs];
+		cell->name = "twl4030_codec_audio";
+		cell->platform_data = pdata->audio;
+		cell->data_size = sizeof(*pdata->audio);
+		childs++;
+	}
+	if (pdata->vibra) {
+		cell = &codec->cells[childs];
+		cell->name = "twl4030_codec_vibra";
+		cell->platform_data = pdata->vibra;
+		cell->data_size = sizeof(*pdata->vibra);
+		childs++;
+	}
+
+	if (childs)
+		ret = mfd_add_devices(&pdev->dev, pdev->id, codec->cells,
+				      childs, NULL, 0);
+	else {
+		dev_err(&pdev->dev, "No platform data found for childs\n");
+		ret = -ENODEV;
+	}
+
+	if (!ret)
+		return 0;
+
+	platform_set_drvdata(pdev, NULL);
+	kfree(codec);
+	twl4030_codec_dev = NULL;
+	return ret;
+}
+
+static int __devexit twl4030_codec_remove(struct platform_device *pdev)
+{
+	struct twl4030_codec *codec = platform_get_drvdata(pdev);
+
+	mfd_remove_devices(&pdev->dev);
+	platform_set_drvdata(pdev, NULL);
+	kfree(codec);
+	twl4030_codec_dev = NULL;
+
+	return 0;
+}
+
+MODULE_ALIAS("platform:twl4030_codec");
+
+static struct platform_driver twl4030_codec_driver = {
+	.probe		= twl4030_codec_probe,
+	.remove		= __devexit_p(twl4030_codec_remove),
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "twl4030_codec",
+	},
+};
+
+static int __devinit twl4030_codec_init(void)
+{
+	return platform_driver_register(&twl4030_codec_driver);
+}
+module_init(twl4030_codec_init);
+
+static void __devexit twl4030_codec_exit(void)
+{
+	platform_driver_unregister(&twl4030_codec_driver);
+}
+module_exit(twl4030_codec_exit);
+
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c
index a1c47ee..cc3f77b 100644
--- a/drivers/mfd/twl4030-core.c
+++ b/drivers/mfd/twl4030-core.c
@@ -114,6 +114,12 @@
 #define twl_has_watchdog()        false
 #endif
 
+#if defined(CONFIG_TWL4030_CODEC) || defined(CONFIG_TWL4030_CODEC_MODULE)
+#define twl_has_codec()	true
+#else
+#define twl_has_codec()	false
+#endif
+
 /* Triton Core internal information (BEGIN) */
 
 /* Last - for index max*/
@@ -601,6 +607,14 @@
 			return PTR_ERR(child);
 	}
 
+	if (twl_has_codec() && pdata->codec) {
+		child = add_child(1, "twl4030_codec",
+				pdata->codec, sizeof(*pdata->codec),
+				false, 0, 0);
+		if (IS_ERR(child))
+			return PTR_ERR(child);
+	}
+
 	if (twl_has_regulator()) {
 		/*
 		child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h
index 508824ee..ba61add 100644
--- a/include/linux/i2c/twl4030.h
+++ b/include/linux/i2c/twl4030.h
@@ -401,6 +401,23 @@
 
 extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
 
+struct twl4030_codec_audio_data {
+	unsigned int	audio_mclk;
+	unsigned int ramp_delay_value;
+	unsigned int hs_extmute:1;
+	void (*set_hs_extmute)(int mute);
+};
+
+struct twl4030_codec_vibra_data {
+	unsigned int	audio_mclk;
+	unsigned int	coexist;
+};
+
+struct twl4030_codec_data {
+	struct twl4030_codec_audio_data		*audio;
+	struct twl4030_codec_vibra_data		*vibra;
+};
+
 struct twl4030_platform_data {
 	unsigned				irq_base, irq_end;
 	struct twl4030_bci_platform_data	*bci;
@@ -409,6 +426,7 @@
 	struct twl4030_keypad_data		*keypad;
 	struct twl4030_usb_data			*usb;
 	struct twl4030_power_data		*power;
+	struct twl4030_codec_data		*codec;
 
 	/* LDO regulators */
 	struct regulator_init_data		*vdac;
diff --git a/include/linux/mfd/twl4030-codec.h b/include/linux/mfd/twl4030-codec.h
new file mode 100644
index 0000000..ef0a304
--- /dev/null
+++ b/include/linux/mfd/twl4030-codec.h
@@ -0,0 +1,271 @@
+/*
+ * MFD driver for twl4030 codec submodule
+ *
+ * Author:	Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * 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 __TWL4030_CODEC_H__
+#define __TWL4030_CODEC_H__
+
+/* Codec registers */
+#define TWL4030_REG_CODEC_MODE		0x01
+#define TWL4030_REG_OPTION		0x02
+#define TWL4030_REG_UNKNOWN		0x03
+#define TWL4030_REG_MICBIAS_CTL		0x04
+#define TWL4030_REG_ANAMICL		0x05
+#define TWL4030_REG_ANAMICR		0x06
+#define TWL4030_REG_AVADC_CTL		0x07
+#define TWL4030_REG_ADCMICSEL		0x08
+#define TWL4030_REG_DIGMIXING		0x09
+#define TWL4030_REG_ATXL1PGA		0x0A
+#define TWL4030_REG_ATXR1PGA		0x0B
+#define TWL4030_REG_AVTXL2PGA		0x0C
+#define TWL4030_REG_AVTXR2PGA		0x0D
+#define TWL4030_REG_AUDIO_IF		0x0E
+#define TWL4030_REG_VOICE_IF		0x0F
+#define TWL4030_REG_ARXR1PGA		0x10
+#define TWL4030_REG_ARXL1PGA		0x11
+#define TWL4030_REG_ARXR2PGA		0x12
+#define TWL4030_REG_ARXL2PGA		0x13
+#define TWL4030_REG_VRXPGA		0x14
+#define TWL4030_REG_VSTPGA		0x15
+#define TWL4030_REG_VRX2ARXPGA		0x16
+#define TWL4030_REG_AVDAC_CTL		0x17
+#define TWL4030_REG_ARX2VTXPGA		0x18
+#define TWL4030_REG_ARXL1_APGA_CTL	0x19
+#define TWL4030_REG_ARXR1_APGA_CTL	0x1A
+#define TWL4030_REG_ARXL2_APGA_CTL	0x1B
+#define TWL4030_REG_ARXR2_APGA_CTL	0x1C
+#define TWL4030_REG_ATX2ARXPGA		0x1D
+#define TWL4030_REG_BT_IF		0x1E
+#define TWL4030_REG_BTPGA		0x1F
+#define TWL4030_REG_BTSTPGA		0x20
+#define TWL4030_REG_EAR_CTL		0x21
+#define TWL4030_REG_HS_SEL		0x22
+#define TWL4030_REG_HS_GAIN_SET		0x23
+#define TWL4030_REG_HS_POPN_SET		0x24
+#define TWL4030_REG_PREDL_CTL		0x25
+#define TWL4030_REG_PREDR_CTL		0x26
+#define TWL4030_REG_PRECKL_CTL		0x27
+#define TWL4030_REG_PRECKR_CTL		0x28
+#define TWL4030_REG_HFL_CTL		0x29
+#define TWL4030_REG_HFR_CTL		0x2A
+#define TWL4030_REG_ALC_CTL		0x2B
+#define TWL4030_REG_ALC_SET1		0x2C
+#define TWL4030_REG_ALC_SET2		0x2D
+#define TWL4030_REG_BOOST_CTL		0x2E
+#define TWL4030_REG_SOFTVOL_CTL		0x2F
+#define TWL4030_REG_DTMF_FREQSEL	0x30
+#define TWL4030_REG_DTMF_TONEXT1H	0x31
+#define TWL4030_REG_DTMF_TONEXT1L	0x32
+#define TWL4030_REG_DTMF_TONEXT2H	0x33
+#define TWL4030_REG_DTMF_TONEXT2L	0x34
+#define TWL4030_REG_DTMF_TONOFF		0x35
+#define TWL4030_REG_DTMF_WANONOFF	0x36
+#define TWL4030_REG_I2S_RX_SCRAMBLE_H	0x37
+#define TWL4030_REG_I2S_RX_SCRAMBLE_M	0x38
+#define TWL4030_REG_I2S_RX_SCRAMBLE_L	0x39
+#define TWL4030_REG_APLL_CTL		0x3A
+#define TWL4030_REG_DTMF_CTL		0x3B
+#define TWL4030_REG_DTMF_PGA_CTL2	0x3C
+#define TWL4030_REG_DTMF_PGA_CTL1	0x3D
+#define TWL4030_REG_MISC_SET_1		0x3E
+#define TWL4030_REG_PCMBTMUX		0x3F
+#define TWL4030_REG_RX_PATH_SEL		0x43
+#define TWL4030_REG_VDL_APGA_CTL	0x44
+#define TWL4030_REG_VIBRA_CTL		0x45
+#define TWL4030_REG_VIBRA_SET		0x46
+#define TWL4030_REG_VIBRA_PWM_SET	0x47
+#define TWL4030_REG_ANAMIC_GAIN		0x48
+#define TWL4030_REG_MISC_SET_2		0x49
+
+/* Bitfield Definitions */
+
+/* TWL4030_CODEC_MODE (0x01) Fields */
+#define TWL4030_APLL_RATE		0xF0
+#define TWL4030_APLL_RATE_8000		0x00
+#define TWL4030_APLL_RATE_11025		0x10
+#define TWL4030_APLL_RATE_12000		0x20
+#define TWL4030_APLL_RATE_16000		0x40
+#define TWL4030_APLL_RATE_22050		0x50
+#define TWL4030_APLL_RATE_24000		0x60
+#define TWL4030_APLL_RATE_32000		0x80
+#define TWL4030_APLL_RATE_44100		0x90
+#define TWL4030_APLL_RATE_48000		0xA0
+#define TWL4030_APLL_RATE_96000		0xE0
+#define TWL4030_SEL_16K			0x08
+#define TWL4030_CODECPDZ		0x02
+#define TWL4030_OPT_MODE		0x01
+#define TWL4030_OPTION_1		(1 << 0)
+#define TWL4030_OPTION_2		(0 << 0)
+
+/* TWL4030_OPTION (0x02) Fields */
+#define TWL4030_ATXL1_EN		(1 << 0)
+#define TWL4030_ATXR1_EN		(1 << 1)
+#define TWL4030_ATXL2_VTXL_EN		(1 << 2)
+#define TWL4030_ATXR2_VTXR_EN		(1 << 3)
+#define TWL4030_ARXL1_VRX_EN		(1 << 4)
+#define TWL4030_ARXR1_EN		(1 << 5)
+#define TWL4030_ARXL2_EN		(1 << 6)
+#define TWL4030_ARXR2_EN		(1 << 7)
+
+/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
+#define TWL4030_MICBIAS2_CTL		0x40
+#define TWL4030_MICBIAS1_CTL		0x20
+#define TWL4030_HSMICBIAS_EN		0x04
+#define TWL4030_MICBIAS2_EN		0x02
+#define TWL4030_MICBIAS1_EN		0x01
+
+/* ANAMICL (0x05) Fields */
+#define TWL4030_CNCL_OFFSET_START	0x80
+#define TWL4030_OFFSET_CNCL_SEL		0x60
+#define TWL4030_OFFSET_CNCL_SEL_ARX1	0x00
+#define TWL4030_OFFSET_CNCL_SEL_ARX2	0x20
+#define TWL4030_OFFSET_CNCL_SEL_VRX	0x40
+#define TWL4030_OFFSET_CNCL_SEL_ALL	0x60
+#define TWL4030_MICAMPL_EN		0x10
+#define TWL4030_CKMIC_EN		0x08
+#define TWL4030_AUXL_EN			0x04
+#define TWL4030_HSMIC_EN		0x02
+#define TWL4030_MAINMIC_EN		0x01
+
+/* ANAMICR (0x06) Fields */
+#define TWL4030_MICAMPR_EN		0x10
+#define TWL4030_AUXR_EN			0x04
+#define TWL4030_SUBMIC_EN		0x01
+
+/* AVADC_CTL (0x07) Fields */
+#define TWL4030_ADCL_EN			0x08
+#define TWL4030_AVADC_CLK_PRIORITY	0x04
+#define TWL4030_ADCR_EN			0x02
+
+/* TWL4030_REG_ADCMICSEL (0x08) Fields */
+#define TWL4030_DIGMIC1_EN		0x08
+#define TWL4030_TX2IN_SEL		0x04
+#define TWL4030_DIGMIC0_EN		0x02
+#define TWL4030_TX1IN_SEL		0x01
+
+/* AUDIO_IF (0x0E) Fields */
+#define TWL4030_AIF_SLAVE_EN		0x80
+#define TWL4030_DATA_WIDTH		0x60
+#define TWL4030_DATA_WIDTH_16S_16W	0x00
+#define TWL4030_DATA_WIDTH_32S_16W	0x40
+#define TWL4030_DATA_WIDTH_32S_24W	0x60
+#define TWL4030_AIF_FORMAT		0x18
+#define TWL4030_AIF_FORMAT_CODEC	0x00
+#define TWL4030_AIF_FORMAT_LEFT		0x08
+#define TWL4030_AIF_FORMAT_RIGHT	0x10
+#define TWL4030_AIF_FORMAT_TDM		0x18
+#define TWL4030_AIF_TRI_EN		0x04
+#define TWL4030_CLK256FS_EN		0x02
+#define TWL4030_AIF_EN			0x01
+
+/* VOICE_IF (0x0F) Fields */
+#define TWL4030_VIF_SLAVE_EN		0x80
+#define TWL4030_VIF_DIN_EN		0x40
+#define TWL4030_VIF_DOUT_EN		0x20
+#define TWL4030_VIF_SWAP		0x10
+#define TWL4030_VIF_FORMAT		0x08
+#define TWL4030_VIF_TRI_EN		0x04
+#define TWL4030_VIF_SUB_EN		0x02
+#define TWL4030_VIF_EN			0x01
+
+/* EAR_CTL (0x21) */
+#define TWL4030_EAR_GAIN		0x30
+
+/* HS_GAIN_SET (0x23) Fields */
+#define TWL4030_HSR_GAIN		0x0C
+#define TWL4030_HSR_GAIN_PWR_DOWN	0x00
+#define TWL4030_HSR_GAIN_PLUS_6DB	0x04
+#define TWL4030_HSR_GAIN_0DB		0x08
+#define TWL4030_HSR_GAIN_MINUS_6DB	0x0C
+#define TWL4030_HSL_GAIN		0x03
+#define TWL4030_HSL_GAIN_PWR_DOWN	0x00
+#define TWL4030_HSL_GAIN_PLUS_6DB	0x01
+#define TWL4030_HSL_GAIN_0DB		0x02
+#define TWL4030_HSL_GAIN_MINUS_6DB	0x03
+
+/* HS_POPN_SET (0x24) Fields */
+#define TWL4030_VMID_EN			0x40
+#define	TWL4030_EXTMUTE			0x20
+#define TWL4030_RAMP_DELAY		0x1C
+#define TWL4030_RAMP_DELAY_20MS		0x00
+#define TWL4030_RAMP_DELAY_40MS		0x04
+#define TWL4030_RAMP_DELAY_81MS		0x08
+#define TWL4030_RAMP_DELAY_161MS	0x0C
+#define TWL4030_RAMP_DELAY_323MS	0x10
+#define TWL4030_RAMP_DELAY_645MS	0x14
+#define TWL4030_RAMP_DELAY_1291MS	0x18
+#define TWL4030_RAMP_DELAY_2581MS	0x1C
+#define TWL4030_RAMP_EN			0x02
+
+/* PREDL_CTL (0x25) */
+#define TWL4030_PREDL_GAIN		0x30
+
+/* PREDR_CTL (0x26) */
+#define TWL4030_PREDR_GAIN		0x30
+
+/* PRECKL_CTL (0x27) */
+#define TWL4030_PRECKL_GAIN		0x30
+
+/* PRECKR_CTL (0x28) */
+#define TWL4030_PRECKR_GAIN		0x30
+
+/* HFL_CTL (0x29, 0x2A) Fields */
+#define TWL4030_HF_CTL_HB_EN		0x04
+#define TWL4030_HF_CTL_LOOP_EN		0x08
+#define TWL4030_HF_CTL_RAMP_EN		0x10
+#define TWL4030_HF_CTL_REF_EN		0x20
+
+/* APLL_CTL (0x3A) Fields */
+#define TWL4030_APLL_EN			0x10
+#define TWL4030_APLL_INFREQ		0x0F
+#define TWL4030_APLL_INFREQ_19200KHZ	0x05
+#define TWL4030_APLL_INFREQ_26000KHZ	0x06
+#define TWL4030_APLL_INFREQ_38400KHZ	0x0F
+
+/* REG_MISC_SET_1 (0x3E) Fields */
+#define TWL4030_CLK64_EN		0x80
+#define TWL4030_SCRAMBLE_EN		0x40
+#define TWL4030_FMLOOP_EN		0x20
+#define TWL4030_SMOOTH_ANAVOL_EN	0x02
+#define TWL4030_DIGMIC_LR_SWAP_EN	0x01
+
+/* VIBRA_CTL (0x45) */
+#define TWL4030_VIBRA_EN		0x01
+#define TWL4030_VIBRA_DIR		0x02
+#define TWL4030_VIBRA_AUDIO_SEL_L1	(0x00 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_R1	(0x01 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_L2	(0x02 << 2)
+#define TWL4030_VIBRA_AUDIO_SEL_R2	(0x03 << 2)
+#define TWL4030_VIBRA_SEL		0x10
+#define TWL4030_VIBRA_DIR_SEL		0x20
+
+/* TWL4030 codec resource IDs */
+enum twl4030_codec_res {
+	TWL4030_CODEC_RES_POWER = 0,
+	TWL4030_CODEC_RES_APLL,
+	TWL4030_CODEC_RES_MAX,
+};
+
+int twl4030_codec_disable_resource(enum twl4030_codec_res id);
+int twl4030_codec_enable_resource(enum twl4030_codec_res id);
+
+#endif	/* End of __TWL4030_CODEC_H__ */
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 97ca9af..ca24e7f 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -30,6 +30,7 @@
 #define SND_SOC_DAIFMT_DSP_A		3 /* L data MSB after FRM LRC */
 #define SND_SOC_DAIFMT_DSP_B		4 /* L data MSB during FRM LRC */
 #define SND_SOC_DAIFMT_AC97		5 /* AC97 */
+#define SND_SOC_DAIFMT_PDM		6 /* Pulse density modulation */
 
 /* left and right justified also known as MSB and LSB respectively */
 #define SND_SOC_DAIFMT_MSB		SND_SOC_DAIFMT_LEFT_J
@@ -106,7 +107,7 @@
 	int div_id, int div);
 
 int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-	int pll_id, unsigned int freq_in, unsigned int freq_out);
+	int pll_id, int source, unsigned int freq_in, unsigned int freq_out);
 
 /* Digital Audio interface formatting */
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
@@ -114,6 +115,10 @@
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
 
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+	unsigned int tx_num, unsigned int *tx_slot,
+	unsigned int rx_num, unsigned int *rx_slot);
+
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
 
 /* Digital Audio Interface mute */
@@ -136,8 +141,8 @@
 	 */
 	int (*set_sysclk)(struct snd_soc_dai *dai,
 		int clk_id, unsigned int freq, int dir);
-	int (*set_pll)(struct snd_soc_dai *dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out);
+	int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
+		unsigned int freq_in, unsigned int freq_out);
 	int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
 
 	/*
@@ -148,6 +153,9 @@
 	int (*set_tdm_slot)(struct snd_soc_dai *dai,
 		unsigned int tx_mask, unsigned int rx_mask,
 		int slots, int slot_width);
+	int (*set_channel_map)(struct snd_soc_dai *dai,
+		unsigned int tx_num, unsigned int *tx_slot,
+		unsigned int rx_num, unsigned int *rx_slot);
 	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
 
 	/*
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index c1410e3..c5c95e1d 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -206,6 +206,12 @@
  	.get = snd_soc_dapm_get_enum_double, \
  	.put = snd_soc_dapm_put_enum_double, \
   	.private_value = (unsigned long)&xenum }
+#define SOC_DAPM_ENUM_VIRT(xname, xenum)		    \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_enum_double, \
+	.get = snd_soc_dapm_get_enum_virt, \
+	.put = snd_soc_dapm_put_enum_virt, \
+	.private_value = (unsigned long)&xenum }
 #define SOC_DAPM_VALUE_ENUM(xname, xenum) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_enum_double, \
@@ -260,6 +266,10 @@
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
@@ -333,6 +343,10 @@
 	const char *sink;
 	const char *control;
 	const char *source;
+
+	/* Note: currently only supported for links where source is a supply */
+	int (*connected)(struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink);
 };
 
 /* dapm audio path between two widgets */
@@ -349,6 +363,9 @@
 	u32 connect:1;	/* source and sink widgets are connected */
 	u32 walked:1;	/* path has been walked */
 
+	int (*connected)(struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink);
+
 	struct list_head list_source;
 	struct list_head list_sink;
 	struct list_head list;
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 475cb7e..7f3a4c5 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -223,15 +223,9 @@
 			       int addr_bits, int data_bits,
 			       enum snd_soc_control_type control);
 
-#ifdef CONFIG_PM
-int snd_soc_suspend_device(struct device *dev);
-int snd_soc_resume_device(struct device *dev);
-#endif
-
 /* pcm <-> DAI connect */
 void snd_soc_free_pcms(struct snd_soc_device *socdev);
 int snd_soc_new_pcms(struct snd_soc_device *socdev, int idx, const char *xid);
-int snd_soc_init_card(struct snd_soc_device *socdev);
 
 /* set runtime hw params */
 int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
@@ -413,6 +407,7 @@
 	unsigned int num_dai;
 
 #ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_codec_root;
 	struct dentry *debugfs_reg;
 	struct dentry *debugfs_pop_time;
 	struct dentry *debugfs_dapm;
diff --git a/include/sound/tlv320dac33-plat.h b/include/sound/tlv320dac33-plat.h
new file mode 100644
index 0000000..5858d06
--- /dev/null
+++ b/include/sound/tlv320dac33-plat.h
@@ -0,0 +1,20 @@
+/*
+ * Platform header for Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author:	Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * 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 __TLV320DAC33_PLAT_H
+#define __TLV320DAC33_PLAT_H
+
+struct tlv320dac33_platform_data {
+	int power_gpio;
+};
+
+#endif /* __TLV320DAC33_PLAT_H */
diff --git a/include/sound/tpa6130a2-plat.h b/include/sound/tpa6130a2-plat.h
new file mode 100644
index 0000000..e8c901e
--- /dev/null
+++ b/include/sound/tpa6130a2-plat.h
@@ -0,0 +1,30 @@
+/*
+ * TPA6130A2 driver platform header
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Written by Peter Ujfalusi <peter.ujfalusi@nokia.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 TPA6130A2_PLAT_H
+#define TPA6130A2_PLAT_H
+
+struct tpa6130a2_platform_data {
+	int power_gpio;
+};
+
+#endif
diff --git a/sound/soc/atmel/playpaq_wm8510.c b/sound/soc/atmel/playpaq_wm8510.c
index 9eb610c..9df4c68 100644
--- a/sound/soc/atmel/playpaq_wm8510.c
+++ b/sound/soc/atmel/playpaq_wm8510.c
@@ -268,7 +268,7 @@
 #endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
 
 
-	ret = snd_soc_dai_set_pll(codec_dai, 0,
+	ret = snd_soc_dai_set_pll(codec_dai, 0, 0,
 					 clk_get_rate(CODEC_CLK), pll_out);
 	if (ret < 0) {
 		pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 885ba01..e028744 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -207,7 +207,7 @@
 	struct clk *pllb;
 	int ret;
 
-	if (!machine_is_at91sam9g20ek())
+	if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc()))
 		return -ENODEV;
 
 	/*
diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c
index 594c6c5..fe9f465 100644
--- a/sound/soc/au1x/dbdma2.c
+++ b/sound/soc/au1x/dbdma2.c
@@ -2,7 +2,7 @@
  * Au12x0/Au1550 PSC ALSA ASoC audio support.
  *
  * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ *	Manuel Lauss <manuel.lauss@gmail.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
@@ -333,6 +333,30 @@
 
 static int au1xpsc_pcm_probe(struct platform_device *pdev)
 {
+	if (!au1xpsc_audio_pcmdma[PCM_TX] || !au1xpsc_audio_pcmdma[PCM_RX])
+		return -ENODEV;
+
+	return 0;
+}
+
+static int au1xpsc_pcm_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+/* au1xpsc audio platform */
+struct snd_soc_platform au1xpsc_soc_platform = {
+	.name		= "au1xpsc-pcm-dbdma",
+	.probe		= au1xpsc_pcm_probe,
+	.remove		= au1xpsc_pcm_remove,
+	.pcm_ops 	= &au1xpsc_pcm_ops,
+	.pcm_new	= au1xpsc_pcm_new,
+	.pcm_free	= au1xpsc_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
+
+static int __devinit au1xpsc_pcm_drvprobe(struct platform_device *pdev)
+{
 	struct resource *r;
 	int ret;
 
@@ -365,7 +389,9 @@
 	}
 	(au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start;
 
-	return 0;
+	ret = snd_soc_register_platform(&au1xpsc_soc_platform);
+	if (!ret)
+		return ret;
 
 out2:
 	kfree(au1xpsc_audio_pcmdma[PCM_RX]);
@@ -376,10 +402,12 @@
 	return ret;
 }
 
-static int au1xpsc_pcm_remove(struct platform_device *pdev)
+static int __devexit au1xpsc_pcm_drvremove(struct platform_device *pdev)
 {
 	int i;
 
+	snd_soc_unregister_platform(&au1xpsc_soc_platform);
+
 	for (i = 0; i < 2; i++) {
 		if (au1xpsc_audio_pcmdma[i]) {
 			au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]);
@@ -391,32 +419,83 @@
 	return 0;
 }
 
-/* au1xpsc audio platform */
-struct snd_soc_platform au1xpsc_soc_platform = {
-	.name		= "au1xpsc-pcm-dbdma",
-	.probe		= au1xpsc_pcm_probe,
-	.remove		= au1xpsc_pcm_remove,
-	.pcm_ops 	= &au1xpsc_pcm_ops,
-	.pcm_new	= au1xpsc_pcm_new,
-	.pcm_free	= au1xpsc_pcm_free_dma_buffers,
+static struct platform_driver au1xpsc_pcm_driver = {
+	.driver	= {
+		.name	= "au1xpsc-pcm",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= au1xpsc_pcm_drvprobe,
+	.remove		= __devexit_p(au1xpsc_pcm_drvremove),
 };
-EXPORT_SYMBOL_GPL(au1xpsc_soc_platform);
 
-static int __init au1xpsc_audio_dbdma_init(void)
+static int __init au1xpsc_audio_dbdma_load(void)
 {
 	au1xpsc_audio_pcmdma[PCM_TX] = NULL;
 	au1xpsc_audio_pcmdma[PCM_RX] = NULL;
-	return snd_soc_register_platform(&au1xpsc_soc_platform);
+	return platform_driver_register(&au1xpsc_pcm_driver);
 }
 
-static void __exit au1xpsc_audio_dbdma_exit(void)
+static void __exit au1xpsc_audio_dbdma_unload(void)
 {
-	snd_soc_unregister_platform(&au1xpsc_soc_platform);
+	platform_driver_unregister(&au1xpsc_pcm_driver);
 }
 
-module_init(au1xpsc_audio_dbdma_init);
-module_exit(au1xpsc_audio_dbdma_exit);
+module_init(au1xpsc_audio_dbdma_load);
+module_exit(au1xpsc_audio_dbdma_unload);
+
+
+struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev)
+{
+	struct resource *res, *r;
+	struct platform_device *pd;
+	int id[2];
+	int ret;
+
+	r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!r)
+		return NULL;
+	id[0] = r->start;
+
+	r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (!r)
+		return NULL;
+	id[1] = r->start;
+
+	res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL);
+	if (!res)
+		return NULL;
+
+	res[0].start = res[0].end = id[0];
+	res[1].start = res[1].end = id[1];
+	res[0].flags = res[1].flags = IORESOURCE_DMA;
+
+	pd = platform_device_alloc("au1xpsc-pcm", -1);
+	if (!pd)
+		goto out;
+
+	pd->resource = res;
+	pd->num_resources = 2;
+
+	ret = platform_device_add(pd);
+	if (!ret)
+		return pd;
+
+out:
+	kfree(res);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(au1xpsc_pcm_add);
+
+void au1xpsc_pcm_destroy(struct platform_device *dmapd)
+{
+	if (dmapd) {
+		kfree(dmapd->resource);
+		dmapd->resource = NULL;
+		platform_device_unregister(dmapd);
+	}
+}
+EXPORT_SYMBOL_GPL(au1xpsc_pcm_destroy);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver");
-MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index a521aa9..340311d 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -61,7 +61,8 @@
 {
 	/* FIXME */
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
-	unsigned short data, retry, tmo;
+	unsigned short retry, tmo;
+	unsigned long data;
 
 	au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
 	au_sync();
@@ -74,20 +75,26 @@
 			  AC97_CDC(pscdata));
 		au_sync();
 
-		tmo = 2000;
-		while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
-			&& --tmo)
-			udelay(2);
+		tmo = 20;
+		do {
+			udelay(21);
+			if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
+				break;
+		} while (--tmo);
 
-		data = au_readl(AC97_CDC(pscdata)) & 0xffff;
+		data = au_readl(AC97_CDC(pscdata));
 
 		au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
 		au_sync();
 
 		mutex_unlock(&pscdata->lock);
+
+		if (reg != ((data >> 16) & 0x7f))
+			tmo = 1;	/* wrong register, try again */
+
 	} while (--retry && !tmo);
 
-	return retry ? data : 0xffff;
+	return retry ? data & 0xffff : 0xffff;
 }
 
 /* AC97 controller writes to codec register */
@@ -109,10 +116,12 @@
 			  AC97_CDC(pscdata));
 		au_sync();
 
-		tmo = 2000;
-		while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD))
-		       && --tmo)
-			udelay(2);
+		tmo = 20;
+		do {
+			udelay(21);
+			if (au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)
+				break;
+		} while (--tmo);
 
 		au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata));
 		au_sync();
@@ -195,7 +204,7 @@
 	/* FIXME */
 	struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata;
 	unsigned long r, ro, stat;
-	int chans, stype = SUBSTREAM_TYPE(substream);
+	int chans, t, stype = SUBSTREAM_TYPE(substream);
 
 	chans = params_channels(params);
 
@@ -237,8 +246,12 @@
 		au_sync();
 
 		/* ...wait for it... */
-		while (au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)
-			asm volatile ("nop");
+		t = 100;
+		while ((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR) && --t)
+			msleep(1);
+
+		if (!t)
+			printk(KERN_ERR "PSC-AC97: can't disable!\n");
 
 		/* ...write config... */
 		au_writel(r, AC97_CFG(pscdata));
@@ -249,8 +262,12 @@
 		au_sync();
 
 		/* ...and wait for ready bit */
-		while (!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR))
-			asm volatile ("nop");
+		t = 100;
+		while ((!(au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && --t)
+			msleep(1);
+
+		if (!t)
+			printk(KERN_ERR "PSC-AC97: can't enable!\n");
 
 		mutex_unlock(&pscdata->lock);
 
@@ -300,109 +317,12 @@
 static int au1xpsc_ac97_probe(struct platform_device *pdev,
 			      struct snd_soc_dai *dai)
 {
-	int ret;
-	struct resource *r;
-	unsigned long sel;
-
-	if (au1xpsc_ac97_workdata)
-		return -EBUSY;
-
-	au1xpsc_ac97_workdata =
-		kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
-	if (!au1xpsc_ac97_workdata)
-		return -ENOMEM;
-
-	mutex_init(&au1xpsc_ac97_workdata->lock);
-
-	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!r) {
-		ret = -ENODEV;
-		goto out0;
-	}
-
-	ret = -EBUSY;
-	au1xpsc_ac97_workdata->ioarea =
-		request_mem_region(r->start, r->end - r->start + 1,
-					"au1xpsc_ac97");
-	if (!au1xpsc_ac97_workdata->ioarea)
-		goto out0;
-
-	au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff);
-	if (!au1xpsc_ac97_workdata->mmio)
-		goto out1;
-
-	/* configuration: max dma trigger threshold, enable ac97 */
-	au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 |
-				     PSC_AC97CFG_TT_FIFO8 |
-				     PSC_AC97CFG_DE_ENABLE;
-
-	/* preserve PSC clock source set up by platform (dev.platform_data
-	 * is already occupied by soc layer)
-	 */
-	sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK;
-	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
-	au_sync();
-	au_writel(0, PSC_SEL(au1xpsc_ac97_workdata));
-	au_sync();
-	au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata));
-	au_sync();
-	/* next up: cold reset.  Dont check for PSC-ready now since
-	 * there may not be any codec clock yet.
-	 */
-
-	return 0;
-
-out1:
-	release_resource(au1xpsc_ac97_workdata->ioarea);
-	kfree(au1xpsc_ac97_workdata->ioarea);
-out0:
-	kfree(au1xpsc_ac97_workdata);
-	au1xpsc_ac97_workdata = NULL;
-	return ret;
+	return au1xpsc_ac97_workdata ? 0 : -ENODEV;
 }
 
 static void au1xpsc_ac97_remove(struct platform_device *pdev,
 				struct snd_soc_dai *dai)
 {
-	/* disable PSC completely */
-	au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
-	au_sync();
-	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
-	au_sync();
-
-	iounmap(au1xpsc_ac97_workdata->mmio);
-	release_resource(au1xpsc_ac97_workdata->ioarea);
-	kfree(au1xpsc_ac97_workdata->ioarea);
-	kfree(au1xpsc_ac97_workdata);
-	au1xpsc_ac97_workdata = NULL;
-}
-
-static int au1xpsc_ac97_suspend(struct snd_soc_dai *dai)
-{
-	/* save interesting registers and disable PSC */
-	au1xpsc_ac97_workdata->pm[0] =
-			au_readl(PSC_SEL(au1xpsc_ac97_workdata));
-
-	au_writel(0, AC97_CFG(au1xpsc_ac97_workdata));
-	au_sync();
-	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata));
-	au_sync();
-
-	return 0;
-}
-
-static int au1xpsc_ac97_resume(struct snd_soc_dai *dai)
-{
-	/* restore PSC clock config */
-	au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE,
-			PSC_SEL(au1xpsc_ac97_workdata));
-	au_sync();
-
-	/* after this point the ac97 core will cold-reset the codec.
-	 * During cold-reset the PSC is reinitialized and the last
-	 * configuration set up in hw_params() is restored.
-	 */
-	return 0;
 }
 
 static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
@@ -415,8 +335,6 @@
 	.ac97_control		= 1,
 	.probe			= au1xpsc_ac97_probe,
 	.remove			= au1xpsc_ac97_remove,
-	.suspend		= au1xpsc_ac97_suspend,
-	.resume			= au1xpsc_ac97_resume,
 	.playback = {
 		.rates		= AC97_RATES,
 		.formats	= AC97_FMTS,
@@ -433,20 +351,165 @@
 };
 EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);
 
-static int __init au1xpsc_ac97_init(void)
+static int __devinit au1xpsc_ac97_drvprobe(struct platform_device *pdev)
+{
+	int ret;
+	struct resource *r;
+	unsigned long sel;
+	struct au1xpsc_audio_data *wd;
+
+	if (au1xpsc_ac97_workdata)
+		return -EBUSY;
+
+	wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+	if (!wd)
+		return -ENOMEM;
+
+	mutex_init(&wd->lock);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		ret = -ENODEV;
+		goto out0;
+	}
+
+	ret = -EBUSY;
+	wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
+					"au1xpsc_ac97");
+	if (!wd->ioarea)
+		goto out0;
+
+	wd->mmio = ioremap(r->start, 0xffff);
+	if (!wd->mmio)
+		goto out1;
+
+	/* configuration: max dma trigger threshold, enable ac97 */
+	wd->cfg = PSC_AC97CFG_RT_FIFO8 | PSC_AC97CFG_TT_FIFO8 |
+		  PSC_AC97CFG_DE_ENABLE;
+
+	/* preserve PSC clock source set up by platform	 */
+	sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+	au_sync();
+	au_writel(0, PSC_SEL(wd));
+	au_sync();
+	au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(wd));
+	au_sync();
+
+	ret = snd_soc_register_dai(&au1xpsc_ac97_dai);
+	if (ret)
+		goto out1;
+
+	wd->dmapd = au1xpsc_pcm_add(pdev);
+	if (wd->dmapd) {
+		platform_set_drvdata(pdev, wd);
+		au1xpsc_ac97_workdata = wd;	/* MDEV */
+		return 0;
+	}
+
+	snd_soc_unregister_dai(&au1xpsc_ac97_dai);
+out1:
+	release_resource(wd->ioarea);
+	kfree(wd->ioarea);
+out0:
+	kfree(wd);
+	return ret;
+}
+
+static int __devexit au1xpsc_ac97_drvremove(struct platform_device *pdev)
+{
+	struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
+
+	if (wd->dmapd)
+		au1xpsc_pcm_destroy(wd->dmapd);
+
+	snd_soc_unregister_dai(&au1xpsc_ac97_dai);
+
+	/* disable PSC completely */
+	au_writel(0, AC97_CFG(wd));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+	au_sync();
+
+	iounmap(wd->mmio);
+	release_resource(wd->ioarea);
+	kfree(wd->ioarea);
+	kfree(wd);
+
+	au1xpsc_ac97_workdata = NULL;	/* MDEV */
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int au1xpsc_ac97_drvsuspend(struct device *dev)
+{
+	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
+	/* save interesting registers and disable PSC */
+	wd->pm[0] = au_readl(PSC_SEL(wd));
+
+	au_writel(0, AC97_CFG(wd));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+	au_sync();
+
+	return 0;
+}
+
+static int au1xpsc_ac97_drvresume(struct device *dev)
+{
+	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
+	/* restore PSC clock config */
+	au_writel(wd->pm[0] | PSC_SEL_PS_AC97MODE, PSC_SEL(wd));
+	au_sync();
+
+	/* after this point the ac97 core will cold-reset the codec.
+	 * During cold-reset the PSC is reinitialized and the last
+	 * configuration set up in hw_params() is restored.
+	 */
+	return 0;
+}
+
+static struct dev_pm_ops au1xpscac97_pmops = {
+	.suspend	= au1xpsc_ac97_drvsuspend,
+	.resume		= au1xpsc_ac97_drvresume,
+};
+
+#define AU1XPSCAC97_PMOPS &au1xpscac97_pmops
+
+#else
+
+#define AU1XPSCAC97_PMOPS NULL
+
+#endif
+
+static struct platform_driver au1xpsc_ac97_driver = {
+	.driver	= {
+		.name	= "au1xpsc_ac97",
+		.owner	= THIS_MODULE,
+		.pm	= AU1XPSCAC97_PMOPS,
+	},
+	.probe		= au1xpsc_ac97_drvprobe,
+	.remove		= __devexit_p(au1xpsc_ac97_drvremove),
+};
+
+static int __init au1xpsc_ac97_load(void)
 {
 	au1xpsc_ac97_workdata = NULL;
-	return snd_soc_register_dai(&au1xpsc_ac97_dai);
+	return platform_driver_register(&au1xpsc_ac97_driver);
 }
 
-static void __exit au1xpsc_ac97_exit(void)
+static void __exit au1xpsc_ac97_unload(void)
 {
-	snd_soc_unregister_dai(&au1xpsc_ac97_dai);
+	platform_driver_unregister(&au1xpsc_ac97_driver);
 }
 
-module_init(au1xpsc_ac97_init);
-module_exit(au1xpsc_ac97_exit);
+module_init(au1xpsc_ac97_load);
+module_exit(au1xpsc_ac97_unload);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver");
-MODULE_AUTHOR("Manuel Lauss <manuel.lauss@gmail.com>");
+MODULE_AUTHOR("Manuel Lauss");
+
diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c
index bb58932..0cf2ca6 100644
--- a/sound/soc/au1x/psc-i2s.c
+++ b/sound/soc/au1x/psc-i2s.c
@@ -2,7 +2,7 @@
  * Au12x0/Au1550 PSC ALSA ASoC audio support.
  *
  * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ *	Manuel Lauss <manuel.lauss@gmail.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
@@ -265,106 +265,12 @@
 static int au1xpsc_i2s_probe(struct platform_device *pdev,
 			     struct snd_soc_dai *dai)
 {
-	struct resource *r;
-	unsigned long sel;
-	int ret;
-
-	if (au1xpsc_i2s_workdata)
-		return -EBUSY;
-
-	au1xpsc_i2s_workdata =
-		kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
-	if (!au1xpsc_i2s_workdata)
-		return -ENOMEM;
-
-	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!r) {
-		ret = -ENODEV;
-		goto out0;
-	}
-
-	ret = -EBUSY;
-	au1xpsc_i2s_workdata->ioarea =
-		request_mem_region(r->start, r->end - r->start + 1,
-					"au1xpsc_i2s");
-	if (!au1xpsc_i2s_workdata->ioarea)
-		goto out0;
-
-	au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff);
-	if (!au1xpsc_i2s_workdata->mmio)
-		goto out1;
-
-	/* preserve PSC clock source set up by platform (dev.platform_data
-	 * is already occupied by soc layer)
-	 */
-	sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK;
-	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
-	au_sync();
-	au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata));
-	au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
-	au_sync();
-
-	/* preconfigure: set max rx/tx fifo depths */
-	au1xpsc_i2s_workdata->cfg |=
-			PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
-
-	/* don't wait for I2S core to become ready now; clocks may not
-	 * be running yet; depending on clock input for PSC a wait might
-	 * time out.
-	 */
-
-	return 0;
-
-out1:
-	release_resource(au1xpsc_i2s_workdata->ioarea);
-	kfree(au1xpsc_i2s_workdata->ioarea);
-out0:
-	kfree(au1xpsc_i2s_workdata);
-	au1xpsc_i2s_workdata = NULL;
-	return ret;
+	return 	au1xpsc_i2s_workdata ? 0 : -ENODEV;
 }
 
 static void au1xpsc_i2s_remove(struct platform_device *pdev,
 			       struct snd_soc_dai *dai)
 {
-	au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
-	au_sync();
-	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
-	au_sync();
-
-	iounmap(au1xpsc_i2s_workdata->mmio);
-	release_resource(au1xpsc_i2s_workdata->ioarea);
-	kfree(au1xpsc_i2s_workdata->ioarea);
-	kfree(au1xpsc_i2s_workdata);
-	au1xpsc_i2s_workdata = NULL;
-}
-
-static int au1xpsc_i2s_suspend(struct snd_soc_dai *cpu_dai)
-{
-	/* save interesting register and disable PSC */
-	au1xpsc_i2s_workdata->pm[0] =
-		au_readl(PSC_SEL(au1xpsc_i2s_workdata));
-
-	au_writel(0, I2S_CFG(au1xpsc_i2s_workdata));
-	au_sync();
-	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
-	au_sync();
-
-	return 0;
-}
-
-static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai)
-{
-	/* select I2S mode and PSC clock */
-	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata));
-	au_sync();
-	au_writel(0, PSC_SEL(au1xpsc_i2s_workdata));
-	au_sync();
-	au_writel(au1xpsc_i2s_workdata->pm[0],
-			PSC_SEL(au1xpsc_i2s_workdata));
-	au_sync();
-
-	return 0;
 }
 
 static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
@@ -377,8 +283,6 @@
 	.name			= "au1xpsc_i2s",
 	.probe			= au1xpsc_i2s_probe,
 	.remove			= au1xpsc_i2s_remove,
-	.suspend		= au1xpsc_i2s_suspend,
-	.resume			= au1xpsc_i2s_resume,
 	.playback = {
 		.rates		= AU1XPSC_I2S_RATES,
 		.formats	= AU1XPSC_I2S_FMTS,
@@ -395,20 +299,167 @@
 };
 EXPORT_SYMBOL(au1xpsc_i2s_dai);
 
-static int __init au1xpsc_i2s_init(void)
+static int __init au1xpsc_i2s_drvprobe(struct platform_device *pdev)
+{
+	struct resource *r;
+	unsigned long sel;
+	int ret;
+	struct au1xpsc_audio_data *wd;
+
+	if (au1xpsc_i2s_workdata)
+		return -EBUSY;
+
+	wd = kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL);
+	if (!wd)
+		return -ENOMEM;
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r) {
+		ret = -ENODEV;
+		goto out0;
+	}
+
+	ret = -EBUSY;
+	wd->ioarea = request_mem_region(r->start, r->end - r->start + 1,
+					"au1xpsc_i2s");
+	if (!wd->ioarea)
+		goto out0;
+
+	wd->mmio = ioremap(r->start, 0xffff);
+	if (!wd->mmio)
+		goto out1;
+
+	/* preserve PSC clock source set up by platform (dev.platform_data
+	 * is already occupied by soc layer)
+	 */
+	sel = au_readl(PSC_SEL(wd)) & PSC_SEL_CLK_MASK;
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+	au_sync();
+	au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(wd));
+	au_writel(0, I2S_CFG(wd));
+	au_sync();
+
+	/* preconfigure: set max rx/tx fifo depths */
+	wd->cfg |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8;
+
+	/* don't wait for I2S core to become ready now; clocks may not
+	 * be running yet; depending on clock input for PSC a wait might
+	 * time out.
+	 */
+
+	ret = snd_soc_register_dai(&au1xpsc_i2s_dai);
+	if (ret)
+		goto out1;
+
+	/* finally add the DMA device for this PSC */
+	wd->dmapd = au1xpsc_pcm_add(pdev);
+	if (wd->dmapd) {
+		platform_set_drvdata(pdev, wd);
+		au1xpsc_i2s_workdata = wd;
+		return 0;
+	}
+
+	snd_soc_unregister_dai(&au1xpsc_i2s_dai);
+out1:
+	release_resource(wd->ioarea);
+	kfree(wd->ioarea);
+out0:
+	kfree(wd);
+	return ret;
+}
+
+static int __devexit au1xpsc_i2s_drvremove(struct platform_device *pdev)
+{
+	struct au1xpsc_audio_data *wd = platform_get_drvdata(pdev);
+
+	if (wd->dmapd)
+		au1xpsc_pcm_destroy(wd->dmapd);
+
+	snd_soc_unregister_dai(&au1xpsc_i2s_dai);
+
+	au_writel(0, I2S_CFG(wd));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+	au_sync();
+
+	iounmap(wd->mmio);
+	release_resource(wd->ioarea);
+	kfree(wd->ioarea);
+	kfree(wd);
+
+	au1xpsc_i2s_workdata = NULL;	/* MDEV */
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int au1xpsc_i2s_drvsuspend(struct device *dev)
+{
+	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
+	/* save interesting register and disable PSC */
+	wd->pm[0] = au_readl(PSC_SEL(wd));
+
+	au_writel(0, I2S_CFG(wd));
+	au_sync();
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+	au_sync();
+
+	return 0;
+}
+
+static int au1xpsc_i2s_drvresume(struct device *dev)
+{
+	struct au1xpsc_audio_data *wd = dev_get_drvdata(dev);
+
+	/* select I2S mode and PSC clock */
+	au_writel(PSC_CTRL_DISABLE, PSC_CTRL(wd));
+	au_sync();
+	au_writel(0, PSC_SEL(wd));
+	au_sync();
+	au_writel(wd->pm[0], PSC_SEL(wd));
+	au_sync();
+
+	return 0;
+}
+
+static struct dev_pm_ops au1xpsci2s_pmops = {
+	.suspend	= au1xpsc_i2s_drvsuspend,
+	.resume		= au1xpsc_i2s_drvresume,
+};
+
+#define AU1XPSCI2S_PMOPS &au1xpsci2s_pmops
+
+#else
+
+#define AU1XPSCI2S_PMOPS NULL
+
+#endif
+
+static struct platform_driver au1xpsc_i2s_driver = {
+	.driver		= {
+		.name	= "au1xpsc_i2s",
+		.owner	= THIS_MODULE,
+		.pm	= AU1XPSCI2S_PMOPS,
+	},
+	.probe		= au1xpsc_i2s_drvprobe,
+	.remove		= __devexit_p(au1xpsc_i2s_drvremove),
+};
+
+static int __init au1xpsc_i2s_load(void)
 {
 	au1xpsc_i2s_workdata = NULL;
-	return snd_soc_register_dai(&au1xpsc_i2s_dai);
+	return platform_driver_register(&au1xpsc_i2s_driver);
 }
 
-static void __exit au1xpsc_i2s_exit(void)
+static void __exit au1xpsc_i2s_unload(void)
 {
-	snd_soc_unregister_dai(&au1xpsc_i2s_dai);
+	platform_driver_unregister(&au1xpsc_i2s_driver);
 }
 
-module_init(au1xpsc_i2s_init);
-module_exit(au1xpsc_i2s_exit);
+module_init(au1xpsc_i2s_load);
+module_exit(au1xpsc_i2s_unload);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver");
-MODULE_AUTHOR("Manuel Lauss <mano@roarinelk.homelinux.net>");
+MODULE_AUTHOR("Manuel Lauss");
diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h
index 3f474e8..32d3807 100644
--- a/sound/soc/au1x/psc.h
+++ b/sound/soc/au1x/psc.h
@@ -2,7 +2,7 @@
  * Au12x0/Au1550 PSC ALSA ASoC audio support.
  *
  * (c) 2007-2008 MSC Vertriebsges.m.b.H.,
- *	Manuel Lauss <mano@roarinelk.homelinux.net>
+ *	Manuel Lauss <manuel.lauss@gmail.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
@@ -21,6 +21,10 @@
 extern struct snd_soc_platform au1xpsc_soc_platform;
 extern struct snd_ac97_bus_ops soc_ac97_ops;
 
+/* DBDMA helpers */
+extern struct platform_device *au1xpsc_pcm_add(struct platform_device *pdev);
+extern void au1xpsc_pcm_destroy(struct platform_device *dmapd);
+
 struct au1xpsc_audio_data {
 	void __iomem *mmio;
 
@@ -30,6 +34,7 @@
 	unsigned long pm[2];
 	struct resource *ioarea;
 	struct mutex lock;
+	struct platform_device *dmapd;
 };
 
 #define PCM_TX	0
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
index cd361e3..0f45a3f 100644
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ b/sound/soc/blackfin/bf5xx-ad1836.c
@@ -52,6 +52,7 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
 	int ret = 0;
 	/* set cpu DAI configuration */
 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@@ -65,6 +66,12 @@
 	if (ret < 0)
 		return ret;
 
+	/* set cpu DAI channel mapping */
+	ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+		channel_map, ARRAY_SIZE(channel_map), channel_map);
+	if (ret < 0)
+		return ret;
+
 	return 0;
 }
 
diff --git a/sound/soc/blackfin/bf5xx-ad1938.c b/sound/soc/blackfin/bf5xx-ad1938.c
index 08269e9..2ef1e50 100644
--- a/sound/soc/blackfin/bf5xx-ad1938.c
+++ b/sound/soc/blackfin/bf5xx-ad1938.c
@@ -61,6 +61,7 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
 	int ret = 0;
 	/* set cpu DAI configuration */
 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@@ -75,7 +76,13 @@
 		return ret;
 
 	/* set codec DAI slots, 8 channels, all channels are enabled */
-	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8);
+	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI channel mapping */
+	ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+		channel_map, ARRAY_SIZE(channel_map), channel_map);
 	if (ret < 0)
 		return ret;
 
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index 084b688..3e6ada0 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -49,7 +49,6 @@
 	u16 rcr1;
 	u16 tcr2;
 	u16 rcr2;
-	int counter;
 	int configured;
 };
 
@@ -133,16 +132,6 @@
 	return ret;
 }
 
-static int bf5xx_i2s_startup(struct snd_pcm_substream *substream,
-			     struct snd_soc_dai *dai)
-{
-	pr_debug("%s enter\n", __func__);
-
-	/*this counter is used for counting how many pcm streams are opened*/
-	bf5xx_i2s.counter++;
-	return 0;
-}
-
 static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
 				struct snd_pcm_hw_params *params,
 				struct snd_soc_dai *dai)
@@ -201,9 +190,8 @@
 			       struct snd_soc_dai *dai)
 {
 	pr_debug("%s enter\n", __func__);
-	bf5xx_i2s.counter--;
 	/* No active stream, SPORT is allowed to be configured again. */
-	if (!bf5xx_i2s.counter)
+	if (!dai->active)
 		bf5xx_i2s.configured = 0;
 }
 
@@ -284,7 +272,6 @@
 	SNDRV_PCM_FMTBIT_S32_LE)
 
 static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
-	.startup	= bf5xx_i2s_startup,
 	.shutdown	= bf5xx_i2s_shutdown,
 	.hw_params	= bf5xx_i2s_hw_params,
 	.set_fmt	= bf5xx_i2s_set_dai_fmt,
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
index ccb5e82..a8c73cb 100644
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.c
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c
@@ -43,7 +43,7 @@
 #include "bf5xx-tdm.h"
 #include "bf5xx-sport.h"
 
-#define PCM_BUFFER_MAX  0x10000
+#define PCM_BUFFER_MAX  0x8000
 #define FRAGMENT_SIZE_MIN  (4*1024)
 #define FRAGMENTS_MIN  2
 #define FRAGMENTS_MAX  32
@@ -177,6 +177,9 @@
 static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
 	snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
 {
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+	struct bf5xx_tdm_port *tdm_port = sport->private_data;
 	unsigned int *src;
 	unsigned int *dst;
 	int i;
@@ -188,7 +191,7 @@
 		dst += pos * 8;
 		while (count--) {
 			for (i = 0; i < substream->runtime->channels; i++)
-				*(dst + i) = *src++;
+				*(dst + tdm_port->tx_map[i]) = *src++;
 			dst += 8;
 		}
 	} else {
@@ -198,7 +201,7 @@
 		src += pos * 8;
 		while (count--) {
 			for (i = 0; i < substream->runtime->channels; i++)
-				*dst++ = *(src+i);
+				*dst++ = *(src + tdm_port->rx_map[i]);
 			src += 8;
 		}
 	}
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c
index ff546e9..4b36012 100644
--- a/sound/soc/blackfin/bf5xx-tdm.c
+++ b/sound/soc/blackfin/bf5xx-tdm.c
@@ -46,14 +46,6 @@
 #include "bf5xx-sport.h"
 #include "bf5xx-tdm.h"
 
-struct bf5xx_tdm_port {
-	u16 tcr1;
-	u16 rcr1;
-	u16 tcr2;
-	u16 rcr2;
-	int configured;
-};
-
 static struct bf5xx_tdm_port bf5xx_tdm;
 static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
 
@@ -181,6 +173,40 @@
 		bf5xx_tdm.configured = 0;
 }
 
+static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
+		unsigned int tx_num, unsigned int *tx_slot,
+		unsigned int rx_num, unsigned int *rx_slot)
+{
+	int i;
+	unsigned int slot;
+	unsigned int tx_mapped = 0, rx_mapped = 0;
+
+	if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
+			(rx_num > BFIN_TDM_DAI_MAX_SLOTS))
+		return -EINVAL;
+
+	for (i = 0; i < tx_num; i++) {
+		slot = tx_slot[i];
+		if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+				(!(tx_mapped & (1 << slot)))) {
+			bf5xx_tdm.tx_map[i] = slot;
+			tx_mapped |= 1 << slot;
+		} else
+			return -EINVAL;
+	}
+	for (i = 0; i < rx_num; i++) {
+		slot = rx_slot[i];
+		if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+				(!(rx_mapped & (1 << slot)))) {
+			bf5xx_tdm.rx_map[i] = slot;
+			rx_mapped |= 1 << slot;
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
 #ifdef CONFIG_PM
 static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
 {
@@ -235,6 +261,7 @@
 	.hw_params      = bf5xx_tdm_hw_params,
 	.set_fmt        = bf5xx_tdm_set_dai_fmt,
 	.shutdown       = bf5xx_tdm_shutdown,
+	.set_channel_map   = bf5xx_tdm_set_channel_map,
 };
 
 struct snd_soc_dai bf5xx_tdm_dai = {
@@ -300,6 +327,8 @@
 		pr_err("Failed to register DAI: %d\n", ret);
 		goto sport_config_err;
 	}
+
+	sport_handle->private_data = &bf5xx_tdm;
 	return 0;
 
 sport_config_err:
diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h
index 618ec3d..04189a1 100644
--- a/sound/soc/blackfin/bf5xx-tdm.h
+++ b/sound/soc/blackfin/bf5xx-tdm.h
@@ -9,6 +9,17 @@
 #ifndef _BF5XX_TDM_H
 #define _BF5XX_TDM_H
 
+#define BFIN_TDM_DAI_MAX_SLOTS 8
+struct bf5xx_tdm_port {
+	u16 tcr1;
+	u16 rcr1;
+	u16 tcr2;
+	u16 rcr2;
+	unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS];
+	unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS];
+	int configured;
+};
+
 extern struct snd_soc_dai bf5xx_tdm_dai;
 
 #endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 0edca93..4a3e8dc 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -19,6 +19,7 @@
 	select SND_SOC_AK4104 if SPI_MASTER
 	select SND_SOC_AK4535 if I2C
 	select SND_SOC_AK4642 if I2C
+	select SND_SOC_AK4671 if I2C
 	select SND_SOC_CS4270 if I2C
 	select SND_SOC_MAX9877 if I2C
 	select SND_SOC_PCM3008
@@ -28,6 +29,8 @@
 	select SND_SOC_TLV320AIC23 if I2C
 	select SND_SOC_TLV320AIC26 if SPI_MASTER
 	select SND_SOC_TLV320AIC3X if I2C
+	select SND_SOC_TPA6130A2 if I2C
+	select SND_SOC_TLV320DAC33 if I2C
 	select SND_SOC_TWL4030 if TWL4030_CORE
 	select SND_SOC_UDA134X
 	select SND_SOC_UDA1380 if I2C
@@ -36,6 +39,8 @@
 	select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8523 if I2C
 	select SND_SOC_WM8580 if I2C
+	select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI
+	select SND_SOC_WM8727
 	select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
@@ -96,6 +101,9 @@
 config SND_SOC_AK4642
 	tristate
 
+config SND_SOC_AK4671
+	tristate
+
 # Cirrus Logic CS4270 Codec
 config SND_SOC_CS4270
 	tristate
@@ -136,7 +144,11 @@
 config SND_SOC_TLV320AIC3X
 	tristate
 
+config SND_SOC_TLV320DAC33
+	tristate
+
 config SND_SOC_TWL4030
+	select TWL4030_CODEC
 	tristate
 
 config SND_SOC_UDA134X
@@ -160,6 +172,12 @@
 config SND_SOC_WM8580
 	tristate
 
+config SND_SOC_WM8711
+	tristate
+
+config SND_SOC_WM8727
+	tristate
+
 config SND_SOC_WM8728
 	tristate
 
@@ -220,3 +238,6 @@
 # Amp
 config SND_SOC_MAX9877
 	tristate
+
+config SND_SOC_TPA6130A2
+	tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index fb4af28..cacfc76 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -6,6 +6,7 @@
 snd-soc-ak4104-objs := ak4104.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-ak4642-objs := ak4642.o
+snd-soc-ak4671-objs := ak4671.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-l3-objs := l3.o
@@ -16,6 +17,7 @@
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-tlv320dac33-objs := tlv320dac33.o
 snd-soc-twl4030-objs := twl4030.o
 snd-soc-uda134x-objs := uda134x.o
 snd-soc-uda1380-objs := uda1380.o
@@ -24,6 +26,8 @@
 snd-soc-wm8510-objs := wm8510.o
 snd-soc-wm8523-objs := wm8523.o
 snd-soc-wm8580-objs := wm8580.o
+snd-soc-wm8711-objs := wm8711.o
+snd-soc-wm8727-objs := wm8727.o
 snd-soc-wm8728-objs := wm8728.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8750-objs := wm8750.o
@@ -47,6 +51,7 @@
 
 # Amp
 snd-soc-max9877-objs := max9877.o
+snd-soc-tpa6130a2-objs := tpa6130a2.o
 
 obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o
 obj-$(CONFIG_SND_SOC_AD1836)	+= snd-soc-ad1836.o
@@ -56,6 +61,7 @@
 obj-$(CONFIG_SND_SOC_AK4104)	+= snd-soc-ak4104.o
 obj-$(CONFIG_SND_SOC_AK4535)	+= snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_AK4642)	+= snd-soc-ak4642.o
+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_L3)	+= snd-soc-l3.o
@@ -66,6 +72,7 @@
 obj-$(CONFIG_SND_SOC_TLV320AIC23)	+= snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)	+= snd-soc-tlv320aic26.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TLV320DAC33)	+= snd-soc-tlv320dac33.o
 obj-$(CONFIG_SND_SOC_TWL4030)	+= snd-soc-twl4030.o
 obj-$(CONFIG_SND_SOC_UDA134X)	+= snd-soc-uda134x.o
 obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o
@@ -74,6 +81,8 @@
 obj-$(CONFIG_SND_SOC_WM8510)	+= snd-soc-wm8510.o
 obj-$(CONFIG_SND_SOC_WM8523)	+= snd-soc-wm8523.o
 obj-$(CONFIG_SND_SOC_WM8580)	+= snd-soc-wm8580.o
+obj-$(CONFIG_SND_SOC_WM8711)	+= snd-soc-wm8711.o
+obj-$(CONFIG_SND_SOC_WM8727)	+= snd-soc-wm8727.o
 obj-$(CONFIG_SND_SOC_WM8728)	+= snd-soc-wm8728.o
 obj-$(CONFIG_SND_SOC_WM8731)	+= snd-soc-wm8731.o
 obj-$(CONFIG_SND_SOC_WM8750)	+= snd-soc-wm8750.o
@@ -97,3 +106,4 @@
 
 # Amp
 obj-$(CONFIG_SND_SOC_MAX9877)	+= snd-soc-max9877.o
+obj-$(CONFIG_SND_SOC_TPA6130A2)	+= snd-soc-tpa6130a2.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 932299b..69bd0ac 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -117,9 +117,6 @@
 	if (ret < 0)
 		goto bus_err;
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0)
-		goto bus_err;
 	return 0;
 
 bus_err:
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index c48485f..2e360c2 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -387,12 +387,6 @@
 	snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
 	snd_soc_dapm_new_widgets(codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
-
 	return ret;
 
 card_err:
diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c
index 34b30ef..09c008a 100644
--- a/sound/soc/codecs/ad1938.c
+++ b/sound/soc/codecs/ad1938.c
@@ -596,12 +596,6 @@
 
 	ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
-
 	return ret;
 
 card_err:
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index d7440a9..39c0f758 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -257,11 +257,6 @@
 
 	snd_soc_add_controls(codec, ad1980_snd_ac97_controls,
 				ARRAY_SIZE(ad1980_snd_ac97_controls));
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "ad1980: failed to register card\n");
-		goto reset_err;
-	}
 
 	return 0;
 
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index e61dac5..d2fcc60 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -64,16 +64,8 @@
 		goto pcm_err;
 	}
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "ad73311: failed to register card\n");
-		goto register_err;
-	}
-
 	return ret;
 
-register_err:
-	snd_soc_free_pcms(socdev);
 pcm_err:
 	kfree(socdev->card->codec);
 	socdev->card->codec = NULL;
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index 4d47bc4..3a14c6f 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -313,14 +313,6 @@
 		return ret;
 	}
 
-	/* Register the socdev */
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card\n");
-		snd_soc_free_pcms(socdev);
-		return ret;
-	}
-
 	return 0;
 }
 
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 0abec0d..57a6846 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -485,17 +485,9 @@
 	snd_soc_add_controls(codec, ak4535_snd_controls,
 				ARRAY_SIZE(ak4535_snd_controls));
 	ak4535_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "ak4535: failed to register card\n");
-		goto card_err;
-	}
 
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	kfree(codec->reg_cache);
 
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index e057c7b..b69861d 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -442,18 +442,9 @@
 		goto pcm_err;
 	}
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "ak4642: failed to register card\n");
-		goto card_err;
-	}
-
 	dev_info(&pdev->dev, "AK4642 Audio Codec %s", AK4642_VERSION);
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	return ret;
 
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
new file mode 100644
index 0000000..364832c
--- /dev/null
+++ b/sound/soc/codecs/ak4671.c
@@ -0,0 +1,816 @@
+/*
+ * ak4671.c  --  audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@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/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "ak4671.h"
+
+static struct snd_soc_codec *ak4671_codec;
+
+/* codec private data */
+struct ak4671_priv {
+	struct snd_soc_codec codec;
+	u8 reg_cache[AK4671_CACHEREGNUM];
+};
+
+/* ak4671 register cache & default register settings */
+static const u8 ak4671_reg[AK4671_CACHEREGNUM] = {
+	0x00,	/* AK4671_AD_DA_POWER_MANAGEMENT	(0x00)	*/
+	0xf6,	/* AK4671_PLL_MODE_SELECT0		(0x01)	*/
+	0x00,	/* AK4671_PLL_MODE_SELECT1		(0x02)	*/
+	0x02,	/* AK4671_FORMAT_SELECT			(0x03)	*/
+	0x00,	/* AK4671_MIC_SIGNAL_SELECT		(0x04)	*/
+	0x55,	/* AK4671_MIC_AMP_GAIN			(0x05)	*/
+	0x00,	/* AK4671_MIXING_POWER_MANAGEMENT0	(0x06)	*/
+	0x00,	/* AK4671_MIXING_POWER_MANAGEMENT1	(0x07)	*/
+	0xb5,	/* AK4671_OUTPUT_VOLUME_CONTROL		(0x08)	*/
+	0x00,	/* AK4671_LOUT1_SIGNAL_SELECT		(0x09)	*/
+	0x00,	/* AK4671_ROUT1_SIGNAL_SELECT		(0x0a)	*/
+	0x00,	/* AK4671_LOUT2_SIGNAL_SELECT		(0x0b)	*/
+	0x00,	/* AK4671_ROUT2_SIGNAL_SELECT		(0x0c)	*/
+	0x00,	/* AK4671_LOUT3_SIGNAL_SELECT		(0x0d)	*/
+	0x00,	/* AK4671_ROUT3_SIGNAL_SELECT		(0x0e)	*/
+	0x00,	/* AK4671_LOUT1_POWER_MANAGERMENT	(0x0f)	*/
+	0x00,	/* AK4671_LOUT2_POWER_MANAGERMENT	(0x10)	*/
+	0x80,	/* AK4671_LOUT3_POWER_MANAGERMENT	(0x11)	*/
+	0x91,	/* AK4671_LCH_INPUT_VOLUME_CONTROL	(0x12)	*/
+	0x91,	/* AK4671_RCH_INPUT_VOLUME_CONTROL	(0x13)	*/
+	0xe1,	/* AK4671_ALC_REFERENCE_SELECT		(0x14)	*/
+	0x00,	/* AK4671_DIGITAL_MIXING_CONTROL	(0x15)	*/
+	0x00,	/* AK4671_ALC_TIMER_SELECT		(0x16)	*/
+	0x00,	/* AK4671_ALC_MODE_CONTROL		(0x17)	*/
+	0x02,	/* AK4671_MODE_CONTROL1			(0x18)	*/
+	0x01,	/* AK4671_MODE_CONTROL2			(0x19)	*/
+	0x18,	/* AK4671_LCH_OUTPUT_VOLUME_CONTROL	(0x1a)	*/
+	0x18,	/* AK4671_RCH_OUTPUT_VOLUME_CONTROL	(0x1b)	*/
+	0x00,	/* AK4671_SIDETONE_A_CONTROL		(0x1c)	*/
+	0x02,	/* AK4671_DIGITAL_FILTER_SELECT		(0x1d)	*/
+	0x00,	/* AK4671_FIL3_COEFFICIENT0		(0x1e)	*/
+	0x00,	/* AK4671_FIL3_COEFFICIENT1		(0x1f)	*/
+	0x00,	/* AK4671_FIL3_COEFFICIENT2		(0x20)	*/
+	0x00,	/* AK4671_FIL3_COEFFICIENT3		(0x21)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT0		(0x22)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT1		(0x23)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT2		(0x24)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT3		(0x25)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT4		(0x26)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT5		(0x27)	*/
+	0xa9,	/* AK4671_FIL1_COEFFICIENT0		(0x28)	*/
+	0x1f,	/* AK4671_FIL1_COEFFICIENT1		(0x29)	*/
+	0xad,	/* AK4671_FIL1_COEFFICIENT2		(0x2a)	*/
+	0x20,	/* AK4671_FIL1_COEFFICIENT3		(0x2b)	*/
+	0x00,	/* AK4671_FIL2_COEFFICIENT0		(0x2c)	*/
+	0x00,	/* AK4671_FIL2_COEFFICIENT1		(0x2d)	*/
+	0x00,	/* AK4671_FIL2_COEFFICIENT2		(0x2e)	*/
+	0x00,	/* AK4671_FIL2_COEFFICIENT3		(0x2f)	*/
+	0x00,	/* AK4671_DIGITAL_FILTER_SELECT2	(0x30)	*/
+	0x00,	/* this register not used			*/
+	0x00,	/* AK4671_E1_COEFFICIENT0		(0x32)	*/
+	0x00,	/* AK4671_E1_COEFFICIENT1		(0x33)	*/
+	0x00,	/* AK4671_E1_COEFFICIENT2		(0x34)	*/
+	0x00,	/* AK4671_E1_COEFFICIENT3		(0x35)	*/
+	0x00,	/* AK4671_E1_COEFFICIENT4		(0x36)	*/
+	0x00,	/* AK4671_E1_COEFFICIENT5		(0x37)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT0		(0x38)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT1		(0x39)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT2		(0x3a)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT3		(0x3b)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT4		(0x3c)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT5		(0x3d)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT0		(0x3e)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT1		(0x3f)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT2		(0x40)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT3		(0x41)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT4		(0x42)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT5		(0x43)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT0		(0x44)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT1		(0x45)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT2		(0x46)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT3		(0x47)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT4		(0x48)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT5		(0x49)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT0		(0x4a)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT1		(0x4b)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT2		(0x4c)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT3		(0x4d)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT4		(0x4e)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT5		(0x4f)	*/
+	0x88,	/* AK4671_EQ_CONTROL_250HZ_100HZ	(0x50)	*/
+	0x88,	/* AK4671_EQ_CONTROL_3500HZ_1KHZ	(0x51)	*/
+	0x08,	/* AK4671_EQ_CONTRO_10KHZ		(0x52)	*/
+	0x00,	/* AK4671_PCM_IF_CONTROL0		(0x53)	*/
+	0x00,	/* AK4671_PCM_IF_CONTROL1		(0x54)	*/
+	0x00,	/* AK4671_PCM_IF_CONTROL2		(0x55)	*/
+	0x18,	/* AK4671_DIGITAL_VOLUME_B_CONTROL	(0x56)	*/
+	0x18,	/* AK4671_DIGITAL_VOLUME_C_CONTROL	(0x57)	*/
+	0x00,	/* AK4671_SIDETONE_VOLUME_CONTROL	(0x58)	*/
+	0x00,	/* AK4671_DIGITAL_MIXING_CONTROL2	(0x59)	*/
+	0x00,	/* AK4671_SAR_ADC_CONTROL		(0x5a)	*/
+};
+
+/*
+ * LOUT1/ROUT1 output volume control:
+ * from -24 to 6 dB in 6 dB steps (mute instead of -30 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out1_tlv, -3000, 600, 1);
+
+/*
+ * LOUT2/ROUT2 output volume control:
+ * from -33 to 6 dB in 3 dB steps (mute instead of -33 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out2_tlv, -3300, 300, 1);
+
+/*
+ * LOUT3/ROUT3 output volume control:
+ * from -6 to 3 dB in 3 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(out3_tlv, -600, 300, 0);
+
+/*
+ * Mic amp gain control:
+ * from -15 to 30 dB in 3 dB steps
+ * REVISIT: The actual min value(0x01) is -12 dB and the reg value 0x00 is not
+ * available
+ */
+static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -1500, 300, 0);
+
+static const struct snd_kcontrol_new ak4671_snd_controls[] = {
+	/* Common playback gain controls */
+	SOC_SINGLE_TLV("Line Output1 Playback Volume",
+			AK4671_OUTPUT_VOLUME_CONTROL, 0, 0x6, 0, out1_tlv),
+	SOC_SINGLE_TLV("Headphone Output2 Playback Volume",
+			AK4671_OUTPUT_VOLUME_CONTROL, 4, 0xd, 0, out2_tlv),
+	SOC_SINGLE_TLV("Line Output3 Playback Volume",
+			AK4671_LOUT3_POWER_MANAGERMENT, 6, 0x3, 0, out3_tlv),
+
+	/* Common capture gain controls */
+	SOC_DOUBLE_TLV("Mic Amp Capture Volume",
+			AK4671_MIC_AMP_GAIN, 0, 4, 0xf, 0, mic_amp_tlv),
+};
+
+/* event handlers */
+static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	u8 reg;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+		reg |= AK4671_MUTEN;
+		snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+		reg &= ~AK4671_MUTEN;
+		snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+		break;
+	}
+
+	return 0;
+}
+
+/* Output Mixers */
+static const struct snd_kcontrol_new ak4671_lout1_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACL", AK4671_LOUT1_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("LINL1", AK4671_LOUT1_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("LINL2", AK4671_LOUT1_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("LINL3", AK4671_LOUT1_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("LINL4", AK4671_LOUT1_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPL", AK4671_LOUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout1_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACR", AK4671_ROUT1_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("RINR1", AK4671_ROUT1_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("RINR2", AK4671_ROUT1_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("RINR3", AK4671_ROUT1_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("RINR4", AK4671_ROUT1_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPR", AK4671_ROUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout2_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACHL", AK4671_LOUT2_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("LINH1", AK4671_LOUT2_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("LINH2", AK4671_LOUT2_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("LINH3", AK4671_LOUT2_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("LINH4", AK4671_LOUT2_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPHL", AK4671_LOUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout2_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACHR", AK4671_ROUT2_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("RINH1", AK4671_ROUT2_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("RINH2", AK4671_ROUT2_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("RINH3", AK4671_ROUT2_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("RINH4", AK4671_ROUT2_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPHR", AK4671_ROUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout3_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACSL", AK4671_LOUT3_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("LINS1", AK4671_LOUT3_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("LINS2", AK4671_LOUT3_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("LINS3", AK4671_LOUT3_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("LINS4", AK4671_LOUT3_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPSL", AK4671_LOUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACSR", AK4671_ROUT3_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("RINS1", AK4671_ROUT3_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("RINS2", AK4671_ROUT3_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("RINS3", AK4671_ROUT3_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("RINS4", AK4671_ROUT3_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPSR", AK4671_ROUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+/* Input MUXs */
+static const char *ak4671_lin_mux_texts[] =
+		{"LIN1", "LIN2", "LIN3", "LIN4"};
+static const struct soc_enum ak4671_lin_mux_enum =
+	SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 0,
+			ARRAY_SIZE(ak4671_lin_mux_texts),
+			ak4671_lin_mux_texts);
+static const struct snd_kcontrol_new ak4671_lin_mux_control =
+	SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum);
+
+static const char *ak4671_rin_mux_texts[] =
+		{"RIN1", "RIN2", "RIN3", "RIN4"};
+static const struct soc_enum ak4671_rin_mux_enum =
+	SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 2,
+			ARRAY_SIZE(ak4671_rin_mux_texts),
+			ak4671_rin_mux_texts);
+static const struct snd_kcontrol_new ak4671_rin_mux_control =
+	SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum);
+
+static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
+	/* Inputs */
+	SND_SOC_DAPM_INPUT("LIN1"),
+	SND_SOC_DAPM_INPUT("RIN1"),
+	SND_SOC_DAPM_INPUT("LIN2"),
+	SND_SOC_DAPM_INPUT("RIN2"),
+	SND_SOC_DAPM_INPUT("LIN3"),
+	SND_SOC_DAPM_INPUT("RIN3"),
+	SND_SOC_DAPM_INPUT("LIN4"),
+	SND_SOC_DAPM_INPUT("RIN4"),
+
+	/* Outputs */
+	SND_SOC_DAPM_OUTPUT("LOUT1"),
+	SND_SOC_DAPM_OUTPUT("ROUT1"),
+	SND_SOC_DAPM_OUTPUT("LOUT2"),
+	SND_SOC_DAPM_OUTPUT("ROUT2"),
+	SND_SOC_DAPM_OUTPUT("LOUT3"),
+	SND_SOC_DAPM_OUTPUT("ROUT3"),
+
+	/* DAC */
+	SND_SOC_DAPM_DAC("DAC Left", "Left HiFi Playback",
+			AK4671_AD_DA_POWER_MANAGEMENT, 6, 0),
+	SND_SOC_DAPM_DAC("DAC Right", "Right HiFi Playback",
+			AK4671_AD_DA_POWER_MANAGEMENT, 7, 0),
+
+	/* ADC */
+	SND_SOC_DAPM_ADC("ADC Left", "Left HiFi Capture",
+			AK4671_AD_DA_POWER_MANAGEMENT, 4, 0),
+	SND_SOC_DAPM_ADC("ADC Right", "Right HiFi Capture",
+			AK4671_AD_DA_POWER_MANAGEMENT, 5, 0),
+
+	/* PGA */
+	SND_SOC_DAPM_PGA("LOUT2 Mix Amp",
+			AK4671_LOUT2_POWER_MANAGERMENT, 5, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("ROUT2 Mix Amp",
+			AK4671_LOUT2_POWER_MANAGERMENT, 6, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("LIN1 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("RIN1 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("LIN2 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("RIN2 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("LIN3 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("RIN3 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 5, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("LIN4 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 6, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("RIN4 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 7, 0, NULL, 0),
+
+	/* Output Mixers */
+	SND_SOC_DAPM_MIXER("LOUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 0, 0,
+			&ak4671_lout1_mixer_controls[0],
+			ARRAY_SIZE(ak4671_lout1_mixer_controls)),
+	SND_SOC_DAPM_MIXER("ROUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 1, 0,
+			&ak4671_rout1_mixer_controls[0],
+			ARRAY_SIZE(ak4671_rout1_mixer_controls)),
+	SND_SOC_DAPM_MIXER_E("LOUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+			0, 0, &ak4671_lout2_mixer_controls[0],
+			ARRAY_SIZE(ak4671_lout2_mixer_controls),
+			ak4671_out2_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_MIXER_E("ROUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+			1, 0, &ak4671_rout2_mixer_controls[0],
+			ARRAY_SIZE(ak4671_rout2_mixer_controls),
+			ak4671_out2_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_MIXER("LOUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 0, 0,
+			&ak4671_lout3_mixer_controls[0],
+			ARRAY_SIZE(ak4671_lout3_mixer_controls)),
+	SND_SOC_DAPM_MIXER("ROUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 1, 0,
+			&ak4671_rout3_mixer_controls[0],
+			ARRAY_SIZE(ak4671_rout3_mixer_controls)),
+
+	/* Input MUXs */
+	SND_SOC_DAPM_MUX("LIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 2, 0,
+			&ak4671_lin_mux_control),
+	SND_SOC_DAPM_MUX("RIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 3, 0,
+			&ak4671_rin_mux_control),
+
+	/* Mic Power */
+	SND_SOC_DAPM_MICBIAS("Mic Bias", AK4671_AD_DA_POWER_MANAGEMENT, 1, 0),
+
+	/* Supply */
+	SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	{"DAC Left", "NULL", "PMPLL"},
+	{"DAC Right", "NULL", "PMPLL"},
+	{"ADC Left", "NULL", "PMPLL"},
+	{"ADC Right", "NULL", "PMPLL"},
+
+	/* Outputs */
+	{"LOUT1", "NULL", "LOUT1 Mixer"},
+	{"ROUT1", "NULL", "ROUT1 Mixer"},
+	{"LOUT2", "NULL", "LOUT2 Mix Amp"},
+	{"ROUT2", "NULL", "ROUT2 Mix Amp"},
+	{"LOUT3", "NULL", "LOUT3 Mixer"},
+	{"ROUT3", "NULL", "ROUT3 Mixer"},
+
+	{"LOUT1 Mixer", "DACL", "DAC Left"},
+	{"ROUT1 Mixer", "DACR", "DAC Right"},
+	{"LOUT2 Mixer", "DACHL", "DAC Left"},
+	{"ROUT2 Mixer", "DACHR", "DAC Right"},
+	{"LOUT2 Mix Amp", "NULL", "LOUT2 Mixer"},
+	{"ROUT2 Mix Amp", "NULL", "ROUT2 Mixer"},
+	{"LOUT3 Mixer", "DACSL", "DAC Left"},
+	{"ROUT3 Mixer", "DACSR", "DAC Right"},
+
+	/* Inputs */
+	{"LIN MUX", "LIN1", "LIN1"},
+	{"LIN MUX", "LIN2", "LIN2"},
+	{"LIN MUX", "LIN3", "LIN3"},
+	{"LIN MUX", "LIN4", "LIN4"},
+
+	{"RIN MUX", "RIN1", "RIN1"},
+	{"RIN MUX", "RIN2", "RIN2"},
+	{"RIN MUX", "RIN3", "RIN3"},
+	{"RIN MUX", "RIN4", "RIN4"},
+
+	{"LIN1", NULL, "Mic Bias"},
+	{"RIN1", NULL, "Mic Bias"},
+	{"LIN2", NULL, "Mic Bias"},
+	{"RIN2", NULL, "Mic Bias"},
+
+	{"ADC Left", "NULL", "LIN MUX"},
+	{"ADC Right", "NULL", "RIN MUX"},
+
+	/* Analog Loops */
+	{"LIN1 Mixing Circuit", "NULL", "LIN1"},
+	{"RIN1 Mixing Circuit", "NULL", "RIN1"},
+	{"LIN2 Mixing Circuit", "NULL", "LIN2"},
+	{"RIN2 Mixing Circuit", "NULL", "RIN2"},
+	{"LIN3 Mixing Circuit", "NULL", "LIN3"},
+	{"RIN3 Mixing Circuit", "NULL", "RIN3"},
+	{"LIN4 Mixing Circuit", "NULL", "LIN4"},
+	{"RIN4 Mixing Circuit", "NULL", "RIN4"},
+
+	{"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"},
+	{"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"},
+	{"LOUT2 Mixer", "LINH1", "LIN1 Mixing Circuit"},
+	{"ROUT2 Mixer", "RINH1", "RIN1 Mixing Circuit"},
+	{"LOUT3 Mixer", "LINS1", "LIN1 Mixing Circuit"},
+	{"ROUT3 Mixer", "RINS1", "RIN1 Mixing Circuit"},
+
+	{"LOUT1 Mixer", "LINL2", "LIN2 Mixing Circuit"},
+	{"ROUT1 Mixer", "RINR2", "RIN2 Mixing Circuit"},
+	{"LOUT2 Mixer", "LINH2", "LIN2 Mixing Circuit"},
+	{"ROUT2 Mixer", "RINH2", "RIN2 Mixing Circuit"},
+	{"LOUT3 Mixer", "LINS2", "LIN2 Mixing Circuit"},
+	{"ROUT3 Mixer", "RINS2", "RIN2 Mixing Circuit"},
+
+	{"LOUT1 Mixer", "LINL3", "LIN3 Mixing Circuit"},
+	{"ROUT1 Mixer", "RINR3", "RIN3 Mixing Circuit"},
+	{"LOUT2 Mixer", "LINH3", "LIN3 Mixing Circuit"},
+	{"ROUT2 Mixer", "RINH3", "RIN3 Mixing Circuit"},
+	{"LOUT3 Mixer", "LINS3", "LIN3 Mixing Circuit"},
+	{"ROUT3 Mixer", "RINS3", "RIN3 Mixing Circuit"},
+
+	{"LOUT1 Mixer", "LINL4", "LIN4 Mixing Circuit"},
+	{"ROUT1 Mixer", "RINR4", "RIN4 Mixing Circuit"},
+	{"LOUT2 Mixer", "LINH4", "LIN4 Mixing Circuit"},
+	{"ROUT2 Mixer", "RINH4", "RIN4 Mixing Circuit"},
+	{"LOUT3 Mixer", "LINS4", "LIN4 Mixing Circuit"},
+	{"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
+};
+
+static int ak4671_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, ak4671_dapm_widgets,
+				  ARRAY_SIZE(ak4671_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+static int ak4671_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;
+	u8 fs;
+
+	fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+	fs &= ~AK4671_FS;
+
+	switch (params_rate(params)) {
+	case 8000:
+		fs |= AK4671_FS_8KHZ;
+		break;
+	case 12000:
+		fs |= AK4671_FS_12KHZ;
+		break;
+	case 16000:
+		fs |= AK4671_FS_16KHZ;
+		break;
+	case 24000:
+		fs |= AK4671_FS_24KHZ;
+		break;
+	case 11025:
+		fs |= AK4671_FS_11_025KHZ;
+		break;
+	case 22050:
+		fs |= AK4671_FS_22_05KHZ;
+		break;
+	case 32000:
+		fs |= AK4671_FS_32KHZ;
+		break;
+	case 44100:
+		fs |= AK4671_FS_44_1KHZ;
+		break;
+	case 48000:
+		fs |= AK4671_FS_48KHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
+
+	return 0;
+}
+
+static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+		unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u8 pll;
+
+	pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+	pll &= ~AK4671_PLL;
+
+	switch (freq) {
+	case 11289600:
+		pll |= AK4671_PLL_11_2896MHZ;
+		break;
+	case 12000000:
+		pll |= AK4671_PLL_12MHZ;
+		break;
+	case 12288000:
+		pll |= AK4671_PLL_12_288MHZ;
+		break;
+	case 13000000:
+		pll |= AK4671_PLL_13MHZ;
+		break;
+	case 13500000:
+		pll |= AK4671_PLL_13_5MHZ;
+		break;
+	case 19200000:
+		pll |= AK4671_PLL_19_2MHZ;
+		break;
+	case 24000000:
+		pll |= AK4671_PLL_24MHZ;
+		break;
+	case 26000000:
+		pll |= AK4671_PLL_26MHZ;
+		break;
+	case 27000000:
+		pll |= AK4671_PLL_27MHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll);
+
+	return 0;
+}
+
+static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u8 mode;
+	u8 format;
+
+	/* set master/slave audio interface */
+	mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		mode |= AK4671_M_S;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		mode &= ~(AK4671_M_S);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
+	format &= ~AK4671_DIF;
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		format |= AK4671_DIF_I2S_MODE;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		format |= AK4671_DIF_MSB_MODE;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		format |= AK4671_DIF_DSP_MODE;
+		format |= AK4671_BCKP;
+		format |= AK4671_MSBS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* set mode and format */
+	snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
+	snd_soc_write(codec, AK4671_FORMAT_SELECT, format);
+
+	return 0;
+}
+
+static int ak4671_set_bias_level(struct snd_soc_codec *codec,
+		enum snd_soc_bias_level level)
+{
+	u8 reg;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+	case SND_SOC_BIAS_STANDBY:
+		reg = snd_soc_read(codec, AK4671_AD_DA_POWER_MANAGEMENT);
+		snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT,
+				reg | AK4671_PMVCM);
+		break;
+	case SND_SOC_BIAS_OFF:
+		snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define AK4671_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)
+
+#define AK4671_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
+
+static struct snd_soc_dai_ops ak4671_dai_ops = {
+	.hw_params	= ak4671_hw_params,
+	.set_sysclk	= ak4671_set_dai_sysclk,
+	.set_fmt	= ak4671_set_dai_fmt,
+};
+
+struct snd_soc_dai ak4671_dai = {
+	.name = "AK4671",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = AK4671_RATES,
+		.formats = AK4671_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = AK4671_RATES,
+		.formats = AK4671_FORMATS,},
+	.ops = &ak4671_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ak4671_dai);
+
+static int ak4671_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (ak4671_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = ak4671_codec;
+	codec = ak4671_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, ak4671_snd_controls,
+			     ARRAY_SIZE(ak4671_snd_controls));
+	ak4671_add_widgets(codec);
+
+	ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return ret;
+
+pcm_err:
+	return ret;
+}
+
+static int ak4671_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_ak4671 = {
+	.probe = ak4671_probe,
+	.remove = ak4671_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4671);
+
+static int ak4671_register(struct ak4671_priv *ak4671,
+		enum snd_soc_control_type control)
+{
+	int ret;
+	struct snd_soc_codec *codec = &ak4671->codec;
+
+	if (ak4671_codec) {
+		dev_err(codec->dev, "Another AK4671 is registered\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = ak4671;
+	codec->name = "AK4671";
+	codec->owner = THIS_MODULE;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = ak4671_set_bias_level;
+	codec->dai = &ak4671_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = AK4671_CACHEREGNUM;
+	codec->reg_cache = &ak4671->reg_cache;
+
+	memcpy(codec->reg_cache, ak4671_reg, sizeof(ak4671_reg));
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, control);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	ak4671_dai.dev = codec->dev;
+	ak4671_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(&ak4671_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(ak4671);
+	return ret;
+}
+
+static void ak4671_unregister(struct ak4671_priv *ak4671)
+{
+	ak4671_set_bias_level(&ak4671->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dai(&ak4671_dai);
+	snd_soc_unregister_codec(&ak4671->codec);
+	kfree(ak4671);
+	ak4671_codec = NULL;
+}
+
+static int __devinit ak4671_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct ak4671_priv *ak4671;
+	struct snd_soc_codec *codec;
+
+	ak4671 = kzalloc(sizeof(struct ak4671_priv), GFP_KERNEL);
+	if (ak4671 == NULL)
+		return -ENOMEM;
+
+	codec = &ak4671->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(client, ak4671);
+	codec->control_data = client;
+
+	codec->dev = &client->dev;
+
+	return ak4671_register(ak4671, SND_SOC_I2C);
+}
+
+static __devexit int ak4671_i2c_remove(struct i2c_client *client)
+{
+	struct ak4671_priv *ak4671 = i2c_get_clientdata(client);
+
+	ak4671_unregister(ak4671);
+
+	return 0;
+}
+
+static const struct i2c_device_id ak4671_i2c_id[] = {
+	{ "ak4671", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
+
+static struct i2c_driver ak4671_i2c_driver = {
+	.driver = {
+		.name = "ak4671",
+		.owner = THIS_MODULE,
+	},
+	.probe = ak4671_i2c_probe,
+	.remove = __devexit_p(ak4671_i2c_remove),
+	.id_table = ak4671_i2c_id,
+};
+
+static int __init ak4671_modinit(void)
+{
+	return i2c_add_driver(&ak4671_i2c_driver);
+}
+module_init(ak4671_modinit);
+
+static void __exit ak4671_exit(void)
+{
+	i2c_del_driver(&ak4671_i2c_driver);
+}
+module_exit(ak4671_exit);
+
+MODULE_DESCRIPTION("ASoC AK4671 codec driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h
new file mode 100644
index 0000000..e2fad96
--- /dev/null
+++ b/sound/soc/codecs/ak4671.h
@@ -0,0 +1,156 @@
+/*
+ * ak4671.h  --  audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@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.
+ *
+ */
+
+#ifndef _AK4671_H
+#define _AK4671_H
+
+#define AK4671_AD_DA_POWER_MANAGEMENT		0x00
+#define AK4671_PLL_MODE_SELECT0			0x01
+#define AK4671_PLL_MODE_SELECT1			0x02
+#define AK4671_FORMAT_SELECT			0x03
+#define AK4671_MIC_SIGNAL_SELECT		0x04
+#define AK4671_MIC_AMP_GAIN			0x05
+#define AK4671_MIXING_POWER_MANAGEMENT0		0x06
+#define AK4671_MIXING_POWER_MANAGEMENT1		0x07
+#define AK4671_OUTPUT_VOLUME_CONTROL		0x08
+#define AK4671_LOUT1_SIGNAL_SELECT		0x09
+#define AK4671_ROUT1_SIGNAL_SELECT		0x0a
+#define AK4671_LOUT2_SIGNAL_SELECT		0x0b
+#define AK4671_ROUT2_SIGNAL_SELECT		0x0c
+#define AK4671_LOUT3_SIGNAL_SELECT		0x0d
+#define AK4671_ROUT3_SIGNAL_SELECT		0x0e
+#define AK4671_LOUT1_POWER_MANAGERMENT		0x0f
+#define AK4671_LOUT2_POWER_MANAGERMENT		0x10
+#define AK4671_LOUT3_POWER_MANAGERMENT		0x11
+#define AK4671_LCH_INPUT_VOLUME_CONTROL		0x12
+#define AK4671_RCH_INPUT_VOLUME_CONTROL		0x13
+#define AK4671_ALC_REFERENCE_SELECT		0x14
+#define AK4671_DIGITAL_MIXING_CONTROL		0x15
+#define AK4671_ALC_TIMER_SELECT			0x16
+#define AK4671_ALC_MODE_CONTROL			0x17
+#define AK4671_MODE_CONTROL1			0x18
+#define AK4671_MODE_CONTROL2			0x19
+#define AK4671_LCH_OUTPUT_VOLUME_CONTROL	0x1a
+#define AK4671_RCH_OUTPUT_VOLUME_CONTROL	0x1b
+#define AK4671_SIDETONE_A_CONTROL		0x1c
+#define AK4671_DIGITAL_FILTER_SELECT		0x1d
+#define AK4671_FIL3_COEFFICIENT0		0x1e
+#define AK4671_FIL3_COEFFICIENT1		0x1f
+#define AK4671_FIL3_COEFFICIENT2		0x20
+#define AK4671_FIL3_COEFFICIENT3		0x21
+#define AK4671_EQ_COEFFICIENT0			0x22
+#define AK4671_EQ_COEFFICIENT1			0x23
+#define AK4671_EQ_COEFFICIENT2			0x24
+#define AK4671_EQ_COEFFICIENT3			0x25
+#define AK4671_EQ_COEFFICIENT4			0x26
+#define AK4671_EQ_COEFFICIENT5			0x27
+#define AK4671_FIL1_COEFFICIENT0		0x28
+#define AK4671_FIL1_COEFFICIENT1		0x29
+#define AK4671_FIL1_COEFFICIENT2		0x2a
+#define AK4671_FIL1_COEFFICIENT3		0x2b
+#define AK4671_FIL2_COEFFICIENT0		0x2c
+#define AK4671_FIL2_COEFFICIENT1		0x2d
+#define AK4671_FIL2_COEFFICIENT2		0x2e
+#define AK4671_FIL2_COEFFICIENT3		0x2f
+#define AK4671_DIGITAL_FILTER_SELECT2		0x30
+#define AK4671_E1_COEFFICIENT0			0x32
+#define AK4671_E1_COEFFICIENT1			0x33
+#define AK4671_E1_COEFFICIENT2			0x34
+#define AK4671_E1_COEFFICIENT3			0x35
+#define AK4671_E1_COEFFICIENT4			0x36
+#define AK4671_E1_COEFFICIENT5			0x37
+#define AK4671_E2_COEFFICIENT0			0x38
+#define AK4671_E2_COEFFICIENT1			0x39
+#define AK4671_E2_COEFFICIENT2			0x3a
+#define AK4671_E2_COEFFICIENT3			0x3b
+#define AK4671_E2_COEFFICIENT4			0x3c
+#define AK4671_E2_COEFFICIENT5			0x3d
+#define AK4671_E3_COEFFICIENT0			0x3e
+#define AK4671_E3_COEFFICIENT1			0x3f
+#define AK4671_E3_COEFFICIENT2			0x40
+#define AK4671_E3_COEFFICIENT3			0x41
+#define AK4671_E3_COEFFICIENT4			0x42
+#define AK4671_E3_COEFFICIENT5			0x43
+#define AK4671_E4_COEFFICIENT0			0x44
+#define AK4671_E4_COEFFICIENT1			0x45
+#define AK4671_E4_COEFFICIENT2			0x46
+#define AK4671_E4_COEFFICIENT3			0x47
+#define AK4671_E4_COEFFICIENT4			0x48
+#define AK4671_E4_COEFFICIENT5			0x49
+#define AK4671_E5_COEFFICIENT0			0x4a
+#define AK4671_E5_COEFFICIENT1			0x4b
+#define AK4671_E5_COEFFICIENT2			0x4c
+#define AK4671_E5_COEFFICIENT3			0x4d
+#define AK4671_E5_COEFFICIENT4			0x4e
+#define AK4671_E5_COEFFICIENT5			0x4f
+#define AK4671_EQ_CONTROL_250HZ_100HZ		0x50
+#define AK4671_EQ_CONTROL_3500HZ_1KHZ		0x51
+#define AK4671_EQ_CONTRO_10KHZ			0x52
+#define AK4671_PCM_IF_CONTROL0			0x53
+#define AK4671_PCM_IF_CONTROL1			0x54
+#define AK4671_PCM_IF_CONTROL2			0x55
+#define AK4671_DIGITAL_VOLUME_B_CONTROL		0x56
+#define AK4671_DIGITAL_VOLUME_C_CONTROL		0x57
+#define AK4671_SIDETONE_VOLUME_CONTROL		0x58
+#define AK4671_DIGITAL_MIXING_CONTROL2		0x59
+#define AK4671_SAR_ADC_CONTROL			0x5a
+
+#define AK4671_CACHEREGNUM			(AK4671_SAR_ADC_CONTROL + 1)
+
+/* Bitfield Definitions */
+
+/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */
+#define AK4671_PMVCM				0x01
+
+/* AK4671_PLL_MODE_SELECT0 (0x01) Fields */
+#define AK4671_PLL				0x0f
+#define AK4671_PLL_11_2896MHZ			(4 << 0)
+#define AK4671_PLL_12_288MHZ			(5 << 0)
+#define AK4671_PLL_12MHZ			(6 << 0)
+#define AK4671_PLL_24MHZ			(7 << 0)
+#define AK4671_PLL_19_2MHZ			(8 << 0)
+#define AK4671_PLL_13_5MHZ			(12 << 0)
+#define AK4671_PLL_27MHZ			(13 << 0)
+#define AK4671_PLL_13MHZ			(14 << 0)
+#define AK4671_PLL_26MHZ			(15 << 0)
+#define AK4671_FS				0xf0
+#define AK4671_FS_8KHZ				(0 << 4)
+#define AK4671_FS_12KHZ				(1 << 4)
+#define AK4671_FS_16KHZ				(2 << 4)
+#define AK4671_FS_24KHZ				(3 << 4)
+#define AK4671_FS_11_025KHZ			(5 << 4)
+#define AK4671_FS_22_05KHZ			(7 << 4)
+#define AK4671_FS_32KHZ				(10 << 4)
+#define AK4671_FS_48KHZ				(11 << 4)
+#define AK4671_FS_44_1KHZ			(15 << 4)
+
+/* AK4671_PLL_MODE_SELECT1 (0x02) Fields */
+#define AK4671_PMPLL				0x01
+#define AK4671_M_S				0x02
+
+/* AK4671_FORMAT_SELECT (0x03) Fields */
+#define AK4671_DIF				0x03
+#define AK4671_DIF_DSP_MODE			(0 << 0)
+#define AK4671_DIF_MSB_MODE			(2 << 0)
+#define AK4671_DIF_I2S_MODE			(3 << 0)
+#define AK4671_BCKP				0x04
+#define AK4671_MSBS				0x08
+#define AK4671_SDOD				0x10
+
+/* AK4671_LOUT2_POWER_MANAGEMENT (0x10) Fields */
+#define AK4671_MUTEN				0x04
+
+extern struct snd_soc_dai ak4671_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4671;
+
+#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index ca1e24a..ffe122d 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -520,6 +520,7 @@
 	SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0),
 	SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0),
 	SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0),
+	SOC_SINGLE("De-emphasis filter", CS4270_TRANS, 0, 1, 0),
 	SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1),
 	SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0),
 	SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1),
@@ -598,13 +599,6 @@
 		goto error_free_pcms;
 	}
 
-	/* And finally, register the socdev */
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card\n");
-		goto error_free_pcms;
-	}
-
 	return 0;
 
 error_free_pcms:
@@ -802,22 +796,6 @@
  * and all registers are written back to the hardware when resuming.
  */
 
-static int cs4270_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
-{
-	struct cs4270_private *cs4270 = i2c_get_clientdata(client);
-	struct snd_soc_codec *codec = &cs4270->codec;
-
-	return snd_soc_suspend_device(codec->dev);
-}
-
-static int cs4270_i2c_resume(struct i2c_client *client)
-{
-	struct cs4270_private *cs4270 = i2c_get_clientdata(client);
-	struct snd_soc_codec *codec = &cs4270->codec;
-
-	return snd_soc_resume_device(codec->dev);
-}
-
 static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
 {
 	struct snd_soc_codec *codec = cs4270_codec;
@@ -853,8 +831,6 @@
 	return snd_soc_write(codec, CS4270_PWRCTL, reg);
 }
 #else
-#define cs4270_i2c_suspend	NULL
-#define cs4270_i2c_resume	NULL
 #define cs4270_soc_suspend	NULL
 #define cs4270_soc_resume	NULL
 #endif /* CONFIG_PM */
@@ -873,8 +849,6 @@
 	.id_table = cs4270_id,
 	.probe = cs4270_i2c_probe,
 	.remove = cs4270_i2c_remove,
-	.suspend = cs4270_i2c_suspend,
-	.resume = cs4270_i2c_resume,
 };
 
 /*
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index 38eac9c..d7f9bf1 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -355,12 +355,6 @@
 
 	cx20442_add_widgets(codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register card\n");
-		goto card_err;
-	}
-
 	return ret;
 
 card_err:
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
index 5cda9e6..2afcd0a 100644
--- a/sound/soc/codecs/pcm3008.c
+++ b/sound/soc/codecs/pcm3008.c
@@ -90,13 +90,6 @@
 		goto pcm_err;
 	}
 
-	/* Register Card. */
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "pcm3008: failed to register card\n");
-		goto card_err;
-	}
-
 	/* DEM1  DEM0  DE-EMPHASIS_MODE
 	 * Low   Low   De-emphasis 44.1 kHz ON
 	 * Low   High  De-emphasis OFF
@@ -136,8 +129,6 @@
 
 gpio_err:
 	pcm3008_gpio_free(setup);
-card_err:
-	snd_soc_free_pcms(socdev);
 pcm_err:
 	kfree(socdev->card->codec);
 
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index c550750..b313033 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -613,17 +613,9 @@
 	snd_soc_add_controls(codec, ssm2602_snd_controls,
 				ARRAY_SIZE(ssm2602_snd_controls));
 	ssm2602_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		pr_err("ssm2602: failed to register card\n");
-		goto card_err;
-	}
 
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	kfree(codec->reg_cache);
 	return ret;
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index befc648..bbc72c2 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -418,9 +418,6 @@
 	snd_soc_add_controls(codec, stac9766_snd_ac97_controls,
 			     ARRAY_SIZE(stac9766_snd_ac97_controls));
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0)
-		goto reset_err;
 	return 0;
 
 reset_err:
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index 0b8dcb5..ee8cb2c 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -707,17 +707,9 @@
 	snd_soc_add_controls(codec, tlv320aic23_snd_controls,
 				ARRAY_SIZE(tlv320aic23_snd_controls));
 	tlv320aic23_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "tlv320aic23: failed to register card\n");
-		goto card_err;
-	}
 
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	kfree(codec->reg_cache);
 	return ret;
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index 3387d9e..357b609 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -356,18 +356,7 @@
 			ARRAY_SIZE(aic26_snd_controls));
 	WARN_ON(err < 0);
 
-	/* CODEC is setup, we can register the card now */
-	dev_dbg(&pdev->dev, "Registering card\n");
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "aic26: failed to register card\n");
-		goto card_err;
-	}
 	return 0;
-
- card_err:
-	snd_soc_free_pcms(socdev);
-	return ret;
 }
 
 static int aic26_remove(struct platform_device *pdev)
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 3395cf9..03cad25 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1405,18 +1405,8 @@
 
 	aic3x_add_widgets(codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "aic3x: failed to register card\n");
-		goto card_err;
-	}
-
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
-
 pcm_err:
 	kfree(codec->reg_cache);
 	return ret;
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
new file mode 100644
index 0000000..bff476d
--- /dev/null
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -0,0 +1,1229 @@
+/*
+ * ALSA SoC Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author:	Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * 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/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/interrupt.h>
+#include <linux/gpio.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/tlv320dac33-plat.h>
+#include "tlv320dac33.h"
+
+#define DAC33_BUFFER_SIZE_BYTES		24576	/* bytes, 12288 16 bit words,
+						 * 6144 stereo */
+#define DAC33_BUFFER_SIZE_SAMPLES	6144
+
+#define NSAMPLE_MAX		5700
+
+#define LATENCY_TIME_MS		20
+
+static struct snd_soc_codec *tlv320dac33_codec;
+
+enum dac33_state {
+	DAC33_IDLE = 0,
+	DAC33_PREFILL,
+	DAC33_PLAYBACK,
+	DAC33_FLUSH,
+};
+
+struct tlv320dac33_priv {
+	struct mutex mutex;
+	struct workqueue_struct *dac33_wq;
+	struct work_struct work;
+	struct snd_soc_codec codec;
+	int power_gpio;
+	int chip_power;
+	int irq;
+	unsigned int refclk;
+
+	unsigned int alarm_threshold;	/* set to be half of LATENCY_TIME_MS */
+	unsigned int nsample_min;	/* nsample should not be lower than
+					 * this */
+	unsigned int nsample_max;	/* nsample should not be higher than
+					 * this */
+	unsigned int nsample_switch;	/* Use FIFO or bypass FIFO switch */
+	unsigned int nsample;		/* burst read amount from host */
+
+	enum dac33_state state;
+};
+
+static const u8 dac33_reg[DAC33_CACHEREGNUM] = {
+0x00, 0x00, 0x00, 0x00, /* 0x00 - 0x03 */
+0x00, 0x00, 0x00, 0x00, /* 0x04 - 0x07 */
+0x00, 0x00, 0x00, 0x00, /* 0x08 - 0x0b */
+0x00, 0x00, 0x00, 0x00, /* 0x0c - 0x0f */
+0x00, 0x00, 0x00, 0x00, /* 0x10 - 0x13 */
+0x00, 0x00, 0x00, 0x00, /* 0x14 - 0x17 */
+0x00, 0x00, 0x00, 0x00, /* 0x18 - 0x1b */
+0x00, 0x00, 0x00, 0x00, /* 0x1c - 0x1f */
+0x00, 0x00, 0x00, 0x00, /* 0x20 - 0x23 */
+0x00, 0x00, 0x00, 0x00, /* 0x24 - 0x27 */
+0x00, 0x00, 0x00, 0x00, /* 0x28 - 0x2b */
+0x00, 0x00, 0x00, 0x80, /* 0x2c - 0x2f */
+0x80, 0x00, 0x00, 0x00, /* 0x30 - 0x33 */
+0x00, 0x00, 0x00, 0x00, /* 0x34 - 0x37 */
+0x00, 0x00,             /* 0x38 - 0x39 */
+/* Registers 0x3a - 0x3f are reserved  */
+            0x00, 0x00, /* 0x3a - 0x3b */
+0x00, 0x00, 0x00, 0x00, /* 0x3c - 0x3f */
+
+0x00, 0x00, 0x00, 0x00, /* 0x40 - 0x43 */
+0x00, 0x80,             /* 0x44 - 0x45 */
+/* Registers 0x46 - 0x47 are reserved  */
+            0x80, 0x80, /* 0x46 - 0x47 */
+
+0x80, 0x00, 0x00,       /* 0x48 - 0x4a */
+/* Registers 0x4b - 0x7c are reserved  */
+                  0x00, /* 0x4b        */
+0x00, 0x00, 0x00, 0x00, /* 0x4c - 0x4f */
+0x00, 0x00, 0x00, 0x00, /* 0x50 - 0x53 */
+0x00, 0x00, 0x00, 0x00, /* 0x54 - 0x57 */
+0x00, 0x00, 0x00, 0x00, /* 0x58 - 0x5b */
+0x00, 0x00, 0x00, 0x00, /* 0x5c - 0x5f */
+0x00, 0x00, 0x00, 0x00, /* 0x60 - 0x63 */
+0x00, 0x00, 0x00, 0x00, /* 0x64 - 0x67 */
+0x00, 0x00, 0x00, 0x00, /* 0x68 - 0x6b */
+0x00, 0x00, 0x00, 0x00, /* 0x6c - 0x6f */
+0x00, 0x00, 0x00, 0x00, /* 0x70 - 0x73 */
+0x00, 0x00, 0x00, 0x00, /* 0x74 - 0x77 */
+0x00, 0x00, 0x00, 0x00, /* 0x78 - 0x7b */
+0x00,                   /* 0x7c        */
+
+      0xda, 0x33, 0x03, /* 0x7d - 0x7f */
+};
+
+/* Register read and write */
+static inline unsigned int dac33_read_reg_cache(struct snd_soc_codec *codec,
+						unsigned reg)
+{
+	u8 *cache = codec->reg_cache;
+	if (reg >= DAC33_CACHEREGNUM)
+		return 0;
+
+	return cache[reg];
+}
+
+static inline void dac33_write_reg_cache(struct snd_soc_codec *codec,
+					 u8 reg, u8 value)
+{
+	u8 *cache = codec->reg_cache;
+	if (reg >= DAC33_CACHEREGNUM)
+		return;
+
+	cache[reg] = value;
+}
+
+static int dac33_read(struct snd_soc_codec *codec, unsigned int reg,
+		      u8 *value)
+{
+	struct tlv320dac33_priv *dac33 = codec->private_data;
+	int val;
+
+	*value = reg & 0xff;
+
+	/* If powered off, return the cached value */
+	if (dac33->chip_power) {
+		val = i2c_smbus_read_byte_data(codec->control_data, value[0]);
+		if (val < 0) {
+			dev_err(codec->dev, "Read failed (%d)\n", val);
+			value[0] = dac33_read_reg_cache(codec, reg);
+		} else {
+			value[0] = val;
+			dac33_write_reg_cache(codec, reg, val);
+		}
+	} else {
+		value[0] = dac33_read_reg_cache(codec, reg);
+	}
+
+	return 0;
+}
+
+static int dac33_write(struct snd_soc_codec *codec, unsigned int reg,
+		       unsigned int value)
+{
+	struct tlv320dac33_priv *dac33 = codec->private_data;
+	u8 data[2];
+	int ret = 0;
+
+	/*
+	 * data is
+	 *   D15..D8 dac33 register offset
+	 *   D7...D0 register data
+	 */
+	data[0] = reg & 0xff;
+	data[1] = value & 0xff;
+
+	dac33_write_reg_cache(codec, data[0], data[1]);
+	if (dac33->chip_power) {
+		ret = codec->hw_write(codec->control_data, data, 2);
+		if (ret != 2)
+			dev_err(codec->dev, "Write failed (%d)\n", ret);
+		else
+			ret = 0;
+	}
+
+	return ret;
+}
+
+static int dac33_write_locked(struct snd_soc_codec *codec, unsigned int reg,
+		       unsigned int value)
+{
+	struct tlv320dac33_priv *dac33 = codec->private_data;
+	int ret;
+
+	mutex_lock(&dac33->mutex);
+	ret = dac33_write(codec, reg, value);
+	mutex_unlock(&dac33->mutex);
+
+	return ret;
+}
+
+#define DAC33_I2C_ADDR_AUTOINC	0x80
+static int dac33_write16(struct snd_soc_codec *codec, unsigned int reg,
+		       unsigned int value)
+{
+	struct tlv320dac33_priv *dac33 = codec->private_data;
+	u8 data[3];
+	int ret = 0;
+
+	/*
+	 * data is
+	 *   D23..D16 dac33 register offset
+	 *   D15..D8  register data MSB
+	 *   D7...D0  register data LSB
+	 */
+	data[0] = reg & 0xff;
+	data[1] = (value >> 8) & 0xff;
+	data[2] = value & 0xff;
+
+	dac33_write_reg_cache(codec, data[0], data[1]);
+	dac33_write_reg_cache(codec, data[0] + 1, data[2]);
+
+	if (dac33->chip_power) {
+		/* We need to set autoincrement mode for 16 bit writes */
+		data[0] |= DAC33_I2C_ADDR_AUTOINC;
+		ret = codec->hw_write(codec->control_data, data, 3);
+		if (ret != 3)
+			dev_err(codec->dev, "Write failed (%d)\n", ret);
+		else
+			ret = 0;
+	}
+
+	return ret;
+}
+
+static void dac33_restore_regs(struct snd_soc_codec *codec)
+{
+	struct tlv320dac33_priv *dac33 = codec->private_data;
+	u8 *cache = codec->reg_cache;
+	u8 data[2];
+	int i, ret;
+
+	if (!dac33->chip_power)
+		return;
+
+	for (i = DAC33_PWR_CTRL; i <= DAC33_INTP_CTRL_B; i++) {
+		data[0] = i;
+		data[1] = cache[i];
+		/* Skip the read only registers */
+		if ((i >= DAC33_INT_OSC_STATUS &&
+				i <= DAC33_INT_OSC_FREQ_RAT_READ_B) ||
+		    (i >= DAC33_FIFO_WPTR_MSB && i <= DAC33_FIFO_IRQ_FLAG) ||
+		    i == DAC33_DAC_STATUS_FLAGS ||
+		    i == DAC33_SRC_EST_REF_CLK_RATIO_A ||
+		    i == DAC33_SRC_EST_REF_CLK_RATIO_B)
+			continue;
+		ret = codec->hw_write(codec->control_data, data, 2);
+		if (ret != 2)
+			dev_err(codec->dev, "Write failed (%d)\n", ret);
+	}
+	for (i = DAC33_LDAC_PWR_CTRL; i <= DAC33_LINEL_TO_LLO_VOL; i++) {
+		data[0] = i;
+		data[1] = cache[i];
+		ret = codec->hw_write(codec->control_data, data, 2);
+		if (ret != 2)
+			dev_err(codec->dev, "Write failed (%d)\n", ret);
+	}
+	for (i = DAC33_LINER_TO_RLO_VOL; i <= DAC33_OSC_TRIM; i++) {
+		data[0] = i;
+		data[1] = cache[i];
+		ret = codec->hw_write(codec->control_data, data, 2);
+		if (ret != 2)
+			dev_err(codec->dev, "Write failed (%d)\n", ret);
+	}
+}
+
+static inline void dac33_soft_power(struct snd_soc_codec *codec, int power)
+{
+	u8 reg;
+
+	reg = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+	if (power)
+		reg |= DAC33_PDNALLB;
+	else
+		reg &= ~DAC33_PDNALLB;
+	dac33_write(codec, DAC33_PWR_CTRL, reg);
+}
+
+static void dac33_hard_power(struct snd_soc_codec *codec, int power)
+{
+	struct tlv320dac33_priv *dac33 = codec->private_data;
+
+	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);
+		}
+		dac33_soft_power(codec, 1);
+	} else {
+		dac33_soft_power(codec, 0);
+		if (dac33->power_gpio >= 0) {
+			gpio_set_value(dac33->power_gpio, 0);
+			dac33->chip_power = 0;
+		}
+	}
+	mutex_unlock(&dac33->mutex);
+
+}
+
+static int dac33_get_nsample(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;
+
+	return 0;
+}
+
+static int dac33_set_nsample(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 == ucontrol->value.integer.value[0])
+		return 0;
+
+	if (ucontrol->value.integer.value[0] < dac33->nsample_min ||
+	    ucontrol->value.integer.value[0] > dac33->nsample_max)
+		ret = -EINVAL;
+	else
+		dac33->nsample = ucontrol->value.integer.value[0];
+
+	return ret;
+}
+
+static int dac33_get_nsample_switch(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;
+
+	return 0;
+}
+
+static int dac33_set_nsample_switch(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])
+		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)
+		ret = -EINVAL;
+	else
+		dac33->nsample_switch = ucontrol->value.integer.value[0];
+
+	return ret;
+}
+
+/*
+ * DACL/R digital volume control:
+ * from 0 dB to -63.5 in 0.5 dB steps
+ * Need to be inverted later on:
+ * 0x00 == 0 dB
+ * 0x7f == -63.5 dB
+ */
+static DECLARE_TLV_DB_SCALE(dac_digivol_tlv, -6350, 50, 0);
+
+static const struct snd_kcontrol_new dac33_snd_controls[] = {
+	SOC_DOUBLE_R_TLV("DAC Digital Playback Volume",
+		DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL,
+		0, 0x7f, 1, dac_digivol_tlv),
+	SOC_DOUBLE_R("DAC Digital Playback Switch",
+		 DAC33_LDAC_DIG_VOL_CTRL, DAC33_RDAC_DIG_VOL_CTRL, 7, 1, 1),
+	SOC_DOUBLE_R("Line to Line Out Volume",
+		 DAC33_LINEL_TO_LLO_VOL, DAC33_LINER_TO_RLO_VOL, 0, 127, 1),
+};
+
+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),
+};
+
+/* Analog bypass */
+static const struct snd_kcontrol_new dac33_dapm_abypassl_control =
+	SOC_DAPM_SINGLE("Switch", DAC33_LINEL_TO_LLO_VOL, 7, 1, 1);
+
+static const struct snd_kcontrol_new dac33_dapm_abypassr_control =
+	SOC_DAPM_SINGLE("Switch", DAC33_LINER_TO_RLO_VOL, 7, 1, 1);
+
+static const struct snd_soc_dapm_widget dac33_dapm_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("LEFT_LO"),
+	SND_SOC_DAPM_OUTPUT("RIGHT_LO"),
+
+	SND_SOC_DAPM_INPUT("LINEL"),
+	SND_SOC_DAPM_INPUT("LINER"),
+
+	SND_SOC_DAPM_DAC("DACL", "Left Playback", DAC33_LDAC_PWR_CTRL, 2, 0),
+	SND_SOC_DAPM_DAC("DACR", "Right Playback", DAC33_RDAC_PWR_CTRL, 2, 0),
+
+	/* Analog bypass */
+	SND_SOC_DAPM_SWITCH("Analog Left Bypass", SND_SOC_NOPM, 0, 0,
+				&dac33_dapm_abypassl_control),
+	SND_SOC_DAPM_SWITCH("Analog Right Bypass", SND_SOC_NOPM, 0, 0,
+				&dac33_dapm_abypassr_control),
+
+	SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Left Amp Power",
+			 DAC33_OUT_AMP_PWR_CTRL, 6, 3, 3, 0),
+	SND_SOC_DAPM_REG(snd_soc_dapm_mixer, "Output Right Amp Power",
+			 DAC33_OUT_AMP_PWR_CTRL, 4, 3, 3, 0),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Analog bypass */
+	{"Analog Left Bypass", "Switch", "LINEL"},
+	{"Analog Right Bypass", "Switch", "LINER"},
+
+	{"Output Left Amp Power", NULL, "DACL"},
+	{"Output Right Amp Power", NULL, "DACR"},
+
+	{"Output Left Amp Power", NULL, "Analog Left Bypass"},
+	{"Output Right Amp Power", NULL, "Analog Right Bypass"},
+
+	/* output */
+	{"LEFT_LO", NULL, "Output Left Amp Power"},
+	{"RIGHT_LO", NULL, "Output Right Amp Power"},
+};
+
+static int dac33_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, dac33_dapm_widgets,
+				  ARRAY_SIZE(dac33_dapm_widgets));
+
+	/* set up audio path interconnects */
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+	snd_soc_dapm_new_widgets(codec);
+
+	return 0;
+}
+
+static int dac33_set_bias_level(struct snd_soc_codec *codec,
+				enum snd_soc_bias_level level)
+{
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		dac33_soft_power(codec, 1);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF)
+			dac33_hard_power(codec, 1);
+		dac33_soft_power(codec, 0);
+		break;
+	case SND_SOC_BIAS_OFF:
+		dac33_hard_power(codec, 0);
+		break;
+	}
+	codec->bias_level = level;
+
+	return 0;
+}
+
+static void dac33_work(struct work_struct *work)
+{
+	struct snd_soc_codec *codec;
+	struct tlv320dac33_priv *dac33;
+	u8 reg;
+
+	dac33 = container_of(work, struct tlv320dac33_priv, work);
+	codec = &dac33->codec;
+
+	mutex_lock(&dac33->mutex);
+	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));
+		break;
+	case DAC33_PLAYBACK:
+		dac33_write16(codec, DAC33_NSAMPLE_MSB,
+				DAC33_THRREG(dac33->nsample));
+		break;
+	case DAC33_IDLE:
+		break;
+	case DAC33_FLUSH:
+		dac33->state = DAC33_IDLE;
+		/* Mask all interrupts from dac33 */
+		dac33_write(codec, DAC33_FIFO_IRQ_MASK, 0);
+
+		/* flush fifo */
+		reg = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+		reg |= DAC33_FIFOFLUSH;
+		dac33_write(codec, DAC33_FIFO_CTRL_A, reg);
+		break;
+	}
+	mutex_unlock(&dac33->mutex);
+}
+
+static irqreturn_t dac33_interrupt_handler(int irq, void *dev)
+{
+	struct snd_soc_codec *codec = dev;
+	struct tlv320dac33_priv *dac33 = codec->private_data;
+
+	queue_work(dac33->dac33_wq, &dac33->work);
+
+	return IRQ_HANDLED;
+}
+
+static void dac33_shutdown(struct snd_pcm_substream *substream,
+			     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 tlv320dac33_priv *dac33 = codec->private_data;
+	unsigned int pwr_ctrl;
+
+	/* Stop pending workqueue */
+	if (dac33->nsample_switch)
+		cancel_work_sync(&dac33->work);
+
+	mutex_lock(&dac33->mutex);
+	pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+	pwr_ctrl &= ~(DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB);
+	dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl);
+	mutex_unlock(&dac33->mutex);
+}
+
+static void dac33_oscwait(struct snd_soc_codec *codec)
+{
+	int timeout = 20;
+	u8 reg;
+
+	do {
+		msleep(1);
+		dac33_read(codec, DAC33_INT_OSC_STATUS, &reg);
+	} while (((reg & 0x03) != DAC33_OSCSTATUS_NORMAL) && timeout--);
+	if ((reg & 0x03) != DAC33_OSCSTATUS_NORMAL)
+		dev_err(codec->dev,
+			"internal oscillator calibration failed\n");
+}
+
+static int dac33_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;
+
+	/* Check parameters for validity */
+	switch (params_rate(params)) {
+	case 44100:
+	case 48000:
+		break;
+	default:
+		dev_err(codec->dev, "unsupported rate %d\n",
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	default:
+		dev_err(codec->dev, "unsupported format %d\n",
+			params_format(params));
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#define CALC_OSCSET(rate, refclk) ( \
+	((((rate * 10000) / refclk) * 4096) + 5000) / 10000)
+#define CALC_RATIOSET(rate, refclk) ( \
+	((((refclk  * 100000) / rate) * 16384) + 50000) / 100000)
+
+/*
+ * tlv320dac33 is strict on the sequence of the register writes, if the register
+ * writes happens in different order, than dac33 might end up in unknown state.
+ * Use the known, working sequence of register writes to initialize the dac33.
+ */
+static int dac33_prepare_chip(struct snd_pcm_substream *substream)
+{
+	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 tlv320dac33_priv *dac33 = codec->private_data;
+	unsigned int oscset, ratioset, pwr_ctrl, reg_tmp;
+	u8 aictrl_a, fifoctrl_a;
+
+	switch (substream->runtime->rate) {
+	case 44100:
+	case 48000:
+		oscset = CALC_OSCSET(substream->runtime->rate, dac33->refclk);
+		ratioset = CALC_RATIOSET(substream->runtime->rate,
+					 dac33->refclk);
+		break;
+	default:
+		dev_err(codec->dev, "unsupported rate %d\n",
+			substream->runtime->rate);
+		return -EINVAL;
+	}
+
+
+	aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
+	aictrl_a &= ~(DAC33_NCYCL_MASK | DAC33_WLEN_MASK);
+	fifoctrl_a = dac33_read_reg_cache(codec, DAC33_FIFO_CTRL_A);
+	fifoctrl_a &= ~DAC33_WIDTH;
+	switch (substream->runtime->format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		aictrl_a |= (DAC33_NCYCL_16 | DAC33_WLEN_16);
+		fifoctrl_a |= DAC33_WIDTH;
+		break;
+	default:
+		dev_err(codec->dev, "unsupported format %d\n",
+			substream->runtime->format);
+		return -EINVAL;
+	}
+
+	mutex_lock(&dac33->mutex);
+	dac33_soft_power(codec, 1);
+
+	reg_tmp = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
+	dac33_write(codec, DAC33_INT_OSC_CTRL, reg_tmp);
+
+	/* Write registers 0x08 and 0x09 (MSB, LSB) */
+	dac33_write16(codec, DAC33_INT_OSC_FREQ_RAT_A, oscset);
+
+	/* calib time: 128 is a nice number ;) */
+	dac33_write(codec, DAC33_CALIB_TIME, 128);
+
+	/* adjustment treshold & step */
+	dac33_write(codec, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) |
+						 DAC33_ADJSTEP(1));
+
+	/* div=4 / gain=1 / div */
+	dac33_write(codec, DAC33_INT_OSC_CTRL_C, DAC33_REFDIV(4));
+
+	pwr_ctrl = dac33_read_reg_cache(codec, DAC33_PWR_CTRL);
+	pwr_ctrl |= DAC33_OSCPDNB | DAC33_DACRPDNB | DAC33_DACLPDNB;
+	dac33_write(codec, DAC33_PWR_CTRL, pwr_ctrl);
+
+	dac33_oscwait(codec);
+
+	if (dac33->nsample_switch) {
+		/* 50-51 : ASRC Control registers */
+		dac33_write(codec, DAC33_ASRC_CTRL_A, (1 << 4)); /* div=2 */
+		dac33_write(codec, DAC33_ASRC_CTRL_B, 1); /* ??? */
+
+		/* Write registers 0x34 and 0x35 (MSB, LSB) */
+		dac33_write16(codec, DAC33_SRC_REF_CLK_RATIO_A, ratioset);
+
+		/* 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 {
+		/* 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)
+		fifoctrl_a &= ~DAC33_FBYPAS;
+	else
+		fifoctrl_a |= DAC33_FBYPAS;
+	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;
+	else
+		reg_tmp |= DAC33_BCLKON;
+	dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_B, reg_tmp);
+
+	if (dac33->nsample_switch) {
+		/* 20: BCLK divide ratio */
+		dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 3);
+
+		dac33_write16(codec, DAC33_ATHR_MSB,
+			      DAC33_THRREG(dac33->alarm_threshold));
+	} else {
+		dac33_write(codec, DAC33_SER_AUDIOIF_CTRL_C, 32);
+	}
+
+	mutex_unlock(&dac33->mutex);
+
+	return 0;
+}
+
+static void dac33_calculate_times(struct snd_pcm_substream *substream)
+{
+	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 tlv320dac33_priv *dac33 = codec->private_data;
+	unsigned int nsample_limit;
+
+	/* Number of samples (16bit, stereo) in one period */
+	dac33->nsample_min = snd_pcm_lib_period_bytes(substream) / 4;
+
+	/* Number of samples (16bit, stereo) in ALSA buffer */
+	dac33->nsample_max = snd_pcm_lib_buffer_bytes(substream) / 4;
+	/* Subtract one period from the total */
+	dac33->nsample_max -= dac33->nsample_min;
+
+	/* Number of samples for LATENCY_TIME_MS / 2 */
+	dac33->alarm_threshold = substream->runtime->rate /
+				 (1000 / (LATENCY_TIME_MS / 2));
+
+	/* Find and fix up the lowest nsmaple limit */
+	nsample_limit = substream->runtime->rate / (1000 / LATENCY_TIME_MS);
+
+	if (dac33->nsample_min < nsample_limit)
+		dac33->nsample_min = nsample_limit;
+
+	if (dac33->nsample < dac33->nsample_min)
+		dac33->nsample = dac33->nsample_min;
+
+	/*
+	 * Find and fix up the highest nsmaple limit
+	 * In order to not overflow the DAC33 buffer substract the
+	 * alarm_threshold value from the size of the DAC33 buffer
+	 */
+	nsample_limit = DAC33_BUFFER_SIZE_SAMPLES - dac33->alarm_threshold;
+
+	if (dac33->nsample_max > nsample_limit)
+		dac33->nsample_max = nsample_limit;
+
+	if (dac33->nsample > dac33->nsample_max)
+		dac33->nsample = dac33->nsample_max;
+}
+
+static int dac33_pcm_prepare(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	dac33_calculate_times(substream);
+	dac33_prepare_chip(substream);
+
+	return 0;
+}
+
+static int dac33_pcm_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_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct tlv320dac33_priv *dac33 = codec->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (dac33->nsample_switch) {
+			dac33->state = DAC33_PREFILL;
+			queue_work(dac33->dac33_wq, &dac33->work);
+		}
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (dac33->nsample_switch) {
+			dac33->state = DAC33_FLUSH;
+			queue_work(dac33->dac33_wq, &dac33->work);
+		}
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int dac33_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 tlv320dac33_priv *dac33 = codec->private_data;
+	u8 ioc_reg, asrcb_reg;
+
+	ioc_reg = dac33_read_reg_cache(codec, DAC33_INT_OSC_CTRL);
+	asrcb_reg = dac33_read_reg_cache(codec, DAC33_ASRC_CTRL_B);
+	switch (clk_id) {
+	case TLV320DAC33_MCLK:
+		ioc_reg |= DAC33_REFSEL;
+		asrcb_reg |= DAC33_SRCREFSEL;
+		break;
+	case TLV320DAC33_SLEEPCLK:
+		ioc_reg &= ~DAC33_REFSEL;
+		asrcb_reg &= ~DAC33_SRCREFSEL;
+		break;
+	default:
+		dev_err(codec->dev, "Invalid clock ID (%d)\n", clk_id);
+		break;
+	}
+	dac33->refclk = freq;
+
+	dac33_write_reg_cache(codec, DAC33_INT_OSC_CTRL, ioc_reg);
+	dac33_write_reg_cache(codec, DAC33_ASRC_CTRL_B, asrcb_reg);
+
+	return 0;
+}
+
+static int dac33_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			     unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u8 aictrl_a, aictrl_b;
+
+	aictrl_a = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A);
+	aictrl_b = dac33_read_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B);
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		/* Codec Master */
+		aictrl_a |= (DAC33_MSBCLK | DAC33_MSWCLK);
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		/* Codec Slave */
+		aictrl_a &= ~(DAC33_MSBCLK | DAC33_MSWCLK);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	aictrl_a &= ~DAC33_AFMT_MASK;
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		aictrl_a |= DAC33_AFMT_I2S;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		aictrl_a |= DAC33_AFMT_DSP;
+		aictrl_b &= ~DAC33_DATA_DELAY_MASK;
+		aictrl_b |= DAC33_DATA_DELAY(1); /* 1 bit delay */
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		aictrl_a |= DAC33_AFMT_DSP;
+		aictrl_b &= ~DAC33_DATA_DELAY_MASK; /* No delay */
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		aictrl_a |= DAC33_AFMT_RIGHT_J;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		aictrl_a |= DAC33_AFMT_LEFT_J;
+		break;
+	default:
+		dev_err(codec->dev, "Unsupported format (%u)\n",
+			fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+		return -EINVAL;
+	}
+
+	dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_A, aictrl_a);
+	dac33_write_reg_cache(codec, DAC33_SER_AUDIOIF_CTRL_B, aictrl_b);
+
+	return 0;
+}
+
+static void dac33_init_chip(struct snd_soc_codec *codec)
+{
+	/* 44-46: DAC Control Registers */
+	/* A : DAC sample rate Fsref/1.5 */
+	dac33_write(codec, DAC33_DAC_CTRL_A, DAC33_DACRATE(1));
+	/* B : DAC src=normal, not muted */
+	dac33_write(codec, DAC33_DAC_CTRL_B, DAC33_DACSRCR_RIGHT |
+					     DAC33_DACSRCL_LEFT);
+	/* C : (defaults) */
+	dac33_write(codec, DAC33_DAC_CTRL_C, 0x00);
+
+	/* 64-65 : L&R DAC power control
+	 Line In -> OUT 1V/V Gain, DAC -> OUT 4V/V Gain*/
+	dac33_write(codec, DAC33_LDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
+	dac33_write(codec, DAC33_RDAC_PWR_CTRL, DAC33_LROUT_GAIN(2));
+
+	/* 73 : volume soft stepping control,
+	 clock source = internal osc (?) */
+	dac33_write(codec, DAC33_ANA_VOL_SOFT_STEP_CTRL, DAC33_VOLCLKEN);
+
+	/* 66 : LOP/LOM Modes */
+	dac33_write(codec, DAC33_OUT_AMP_CM_CTRL, 0xff);
+
+	/* 68 : LOM inverted from LOP */
+	dac33_write(codec, DAC33_OUT_AMP_CTRL, (3<<2));
+
+	dac33_write(codec, DAC33_PWR_CTRL, DAC33_PDNALLB);
+}
+
+static int dac33_soc_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	struct tlv320dac33_priv *dac33;
+	int ret = 0;
+
+	BUG_ON(!tlv320dac33_codec);
+
+	codec = tlv320dac33_codec;
+	socdev->card->codec = codec;
+	dac33 = codec->private_data;
+
+	/* Power up the codec */
+	dac33_hard_power(codec, 1);
+	/* Set default configuration */
+	dac33_init_chip(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\n");
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, dac33_snd_controls,
+			     ARRAY_SIZE(dac33_snd_controls));
+	/* Only add the nSample controls, if we have valid IRQ number */
+	if (dac33->irq >= 0)
+		snd_soc_add_controls(codec, dac33_nsample_snd_controls,
+				     ARRAY_SIZE(dac33_nsample_snd_controls));
+
+	dac33_add_widgets(codec);
+
+	/* power on device */
+	dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+
+pcm_err:
+	dac33_hard_power(codec, 0);
+	return ret;
+}
+
+static int dac33_soc_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+static int dac33_soc_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;
+
+	dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int dac33_soc_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	dac33_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	dac33_set_bias_level(codec, codec->suspend_bias_level);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_tlv320dac33 = {
+	.probe = dac33_soc_probe,
+	.remove = dac33_soc_remove,
+	.suspend = dac33_soc_suspend,
+	.resume = dac33_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_tlv320dac33);
+
+#define DAC33_RATES	(SNDRV_PCM_RATE_44100 | \
+			 SNDRV_PCM_RATE_48000)
+#define DAC33_FORMATS	SNDRV_PCM_FMTBIT_S16_LE
+
+static struct snd_soc_dai_ops dac33_dai_ops = {
+	.shutdown	= dac33_shutdown,
+	.hw_params	= dac33_hw_params,
+	.prepare	= dac33_pcm_prepare,
+	.trigger	= dac33_pcm_trigger,
+	.set_sysclk	= dac33_set_dai_sysclk,
+	.set_fmt	= dac33_set_dai_fmt,
+};
+
+struct snd_soc_dai dac33_dai = {
+	.name = "tlv320dac33",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = DAC33_RATES,
+		.formats = DAC33_FORMATS,},
+	.ops = &dac33_dai_ops,
+};
+EXPORT_SYMBOL_GPL(dac33_dai);
+
+static int 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;
+
+	if (client->dev.platform_data == NULL) {
+		dev_err(&client->dev, "Platform data not set\n");
+		return -ENODEV;
+	}
+	pdata = client->dev.platform_data;
+
+	dac33 = kzalloc(sizeof(struct tlv320dac33_priv), GFP_KERNEL);
+	if (dac33 == NULL)
+		return -ENOMEM;
+
+	codec = &dac33->codec;
+	codec->private_data = dac33;
+	codec->control_data = client;
+
+	mutex_init(&codec->mutex);
+	mutex_init(&dac33->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->name = "tlv320dac33";
+	codec->owner = THIS_MODULE;
+	codec->read = dac33_read_reg_cache;
+	codec->write = dac33_write_locked;
+	codec->hw_write = (hw_write_t) i2c_master_send;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = dac33_set_bias_level;
+	codec->dai = &dac33_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = ARRAY_SIZE(dac33_reg);
+	codec->reg_cache = kmemdup(dac33_reg, ARRAY_SIZE(dac33_reg),
+				   GFP_KERNEL);
+	if (codec->reg_cache == NULL) {
+		ret = -ENOMEM;
+		goto error_reg;
+	}
+
+	i2c_set_clientdata(client, dac33);
+
+	dac33->power_gpio = pdata->power_gpio;
+	dac33->irq = client->irq;
+	dac33->nsample = NSAMPLE_MAX;
+	/* Disable FIFO use by default */
+	dac33->nsample_switch = 0;
+
+	tlv320dac33_codec = codec;
+
+	codec->dev = &client->dev;
+	dac33_dai.dev = codec->dev;
+
+	/* Check if the reset GPIO number is valid and request it */
+	if (dac33->power_gpio >= 0) {
+		ret = gpio_request(dac33->power_gpio, "tlv320dac33 reset");
+		if (ret < 0) {
+			dev_err(codec->dev,
+				"Failed to request reset GPIO (%d)\n",
+				dac33->power_gpio);
+			snd_soc_unregister_dai(&dac33_dai);
+			snd_soc_unregister_codec(codec);
+			goto error_gpio;
+		}
+		gpio_direction_output(dac33->power_gpio, 0);
+	} else {
+		dac33->chip_power = 1;
+	}
+
+	/* Check if the IRQ number is valid and request it */
+	if (dac33->irq >= 0) {
+		ret = request_irq(dac33->irq, dac33_interrupt_handler,
+				  IRQF_TRIGGER_RISING | IRQF_DISABLED,
+				  codec->name, codec);
+		if (ret < 0) {
+			dev_err(codec->dev, "Could not request IRQ%d (%d)\n",
+						dac33->irq, ret);
+			dac33->irq = -1;
+		}
+		if (dac33->irq != -1) {
+			/* Setup work queue */
+			dac33->dac33_wq = create_rt_workqueue("tlv320dac33");
+			if (dac33->dac33_wq == NULL) {
+				free_irq(dac33->irq, &dac33->codec);
+				ret = -ENOMEM;
+				goto error_wq;
+			}
+
+			INIT_WORK(&dac33->work, dac33_work);
+		}
+	}
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto error_codec;
+	}
+
+	ret = snd_soc_register_dai(&dac33_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		goto error_codec;
+	}
+
+	/* Shut down the codec for now */
+	dac33_hard_power(codec, 0);
+
+	return ret;
+
+error_codec:
+	if (dac33->irq >= 0) {
+		free_irq(dac33->irq, &dac33->codec);
+		destroy_workqueue(dac33->dac33_wq);
+	}
+error_wq:
+	if (dac33->power_gpio >= 0)
+		gpio_free(dac33->power_gpio);
+error_gpio:
+	kfree(codec->reg_cache);
+error_reg:
+	tlv320dac33_codec = NULL;
+	kfree(dac33);
+
+	return ret;
+}
+
+static int dac33_i2c_remove(struct i2c_client *client)
+{
+	struct tlv320dac33_priv *dac33;
+
+	dac33 = i2c_get_clientdata(client);
+	dac33_hard_power(&dac33->codec, 0);
+
+	if (dac33->power_gpio >= 0)
+		gpio_free(dac33->power_gpio);
+	if (dac33->irq >= 0)
+		free_irq(dac33->irq, &dac33->codec);
+
+	destroy_workqueue(dac33->dac33_wq);
+	snd_soc_unregister_dai(&dac33_dai);
+	snd_soc_unregister_codec(&dac33->codec);
+	kfree(dac33->codec.reg_cache);
+	kfree(dac33);
+	tlv320dac33_codec = NULL;
+
+	return 0;
+}
+
+static const struct i2c_device_id tlv320dac33_i2c_id[] = {
+	{
+		.name = "tlv320dac33",
+		.driver_data = 0,
+	},
+	{ },
+};
+
+static struct i2c_driver tlv320dac33_i2c_driver = {
+	.driver = {
+		.name = "tlv320dac33",
+		.owner = THIS_MODULE,
+	},
+	.probe		= dac33_i2c_probe,
+	.remove		= __devexit_p(dac33_i2c_remove),
+	.id_table	= tlv320dac33_i2c_id,
+};
+
+static int __init dac33_module_init(void)
+{
+	int r;
+	r = i2c_add_driver(&tlv320dac33_i2c_driver);
+	if (r < 0) {
+		printk(KERN_ERR "DAC33: driver registration failed\n");
+		return r;
+	}
+	return 0;
+}
+module_init(dac33_module_init);
+
+static void __exit dac33_module_exit(void)
+{
+	i2c_del_driver(&tlv320dac33_i2c_driver);
+}
+module_exit(dac33_module_exit);
+
+
+MODULE_DESCRIPTION("ASoC TLV320DAC33 codec driver");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@nokia.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320dac33.h b/sound/soc/codecs/tlv320dac33.h
new file mode 100644
index 0000000..eb8ae07
--- /dev/null
+++ b/sound/soc/codecs/tlv320dac33.h
@@ -0,0 +1,267 @@
+/*
+ * ALSA SoC Texas Instruments TLV320DAC33 codec driver
+ *
+ * Author:	Peter Ujfalusi <peter.ujfalusi@nokia.com>
+ *
+ * Copyright:   (C) 2009 Nokia Corporation
+ *
+ * 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 __TLV320DAC33_H
+#define __TLV320DAC33_H
+
+#define DAC33_PAGE_SELECT		0x00
+#define DAC33_PWR_CTRL			0x01
+#define DAC33_PLL_CTRL_A		0x02
+#define DAC33_PLL_CTRL_B		0x03
+#define DAC33_PLL_CTRL_C		0x04
+#define DAC33_PLL_CTRL_D		0x05
+#define DAC33_PLL_CTRL_E		0x06
+#define DAC33_INT_OSC_CTRL		0x07
+#define DAC33_INT_OSC_FREQ_RAT_A	0x08
+#define DAC33_INT_OSC_FREQ_RAT_B	0x09
+#define DAC33_INT_OSC_DAC_RATIO_SET	0x0A
+#define DAC33_CALIB_TIME		0x0B
+#define DAC33_INT_OSC_CTRL_B		0x0C
+#define DAC33_INT_OSC_CTRL_C		0x0D
+#define DAC33_INT_OSC_STATUS		0x0E
+#define DAC33_INT_OSC_DAC_RATIO_READ	0x0F
+#define DAC33_INT_OSC_FREQ_RAT_READ_A	0x10
+#define DAC33_INT_OSC_FREQ_RAT_READ_B	0x11
+#define DAC33_SER_AUDIOIF_CTRL_A	0x12
+#define DAC33_SER_AUDIOIF_CTRL_B	0x13
+#define DAC33_SER_AUDIOIF_CTRL_C	0x14
+#define DAC33_FIFO_CTRL_A		0x15
+#define DAC33_UTHR_MSB			0x16
+#define DAC33_UTHR_LSB			0x17
+#define DAC33_ATHR_MSB			0x18
+#define DAC33_ATHR_LSB			0x19
+#define DAC33_LTHR_MSB			0x1A
+#define DAC33_LTHR_LSB			0x1B
+#define DAC33_PREFILL_MSB		0x1C
+#define DAC33_PREFILL_LSB		0x1D
+#define DAC33_NSAMPLE_MSB		0x1E
+#define DAC33_NSAMPLE_LSB		0x1F
+#define DAC33_FIFO_WPTR_MSB		0x20
+#define DAC33_FIFO_WPTR_LSB		0x21
+#define DAC33_FIFO_RPTR_MSB		0x22
+#define DAC33_FIFO_RPTR_LSB		0x23
+#define DAC33_FIFO_DEPTH_MSB		0x24
+#define DAC33_FIFO_DEPTH_LSB		0x25
+#define DAC33_SAMPLES_REMAINING_MSB	0x26
+#define DAC33_SAMPLES_REMAINING_LSB	0x27
+#define DAC33_FIFO_IRQ_FLAG		0x28
+#define DAC33_FIFO_IRQ_MASK		0x29
+#define DAC33_FIFO_IRQ_MODE_A		0x2A
+#define DAC33_FIFO_IRQ_MODE_B		0x2B
+#define DAC33_DAC_CTRL_A		0x2C
+#define DAC33_DAC_CTRL_B		0x2D
+#define DAC33_DAC_CTRL_C		0x2E
+#define DAC33_LDAC_DIG_VOL_CTRL		0x2F
+#define DAC33_RDAC_DIG_VOL_CTRL		0x30
+#define DAC33_DAC_STATUS_FLAGS		0x31
+#define DAC33_ASRC_CTRL_A		0x32
+#define DAC33_ASRC_CTRL_B		0x33
+#define DAC33_SRC_REF_CLK_RATIO_A	0x34
+#define DAC33_SRC_REF_CLK_RATIO_B	0x35
+#define DAC33_SRC_EST_REF_CLK_RATIO_A	0x36
+#define DAC33_SRC_EST_REF_CLK_RATIO_B	0x37
+#define DAC33_INTP_CTRL_A		0x38
+#define DAC33_INTP_CTRL_B		0x39
+/* Registers 0x3A - 0x3F Reserved */
+#define DAC33_LDAC_PWR_CTRL		0x40
+#define DAC33_RDAC_PWR_CTRL		0x41
+#define DAC33_OUT_AMP_CM_CTRL		0x42
+#define DAC33_OUT_AMP_PWR_CTRL		0x43
+#define DAC33_OUT_AMP_CTRL		0x44
+#define DAC33_LINEL_TO_LLO_VOL		0x45
+/* Registers 0x45 - 0x47 Reserved */
+#define DAC33_LINER_TO_RLO_VOL		0x48
+#define DAC33_ANA_VOL_SOFT_STEP_CTRL	0x49
+#define DAC33_OSC_TRIM			0x4A
+/* Registers 0x4B - 0x7C Reserved */
+#define DAC33_DEVICE_ID_MSB		0x7D
+#define DAC33_DEVICE_ID_LSB		0x7E
+#define DAC33_DEVICE_REV_ID		0x7F
+
+#define DAC33_CACHEREGNUM               128
+
+/* Bit definitions */
+
+/* DAC33_PWR_CTRL (0x01) */
+#define DAC33_DACRPDNB			(0x01 << 0)
+#define DAC33_DACLPDNB			(0x01 << 1)
+#define DAC33_OSCPDNB			(0x01 << 2)
+#define DAC33_PLLPDNB			(0x01 << 3)
+#define DAC33_PDNALLB			(0x01 << 4)
+#define DAC33_SOFT_RESET		(0x01 << 7)
+
+/* DAC33_INT_OSC_CTRL (0x07) */
+#define DAC33_REFSEL			(0x01 << 1)
+
+/* DAC33_INT_OSC_CTRL_B (0x0C) */
+#define DAC33_ADJSTEP(x)		(x << 0)
+#define DAC33_ADJTHRSHLD(x)		(x << 4)
+
+/* DAC33_INT_OSC_CTRL_C (0x0D) */
+#define DAC33_REFDIV(x)			(x << 4)
+
+/* DAC33_INT_OSC_STATUS (0x0E) */
+#define DAC33_OSCSTATUS_IDLE_CALIB	(0x00)
+#define DAC33_OSCSTATUS_NORMAL		(0x01)
+#define DAC33_OSCSTATUS_ADJUSTMENT	(0x03)
+#define DAC33_OSCSTATUS_NOT_USED	(0x02)
+
+/* DAC33_SER_AUDIOIF_CTRL_A (0x12) */
+#define DAC33_MSWCLK			(0x01 << 0)
+#define DAC33_MSBCLK			(0x01 << 1)
+#define DAC33_AFMT_MASK			(0x03 << 2)
+#define DAC33_AFMT_I2S			(0x00 << 2)
+#define DAC33_AFMT_DSP			(0x01 << 2)
+#define DAC33_AFMT_RIGHT_J		(0x02 << 2)
+#define DAC33_AFMT_LEFT_J		(0x03 << 2)
+#define DAC33_WLEN_MASK			(0x03 << 4)
+#define DAC33_WLEN_16			(0x00 << 4)
+#define DAC33_WLEN_20			(0x01 << 4)
+#define DAC33_WLEN_24			(0x02 << 4)
+#define DAC33_WLEN_32			(0x03 << 4)
+#define DAC33_NCYCL_MASK		(0x03 << 6)
+#define DAC33_NCYCL_16			(0x00 << 6)
+#define DAC33_NCYCL_20			(0x01 << 6)
+#define DAC33_NCYCL_24			(0x02 << 6)
+#define DAC33_NCYCL_32			(0x03 << 6)
+
+/* DAC33_SER_AUDIOIF_CTRL_B (0x13) */
+#define DAC33_DATA_DELAY_MASK		(0x03 << 2)
+#define DAC33_DATA_DELAY(x)		(x << 2)
+#define DAC33_BCLKON			(0x01 << 5)
+
+/* DAC33_FIFO_CTRL_A (0x15) */
+#define DAC33_WIDTH				(0x01 << 0)
+#define DAC33_FBYPAS				(0x01 << 1)
+#define DAC33_FAUTO				(0x01 << 2)
+#define DAC33_FIFOFLUSH			(0x01 << 3)
+
+/*
+ * UTHR, ATHR, LTHR, PREFILL, NSAMPLE (0x16 - 0x1F)
+ * 13-bit values
+*/
+#define DAC33_THRREG(x)			(((x) & 0x1FFF) << 3)
+
+/* DAC33_FIFO_IRQ_MASK (0x29) */
+#define DAC33_MNS			(0x01 << 0)
+#define DAC33_MPS			(0x01 << 1)
+#define DAC33_MAT			(0x01 << 2)
+#define DAC33_MLT			(0x01 << 3)
+#define DAC33_MUT			(0x01 << 4)
+#define DAC33_MUF			(0x01 << 5)
+#define DAC33_MOF			(0x01 << 6)
+
+#define DAC33_FIFO_IRQ_MODE_MASK	(0x03)
+#define DAC33_FIFO_IRQ_MODE_RISING	(0x00)
+#define DAC33_FIFO_IRQ_MODE_FALLING	(0x01)
+#define DAC33_FIFO_IRQ_MODE_LEVEL	(0x02)
+#define DAC33_FIFO_IRQ_MODE_EDGE	(0x03)
+
+/* DAC33_FIFO_IRQ_MODE_A (0x2A) */
+#define DAC33_UTM(x)			(x << 0)
+#define DAC33_UFM(x)			(x << 2)
+#define DAC33_OFM(x)			(x << 4)
+
+/* DAC33_FIFO_IRQ_MODE_B (0x2B) */
+#define DAC33_NSM(x)			(x << 0)
+#define DAC33_PSM(x)			(x << 2)
+#define DAC33_ATM(x)			(x << 4)
+#define DAC33_LTM(x)			(x << 6)
+
+/* DAC33_DAC_CTRL_A (0x2C) */
+#define DAC33_DACRATE(x)		(x << 0)
+#define DAC33_DACDUAL			(0x01 << 4)
+#define DAC33_DACLKSEL_MASK		(0x03 << 5)
+#define DAC33_DACLKSEL_INTSOC		(0x00 << 5)
+#define DAC33_DACLKSEL_PLL		(0x01 << 5)
+#define DAC33_DACLKSEL_MCLK		(0x02 << 5)
+#define DAC33_DACLKSEL_BCLK		(0x03 << 5)
+
+/* DAC33_DAC_CTRL_B (0x2D) */
+#define DAC33_DACSRCR_MASK		(0x03 << 0)
+#define DAC33_DACSRCR_MUTE		(0x00 << 0)
+#define DAC33_DACSRCR_RIGHT		(0x01 << 0)
+#define DAC33_DACSRCR_LEFT		(0x02 << 0)
+#define DAC33_DACSRCR_MONOMIX		(0x03 << 0)
+#define DAC33_DACSRCL_MASK		(0x03 << 2)
+#define DAC33_DACSRCL_MUTE		(0x00 << 2)
+#define DAC33_DACSRCL_LEFT		(0x01 << 2)
+#define DAC33_DACSRCL_RIGHT		(0x02 << 2)
+#define DAC33_DACSRCL_MONOMIX		(0x03 << 2)
+#define DAC33_DVOLSTEP_MASK		(0x03 << 4)
+#define DAC33_DVOLSTEP_SS_PERFS		(0x00 << 4)
+#define DAC33_DVOLSTEP_SS_PER2FS	(0x01 << 4)
+#define DAC33_DVOLSTEP_SS_DISABLED	(0x02 << 4)
+#define DAC33_DVOLCTRL_MASK		(0x03 << 6)
+#define DAC33_DVOLCTRL_LR_INDEPENDENT1	(0x00 << 6)
+#define DAC33_DVOLCTRL_LR_RIGHT_CONTROL	(0x01 << 6)
+#define DAC33_DVOLCTRL_LR_LEFT_CONTROL	(0x02 << 6)
+#define DAC33_DVOLCTRL_LR_INDEPENDENT2	(0x03 << 6)
+
+/* DAC33_DAC_CTRL_C (0x2E) */
+#define DAC33_DEEMENR			(0x01 << 0)
+#define DAC33_EFFENR			(0x01 << 1)
+#define DAC33_DEEMENL			(0x01 << 2)
+#define DAC33_EFFENL			(0x01 << 3)
+#define DAC33_EN3D			(0x01 << 4)
+#define DAC33_RESYNMUTE			(0x01 << 5)
+#define DAC33_RESYNEN			(0x01 << 6)
+
+/* DAC33_ASRC_CTRL_A (0x32) */
+#define DAC33_SRCBYP			(0x01 << 0)
+#define DAC33_SRCLKSEL_MASK		(0x03 << 1)
+#define DAC33_SRCLKSEL_INTSOC		(0x00 << 1)
+#define DAC33_SRCLKSEL_PLL		(0x01 << 1)
+#define DAC33_SRCLKSEL_MCLK		(0x02 << 1)
+#define DAC33_SRCLKSEL_BCLK		(0x03 << 1)
+#define DAC33_SRCLKDIV(x)		(x << 3)
+
+/* DAC33_ASRC_CTRL_B (0x33) */
+#define DAC33_SRCSETUP(x)		(x << 0)
+#define DAC33_SRCREFSEL			(0x01 << 4)
+#define DAC33_SRCREFDIV(x)		(x << 5)
+
+/* DAC33_INTP_CTRL_A (0x38) */
+#define DAC33_INTPSEL			(0x01 << 0)
+#define DAC33_INTPM_MASK		(0x03 << 1)
+#define DAC33_INTPM_ALOW_OPENDRAIN	(0x00 << 1)
+#define DAC33_INTPM_ALOW		(0x01 << 1)
+#define DAC33_INTPM_AHIGH		(0x02 << 1)
+
+/* DAC33_LDAC_PWR_CTRL (0x40) */
+/* DAC33_RDAC_PWR_CTRL (0x41) */
+#define DAC33_DACLRNUM			(0x01 << 2)
+#define DAC33_LROUT_GAIN(x)		(x << 0)
+
+/* DAC33_ANA_VOL_SOFT_STEP_CTRL (0x49) */
+#define DAC33_VOLCLKSEL			(0x01 << 0)
+#define DAC33_VOLCLKEN			(0x01 << 1)
+#define DAC33_VOLBYPASS			(0x01 << 2)
+
+#define TLV320DAC33_MCLK		0
+#define TLV320DAC33_SLEEPCLK		1
+
+extern struct snd_soc_dai dac33_dai;
+extern struct snd_soc_codec_device soc_codec_dev_tlv320dac33;
+
+#endif /* __TLV320DAC33_H */
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
new file mode 100644
index 0000000..6b650c1
--- /dev/null
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -0,0 +1,463 @@
+/*
+ * ALSA SoC Texas Instruments TPA6130A2 headset stereo amplifier driver
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.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/module.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <sound/tpa6130a2-plat.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "tpa6130a2.h"
+
+static struct i2c_client *tpa6130a2_client;
+
+/* This struct is used to save the context */
+struct tpa6130a2_data {
+	struct mutex mutex;
+	unsigned char regs[TPA6130A2_CACHEREGNUM];
+	int power_gpio;
+	unsigned char power_state;
+};
+
+static int tpa6130a2_i2c_read(int reg)
+{
+	struct tpa6130a2_data *data;
+	int val;
+
+	BUG_ON(tpa6130a2_client == NULL);
+	data = i2c_get_clientdata(tpa6130a2_client);
+
+	/* If powered off, return the cached value */
+	if (data->power_state) {
+		val = i2c_smbus_read_byte_data(tpa6130a2_client, reg);
+		if (val < 0)
+			dev_err(&tpa6130a2_client->dev, "Read failed\n");
+		else
+			data->regs[reg] = val;
+	} else {
+		val = data->regs[reg];
+	}
+
+	return val;
+}
+
+static int tpa6130a2_i2c_write(int reg, u8 value)
+{
+	struct tpa6130a2_data *data;
+	int val = 0;
+
+	BUG_ON(tpa6130a2_client == NULL);
+	data = i2c_get_clientdata(tpa6130a2_client);
+
+	if (data->power_state) {
+		val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value);
+		if (val < 0)
+			dev_err(&tpa6130a2_client->dev, "Write failed\n");
+	}
+
+	/* Either powered on or off, we save the context */
+	data->regs[reg] = value;
+
+	return val;
+}
+
+static u8 tpa6130a2_read(int reg)
+{
+	struct tpa6130a2_data *data;
+
+	BUG_ON(tpa6130a2_client == NULL);
+	data = i2c_get_clientdata(tpa6130a2_client);
+
+	return data->regs[reg];
+}
+
+static void tpa6130a2_initialize(void)
+{
+	struct tpa6130a2_data *data;
+	int i;
+
+	BUG_ON(tpa6130a2_client == NULL);
+	data = i2c_get_clientdata(tpa6130a2_client);
+
+	for (i = 1; i < TPA6130A2_REG_VERSION; i++)
+		tpa6130a2_i2c_write(i, data->regs[i]);
+}
+
+static void tpa6130a2_power(int power)
+{
+	struct	tpa6130a2_data *data;
+	u8	val;
+
+	BUG_ON(tpa6130a2_client == NULL);
+	data = i2c_get_clientdata(tpa6130a2_client);
+
+	mutex_lock(&data->mutex);
+	if (power) {
+		/* Power on */
+		if (data->power_gpio >= 0) {
+			gpio_set_value(data->power_gpio, 1);
+			data->power_state = 1;
+			tpa6130a2_initialize();
+		}
+		/* Clear SWS */
+		val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+		val &= ~TPA6130A2_SWS;
+		tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+	} else {
+		/* set SWS */
+		val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+		val |= TPA6130A2_SWS;
+		tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+		/* Power off */
+		if (data->power_gpio >= 0) {
+			gpio_set_value(data->power_gpio, 0);
+			data->power_state = 0;
+		}
+	}
+	mutex_unlock(&data->mutex);
+}
+
+static int tpa6130a2_get_reg(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct tpa6130a2_data *data;
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int mask = mc->max;
+	unsigned int invert = mc->invert;
+
+	BUG_ON(tpa6130a2_client == NULL);
+	data = i2c_get_clientdata(tpa6130a2_client);
+
+	mutex_lock(&data->mutex);
+
+	ucontrol->value.integer.value[0] =
+		(tpa6130a2_read(reg) >> shift) & mask;
+
+	if (invert)
+		ucontrol->value.integer.value[0] =
+			mask - ucontrol->value.integer.value[0];
+
+	mutex_unlock(&data->mutex);
+	return 0;
+}
+
+static int tpa6130a2_set_reg(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct tpa6130a2_data *data;
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int mask = mc->max;
+	unsigned int invert = mc->invert;
+	unsigned int val = (ucontrol->value.integer.value[0] & mask);
+	unsigned int val_reg;
+
+	BUG_ON(tpa6130a2_client == NULL);
+	data = i2c_get_clientdata(tpa6130a2_client);
+
+	if (invert)
+		val = mask - val;
+
+	mutex_lock(&data->mutex);
+
+	val_reg = tpa6130a2_read(reg);
+	if (((val_reg >> shift) & mask) == val) {
+		mutex_unlock(&data->mutex);
+		return 0;
+	}
+
+	val_reg &= ~(mask << shift);
+	val_reg |= val << shift;
+	tpa6130a2_i2c_write(reg, val_reg);
+
+	mutex_unlock(&data->mutex);
+
+	return 1;
+}
+
+/*
+ * TPA6130 volume. From -59.5 to 4 dB with increasing step size when going
+ * down in gain.
+ */
+static const unsigned int tpa6130_tlv[] = {
+	TLV_DB_RANGE_HEAD(10),
+	0, 1, TLV_DB_SCALE_ITEM(-5950, 600, 0),
+	2, 3, TLV_DB_SCALE_ITEM(-5000, 250, 0),
+	4, 5, TLV_DB_SCALE_ITEM(-4550, 160, 0),
+	6, 7, TLV_DB_SCALE_ITEM(-4140, 190, 0),
+	8, 9, TLV_DB_SCALE_ITEM(-3650, 120, 0),
+	10, 11, TLV_DB_SCALE_ITEM(-3330, 160, 0),
+	12, 13, TLV_DB_SCALE_ITEM(-3040, 180, 0),
+	14, 20, TLV_DB_SCALE_ITEM(-2710, 110, 0),
+	21, 37, TLV_DB_SCALE_ITEM(-1960, 74, 0),
+	38, 63, TLV_DB_SCALE_ITEM(-720, 45, 0),
+};
+
+static const struct snd_kcontrol_new tpa6130a2_controls[] = {
+	SOC_SINGLE_EXT_TLV("TPA6130A2 Headphone Playback Volume",
+		       TPA6130A2_REG_VOL_MUTE, 0, 0x3f, 0,
+		       tpa6130a2_get_reg, tpa6130a2_set_reg,
+		       tpa6130_tlv),
+};
+
+/*
+ * Enable or disable channel (left or right)
+ * The bit number for mute and amplifier are the same per channel:
+ * bit 6: Right channel
+ * bit 7: Left channel
+ * in both registers.
+ */
+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 */
+		val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+		val |= channel;
+		tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+
+		/* Unmute channel */
+		val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
+		val &= ~channel;
+		tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
+	} else {
+		/* Disable channel */
+		/* Mute channel */
+		val = tpa6130a2_read(TPA6130A2_REG_VOL_MUTE);
+		val |= channel;
+		tpa6130a2_i2c_write(TPA6130A2_REG_VOL_MUTE, val);
+
+		/* Disable amplifier */
+		val = tpa6130a2_read(TPA6130A2_REG_CONTROL);
+		val &= ~channel;
+		tpa6130a2_i2c_write(TPA6130A2_REG_CONTROL, val);
+	}
+}
+
+static int tpa6130a2_left_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 1);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tpa6130a2_channel_enable(TPA6130A2_HP_EN_L, 0);
+		break;
+	}
+	return 0;
+}
+
+static int tpa6130a2_right_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 1);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tpa6130a2_channel_enable(TPA6130A2_HP_EN_R, 0);
+		break;
+	}
+	return 0;
+}
+
+static int tpa6130a2_supply_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		tpa6130a2_power(1);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tpa6130a2_power(0);
+		break;
+	}
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget tpa6130a2_dapm_widgets[] = {
+	SND_SOC_DAPM_PGA_E("TPA6130A2 Left", SND_SOC_NOPM,
+			0, 0, NULL, 0, tpa6130a2_left_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("TPA6130A2 Right", SND_SOC_NOPM,
+			0, 0, NULL, 0, tpa6130a2_right_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("TPA6130A2 Enable", SND_SOC_NOPM,
+			0, 0, tpa6130a2_supply_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	/* Outputs */
+	SND_SOC_DAPM_HP("TPA6130A2 Headphone Left", NULL),
+	SND_SOC_DAPM_HP("TPA6130A2 Headphone Right", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	{"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Left"},
+	{"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Right"},
+
+	{"TPA6130A2 Headphone Left", NULL, "TPA6130A2 Enable"},
+	{"TPA6130A2 Headphone Right", NULL, "TPA6130A2 Enable"},
+};
+
+int tpa6130a2_add_controls(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, tpa6130a2_dapm_widgets,
+				ARRAY_SIZE(tpa6130a2_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	return snd_soc_add_controls(codec, tpa6130a2_controls,
+				ARRAY_SIZE(tpa6130a2_controls));
+
+}
+EXPORT_SYMBOL_GPL(tpa6130a2_add_controls);
+
+static int 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;
+
+	dev = &client->dev;
+
+	if (client->dev.platform_data == NULL) {
+		dev_err(dev, "Platform data not set\n");
+		dump_stack();
+		return -ENODEV;
+	}
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (data == NULL) {
+		dev_err(dev, "Can not allocate memory\n");
+		return -ENOMEM;
+	}
+
+	tpa6130a2_client = client;
+
+	i2c_set_clientdata(tpa6130a2_client, data);
+
+	pdata = client->dev.platform_data;
+	data->power_gpio = pdata->power_gpio;
+
+	mutex_init(&data->mutex);
+
+	/* Set default register values */
+	data->regs[TPA6130A2_REG_CONTROL] =	TPA6130A2_SWS;
+	data->regs[TPA6130A2_REG_VOL_MUTE] =	TPA6130A2_MUTE_R |
+						TPA6130A2_MUTE_L;
+
+	if (data->power_gpio >= 0) {
+		ret = gpio_request(data->power_gpio, "tpa6130a2 enable");
+		if (ret < 0) {
+			dev_err(dev, "Failed to request power GPIO (%d)\n",
+				data->power_gpio);
+			goto fail;
+		}
+		gpio_direction_output(data->power_gpio, 0);
+	} else {
+		data->power_state = 1;
+		tpa6130a2_initialize();
+	}
+
+	tpa6130a2_power(1);
+
+	/* Read version */
+	ret = tpa6130a2_i2c_read(TPA6130A2_REG_VERSION) &
+				 TPA6130A2_VERSION_MASK;
+	if ((ret != 1) && (ret != 2))
+		dev_warn(dev, "UNTESTED version detected (%d)\n", ret);
+
+	/* Disable the chip */
+	tpa6130a2_power(0);
+
+	return 0;
+fail:
+	kfree(data);
+	i2c_set_clientdata(tpa6130a2_client, NULL);
+	tpa6130a2_client = NULL;
+
+	return ret;
+}
+
+static int tpa6130a2_remove(struct i2c_client *client)
+{
+	struct tpa6130a2_data *data = i2c_get_clientdata(client);
+
+	tpa6130a2_power(0);
+
+	if (data->power_gpio >= 0)
+		gpio_free(data->power_gpio);
+	kfree(data);
+	tpa6130a2_client = NULL;
+
+	return 0;
+}
+
+static const struct i2c_device_id tpa6130a2_id[] = {
+	{ "tpa6130a2", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tpa6130a2_id);
+
+static struct i2c_driver tpa6130a2_i2c_driver = {
+	.driver = {
+		.name = "tpa6130a2",
+		.owner = THIS_MODULE,
+	},
+	.probe = tpa6130a2_probe,
+	.remove = __devexit_p(tpa6130a2_remove),
+	.id_table = tpa6130a2_id,
+};
+
+static int __init tpa6130a2_init(void)
+{
+	return i2c_add_driver(&tpa6130a2_i2c_driver);
+}
+
+static void __exit tpa6130a2_exit(void)
+{
+	i2c_del_driver(&tpa6130a2_i2c_driver);
+}
+
+MODULE_AUTHOR("Peter Ujfalusi");
+MODULE_DESCRIPTION("TPA6130A2 Headphone amplifier driver");
+MODULE_LICENSE("GPL");
+
+module_init(tpa6130a2_init);
+module_exit(tpa6130a2_exit);
diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h
new file mode 100644
index 0000000..57e867f
--- /dev/null
+++ b/sound/soc/codecs/tpa6130a2.h
@@ -0,0 +1,61 @@
+/*
+ * ALSA SoC TPA6130A2 amplifier driver
+ *
+ * Copyright (C) Nokia Corporation
+ *
+ * Author: Peter Ujfalusi <peter.ujfalusi@nokia.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 __TPA6130A2_H__
+#define __TPA6130A2_H__
+
+/* Register addresses */
+#define TPA6130A2_REG_CONTROL		0x01
+#define TPA6130A2_REG_VOL_MUTE		0x02
+#define TPA6130A2_REG_OUT_IMPEDANCE	0x03
+#define TPA6130A2_REG_VERSION		0x04
+
+#define TPA6130A2_CACHEREGNUM	(TPA6130A2_REG_VERSION + 1)
+
+/* Register bits */
+/* TPA6130A2_REG_CONTROL (0x01) */
+#define TPA6130A2_SWS			(0x01 << 0)
+#define TPA6130A2_TERMAL		(0x01 << 1)
+#define TPA6130A2_MODE(x)		(x << 4)
+#define TPA6130A2_MODE_STEREO		(0x00)
+#define TPA6130A2_MODE_DUAL_MONO	(0x01)
+#define TPA6130A2_MODE_BRIDGE		(0x02)
+#define TPA6130A2_MODE_MASK		(0x03)
+#define TPA6130A2_HP_EN_R		(0x01 << 6)
+#define TPA6130A2_HP_EN_L		(0x01 << 7)
+
+/* TPA6130A2_REG_VOL_MUTE (0x02) */
+#define TPA6130A2_VOLUME(x)		((x & 0x3f) << 0)
+#define TPA6130A2_MUTE_R		(0x01 << 6)
+#define TPA6130A2_MUTE_L		(0x01 << 7)
+
+/* TPA6130A2_REG_OUT_IMPEDANCE (0x03) */
+#define TPA6130A2_HIZ_R			(0x01 << 0)
+#define TPA6130A2_HIZ_L			(0x01 << 1)
+
+/* TPA6130A2_REG_VERSION (0x04) */
+#define TPA6130A2_VERSION_MASK		(0x0f)
+
+extern int tpa6130a2_add_controls(struct snd_soc_codec *codec);
+
+#endif /* __TPA6130A2_H__ */
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 4df7c6c..928257b 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -120,9 +120,10 @@
 
 /* codec private data */
 struct twl4030_priv {
-	unsigned int bypass_state;
+	struct snd_soc_codec codec;
+
 	unsigned int codec_powered;
-	unsigned int codec_muted;
+	unsigned int apll_enabled;
 
 	struct snd_pcm_substream *master_substream;
 	struct snd_pcm_substream *slave_substream;
@@ -183,19 +184,20 @@
 static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
 {
 	struct twl4030_priv *twl4030 = codec->private_data;
-	u8 mode;
+	int mode;
 
 	if (enable == twl4030->codec_powered)
 		return;
 
-	mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
 	if (enable)
-		mode |= TWL4030_CODECPDZ;
+		mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);
 	else
-		mode &= ~TWL4030_CODECPDZ;
+		mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
 
-	twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
-	twl4030->codec_powered = enable;
+	if (mode >= 0) {
+		twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode);
+		twl4030->codec_powered = enable;
+	}
 
 	/* REVISIT: this delay is present in TI sample drivers */
 	/* but there seems to be no TRM requirement for it     */
@@ -216,27 +218,25 @@
 
 }
 
-static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
+static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
 {
 	struct twl4030_priv *twl4030 = codec->private_data;
-	u8 reg_val;
+	int status;
 
-	if (mute == twl4030->codec_muted)
+	if (enable == twl4030->apll_enabled)
 		return;
 
-	if (mute) {
-		/* Disable PLL */
-		reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
-		reg_val &= ~TWL4030_APLL_EN;
-		twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
-	} else {
+	if (enable)
 		/* Enable PLL */
-		reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
-		reg_val |= TWL4030_APLL_EN;
-		twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
-	}
+		status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);
+	else
+		/* Disable PLL */
+		status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);
 
-	twl4030->codec_muted = mute;
+	if (status >= 0)
+		twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
+
+	twl4030->apll_enabled = enable;
 }
 
 static void twl4030_power_up(struct snd_soc_codec *codec)
@@ -613,6 +613,27 @@
 	return 0;
 }
 
+static int vibramux_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	twl4030_write(w->codec, TWL4030_REG_VIBRA_SET, 0xff);
+	return 0;
+}
+
+static int apll_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		twl4030_apll_enable(w->codec, 1);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		twl4030_apll_enable(w->codec, 0);
+		break;
+	}
+	return 0;
+}
+
 static void headset_ramp(struct snd_soc_codec *codec, int ramp)
 {
 	struct snd_soc_device *socdev = codec->socdev;
@@ -724,67 +745,6 @@
 	return 0;
 }
 
-static int bypass_event(struct snd_soc_dapm_widget *w,
-		struct snd_kcontrol *kcontrol, int event)
-{
-	struct soc_mixer_control *m =
-		(struct soc_mixer_control *)w->kcontrols->private_value;
-	struct twl4030_priv *twl4030 = w->codec->private_data;
-	unsigned char reg, misc;
-
-	reg = twl4030_read_reg_cache(w->codec, m->reg);
-
-	/*
-	 * bypass_state[0:3] - analog HiFi bypass
-	 * bypass_state[4]   - analog voice bypass
-	 * bypass_state[5]   - digital voice bypass
-	 * bypass_state[6:7] - digital HiFi bypass
-	 */
-	if (m->reg == TWL4030_REG_VSTPGA) {
-		/* Voice digital bypass */
-		if (reg)
-			twl4030->bypass_state |= (1 << 5);
-		else
-			twl4030->bypass_state &= ~(1 << 5);
-	} else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
-		/* Analog bypass */
-		if (reg & (1 << m->shift))
-			twl4030->bypass_state |=
-				(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
-		else
-			twl4030->bypass_state &=
-				~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
-	} else if (m->reg == TWL4030_REG_VDL_APGA_CTL) {
-		/* Analog voice bypass */
-		if (reg & (1 << m->shift))
-			twl4030->bypass_state |= (1 << 4);
-		else
-			twl4030->bypass_state &= ~(1 << 4);
-	} else {
-		/* Digital bypass */
-		if (reg & (0x7 << m->shift))
-			twl4030->bypass_state |= (1 << (m->shift ? 7 : 6));
-		else
-			twl4030->bypass_state &= ~(1 << (m->shift ? 7 : 6));
-	}
-
-	/* Enable master analog loopback mode if any analog switch is enabled*/
-	misc = twl4030_read_reg_cache(w->codec, TWL4030_REG_MISC_SET_1);
-	if (twl4030->bypass_state & 0x1F)
-		misc |= TWL4030_FMLOOP_EN;
-	else
-		misc &= ~TWL4030_FMLOOP_EN;
-	twl4030_write(w->codec, TWL4030_REG_MISC_SET_1, misc);
-
-	if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) {
-		if (twl4030->bypass_state)
-			twl4030_codec_mute(w->codec, 0);
-		else
-			twl4030_codec_mute(w->codec, 1);
-	}
-	return 0;
-}
-
 /*
  * Some of the gain controls in TWL (mostly those which are associated with
  * the outputs) are implemented in an interesting way:
@@ -1192,32 +1152,28 @@
 			SND_SOC_NOPM, 0, 0),
 
 	/* Analog bypasses */
-	SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
-			&twl4030_dapm_abypassr1_control, bypass_event,
-			SND_SOC_DAPM_POST_REG),
-	SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
-			&twl4030_dapm_abypassl1_control,
-			bypass_event, SND_SOC_DAPM_POST_REG),
-	SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
-			&twl4030_dapm_abypassr2_control,
-			bypass_event, SND_SOC_DAPM_POST_REG),
-	SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
-			&twl4030_dapm_abypassl2_control,
-			bypass_event, SND_SOC_DAPM_POST_REG),
-	SND_SOC_DAPM_SWITCH_E("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
-			&twl4030_dapm_abypassv_control,
-			bypass_event, SND_SOC_DAPM_POST_REG),
+	SND_SOC_DAPM_SWITCH("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_abypassr1_control),
+	SND_SOC_DAPM_SWITCH("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_abypassl1_control),
+	SND_SOC_DAPM_SWITCH("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_abypassr2_control),
+	SND_SOC_DAPM_SWITCH("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_abypassl2_control),
+	SND_SOC_DAPM_SWITCH("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_abypassv_control),
+
+	/* Master analog loopback switch */
+	SND_SOC_DAPM_SUPPLY("FM Loop Enable", TWL4030_REG_MISC_SET_1, 5, 0,
+			    NULL, 0),
 
 	/* Digital bypasses */
-	SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
-			&twl4030_dapm_dbypassl_control, bypass_event,
-			SND_SOC_DAPM_POST_REG),
-	SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
-			&twl4030_dapm_dbypassr_control, bypass_event,
-			SND_SOC_DAPM_POST_REG),
-	SND_SOC_DAPM_SWITCH_E("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
-			&twl4030_dapm_dbypassv_control, bypass_event,
-			SND_SOC_DAPM_POST_REG),
+	SND_SOC_DAPM_SWITCH("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_dbypassl_control),
+	SND_SOC_DAPM_SWITCH("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_dbypassr_control),
+	SND_SOC_DAPM_SWITCH("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_dbypassv_control),
 
 	/* Digital mixers, power control for the physical DACs */
 	SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer",
@@ -1243,6 +1199,9 @@
 	SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer",
 			TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0),
 
+	SND_SOC_DAPM_SUPPLY("APLL Enable", SND_SOC_NOPM, 0, 0, apll_event,
+			    SND_SOC_DAPM_PRE_PMU|SND_SOC_DAPM_POST_PMD),
+
 	/* Output MIXER controls */
 	/* Earpiece */
 	SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
@@ -1308,8 +1267,9 @@
 			0, 0, NULL, 0, handsfreerpga_event,
 			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
 	/* Vibra */
-	SND_SOC_DAPM_MUX("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
-		&twl4030_dapm_vibra_control),
+	SND_SOC_DAPM_MUX_E("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
+			   &twl4030_dapm_vibra_control, vibramux_event,
+			   SND_SOC_DAPM_PRE_PMU),
 	SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0,
 		&twl4030_dapm_vibrapath_control),
 
@@ -1369,6 +1329,13 @@
 	{"Digital R2 Playback Mixer", NULL, "DAC Right2"},
 	{"Digital Voice Playback Mixer", NULL, "DAC Voice"},
 
+	/* Supply for the digital part (APLL) */
+	{"Digital R1 Playback Mixer", NULL, "APLL Enable"},
+	{"Digital L1 Playback Mixer", NULL, "APLL Enable"},
+	{"Digital R2 Playback Mixer", NULL, "APLL Enable"},
+	{"Digital L2 Playback Mixer", NULL, "APLL Enable"},
+	{"Digital Voice Playback Mixer", NULL, "APLL 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"},
@@ -1482,6 +1449,11 @@
 	{"ADC Virtual Left2", NULL, "TX2 Capture Route"},
 	{"ADC Virtual Right2", NULL, "TX2 Capture Route"},
 
+	{"ADC Virtual Left1", NULL, "APLL Enable"},
+	{"ADC Virtual Right1", NULL, "APLL Enable"},
+	{"ADC Virtual Left2", NULL, "APLL Enable"},
+	{"ADC Virtual Right2", NULL, "APLL Enable"},
+
 	/* Analog bypass routes */
 	{"Right1 Analog Loopback", "Switch", "Analog Right"},
 	{"Left1 Analog Loopback", "Switch", "Analog Left"},
@@ -1489,6 +1461,13 @@
 	{"Left2 Analog Loopback", "Switch", "Analog Left"},
 	{"Voice Analog Loopback", "Switch", "Analog Left"},
 
+	/* Supply for the Analog loopbacks */
+	{"Right1 Analog Loopback", NULL, "FM Loop Enable"},
+	{"Left1 Analog Loopback", NULL, "FM Loop Enable"},
+	{"Right2 Analog Loopback", NULL, "FM Loop Enable"},
+	{"Left2 Analog Loopback", NULL, "FM Loop Enable"},
+	{"Voice Analog Loopback", NULL, "FM Loop Enable"},
+
 	{"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
 	{"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
 	{"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
@@ -1520,25 +1499,14 @@
 static int twl4030_set_bias_level(struct snd_soc_codec *codec,
 				  enum snd_soc_bias_level level)
 {
-	struct twl4030_priv *twl4030 = codec->private_data;
-
 	switch (level) {
 	case SND_SOC_BIAS_ON:
-		twl4030_codec_mute(codec, 0);
 		break;
 	case SND_SOC_BIAS_PREPARE:
-		twl4030_power_up(codec);
-		if (twl4030->bypass_state)
-			twl4030_codec_mute(codec, 0);
-		else
-			twl4030_codec_mute(codec, 1);
 		break;
 	case SND_SOC_BIAS_STANDBY:
-		twl4030_power_up(codec);
-		if (twl4030->bypass_state)
-			twl4030_codec_mute(codec, 0);
-		else
-			twl4030_codec_mute(codec, 1);
+		if (codec->bias_level == SND_SOC_BIAS_OFF)
+			twl4030_power_up(codec);
 		break;
 	case SND_SOC_BIAS_OFF:
 		twl4030_power_down(codec);
@@ -1785,19 +1753,21 @@
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct twl4030_priv *twl4030 = codec->private_data;
-	u8 infreq;
+	u8 apll_ctrl;
 
+	apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
+	apll_ctrl &= ~TWL4030_APLL_INFREQ;
 	switch (freq) {
 	case 19200000:
-		infreq = TWL4030_APLL_INFREQ_19200KHZ;
+		apll_ctrl |= TWL4030_APLL_INFREQ_19200KHZ;
 		twl4030->sysclk = 19200;
 		break;
 	case 26000000:
-		infreq = TWL4030_APLL_INFREQ_26000KHZ;
+		apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ;
 		twl4030->sysclk = 26000;
 		break;
 	case 38400000:
-		infreq = TWL4030_APLL_INFREQ_38400KHZ;
+		apll_ctrl |= TWL4030_APLL_INFREQ_38400KHZ;
 		twl4030->sysclk = 38400;
 		break;
 	default:
@@ -1806,8 +1776,7 @@
 		return -EINVAL;
 	}
 
-	infreq |= TWL4030_APLL_EN;
-	twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+	twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl);
 
 	return 0;
 }
@@ -1989,11 +1958,13 @@
 		int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
-	u8 infreq;
+	u8 apll_ctrl;
 
+	apll_ctrl = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
+	apll_ctrl &= ~TWL4030_APLL_INFREQ;
 	switch (freq) {
 	case 26000000:
-		infreq = TWL4030_APLL_INFREQ_26000KHZ;
+		apll_ctrl |= TWL4030_APLL_INFREQ_26000KHZ;
 		break;
 	default:
 		printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n",
@@ -2001,8 +1972,7 @@
 		return -EINVAL;
 	}
 
-	infreq |= TWL4030_APLL_EN;
-	twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+	twl4030_write(codec, TWL4030_REG_APLL_CTL, apll_ctrl);
 
 	return 0;
 }
@@ -2121,7 +2091,7 @@
 };
 EXPORT_SYMBOL_GPL(twl4030_dai);
 
-static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
+static int twl4030_soc_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;
@@ -2131,7 +2101,7 @@
 	return 0;
 }
 
-static int twl4030_resume(struct platform_device *pdev)
+static int twl4030_soc_resume(struct platform_device *pdev)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->card->codec;
@@ -2141,32 +2111,21 @@
 	return 0;
 }
 
-/*
- * initialize the driver
- * register the mixer and dsp interfaces with the kernel
- */
+static struct snd_soc_codec *twl4030_codec;
 
-static int twl4030_init(struct snd_soc_device *socdev)
+static int twl4030_soc_probe(struct platform_device *pdev)
 {
-	struct snd_soc_codec *codec = socdev->card->codec;
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct twl4030_setup_data *setup = socdev->codec_data;
-	struct twl4030_priv *twl4030 = codec->private_data;
-	int ret = 0;
+	struct snd_soc_codec *codec;
+	struct twl4030_priv *twl4030;
+	int ret;
 
-	printk(KERN_INFO "TWL4030 Audio Codec init \n");
+	BUG_ON(!twl4030_codec);
 
-	codec->name = "twl4030";
-	codec->owner = THIS_MODULE;
-	codec->read = twl4030_read_reg_cache;
-	codec->write = twl4030_write;
-	codec->set_bias_level = twl4030_set_bias_level;
-	codec->dai = 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);
-	if (codec->reg_cache == NULL)
-		return -ENOMEM;
+	codec = twl4030_codec;
+	twl4030 = codec->private_data;
+	socdev->card->codec = codec;
 
 	/* Configuration for headset ramp delay from setup data */
 	if (setup) {
@@ -2188,71 +2147,22 @@
 	/* register pcms */
 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 	if (ret < 0) {
-		printk(KERN_ERR "twl4030: failed to create pcms\n");
-		goto pcm_err;
+		dev_err(&pdev->dev, "failed to create pcms\n");
+		return ret;
 	}
 
-	twl4030_init_chip(codec);
-
-	/* power on device */
-	twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
 	snd_soc_add_controls(codec, twl4030_snd_controls,
 				ARRAY_SIZE(twl4030_snd_controls));
 	twl4030_add_widgets(codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "twl4030: failed to register card\n");
-		goto card_err;
-	}
-
-	return ret;
-
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
-pcm_err:
-	kfree(codec->reg_cache);
-	return ret;
-}
-
-static struct snd_soc_device *twl4030_socdev;
-
-static int twl4030_probe(struct platform_device *pdev)
-{
-	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct snd_soc_codec *codec;
-	struct twl4030_priv *twl4030;
-
-	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-	if (codec == NULL)
-		return -ENOMEM;
-
-	twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
-	if (twl4030 == NULL) {
-		kfree(codec);
-		return -ENOMEM;
-	}
-
-	codec->private_data = twl4030;
-	socdev->card->codec = codec;
-	mutex_init(&codec->mutex);
-	INIT_LIST_HEAD(&codec->dapm_widgets);
-	INIT_LIST_HEAD(&codec->dapm_paths);
-
-	twl4030_socdev = socdev;
-	twl4030_init(socdev);
-
 	return 0;
 }
 
-static int twl4030_remove(struct platform_device *pdev)
+static int twl4030_soc_remove(struct platform_device *pdev)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->card->codec;
 
-	printk(KERN_INFO "TWL4030 Audio Codec remove\n");
 	twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
@@ -2262,26 +2172,123 @@
 	return 0;
 }
 
-struct snd_soc_codec_device soc_codec_dev_twl4030 = {
-	.probe = twl4030_probe,
-	.remove = twl4030_remove,
-	.suspend = twl4030_suspend,
-	.resume = twl4030_resume,
+static int __devinit twl4030_codec_probe(struct platform_device *pdev)
+{
+	struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data;
+	struct snd_soc_codec *codec;
+	struct twl4030_priv *twl4030;
+	int ret;
+
+	if (!pdata || !(pdata->audio_mclk == 19200000 ||
+			pdata->audio_mclk == 26000000 ||
+			pdata->audio_mclk == 38400000)) {
+		dev_err(&pdev->dev, "Invalid platform_data\n");
+		return -EINVAL;
+	}
+
+	twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
+	if (twl4030 == NULL) {
+		dev_err(&pdev->dev, "Can not allocate memroy\n");
+		return -ENOMEM;
+	}
+
+	codec = &twl4030->codec;
+	codec->private_data = twl4030;
+	codec->dev = &pdev->dev;
+	twl4030_dai[0].dev = &pdev->dev;
+	twl4030_dai[1].dev = &pdev->dev;
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->name = "twl4030";
+	codec->owner = THIS_MODULE;
+	codec->read = twl4030_read_reg_cache;
+	codec->write = twl4030_write;
+	codec->set_bias_level = twl4030_set_bias_level;
+	codec->dai = 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);
+	if (codec->reg_cache == NULL) {
+		ret = -ENOMEM;
+		goto error_cache;
+	}
+
+	platform_set_drvdata(pdev, twl4030);
+	twl4030_codec = codec;
+
+	/* Set the defaults, and power up the codec */
+	twl4030_init_chip(codec);
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto error_codec;
+	}
+
+	ret = snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		goto error_codec;
+	}
+
+	return 0;
+
+error_codec:
+	twl4030_power_down(codec);
+	kfree(codec->reg_cache);
+error_cache:
+	kfree(twl4030);
+	return ret;
+}
+
+static int __devexit twl4030_codec_remove(struct platform_device *pdev)
+{
+	struct twl4030_priv *twl4030 = platform_get_drvdata(pdev);
+
+	kfree(twl4030);
+
+	twl4030_codec = NULL;
+	return 0;
+}
+
+MODULE_ALIAS("platform:twl4030_codec_audio");
+
+static struct platform_driver twl4030_codec_driver = {
+	.probe		= twl4030_codec_probe,
+	.remove		= __devexit_p(twl4030_codec_remove),
+	.driver		= {
+		.name	= "twl4030_codec_audio",
+		.owner	= THIS_MODULE,
+	},
 };
-EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
 
 static int __init twl4030_modinit(void)
 {
-	return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+	return platform_driver_register(&twl4030_codec_driver);
 }
 module_init(twl4030_modinit);
 
 static void __exit twl4030_exit(void)
 {
-	snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
+	platform_driver_unregister(&twl4030_codec_driver);
 }
 module_exit(twl4030_exit);
 
+struct snd_soc_codec_device soc_codec_dev_twl4030 = {
+	.probe = twl4030_soc_probe,
+	.remove = twl4030_soc_remove,
+	.suspend = twl4030_soc_suspend,
+	.resume = twl4030_soc_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
+
 MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
 MODULE_AUTHOR("Steve Sakoman");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index 2b4bfa2..dd6396e 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -22,245 +22,13 @@
 #ifndef __TWL4030_AUDIO_H__
 #define __TWL4030_AUDIO_H__
 
-#define TWL4030_REG_CODEC_MODE		0x1
-#define TWL4030_REG_OPTION		0x2
-#define TWL4030_REG_UNKNOWN		0x3
-#define TWL4030_REG_MICBIAS_CTL		0x4
-#define TWL4030_REG_ANAMICL		0x5
-#define TWL4030_REG_ANAMICR		0x6
-#define TWL4030_REG_AVADC_CTL		0x7
-#define TWL4030_REG_ADCMICSEL		0x8
-#define TWL4030_REG_DIGMIXING		0x9
-#define TWL4030_REG_ATXL1PGA		0xA
-#define TWL4030_REG_ATXR1PGA		0xB
-#define TWL4030_REG_AVTXL2PGA		0xC
-#define TWL4030_REG_AVTXR2PGA		0xD
-#define TWL4030_REG_AUDIO_IF		0xE
-#define TWL4030_REG_VOICE_IF		0xF
-#define TWL4030_REG_ARXR1PGA		0x10
-#define TWL4030_REG_ARXL1PGA		0x11
-#define TWL4030_REG_ARXR2PGA		0x12
-#define TWL4030_REG_ARXL2PGA		0x13
-#define TWL4030_REG_VRXPGA		0x14
-#define TWL4030_REG_VSTPGA		0x15
-#define TWL4030_REG_VRX2ARXPGA		0x16
-#define TWL4030_REG_AVDAC_CTL		0x17
-#define TWL4030_REG_ARX2VTXPGA		0x18
-#define TWL4030_REG_ARXL1_APGA_CTL	0x19
-#define TWL4030_REG_ARXR1_APGA_CTL	0x1A
-#define TWL4030_REG_ARXL2_APGA_CTL	0x1B
-#define TWL4030_REG_ARXR2_APGA_CTL	0x1C
-#define TWL4030_REG_ATX2ARXPGA		0x1D
-#define TWL4030_REG_BT_IF		0x1E
-#define TWL4030_REG_BTPGA		0x1F
-#define TWL4030_REG_BTSTPGA		0x20
-#define TWL4030_REG_EAR_CTL		0x21
-#define TWL4030_REG_HS_SEL		0x22
-#define TWL4030_REG_HS_GAIN_SET		0x23
-#define TWL4030_REG_HS_POPN_SET		0x24
-#define TWL4030_REG_PREDL_CTL		0x25
-#define TWL4030_REG_PREDR_CTL		0x26
-#define TWL4030_REG_PRECKL_CTL		0x27
-#define TWL4030_REG_PRECKR_CTL		0x28
-#define TWL4030_REG_HFL_CTL		0x29
-#define TWL4030_REG_HFR_CTL		0x2A
-#define TWL4030_REG_ALC_CTL		0x2B
-#define TWL4030_REG_ALC_SET1		0x2C
-#define TWL4030_REG_ALC_SET2		0x2D
-#define TWL4030_REG_BOOST_CTL		0x2E
-#define TWL4030_REG_SOFTVOL_CTL		0x2F
-#define TWL4030_REG_DTMF_FREQSEL	0x30
-#define TWL4030_REG_DTMF_TONEXT1H	0x31
-#define TWL4030_REG_DTMF_TONEXT1L	0x32
-#define TWL4030_REG_DTMF_TONEXT2H	0x33
-#define TWL4030_REG_DTMF_TONEXT2L	0x34
-#define TWL4030_REG_DTMF_TONOFF		0x35
-#define TWL4030_REG_DTMF_WANONOFF	0x36
-#define TWL4030_REG_I2S_RX_SCRAMBLE_H	0x37
-#define TWL4030_REG_I2S_RX_SCRAMBLE_M	0x38
-#define TWL4030_REG_I2S_RX_SCRAMBLE_L	0x39
-#define TWL4030_REG_APLL_CTL		0x3A
-#define TWL4030_REG_DTMF_CTL		0x3B
-#define TWL4030_REG_DTMF_PGA_CTL2	0x3C
-#define TWL4030_REG_DTMF_PGA_CTL1	0x3D
-#define TWL4030_REG_MISC_SET_1		0x3E
-#define TWL4030_REG_PCMBTMUX		0x3F
-#define TWL4030_REG_RX_PATH_SEL		0x43
-#define TWL4030_REG_VDL_APGA_CTL	0x44
-#define TWL4030_REG_VIBRA_CTL		0x45
-#define TWL4030_REG_VIBRA_SET		0x46
-#define TWL4030_REG_VIBRA_PWM_SET	0x47
-#define TWL4030_REG_ANAMIC_GAIN		0x48
-#define TWL4030_REG_MISC_SET_2		0x49
+/* Register descriptions are here */
+#include <linux/mfd/twl4030-codec.h>
+
+/* Sgadow register used by the audio driver */
 #define TWL4030_REG_SW_SHADOW		0x4A
-
 #define TWL4030_CACHEREGNUM	(TWL4030_REG_SW_SHADOW + 1)
 
-/* Bitfield Definitions */
-
-/* TWL4030_CODEC_MODE (0x01) Fields */
-
-#define TWL4030_APLL_RATE		0xF0
-#define TWL4030_APLL_RATE_8000		0x00
-#define TWL4030_APLL_RATE_11025		0x10
-#define TWL4030_APLL_RATE_12000		0x20
-#define TWL4030_APLL_RATE_16000		0x40
-#define TWL4030_APLL_RATE_22050		0x50
-#define TWL4030_APLL_RATE_24000		0x60
-#define TWL4030_APLL_RATE_32000		0x80
-#define TWL4030_APLL_RATE_44100		0x90
-#define TWL4030_APLL_RATE_48000		0xA0
-#define TWL4030_APLL_RATE_96000		0xE0
-#define TWL4030_SEL_16K			0x08
-#define TWL4030_CODECPDZ		0x02
-#define TWL4030_OPT_MODE		0x01
-#define TWL4030_OPTION_1		(1 << 0)
-#define TWL4030_OPTION_2		(0 << 0)
-
-/* TWL4030_OPTION (0x02) Fields */
-
-#define TWL4030_ATXL1_EN		(1 << 0)
-#define TWL4030_ATXR1_EN		(1 << 1)
-#define TWL4030_ATXL2_VTXL_EN		(1 << 2)
-#define TWL4030_ATXR2_VTXR_EN		(1 << 3)
-#define TWL4030_ARXL1_VRX_EN		(1 << 4)
-#define TWL4030_ARXR1_EN		(1 << 5)
-#define TWL4030_ARXL2_EN		(1 << 6)
-#define TWL4030_ARXR2_EN		(1 << 7)
-
-/* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
-
-#define TWL4030_MICBIAS2_CTL		0x40
-#define TWL4030_MICBIAS1_CTL		0x20
-#define TWL4030_HSMICBIAS_EN		0x04
-#define TWL4030_MICBIAS2_EN		0x02
-#define TWL4030_MICBIAS1_EN		0x01
-
-/* ANAMICL (0x05) Fields */
-
-#define TWL4030_CNCL_OFFSET_START	0x80
-#define TWL4030_OFFSET_CNCL_SEL		0x60
-#define TWL4030_OFFSET_CNCL_SEL_ARX1	0x00
-#define TWL4030_OFFSET_CNCL_SEL_ARX2	0x20
-#define TWL4030_OFFSET_CNCL_SEL_VRX	0x40
-#define TWL4030_OFFSET_CNCL_SEL_ALL	0x60
-#define TWL4030_MICAMPL_EN		0x10
-#define TWL4030_CKMIC_EN		0x08
-#define TWL4030_AUXL_EN			0x04
-#define TWL4030_HSMIC_EN		0x02
-#define TWL4030_MAINMIC_EN		0x01
-
-/* ANAMICR (0x06) Fields */
-
-#define TWL4030_MICAMPR_EN		0x10
-#define TWL4030_AUXR_EN			0x04
-#define TWL4030_SUBMIC_EN		0x01
-
-/* AVADC_CTL (0x07) Fields */
-
-#define TWL4030_ADCL_EN			0x08
-#define TWL4030_AVADC_CLK_PRIORITY	0x04
-#define TWL4030_ADCR_EN			0x02
-
-/* TWL4030_REG_ADCMICSEL (0x08) Fields */
-
-#define TWL4030_DIGMIC1_EN		0x08
-#define TWL4030_TX2IN_SEL		0x04
-#define TWL4030_DIGMIC0_EN		0x02
-#define TWL4030_TX1IN_SEL		0x01
-
-/* AUDIO_IF (0x0E) Fields */
-
-#define TWL4030_AIF_SLAVE_EN		0x80
-#define TWL4030_DATA_WIDTH		0x60
-#define TWL4030_DATA_WIDTH_16S_16W	0x00
-#define TWL4030_DATA_WIDTH_32S_16W	0x40
-#define TWL4030_DATA_WIDTH_32S_24W	0x60
-#define TWL4030_AIF_FORMAT		0x18
-#define TWL4030_AIF_FORMAT_CODEC	0x00
-#define TWL4030_AIF_FORMAT_LEFT		0x08
-#define TWL4030_AIF_FORMAT_RIGHT	0x10
-#define TWL4030_AIF_FORMAT_TDM		0x18
-#define TWL4030_AIF_TRI_EN		0x04
-#define TWL4030_CLK256FS_EN		0x02
-#define TWL4030_AIF_EN			0x01
-
-/* VOICE_IF (0x0F) Fields */
-
-#define TWL4030_VIF_SLAVE_EN		0x80
-#define TWL4030_VIF_DIN_EN		0x40
-#define TWL4030_VIF_DOUT_EN		0x20
-#define TWL4030_VIF_SWAP		0x10
-#define TWL4030_VIF_FORMAT		0x08
-#define TWL4030_VIF_TRI_EN		0x04
-#define TWL4030_VIF_SUB_EN		0x02
-#define TWL4030_VIF_EN			0x01
-
-/* EAR_CTL (0x21) */
-#define TWL4030_EAR_GAIN		0x30
-
-/* HS_GAIN_SET (0x23) Fields */
-
-#define TWL4030_HSR_GAIN		0x0C
-#define TWL4030_HSR_GAIN_PWR_DOWN	0x00
-#define TWL4030_HSR_GAIN_PLUS_6DB	0x04
-#define TWL4030_HSR_GAIN_0DB		0x08
-#define TWL4030_HSR_GAIN_MINUS_6DB	0x0C
-#define TWL4030_HSL_GAIN		0x03
-#define TWL4030_HSL_GAIN_PWR_DOWN	0x00
-#define TWL4030_HSL_GAIN_PLUS_6DB	0x01
-#define TWL4030_HSL_GAIN_0DB		0x02
-#define TWL4030_HSL_GAIN_MINUS_6DB	0x03
-
-/* HS_POPN_SET (0x24) Fields */
-
-#define TWL4030_VMID_EN			0x40
-#define	TWL4030_EXTMUTE			0x20
-#define TWL4030_RAMP_DELAY		0x1C
-#define TWL4030_RAMP_DELAY_20MS		0x00
-#define TWL4030_RAMP_DELAY_40MS		0x04
-#define TWL4030_RAMP_DELAY_81MS		0x08
-#define TWL4030_RAMP_DELAY_161MS	0x0C
-#define TWL4030_RAMP_DELAY_323MS	0x10
-#define TWL4030_RAMP_DELAY_645MS	0x14
-#define TWL4030_RAMP_DELAY_1291MS	0x18
-#define TWL4030_RAMP_DELAY_2581MS	0x1C
-#define TWL4030_RAMP_EN			0x02
-
-/* PREDL_CTL (0x25) */
-#define TWL4030_PREDL_GAIN		0x30
-
-/* PREDR_CTL (0x26) */
-#define TWL4030_PREDR_GAIN		0x30
-
-/* PRECKL_CTL (0x27) */
-#define TWL4030_PRECKL_GAIN		0x30
-
-/* PRECKR_CTL (0x28) */
-#define TWL4030_PRECKR_GAIN		0x30
-
-/* HFL_CTL (0x29, 0x2A) Fields */
-#define TWL4030_HF_CTL_HB_EN		0x04
-#define TWL4030_HF_CTL_LOOP_EN		0x08
-#define TWL4030_HF_CTL_RAMP_EN		0x10
-#define TWL4030_HF_CTL_REF_EN		0x20
-
-/* APLL_CTL (0x3A) Fields */
-
-#define TWL4030_APLL_EN			0x10
-#define TWL4030_APLL_INFREQ		0x0F
-#define TWL4030_APLL_INFREQ_19200KHZ	0x05
-#define TWL4030_APLL_INFREQ_26000KHZ	0x06
-#define TWL4030_APLL_INFREQ_38400KHZ	0x0F
-
-/* REG_MISC_SET_1 (0x3E) Fields */
-
-#define TWL4030_CLK64_EN		0x80
-#define TWL4030_SCRAMBLE_EN		0x40
-#define TWL4030_FMLOOP_EN		0x20
-#define TWL4030_SMOOTH_ANAVOL_EN	0x02
-#define TWL4030_DIGMIC_LR_SWAP_EN	0x01
-
 /* TWL4030_REG_SW_SHADOW (0x4A) Fields */
 #define TWL4030_HFL_EN			0x01
 #define TWL4030_HFR_EN			0x02
@@ -279,3 +47,5 @@
 };
 
 #endif	/* End of __TWL4030_AUDIO_H__ */
+
+
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index c33b92e..aa40d98 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -562,17 +562,8 @@
 		goto pcm_err;
 	}
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "UDA134X: failed to register card\n");
-		goto card_err;
-	}
-
 	return 0;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	kfree(codec->reg_cache);
 reg_err:
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 92ec034..a42e47d 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -713,17 +713,9 @@
 	snd_soc_add_controls(codec, uda1380_snd_controls,
 				ARRAY_SIZE(uda1380_snd_controls));
 	uda1380_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
 
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	return ret;
 }
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 593d5b9..2e35a35 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1101,7 +1101,7 @@
 }
 
 static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
-			  int pll_id, unsigned int freq_in,
+			  int pll_id, int source, unsigned int freq_in,
 			  unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1501,18 +1501,7 @@
 
 	wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register card\n");
-		goto card_err;
-	}
-
 	return 0;
-
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
-	return ret;
 }
 
 static int wm8350_remove(struct platform_device *pdev)
@@ -1680,21 +1669,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8350_codec_suspend(struct platform_device *pdev, pm_message_t m)
-{
-	return snd_soc_suspend_device(&pdev->dev);
-}
-
-static int wm8350_codec_resume(struct platform_device *pdev)
-{
-	return snd_soc_resume_device(&pdev->dev);
-}
-#else
-#define wm8350_codec_suspend NULL
-#define wm8350_codec_resume NULL
-#endif
-
 static struct platform_driver wm8350_codec_driver = {
 	.driver = {
 		   .name = "wm8350-codec",
@@ -1702,8 +1676,6 @@
 		   },
 	.probe = wm8350_codec_probe,
 	.remove = __devexit_p(wm8350_codec_remove),
-	.suspend = wm8350_codec_suspend,
-	.resume = wm8350_codec_resume,
 };
 
 static __init int wm8350_init(void)
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index b9ef4d9..0e30997 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -1011,7 +1011,8 @@
 }
 
 static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
-			      unsigned int freq_in, unsigned int freq_out)
+			      int source, unsigned int freq_in,
+			      unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct wm8400_priv *wm8400 = codec->private_data;
@@ -1399,12 +1400,6 @@
 	wm8400_add_controls(codec);
 	wm8400_add_widgets(codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to register card\n");
-		goto card_err;
-	}
-
 	return ret;
 
 card_err:
@@ -1558,21 +1553,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8400_pdev_suspend(struct platform_device *pdev, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&pdev->dev);
-}
-
-static int wm8400_pdev_resume(struct platform_device *pdev)
-{
-	return snd_soc_resume_device(&pdev->dev);
-}
-#else
-#define wm8400_pdev_suspend NULL
-#define wm8400_pdev_resume NULL
-#endif
-
 static struct platform_driver wm8400_codec_driver = {
 	.driver = {
 		.name = "wm8400-codec",
@@ -1580,8 +1560,6 @@
 	},
 	.probe = wm8400_codec_probe,
 	.remove	= __exit_p(wm8400_codec_remove),
-	.suspend = wm8400_pdev_suspend,
-	.resume = wm8400_pdev_resume,
 };
 
 static int __init wm8400_codec_init(void)
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 060d5d0..e3c21eb 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -271,8 +271,8 @@
 	pll_div.k = K;
 }
 
-static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 reg;
@@ -604,16 +604,9 @@
 	snd_soc_add_controls(codec, wm8510_snd_controls,
 				ARRAY_SIZE(wm8510_snd_controls));
 	wm8510_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "wm8510: failed to register card\n");
-		goto card_err;
-	}
+
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 err:
 	kfree(codec->reg_cache);
 	return ret;
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 25870a4..2e2b01d 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -448,17 +448,9 @@
 	snd_soc_add_controls(codec, wm8523_snd_controls,
 			     ARRAY_SIZE(wm8523_snd_controls));
 	wm8523_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
 
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	return ret;
 }
@@ -638,21 +630,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8523_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8523_i2c_resume(struct i2c_client *i2c)
-{
-	return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8523_i2c_suspend NULL
-#define wm8523_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8523_i2c_id[] = {
 	{ "wm8523", 0 },
 	{ }
@@ -666,8 +643,6 @@
 	},
 	.probe =    wm8523_i2c_probe,
 	.remove =   __devexit_p(wm8523_i2c_remove),
-	.suspend =  wm8523_i2c_suspend,
-	.resume =   wm8523_i2c_resume,
 	.id_table = wm8523_i2c_id,
 };
 #endif
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 6bded8c..dde50d1 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -407,8 +407,8 @@
 	return 0;
 }
 
-static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	int offset;
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -800,17 +800,9 @@
 	snd_soc_add_controls(codec, wm8580_snd_controls,
 			     ARRAY_SIZE(wm8580_snd_controls));
 	wm8580_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
 
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	return ret;
 }
@@ -961,21 +953,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8580_i2c_resume(struct i2c_client *client)
-{
-	return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8580_i2c_suspend NULL
-#define wm8580_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8580_i2c_id[] = {
 	{ "wm8580", 0 },
 	{ }
@@ -989,8 +966,6 @@
 	},
 	.probe =    wm8580_i2c_probe,
 	.remove =   wm8580_i2c_remove,
-	.suspend =  wm8580_i2c_suspend,
-	.resume =   wm8580_i2c_resume,
 	.id_table = wm8580_i2c_id,
 };
 #endif
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
new file mode 100644
index 0000000..70e0675
--- /dev/null
+++ b/sound/soc/codecs/wm8711.c
@@ -0,0 +1,634 @@
+/*
+ * wm8711.c  --  WM8711 ALSA SoC Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <linux@wolfsonmicro.com>
+ *
+ * Based on wm8731.c by Richard Purdie
+ *
+ * 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/spi/spi.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 "wm8711.h"
+
+static struct snd_soc_codec *wm8711_codec;
+
+/* codec private data */
+struct wm8711_priv {
+	struct snd_soc_codec codec;
+	u16 reg_cache[WM8711_CACHEREGNUM];
+	unsigned int sysclk;
+};
+
+/*
+ * wm8711 register cache
+ * We can't read the WM8711 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ * There is no point in caching the reset register
+ */
+static const u16 wm8711_reg[WM8711_CACHEREGNUM] = {
+	0x0079, 0x0079, 0x000a, 0x0008,
+	0x009f, 0x000a, 0x0000, 0x0000
+};
+
+#define wm8711_reset(c)	snd_soc_write(c, WM8711_RESET, 0)
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+
+static const struct snd_kcontrol_new wm8711_snd_controls[] = {
+
+SOC_DOUBLE_R_TLV("Master Playback Volume", WM8711_LOUT1V, WM8711_ROUT1V,
+		 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Master Playback ZC Switch", WM8711_LOUT1V, WM8711_ROUT1V,
+	7, 1, 0),
+
+};
+
+/* Output Mixer */
+static const struct snd_kcontrol_new wm8711_output_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8711_APANA, 3, 1, 0),
+SOC_DAPM_SINGLE("HiFi Playback Switch", WM8711_APANA, 4, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8711_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Output Mixer", WM8711_PWR, 4, 1,
+	&wm8711_output_mixer_controls[0],
+	ARRAY_SIZE(wm8711_output_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8711_PWR, 3, 1),
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("LHPOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+SND_SOC_DAPM_OUTPUT("RHPOUT"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	/* output mixer */
+	{"Output Mixer", "Line Bypass Switch", "Line Input"},
+	{"Output Mixer", "HiFi Playback Switch", "DAC"},
+
+	/* outputs */
+	{"RHPOUT", NULL, "Output Mixer"},
+	{"ROUT", NULL, "Output Mixer"},
+	{"LHPOUT", NULL, "Output Mixer"},
+	{"LOUT", NULL, "Output Mixer"},
+};
+
+static int wm8711_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8711_dapm_widgets,
+				  ARRAY_SIZE(wm8711_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+struct _coeff_div {
+	u32 mclk;
+	u32 rate;
+	u16 fs;
+	u8 sr:4;
+	u8 bosr:1;
+	u8 usb:1;
+};
+
+/* codec mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+	/* 48k */
+	{12288000, 48000, 256, 0x0, 0x0, 0x0},
+	{18432000, 48000, 384, 0x0, 0x1, 0x0},
+	{12000000, 48000, 250, 0x0, 0x0, 0x1},
+
+	/* 32k */
+	{12288000, 32000, 384, 0x6, 0x0, 0x0},
+	{18432000, 32000, 576, 0x6, 0x1, 0x0},
+	{12000000, 32000, 375, 0x6, 0x0, 0x1},
+
+	/* 8k */
+	{12288000, 8000, 1536, 0x3, 0x0, 0x0},
+	{18432000, 8000, 2304, 0x3, 0x1, 0x0},
+	{11289600, 8000, 1408, 0xb, 0x0, 0x0},
+	{16934400, 8000, 2112, 0xb, 0x1, 0x0},
+	{12000000, 8000, 1500, 0x3, 0x0, 0x1},
+
+	/* 96k */
+	{12288000, 96000, 128, 0x7, 0x0, 0x0},
+	{18432000, 96000, 192, 0x7, 0x1, 0x0},
+	{12000000, 96000, 125, 0x7, 0x0, 0x1},
+
+	/* 44.1k */
+	{11289600, 44100, 256, 0x8, 0x0, 0x0},
+	{16934400, 44100, 384, 0x8, 0x1, 0x0},
+	{12000000, 44100, 272, 0x8, 0x1, 0x1},
+
+	/* 88.2k */
+	{11289600, 88200, 128, 0xf, 0x0, 0x0},
+	{16934400, 88200, 192, 0xf, 0x1, 0x0},
+	{12000000, 88200, 136, 0xf, 0x1, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+			return i;
+	}
+	return 0;
+}
+
+static int wm8711_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 wm8711_priv *wm8711 = codec->private_data;
+	u16 iface = snd_soc_read(codec, WM8711_IFACE) & 0xfffc;
+	int i = get_coeff(wm8711->sysclk, params_rate(params));
+	u16 srate = (coeff_div[i].sr << 2) |
+		(coeff_div[i].bosr << 1) | coeff_div[i].usb;
+
+	snd_soc_write(codec, WM8711_SRATE, srate);
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= 0x0004;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= 0x0008;
+		break;
+	}
+
+	snd_soc_write(codec, WM8711_IFACE, iface);
+	return 0;
+}
+
+static int wm8711_pcm_prepare(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	/* set active */
+	snd_soc_write(codec, WM8711_ACTIVE, 0x0001);
+
+	return 0;
+}
+
+static void wm8711_shutdown(struct snd_pcm_substream *substream,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	/* deactivate */
+	if (!codec->active) {
+		udelay(50);
+		snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+	}
+}
+
+static int wm8711_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = snd_soc_read(codec, WM8711_APDIGI) & 0xfff7;
+
+	if (mute)
+		snd_soc_write(codec, WM8711_APDIGI, mute_reg | 0x8);
+	else
+		snd_soc_write(codec, WM8711_APDIGI, mute_reg);
+
+	return 0;
+}
+
+static int wm8711_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 wm8711_priv *wm8711 = codec->private_data;
+
+	switch (freq) {
+	case 11289600:
+	case 12000000:
+	case 12288000:
+	case 16934400:
+	case 18432000:
+		wm8711->sysclk = freq;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int wm8711_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = 0;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		iface |= 0x0040;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= 0x0002;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x0001;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x0003;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		iface |= 0x0013;
+		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 |= 0x0090;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= 0x0080;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x0010;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* set iface */
+	snd_soc_write(codec, WM8711_IFACE, iface);
+	return 0;
+}
+
+
+static int wm8711_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	u16 reg = snd_soc_read(codec, WM8711_PWR) & 0xff7f;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		snd_soc_write(codec, WM8711_PWR, reg);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		snd_soc_write(codec, WM8711_PWR, reg | 0x0040);
+		break;
+	case SND_SOC_BIAS_OFF:
+		snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+		snd_soc_write(codec, WM8711_PWR, 0xffff);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8711_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+	SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8711_ops = {
+	.prepare = wm8711_pcm_prepare,
+	.hw_params = wm8711_hw_params,
+	.shutdown = wm8711_shutdown,
+	.digital_mute = wm8711_mute,
+	.set_sysclk = wm8711_set_dai_sysclk,
+	.set_fmt = wm8711_set_dai_fmt,
+};
+
+struct snd_soc_dai wm8711_dai = {
+	.name = "WM8711",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8711_RATES,
+		.formats = WM8711_FORMATS,
+	},
+	.ops = &wm8711_ops,
+};
+EXPORT_SYMBOL_GPL(wm8711_dai);
+
+static int wm8711_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;
+
+	snd_soc_write(codec, WM8711_ACTIVE, 0x0);
+	wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8711_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	int i;
+	u8 data[2];
+	u16 *cache = codec->reg_cache;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < ARRAY_SIZE(wm8711_reg); i++) {
+		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+	wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	wm8711_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+static int wm8711_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8711_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8711_codec;
+	codec = wm8711_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, wm8711_snd_controls,
+			     ARRAY_SIZE(wm8711_snd_controls));
+	wm8711_add_widgets(codec);
+
+	return ret;
+
+pcm_err:
+	return ret;
+}
+
+/* power down chip */
+static int wm8711_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_wm8711 = {
+	.probe = 	wm8711_probe,
+	.remove = 	wm8711_remove,
+	.suspend = 	wm8711_suspend,
+	.resume =	wm8711_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8711);
+
+static int wm8711_register(struct wm8711_priv *wm8711,
+			   enum snd_soc_control_type control)
+{
+	int ret;
+	struct snd_soc_codec *codec = &wm8711->codec;
+	u16 reg;
+
+	if (wm8711_codec) {
+		dev_err(codec->dev, "Another WM8711 is registered\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8711;
+	codec->name = "WM8711";
+	codec->owner = THIS_MODULE;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8711_set_bias_level;
+	codec->dai = &wm8711_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = WM8711_CACHEREGNUM;
+	codec->reg_cache = &wm8711->reg_cache;
+
+	memcpy(codec->reg_cache, wm8711_reg, sizeof(wm8711_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;
+	}
+
+	ret = wm8711_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		goto err;
+	}
+
+	wm8711_dai.dev = codec->dev;
+
+	wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* Latch the update bits */
+	reg = snd_soc_read(codec, WM8711_LOUT1V);
+	snd_soc_write(codec, WM8711_LOUT1V, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8711_ROUT1V);
+	snd_soc_write(codec, WM8711_ROUT1V, reg | 0x0100);
+
+	wm8711_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(&wm8711_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(wm8711);
+	return ret;
+}
+
+static void wm8711_unregister(struct wm8711_priv *wm8711)
+{
+	wm8711_set_bias_level(&wm8711->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dai(&wm8711_dai);
+	snd_soc_unregister_codec(&wm8711->codec);
+	kfree(wm8711);
+	wm8711_codec = NULL;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8711_spi_probe(struct spi_device *spi)
+{
+	struct snd_soc_codec *codec;
+	struct wm8711_priv *wm8711;
+
+	wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+	if (wm8711 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8711->codec;
+	codec->control_data = spi;
+	codec->dev = &spi->dev;
+
+	dev_set_drvdata(&spi->dev, wm8711);
+
+	return wm8711_register(wm8711, SND_SOC_SPI);
+}
+
+static int __devexit wm8711_spi_remove(struct spi_device *spi)
+{
+	struct wm8711_priv *wm8711 = dev_get_drvdata(&spi->dev);
+
+	wm8711_unregister(wm8711);
+
+	return 0;
+}
+
+static struct spi_driver wm8711_spi_driver = {
+	.driver = {
+		.name	= "wm8711",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= wm8711_spi_probe,
+	.remove		= __devexit_p(wm8711_spi_remove),
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8711_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8711_priv *wm8711;
+	struct snd_soc_codec *codec;
+
+	wm8711 = kzalloc(sizeof(struct wm8711_priv), GFP_KERNEL);
+	if (wm8711 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8711->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(i2c, wm8711);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+
+	return wm8711_register(wm8711, SND_SOC_I2C);
+}
+
+static __devexit int wm8711_i2c_remove(struct i2c_client *client)
+{
+	struct wm8711_priv *wm8711 = i2c_get_clientdata(client);
+	wm8711_unregister(wm8711);
+	return 0;
+}
+
+static const struct i2c_device_id wm8711_i2c_id[] = {
+	{ "wm8711", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
+
+static struct i2c_driver wm8711_i2c_driver = {
+	.driver = {
+		.name = "WM8711 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm8711_i2c_probe,
+	.remove =   __devexit_p(wm8711_i2c_remove),
+	.id_table = wm8711_i2c_id,
+};
+#endif
+
+static int __init wm8711_modinit(void)
+{
+	int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&wm8711_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8711 I2C driver: %d\n",
+		       ret);
+	}
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	ret = spi_register_driver(&wm8711_spi_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8711 SPI driver: %d\n",
+		       ret);
+	}
+#endif
+	return 0;
+}
+module_init(wm8711_modinit);
+
+static void __exit wm8711_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8711_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	spi_unregister_driver(&wm8711_spi_driver);
+#endif
+}
+module_exit(wm8711_exit);
+
+MODULE_DESCRIPTION("ASoC WM8711 driver");
+MODULE_AUTHOR("Mike Arthur");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8711.h b/sound/soc/codecs/wm8711.h
new file mode 100644
index 0000000..381e84a
--- /dev/null
+++ b/sound/soc/codecs/wm8711.h
@@ -0,0 +1,42 @@
+/*
+ * wm8711.h  --  WM8711 Soc Audio driver
+ *
+ * Copyright 2006 Wolfson Microelectronics
+ *
+ * Author: Mike Arthur <linux@wolfsonmicro.com>
+ *
+ * Based on wm8731.h
+ *
+ * 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 _WM8711_H
+#define _WM8711_H
+
+/* WM8711 register space */
+
+#define WM8711_LOUT1V   0x02
+#define WM8711_ROUT1V   0x03
+#define WM8711_APANA    0x04
+#define WM8711_APDIGI   0x05
+#define WM8711_PWR      0x06
+#define WM8711_IFACE    0x07
+#define WM8711_SRATE    0x08
+#define WM8711_ACTIVE   0x09
+#define WM8711_RESET	0x0f
+
+#define WM8711_CACHEREGNUM 	8
+
+#define WM8711_SYSCLK	0
+#define WM8711_DAI		0
+
+struct wm8711_setup_data {
+	unsigned short i2c_address;
+};
+
+extern struct snd_soc_dai wm8711_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8711;
+
+#endif
diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c
new file mode 100644
index 0000000..d8ffbd6
--- /dev/null
+++ b/sound/soc/codecs/wm8727.c
@@ -0,0 +1,135 @@
+/*
+ * wm8727.c
+ *
+ *  Created on: 15-Oct-2009
+ *      Author: neil.jones@imgtec.com
+ *
+ * Copyright (C) 2009 Imagination Technologies Ltd.
+ *
+ *  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/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "wm8727.h"
+/*
+ * Note this is a simple chip with no configuration interface, sample rate is
+ * determined automatically by examining the Master clock and Bit clock ratios
+ */
+#define WM8727_RATES  (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\
+			SNDRV_PCM_RATE_192000)
+
+
+struct snd_soc_dai wm8727_dai = {
+	.name = "WM8727",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = WM8727_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+		},
+};
+EXPORT_SYMBOL_GPL(wm8727_dai);
+
+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);
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8727: failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	return ret;
+
+pcm_err:
+	kfree(socdev->card->codec);
+	socdev->card->codec = NULL;
+	return ret;
+}
+
+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;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8727 = {
+	.probe = 	wm8727_soc_probe,
+	.remove = 	wm8727_soc_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8727);
+
+
+static __devinit int wm8727_platform_probe(struct platform_device *pdev)
+{
+	wm8727_dai.dev = &pdev->dev;
+	return snd_soc_register_dai(&wm8727_dai);
+}
+
+static int __devexit wm8727_platform_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_dai(&wm8727_dai);
+	return 0;
+}
+
+static struct platform_driver wm8727_codec_driver = {
+	.driver = {
+			.name = "wm8727-codec",
+			.owner = THIS_MODULE,
+	},
+
+	.probe = wm8727_platform_probe,
+	.remove = __devexit_p(wm8727_platform_remove),
+};
+
+static int __init wm8727_init(void)
+{
+	return platform_driver_register(&wm8727_codec_driver);
+}
+module_init(wm8727_init);
+
+static void __exit wm8727_exit(void)
+{
+	platform_driver_unregister(&wm8727_codec_driver);
+}
+module_exit(wm8727_exit);
+
+MODULE_DESCRIPTION("ASoC wm8727 driver");
+MODULE_AUTHOR("Neil Jones");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8727.h b/sound/soc/codecs/wm8727.h
new file mode 100644
index 0000000..ee19aa7
--- /dev/null
+++ b/sound/soc/codecs/wm8727.h
@@ -0,0 +1,21 @@
+/*
+ * wm8727.h
+ *
+ *  Created on: 15-Oct-2009
+ *      Author: neil.jones@imgtec.com
+ *
+ * Copyright (C) 2009 Imagination Technologies Ltd.
+ *
+ *  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 WM8727_H_
+#define WM8727_H_
+
+extern struct snd_soc_dai wm8727_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8727;
+
+#endif /* WM8727_H_ */
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 16e969a..1252a8a 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -287,17 +287,9 @@
 	snd_soc_add_controls(codec, wm8728_snd_controls,
 				ARRAY_SIZE(wm8728_snd_controls));
 	wm8728_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "wm8728: failed to register card\n");
-		goto card_err;
-	}
 
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 err:
 	kfree(codec->reg_cache);
 	return ret;
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index d3fd4f2..e3675e7 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -19,6 +19,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -33,9 +34,18 @@
 static struct snd_soc_codec *wm8731_codec;
 struct snd_soc_codec_device soc_codec_dev_wm8731;
 
+#define WM8731_NUM_SUPPLIES 4
+static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
+	"AVDD",
+	"HPVDD",
+	"DCVDD",
+	"DBVDD",
+};
+
 /* codec private data */
 struct wm8731_priv {
 	struct snd_soc_codec codec;
+	struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
 	u16 reg_cache[WM8731_CACHEREGNUM];
 	unsigned int sysclk;
 };
@@ -422,9 +432,12 @@
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->card->codec;
+	struct wm8731_priv *wm8731 = codec->private_data;
 
 	snd_soc_write(codec, WM8731_ACTIVE, 0x0);
 	wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies),
+			       wm8731->supplies);
 	return 0;
 }
 
@@ -432,10 +445,16 @@
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->card->codec;
-	int i;
+	struct wm8731_priv *wm8731 = codec->private_data;
+	int i, ret;
 	u8 data[2];
 	u16 *cache = codec->reg_cache;
 
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+				    wm8731->supplies);
+	if (ret != 0)
+		return ret;
+
 	/* Sync reg_cache with the hardware */
 	for (i = 0; i < ARRAY_SIZE(wm8731_reg); i++) {
 		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
@@ -444,6 +463,7 @@
 	}
 	wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	wm8731_set_bias_level(codec, codec->suspend_bias_level);
+
 	return 0;
 }
 #else
@@ -475,17 +495,9 @@
 	snd_soc_add_controls(codec, wm8731_snd_controls,
 			     ARRAY_SIZE(wm8731_snd_controls));
 	wm8731_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
 
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	return ret;
 }
@@ -512,7 +524,7 @@
 static int wm8731_register(struct wm8731_priv *wm8731,
 			   enum snd_soc_control_type control)
 {
-	int ret;
+	int ret, i;
 	struct snd_soc_codec *codec = &wm8731->codec;
 
 	if (wm8731_codec) {
@@ -543,10 +555,27 @@
 		goto err;
 	}
 
+	for (i = 0; i < ARRAY_SIZE(wm8731->supplies); i++)
+		wm8731->supplies[i].supply = wm8731_supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8731->supplies),
+				 wm8731->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+		goto err;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8731->supplies),
+				    wm8731->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+		goto err_regulator_get;
+	}
+
 	ret = wm8731_reset(codec);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
-		goto err;
+		goto err_regulator_enable;
 	}
 
 	wm8731_dai.dev = codec->dev;
@@ -567,7 +596,7 @@
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-		goto err;
+		goto err_regulator_enable;
 	}
 
 	ret = snd_soc_register_dai(&wm8731_dai);
@@ -581,6 +610,10 @@
 
 err_codec:
 	snd_soc_unregister_codec(codec);
+err_regulator_enable:
+	regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+err_regulator_get:
+	regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
 err:
 	kfree(wm8731);
 	return ret;
@@ -591,6 +624,8 @@
 	wm8731_set_bias_level(&wm8731->codec, SND_SOC_BIAS_OFF);
 	snd_soc_unregister_dai(&wm8731_dai);
 	snd_soc_unregister_codec(&wm8731->codec);
+	regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
+	regulator_bulk_free(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
 	kfree(wm8731);
 	wm8731_codec = NULL;
 }
@@ -623,21 +658,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8731_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8731_spi_resume(struct spi_device *spi)
-{
-	return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8731_spi_suspend NULL
-#define wm8731_spi_resume NULL
-#endif
-
 static struct spi_driver wm8731_spi_driver = {
 	.driver = {
 		.name	= "wm8731",
@@ -645,8 +665,6 @@
 		.owner	= THIS_MODULE,
 	},
 	.probe		= wm8731_spi_probe,
-	.suspend	= wm8731_spi_suspend,
-	.resume		= wm8731_spi_resume,
 	.remove		= __devexit_p(wm8731_spi_remove),
 };
 #endif /* CONFIG_SPI_MASTER */
@@ -679,21 +697,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8731_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8731_i2c_resume(struct i2c_client *i2c)
-{
-	return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8731_i2c_suspend NULL
-#define wm8731_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8731_i2c_id[] = {
 	{ "wm8731", 0 },
 	{ }
@@ -707,8 +710,6 @@
 	},
 	.probe =    wm8731_i2c_probe,
 	.remove =   __devexit_p(wm8731_i2c_remove),
-	.suspend =  wm8731_i2c_suspend,
-	.resume =   wm8731_i2c_resume,
 	.id_table = wm8731_i2c_id,
 };
 #endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 4ba1e7e..50a3d65 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -772,16 +772,8 @@
 	snd_soc_add_controls(codec, wm8750_snd_controls,
 				ARRAY_SIZE(wm8750_snd_controls));
 	wm8750_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "wm8750: failed to register card\n");
-		goto card_err;
-	}
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 err:
 	kfree(codec->reg_cache);
 	return ret;
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 5ad677c..c652bc0 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -724,8 +724,8 @@
 	pll_div->k = K;
 }
 
-static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	u16 reg, enable;
 	int offset;
@@ -1583,18 +1583,9 @@
 	snd_soc_add_controls(codec, wm8753_snd_controls,
 			     ARRAY_SIZE(wm8753_snd_controls));
 	wm8753_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "wm8753: failed to register card\n");
-		goto card_err;
-	}
 
 	return 0;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
-
 pcm_err:
 	return ret;
 }
@@ -1767,21 +1758,6 @@
         return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8753_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8753_i2c_resume(struct i2c_client *client)
-{
-	return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8753_i2c_suspend NULL
-#define wm8753_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8753_i2c_id[] = {
 	{ "wm8753", 0 },
 	{ }
@@ -1795,8 +1771,6 @@
 	},
 	.probe =    wm8753_i2c_probe,
 	.remove =   wm8753_i2c_remove,
-	.suspend =  wm8753_i2c_suspend,
-	.resume =   wm8753_i2c_resume,
 	.id_table = wm8753_i2c_id,
 };
 #endif
@@ -1852,22 +1826,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8753_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8753_spi_resume(struct spi_device *spi)
-{
-	return snd_soc_resume_device(&spi->dev);
-}
-
-#else
-#define wm8753_spi_suspend NULL
-#define wm8753_spi_resume NULL
-#endif
-
 static struct spi_driver wm8753_spi_driver = {
 	.driver = {
 		.name	= "wm8753",
@@ -1876,8 +1834,6 @@
 	},
 	.probe		= wm8753_spi_probe,
 	.remove		= __devexit_p(wm8753_spi_remove),
-	.suspend	= wm8753_spi_suspend,
-	.resume		= wm8753_spi_resume,
 };
 #endif
 
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index a9829aa..ab2c0da 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -447,17 +447,8 @@
 				  ARRAY_SIZE(wm8776_dapm_widgets));
 	snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes));
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
-
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	return ret;
 }
@@ -616,21 +607,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8776_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8776_spi_resume(struct spi_device *spi)
-{
-	return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8776_spi_suspend NULL
-#define wm8776_spi_resume NULL
-#endif
-
 static struct spi_driver wm8776_spi_driver = {
 	.driver = {
 		.name	= "wm8776",
@@ -638,8 +614,6 @@
 		.owner	= THIS_MODULE,
 	},
 	.probe		= wm8776_spi_probe,
-	.suspend	= wm8776_spi_suspend,
-	.resume		= wm8776_spi_resume,
 	.remove		= __devexit_p(wm8776_spi_remove),
 };
 #endif /* CONFIG_SPI_MASTER */
@@ -673,21 +647,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8776_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&i2c->dev);
-}
-
-static int wm8776_i2c_resume(struct i2c_client *i2c)
-{
-	return snd_soc_resume_device(&i2c->dev);
-}
-#else
-#define wm8776_i2c_suspend NULL
-#define wm8776_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8776_i2c_id[] = {
 	{ "wm8776", 0 },
 	{ }
@@ -701,8 +660,6 @@
 	},
 	.probe =    wm8776_i2c_probe,
 	.remove =   __devexit_p(wm8776_i2c_remove),
-	.suspend =  wm8776_i2c_suspend,
-	.resume =   wm8776_i2c_resume,
 	.id_table = wm8776_i2c_id,
 };
 #endif
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 5e9c855..0d185cb 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -814,8 +814,8 @@
 	return 0;
 }
 
-static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out);
 }
@@ -1312,21 +1312,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8900_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8900_i2c_resume(struct i2c_client *client)
-{
-	return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8900_i2c_suspend NULL
-#define wm8900_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8900_i2c_id[] = {
 	{ "wm8900", 0 },
 	{ }
@@ -1340,8 +1325,6 @@
 	},
 	.probe = wm8900_i2c_probe,
 	.remove = __devexit_p(wm8900_i2c_remove),
-	.suspend = wm8900_i2c_suspend,
-	.resume = wm8900_i2c_resume,
 	.id_table = wm8900_i2c_id,
 };
 
@@ -1370,12 +1353,6 @@
 				ARRAY_SIZE(wm8900_snd_controls));
 	wm8900_add_widgets(codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "Failed to register card\n");
-		goto card_err;
-	}
-
 	return ret;
 
 card_err:
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index fe1307b..bfeff4e 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1655,21 +1655,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8903_i2c_resume(struct i2c_client *client)
-{
-	return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8903_i2c_suspend NULL
-#define wm8903_i2c_resume NULL
-#endif
-
 /* i2c codec control layer */
 static const struct i2c_device_id wm8903_i2c_id[] = {
        { "wm8903", 0 },
@@ -1684,8 +1669,6 @@
 	},
 	.probe    = wm8903_i2c_probe,
 	.remove   = __devexit_p(wm8903_i2c_remove),
-	.suspend  = wm8903_i2c_suspend,
-	.resume   = wm8903_i2c_resume,
 	.id_table = wm8903_i2c_id,
 };
 
@@ -1712,17 +1695,8 @@
 				ARRAY_SIZE(wm8903_snd_controls));
 	wm8903_add_widgets(socdev->card->codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "wm8903: failed to register card\n");
-		goto card_err;
-	}
-
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 err:
 	return ret;
 }
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 1ef2454..fc80aa6 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -536,8 +536,8 @@
 }
 
 /* Untested at the moment */
-static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 reg;
@@ -731,12 +731,6 @@
 	if (ret)
 		goto error_free_pcms;
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto error_free_pcms;
-	}
-
 	return ret;
 
 error_free_pcms:
@@ -877,21 +871,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8940_i2c_resume(struct i2c_client *client)
-{
-	return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8940_i2c_suspend NULL
-#define wm8940_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8940_i2c_id[] = {
 	{ "wm8940", 0 },
 	{ }
@@ -905,8 +884,6 @@
 	},
 	.probe = wm8940_i2c_probe,
 	.remove = __devexit_p(wm8940_i2c_remove),
-	.suspend = wm8940_i2c_suspend,
-	.resume = wm8940_i2c_resume,
 	.id_table = wm8940_i2c_id,
 };
 
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index f59703b..40390af 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -540,8 +540,8 @@
 	return 0;
 }
 
-static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 reg;
@@ -713,17 +713,9 @@
 	snd_soc_add_controls(codec, wm8960_snd_controls,
 			     ARRAY_SIZE(wm8960_snd_controls));
 	wm8960_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
 
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	return ret;
 }
@@ -883,21 +875,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8960_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8960_i2c_resume(struct i2c_client *client)
-{
-	return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8960_i2c_suspend NULL
-#define wm8960_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8960_i2c_id[] = {
 	{ "wm8960", 0 },
 	{ }
@@ -911,8 +888,6 @@
 	},
 	.probe =    wm8960_i2c_probe,
 	.remove =   __devexit_p(wm8960_i2c_remove),
-	.suspend =  wm8960_i2c_suspend,
-	.resume =   wm8960_i2c_resume,
 	.id_table = wm8960_i2c_id,
 };
 
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index 5030320..07e3895 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -988,17 +988,8 @@
 	snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
 	snd_soc_dapm_new_widgets(codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
-
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	return ret;
 }
@@ -1206,21 +1197,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8961_i2c_suspend(struct i2c_client *client, pm_message_t state)
-{
-	return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8961_i2c_resume(struct i2c_client *client)
-{
-	return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8961_i2c_suspend NULL
-#define wm8961_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8961_i2c_id[] = {
 	{ "wm8961", 0 },
 	{ }
@@ -1234,8 +1210,6 @@
 	},
 	.probe =    wm8961_i2c_probe,
 	.remove =   __devexit_p(wm8961_i2c_remove),
-	.suspend =  wm8961_i2c_suspend,
-	.resume =   wm8961_i2c_resume,
 	.id_table = wm8961_i2c_id,
 };
 
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index d66efb0..56a66e8 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -703,16 +703,9 @@
 	snd_soc_add_controls(codec, wm8971_snd_controls,
 				ARRAY_SIZE(wm8971_snd_controls));
 	wm8971_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "wm8971: failed to register card\n");
-		goto card_err;
-	}
+
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 err:
 	kfree(codec->reg_cache);
 	return ret;
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 98d663a..c245f0e 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -281,36 +281,38 @@
 }
 
 struct pll_ {
-	unsigned int pre_div:4; /* prescale - 1 */
+	unsigned int pre_div:1;
 	unsigned int n:4;
 	unsigned int k;
 };
 
-static struct pll_ pll_div;
-
 /* The size in bits of the pll divide multiplied by 10
  * to allow rounding later */
 #define FIXED_PLL_SIZE ((1 << 24) * 10)
 
-static void pll_factors(unsigned int target, unsigned int source)
+static void pll_factors(struct pll_ *pll_div,
+			unsigned int target, unsigned int source)
 {
 	unsigned long long Kpart;
 	unsigned int K, Ndiv, Nmod;
 
+	/* There is a fixed divide by 4 in the output path */
+	target *= 4;
+
 	Ndiv = target / source;
 	if (Ndiv < 6) {
-		source >>= 1;
-		pll_div.pre_div = 1;
+		source /= 2;
+		pll_div->pre_div = 1;
 		Ndiv = target / source;
 	} else
-		pll_div.pre_div = 0;
+		pll_div->pre_div = 0;
 
 	if ((Ndiv < 6) || (Ndiv > 12))
 		printk(KERN_WARNING
 			"WM8974 N value %u outwith recommended range!\n",
 			Ndiv);
 
-	pll_div.n = Ndiv;
+	pll_div->n = Ndiv;
 	Nmod = target % source;
 	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
 
@@ -325,13 +327,14 @@
 	/* Move down to proper range now rounding is done */
 	K /= 10;
 
-	pll_div.k = K;
+	pll_div->k = K;
 }
 
-static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
+	struct pll_ pll_div;
 	u16 reg;
 
 	if (freq_in == 0 || freq_out == 0) {
@@ -345,7 +348,7 @@
 		return 0;
 	}
 
-	pll_factors(freq_out*4, freq_in);
+	pll_factors(&pll_div, freq_out, freq_in);
 
 	snd_soc_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
 	snd_soc_write(codec, WM8974_PLLK1, pll_div.k >> 18);
@@ -638,17 +641,9 @@
 	snd_soc_add_controls(codec, wm8974_snd_controls,
 			     ARRAY_SIZE(wm8974_snd_controls));
 	wm8974_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
 
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	return ret;
 }
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 3f530f8..bee292e 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -792,17 +792,8 @@
 	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
 	snd_soc_dapm_new_widgets(codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
-
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	return ret;
 }
@@ -944,21 +935,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8988_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm8988_i2c_resume(struct i2c_client *client)
-{
-	return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm8988_i2c_suspend NULL
-#define wm8988_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm8988_i2c_id[] = {
 	{ "wm8988", 0 },
 	{ }
@@ -972,8 +948,6 @@
 	},
 	.probe = wm8988_i2c_probe,
 	.remove = wm8988_i2c_remove,
-	.suspend = wm8988_i2c_suspend,
-	.resume = wm8988_i2c_resume,
 	.id_table = wm8988_i2c_id,
 };
 #endif
@@ -1006,21 +980,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8988_spi_suspend(struct spi_device *spi, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&spi->dev);
-}
-
-static int wm8988_spi_resume(struct spi_device *spi)
-{
-	return snd_soc_resume_device(&spi->dev);
-}
-#else
-#define wm8988_spi_suspend NULL
-#define wm8988_spi_resume NULL
-#endif
-
 static struct spi_driver wm8988_spi_driver = {
 	.driver = {
 		.name	= "wm8988",
@@ -1029,8 +988,6 @@
 	},
 	.probe		= wm8988_spi_probe,
 	.remove		= __devexit_p(wm8988_spi_remove),
-	.suspend	= wm8988_spi_suspend,
-	.resume		= wm8988_spi_resume,
 };
 #endif
 
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 2d702db..e43cb2c 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -972,8 +972,8 @@
 	pll_div->k = K;
 }
 
-static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	u16 reg;
 	struct snd_soc_codec *codec = codec_dai->codec;
@@ -1409,16 +1409,9 @@
 	snd_soc_add_controls(codec, wm8990_snd_controls,
 				ARRAY_SIZE(wm8990_snd_controls));
 	wm8990_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "wm8990: failed to register card\n");
-		goto card_err;
-	}
+
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	kfree(codec->reg_cache);
 	return ret;
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index d998799..0d4d2be 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -422,7 +422,7 @@
 	return 0;
 }
 
-static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id,
+static int wm8993_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;
@@ -1466,17 +1466,8 @@
 
 	snd_soc_dapm_new_widgets(codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card\n");
-		goto card_err;
-	}
-
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 err:
 	return ret;
 }
@@ -1572,33 +1563,15 @@
 	/* Use automatic clock configuration */
 	snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);
 
-	if (!wm8993->pdata.lineout1_diff)
-		snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
-				    WM8993_LINEOUT1_MODE,
-				    WM8993_LINEOUT1_MODE);
-	if (!wm8993->pdata.lineout2_diff)
-		snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
-				    WM8993_LINEOUT2_MODE,
-				    WM8993_LINEOUT2_MODE);
-
-	if (wm8993->pdata.lineout1fb)
-		snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
-				    WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
-
-	if (wm8993->pdata.lineout2fb)
-		snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
-				    WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
-
-	/* Apply the microphone bias/detection configuration - the
-	 * platform data is directly applicable to the register. */
-	snd_soc_update_bits(codec, WM8993_MICBIAS,
-			    WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
-			    WM8993_MICB1_LVL | WM8993_MICB2_LVL,
-			    wm8993->pdata.jd_scthr << WM8993_JD_SCTHR_SHIFT |
-			    wm8993->pdata.jd_thr << WM8993_JD_THR_SHIFT |
-			    wm8993->pdata.micbias1_lvl |
-			    wm8993->pdata.micbias1_lvl << 1);
-
+	wm_hubs_handle_analogue_pdata(codec, wm8993->pdata.lineout1_diff,
+				      wm8993->pdata.lineout2_diff,
+				      wm8993->pdata.lineout1fb,
+				      wm8993->pdata.lineout2fb,
+				      wm8993->pdata.jd_scthr,
+				      wm8993->pdata.jd_thr,
+				      wm8993->pdata.micbias1_lvl,
+				      wm8993->pdata.micbias2_lvl);
+			     
 	ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	if (ret != 0)
 		goto err;
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 686e5aa..3f1f844 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1264,17 +1264,8 @@
 	snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
 	snd_soc_dapm_new_widgets(codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		dev_err(codec->dev, "failed to register card: %d\n", ret);
-		goto card_err;
-	}
-
 	return ret;
 
-card_err:
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
 pcm_err:
 	return ret;
 }
@@ -1452,21 +1443,6 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm9081_i2c_suspend(struct i2c_client *client, pm_message_t msg)
-{
-	return snd_soc_suspend_device(&client->dev);
-}
-
-static int wm9081_i2c_resume(struct i2c_client *client)
-{
-	return snd_soc_resume_device(&client->dev);
-}
-#else
-#define wm9081_i2c_suspend NULL
-#define wm9081_i2c_resume NULL
-#endif
-
 static const struct i2c_device_id wm9081_i2c_id[] = {
 	{ "wm9081", 0 },
 	{ }
@@ -1480,8 +1456,6 @@
 	},
 	.probe =    wm9081_i2c_probe,
 	.remove =   __devexit_p(wm9081_i2c_remove),
-	.suspend =  wm9081_i2c_suspend,
-	.resume =   wm9081_i2c_resume,
 	.id_table = wm9081_i2c_id,
 };
 
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index e7d2840..0e817b8 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -403,16 +403,8 @@
 				ARRAY_SIZE(wm9705_snd_ac97_controls));
 	wm9705_add_widgets(codec);
 
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "wm9705: failed to register card\n");
-		goto reset_err;
-	}
-
 	return 0;
 
-reset_err:
-	snd_soc_free_pcms(socdev);
 pcm_err:
 	snd_soc_free_ac97_codec(codec);
 codec_err:
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 1fd4e88..155cacf 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -695,17 +695,9 @@
 	snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
 				ARRAY_SIZE(wm9712_snd_ac97_controls));
 	wm9712_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0) {
-		printk(KERN_ERR "wm9712: failed to register card\n");
-		goto reset_err;
-	}
 
 	return 0;
 
-reset_err:
-	snd_soc_free_pcms(socdev);
-
 pcm_err:
 	snd_soc_free_ac97_codec(codec);
 
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index abed37a..5f81ecd 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -800,8 +800,8 @@
 	return 0;
 }
 
-static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
@@ -1247,13 +1247,8 @@
 	snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
 				ARRAY_SIZE(wm9713_snd_ac97_controls));
 	wm9713_add_widgets(codec);
-	ret = snd_soc_init_card(socdev);
-	if (ret < 0)
-		goto reset_err;
-	return 0;
 
-reset_err:
-	snd_soc_free_pcms(socdev);
+	return 0;
 
 pcm_err:
 	snd_soc_free_ac97_codec(codec);
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index e542027..810a563 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -738,6 +738,41 @@
 }
 EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
 
+int wm_hubs_handle_analogue_pdata(struct snd_soc_codec *codec,
+				  int lineout1_diff, int lineout2_diff,
+				  int lineout1fb, int lineout2fb,
+				  int jd_scthr, int jd_thr, int micbias1_lvl,
+				  int micbias2_lvl)
+{
+	if (!lineout1_diff)
+		snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
+				    WM8993_LINEOUT1_MODE,
+				    WM8993_LINEOUT1_MODE);
+	if (!lineout2_diff)
+		snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
+				    WM8993_LINEOUT2_MODE,
+				    WM8993_LINEOUT2_MODE);
+
+	if (lineout1fb)
+		snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+				    WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
+
+	if (lineout2fb)
+		snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+				    WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
+
+	snd_soc_update_bits(codec, WM8993_MICBIAS,
+			    WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
+			    WM8993_MICB1_LVL | WM8993_MICB2_LVL,
+			    jd_scthr << WM8993_JD_SCTHR_SHIFT |
+			    jd_thr << WM8993_JD_THR_SHIFT |
+			    micbias1_lvl |
+			    micbias2_lvl << WM8993_MICB2_LVL_SHIFT);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_handle_analogue_pdata);
+
 MODULE_DESCRIPTION("Shared support for Wolfson hubs products");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
index ec09cb6..36d3fba 100644
--- a/sound/soc/codecs/wm_hubs.h
+++ b/sound/soc/codecs/wm_hubs.h
@@ -20,5 +20,10 @@
 
 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 *,
+					 int lineout1_diff, int lineout2_diff,
+					 int lineout1fb, int lineout2fb,
+					 int jd_scthr, int jd_thr,
+					 int micbias1_lvl, int micbias2_lvl);
 
 #endif
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 4dfd4ad..047ee39 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -13,9 +13,9 @@
 	tristate
 
 config SND_DAVINCI_SOC_EVM
-	tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM"
+	tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
 	depends on SND_DAVINCI_SOC
-	depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM
+	depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM  || MACH_DAVINCI_DM365_EVM
 	select SND_DAVINCI_SOC_I2S
 	select SND_SOC_TLV320AIC3X
 	help
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 67414f6..7ccbe66 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -45,7 +45,8 @@
 	unsigned sysclk;
 
 	/* ASP1 on DM355 EVM is clocked by an external oscillator */
-	if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm())
+	if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() ||
+	    machine_is_davinci_dm365_evm())
 		sysclk = 27000000;
 
 	/* ASP0 in DM6446 EVM is clocked by U55, as configured by
@@ -176,7 +177,7 @@
 	.ops = &evm_ops,
 };
 
-/* davinci-evm audio machine driver */
+/* davinci dm6446, dm355 or dm365 evm audio machine driver */
 static struct snd_soc_card snd_soc_card_evm = {
 	.name = "DaVinci EVM",
 	.platform = &davinci_soc_platform,
@@ -243,7 +244,7 @@
 	int index;
 	int ret;
 
-	if (machine_is_davinci_evm()) {
+	if (machine_is_davinci_evm() || machine_is_davinci_dm365_evm()) {
 		evm_snd_dev_data = &evm_snd_devdata;
 		index = 0;
 	} else if (machine_is_davinci_dm355_evm()) {
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index 4ae7070..2ab8093 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -397,6 +397,8 @@
 	}
 
 	dma_params->acnt  = dma_params->data_type;
+	dma_params->fifo_level = 0;
+
 	rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
 	xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
 
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 5d1f98a..50ad051 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -714,16 +714,13 @@
 	struct davinci_pcm_dma_params *dma_params =
 					&dev->dma_params[substream->stream];
 	int word_length;
-	u8 numevt;
+	u8 fifo_level;
 
 	davinci_hw_common_param(dev, substream->stream);
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		numevt = dev->txnumevt;
+		fifo_level = dev->txnumevt;
 	else
-		numevt = dev->rxnumevt;
-
-	if (!numevt)
-		numevt = 1;
+		fifo_level = dev->rxnumevt;
 
 	if (dev->op_mode == DAVINCI_MCASP_DIT_MODE)
 		davinci_hw_dit_param(dev);
@@ -751,12 +748,12 @@
 		return -EINVAL;
 	}
 
-	if (dev->version == MCASP_VERSION_2) {
-		dma_params->data_type *= numevt;
-		dma_params->acnt = 4 * numevt;
-	} else
+	if (dev->version == MCASP_VERSION_2 && !fifo_level)
+		dma_params->acnt = 4;
+	else
 		dma_params->acnt = dma_params->data_type;
 
+	dma_params->fifo_level = fifo_level;
 	davinci_config_channel_size(dev, word_length);
 
 	return 0;
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index c73a915..fb10f1d 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -66,38 +66,53 @@
 	dma_addr_t dma_pos;
 	dma_addr_t src, dst;
 	unsigned short src_bidx, dst_bidx;
+	unsigned short src_cidx, dst_cidx;
 	unsigned int data_type;
 	unsigned short acnt;
 	unsigned int count;
+	unsigned int fifo_level;
 
 	period_size = snd_pcm_lib_period_bytes(substream);
 	dma_offset = prtd->period * period_size;
 	dma_pos = runtime->dma_addr + dma_offset;
+	fifo_level = prtd->params->fifo_level;
 
 	pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d "
 		"dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
 
 	data_type = prtd->params->data_type;
 	count = period_size / data_type;
+	if (fifo_level)
+		count /= fifo_level;
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		src = dma_pos;
 		dst = prtd->params->dma_addr;
 		src_bidx = data_type;
 		dst_bidx = 0;
+		src_cidx = data_type * fifo_level;
+		dst_cidx = 0;
 	} else {
 		src = prtd->params->dma_addr;
 		dst = dma_pos;
 		src_bidx = 0;
 		dst_bidx = data_type;
+		src_cidx = 0;
+		dst_cidx = data_type * fifo_level;
 	}
 
 	acnt = prtd->params->acnt;
 	edma_set_src(lch, src, INCR, W8BIT);
 	edma_set_dest(lch, dst, INCR, W8BIT);
-	edma_set_src_index(lch, src_bidx, 0);
-	edma_set_dest_index(lch, dst_bidx, 0);
-	edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
+
+	edma_set_src_index(lch, src_bidx, src_cidx);
+	edma_set_dest_index(lch, dst_bidx, dst_cidx);
+
+	if (!fifo_level)
+		edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
+	else
+		edma_set_transfer_params(lch, acnt, fifo_level, count,
+							fifo_level, ABSYNC);
 
 	prtd->period++;
 	if (unlikely(prtd->period >= runtime->periods))
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
index 8746606..c8b0d2b 100644
--- a/sound/soc/davinci/davinci-pcm.h
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -23,6 +23,7 @@
 	enum dma_event_q eventq_no;	/* event queue number */
 	unsigned char data_type;	/* xfer data type */
 	unsigned char convert_mono_stereo;
+	unsigned int fifo_level;
 };
 
 
diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c
index e4dcb53..0267d2d 100644
--- a/sound/soc/imx/mx27vis_wm8974.c
+++ b/sound/soc/imx/mx27vis_wm8974.c
@@ -157,7 +157,7 @@
 
 
 	/* codec PLL input is 25 MHz */
-	ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG,
+	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");
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 653a362..bb5731a 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -66,6 +66,15 @@
 	help
 	  Say Y if you want to add support for SoC audio on the omap3evm board.
 
+config SND_OMAP_SOC_AM3517EVM
+	tristate "SoC Audio support for OMAP3517 / AM3517 EVM"
+	depends on SND_OMAP_SOC && MACH_OMAP3517EVM && I2C
+	select SND_OMAP_SOC_MCBSP
+	select SND_SOC_TLV320AIC23
+	help
+	  Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517
+	  EVM.
+
 config SND_OMAP_SOC_SDP3430
 	tristate "SoC Audio support for Texas Instruments SDP3430"
 	depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 02d6947..0c78ae4 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -12,6 +12,7 @@
 snd-soc-overo-objs := overo.o
 snd-soc-omap2evm-objs := omap2evm.o
 snd-soc-omap3evm-objs := omap3evm.o
+snd-soc-am3517evm-objs := am3517evm.o
 snd-soc-sdp3430-objs := sdp3430.o
 snd-soc-omap3pandora-objs := omap3pandora.o
 snd-soc-omap3beagle-objs := omap3beagle.o
@@ -23,6 +24,7 @@
 obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
 obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
 obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o
+obj-$(CONFIG_MACH_OMAP3517EVM) += snd-soc-am3517evm.o
 obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c
new file mode 100644
index 0000000..135901b
--- /dev/null
+++ b/sound/soc/omap/am3517evm.c
@@ -0,0 +1,202 @@
+/*
+ * am3517evm.c  -- ALSA SoC support for OMAP3517 / AM3517 EVM
+ *
+ * Author: Anuj Aggarwal <anuj.aggarwal@ti.com>
+ *
+ * Based on sound/soc/omap/beagle.c by Steve Sakoman
+ *
+ * Copyright (C) 2009 Texas Instruments Incorporated
+ *
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <plat/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+
+#include "../codecs/tlv320aic23.h"
+
+#define CODEC_CLOCK 	12000000
+
+static int am3517evm_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;
+	int ret;
+
+	/* Set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai,
+				  SND_SOC_DAIFMT_DSP_B |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set codec DAI configuration\n");
+		return ret;
+	}
+
+	/* Set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai,
+				  SND_SOC_DAIFMT_DSP_B |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set cpu DAI configuration\n");
+		return ret;
+	}
+
+	/* Set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+			CODEC_CLOCK, SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set codec system clock\n");
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_CLKR_SRC_CLKX, 0,
+				SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_CLKR_SRC_CLKX\n");
+		return ret;
+	}
+
+	snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_FSR_SRC_FSX, 0,
+				SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set CPU system clock OMAP_MCBSP_FSR_SRC_FSX\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops am3517evm_ops = {
+	.hw_params = am3517evm_hw_params,
+};
+
+/* am3517evm machine dapm widgets */
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Line Out", NULL),
+	SND_SOC_DAPM_LINE("Line In", NULL),
+	SND_SOC_DAPM_MIC("Mic In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Line Out connected to LLOUT, RLOUT */
+	{"Line Out", NULL, "LOUT"},
+	{"Line Out", NULL, "ROUT"},
+
+	{"LLINEIN", NULL, "Line In"},
+	{"RLINEIN", NULL, "Line In"},
+
+	{"MICIN", NULL, "Mic In"},
+};
+
+static int am3517evm_aic23_init(struct snd_soc_codec *codec)
+{
+	/* Add am3517-evm specific widgets */
+	snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
+				  ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+	/* Set up davinci-evm specific audio path audio_map */
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	/* always connected */
+	snd_soc_dapm_enable_pin(codec, "Line Out");
+	snd_soc_dapm_enable_pin(codec, "Line In");
+	snd_soc_dapm_enable_pin(codec, "Mic In");
+
+	snd_soc_dapm_sync(codec);
+
+	return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link am3517evm_dai = {
+	.name = "TLV320AIC23",
+	.stream_name = "AIC23",
+	.cpu_dai = &omap_mcbsp_dai[0],
+	.codec_dai = &tlv320aic23_dai,
+	.init = am3517evm_aic23_init,
+	.ops = &am3517evm_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_am3517evm = {
+	.name = "am3517evm",
+	.platform = &omap_soc_platform,
+	.dai_link = &am3517evm_dai,
+	.num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device am3517evm_snd_devdata = {
+	.card = &snd_soc_am3517evm,
+	.codec_dev = &soc_codec_dev_tlv320aic23,
+};
+
+static struct platform_device *am3517evm_snd_device;
+
+static int __init am3517evm_soc_init(void)
+{
+	int ret;
+
+	if (!machine_is_omap3517evm()) {
+		pr_err("Not OMAP3517 / AM3517 EVM!\n");
+		return -ENODEV;
+	}
+	pr_info("OMAP3517 / AM3517 EVM SoC init\n");
+
+	am3517evm_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!am3517evm_snd_device) {
+		printk(KERN_ERR "Platform device allocation failed\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(am3517evm_snd_device, &am3517evm_snd_devdata);
+	am3517evm_snd_devdata.dev = &am3517evm_snd_device->dev;
+	*(unsigned int *)am3517evm_dai.cpu_dai->private_data = 0; /* McBSP1 */
+
+	ret = platform_device_add(am3517evm_snd_device);
+	if (ret)
+		goto err1;
+
+	return 0;
+
+err1:
+	printk(KERN_ERR "Unable to add platform device\n");
+	platform_device_put(am3517evm_snd_device);
+
+	return ret;
+}
+
+static void __exit am3517evm_soc_exit(void)
+{
+	platform_device_unregister(am3517evm_snd_device);
+}
+
+module_init(am3517evm_soc_init);
+module_exit(am3517evm_soc_exit);
+
+MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3517 / AM3517 EVM");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c
index 5a5166a..ae0fc9b 100644
--- a/sound/soc/omap/ams-delta.c
+++ b/sound/soc/omap/ams-delta.c
@@ -40,7 +40,7 @@
 
 
 /* Board specific DAPM widgets */
- const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
 	/* Handset */
 	SND_SOC_DAPM_MIC("Mouthpiece", NULL),
 	SND_SOC_DAPM_HP("Earpiece", NULL),
@@ -81,7 +81,7 @@
 						(1 << AMS_DELTA_SPEAKER))
 #define AMS_DELTA_SPEAKERPHONE	(AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC))
 
-unsigned short ams_delta_audio_mode_pins[] = {
+static const unsigned short ams_delta_audio_mode_pins[] = {
 	AMS_DELTA_MIXED,
 	AMS_DELTA_HANDSET,
 	AMS_DELTA_HANDSFREE,
diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c
index 9114c26..8deb59b 100644
--- a/sound/soc/omap/omap3evm.c
+++ b/sound/soc/omap/omap3evm.c
@@ -93,10 +93,17 @@
 	.num_links = 1,
 };
 
+/* twl4030 setup */
+static struct twl4030_setup_data twl4030_setup = {
+	.ramp_delay_value = 4,
+	.sysclk = 26000,
+};
+
 /* Audio subsystem */
 static struct snd_soc_device omap3evm_snd_devdata = {
 	.card = &snd_soc_omap3evm,
 	.codec_dev = &soc_codec_dev_twl4030,
+	.codec_data = &twl4030_setup,
 };
 
 static struct platform_device *omap3evm_snd_device;
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index dcb3181b..d4f4031 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -90,7 +90,8 @@
 
 config SND_PXA2XX_SOC_EM_X270
 	tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300"
-	depends on SND_PXA2XX_SOC && MACH_EM_X270
+	depends on SND_PXA2XX_SOC && (MACH_EM_X270 || MACH_EXEDA || \
+			MACH_CM_X300)
 	select SND_PXA2XX_SOC_AC97
 	select SND_SOC_WM9712
 	help
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
index 9f7c61e..4c8d99a 100644
--- a/sound/soc/pxa/magician.c
+++ b/sound/soc/pxa/magician.c
@@ -213,7 +213,7 @@
 		return ret;
 
 	/* set SSP audio pll clock */
-	ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, acps);
+	ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, acps);
 	if (ret < 0)
 		return ret;
 
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index d11a6d7..3bd7712 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -305,8 +305,8 @@
 /*
  * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
  */
-static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
-	int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
+	int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct ssp_priv *priv = cpu_dai->private_data;
 	struct ssp_device *ssp = priv->dev.ssp;
@@ -760,13 +760,13 @@
 		.resume = pxa_ssp_resume,
 		.playback = {
 			.channels_min = 1,
-			.channels_max = 2,
+			.channels_max = 8,
 			.rates = PXA_SSP_RATES,
 			.formats = PXA_SSP_FORMATS,
 		},
 		.capture = {
 			 .channels_min = 1,
-			 .channels_max = 2,
+			 .channels_max = 8,
 			.rates = PXA_SSP_RATES,
 			.formats = PXA_SSP_FORMATS,
 		 },
@@ -780,13 +780,13 @@
 		.resume = pxa_ssp_resume,
 		.playback = {
 			.channels_min = 1,
-			.channels_max = 2,
+			.channels_max = 8,
 			.rates = PXA_SSP_RATES,
 			.formats = PXA_SSP_FORMATS,
 		},
 		.capture = {
 			.channels_min = 1,
-			.channels_max = 2,
+			.channels_max = 8,
 			.rates = PXA_SSP_RATES,
 			.formats = PXA_SSP_FORMATS,
 		 },
@@ -801,13 +801,13 @@
 		.resume = pxa_ssp_resume,
 		.playback = {
 			.channels_min = 1,
-			.channels_max = 2,
+			.channels_max = 8,
 			.rates = PXA_SSP_RATES,
 			.formats = PXA_SSP_FORMATS,
 		},
 		.capture = {
 			.channels_min = 1,
-			.channels_max = 2,
+			.channels_max = 8,
 			.rates = PXA_SSP_RATES,
 			.formats = PXA_SSP_FORMATS,
 		 },
@@ -822,13 +822,13 @@
 		.resume = pxa_ssp_resume,
 		.playback = {
 			.channels_min = 1,
-			.channels_max = 2,
+			.channels_max = 8,
 			.rates = PXA_SSP_RATES,
 			.formats = PXA_SSP_FORMATS,
 		},
 		.capture = {
 			.channels_min = 1,
-			.channels_max = 2,
+			.channels_max = 8,
 			.rates = PXA_SSP_RATES,
 			.formats = PXA_SSP_FORMATS,
 		 },
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
index 9a386b4..dd678ae 100644
--- a/sound/soc/pxa/zylonite.c
+++ b/sound/soc/pxa/zylonite.c
@@ -74,7 +74,8 @@
 static int zylonite_wm9713_init(struct snd_soc_codec *codec)
 {
 	if (clk_pout)
-		snd_soc_dai_set_pll(&codec->dai[0], 0, clk_get_rate(pout), 0);
+		snd_soc_dai_set_pll(&codec->dai[0], 0, 0,
+				    clk_get_rate(pout), 0);
 
 	snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets,
 				  ARRAY_SIZE(zylonite_dapm_widgets));
@@ -128,7 +129,7 @@
 	if (ret < 0)
 		return ret;
 
-	ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out);
+	ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, pll_out);
 	if (ret < 0)
 		return ret;
 
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index 923428f..d7912f1 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -56,6 +56,15 @@
 	help
 	  Sat Y if you want to add support for SoC audio on the Jive.
 
+config SND_S3C64XX_SOC_WM8580
+	tristate "SoC I2S Audio support for WM8580 on SMDK64XX"
+	depends on SND_S3C24XX_SOC && (MACH_SMDK6400 || MACH_SMDK6410)
+	depends on BROKEN
+	select SND_SOC_WM8580
+	select SND_S3C64XX_SOC_I2S
+	help
+	  Sat Y if you want to add support for SoC audio on the SMDK64XX.
+
 config SND_S3C24XX_SOC_SMDK2443_WM9710
 	tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
 	depends on SND_S3C24XX_SOC && MACH_SMDK2443
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index 99f5a7d..7790406 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -23,6 +23,7 @@
 snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
 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
 
 obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -33,4 +34,5 @@
 obj-$(CONFIG_SND_S3C24XX_SOC_SIMTEC) += snd-soc-s3c24xx-simtec.o
 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
 
diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
index 0c52e36..26409a9 100644
--- a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
@@ -119,7 +119,7 @@
 		return ret;
 
 	/* codec PLL input is PCLK/4 */
-	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
 		iis_clkrate / 4, pll_out);
 	if (ret < 0)
 		return ret;
@@ -133,7 +133,7 @@
 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 
 	/* disable the PLL */
-	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
 }
 
 /*
@@ -183,7 +183,7 @@
 		return ret;
 
 	/* configue and enable PLL for 12.288MHz output */
-	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
 		iis_clkrate / 4, 12288000);
 	if (ret < 0)
 		return ret;
@@ -197,7 +197,7 @@
 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 
 	/* disable the PLL */
-	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
 }
 
 static struct snd_soc_ops neo1973_gta02_voice_ops = {
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 906709e..77de6c5 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -29,7 +29,6 @@
 #include <mach/regs-clock.h>
 #include <mach/regs-gpio.h>
 #include <mach/hardware.h>
-#include <plat/audio.h>
 #include <linux/io.h>
 #include <mach/spi-gpio.h>
 
@@ -137,7 +136,7 @@
 		return ret;
 
 	/* codec PLL input is PCLK/4 */
-	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
 		iis_clkrate / 4, pll_out);
 	if (ret < 0)
 		return ret;
@@ -153,7 +152,7 @@
 	pr_debug("Entered %s\n", __func__);
 
 	/* disable the PLL */
-	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0, 0);
 }
 
 /*
@@ -203,7 +202,7 @@
 		return ret;
 
 	/* configue and enable PLL for 12.288MHz output */
-	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
 		iis_clkrate / 4, 12288000);
 	if (ret < 0)
 		return ret;
@@ -219,7 +218,7 @@
 	pr_debug("Entered %s\n", __func__);
 
 	/* disable the PLL */
-	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0, 0);
 }
 
 static struct snd_soc_ops neo1973_voice_ops = {
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c
index 9bc4aa3..28b0ab2 100644
--- a/sound/soc/s3c24xx/s3c-i2s-v2.c
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.c
@@ -32,7 +32,6 @@
 
 #include <plat/regs-s3c2412-iis.h>
 
-#include <plat/audio.h>
 #include <mach/dma.h>
 
 #include "s3c-i2s-v2.h"
@@ -312,12 +311,15 @@
 
 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 	case SND_SOC_DAIFMT_RIGHT_J:
+		iismod |= S3C2412_IISMOD_LR_RLOW;
 		iismod |= S3C2412_IISMOD_SDF_MSB;
 		break;
 	case SND_SOC_DAIFMT_LEFT_J:
+		iismod |= S3C2412_IISMOD_LR_RLOW;
 		iismod |= S3C2412_IISMOD_SDF_LSB;
 		break;
 	case SND_SOC_DAIFMT_I2S:
+		iismod &= ~S3C2412_IISMOD_LR_RLOW;
 		iismod |= S3C2412_IISMOD_SDF_IIS;
 		break;
 	default:
@@ -467,6 +469,31 @@
 
 	switch (div_id) {
 	case S3C_I2SV2_DIV_BCLK:
+		if (div > 3) {
+			/* convert value to bit field */
+
+			switch (div) {
+			case 16:
+				div = S3C2412_IISMOD_BCLK_16FS;
+				break;
+
+			case 32:
+				div = S3C2412_IISMOD_BCLK_32FS;
+				break;
+
+			case 24:
+				div = S3C2412_IISMOD_BCLK_24FS;
+				break;
+
+			case 48:
+				div = S3C2412_IISMOD_BCLK_48FS;
+				break;
+
+			default:
+				return -EINVAL;
+			}
+		}
+
 		reg = readl(i2s->regs + S3C2412_IISMOD);
 		reg &= ~S3C2412_IISMOD_BCLK_MASK;
 		writel(reg | div, i2s->regs + S3C2412_IISMOD);
@@ -626,7 +653,7 @@
 	}
 
 	i2s->iis_pclk = clk_get(dev, "iis");
-	if (i2s->iis_pclk == NULL) {
+	if (IS_ERR(i2s->iis_pclk)) {
 		dev_err(dev, "failed to get iis_clock\n");
 		iounmap(i2s->regs);
 		return -ENOENT;
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index a587ec4..ac5e47b 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -34,7 +34,6 @@
 
 #include <plat/regs-s3c2412-iis.h>
 
-#include <plat/audio.h>
 #include <mach/regs-gpio.h>
 #include <mach/dma.h>
 
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index fc1beb0..b25e9f9 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -32,7 +32,6 @@
 #include <plat/regs-ac97.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-clock.h>
-#include <plat/audio.h>
 #include <asm/dma.h>
 #include <mach/dma.h>
 
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index 40e2c47..c76b8bb 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -32,7 +32,7 @@
 #include <mach/hardware.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-clock.h>
-#include <plat/audio.h>
+
 #include <asm/dma.h>
 #include <mach/dma.h>
 
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index 1f35c6f..151a694 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -29,7 +29,6 @@
 #include <asm/dma.h>
 #include <mach/hardware.h>
 #include <mach/dma.h>
-#include <plat/audio.h>
 
 #include "s3c24xx-pcm.h"
 
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c
index 105a77e..d68cae1 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.c
@@ -31,7 +31,6 @@
 #include <plat/gpio-bank-d.h>
 #include <plat/gpio-bank-e.h>
 #include <plat/gpio-cfg.h>
-#include <plat/audio.h>
 
 #include <mach/map.h>
 #include <mach/dma.h>
@@ -99,6 +98,19 @@
 		iismod |= S3C64XX_IISMOD_IMS_SYSMUX;
 		break;
 
+	case S3C64XX_CLKSRC_CDCLK:
+		switch (dir) {
+		case SND_SOC_CLOCK_IN:
+			iismod |= S3C64XX_IISMOD_CDCLKCON;
+			break;
+		case SND_SOC_CLOCK_OUT:
+			iismod &= ~S3C64XX_IISMOD_CDCLKCON;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
 	default:
 		return -EINVAL;
 	}
@@ -111,8 +123,12 @@
 struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai)
 {
 	struct s3c_i2sv2_info *i2s = to_info(dai);
+	u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
 
-	return i2s->iis_cclk;
+	if (iismod & S3C64XX_IISMOD_IMS_SYSMUX)
+		return i2s->iis_cclk;
+	else
+		return i2s->iis_pclk;
 }
 EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock);
 
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h
index 02148ce..abe7253 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.h
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.h
@@ -25,6 +25,7 @@
 
 #define S3C64XX_CLKSRC_PCLK	(0)
 #define S3C64XX_CLKSRC_MUX	(1)
+#define S3C64XX_CLKSRC_CDCLK    (2)
 
 extern struct snd_soc_dai s3c64xx_i2s_dai[];
 
diff --git a/sound/soc/s3c24xx/smdk64xx_wm8580.c b/sound/soc/s3c24xx/smdk64xx_wm8580.c
new file mode 100644
index 0000000..cb8a916
--- /dev/null
+++ b/sound/soc/s3c24xx/smdk64xx_wm8580.c
@@ -0,0 +1,268 @@
+/*
+ *  smdk64xx_wm8580.c
+ *
+ *  Copyright (c) 2009 Samsung Electronics Co. Ltd
+ *  Author: Jaswinder Singh <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/platform_device.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm8580.h"
+#include "s3c24xx-pcm.h"
+#include "s3c64xx-i2s.h"
+
+#define S3C64XX_I2S_V4 2
+
+/* SMDK64XX has a 12MHZ crystal attached to WM8580 */
+#define SMDK64XX_WM8580_FREQ 12000000
+
+static int smdk64xx_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	unsigned int pll_out;
+	int bfs, rfs, ret;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_U8:
+	case SNDRV_PCM_FORMAT_S8:
+		bfs = 16;
+		break;
+	case SNDRV_PCM_FORMAT_U16_LE:
+	case SNDRV_PCM_FORMAT_S16_LE:
+		bfs = 32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* The Fvco for WM8580 PLLs must fall within [90,100]MHz.
+	 * This criterion can't be met if we request PLL output
+	 * as {8000x256, 64000x256, 11025x256}Hz.
+	 * As a wayout, we rather change rfs to a minimum value that
+	 * results in (params_rate(params) * rfs), and itself, acceptable
+	 * to both - the CODEC and the CPU.
+	 */
+	switch (params_rate(params)) {
+	case 16000:
+	case 22050:
+	case 32000:
+	case 44100:
+	case 48000:
+	case 88200:
+	case 96000:
+		rfs = 256;
+		break;
+	case 64000:
+		rfs = 384;
+		break;
+	case 8000:
+	case 11025:
+		rfs = 512;
+		break;
+	default:
+		return -EINVAL;
+	}
+	pll_out = params_rate(params) * rfs;
+
+	/* Set the Codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+					 | SND_SOC_DAIFMT_NB_NF
+					 | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* Set the AP DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
+					 | SND_SOC_DAIFMT_NB_NF
+					 | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_CDCLK,
+					0, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* We use PCLK for basic ops in SoC-Slave mode */
+	ret = snd_soc_dai_set_sysclk(cpu_dai, S3C64XX_CLKSRC_PCLK,
+					0, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* Set WM8580 to drive MCLK from its PLLA */
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK,
+					WM8580_CLKSRC_PLLA);
+	if (ret < 0)
+		return ret;
+
+	/* Explicitly set WM8580-DAC to source from MCLK */
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_DAC_CLKSEL,
+					WM8580_CLKSRC_MCLK);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_pll(codec_dai, 0, WM8580_PLLA,
+					SMDK64XX_WM8580_FREQ, pll_out);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_BCLK, bfs);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C_I2SV2_DIV_RCLK, rfs);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/*
+ * SMDK64XX WM8580 DAI operations.
+ */
+static struct snd_soc_ops smdk64xx_ops = {
+	.hw_params = smdk64xx_hw_params,
+};
+
+/* SMDK64xx Playback widgets */
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets_pbk[] = {
+	SND_SOC_DAPM_HP("Front-L/R", NULL),
+	SND_SOC_DAPM_HP("Center/Sub", NULL),
+	SND_SOC_DAPM_HP("Rear-L/R", NULL),
+};
+
+/* SMDK64xx Capture widgets */
+static const struct snd_soc_dapm_widget wm8580_dapm_widgets_cpt[] = {
+	SND_SOC_DAPM_MIC("MicIn", NULL),
+	SND_SOC_DAPM_LINE("LineIn", NULL),
+};
+
+/* SMDK-PAIFTX connections */
+static const struct snd_soc_dapm_route audio_map_tx[] = {
+	/* MicIn feeds AINL */
+	{"AINL", NULL, "MicIn"},
+
+	/* LineIn feeds AINL/R */
+	{"AINL", NULL, "LineIn"},
+	{"AINR", NULL, "LineIn"},
+};
+
+/* SMDK-PAIFRX connections */
+static const struct snd_soc_dapm_route audio_map_rx[] = {
+	/* Front Left/Right are fed VOUT1L/R */
+	{"Front-L/R", NULL, "VOUT1L"},
+	{"Front-L/R", NULL, "VOUT1R"},
+
+	/* Center/Sub are fed VOUT2L/R */
+	{"Center/Sub", NULL, "VOUT2L"},
+	{"Center/Sub", NULL, "VOUT2R"},
+
+	/* Rear Left/Right are fed VOUT3L/R */
+	{"Rear-L/R", NULL, "VOUT3L"},
+	{"Rear-L/R", NULL, "VOUT3R"},
+};
+
+static int smdk64xx_wm8580_init_paiftx(struct snd_soc_codec *codec)
+{
+	/* Add smdk64xx specific Capture widgets */
+	snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_cpt,
+				  ARRAY_SIZE(wm8580_dapm_widgets_cpt));
+
+	/* Set up PAIFTX audio path */
+	snd_soc_dapm_add_routes(codec, audio_map_tx, ARRAY_SIZE(audio_map_tx));
+
+	/* Enabling the microphone requires the fitting of a 0R
+	 * resistor to connect the line from the microphone jack.
+	 */
+	snd_soc_dapm_disable_pin(codec, "MicIn");
+
+	/* signal a DAPM event */
+	snd_soc_dapm_sync(codec);
+
+	return 0;
+}
+
+static int smdk64xx_wm8580_init_paifrx(struct snd_soc_codec *codec)
+{
+	/* Add smdk64xx specific Playback widgets */
+	snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets_pbk,
+				  ARRAY_SIZE(wm8580_dapm_widgets_pbk));
+
+	/* Set up PAIFRX audio path */
+	snd_soc_dapm_add_routes(codec, audio_map_rx, ARRAY_SIZE(audio_map_rx));
+
+	/* signal a DAPM event */
+	snd_soc_dapm_sync(codec);
+
+	return 0;
+}
+
+static struct snd_soc_dai_link smdk64xx_dai[] = {
+{ /* Primary Playback i/f */
+	.name = "WM8580 PAIF RX",
+	.stream_name = "Playback",
+	.cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4],
+	.codec_dai = &wm8580_dai[WM8580_DAI_PAIFRX],
+	.init = smdk64xx_wm8580_init_paifrx,
+	.ops = &smdk64xx_ops,
+},
+{ /* Primary Capture i/f */
+	.name = "WM8580 PAIF TX",
+	.stream_name = "Capture",
+	.cpu_dai = &s3c64xx_i2s_dai[S3C64XX_I2S_V4],
+	.codec_dai = &wm8580_dai[WM8580_DAI_PAIFTX],
+	.init = smdk64xx_wm8580_init_paiftx,
+	.ops = &smdk64xx_ops,
+},
+};
+
+static struct snd_soc_card smdk64xx = {
+	.name = "smdk64xx",
+	.platform = &s3c24xx_soc_platform,
+	.dai_link = smdk64xx_dai,
+	.num_links = ARRAY_SIZE(smdk64xx_dai),
+};
+
+static struct snd_soc_device smdk64xx_snd_devdata = {
+	.card = &smdk64xx,
+	.codec_dev = &soc_codec_dev_wm8580,
+};
+
+static struct platform_device *smdk64xx_snd_device;
+
+static int __init smdk64xx_audio_init(void)
+{
+	int ret;
+
+	smdk64xx_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!smdk64xx_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(smdk64xx_snd_device, &smdk64xx_snd_devdata);
+	smdk64xx_snd_devdata.dev = &smdk64xx_snd_device->dev;
+	ret = platform_device_add(smdk64xx_snd_device);
+
+	if (ret)
+		platform_device_put(smdk64xx_snd_device);
+
+	return ret;
+}
+module_init(smdk64xx_audio_init);
+
+MODULE_AUTHOR("Jaswinder Singh, jassi.brar@samsung.com");
+MODULE_DESCRIPTION("ALSA SoC SMDK64XX WM8580");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index 9154b43..9e69765 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -23,7 +23,6 @@
 config SND_SOC_SH4_FSI
 	tristate "SH4 FSI support"
 	depends on CPU_SUBTYPE_SH7724
-        select SH_DMA
 	help
 	  This option enables FSI sound support
 
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 4412324..e1a3d1a 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -26,8 +26,6 @@
 #include <sound/pcm_params.h>
 #include <sound/sh_fsi.h>
 #include <asm/atomic.h>
-#include <asm/dma.h>
-#include <asm/dma-sh.h>
 
 #define DO_FMT		0x0000
 #define DOFF_CTL	0x0004
@@ -97,7 +95,6 @@
 
 	int fifo_max;
 	int chan;
-	int dma_chan;
 
 	int byte_offset;
 	int period_len;
@@ -308,62 +305,6 @@
 	return residue;
 }
 
-static int fsi_get_residue(struct fsi_priv *fsi, int is_play)
-{
-	int residue;
-	int width;
-	struct snd_pcm_runtime *runtime;
-
-	runtime = fsi->substream->runtime;
-
-	/* get 1 channel data width */
-	width = frames_to_bytes(runtime, 1) / fsi->chan;
-
-	if (2 == width)
-		residue = fsi_get_fifo_residue(fsi, is_play);
-	else
-		residue = get_dma_residue(fsi->dma_chan);
-
-	return residue;
-}
-
-/************************************************************************
-
-
-		basic dma function
-
-
-************************************************************************/
-#define PORTA_DMA 0
-#define PORTB_DMA 1
-
-static int fsi_get_dma_chan(void)
-{
-	if (0 != request_dma(PORTA_DMA, "fsia"))
-		return -EIO;
-
-	if (0 != request_dma(PORTB_DMA, "fsib")) {
-		free_dma(PORTA_DMA);
-		return -EIO;
-	}
-
-	master->fsia.dma_chan = PORTA_DMA;
-	master->fsib.dma_chan = PORTB_DMA;
-
-	return 0;
-}
-
-static void fsi_free_dma_chan(void)
-{
-	dma_wait_for_completion(PORTA_DMA);
-	dma_wait_for_completion(PORTB_DMA);
-	free_dma(PORTA_DMA);
-	free_dma(PORTB_DMA);
-
-	master->fsia.dma_chan = -1;
-	master->fsib.dma_chan = -1;
-}
-
 /************************************************************************
 
 
@@ -435,44 +376,6 @@
 	mdelay(10);
 }
 
-static void fsi_16data_push(struct fsi_priv *fsi,
-			   struct snd_pcm_runtime *runtime,
-			   int send)
-{
-	u16 *dma_start;
-	u32 snd;
-	int i;
-
-	/* get dma start position for FSI */
-	dma_start = (u16 *)runtime->dma_area;
-	dma_start += fsi->byte_offset / 2;
-
-	/*
-	 * soft dma
-	 * FSI can not use DMA when 16bpp
-	 */
-	for (i = 0; i < send; i++) {
-		snd = (u32)dma_start[i];
-		fsi_reg_write(fsi, DODT, snd << 8);
-	}
-}
-
-static void fsi_32data_push(struct fsi_priv *fsi,
-			   struct snd_pcm_runtime *runtime,
-			   int send)
-{
-	u32 *dma_start;
-
-	/* get dma start position for FSI */
-	dma_start = (u32 *)runtime->dma_area;
-	dma_start += fsi->byte_offset / 4;
-
-	dma_wait_for_completion(fsi->dma_chan);
-	dma_configure_channel(fsi->dma_chan, (SM_INC|0x400|TS_32|TM_BUR));
-	dma_write(fsi->dma_chan, (u32)dma_start,
-		  (u32)(fsi->base + DODT), send * 4);
-}
-
 /* playback interrupt */
 static int fsi_data_push(struct fsi_priv *fsi)
 {
@@ -481,6 +384,8 @@
 	int send;
 	int fifo_free;
 	int width;
+	u8 *start;
+	int i;
 
 	if (!fsi			||
 	    !fsi->substream		||
@@ -515,12 +420,22 @@
 	if (fifo_free < send)
 		send = fifo_free;
 
-	if (2 == width)
-		fsi_16data_push(fsi, runtime, send);
-	else if (4 == width)
-		fsi_32data_push(fsi, runtime, send);
-	else
+	start = runtime->dma_area;
+	start += fsi->byte_offset;
+
+	switch (width) {
+	case 2:
+		for (i = 0; i < send; i++)
+			fsi_reg_write(fsi, DODT,
+				      ((u32)*((u16 *)start + i) << 8));
+		break;
+	case 4:
+		for (i = 0; i < send; i++)
+			fsi_reg_write(fsi, DODT, *((u32 *)start + i));
+		break;
+	default:
 		return -EINVAL;
+	}
 
 	fsi->byte_offset += send * width;
 
@@ -532,6 +447,75 @@
 	return 0;
 }
 
+static int fsi_data_pop(struct fsi_priv *fsi)
+{
+	struct snd_pcm_runtime *runtime;
+	struct snd_pcm_substream *substream = NULL;
+	int free;
+	int fifo_fill;
+	int width;
+	u8 *start;
+	int i;
+
+	if (!fsi			||
+	    !fsi->substream		||
+	    !fsi->substream->runtime)
+		return -EINVAL;
+
+	runtime = fsi->substream->runtime;
+
+	/* FSI FIFO has limit.
+	 * So, this driver can not send periods data at a time
+	 */
+	if (fsi->byte_offset >=
+	    fsi->period_len * (fsi->periods + 1)) {
+
+		substream = fsi->substream;
+		fsi->periods = (fsi->periods + 1) % runtime->periods;
+
+		if (0 == fsi->periods)
+			fsi->byte_offset = 0;
+	}
+
+	/* get 1 channel data width */
+	width = frames_to_bytes(runtime, 1) / fsi->chan;
+
+	/* get free space for alsa */
+	free = (fsi->buffer_len - fsi->byte_offset) / width;
+
+	/* get recv size */
+	fifo_fill = fsi_get_fifo_residue(fsi, 0);
+
+	if (free < fifo_fill)
+		fifo_fill = free;
+
+	start = runtime->dma_area;
+	start += fsi->byte_offset;
+
+	switch (width) {
+	case 2:
+		for (i = 0; i < fifo_fill; i++)
+			*((u16 *)start + i) =
+				(u16)(fsi_reg_read(fsi, DIDT) >> 8);
+		break;
+	case 4:
+		for (i = 0; i < fifo_fill; i++)
+			*((u32 *)start + i) = fsi_reg_read(fsi, DIDT);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	fsi->byte_offset += fifo_fill * width;
+
+	fsi_irq_enable(fsi, 0);
+
+	if (substream)
+		snd_pcm_period_elapsed(substream);
+
+	return 0;
+}
+
 static irqreturn_t fsi_interrupt(int irq, void *data)
 {
 	u32 status = fsi_master_read(SOFT_RST) & ~0x00000010;
@@ -545,6 +529,10 @@
 		fsi_data_push(&master->fsia);
 	if (int_st & INT_B_OUT)
 		fsi_data_push(&master->fsib);
+	if (int_st & INT_A_IN)
+		fsi_data_pop(&master->fsia);
+	if (int_st & INT_B_IN)
+		fsi_data_pop(&master->fsib);
 
 	fsi_master_write(INT_ST, 0x0000000);
 
@@ -664,8 +652,6 @@
 	}
 
 	fsi_reg_write(fsi, reg, data);
-	dev_dbg(dai->dev, "use %s format (%d channel) use %d DMAC\n",
-		msg, fsi->chan, fsi->dma_chan);
 
 	/*
 	 * clear clk reset if master mode
@@ -699,16 +685,12 @@
 	int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 	int ret = 0;
 
-	/* capture not supported */
-	if (!is_play)
-		return -ENODEV;
-
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 		fsi_stream_push(fsi, substream,
 				frames_to_bytes(runtime, runtime->buffer_size),
 				frames_to_bytes(runtime, runtime->period_size));
-		ret = fsi_data_push(fsi);
+		ret = is_play ? fsi_data_push(fsi) : fsi_data_pop(fsi);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 		fsi_irq_disable(fsi, is_play);
@@ -780,10 +762,9 @@
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
 	struct fsi_priv *fsi = fsi_get(substream);
-	int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
 	long location;
 
-	location = (fsi->byte_offset - 1) - fsi_get_residue(fsi, is_play);
+	location = (fsi->byte_offset - 1);
 	if (location < 0)
 		location = 0;
 
@@ -845,7 +826,12 @@
 			.channels_min	= 1,
 			.channels_max	= 8,
 		},
-		/* capture not supported */
+		.capture = {
+			.rates		= FSI_RATES,
+			.formats	= FSI_FMTS,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
 		.ops = &fsi_dai_ops,
 	},
 	{
@@ -857,7 +843,12 @@
 			.channels_min	= 1,
 			.channels_max	= 8,
 		},
-		/* capture not supported */
+		.capture = {
+			.rates		= FSI_RATES,
+			.formats	= FSI_FMTS,
+			.channels_min	= 1,
+			.channels_max	= 8,
+		},
 		.ops = &fsi_dai_ops,
 	},
 };
@@ -912,22 +903,13 @@
 	master->fsia.base	= master->base;
 	master->fsib.base	= master->base + 0x40;
 
-	master->fsia.dma_chan = -1;
-	master->fsib.dma_chan = -1;
-
-	ret = fsi_get_dma_chan();
-	if (ret < 0) {
-		dev_err(&pdev->dev, "cannot get dma api\n");
-		goto exit_iounmap;
-	}
-
 	/* FSI is based on SPU mstp */
 	snprintf(clk_name, sizeof(clk_name), "spu%d", pdev->id);
 	master->clk = clk_get(NULL, clk_name);
 	if (IS_ERR(master->clk)) {
 		dev_err(&pdev->dev, "cannot get %s mstp\n", clk_name);
 		ret = -EIO;
-		goto exit_free_dma;
+		goto exit_iounmap;
 	}
 
 	fsi_soc_dai[0].dev		= &pdev->dev;
@@ -938,7 +920,7 @@
 	ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master);
 	if (ret) {
 		dev_err(&pdev->dev, "irq request err\n");
-		goto exit_free_dma;
+		goto exit_iounmap;
 	}
 
 	ret = snd_soc_register_platform(&fsi_soc_platform);
@@ -951,8 +933,6 @@
 
 exit_free_irq:
 	free_irq(irq, master);
-exit_free_dma:
-	fsi_free_dma_chan();
 exit_iounmap:
 	iounmap(master->base);
 exit_kfree:
@@ -969,8 +949,6 @@
 
 	clk_put(master->clk);
 
-	fsi_free_dma_chan();
-
 	free_irq(master->irq, master);
 
 	iounmap(master->base);
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index c8ceddc..d2505e8 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -77,6 +77,35 @@
 #define snd_soc_7_9_spi_write NULL
 #endif
 
+static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
+			     unsigned int 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)
+		cache[reg] = value;
+
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
+				     unsigned int reg)
+{
+	u8 *cache = codec->reg_cache;
+	if (reg >= codec->reg_cache_size)
+		return -1;
+	return cache[reg];
+}
+
 static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
 			      unsigned int value)
 {
@@ -150,9 +179,20 @@
 	unsigned int (*read)(struct snd_soc_codec *, unsigned int);
 	unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
 } io_types[] = {
-	{ 7, 9, snd_soc_7_9_write, snd_soc_7_9_spi_write, snd_soc_7_9_read },
-	{ 8, 16, snd_soc_8_16_write, NULL, snd_soc_8_16_read,
-	  snd_soc_8_16_read_i2c },
+	{
+		.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 
+	},
+	{
+		.addr_bits = 8, .data_bits = 8,
+		.write = snd_soc_8_8_write, .read = snd_soc_8_8_read,
+	},
+	{
+		.addr_bits = 8, .data_bits = 16,
+		.write = snd_soc_8_16_write, .read = snd_soc_8_16_read,
+		.i2c_read = snd_soc_8_16_read_i2c,
+	},
 };
 
 /**
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 0a1b2f6..e2b6d75 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -37,7 +37,6 @@
 #include <sound/initval.h>
 
 static DEFINE_MUTEX(pcm_mutex);
-static DEFINE_MUTEX(io_mutex);
 static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
 
 #ifdef CONFIG_DEBUG_FS
@@ -81,6 +80,173 @@
 	return ret;
 }
 
+/* codec register dump */
+static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
+{
+	int i, step = 1, count = 0;
+
+	if (!codec->reg_cache_size)
+		return 0;
+
+	if (codec->reg_cache_step)
+		step = codec->reg_cache_step;
+
+	count += sprintf(buf, "%s registers\n", codec->name);
+	for (i = 0; i < codec->reg_cache_size; i += step) {
+		if (codec->readable_register && !codec->readable_register(i))
+			continue;
+
+		count += sprintf(buf + count, "%2x: ", i);
+		if (count >= PAGE_SIZE - 1)
+			break;
+
+		if (codec->display_register)
+			count += codec->display_register(codec, buf + count,
+							 PAGE_SIZE - count, i);
+		else
+			count += snprintf(buf + count, PAGE_SIZE - count,
+					  "%4x", codec->read(codec, i));
+
+		if (count >= PAGE_SIZE - 1)
+			break;
+
+		count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+		if (count >= PAGE_SIZE - 1)
+			break;
+	}
+
+	/* Truncate count; min() would cause a warning */
+	if (count >= PAGE_SIZE)
+		count = PAGE_SIZE - 1;
+
+	return count;
+}
+static ssize_t codec_reg_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct snd_soc_device *devdata = dev_get_drvdata(dev);
+	return soc_codec_reg_show(devdata->card->codec, buf);
+}
+
+static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
+
+#ifdef CONFIG_DEBUG_FS
+static int codec_reg_open_file(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
+			       size_t count, loff_t *ppos)
+{
+	ssize_t ret;
+	struct snd_soc_codec *codec = file->private_data;
+	char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+	ret = soc_codec_reg_show(codec, buf);
+	if (ret >= 0)
+		ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t codec_reg_write_file(struct file *file,
+		const char __user *user_buf, size_t count, loff_t *ppos)
+{
+	char buf[32];
+	int buf_size;
+	char *start = buf;
+	unsigned long reg, value;
+	int step = 1;
+	struct snd_soc_codec *codec = file->private_data;
+
+	buf_size = min(count, (sizeof(buf)-1));
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+	buf[buf_size] = 0;
+
+	if (codec->reg_cache_step)
+		step = codec->reg_cache_step;
+
+	while (*start == ' ')
+		start++;
+	reg = simple_strtoul(start, &start, 16);
+	if ((reg >= codec->reg_cache_size) || (reg % step))
+		return -EINVAL;
+	while (*start == ' ')
+		start++;
+	if (strict_strtoul(start, 16, &value))
+		return -EINVAL;
+	codec->write(codec, reg, value);
+	return buf_size;
+}
+
+static const struct file_operations codec_reg_fops = {
+	.open = codec_reg_open_file,
+	.read = codec_reg_read_file,
+	.write = codec_reg_write_file,
+};
+
+static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+	char codec_root[128];
+
+	if (codec->dev)
+		snprintf(codec_root, sizeof(codec_root),
+			"%s.%s", codec->name, dev_name(codec->dev));
+	else
+		snprintf(codec_root, sizeof(codec_root),
+			"%s", codec->name);
+
+	codec->debugfs_codec_root = debugfs_create_dir(codec_root,
+						       debugfs_root);
+	if (!codec->debugfs_codec_root) {
+		printk(KERN_WARNING
+		       "ASoC: Failed to create codec debugfs directory\n");
+		return;
+	}
+
+	codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
+						 codec->debugfs_codec_root,
+						 codec, &codec_reg_fops);
+	if (!codec->debugfs_reg)
+		printk(KERN_WARNING
+		       "ASoC: Failed to create codec register debugfs file\n");
+
+	codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
+						     codec->debugfs_codec_root,
+						     &codec->pop_time);
+	if (!codec->debugfs_pop_time)
+		printk(KERN_WARNING
+		       "Failed to create pop time debugfs file\n");
+
+	codec->debugfs_dapm = debugfs_create_dir("dapm",
+						 codec->debugfs_codec_root);
+	if (!codec->debugfs_dapm)
+		printk(KERN_WARNING
+		       "Failed to create DAPM debugfs directory\n");
+
+	snd_soc_dapm_debugfs_init(codec);
+}
+
+static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+{
+	debugfs_remove_recursive(codec->debugfs_codec_root);
+}
+
+#else
+
+static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+
+static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
+{
+}
+#endif
+
 #ifdef CONFIG_SND_SOC_AC97_BUS
 /* unregister ac97 codec */
 static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
@@ -790,45 +956,6 @@
 
 	return 0;
 }
-
-/**
- * snd_soc_suspend_device: Notify core of device suspend
- *
- * @dev: Device being suspended.
- *
- * In order to ensure that the entire audio subsystem is suspended in a
- * coordinated fashion ASoC devices should suspend themselves when
- * called by ASoC.  When the standard kernel suspend process asks the
- * device to suspend it should call this function to initiate a suspend
- * of the entire ASoC card.
- *
- * \note Currently this function is stubbed out.
- */
-int snd_soc_suspend_device(struct device *dev)
-{
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_suspend_device);
-
-/**
- * snd_soc_resume_device: Notify core of device resume
- *
- * @dev: Device being resumed.
- *
- * In order to ensure that the entire audio subsystem is resumed in a
- * coordinated fashion ASoC devices should resume themselves when called
- * by ASoC.  When the standard kernel resume process asks the device
- * to resume it should call this function.  Once all the components of
- * the card have notified that they are ready to be resumed the card
- * will be resumed.
- *
- * \note Currently this function is stubbed out.
- */
-int snd_soc_resume_device(struct device *dev)
-{
-	return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_resume_device);
 #else
 #define soc_suspend	NULL
 #define soc_resume	NULL
@@ -843,6 +970,7 @@
 						    struct platform_device,
 						    dev);
 	struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
+	struct snd_soc_codec *codec;
 	struct snd_soc_platform *platform;
 	struct snd_soc_dai *dai;
 	int i, found, ret, ac97;
@@ -931,6 +1059,7 @@
 		if (ret < 0)
 			goto cpu_dai_err;
 	}
+	codec = card->codec;
 
 	if (platform->probe) {
 		ret = platform->probe(pdev);
@@ -945,10 +1074,72 @@
 	INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
 #endif
 
+	for (i = 0; i < card->num_links; i++) {
+		if (card->dai_link[i].init) {
+			ret = card->dai_link[i].init(codec);
+			if (ret < 0) {
+				printk(KERN_ERR "asoc: failed to init %s\n",
+					card->dai_link[i].stream_name);
+				continue;
+			}
+		}
+		if (card->dai_link[i].codec_dai->ac97_control) {
+			ac97 = 1;
+			snd_ac97_dev_add_pdata(codec->ac97,
+				card->dai_link[i].cpu_dai->ac97_pdata);
+		}
+	}
+
+	snprintf(codec->card->shortname, sizeof(codec->card->shortname),
+		 "%s",  card->name);
+	snprintf(codec->card->longname, sizeof(codec->card->longname),
+		 "%s (%s)", card->name, codec->name);
+
+	/* Make sure all DAPM widgets are instantiated */
+	snd_soc_dapm_new_widgets(codec);
+
+	ret = snd_card_register(codec->card);
+	if (ret < 0) {
+		printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
+				codec->name);
+		goto card_err;
+	}
+
+	mutex_lock(&codec->mutex);
+#ifdef CONFIG_SND_SOC_AC97_BUS
+	/* Only instantiate AC97 if not already done by the adaptor
+	 * for the generic AC97 subsystem.
+	 */
+	if (ac97 && strcmp(codec->name, "AC97") != 0) {
+		ret = soc_ac97_dev_register(codec);
+		if (ret < 0) {
+			printk(KERN_ERR "asoc: AC97 device register failed\n");
+			snd_card_free(codec->card);
+			mutex_unlock(&codec->mutex);
+			goto card_err;
+		}
+	}
+#endif
+
+	ret = snd_soc_dapm_sys_add(card->socdev->dev);
+	if (ret < 0)
+		printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\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");
+
+	soc_init_codec_debugfs(codec);
+	mutex_unlock(&codec->mutex);
+
 	card->instantiated = 1;
 
 	return;
 
+card_err:
+	if (platform->remove)
+		platform->remove(pdev);
+
 platform_err:
 	if (codec_dev->remove)
 		codec_dev->remove(pdev);
@@ -1151,157 +1342,6 @@
 }
 EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
 
-/* codec register dump */
-static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
-{
-	int i, step = 1, count = 0;
-
-	if (!codec->reg_cache_size)
-		return 0;
-
-	if (codec->reg_cache_step)
-		step = codec->reg_cache_step;
-
-	count += sprintf(buf, "%s registers\n", codec->name);
-	for (i = 0; i < codec->reg_cache_size; i += step) {
-		if (codec->readable_register && !codec->readable_register(i))
-			continue;
-
-		count += sprintf(buf + count, "%2x: ", i);
-		if (count >= PAGE_SIZE - 1)
-			break;
-
-		if (codec->display_register)
-			count += codec->display_register(codec, buf + count,
-							 PAGE_SIZE - count, i);
-		else
-			count += snprintf(buf + count, PAGE_SIZE - count,
-					  "%4x", codec->read(codec, i));
-
-		if (count >= PAGE_SIZE - 1)
-			break;
-
-		count += snprintf(buf + count, PAGE_SIZE - count, "\n");
-		if (count >= PAGE_SIZE - 1)
-			break;
-	}
-
-	/* Truncate count; min() would cause a warning */
-	if (count >= PAGE_SIZE)
-		count = PAGE_SIZE - 1;
-
-	return count;
-}
-static ssize_t codec_reg_show(struct device *dev,
-	struct device_attribute *attr, char *buf)
-{
-	struct snd_soc_device *devdata = dev_get_drvdata(dev);
-	return soc_codec_reg_show(devdata->card->codec, buf);
-}
-
-static DEVICE_ATTR(codec_reg, 0444, codec_reg_show, NULL);
-
-#ifdef CONFIG_DEBUG_FS
-static int codec_reg_open_file(struct inode *inode, struct file *file)
-{
-	file->private_data = inode->i_private;
-	return 0;
-}
-
-static ssize_t codec_reg_read_file(struct file *file, char __user *user_buf,
-			       size_t count, loff_t *ppos)
-{
-	ssize_t ret;
-	struct snd_soc_codec *codec = file->private_data;
-	char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-	ret = soc_codec_reg_show(codec, buf);
-	if (ret >= 0)
-		ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
-	kfree(buf);
-	return ret;
-}
-
-static ssize_t codec_reg_write_file(struct file *file,
-		const char __user *user_buf, size_t count, loff_t *ppos)
-{
-	char buf[32];
-	int buf_size;
-	char *start = buf;
-	unsigned long reg, value;
-	int step = 1;
-	struct snd_soc_codec *codec = file->private_data;
-
-	buf_size = min(count, (sizeof(buf)-1));
-	if (copy_from_user(buf, user_buf, buf_size))
-		return -EFAULT;
-	buf[buf_size] = 0;
-
-	if (codec->reg_cache_step)
-		step = codec->reg_cache_step;
-
-	while (*start == ' ')
-		start++;
-	reg = simple_strtoul(start, &start, 16);
-	if ((reg >= codec->reg_cache_size) || (reg % step))
-		return -EINVAL;
-	while (*start == ' ')
-		start++;
-	if (strict_strtoul(start, 16, &value))
-		return -EINVAL;
-	codec->write(codec, reg, value);
-	return buf_size;
-}
-
-static const struct file_operations codec_reg_fops = {
-	.open = codec_reg_open_file,
-	.read = codec_reg_read_file,
-	.write = codec_reg_write_file,
-};
-
-static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
-{
-	codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
-						 debugfs_root, codec,
-						 &codec_reg_fops);
-	if (!codec->debugfs_reg)
-		printk(KERN_WARNING
-		       "ASoC: Failed to create codec register debugfs file\n");
-
-	codec->debugfs_pop_time = debugfs_create_u32("dapm_pop_time", 0744,
-						     debugfs_root,
-						     &codec->pop_time);
-	if (!codec->debugfs_pop_time)
-		printk(KERN_WARNING
-		       "Failed to create pop time debugfs file\n");
-
-	codec->debugfs_dapm = debugfs_create_dir("dapm", debugfs_root);
-	if (!codec->debugfs_dapm)
-		printk(KERN_WARNING
-		       "Failed to create DAPM debugfs directory\n");
-
-	snd_soc_dapm_debugfs_init(codec);
-}
-
-static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
-{
-	debugfs_remove_recursive(codec->debugfs_dapm);
-	debugfs_remove(codec->debugfs_pop_time);
-	debugfs_remove(codec->debugfs_reg);
-}
-
-#else
-
-static inline void soc_init_codec_debugfs(struct snd_soc_codec *codec)
-{
-}
-
-static inline void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
-{
-}
-#endif
-
 /**
  * snd_soc_new_ac97_codec - initailise AC97 device
  * @codec: audio codec
@@ -1369,19 +1409,41 @@
 	int change;
 	unsigned int old, new;
 
-	mutex_lock(&io_mutex);
 	old = snd_soc_read(codec, reg);
 	new = (old & ~mask) | value;
 	change = old != new;
 	if (change)
 		snd_soc_write(codec, reg, new);
 
-	mutex_unlock(&io_mutex);
 	return change;
 }
 EXPORT_SYMBOL_GPL(snd_soc_update_bits);
 
 /**
+ * snd_soc_update_bits_locked - update codec register bits
+ * @codec: audio codec
+ * @reg: codec register
+ * @mask: register mask
+ * @value: new value
+ *
+ * Writes new register value, and takes the codec mutex.
+ *
+ * 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 change;
+
+	mutex_lock(&codec->mutex);
+	change = snd_soc_update_bits(codec, reg, mask, value);
+	mutex_unlock(&codec->mutex);
+
+	return change;
+}
+
+/**
  * snd_soc_test_bits - test register for change
  * @codec: audio codec
  * @reg: codec register
@@ -1399,11 +1461,9 @@
 	int change;
 	unsigned int old, new;
 
-	mutex_lock(&io_mutex);
 	old = snd_soc_read(codec, reg);
 	new = (old & ~mask) | value;
 	change = old != new;
-	mutex_unlock(&io_mutex);
 
 	return change;
 }
@@ -1458,83 +1518,6 @@
 EXPORT_SYMBOL_GPL(snd_soc_new_pcms);
 
 /**
- * snd_soc_init_card - register sound card
- * @socdev: the SoC audio device
- *
- * Register a SoC sound card. Also registers an AC97 device if the
- * codec is AC97 for ad hoc devices.
- *
- * Returns 0 for success, else error.
- */
-int snd_soc_init_card(struct snd_soc_device *socdev)
-{
-	struct snd_soc_card *card = socdev->card;
-	struct snd_soc_codec *codec = card->codec;
-	int ret = 0, i, ac97 = 0, err = 0;
-
-	for (i = 0; i < card->num_links; i++) {
-		if (card->dai_link[i].init) {
-			err = card->dai_link[i].init(codec);
-			if (err < 0) {
-				printk(KERN_ERR "asoc: failed to init %s\n",
-					card->dai_link[i].stream_name);
-				continue;
-			}
-		}
-		if (card->dai_link[i].codec_dai->ac97_control) {
-			ac97 = 1;
-			snd_ac97_dev_add_pdata(codec->ac97,
-				card->dai_link[i].cpu_dai->ac97_pdata);
-		}
-	}
-	snprintf(codec->card->shortname, sizeof(codec->card->shortname),
-		 "%s",  card->name);
-	snprintf(codec->card->longname, sizeof(codec->card->longname),
-		 "%s (%s)", card->name, codec->name);
-
-	/* Make sure all DAPM widgets are instantiated */
-	snd_soc_dapm_new_widgets(codec);
-
-	ret = snd_card_register(codec->card);
-	if (ret < 0) {
-		printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
-				codec->name);
-		goto out;
-	}
-
-	mutex_lock(&codec->mutex);
-#ifdef CONFIG_SND_SOC_AC97_BUS
-	/* Only instantiate AC97 if not already done by the adaptor
-	 * for the generic AC97 subsystem.
-	 */
-	if (ac97 && strcmp(codec->name, "AC97") != 0) {
-		ret = soc_ac97_dev_register(codec);
-		if (ret < 0) {
-			printk(KERN_ERR "asoc: AC97 device register failed\n");
-			snd_card_free(codec->card);
-			mutex_unlock(&codec->mutex);
-			goto out;
-		}
-	}
-#endif
-
-	err = snd_soc_dapm_sys_add(socdev->dev);
-	if (err < 0)
-		printk(KERN_WARNING "asoc: failed to add dapm sysfs entries\n");
-
-	err = device_create_file(socdev->dev, &dev_attr_codec_reg);
-	if (err < 0)
-		printk(KERN_WARNING "asoc: failed to add codec sysfs files\n");
-
-	soc_init_codec_debugfs(codec);
-	mutex_unlock(&codec->mutex);
-
-out:
-	return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_init_card);
-
-/**
  * snd_soc_free_pcms - free sound card and pcms
  * @socdev: the SoC audio device
  *
@@ -1734,7 +1717,7 @@
 		mask |= (bitmask - 1) << e->shift_r;
 	}
 
-	return snd_soc_update_bits(codec, e->reg, mask, val);
+	return snd_soc_update_bits_locked(codec, e->reg, mask, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
 
@@ -1808,7 +1791,7 @@
 		mask |= e->mask << e->shift_r;
 	}
 
-	return snd_soc_update_bits(codec, e->reg, mask, val);
+	return snd_soc_update_bits_locked(codec, e->reg, mask, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
 
@@ -1969,7 +1952,7 @@
 		val_mask |= mask << rshift;
 		val |= val2 << rshift;
 	}
-	return snd_soc_update_bits(codec, reg, val_mask, val);
+	return snd_soc_update_bits_locked(codec, reg, val_mask, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
 
@@ -2075,11 +2058,11 @@
 	val = val << shift;
 	val2 = val2 << shift;
 
-	err = snd_soc_update_bits(codec, reg, val_mask, val);
+	err = snd_soc_update_bits_locked(codec, reg, val_mask, val);
 	if (err < 0)
 		return err;
 
-	err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+	err = snd_soc_update_bits_locked(codec, reg2, val_mask, val2);
 	return err;
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r);
@@ -2158,7 +2141,7 @@
 	val = (ucontrol->value.integer.value[0]+min) & 0xff;
 	val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
 
-	return snd_soc_update_bits(codec, reg, 0xffff, val);
+	return snd_soc_update_bits_locked(codec, reg, 0xffff, val);
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 
@@ -2205,16 +2188,18 @@
  * snd_soc_dai_set_pll - configure DAI PLL.
  * @dai: DAI
  * @pll_id: DAI specific PLL ID
+ * @source: DAI specific source for the PLL
  * @freq_in: PLL input clock frequency in Hz
  * @freq_out: requested PLL output clock frequency in Hz
  *
  * Configures and enables PLL to generate output clock based on input clock.
  */
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-	int pll_id, unsigned int freq_in, unsigned int freq_out)
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+	unsigned int freq_in, unsigned int freq_out)
 {
 	if (dai->ops && dai->ops->set_pll)
-		return dai->ops->set_pll(dai, pll_id, freq_in, freq_out);
+		return dai->ops->set_pll(dai, pll_id, source,
+					 freq_in, freq_out);
 	else
 		return -EINVAL;
 }
@@ -2259,6 +2244,30 @@
 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
 
 /**
+ * snd_soc_dai_set_channel_map - configure DAI audio channel map
+ * @dai: DAI
+ * @tx_num: how many TX channels
+ * @tx_slot: pointer to an array which imply the TX slot number channel
+ *           0~num-1 uses
+ * @rx_num: how many RX channels
+ * @rx_slot: pointer to an array which imply the RX slot number channel
+ *           0~num-1 uses
+ *
+ * configure the relationship between channel number and TDM slot number.
+ */
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+	unsigned int tx_num, unsigned int *tx_slot,
+	unsigned int rx_num, unsigned int *rx_slot)
+{
+	if (dai->ops && dai->ops->set_channel_map)
+		return dai->ops->set_channel_map(dai, tx_num, tx_slot,
+			rx_num, rx_slot);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map);
+
+/**
  * snd_soc_dai_set_tristate - configure DAI system or master clock.
  * @dai: DAI
  * @tristate: tristate enable
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index d89f6dc..eaadb4b 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -719,6 +719,10 @@
 
 	/* Check if one of our outputs is connected */
 	list_for_each_entry(path, &w->sinks, list_source) {
+		if (path->connected &&
+		    !path->connected(path->source, path->sink))
+			continue;
+
 		if (path->sink && path->sink->power_check &&
 		    path->sink->power_check(path->sink)) {
 			power = 1;
@@ -1138,6 +1142,9 @@
 				w->active ? "active" : "inactive");
 
 	list_for_each_entry(p, &w->sources, list_sink) {
+		if (p->connected && !p->connected(w, p->sink))
+			continue;
+
 		if (p->connect)
 			ret += snprintf(buf + ret, PAGE_SIZE - ret,
 					" in  %s %s\n",
@@ -1145,6 +1152,9 @@
 					p->source->name);
 	}
 	list_for_each_entry(p, &w->sinks, list_source) {
+		if (p->connected && !p->connected(w, p->sink))
+			continue;
+
 		if (p->connect)
 			ret += snprintf(buf + ret, PAGE_SIZE - ret,
 					" out %s %s\n",
@@ -1192,8 +1202,8 @@
 
 /* test and update the power status of a mux widget */
 static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
-				 struct snd_kcontrol *kcontrol, int mask,
-				 int mux, int val, struct soc_enum *e)
+				 struct snd_kcontrol *kcontrol, int change,
+				 int mux, struct soc_enum *e)
 {
 	struct snd_soc_dapm_path *path;
 	int found = 0;
@@ -1202,7 +1212,7 @@
 	    widget->id != snd_soc_dapm_value_mux)
 		return -ENODEV;
 
-	if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
+	if (!change)
 		return 0;
 
 	/* find dapm widget path assoc with kcontrol */
@@ -1387,10 +1397,13 @@
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
 static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
-	const char *sink, const char *control, const char *source)
+				  const struct snd_soc_dapm_route *route)
 {
 	struct snd_soc_dapm_path *path;
 	struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
+	const char *sink = route->sink;
+	const char *control = route->control;
+	const char *source = route->source;
 	int ret = 0;
 
 	/* find src and dest widgets */
@@ -1414,6 +1427,7 @@
 
 	path->source = wsource;
 	path->sink = wsink;
+	path->connected = route->connected;
 	INIT_LIST_HEAD(&path->list);
 	INIT_LIST_HEAD(&path->list_source);
 	INIT_LIST_HEAD(&path->list_sink);
@@ -1514,8 +1528,7 @@
 	int i, ret;
 
 	for (i = 0; i < num; i++) {
-		ret = snd_soc_dapm_add_route(codec, route->sink,
-					     route->control, route->source);
+		ret = snd_soc_dapm_add_route(codec, route);
 		if (ret < 0) {
 			printk(KERN_ERR "Failed to add route %s->%s\n",
 			       route->source,
@@ -1752,7 +1765,7 @@
 {
 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int val, mux;
+	unsigned int val, mux, change;
 	unsigned int mask, bitmask;
 	int ret = 0;
 
@@ -1772,20 +1785,21 @@
 
 	mutex_lock(&widget->codec->mutex);
 	widget->value = val;
-	dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
-	if (widget->event) {
-		if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
-			ret = widget->event(widget,
-				kcontrol, SND_SOC_DAPM_PRE_REG);
-			if (ret < 0)
-				goto out;
-		}
-		ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
-		if (widget->event_flags & SND_SOC_DAPM_POST_REG)
-			ret = widget->event(widget,
-				kcontrol, SND_SOC_DAPM_POST_REG);
-	} else
-		ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+	change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+	dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+	if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+		ret = widget->event(widget,
+				    kcontrol, SND_SOC_DAPM_PRE_REG);
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+
+	if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+		ret = widget->event(widget,
+				    kcontrol, SND_SOC_DAPM_POST_REG);
 
 out:
 	mutex_unlock(&widget->codec->mutex);
@@ -1794,6 +1808,54 @@
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
 
 /**
+ * snd_soc_dapm_get_enum_virt - Get virtual DAPM mux
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+
+	ucontrol->value.enumerated.item[0] = widget->value;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
+
+/**
+ * snd_soc_dapm_put_enum_virt - Set virtual DAPM mux
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+	struct soc_enum *e =
+		(struct soc_enum *)kcontrol->private_value;
+	int change;
+	int ret = 0;
+
+	if (ucontrol->value.enumerated.item[0] >= e->max)
+		return -EINVAL;
+
+	mutex_lock(&widget->codec->mutex);
+
+	change = widget->value != ucontrol->value.enumerated.item[0];
+	widget->value = ucontrol->value.enumerated.item[0];
+	dapm_mux_update_power(widget, kcontrol, change, widget->value, e);
+
+	mutex_unlock(&widget->codec->mutex);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
+
+/**
  * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get
  *					callback
  * @kcontrol: mixer control
@@ -1851,7 +1913,7 @@
 {
 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned int val, mux;
+	unsigned int val, mux, change;
 	unsigned int mask;
 	int ret = 0;
 
@@ -1869,20 +1931,21 @@
 
 	mutex_lock(&widget->codec->mutex);
 	widget->value = val;
-	dapm_mux_update_power(widget, kcontrol, mask, mux, val, e);
-	if (widget->event) {
-		if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
-			ret = widget->event(widget,
-				kcontrol, SND_SOC_DAPM_PRE_REG);
-			if (ret < 0)
-				goto out;
-		}
-		ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
-		if (widget->event_flags & SND_SOC_DAPM_POST_REG)
-			ret = widget->event(widget,
-				kcontrol, SND_SOC_DAPM_POST_REG);
-	} else
-		ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+	change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+	dapm_mux_update_power(widget, kcontrol, change, mux, e);
+
+	if (widget->event_flags & SND_SOC_DAPM_PRE_REG) {
+		ret = widget->event(widget,
+				    kcontrol, SND_SOC_DAPM_PRE_REG);
+		if (ret < 0)
+			goto out;
+	}
+
+	ret = snd_soc_update_bits(widget->codec, e->reg, mask, val);
+
+	if (widget->event_flags & SND_SOC_DAPM_POST_REG)
+		ret = widget->event(widget,
+				    kcontrol, SND_SOC_DAPM_POST_REG);
 
 out:
 	mutex_unlock(&widget->codec->mutex);
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 1d455ab7..1212414 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -58,7 +58,7 @@
  */
 void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask)
 {
-	struct snd_soc_codec *codec = jack->card->codec;
+	struct snd_soc_codec *codec;
 	struct snd_soc_jack_pin *pin;
 	int enable;
 	int oldstatus;
@@ -67,6 +67,7 @@
 		WARN_ON_ONCE(!jack);
 		return;
 	}
+	codec = jack->card->codec;
 
 	mutex_lock(&codec->mutex);