diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
new file mode 100644
index 0000000..ff7d725
--- /dev/null
+++ b/sound/soc/Kconfig
@@ -0,0 +1,30 @@
+#
+# SoC audio configuration
+#
+
+menu "SoC audio support"
+
+config SND_SOC
+	tristate "SoC audio support"
+	---help---
+
+	  If you want SoC support, you should say Y here and also to the
+	  specific driver for your SoC below. You will also need to select the 
+	  specific codec(s) attached to the SoC
+
+	  This SoC audio support can also be built as a module.  If so, the module
+	  will be called snd-soc-core.
+
+# All the supported Soc's
+menu "Soc Platforms"
+depends on SND_SOC
+source "sound/soc/pxa/Kconfig"
+endmenu
+
+# Supported codecs
+menu "Soc Codecs"
+depends on SND_SOC
+source "sound/soc/codecs/Kconfig"
+endmenu
+
+endmenu
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
new file mode 100644
index 0000000..11d39f5
--- /dev/null
+++ b/sound/soc/Makefile
@@ -0,0 +1,5 @@
+
+snd-soc-core-objs := soc-core.o soc-dpm.o
+
+obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
+obj-$(CONFIG_SND_SOC)	+= pxa/ codecs/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
new file mode 100644
index 0000000..44db053
--- /dev/null
+++ b/sound/soc/codecs/Kconfig
@@ -0,0 +1,36 @@
+config SND_SOC_AC97_CODEC
+	tristate "SoC AC97 support"
+	depends SND_SOC
+	help
+	  Say Y or M if you want to support AC97 codecs under ALSA SoC.
+
+config SND_SOC_WM8753
+	tristate "SoC driver for the WM8753 codec"
+	depends SND_SOC
+	help
+	  Say Y or M if you want to support the WM8753 codec.
+
+config SND_SOC_WM8731
+	tristate "SoC driver for the WM8731 codec"
+	depends SND_SOC
+	help
+	  Say Y or M if you want to support the WM8731 codec.
+ 
+config SND_SOC_WM8750
+	tristate "SoC driver for the WM8750 codec"
+	depends SND_SOC
+	help
+	  Say Y or M if you want to support the WM8750 codec.
+	  
+config SND_SOC_WM8971
+	tristate "SoC driver for the WM8971 codec"
+	depends SND_SOC
+	help
+	  Say Y or M if you want to support the WM8971 codec.
+
+config SND_SOC_WM9713_VOICE
+	tristate "SoC driver for the WM9713 Voice codec"
+	depends SND_SOC
+	depends SND_SOC_AC97_CODEC
+	help
+	  Say Y or M if you want to support the WM9713 voice codec.
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
new file mode 100644
index 0000000..9421304
--- /dev/null
+++ b/sound/soc/codecs/Makefile
@@ -0,0 +1,13 @@
+snd-soc-ac97-objs := ac97.o
+snd-soc-wm8753-objs := wm8753.o
+snd-soc-wm8731-objs := wm8731.o
+snd-soc-wm8750-objs := wm8750.o
+snd-soc-wm8971-objs := wm8971.o
+snd-soc-wm9713-voice-objs := wm9713-voice.o
+
+obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_WM8753)	+= snd-soc-wm8753.o
+obj-$(CONFIG_SND_SOC_WM8731)	+= snd-soc-wm8731.o
+obj-$(CONFIG_SND_SOC_WM8750)	+= snd-soc-wm8750.o
+obj-$(CONFIG_SND_SOC_WM8971)	+= snd-soc-wm8971.o
+obj-$(CONFIG_SND_SOC_WM9713_VOICE)	+= snd-soc-wm9713-voice.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
new file mode 100644
index 0000000..3278938
--- /dev/null
+++ b/sound/soc/codecs/ac97.c
@@ -0,0 +1,124 @@
+/*
+ * ac97.c  --  ALSA Soc AC97 codec support
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    17th Oct 2005   Initial version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+static int ac97_soc_add_widgets(struct snd_soc_codec * codec)
+{
+	return 0;
+}
+
+static void ac97_soc_device_release(struct device * dev)
+{
+}
+
+static int ac97_soc_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+	//snd_soc_pcm_interface_t *i = codec->interface;
+	//snd_ac97_suspend(i->ac97);
+	return 0;
+}	
+
+static int ac97_soc_resume(struct snd_soc_codec *codec)
+{
+	//snd_soc_pcm_interface_t *i = codec->interface;
+	//snd_ac97_resume(i->ac97);
+	return 0;
+}
+
+static struct snd_soc_pcm_codec ac97_soc_client = {
+	.name = "AC97 Audio",
+	.playback = {
+		.sname = "AC97 Playback",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,},
+	.capture = {
+		.sname = "AC97 Capture",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | 
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,},
+};
+
+static struct snd_soc_codec ac97_soc_codec;
+static snd_pcm_t *pcm_ac97;
+
+static int ac97_soc_probe(struct snd_soc_codec *codec)
+{
+	int ret;
+	
+//	if((ret = snd_soc_pcm_new(codec, 0, &ac97_soc_client, 
+//		&pcm_ac97, 1, 1, SND_SOC_AC97, 0)) < 0)
+//		return ret;
+	
+	ac97_soc_add_widgets(&ac97_soc_codec);
+
+	return ret;
+}
+
+/* 
+ * unregister interfaces and clean up
+ */
+static void ac97_soc_remove(struct snd_soc_codec *codec)
+{
+
+}
+
+static struct snd_soc_codec ac97_soc_codec = {
+	.name = "SoC AC97",
+	.longname = "ALSA SoC Generic AC97 Codec",
+	.owner = THIS_MODULE,
+	.pdev = {
+		.name		= "SoC AC97 Codec",
+		.id		= -1,
+		.dev		= {.release = ac97_soc_device_release},
+	},
+	.probe = ac97_soc_probe,
+	.remove = ac97_soc_remove,
+	.suspend = ac97_soc_suspend,
+	.resume = ac97_soc_resume,
+};	
+
+static int __init ac97_init(void)
+{	
+	return snd_soc_register_codec(&ac97_soc_codec);
+}
+
+static void __exit ac97_exit(void)
+{
+	snd_soc_unregister_codec(&ac97_soc_codec);
+}
+
+module_init(ac97_init);
+module_exit(ac97_exit);
+
+MODULE_DESCRIPTION("Soc Generic AC97 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
new file mode 100644
index 0000000..c88dacc
--- /dev/null
+++ b/sound/soc/codecs/wm8731.c
@@ -0,0 +1,574 @@
+/*
+ * wm8731.c  --  WM8731 Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpm.h>
+#include <sound/initval.h>
+
+#include "wm8731.h"
+
+#define AUDIO_NAME "wm8731"
+#define WM8731_VERSION "0.4"
+
+
+/*
+ * Debug
+ */
+ 
+#define PFX AUDIO_NAME
+#define WM8731_DEBUG 0
+
+#ifdef WM8731_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg)
+
+#define I2C_DRIVERID_WM8731 0xf2fe
+
+static unsigned short normal_i2c[] = { 0x1a, 0x1b,
+                                       I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8731_i2c_driver;
+static struct i2c_client client_template;
+
+/*
+ * wm8731 register cache
+ * We can't read the WM8731 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 wm8731_reg[WM8731_CACHEREGNUM] = {
+    0x0097, 0x0097, 0x0079, 0x0079, 
+    0x000a, 0x0008, 0x009f, 0x000a, 
+    0x0000, 0x0000
+};
+
+#define WM8731_HWFMT \
+	(SND_SOC_HWFMT_I2S | SND_SOC_HWFMT_LEFT_J | SND_SOC_HWFMT_RIGHT_J | \
+	SND_SOC_HWFMT_CBS_CFS | SND_SOC_HWFMT_CBM_CFM | \
+	SND_SOC_HWFMT_NB_NF | SND_SOC_HWFMT_NB_IF | SND_SOC_HWFMT_IB_NF | \
+	SND_SOC_HWFMT_IB_IF)
+
+/*
+ * priv1 is srate register setting
+ */
+static struct snd_soc_hw_mode wm8731_hwfmt[] = {
+
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 8000 ,  64, 0x000c},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 8000,   96, 0x000e},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 8000 , 128, 0x004c},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 8000,  192, 0x004e},
+
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 32000,  64, 0x0018},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 32000,  96, 0x001a},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 32000, 128, 0x0058},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 32000, 192, 0x005a},
+
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 44100,  64, 0x0020},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 44100,  96, 0x0022},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 44100, 128, 0x0060},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 44100, 192, 0x0062},
+
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 48000,  64, 0x0000},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 48000,  96, 0x0002},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 48000, 128, 0x0040},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 48000, 192, 0x0042},
+
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 88200,  32, 0x003c},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 88200,  48, 0x003e},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 88200,  64, 0x007c},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 88200,  96, 0x007e},
+
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 96000,  32, 0x001c},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 96000,  48, 0x001e},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 96000,  64, 0x005c},
+	{WM8731_HWFMT, SND_SOC_HWBITS(16), 96000,  96, 0x005e},
+};
+
+
+/*
+ * read wm8731 register cache
+ */
+static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec * codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg == WM8731_RESET) 
+		return 0;
+	if (reg > WM8731_CACHEREGNUM)
+		return -1;
+	return cache[reg];
+}
+
+/*
+ * write wm8731 register cache
+ */
+static inline void wm8731_write_reg_cache(struct snd_soc_codec * codec,
+	u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg > WM8731_CACHEREGNUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * write to the WM8731 register space
+ */
+static int wm8731_write(struct snd_soc_codec * codec, unsigned int reg, 
+	unsigned int value)
+{
+	u8 data[2];
+	struct i2c_client* i2c = (struct i2c_client*)codec->control_data;
+	/* data is 
+	 *   D15..D9 WM8731 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	wm8731_write_reg_cache (codec, reg, value);
+
+	if (i2c_master_send(i2c, data, 2) == 2)
+		return 0;
+	else
+		return -1;
+}
+
+#define wm8731_reset(c)	wm8731_write(c, WM8731_RESET, 0)
+
+static const char* wm8731_input_select[] = {"Line In", "Mic"};
+static const char* wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+
+static const struct soc_enum wm8731_enum[] = {
+	SOC_ENUM_SINGLE(WM8731_APANA, 2, 2, wm8731_input_select),
+	SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph),
+};
+
+static const snd_kcontrol_new_t wm8731_snd_controls[] = {
+
+SOC_SINGLE("Left Playback Volume", WM8731_LOUT1V, 0, 127, 0),
+SOC_SINGLE("Left Playback ZC Switch", WM8731_LOUT1V, 7, 1, 0),
+SOC_SINGLE("Right Playback Volume", WM8731_ROUT1V, 0, 127, 0),
+SOC_SINGLE("Right Playback ZC Switch", WM8731_ROUT1V, 7, 1, 0),
+
+SOC_SINGLE("Line Left Capture Volume", WM8731_LINVOL, 0, 31, 0),
+SOC_SINGLE("Line Left Capture Switch", WM8731_LINVOL, 7, 1, 1),
+SOC_SINGLE("Line Right Capture Volume", WM8731_RINVOL, 0, 31, 0),
+SOC_SINGLE("Line Right Capture Switch", WM8731_RINVOL, 7, 1, 1),
+
+SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0),
+SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 0),
+
+SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1),
+
+SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
+SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
+	
+SOC_ENUM("Playback De-emphasis", wm8731_enum[1]),
+};
+
+/* add non dpm controls */
+static int wm8731_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) {
+		if ((err = snd_ctl_add(codec->card, snd_soc_cnew(&wm8731_snd_controls[i],codec, NULL))) < 0)
+			return err;
+	}
+	
+	return 0;
+}
+
+
+/* Output Mixer */
+static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = {
+SOC_DPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
+SOC_DPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
+SOC_DPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
+};
+
+/* Input mux */
+static const snd_kcontrol_new_t wm8753_input_mux_controls = 
+SOC_DPM_ENUM("Input Select", wm8731_enum[0]);
+
+
+static const struct snd_soc_dpm_widget wm8731_dpm_widgets[] = {
+SND_SOC_DPM_CLOCK("WM8731 Clock", WM8731_PWR, 6, 1),
+SND_SOC_DPM_CLOCK("WM8731 Osc", WM8731_PWR, 5, 1),
+SND_SOC_DPM_MUTE("DAC Mute", WM8731_APDIGI, 3, 0),
+SND_SOC_DPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, &wm8731_output_mixer_controls[0], 
+	ARRAY_SIZE(wm8731_output_mixer_controls)),
+SND_SOC_DPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1, NULL, 0),
+SND_SOC_DPM_OUTPUT("LOUT"),
+SND_SOC_DPM_OUTPUT("LHPOUT"),
+SND_SOC_DPM_OUTPUT("ROUT"),
+SND_SOC_DPM_OUTPUT("LRHPOUT"),
+SND_SOC_DPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1, NULL, 0),
+SND_SOC_DPM_MUX("Input Mux", -1, 0, 0, &wm8753_input_mux_controls),
+SND_SOC_DPM_VOLUME("Line Input", WM8731_PWR, 0, 1, NULL, 0),
+SND_SOC_DPM_MICBIAS("Mic", WM8731_PWR, 1, 1),
+SND_SOC_DPM_INPUT("MICIN"),
+SND_SOC_DPM_INPUT("RLINEIN"),
+SND_SOC_DPM_INPUT("LLINEIN"),
+};
+
+static const char* intercon[][3] = {
+	/* output mixer */
+	{"Output Mixer", "Line Bypass Switch", "Line Input"},
+	{"Output Mixer", "HiFi Playback Switch", "DAC"},
+	{"Output Mixer", "Mic Sidetone Switch", "Mic"},
+	
+	/* outputs */
+	{"RHPOUT", NULL, "Output Mixer"},
+	{"ROUT", NULL, "Output Mixer"},
+	{"LHPOUT", NULL, "Output Mixer"},
+	{"LOUT", NULL, "Output Mixer"},
+	
+	/* input mux */
+	{"Input Mux", "Line In", "Line Input"},
+	{"Input Mux", "Mic", "Mic"},
+	
+	/* outputs */
+	{"Line Input", NULL, "LLINEIN"},
+	{"Line Input", NULL, "RLINEIN"},
+	{"Mic", NULL, "MICIN"},
+	
+	/* terminator */
+	{NULL, NULL, NULL},
+};
+
+static int wm8731_add_widgets(struct snd_soc_codec *codec)
+{
+	int i;
+	
+	for(i = 0; i < ARRAY_SIZE(wm8731_dpm_widgets); i++) {
+		snd_soc_dpm_new_control(codec, &wm8731_dpm_widgets[i]);
+	}
+	
+	/* set up audio path interconnects */
+	for(i = 0; intercon[i][0] != NULL; i++) {
+		snd_soc_dpm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]);
+	}
+	
+	snd_soc_dpm_sync(codec);
+	return 0;
+}
+
+static int wm8731_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_codec *codec = pcm_c->codec;
+	u16 iface = 0;
+	
+	/* set master/slave audio interface */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_CLOCK_MASK) {
+		case SND_SOC_HWFMT_CBM_CFM:
+			iface |= 0x0040;
+			break;
+		case SND_SOC_HWFMT_CBS_CFS:
+			break;
+	
+	}
+	
+	/* interface format */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_FORMAT_MASK) {
+		case SND_SOC_HWFMT_I2S:
+			iface |= 0x0002;
+			break;
+		case SND_SOC_HWFMT_RIGHT_J:
+			break;
+		case SND_SOC_HWFMT_LEFT_J:
+			iface |= 0x0001;
+			break;
+		case SND_SOC_HWFMT_DSP_A:
+			iface |= 0x0003;
+			break;
+		case SND_SOC_HWFMT_DSP_B:
+			iface |= 0x0013;
+			break;
+	}
+	
+	/* bit size */
+	switch(pcm_c->hw_runtime.hbits) {
+		case SND_SOC_HWBITS(16):
+			break;
+		case SND_SOC_HWBITS(20):
+			iface |= 0x0004;
+			break;
+		case SND_SOC_HWBITS(24):
+			iface |= 0x0008;
+			break;
+		case SND_SOC_HWBITS(32):
+			iface |= 0x000c;
+			break;
+	}
+
+	/* clock inversion */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_INV_MASK) {
+		case SND_SOC_HWFMT_NB_NF:
+			break;
+		case SND_SOC_HWFMT_IB_IF:
+			iface |= 0x0090;
+			break;
+		case SND_SOC_HWFMT_IB_NF:
+			iface |= 0x0080;
+			break;
+		case SND_SOC_HWFMT_NB_IF:
+			iface |= 0x0010;
+			break;
+	}
+	
+	if (wm8731_write(codec, WM8731_ACTIVE, 0x00) != 0)
+		return -EIO;
+
+	/* set rate */
+	wm8731_write(codec, WM8731_SRATE, pcm_c->hw_runtime.priv1);
+	wm8731_write(codec, WM8731_IFACE, iface);
+
+	return 0;
+}
+
+static void wm8731_device_release(struct device * dev)
+{
+}
+
+static int wm8731_dpm_event(struct snd_soc_codec *codec, int event)
+{
+	switch (event) {
+		case SNDRV_CTL_POWER_D0: /* full On */
+			/* vref/mid, clk and osc on, dac unmute, active */
+			wm8731_write(codec, WM8731_ACTIVE, 0x0001);
+			break;
+		case SNDRV_CTL_POWER_D1: /* partial On */
+		case SNDRV_CTL_POWER_D2: /* partial On */
+			break;
+		case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+			/* everything off except vref/vmid, dac mute, inactive */
+			wm8731_write(codec, WM8731_ACTIVE, 0x0);
+			wm8731_write(codec, WM8731_PWR, 0xff7f);
+			break;
+		case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+			/* everything off, dac mute, inactive */
+			wm8731_write(codec, WM8731_ACTIVE, 0x0);
+			wm8731_write(codec, WM8731_PWR, 0xffff);
+			break;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_pcm_codec wm8731_pcm_client = {
+	.name = "WM8731",
+	.playback = {
+		.sname = "Playback",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 |
+		SNDRV_PCM_RATE_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,
+	},
+	.nplayback = 1,
+	.capture = {
+		.sname = "Capture",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | 
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 |
+		SNDRV_PCM_RATE_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,
+	},
+	.ncapture = 1,
+	.ops = {
+		.prepare = wm8731_pcm_prepare,
+	},
+	.hw = {
+		.num_hmodes = ARRAY_SIZE(wm8731_hwfmt),
+		.hmodes = &wm8731_hwfmt[0],
+	},
+};
+
+/*
+ * initialise the WM8731 driver
+ * register the mixer and dsp interfaces with the kernel 
+ */
+static int wm8731_probe(struct snd_soc_codec *codec)
+{
+	int reg;
+	
+	wm8731_reset(codec);
+
+	wm8731_dpm_event(codec, SNDRV_CTL_POWER_D3hot);
+	
+	/* set the update bits */
+	reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
+	wm8731_write(codec, WM8731_LOUT1V, reg | 0x0100);
+	reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
+	wm8731_write(codec, WM8731_ROUT1V, reg | 0x0100);
+	reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
+	wm8731_write(codec, WM8731_LINVOL, reg | 0x0100);
+	reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
+	wm8731_write(codec, WM8731_RINVOL, reg | 0x0100);
+
+	wm8731_add_controls(codec);
+	wm8731_add_widgets(codec);
+
+	return 0;
+}
+
+/* 
+ * unregister interfaces and clean up
+ */
+static void wm8731_remove(struct snd_soc_codec *codec)
+{
+	wm8731_dpm_event(codec, SNDRV_CTL_POWER_D3cold);
+}
+
+/*
+ * Attach WM8731 2 wire client 
+ */
+static int wm8731_i2c_probe(struct i2c_adapter *adap, int addr, int kind)
+{		
+	int ret = 0;
+	struct snd_soc_codec *codec;
+	struct i2c_client *i2c;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+	
+	if ((codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+ 
+	if ((i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL)) == NULL){
+		kfree(codec);
+		return -ENOMEM;
+	}
+	memcpy(i2c, &client_template, sizeof(struct i2c_client));
+	codec->control_data = i2c;
+	i2c->dev.platform_data = codec;
+
+	if((ret = i2c_attach_client(i2c)) < 0) {
+		err("failed to attach codec at addr %x\n", addr);
+		kfree(i2c);
+		kfree(codec);
+		return ret;
+	}
+
+	codec->name = "WM8731";
+	codec->longname = "WM8731 Portable Codec";
+	codec->owner = THIS_MODULE;
+	codec->pdev.name = "alsa-wm8731";
+	codec->pdev.id = -1;
+	codec->pdev.dev.release = wm8731_device_release;
+	codec->probe = wm8731_probe;
+	codec->remove = wm8731_remove;
+	codec->read = wm8731_read_reg_cache;
+	codec->write = wm8731_write;
+	codec->dpm_event = wm8731_dpm_event;
+	codec->pcms = &wm8731_pcm_client;
+	codec->npcms = 1;
+	if ((codec->reg_cache = 
+			kzalloc(sizeof(u16) * ARRAY_SIZE(wm8731_reg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	memcpy(codec->reg_cache, wm8731_reg, sizeof(u16) * ARRAY_SIZE(wm8731_reg));
+
+	if((ret = snd_soc_register_codec(codec)) < 0) {
+		err("can't register codec");
+		i2c_del_driver(&wm8731_i2c_driver);
+		kfree(i2c);
+		kfree(codec->reg_cache);
+		kfree(codec);
+	}
+
+	return ret;
+}
+
+static int wm8731_i2c_detach(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = 
+		(struct snd_soc_codec*)client->dev.platform_data;
+
+	i2c_detach_client(client);
+	snd_soc_unregister_codec(codec);
+	kfree(codec->reg_cache);
+	kfree(codec->control_data);
+	kfree(codec);
+	return 0;
+}
+
+static int wm8731_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, wm8731_i2c_probe);
+}
+
+/* WM8731 2 Wire layer */ 
+static struct i2c_driver wm8731_i2c_driver = {
+	.name =           "i2c wm8731 driver",
+	.id =             I2C_DRIVERID_WM8731,
+	.flags =          I2C_DF_NOTIFY,
+	.attach_adapter = wm8731_i2c_attach,
+	.detach_client =  wm8731_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "WM8731",
+	.flags =  I2C_CLIENT_ALLOW_USE,
+	.driver = &wm8731_i2c_driver,
+};
+
+static int __init wm8731_init(void)
+{	
+	int ret = 0;
+	
+	info("WM8731 Audio Codec %s", WM8731_VERSION);
+
+	if ((ret = i2c_add_driver(&wm8731_i2c_driver)) != 0)
+		err("can't add i2c driver");
+	
+	return ret;
+}
+
+static void __exit wm8731_exit(void)
+{
+	i2c_del_driver(&wm8731_i2c_driver);
+}
+
+module_init(wm8731_init);
+module_exit(wm8731_exit);
+
+MODULE_DESCRIPTION("Soc WM8731 driver");
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
new file mode 100644
index 0000000..9db1778
--- /dev/null
+++ b/sound/soc/codecs/wm8731.h
@@ -0,0 +1,34 @@
+/*
+ * wm8731.h  --  WM8731 Soc Audio driver
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on wm8753.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 _WM8731_H
+#define _WM8731_H
+
+/* WM8731 register space */
+
+#define WM8731_LINVOL   0x00
+#define WM8731_RINVOL   0x01
+#define WM8731_LOUT1V   0x02
+#define WM8731_ROUT1V   0x03
+#define WM8731_APANA    0x04
+#define WM8731_APDIGI   0x05
+#define WM8731_PWR      0x06
+#define WM8731_IFACE    0x07
+#define WM8731_SRATE    0x08
+#define WM8731_ACTIVE   0x09
+#define WM8731_RESET	0x0f
+
+#define WM8731_CACHEREGNUM 	10
+
+#endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
new file mode 100644
index 0000000..7647fdb
--- /dev/null
+++ b/sound/soc/codecs/wm8750.c
@@ -0,0 +1,867 @@
+/*
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on WM8753.c
+ *
+ * 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/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpm.h>
+#include <sound/initval.h>
+
+#include "wm8750.h"
+
+#define AUDIO_NAME "WM8750"
+#define WM8750_VERSION "0.4"
+
+/*
+ * Debug
+ */
+ 
+#define PFX AUDIO_NAME
+#define WM8750_DEBUG 0
+
+#ifdef WM8750_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg)
+
+
+/* 
+ * WM8750 2 wire address is determined by CSB pin state during powerup */
+#define I2C_DRIVERID_WM8750 0xfde
+
+static unsigned short normal_i2c[] = {  0x1a, 0x1b,
+                                       I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8750_i2c_driver;
+static struct i2c_client client_template;
+	
+/*
+ * wm8750 register cache
+ * We can't read the WM8750 register space when we 
+ * are using 2 wire for device control, so we cache them instead. 
+ */
+static const u16 wm8750_reg[] = {
+	0x0097, 0x0097, 0x0079, 0x0079,  /*  0 */
+	0x0000, 0x0008, 0x0000, 0x000a,  /*  4 */
+	0x0000, 0x0000, 0x00ff, 0x00ff,  /*  8 */
+	0x000f, 0x000f, 0x0000, 0x0000,  /* 12 */
+	0x0000, 0x007b, 0x0000, 0x0032,  /* 16 */
+	0x0000, 0x00c3, 0x00c3, 0x00c0,  /* 20 */
+	0x0000, 0x0000, 0x0000, 0x0000,  /* 24 */
+	0x0000, 0x0000, 0x0000, 0x0000,  /* 28 */
+	0x0000, 0x0000, 0x0050, 0x0050,  /* 32 */
+	0x0050, 0x0050, 0x0050, 0x0050,  /* 36 */
+	0x0079, 0x0079, 0x0079,          /* 40 */
+};
+
+#define WM8750_HWFMT \
+	(SND_SOC_HWFMT_I2S | SND_SOC_HWFMT_LEFT_J | SND_SOC_HWFMT_RIGHT_J | \
+	SND_SOC_HWFMT_CBS_CFS | SND_SOC_HWFMT_CBM_CFM |\
+	SND_SOC_HWFMT_NB_NF | SND_SOC_HWFMT_NB_IF | SND_SOC_HWFMT_IB_NF | \
+	SND_SOC_HWFMT_IB_IF)
+
+/* priv1 is srate register setting */
+static struct snd_soc_hw_mode wm8750_hwfmt[] = {
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 8000,   64, 0x000c},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 8000,   96, 0x000e},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 8000,  128, 0x004c},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 8000,  192, 0x004e},
+
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 11025, 64,  0x0030},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 11025, 96,  0x0032},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 11025, 128, 0x0070},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 11025, 192, 0x0072},
+
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 12000, 64,  0x0010},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 12000, 96,  0x0012},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 12000, 128, 0x0050},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 12000, 192, 0x0052},
+
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 16000, 64,  0x0014},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 16000, 96,  0x0016},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 16000, 128, 0x0054},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 16000, 192, 0x0056},
+
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 22050, 64,  0x0034},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 22050, 96,  0x0036},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 22050, 128, 0x0074},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 22050, 192, 0x0076},
+
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 24000, 64,  0x0038},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 24000, 96,  0x003a},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 24000, 128, 0x0078},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 24000, 192, 0x007a},
+
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 32000,  64, 0x0018},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 32000,  96, 0x001a},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 32000, 128, 0x0058},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 32000, 192, 0x005a},
+
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 44100,  64, 0x0020},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 44100,  96, 0x0022},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 44100, 128, 0x0060},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 44100, 192, 0x0062},
+
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 48000,  64, 0x0000},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 48000,  96, 0x0002},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 48000, 128, 0x0040},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 48000, 192, 0x0042},
+
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 88200,  32, 0x003c},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 88200,  48, 0x003e},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 88200,  64, 0x007c},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 88200,  96, 0x007e},
+
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 96000,  32, 0x001c},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 96000,  48, 0x001e},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 96000,  64, 0x005c},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 96000,  96, 0x005e},
+};
+
+/*
+ * read wm8750 register cache
+ */
+static inline unsigned int wm8750_read_reg_cache(struct snd_soc_codec * codec, 
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg > WM8750_CACHE_REGNUM)
+		return -1;
+	return cache[reg];
+}
+
+/*
+ * write wm8750 register cache
+ */
+static inline void wm8750_write_reg_cache(struct snd_soc_codec * codec, 
+	unsigned int reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg > WM8750_CACHE_REGNUM)
+		return;
+	cache[reg] = value;
+}
+
+static int wm8750_write(struct snd_soc_codec * codec, unsigned int reg, 
+	unsigned int value)
+{
+	u8 data[2];
+	struct i2c_client *i2c = (struct i2c_client*)codec->control_data;
+
+	/* data is 
+	 *   D15..D9 WM8750 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	wm8750_write_reg_cache (codec, reg, value);
+	if (i2c_master_send(i2c, data, 2) == 2)
+		return 0;
+	else
+		return -1;
+}
+
+#define wm8750_reset(c)	wm8750_write(c, WM8750_RESET, 0)
+
+/*
+ * WM8750 Controls
+ */
+static const char* wm8750_bass[] = {"Linear Control", "Adaptive Boost"};
+static const char* wm8750_bass_filter[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
+static const char* wm8750_treble[] = {"8kHz", "4kHz"};
+static const char* wm8750_3d_lc[] = {"200Hz", "500Hz"};
+static const char* wm8750_3d_uc[] = {"2.2kHz", "1.5kHz"};
+static const char* wm8750_3d_func[] = {"Capture", "Playback"};
+static const char* wm8750_alc_func[] = {"Off", "Right", "Left", "Stereo"};
+static const char* wm8750_ng_type[] = {"Constant PGA Gain", "Mute ADC Output"};
+static const char* wm8750_line_mux[] = {"Line 1", "Line 2", "Line 3", "PGA", "Differential"};
+static const char* wm8750_pga_sel[] = {"Line 1", "Line 2", "Line 3", "Differential"};
+static const char* wm8750_out3[] = {"VREF", "ROUT1 + Vol", "MonoOut", "ROUT1"};
+static const char* wm8750_diff_sel[] = {"Line 1", "Line 2"};
+static const char* wm8750_adcpol[] = {"Normal", "L Invert", "R Invert", "L + R Invert"};
+static const char* wm8750_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const char* wm8750_mono_mux[] = {"Stereo", "Mono (Left)", "Mono (Right)", "Digital Mono"};
+
+static const struct soc_enum wm8750_enum[] = {
+SOC_ENUM_SINGLE(WM8750_BASS, 7, 2, wm8750_bass),
+SOC_ENUM_SINGLE(WM8750_BASS, 6, 2, wm8750_bass_filter),
+SOC_ENUM_SINGLE(WM8750_TREBLE, 6, 2, wm8750_treble),
+SOC_ENUM_SINGLE(WM8750_3D, 5, 2, wm8750_3d_lc),
+SOC_ENUM_SINGLE(WM8750_3D, 6, 2, wm8750_3d_uc),
+SOC_ENUM_SINGLE(WM8750_3D, 7, 2, wm8750_3d_func),
+SOC_ENUM_SINGLE(WM8750_ALC1, 7, 4, wm8750_alc_func),
+SOC_ENUM_SINGLE(WM8750_NGATE, 1, 2, wm8750_ng_type),
+SOC_ENUM_SINGLE(WM8750_LOUTM1, 0, 5, wm8750_line_mux),
+SOC_ENUM_SINGLE(WM8750_ROUTM1, 0, 5, wm8750_line_mux),
+SOC_ENUM_SINGLE(WM8750_LADCIN, 6, 4, wm8750_pga_sel), /* 10 */
+SOC_ENUM_SINGLE(WM8750_RADCIN, 6, 4, wm8750_pga_sel),
+SOC_ENUM_SINGLE(WM8750_ADCTL2, 7, 4, wm8750_out3),
+SOC_ENUM_SINGLE(WM8750_ADCIN, 8, 2, wm8750_diff_sel),
+SOC_ENUM_SINGLE(WM8750_ADCDAC, 5, 4, wm8750_adcpol),
+SOC_ENUM_SINGLE(WM8750_ADCDAC, 1, 4, wm8750_deemph),
+SOC_ENUM_SINGLE(WM8750_ADCIN, 6, 4, wm8750_mono_mux), /* 16 */
+
+};
+
+static const snd_kcontrol_new_t wm8750_snd_controls[] = {
+
+SOC_SINGLE("Left Capture Volume", WM8750_LINVOL, 0, 63, 0),
+SOC_SINGLE("Left Capture ZC Switch", WM8750_LINVOL, 6, 1, 0),
+SOC_SINGLE("Left Capture Switch", WM8750_LINVOL, 7, 1, 1),
+SOC_SINGLE("Right Capture Volume", WM8750_RINVOL, 0, 63, 0),
+SOC_SINGLE("Right Capture ZC Switch", WM8750_RINVOL, 6, 1, 0),
+SOC_SINGLE("Right Capture Switch", WM8750_RINVOL, 7, 1, 1),
+
+SOC_SINGLE("Left Out1 Playback ZC Switch", WM8750_LOUT1V, 7, 1, 0),
+SOC_SINGLE("Right Out1 Playback ZC Switch", WM8750_ROUT1V, 7, 1, 0),
+
+SOC_SINGLE("Left Out2 Playback ZC Switch", WM8750_LOUT2V, 7, 1, 0),
+SOC_SINGLE("Right Out2 Playback ZC Switch", WM8750_ROUT2V, 7, 1, 0),
+
+SOC_ENUM("Playback De-emphasis", wm8750_enum[15]),
+
+SOC_ENUM("Capture Polarity", wm8750_enum[14]),
+SOC_SINGLE("Playback 6dB Attenuate", WM8750_ADCDAC, 7, 1, 0),
+SOC_SINGLE("Capture 6dB Attenuate", WM8750_ADCDAC, 8, 1, 0),
+
+SOC_SINGLE("Left PCM Volume", WM8750_LDAC, 0, 255, 0),
+SOC_SINGLE("Right PCM Volume", WM8750_RDAC, 0, 255, 0),
+
+SOC_ENUM("Bass Boost", wm8750_enum[0]),
+SOC_ENUM("Bass Filter", wm8750_enum[1]),
+SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1),
+
+SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 0),
+SOC_ENUM("Treble Cut-off", wm8750_enum[2]),
+
+SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0),
+SOC_SINGLE("3D Volume", WM8750_3D, 1, 15, 0),
+SOC_ENUM("3D Lower Cut-off", wm8750_enum[3]),
+SOC_ENUM("3D Upper Cut-off", wm8750_enum[4]),
+SOC_ENUM("3D Mode", wm8750_enum[5]),
+
+SOC_SINGLE("ALC Capture Target Volume", WM8750_ALC1, 0, 7, 0),
+SOC_SINGLE("ALC Capture Max Volume", WM8750_ALC1, 4, 7, 0),
+SOC_ENUM("ALC Capture Function", wm8750_enum[6]),
+SOC_SINGLE("ALC Capture ZC Switch", WM8750_ALC2, 7, 1, 0),
+SOC_SINGLE("ALC Capture Hold Time", WM8750_ALC2, 0, 15, 0),
+SOC_SINGLE("ALC Capture Decay Time", WM8750_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack Time", WM8750_ALC3, 0, 15, 0),
+SOC_SINGLE("ALC Capture NG Threshold", WM8750_NGATE, 3, 31, 0),
+SOC_ENUM("ALC Capture NG Type", wm8750_enum[4]),
+SOC_SINGLE("ALC Capture NG Switch", WM8750_NGATE, 0, 1, 0),
+
+SOC_SINGLE("Left ADC Capture Volume", WM8750_LADC, 0, 255, 0),
+SOC_SINGLE("Right ADC Capture Volume", WM8750_RADC, 0, 255, 0),
+
+SOC_SINGLE("ZC Timeout Switch", WM8750_ADCTL1, 0, 1, 0),
+SOC_SINGLE("Playback Invert Switch", WM8750_ADCTL1, 1, 1, 0),
+
+SOC_SINGLE("Right Out2 Playback Invert Switch", WM8750_ADCTL2, 4, 1, 0),
+
+
+/* Unimplemented */
+/* ADCDAC Bit 0 - ADCHPD */
+/* ADCDAC Bit 4 - HPOR */
+/* ADCTL1 Bit 2,3 - DATSEL */
+/* ADCTL1 Bit 4,5 - DMONOMIX */
+/* ADCTL1 Bit 6,7 - VSEL */
+/* ADCTL2 Bit 2 - LRCM */
+/* ADCTL2 Bit 3 - TRI */
+/* ADCTL3 Bit 5 - HPFLREN */
+/* ADCTL3 Bit 6 - VROI */
+/* ADCTL3 Bit 7,8 - ADCLRM */
+/* ADCIN Bit 4 - LDCM */ 
+/* ADCIN Bit 5 - RDCM */
+
+SOC_SINGLE("Left Mic Boost", WM8750_LADCIN, 4, 3, 0),
+SOC_SINGLE("Right Mic Boost", WM8750_RADCIN, 4, 3, 0),
+
+SOC_SINGLE("Left Bypass Left Playback Volume", WM8750_LOUTM1, 4, 7, 1),
+SOC_SINGLE("Right Bypass Left Playback Volume", WM8750_LOUTM2, 4, 7, 1),
+
+SOC_SINGLE("Left Bypass Right Playback Volume", WM8750_ROUTM1, 4, 7, 1),
+SOC_SINGLE("Right Bypass Right Playback Volume", WM8750_ROUTM2, 4, 7, 1),
+
+SOC_SINGLE("Left Bypass Mono Playback Volume", WM8750_MOUTM1, 4, 7, 1),
+SOC_SINGLE("Right Bypass Mono Playback Volume", WM8750_MOUTM2, 4, 7, 1), 
+
+SOC_SINGLE("Mono Playback ZC Switch", WM8750_MOUTV, 7, 1, 0),
+
+};
+
+/* add non dpm controls */
+static int wm8750_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8750_snd_controls); i++) {
+		if ((err = snd_ctl_add(codec->card, snd_soc_cnew(&wm8750_snd_controls[i],codec, NULL))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+/*
+ * DPM Controls
+ */
+
+/* Left Mixer */
+static const snd_kcontrol_new_t wm8750_left_mixer_controls[] = {
+SOC_DPM_SINGLE("Playback Switch", WM8750_LOUTM1, 8, 1, 0),
+SOC_DPM_SINGLE("Left Bypass Switch", WM8750_LOUTM1, 7, 1, 0),
+SOC_DPM_SINGLE("Right Playback Switch", WM8750_LOUTM2, 8, 1, 0),
+SOC_DPM_SINGLE("Right Bypass Switch", WM8750_LOUTM2, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const snd_kcontrol_new_t wm8750_right_mixer_controls[] = {
+SOC_DPM_SINGLE("Left Playback Switch", WM8750_ROUTM1, 8, 1, 0),
+SOC_DPM_SINGLE("Left Bypass Switch", WM8750_ROUTM1, 7, 1, 0),
+SOC_DPM_SINGLE("Playback Switch", WM8750_ROUTM2, 8, 1, 0),
+SOC_DPM_SINGLE("Right Bypass Switch", WM8750_ROUTM2, 7, 1, 0),
+};
+
+/* Mono Mixer */
+static const snd_kcontrol_new_t wm8750_mono_mixer_controls[] = {
+SOC_DPM_SINGLE("Left Playback Switch", WM8750_MOUTM1, 8, 1, 0),
+SOC_DPM_SINGLE("Left Bypass Switch", WM8750_MOUTM1, 7, 1, 0),
+SOC_DPM_SINGLE("Right Playback Switch", WM8750_MOUTM2, 8, 1, 0),
+SOC_DPM_SINGLE("Right Bypass Switch", WM8750_MOUTM2, 7, 1, 0),
+};
+
+/* Left Line Mux */
+static const snd_kcontrol_new_t wm8750_left_line_controls =
+SOC_DPM_ENUM("Route", wm8750_enum[8]);
+
+/* Right Line Mux */
+static const snd_kcontrol_new_t wm8750_right_line_controls =
+SOC_DPM_ENUM("Route", wm8750_enum[9]);
+
+/* Left PGA Mux */
+static const snd_kcontrol_new_t wm8750_left_pga_controls =
+SOC_DPM_ENUM("Route", wm8750_enum[10]);
+
+/* Right PGA Mux */
+static const snd_kcontrol_new_t wm8750_right_pga_controls =
+SOC_DPM_ENUM("Route", wm8750_enum[11]);
+
+/* Out 3 Mux */
+static const snd_kcontrol_new_t wm8750_out3_controls =
+SOC_DPM_ENUM("Route", wm8750_enum[12]);
+
+/* Differential Mux */
+static const snd_kcontrol_new_t wm8750_diffmux_controls =
+SOC_DPM_ENUM("Route", wm8750_enum[13]);
+
+/* Mono ADC Mux */
+static const snd_kcontrol_new_t wm8750_monomux_controls =
+SOC_DPM_ENUM("Route", wm8750_enum[16]);
+
+/* LOUT 1 Volume */
+static const snd_kcontrol_new_t wm8750_lout1_vol_controls =
+SOC_DPM_SINGLE("Left Out1 Playback Volume", WM8750_LOUT1V, 0, 127, 0);
+
+/* ROUT 1 Volume */
+static const snd_kcontrol_new_t wm8750_rout1_vol_controls =
+SOC_DPM_SINGLE("Right Out1 Playback Volume", WM8750_ROUT1V, 0, 127, 0);
+
+/* LOUT 2 Volume */
+static const snd_kcontrol_new_t wm8750_lout2_vol_controls =
+SOC_DPM_SINGLE("Left Out2 Playback Volume", WM8750_LOUT2V, 0, 127, 0);
+
+/* ROUT 2 Volume */
+static const snd_kcontrol_new_t wm8750_rout2_vol_controls =
+SOC_DPM_SINGLE("Right Out2 Playback Volume", WM8750_ROUT2V, 0, 127, 0);
+
+/* Mono Volume */
+static const snd_kcontrol_new_t wm8750_mout_vol_controls =
+SOC_DPM_SINGLE("Mono Playback Volume", WM8750_MOUTV, 0, 127, 0);
+
+static const struct snd_soc_dpm_widget wm8750_dpm_widgets[] = {
+	SND_SOC_DPM_CLOCK("WM8750 Clock", WM8750_PWR1, 0, 1),
+	SND_SOC_DPM_MUTE("DAC Mute", WM8750_ADCDAC, 3, 0),
+	SND_SOC_DPM_MIXER("Left Mixer", -1, 0, 0, &wm8750_left_mixer_controls[0], ARRAY_SIZE(wm8750_left_mixer_controls)),
+	SND_SOC_DPM_MIXER("Right Mixer", -1, 0, 0, &wm8750_right_mixer_controls[0], ARRAY_SIZE(wm8750_right_mixer_controls)),
+	SND_SOC_DPM_MIXER("Mono Mixer", WM8750_PWR2, 2, 0, &wm8750_mono_mixer_controls[0], ARRAY_SIZE(wm8750_mono_mixer_controls)),
+
+	SND_SOC_DPM_VOLUME("Right Out 2", WM8750_PWR2, 3, 0, &wm8750_rout2_vol_controls, 1),
+	SND_SOC_DPM_VOLUME("Left Out 2", WM8750_PWR2, 4, 0, &wm8750_lout2_vol_controls, 1),
+	SND_SOC_DPM_VOLUME("Right Out 1", WM8750_PWR2, 5, 0, &wm8750_rout1_vol_controls, 1),
+	SND_SOC_DPM_VOLUME("Left Out 1", WM8750_PWR2, 6, 0, &wm8750_lout1_vol_controls, 1),
+	SND_SOC_DPM_DAC("Right DAC", "Right Playback", WM8750_PWR2, 7, 0, NULL, 0),
+	SND_SOC_DPM_DAC("Left DAC", "Left Playback", WM8750_PWR2, 8, 0, NULL, 0),
+
+	SND_SOC_DPM_MICBIAS("Mic Bias", WM8750_PWR1, 1, 0),
+	SND_SOC_DPM_ADC("Right ADC", "Right Capture", WM8750_PWR1, 2, 0, NULL, 0),
+	SND_SOC_DPM_ADC("Left ADC", "Left Capture", WM8750_PWR1, 3, 0, NULL, 0),
+
+	SND_SOC_DPM_MUX("Left PGA Mux", WM8750_PWR1, 5, 0, &wm8750_left_pga_controls),
+	SND_SOC_DPM_MUX("Right PGA Mux", WM8750_PWR1, 4, 0, &wm8750_right_pga_controls),
+	SND_SOC_DPM_MUX("Left Line Mux", -1, 0, 0, &wm8750_left_line_controls),
+	SND_SOC_DPM_MUX("Right Line Mux", -1, 0, 0, &wm8750_right_line_controls),
+
+	SND_SOC_DPM_MUX("Out3 Mux", -1, 0, 0, &wm8750_out3_controls),
+	SND_SOC_DPM_VOLUME("Out 3", WM8750_PWR2, 1, 0, NULL, 0),
+	SND_SOC_DPM_VOLUME("Mono Out 1", WM8750_PWR2, 2, 0, &wm8750_mout_vol_controls, 1),
+	
+	SND_SOC_DPM_MUX("Differential Mux", -1, 0, 0, &wm8750_diffmux_controls),
+	SND_SOC_DPM_MUX("Left ADC Mux", -1, 0, 0, &wm8750_monomux_controls),
+	SND_SOC_DPM_MUX("Right ADC Mux", -1, 0, 0, &wm8750_monomux_controls),
+
+	SND_SOC_DPM_OUTPUT("LOUT1"),
+	SND_SOC_DPM_OUTPUT("ROUT1"),
+	SND_SOC_DPM_OUTPUT("LOUT2"),
+	SND_SOC_DPM_OUTPUT("ROUT2"),
+	SND_SOC_DPM_OUTPUT("MONO"),
+	SND_SOC_DPM_OUTPUT("OUT3"),
+
+	SND_SOC_DPM_INPUT("LINPUT1"),
+	SND_SOC_DPM_INPUT("LINPUT2"),
+	SND_SOC_DPM_INPUT("LINPUT3"),
+	SND_SOC_DPM_INPUT("RINPUT1"),
+	SND_SOC_DPM_INPUT("RINPUT2"),
+	SND_SOC_DPM_INPUT("RINPUT3"),
+};
+
+static const char* intercon[][3] = {
+	/* left mixer */
+	{"Left Mixer", "Playback Switch", "Left DAC"},
+	{"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
+	{"Left Mixer", "Right Playback Switch", "Right DAC"},
+	{"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+	/* right mixer */
+	{"Right Mixer", "Left Playback Switch", "Left DAC"},
+	{"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
+	{"Right Mixer", "Playback Switch", "Right DAC"},
+	{"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+	/* left out 1 */
+	{"Left Out 1", NULL, "Left Mixer"},
+	{"LOUT1", NULL, "Left Out 1"},
+
+	/* left out 2 */
+	{"Left Out 2", NULL, "Left Mixer"},
+	{"LOUT2", NULL, "Left Out 2"},
+	
+	/* right out 1 */
+	{"Right Out 1", NULL, "Right Mixer"},
+	{"ROUT1", NULL, "Right Out 1"},
+
+	/* right out 2 */
+	{"Right Out 2", NULL, "Right Mixer"},
+	{"ROUT2", NULL, "Right Out 2"},
+
+	/* mono mixer */
+	{"Mono Mixer", "Left Playback Switch", "Left DAC"},
+	{"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
+	{"Mono Mixer", "Right Playback Switch", "Right DAC"},
+	{"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
+	
+	/* mono out */
+	{"Mono Out 1", NULL, "Mono Mixer"},
+	{"MONO1", NULL, "Mono Out 1"},
+
+	/* out 3 */
+	{"Out3 Mux", "VREF", "VREF"},
+	{"Out3 Mux", "ROUT1 + Vol", "ROUT1"},
+	{"Out3 Mux", "ROUT1", "Right Mixer"},
+	{"Out3 Mux", "MonoOut", "MONO1"},
+	{"Out 3", NULL, "Out3 Mux"},
+	{"OUT3", NULL, "Out 3"},
+
+	/* Left Line Mux */
+	{"Left Line Mux", "Line 1", "LINPUT1"},
+	{"Left Line Mux", "Line 2", "LINPUT2"},
+	{"Left Line Mux", "Line 3", "LINPUT3"},
+	{"Left Line Mux", "PGA", "Left PGA Mux"},
+	{"Left Line Mux", "Differential", "Differential Mux"},
+
+	/* Right Line Mux */
+	{"Right Line Mux", "Line 1", "RINPUT1"},
+	{"Right Line Mux", "Line 2", "RINPUT2"},
+	{"Right Line Mux", "Line 3", "RINPUT3"},
+	{"Right Line Mux", "PGA", "Right PGA Mux"},
+	{"Right Line Mux", "Differential", "Differential Mux"},
+
+	/* Left PGA Mux */
+	{"Left PGA Mux", "Line 1", "LINPUT1"},
+	{"Left PGA Mux", "Line 2", "LINPUT2"},
+	{"Left PGA Mux", "Line 3", "LINPUT3"},
+	{"Left PGA Mux", "Differential", "Differential Mux"},
+
+	/* Right PGA Mux */
+	{"Right PGA Mux", "Line 1", "RINPUT1"},
+	{"Right PGA Mux", "Line 2", "RINPUT2"},
+	{"Right PGA Mux", "Line 3", "RINPUT3"},
+	{"Right PGA Mux", "Differential", "Differential Mux"},
+
+	/* Differential Mux */
+	{"Differential Mux", "Line 1", "LINPUT1"},
+	{"Differential Mux", "Line 1", "RINPUT1"},
+	{"Differential Mux", "Line 2", "LINPUT2"},
+	{"Differential Mux", "Line 2", "RINPUT2"},
+
+	/* Left ADC Mux */
+	{"Left ADC Mux", "Stereo", "Left PGA Mux"},
+	{"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
+	{"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
+
+	/* Right ADC Mux */
+	{"Right ADC Mux", "Stereo", "Right PGA Mux"},
+	{"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
+	{"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
+
+	/* ADC */
+	{"Left ADC", NULL, "Left ADC Mux"},
+	{"Right ADC", NULL, "Right ADC Mux"},
+	
+	/* terminator */
+	{NULL, NULL, NULL},
+};
+
+static int wm8750_add_widgets(struct snd_soc_codec *codec)
+{
+	int i;
+	
+	for(i = 0; i < ARRAY_SIZE(wm8750_dpm_widgets); i++) {
+		snd_soc_dpm_new_control(codec, &wm8750_dpm_widgets[i]);
+	}
+	
+	/* set up audio path interconnects */
+	for(i = 0; intercon[i][0] != NULL; i++) {
+		snd_soc_dpm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]);
+	}
+
+	snd_soc_dpm_sync(codec);
+	return 0;
+}
+
+
+static int wm8750_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_codec *codec = pcm_c->codec;
+	u16 iface = 0;
+	
+	/* set master/slave audio interface */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_CLOCK_MASK) {
+		case SND_SOC_HWFMT_CBM_CFM:
+			iface |= 0x0040;
+			break;
+		case SND_SOC_HWFMT_CBS_CFS:
+			break;
+	}
+	
+	/* interface format */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_FORMAT_MASK) {
+		case SND_SOC_HWFMT_I2S:
+			iface |= 0x0002;
+			break;
+		case SND_SOC_HWFMT_RIGHT_J:
+			break;
+		case SND_SOC_HWFMT_LEFT_J:
+			iface |= 0x0001;
+			break;
+		case SND_SOC_HWFMT_DSP_A:
+			iface |= 0x0003;
+			break;
+		case SND_SOC_HWFMT_DSP_B:
+			iface |= 0x0013;
+			break;
+	}
+		
+	/* bit size */
+	switch(pcm_c->hw_runtime.hbits) {
+		case SND_SOC_HWBITS(16):
+			break;
+		case SND_SOC_HWBITS(20):
+			iface |= 0x0004;
+			break;
+		case SND_SOC_HWBITS(24):
+			iface |= 0x0008;
+			break;
+		case SND_SOC_HWBITS(32):
+			iface |= 0x000c;
+			break;
+	}
+
+	/* clock inversion */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_INV_MASK) {
+		case SND_SOC_HWFMT_NB_NF:
+			break;
+		case SND_SOC_HWFMT_IB_IF:
+			iface |= 0x0090;
+			break;
+		case SND_SOC_HWFMT_IB_NF:
+			iface |= 0x0080;
+			break;
+		case SND_SOC_HWFMT_NB_IF:
+			iface |= 0x0010;
+			break;
+	}
+	
+	/* set rate */
+	wm8750_write(codec, WM8750_SRATE, pcm_c->hw_runtime.priv1);
+	wm8750_write(codec, WM8750_IFACE, iface);
+	
+	return 0;
+}
+
+static void wm8750_device_release(struct device * dev)
+{
+}
+
+static int wm8750_dpm_event(struct snd_soc_codec *codec, int event)
+{
+	u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3f;
+	
+	switch (event) {
+		case SNDRV_CTL_POWER_D0: /* full On */
+			/* set vmid to 50k and unmute dac */
+			wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
+			break;
+		case SNDRV_CTL_POWER_D1: /* partial On */
+		case SNDRV_CTL_POWER_D2: /* partial On */
+			/* set vmid to 5k for quick power up */
+			wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c0);
+			break;
+		case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+			/* mute dac and set vmid to 500k, enable VREF */
+			wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0140);
+			break;
+		case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+			wm8750_write(codec, WM8750_PWR1, 0x0000);
+			break;
+	}
+	
+	return 0;
+}
+
+static struct snd_soc_pcm_codec wm8750_pcm_client = {
+	.name = "WM8750",
+	.playback = {
+		.sname = "Playback",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 |
+		SNDRV_PCM_RATE_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,
+	},
+	.nplayback = 1,
+	.capture = {
+		.sname = "Capture",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | 
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 |
+		SNDRV_PCM_RATE_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,
+	},
+	.ncapture = 1,
+	.ops = {
+		.prepare = wm8750_pcm_prepare,
+	},
+	.hw = {
+		.num_hmodes = ARRAY_SIZE(wm8750_hwfmt),
+		.hmodes = &wm8750_hwfmt[0],
+	},
+};
+
+/*
+ * initialise the WM8750 driver
+ * register the mixer and dsp interfaces with the kernel 
+ */
+static int wm8750_probe(struct snd_soc_codec *codec)
+{
+	int reg;
+	
+	info("WM8750 Audio Codec %s", WM8750_VERSION);
+	
+	wm8750_reset(codec);
+	
+	/* charge output caps */
+	wm8750_dpm_event(codec, SNDRV_CTL_POWER_D2);
+
+	/* set the update bits */
+	reg = wm8750_read_reg_cache(codec, WM8750_LDAC);
+	wm8750_write(codec, WM8750_LDAC, reg | 0x0100);
+	reg = wm8750_read_reg_cache(codec, WM8750_RDAC);
+	wm8750_write(codec, WM8750_RDAC, reg | 0x0100);
+	reg = wm8750_read_reg_cache(codec, WM8750_LOUT1V);
+	wm8750_write(codec, WM8750_LOUT1V, reg | 0x0100);
+	reg = wm8750_read_reg_cache(codec, WM8750_ROUT1V);
+	wm8750_write(codec, WM8750_ROUT1V, reg | 0x0100);
+	reg = wm8750_read_reg_cache(codec, WM8750_LOUT2V);
+	wm8750_write(codec, WM8750_LOUT2V, reg | 0x0100);
+	reg = wm8750_read_reg_cache(codec, WM8750_ROUT2V);
+	wm8750_write(codec, WM8750_ROUT2V, reg | 0x0100);
+	reg = wm8750_read_reg_cache(codec, WM8750_LINVOL);
+	wm8750_write(codec, WM8750_LINVOL, reg | 0x0100);
+	reg = wm8750_read_reg_cache(codec, WM8750_RINVOL);
+	wm8750_write(codec, WM8750_RINVOL, reg | 0x0100);
+
+	wm8750_add_controls(codec);
+	wm8750_add_widgets(codec);
+	
+	/* wait for caps to finish charging - liam make this thread */
+	ssleep(1);
+	wm8750_dpm_event(codec, SNDRV_CTL_POWER_D3hot);
+
+	return 0;
+}
+
+/* 
+ * unregister interfaces and clean up
+ */
+static void wm8750_remove(struct snd_soc_codec *codec)
+{
+	wm8750_dpm_event(codec, SNDRV_CTL_POWER_D3cold);
+}
+
+/*
+ * Attach WM8750 2 wire client 
+ */
+static int wm8750_i2c_probe(struct i2c_adapter *adap, int addr, int kind)
+{		
+	int ret = 0;
+	struct snd_soc_codec *codec;
+	struct i2c_client *i2c;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+	
+	if ((codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+ 
+	if ((i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL)) == NULL){
+		kfree(codec);
+		return -ENOMEM;
+	}
+	memcpy(i2c, &client_template, sizeof(struct i2c_client));
+	codec->control_data = i2c;
+	i2c->dev.platform_data = codec;
+
+	if((ret = i2c_attach_client(i2c)) < 0) {
+		err("failed to attach codec at addr %x\n", addr);
+		kfree(i2c);
+		kfree(codec);
+		return ret;
+	}
+
+	codec->name = "WM8750";
+	codec->longname = "WM8750 Portable Codec";
+	codec->owner = THIS_MODULE;
+	codec->pdev.name = "alsa-wm8750";
+	codec->pdev.id = -1;
+	codec->pdev.dev.release = wm8750_device_release;
+	codec->probe = wm8750_probe;
+	codec->remove = wm8750_remove;
+	codec->read = wm8750_read_reg_cache;
+	codec->write = wm8750_write;
+	codec->dpm_event = wm8750_dpm_event;
+	codec->pcms = &wm8750_pcm_client;
+	codec->npcms = 1;
+	if ((codec->reg_cache = 
+			kzalloc(sizeof(u16) * ARRAY_SIZE(wm8750_reg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	memcpy(codec->reg_cache, wm8750_reg, sizeof(u16) * ARRAY_SIZE(wm8750_reg));
+
+	if((ret = snd_soc_register_codec(codec)) < 0) {
+		err("can't register codec");
+		i2c_del_driver(&wm8750_i2c_driver);
+		kfree(i2c);
+		kfree(codec->reg_cache);
+		kfree(codec);
+	}
+
+	return ret;
+}
+
+static int wm8750_i2c_detach(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = 
+		(struct snd_soc_codec*)client->dev.platform_data;
+			
+	i2c_detach_client(client);
+	snd_soc_unregister_codec(codec);
+	kfree(codec->control_data);
+	kfree(codec->reg_cache);
+	kfree(codec);
+	return 0;
+}
+
+static int wm8750_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, wm8750_i2c_probe);
+}
+
+/* WM8750 2 Wire layer */ 
+static struct i2c_driver wm8750_i2c_driver = {
+	.name =           "i2c wm8750 driver",
+	.id =             I2C_DRIVERID_WM8750,
+	.flags =          I2C_DF_NOTIFY,
+	.attach_adapter = wm8750_i2c_attach,
+	.detach_client =  wm8750_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "WM8750",
+	.flags =  I2C_CLIENT_ALLOW_USE,
+	.driver = &wm8750_i2c_driver,
+};
+
+static int __init wm8750_init(void)
+{	
+	int ret = 0;
+
+	info("WM8750 Audio Codec %s", WM8750_VERSION);
+
+	if ((ret = i2c_add_driver(&wm8750_i2c_driver)) != 0)
+		err("can't add i2c driver");
+	
+	return ret;
+}
+
+static void __exit wm8750_exit(void)
+{
+	i2c_del_driver(&wm8750_i2c_driver);
+}
+
+module_init(wm8750_init);
+module_exit(wm8750_exit);
+
+MODULE_DESCRIPTION("Soc WM8750 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8750.h b/sound/soc/codecs/wm8750.h
new file mode 100644
index 0000000..6f5ba47
--- /dev/null
+++ b/sound/soc/codecs/wm8750.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on WM8753.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 _WM8750_H
+#define _WM8750_H
+
+/* WM8750 register space */
+
+#define WM8750_LINVOL    0x00
+#define WM8750_RINVOL    0x01
+#define WM8750_LOUT1V    0x02
+#define WM8750_ROUT1V    0x03
+#define WM8750_ADCDAC    0x05
+#define WM8750_IFACE     0x07
+#define WM8750_SRATE     0x08
+#define WM8750_LDAC      0x0a
+#define WM8750_RDAC      0x0b
+#define WM8750_BASS      0x0c
+#define WM8750_TREBLE    0x0d
+#define WM8750_RESET     0x0f
+#define WM8750_3D        0x10
+#define WM8750_ALC1      0x11
+#define WM8750_ALC2      0x12
+#define WM8750_ALC3      0x13
+#define WM8750_NGATE     0x14
+#define WM8750_LADC      0x15
+#define WM8750_RADC      0x16
+#define WM8750_ADCTL1    0x17
+#define WM8750_ADCTL2    0x18
+#define WM8750_PWR1      0x19
+#define WM8750_PWR2      0x1a
+#define WM8750_ADCTL3    0x1b
+#define WM8750_ADCIN     0x1f
+#define WM8750_LADCIN    0x20
+#define WM8750_RADCIN    0x21
+#define WM8750_LOUTM1    0x22
+#define WM8750_LOUTM2    0x23
+#define WM8750_ROUTM1    0x24
+#define WM8750_ROUTM2    0x25
+#define WM8750_MOUTM1    0x26
+#define WM8750_MOUTM2    0x27
+#define WM8750_LOUT2V    0x28
+#define WM8750_ROUT2V    0x29
+#define WM8750_MOUTV     0x2a
+
+#define WM8750_CACHE_REGNUM 0x2a
+
+#endif
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
new file mode 100644
index 0000000..5c5316a
--- /dev/null
+++ b/sound/soc/codecs/wm8753.c
@@ -0,0 +1,1279 @@
+/*
+ * wm8753.c  --  WM8753 Soc Audio driver
+ *
+ * Copyright 2003 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ * Notes:
+ *  The WM8753 is a low power, high quality stereo codec with integrated PCM
+ *  codec designed for portable digital telephony applications.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpm.h>
+#include <sound/initval.h>
+
+#include "wm8753.h"
+
+#define AUDIO_NAME "wm8753"
+#define WM8753_VERSION "0.5"
+
+/*
+ * Debug
+ */
+ 
+#define PFX AUDIO_NAME
+#define WM8753_DEBUG 0
+
+#ifdef WM8753_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg)
+
+
+
+/* 
+ * WM8753 2 wire address is determined by GPIO5
+ * state during powerup.
+ *    low  = WM8753_2W_ADDR1
+ *    high = WM8753_2W_ADDR2
+ */
+#define WM8753_2W_ADDR1	0x1a
+#define WM8753_2W_ADDR2	0x1b
+#define I2C_DRIVERID_WM8753 0xfefe /* liam -  need a proper id */
+
+static unsigned short normal_i2c[] = { WM8753_2W_ADDR1, WM8753_2W_ADDR2,
+                                       I2C_CLIENT_END };
+
+/* Magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8753_i2c_driver;
+static struct i2c_client client_template;
+
+/*
+ * wm8753 register cache
+ * We can't read the WM8753 register space when we 
+ * are using 2 wire for device control, so we cache them instead. 
+ */
+static const u16 wm8753_reg[] = {
+    0x0008, 0x0000, 0x000a, 0x000a,  
+    0x0033, 0x0000, 0x0007, 0x00ff,
+    0x00ff, 0x000f, 0x000f, 0x007b, 
+    0x0000, 0x0032, 0x0000, 0x00c3,
+    0x00c3, 0x00c0, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0055, 
+	0x0005, 0x0050, 0x0055, 0x0050,
+	0x0055, 0x0050, 0x0055, 0x0079, 
+	0x0079, 0x0079, 0x0079, 0x0079,
+	0x0000, 0x0000, 0x0000, 0x0000, 
+	0x0097, 0x0097, 0x0000, 0x0004,
+	0x0000, 0x0083, 0x0024, 0x01ba,
+	0x0000, 0x0083, 0x0024, 0x01ba, 
+	0x0000, 0x0000
+};
+
+/* PLL divisors */
+struct _pll_div {
+	u32 hz;
+	u32 n;
+	u32 k;
+};
+
+/* 
+ * MCLCK crystal frequency
+ */
+#define MCLK_13MHZ
+
+static struct _pll_div pll_div[] = {
+#ifdef MCLK_13MHZ	
+   {12288000,    0x7,    0x23F54A},
+   {11289600,    0x6,    0x3CA2F5},
+#endif
+#ifdef MCLK_12MHZ	
+   {12288000,    0x8,    0x0C49BA},
+   {11289600,    0x7,    0x21B08A},
+#endif
+#ifdef MCLK_12_288MHZ
+   {12288000,	0x0,    0x000000},
+   {11289600,	0x7,	0x166667},
+#endif
+   {0,           0,      0}  /* must be last */
+};
+
+#define WM8753_HIFI_HWFMT \
+	(SND_SOC_HWFMT_I2S | SND_SOC_HWFMT_LEFT_J | SND_SOC_HWFMT_RIGHT_J | \
+	SND_SOC_HWFMT_CBM_CFM | SND_SOC_HWFMT_CBM_CFS | SND_SOC_HWFMT_CBS_CFS | \
+	SND_SOC_HWFMT_NB_NF | SND_SOC_HWFMT_NB_IF | SND_SOC_HWFMT_IB_NF | \
+	SND_SOC_HWFMT_IB_IF)
+
+/*
+ * priv1 is pll/mclk freq
+ * priv2 is bits[15..8] = MCLK divisor, bits[7..0] = sr
+ */
+static struct snd_soc_hw_mode wm8753_hifi[] = {
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		8000,	768,	12288000, 	0x020c},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		8000,	384,	12288000, 	0x040c},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		8000,	192,	12288000, 	0x080c},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		8000,	96,		12288000, 	0x100c},
+	
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		11025,	512,	11289600,	0x0230},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		11025,	256,	11289600,	0x0430},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		11025, 	128,	11289600,	0x0830},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		11025,	64,		11289600,	0x1030},
+	
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		12000, 	512,	12288000,   0x0210},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		12000, 	256,	12288000,   0x0410},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		12000, 	128,	12288000,   0x0810},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		12000, 	64,		12288000,   0x1010},
+	
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		16000, 	384,	12288000,   0x0214},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		16000, 	192,	12288000,   0x0414},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		16000, 	96,		12288000,   0x0814},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		16000, 	48, 	12288000,   0x1014},
+
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		22050, 	512,	11289600,   0x0034},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		22050, 	256,	11289600,   0x0234},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		22050, 	128,	11289600,   0x0434},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		22050, 	64,		11289600,   0x0834},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		22050, 	32,		11289600,   0x1034},
+	
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		24000, 	512,	12288000,   0x0038},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		24000, 	256,	12288000,   0x0238},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		24000, 	128,	12288000,   0x0438},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		24000, 	64,		12288000,   0x0838},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		24000, 	32,		12288000,   0x1038},
+	
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		32000, 	384,	12288000,	0x0018},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		32000, 	192,	12288000,	0x0218},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		32000, 	96,		12288000,	0x0418},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		32000, 	48,		12288000,	0x0818},
+	
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		44100, 	256,	11289600,  	0x0020},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		44100, 	128,	11289600,  	0x0220},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		44100, 	64,		11289600,  	0x0420},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		44100, 	32,		11289600,  	0x0820},
+	
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		48000, 	256,	12288000,  	0x0000},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		48000, 	128,	12288000,  	0x0200},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		48000, 	64,		12288000,  	0x0400},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		48000, 	32,		12288000,  	0x0800},
+	
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		88200, 	128,	11289600,   0x003c},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		88200, 	64,		11289600,   0x023c},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		88200, 	32,		11289600,   0x043c},
+	
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		96000, 	128,	12288000,   0x001c},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		96000, 	64,		12288000,   0x021c},
+	{WM8753_HIFI_HWFMT, 	SND_SOC_HWBITS(16),		96000, 	32,		12288000,   0x041c},
+};
+
+#define WM8753_VOICE_HWFMT \
+	(SND_SOC_HWFMT_I2S | SND_SOC_HWFMT_LEFT_J | SND_SOC_HWFMT_RIGHT_J | \
+	SND_SOC_HWFMT_DSP_A | SND_SOC_HWFMT_DSP_B | SND_SOC_HWFMT_CBM_CFM | \
+	SND_SOC_HWFMT_CBM_CFS | SND_SOC_HWFMT_CBS_CFS | SND_SOC_HWFMT_NB_NF | \
+	SND_SOC_HWFMT_NB_IF | SND_SOC_HWFMT_IB_NF | SND_SOC_HWFMT_IB_IF)
+
+/*
+ * priv1 is pll/mclk freq
+ * priv2 is bits[15..8] = PCM divisor, bits[7..0] = psr
+ */
+static struct snd_soc_hw_mode wm8753_voice[] = {
+	{WM8753_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		48000,	256,	12288000, 	0x0000},
+	{WM8753_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		44100,	256,	11289600, 	0x0000},
+	{WM8753_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		32000,	384,	12288000, 	0x0001},
+	{WM8753_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		24000,	256,	12288000, 	0x0400},
+	{WM8753_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		22050,	256,	11289600, 	0x0400},
+	{WM8753_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		16000,	256,	12288000, 	0x0200},
+	{WM8753_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		16000,	384,	12288000, 	0x0401},
+	{WM8753_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		8000,	256,	12288000, 	0x0600},
+	{WM8753_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		8000,	384,	12288000, 	0x0501},
+};
+
+/*
+ * read wm8753 register cache
+ */
+static inline unsigned int wm8753_read_reg_cache(struct snd_soc_codec * codec, 
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if(reg < 1 || reg > (ARRAY_SIZE(wm8753_reg) + 1))
+		return -1;
+	return cache[reg - 1];
+}
+
+/*
+ * write wm8753 register cache
+ */
+static inline void wm8753_write_reg_cache(struct snd_soc_codec * codec, 
+	unsigned int reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if(reg < 1 || reg > (ARRAY_SIZE(wm8753_reg) + 1))
+		return;
+	cache[reg - 1] = value;
+}
+
+static struct i2c_client client_template;
+
+/*
+ * write to the WM8753 register space
+ */
+static int wm8753_write(struct snd_soc_codec * codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+	struct i2c_client *i2c = (struct i2c_client*)codec->control_data;
+	/* data is 
+	 *   D15..D9 WM8753 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	wm8753_write_reg_cache (codec, reg, value);
+	if (i2c_master_send(i2c, data, 2) == 2)
+		return 0;
+	else
+		return -1;
+}
+
+#define wm8753_reset(c) wm8753_write(c, WM8753_RESET, 0)
+
+
+/*
+ * WM8753 Controls
+ */
+static const char* wm8753_base[] = {"Linear Control", "Adaptive Boost"};
+static const char* wm8753_base_filter[] = 
+	{"130Hz @ 48kHz", "200Hz @ 48kHz", "100Hz @ 16kHz", "400Hz @ 48kHz", 
+	"100Hz @ 8kHz", "200Hz @ 8kHz"};
+static const char* wm8753_treble[] = {"8kHz", "4kHz"};
+static const char* wm8753_alc_func[] = {"Off", "Right", "Left", "Stereo"};
+static const char* wm8753_ng_type[] = {"Constant PGA Gain", "Mute ADC Output"};
+static const char* wm8753_3d_func[] = {"Capture", "Playback"};
+static const char* wm8753_3d_uc[] = {"2.2kHz", "1.5kHz"};
+static const char* wm8753_3d_lc[] = {"200Hz", "500Hz"};
+static const char* wm8753_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz"};
+static const char* wm8753_mono_mix[] = {"Stereo", "Left", "Right", "Mono"};
+static const char* wm8753_dac_phase[] = {"Non Inverted", "Inverted"};
+static const char* wm8753_line_mix[] = {"Line 1 + 2", "Line 1 - 2", 
+	"Line 1", "Line 2"};
+static const char* wm8753_mono_mux[] = {"Line Mix", "Rx Mix"};
+static const char* wm8753_right_mux[] = {"Line 2", "Rx Mix"};
+static const char* wm8753_left_mux[] = {"Line 1", "Rx Mix"};
+static const char* wm8753_rxmsel[] = {"RXP - RXN", "RXP + RXN", "RXP", "RXN"};
+static const char* wm8753_sidetone_mux[] = {"Left PGA", "Mic 1", "Mic 2", "Right PGA"};
+static const char* wm8753_mono2_src[] = {"Inverted Mono 1", "Left", "Right", "Left + Right"};
+static const char* wm8753_out3[] = {"VREF", "ROUT2", "Left + Right"};
+static const char* wm8753_out4[] = {"VREF", "Capture ST", "LOUT2"};
+static const char* wm8753_radcsel[] = {"PGA", "Line or RXP-RXN", "Sidetone"};
+static const char* wm8753_ladcsel[] = {"PGA", "Line or RXP-RXN", "Line"};
+static const char* wm8753_mono_adc[] = {"Stereo", "Analogue Mix Left", "Analogue Mix Right",
+	"Digital Mono Mix"};
+static const char* wm8753_adc_hp[] = {"3.4Hz @ 48kHz", "82Hz @ 16k", "82Hz @ 8kHz", "170Hz @ 8kHz"};
+static const char* wm8753_adc_filter[] = {"HiFi", "Voice"};
+static const char* wm8753_mic_sel[] = {"Mic 1", "Mic 2", "Mic 3"};
+
+static const struct soc_enum wm8753_enum[] = {
+SOC_ENUM_SINGLE(WM8753_BASS, 7, 2, wm8753_base),		// 0
+SOC_ENUM_SINGLE(WM8753_BASS, 4, 6, wm8753_base_filter),	// 1
+SOC_ENUM_SINGLE(WM8753_TREBLE, 6, 2, wm8753_treble), 	// 2
+SOC_ENUM_SINGLE(WM8753_ALC1, 7, 4, wm8753_alc_func),	// 3
+SOC_ENUM_SINGLE(WM8753_NGATE, 1, 2, wm8753_ng_type),	// 4
+SOC_ENUM_SINGLE(WM8753_3D, 7, 2, wm8753_3d_func),		// 5
+SOC_ENUM_SINGLE(WM8753_3D, 6, 2, wm8753_3d_uc),			// 6
+SOC_ENUM_SINGLE(WM8753_3D, 5, 2, wm8753_3d_lc),			// 7
+SOC_ENUM_SINGLE(WM8753_DAC, 1, 4, wm8753_deemp),		// 8
+SOC_ENUM_SINGLE(WM8753_DAC, 4, 4, wm8753_mono_mix),		// 9
+SOC_ENUM_SINGLE(WM8753_DAC, 6, 2, wm8753_dac_phase),	// 10
+SOC_ENUM_SINGLE(WM8753_INCTL1, 3, 4, wm8753_line_mix),	// 11
+SOC_ENUM_SINGLE(WM8753_INCTL1, 2, 2, wm8753_mono_mux),	// 12
+SOC_ENUM_SINGLE(WM8753_INCTL1, 1, 2, wm8753_right_mux),	// 13
+SOC_ENUM_SINGLE(WM8753_INCTL1, 0, 2, wm8753_left_mux),	// 14
+SOC_ENUM_SINGLE(WM8753_INCTL2, 6, 4, wm8753_rxmsel),	// 15
+SOC_ENUM_SINGLE(WM8753_INCTL2, 4, 4, wm8753_sidetone_mux),// 16
+SOC_ENUM_SINGLE(WM8753_OUTCTL, 7, 4, wm8753_mono2_src),	// 17
+SOC_ENUM_SINGLE(WM8753_OUTCTL, 0, 3, wm8753_out3),		// 18
+SOC_ENUM_SINGLE(WM8753_ADCTL2, 7, 3, wm8753_out4),		// 19
+SOC_ENUM_SINGLE(WM8753_ADCIN, 2, 3, wm8753_radcsel),	// 20
+SOC_ENUM_SINGLE(WM8753_ADCIN, 0, 3, wm8753_ladcsel),	// 21
+SOC_ENUM_SINGLE(WM8753_ADCIN, 4, 4, wm8753_mono_adc),	// 22
+SOC_ENUM_SINGLE(WM8753_ADC, 2, 4, wm8753_adc_hp),		// 23
+SOC_ENUM_SINGLE(WM8753_ADC, 4, 2, wm8753_adc_filter),	// 24
+SOC_ENUM_SINGLE(WM8753_MICBIAS, 6, 3, wm8753_mic_sel),	// 25
+};
+
+static const snd_kcontrol_new_t wm8753_snd_controls[] = {
+SOC_SINGLE("Left PCM Volume", WM8753_LDAC, 0, 255, 0),
+SOC_SINGLE("Right PCM Volume", WM8753_RDAC, 0, 255, 0),
+	
+SOC_SINGLE("Left ADC Capture Volume", WM8753_LADC, 0, 63, 0),
+SOC_SINGLE("Left ADC Capture Switch", WM8753_LADC, 7, 1, 0),
+SOC_SINGLE("Left ADC Capture ZC Switch", WM8753_LADC, 6, 1, 0),
+SOC_SINGLE("Right ADC Capture Volume", WM8753_RADC, 0, 63, 0),
+SOC_SINGLE("Right ADC Capture Switch", WM8753_RADC, 7, 1, 0),
+SOC_SINGLE("Right ADC Capture ZC Switch", WM8753_RADC, 6, 1, 0),
+
+SOC_SINGLE("Left Out1 Playback ZC Switch", WM8753_LOUT1V, 7, 1, 0),
+SOC_SINGLE("Left Out2 Playback ZC Switch", WM8753_LOUT2V, 7, 1, 0),
+	
+SOC_SINGLE("Left Bypass Playback Volume", WM8753_LOUTM1, 4, 7, 1),
+SOC_SINGLE("Left Sidetone Playback Volume", WM8753_LOUTM2, 4, 7, 1),
+SOC_SINGLE("Left Voice Playback Volume", WM8753_LOUTM2, 0, 7, 1),
+
+SOC_SINGLE("Right Out1 Playback ZC Switch", WM8753_ROUT1V, 7, 1, 0),
+SOC_SINGLE("Right Out2 Playback ZC Switch", WM8753_ROUT2V, 7, 1, 0),
+
+SOC_SINGLE("Right Bypass Playback Volume", WM8753_ROUTM1, 4, 7, 1),
+SOC_SINGLE("Right Sidetone Playback Volume", WM8753_ROUTM2, 4, 7, 1),
+SOC_SINGLE("Right Voice Playback Volume", WM8753_ROUTM2, 0, 7, 1),
+
+SOC_SINGLE("Mono Bypass Playback Volume", WM8753_MOUTM1, 4, 7, 1),
+SOC_SINGLE("Mono Sidetone Playback Volume", WM8753_MOUTM2, 4, 7, 1),
+SOC_SINGLE("Mono Voice Playback Volume", WM8753_MOUTM2, 4, 7, 1),
+
+SOC_SINGLE("Mono Playback ZC Switch", WM8753_MOUTV, 7, 1, 0),
+
+SOC_ENUM("Bass Boost", wm8753_enum[0]),
+SOC_ENUM("Bass Filter", wm8753_enum[1]),
+SOC_SINGLE("Bass Volume", WM8753_BASS, 0, 7, 1),
+
+SOC_SINGLE("Treble Volume", WM8753_TREBLE, 0, 7, 0),
+SOC_ENUM("Treble Cut-off", wm8753_enum[2]),
+
+SOC_SINGLE("Right Sidetone Capture Volume", WM8753_RECMIX1, 4, 7, 1),
+SOC_SINGLE("Left Sidetone Capture Volume", WM8753_RECMIX1, 0, 7, 1),
+SOC_SINGLE("Voice Sidetone Capture Volume", WM8753_RECMIX2, 0, 7, 1),
+
+SOC_SINGLE("Left Capture Volume", WM8753_LINVOL, 0, 63, 0),
+SOC_SINGLE("Left Capture ZC Switch", WM8753_LINVOL, 6, 1, 0),
+SOC_SINGLE("Left Capture Switch", WM8753_LINVOL, 7, 1, 1),
+SOC_SINGLE("Right Capture Volume", WM8753_RINVOL, 0, 63, 0),
+SOC_SINGLE("Right Capture ZC Switch", WM8753_RINVOL, 6, 1, 0),
+SOC_SINGLE("Right Capture Switch", WM8753_RINVOL, 7, 1, 1),
+
+SOC_ENUM("Capture Filter Select", wm8753_enum[23]),
+SOC_ENUM("Capture Filter Cut-off", wm8753_enum[24]),
+SOC_SINGLE("Capture Filter Switch", WM8753_ADC, 0, 1, 1),
+
+SOC_SINGLE("ALC Capture Target Volume", WM8753_ALC1, 0, 7, 0),
+SOC_SINGLE("ALC Capture Max Volume", WM8753_ALC1, 4, 7, 0),
+SOC_ENUM("ALC Capture Function", wm8753_enum[3]),
+SOC_SINGLE("ALC Capture ZC Switch", WM8753_ALC2, 8, 1, 0),
+SOC_SINGLE("ALC Capture Hold Time", WM8753_ALC2, 0, 15, 1),
+SOC_SINGLE("ALC Capture Decay Time", WM8753_ALC3, 4, 15, 1),
+SOC_SINGLE("ALC Capture Attack Time", WM8753_ALC3, 0, 15, 0),
+SOC_SINGLE("ALC Capture NG Threshold", WM8753_NGATE, 3, 31, 0),
+SOC_ENUM("ALC Capture NG Type", wm8753_enum[4]),
+SOC_SINGLE("ALC Capture NG Switch", WM8753_NGATE, 0, 1, 0),
+
+SOC_ENUM("3D Function", wm8753_enum[5]),
+SOC_ENUM("3D Upper Cut-off", wm8753_enum[6]),
+SOC_ENUM("3D Lower Cut-off", wm8753_enum[7]),
+SOC_SINGLE("3D Volume", WM8753_3D, 1, 15, 0),
+SOC_SINGLE("3D Switch", WM8753_3D, 0, 1, 0),
+
+SOC_SINGLE("Capture 6dB Attenuate", WM8753_ADCTL1, 2, 1, 0),
+SOC_SINGLE("Playback 6dB Attenuate", WM8753_ADCTL1, 1, 1, 0),
+
+SOC_ENUM("De-emphasis", wm8753_enum[8]),
+SOC_ENUM("Playback Mono Mix", wm8753_enum[9]),
+SOC_ENUM("Playback Phase", wm8753_enum[10]),
+
+SOC_SINGLE("Mic2 Capture Volume", WM8753_INCTL1, 7, 3, 0),
+SOC_SINGLE("Mic1 Capture Volume", WM8753_INCTL1, 5, 3, 0),
+};
+
+/* add non dpm controls */
+static int wm8753_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8753_snd_controls); i++) {
+		if ((err = snd_ctl_add(codec->card, snd_soc_cnew(&wm8753_snd_controls[i],codec, NULL))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+/*
+ * DPM Controls
+ */
+
+/* Left Mixer */
+static const snd_kcontrol_new_t wm8753_left_mixer_controls[] = {
+SOC_DPM_SINGLE("Voice Playback Switch", WM8753_LOUTM2, 8, 1, 0),
+SOC_DPM_SINGLE("Sidetone Playback Switch", WM8753_LOUTM2, 7, 1, 0),
+SOC_DPM_SINGLE("Left Playback Switch", WM8753_LOUTM1, 8, 1, 0),
+SOC_DPM_SINGLE("Bypass Playback Switch", WM8753_LOUTM1, 7, 1, 0),
+};
+
+/* Right mixer */
+static const snd_kcontrol_new_t wm8753_right_mixer_controls[] = {
+SOC_DPM_SINGLE("Voice Playback Switch", WM8753_ROUTM2, 8, 1, 0),
+SOC_DPM_SINGLE("Sidetone Playback Switch", WM8753_ROUTM2, 7, 1, 0),
+SOC_DPM_SINGLE("Right Playback Switch", WM8753_ROUTM1, 8, 1, 0),
+SOC_DPM_SINGLE("Bypass Playback Switch", WM8753_ROUTM1, 7, 1, 0),
+};
+
+/* Mono mixer */
+static const snd_kcontrol_new_t wm8753_mono_mixer_controls[] = {
+SOC_DPM_SINGLE("Left Playback Switch", WM8753_MOUTM1, 8, 1, 0),
+SOC_DPM_SINGLE("Right Playback Switch", WM8753_MOUTM2, 8, 1, 0),
+SOC_DPM_SINGLE("Voice Playback Switch", WM8753_MOUTM2, 4, 1, 0),
+SOC_DPM_SINGLE("Sidetone Playback Switch", WM8753_MOUTM2, 7, 1, 0),
+SOC_DPM_SINGLE("Bypass Playback Switch", WM8753_ROUTM1, 7, 1, 0),
+};
+
+/* Mono 2 Mux */
+static const snd_kcontrol_new_t wm8753_mono2_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[17]);
+
+/* Out 3 Mux */
+static const snd_kcontrol_new_t wm8753_out3_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[18]);
+
+/* Out 4 Mux */
+static const snd_kcontrol_new_t wm8753_out4_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[19]);
+
+/* ADC Mono Mix */
+static const snd_kcontrol_new_t wm8753_adc_mono_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[22]);
+
+/* Record mixer */
+static const snd_kcontrol_new_t wm8753_record_mixer_controls[] = {
+SOC_DPM_SINGLE("Voice Capture Switch", WM8753_RECMIX2, 3, 1, 0),
+SOC_DPM_SINGLE("Left Capture Switch", WM8753_RECMIX1, 3, 1, 0),
+SOC_DPM_SINGLE("Right Capture Switch", WM8753_RECMIX1, 7, 1, 0),
+};
+
+/* Left ADC mux */
+static const snd_kcontrol_new_t wm8753_adc_left_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[21]);
+
+/* Right ADC mux */
+static const snd_kcontrol_new_t wm8753_adc_right_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[20]);
+
+/* MIC mux */
+static const snd_kcontrol_new_t wm8753_mic_mux_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[16]);
+
+/* ALC mixer */
+static const snd_kcontrol_new_t wm8753_alc_mixer_controls[] = {
+SOC_DPM_SINGLE("Line Capture Switch", WM8753_INCTL2, 3, 1, 0),
+SOC_DPM_SINGLE("Mic2 Capture Switch", WM8753_INCTL2, 2, 1, 0),
+SOC_DPM_SINGLE("Mic1 Capture Switch", WM8753_INCTL2, 1, 1, 0),
+SOC_DPM_SINGLE("Rx Capture Switch", WM8753_INCTL2, 0, 1, 0),
+};
+
+/* Left Line mux */
+static const snd_kcontrol_new_t wm8753_line_left_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[14]);
+
+/* Right Line mux */
+static const snd_kcontrol_new_t wm8753_line_right_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[13]);
+
+/* Mono Line mux */
+static const snd_kcontrol_new_t wm8753_line_mono_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[12]);
+
+/* Line mux and mixer */
+static const snd_kcontrol_new_t wm8753_line_mux_mix_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[11]);
+
+/* Rx mux and mixer */
+static const snd_kcontrol_new_t wm8753_rx_mux_mix_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[15]);
+
+/* Mic Selector Mux */
+static const snd_kcontrol_new_t wm8753_mic_sel_mux_controls =
+SOC_DPM_ENUM("Route", wm8753_enum[25]);
+
+/* LOUT 1 Volume */
+static const snd_kcontrol_new_t wm8753_lout1_vol_controls =
+SOC_DPM_SINGLE("Left Out1 Playback Volume", WM8753_LOUT1V, 0, 127, 0);
+
+/* LOUT 2 Volume */
+static const snd_kcontrol_new_t wm8753_lout2_vol_controls =
+SOC_DPM_SINGLE("Left Out2 Playback Volume", WM8753_LOUT2V, 0, 127, 0);
+
+/* ROUT 1 Volume */
+static const snd_kcontrol_new_t wm8753_rout1_vol_controls =
+SOC_DPM_SINGLE("Right Out1 Playback Volume", WM8753_ROUT1V, 0, 127, 0);
+
+/* ROUT 2 Volume */
+static const snd_kcontrol_new_t wm8753_rout2_vol_controls =
+SOC_DPM_SINGLE("Right Out2 Playback Volume", WM8753_ROUT2V, 0, 127, 0);
+
+/* Mono 1 Volume */
+static const snd_kcontrol_new_t wm8753_mout1_vol_controls =
+SOC_DPM_SINGLE("Mono Playback Volume", WM8753_MOUTV, 0, 127, 0);
+
+static const struct snd_soc_dpm_widget wm8753_dpm_widgets[] = {
+SND_SOC_DPM_CLOCK("WM8753 Clock", WM8753_PWR1, 0, 1),
+SND_SOC_DPM_MUTE("DAC Mute", WM8753_DAC, 3, 0),
+SND_SOC_DPM_MICBIAS("Mic Bias", WM8753_PWR1, 5, 0),
+SND_SOC_DPM_MIXER("Left Mixer", WM8753_PWR4, 0, 0, &wm8753_left_mixer_controls[0], 
+	ARRAY_SIZE(wm8753_left_mixer_controls)),
+SND_SOC_DPM_VOLUME("Left Out 1", WM8753_PWR3, 8, 0, &wm8753_lout1_vol_controls, 1),
+SND_SOC_DPM_VOLUME("Left Out 2", WM8753_PWR3, 6, 0, &wm8753_lout2_vol_controls, 1),
+SND_SOC_DPM_DAC("Left DAC", "Left HiFi Playback", WM8753_PWR1, 3, 0, NULL, 0),
+SND_SOC_DPM_OUTPUT("LOUT1"),
+SND_SOC_DPM_OUTPUT("LOUT2"),
+SND_SOC_DPM_MIXER("Right Mixer", WM8753_PWR4, 1, 0, &wm8753_right_mixer_controls[0], 
+	ARRAY_SIZE(wm8753_right_mixer_controls)),
+SND_SOC_DPM_VOLUME("Right Out 1", WM8753_PWR3, 7, 0, &wm8753_rout1_vol_controls, 1),
+SND_SOC_DPM_VOLUME("Right Out 2", WM8753_PWR3, 5, 0, &wm8753_rout2_vol_controls, 1),
+SND_SOC_DPM_DAC("Right DAC", "Right HiFi Playback", WM8753_PWR1, 2, 0, NULL, 0),
+SND_SOC_DPM_OUTPUT("ROUT1"),
+SND_SOC_DPM_OUTPUT("ROUT2"),
+SND_SOC_DPM_MIXER("Mono Mixer", WM8753_PWR4, 2, 0, &wm8753_mono_mixer_controls[0], 
+	ARRAY_SIZE(wm8753_mono_mixer_controls)),
+SND_SOC_DPM_VOLUME("Mono Out 1", WM8753_PWR3, 2, 0, &wm8753_mout1_vol_controls, 1),
+SND_SOC_DPM_VOLUME("Mono Out 2", WM8753_PWR3, 1, 0, NULL, 0),
+SND_SOC_DPM_DAC("Voice DAC", "Voice Playback", WM8753_PWR1, 4, 0, NULL, 0),
+SND_SOC_DPM_OUTPUT("MONO1"),
+SND_SOC_DPM_MUX("Mono 2 Mux", -1, 0, 0, &wm8753_mono2_controls),
+SND_SOC_DPM_OUTPUT("MONO2"),
+SND_SOC_DPM_MIXER("Out3 Left + Right", -1, 0, 0, NULL, 0),
+SND_SOC_DPM_MUX("Out3 Mux", -1, 0, 0, &wm8753_out3_controls),
+SND_SOC_DPM_VOLUME("Out 3", WM8753_PWR3, 4, 0, NULL, 0),
+SND_SOC_DPM_OUTPUT("OUT3"),
+SND_SOC_DPM_MUX("Out4 Mux", -1, 0, 0, &wm8753_out4_controls),
+SND_SOC_DPM_VOLUME("Out 4", WM8753_PWR3, 3, 0, NULL, 0),
+SND_SOC_DPM_OUTPUT("OUT4"),
+SND_SOC_DPM_MIXER("Playback Mixer", WM8753_PWR4, 3, 0, &wm8753_record_mixer_controls[0], 
+	ARRAY_SIZE(wm8753_record_mixer_controls)),
+SND_SOC_DPM_ADC("Left ADC", "Left Voice Capture", WM8753_PWR2, 3, 0, NULL, 0),
+SND_SOC_DPM_ADC("Right ADC", "Right Voice Capture", WM8753_PWR2, 2, 0, NULL, 0),
+SND_SOC_DPM_MUX("Capture Left Mixer", -1, 0, 0, &wm8753_adc_mono_controls),
+SND_SOC_DPM_MUX("Capture Right Mixer", -1, 0, 0, &wm8753_adc_mono_controls),
+SND_SOC_DPM_MUX("Capture Left Mux", -1, 0, 0, &wm8753_adc_left_controls),
+SND_SOC_DPM_MUX("Capture Right Mux", -1, 0, 0, &wm8753_adc_right_controls),
+SND_SOC_DPM_MUX("Mic Sidetone Mux", -1, 0, 0, &wm8753_mic_mux_controls),
+SND_SOC_DPM_VOLUME("Left Capture Volume", WM8753_PWR2, 5, 0, NULL, 0),
+SND_SOC_DPM_VOLUME("Right Capture Volume", WM8753_PWR2, 4, 0, NULL, 0),
+SND_SOC_DPM_MIXER("ALC Mixer", WM8753_PWR2, 6, 0, &wm8753_alc_mixer_controls[0], 
+	ARRAY_SIZE(wm8753_alc_mixer_controls)),
+SND_SOC_DPM_MUX("Line Left Mux", -1, 0, 0, &wm8753_line_left_controls),
+SND_SOC_DPM_MUX("Line Right Mux", -1, 0, 0, &wm8753_line_right_controls),
+SND_SOC_DPM_MUX("Line Mono Mux", -1, 0, 0, &wm8753_line_mono_controls),
+SND_SOC_DPM_MUX("Line Mixer", -1, 0, 0, &wm8753_line_mux_mix_controls),
+SND_SOC_DPM_MUX("Rx Mixer", -1, 0, 0, &wm8753_rx_mux_mix_controls),
+SND_SOC_DPM_VOLUME("Mic 1 Volume", WM8753_PWR2, 8, 0, NULL, 0),
+SND_SOC_DPM_VOLUME("Mic 2 Volume", WM8753_PWR2, 7, 0, NULL, 0),
+SND_SOC_DPM_MUX("Mic Selection Mux", -1, 0, 0, &wm8753_mic_sel_mux_controls),
+SND_SOC_DPM_INPUT("LINE1"),
+SND_SOC_DPM_INPUT("LINE2"),
+SND_SOC_DPM_INPUT("RXP"),
+SND_SOC_DPM_INPUT("RXN"),
+SND_SOC_DPM_INPUT("ACIN"),
+SND_SOC_DPM_INPUT("ACOP"),
+SND_SOC_DPM_INPUT("MIC1N"),
+SND_SOC_DPM_INPUT("MIC1"),
+SND_SOC_DPM_INPUT("MIC2N"),
+SND_SOC_DPM_INPUT("MIC2"),
+};
+
+static const char* intercon[][3] = {
+	/* left mixer */
+	{"Left Mixer", "Left Playback Switch", "Left DAC"},
+	{"Left Mixer", "Voice Playback Switch", "Voice DAC"},
+	{"Left Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
+	{"Left Mixer", "Bypass Playback Switch", "Line Left Mux"},
+	
+	/* right mixer */
+	{"Right Mixer", "Right Playback Switch", "Right DAC"},
+	{"Right Mixer", "Voice Playback Switch", "Voice DAC"},
+	{"Right Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
+	{"Right Mixer", "Bypass Playback Switch", "Line Right Mux"},
+	
+	/* mono mixer */
+	{"Mono Mixer", "Voice Playback Switch", "Voice DAC"},
+	{"Mono Mixer", "Left Playback Switch", "Left DAC"},
+	{"Mono Mixer", "Right Playback Switch", "Right DAC"},
+	{"Mono Mixer", "Sidetone Playback Switch", "Mic Sidetone Mux"},
+	{"Mono Mixer", "Bypass Playback Switch", "Line Mono Mux"},
+	
+	/* left out */
+	{"Left Out 1", NULL, "Left Mixer"},
+	{"Left Out 2", NULL, "Left Mixer"},
+	{"LOUT1", NULL, "Left Out 1"},
+	{"LOUT2", NULL, "Left Out 2"},
+
+	/* right out */
+	{"Right Out 1", NULL, "Right Mixer"},
+	{"Right Out 2", NULL, "Right Mixer"},
+	{"ROUT1", NULL, "Right Out 1"},
+	{"ROUT2", NULL, "Right Out 2"},
+	
+	/* mono 1 out */
+	{"Mono Out 1", NULL, "Mono Mixer"},
+	{"MONO1", NULL, "Mono Out 1"},
+	
+	/* mono 2 out */
+	{"Mono 2 Mux", "Left + Right", "Out3 Left + Right"},
+	{"Mono 2 Mux", "Inverted Mono 1", "MONO1"},
+	{"Mono 2 Mux", "Left", "Left Mixer"},
+	{"Mono 2 Mux", "Right", "Right Mixer"},
+	{"Mono Out 2", NULL, "Mono 2 Mux"},
+	{"MONO2", NULL, "Mono Out 2"},
+	
+	/* out 3 */
+	{"Out3 Left + Right", NULL, "Left Mixer"},
+	{"Out3 Left + Right", NULL, "Right Mixer"},
+	{"Out3 Mux", "VREF", "VREF"},
+	{"Out3 Mux", "Left + Right", "Out3 Left + Right"},
+	{"Out3 Mux", "ROUT2", "ROUT2"},
+	{"Out 3", NULL, "Out3 Mux"},
+	{"OUT3", NULL, "Out 3"},
+	
+	/* out 4 */
+	{"Out4 Mux", "VREF", "VREF"},
+	{"Out4 Mux", "Capture ST", "Capture ST Mixer"},
+	{"Out4 Mux", "LOUT2", "LOUT2"},
+	{"Out 4", NULL, "Out4 Mux"},
+	{"OUT4", NULL, "Out 4"},
+	
+	/* record mixer  */
+	{"Playback Mixer", "Left Capture Switch", "Left Mixer"},
+	{"Playback Mixer", "Voice Capture Switch", "Mono Mixer"},
+	{"Playback Mixer", "Right Capture Switch", "Right Mixer"},
+	
+	/* Mic/SideTone Mux */
+	{"Mic Sidetone Mux", "Left PGA", "Left Capture Volume"},
+	{"Mic Sidetone Mux", "Right PGA", "Right Capture Volume"},
+	{"Mic Sidetone Mux", "Mic 1", "Mic 1 Volume"},
+	{"Mic Sidetone Mux", "Mic 2", "Mic 2 Volume"},
+	
+	/* Capture Left Mux */
+	{"Capture Left Mux", "PGA", "Left Capture Volume"},
+	{"Capture Left Mux", "Line or RXP-RXN", "Line Left Mux"},
+	{"Capture Left Mux", "Line", "LINE1"},
+	
+	/* Capture Right Mux */
+	{"Capture Right Mux", "PGA", "Right Capture Volume"},
+	{"Capture Right Mux", "Line or RXP-RXN", "Line Right Mux"},
+	{"Capture Right Mux", "Sidetone", "Capture ST Mixer"},
+	
+	/* Mono Capture mixer-mux */
+	{"Capture Right Mixer", "Stereo", "Capture Right Mux"},
+	{"Capture Left Mixer", "Analogue Mix Left", "Capture Left Mux"},
+	{"Capture Left Mixer", "Analogue Mix Left", "Capture Right Mux"},
+	{"Capture Right Mixer", "Analogue Mix Right", "Capture Left Mux"},
+	{"Capture Right Mixer", "Analogue Mix Right", "Capture Right Mux"},
+	{"Capture Left Mixer", "Digital Mono Mix", "Capture Left Mux"},
+	{"Capture Left Mixer", "Digital Mono Mix", "Capture Right Mux"},
+	{"Capture Right Mixer", "Digital Mono Mix", "Capture Left Mux"},
+	{"Capture Right Mixer", "Digital Mono Mix", "Capture Right Mux"},
+	
+	/* ADC */
+	{"Left ADC", NULL, "Capture Left Mixer"},
+	{"Right ADC", NULL, "Capture Right Mixer"},
+
+	/* Left Capture Volume */
+	{"Left Capture Volume", NULL, "ACIN"},
+	
+	/* Right Capture Volume */
+	{"Right Capture Volume", NULL, "Mic 2 Volume"},
+
+	/* ALC Mixer */
+	{"ALC Mixer", "Line Capture Switch", "Line Mixer"},
+	{"ALC Mixer", "Mic2 Capture Switch", "Mic 2 Volume"},
+	{"ALC Mixer", "Mic1 Capture Switch", "Mic 1 Volume"},
+	{"ALC Mixer", "Rx Capture Switch", "Rx Mixer"},
+	
+	/* Line Left Mux */
+	{"Line Left Mux", "Line 1", "LINE1"},
+	{"Line Left Mux", "Rx Mix", "Rx Mixer"},
+	
+	/* Line Right Mux */
+	{"Line Right Mux", "Line 2", "LINE2"},
+	{"Line Right Mux", "Rx Mix", "Rx Mixer"},
+	
+	/* Line Mono Mux */
+	{"Line Mono Mux", "Line Mix", "Line Mixer"},
+	{"Line Mono Mux", "Rx Mix", "Rx Mixer"},
+	
+	/* Line Mixer/Mux */
+	{"Line Mixer", "Line 1 + 2", "LINE1"},
+	{"Line Mixer", "Line 1 - 2", "LINE1"},
+	{"Line Mixer", "Line 1 + 2", "LINE2"},
+	{"Line Mixer", "Line 1 - 2", "LINE2"},
+	{"Line Mixer", "Line 1", "LINE1"},
+	{"Line Mixer", "Line 2", "LINE2"},
+	
+	/* Rx Mixer/Mux */
+	{"Rx Mixer", "RXP - RXN", "RXP"},
+	{"Rx Mixer", "RXP + RXN", "RXP"},
+	{"Rx Mixer", "RXP - RXN", "RXN"},
+	{"Rx Mixer", "RXP + RXN", "RXN"},
+	{"Rx Mixer", "RXP", "RXP"},
+	{"Rx Mixer", "RXN", "RXN"},
+	
+	/* Mic 1 Volume */
+	{"Mic 1 Volume", NULL, "MIC1N"},
+	{"Mic 1 Volume", NULL, "Mic Selection Mux"},
+	
+	/* Mic 2 Volume */
+	{"Mic 2 Volume", NULL, "MIC2N"},
+	{"Mic 2 Volume", NULL, "MIC2"},
+	
+	/* Mic Selector Mux */
+	{"Mic Selection Mux", "Mic 1", "MIC1"},
+	{"Mic Selection Mux", "Mic 2", "MIC2N"},
+	{"Mic Selection Mux", "Mic 3", "MIC2"},
+	
+	/* ACOP */
+	{"ACOP", NULL, "ALC Mixer"},
+	
+	/* terminator */
+	{NULL, NULL, NULL},
+};
+
+static int wm8753_add_widgets(struct snd_soc_codec *codec)
+{
+	int i;
+	
+	for(i = 0; i < ARRAY_SIZE(wm8753_dpm_widgets); i++) {
+		snd_soc_dpm_new_control(codec, &wm8753_dpm_widgets[i]);
+	}
+	
+	/* set up audio path interconnects */
+	for(i = 0; intercon[i][0] != NULL; i++) {
+		snd_soc_dpm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]);
+	}
+	
+	snd_soc_dpm_sync(codec);
+	return 0;
+}
+
+/*
+ * set the PLL2 to generate 12.288MHz and then div by 6 to generate
+ * DAC/ADC clocks from 12MHz MCLK 
+ */
+static u32 wm8753_config_pll(struct snd_soc_codec *codec, int pll, u32 speed)
+{
+	u16 clk;
+
+    if (pll == 1) {
+		clk = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0x0010;
+        if (!speed) {
+            /* disable PLL1  */
+            wm8753_write(codec, WM8753_PLL1CTL1, 0x0026);
+			wm8753_write(codec, WM8753_CLOCK, clk);
+        } else {
+            u16 value = 0;
+            int i = 0;   
+       
+            /* if we cant match, then use good values for N and K */
+            for (;i < ARRAY_SIZE(pll_div); i++) {
+                if (pll_div[i].hz == speed)
+                    break;
+            }
+			
+			/* do we need to enable the PLL ? */
+			if (!pll_div[i].n) {
+				wm8753_write(codec, WM8753_CLOCK, 0x0);
+				return 0;
+			}
+    
+            /* set up N and K PLL divisor ratios */
+            /* bits 8:5 = PLL_N, bits 3:0 = PLL_K[21:18] */
+            value = (pll_div[i].n << 5) + ((pll_div[i].k & 0x3c0000) >> 18);  
+            wm8753_write(codec, WM8753_PLL1CTL2, value);
+     
+            /* bits 8:0 = PLL_K[17:9] */
+            value = (pll_div[i].k & 0x03fe00) >> 9;
+            wm8753_write(codec, WM8753_PLL1CTL3, value);
+    
+            /* bits 8:0 = PLL_K[8:0] */
+            value = pll_div[i].k & 0x0001ff;
+            wm8753_write(codec, WM8753_PLL1CTL4, value);
+     
+            /* set PLL1 as input and enable */
+            wm8753_write(codec, WM8753_PLL1CTL1, 0x0027);
+			wm8753_write(codec, WM8753_CLOCK, clk | 0x0010);
+        }
+    } else {
+		clk = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0x0008;
+        if (!speed) {
+            /* disable PLL2  */
+            wm8753_write(codec, WM8753_PLL2CTL1, 0x0026);
+			wm8753_write(codec, WM8753_CLOCK, clk);
+        } else {
+            u16 value = 0;
+            int i = 0;   
+       
+            /* if we cant match, then use good values for N and K */
+            for (;i < ARRAY_SIZE(pll_div); i++) {
+                if (pll_div[i].hz == speed)
+                    break;
+            }
+			
+    		/* do we need to enable the PLL ? */
+			if (!pll_div[i].n) {
+				wm8753_write(codec, WM8753_CLOCK, 0x0);
+				return 0;
+			}
+			
+            /* set up N and K PLL divisor ratios */
+            /* bits 8:5 = PLL_N, bits 3:0 = PLL_K[21:18] */
+            value = (pll_div[i].n << 5) + ((pll_div[i].k & 0x3c0000) >> 18);  
+            wm8753_write(codec, WM8753_PLL2CTL2, value);
+     
+            /* bits 8:0 = PLL_K[17:9] */
+            value = (pll_div[i].k & 0x03fe00) >> 9;
+            wm8753_write(codec, WM8753_PLL2CTL3, value);
+    
+            /* bits 8:0 = PLL_K[8:0] */
+            value = pll_div[i].k & 0x0001ff;
+            wm8753_write(codec, WM8753_PLL2CTL4, value);
+     
+            /* set PLL1 as input and enable */
+            wm8753_write(codec, WM8753_PLL2CTL1, 0x0027);
+			wm8753_write(codec, WM8753_CLOCK, clk | 0x0008);
+        }
+    }
+	return speed;
+}
+
+static int wm8753_pcm_voice_prepare(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_codec *codec = pcm_c->codec;
+	u16 voice = 0, ioctl, clock, srate;
+	
+	/* enable PLL */
+	wm8753_config_pll(codec, 2, pcm_c->hw_runtime.priv1);
+	
+	/* set master/slave audio interface */
+	ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x01f1;
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_CLOCK_MASK) {
+		case SND_SOC_HWFMT_CBM_CFM:
+			voice |= 0x0040;
+			ioctl |= 0x0002;
+			break;
+		case SND_SOC_HWFMT_CBM_CFS:
+			voice |= 0x0040;
+			break;
+	}
+	
+	/* interface format */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_FORMAT_MASK) {
+		case SND_SOC_HWFMT_I2S:
+			voice |= 0x0002;
+			break;
+		case SND_SOC_HWFMT_RIGHT_J:
+			break;
+		case SND_SOC_HWFMT_LEFT_J:
+			voice |= 0x0001;
+			break;
+		case SND_SOC_HWFMT_DSP_A:
+			voice |= 0x0003;
+			break;
+		case SND_SOC_HWFMT_DSP_B:
+			voice |= 0x0013;
+			break;
+	}
+	
+	/* bit size */
+	switch(pcm_c->hw_runtime.hbits) {
+		case SND_SOC_HWBITS(16):
+			break;
+		case SND_SOC_HWBITS(20):
+			voice |= 0x0004;
+			break;
+		case SND_SOC_HWBITS(24):
+			voice |= 0x0008;
+			break;
+		case SND_SOC_HWBITS(32):
+			voice |= 0x000c;
+			break;
+	}
+	
+	/* set rate */
+	clock = wm8753_read_reg_cache(codec, WM8753_CLOCK) & 0x003f;
+	clock |= (pcm_c->hw_runtime.priv2 & 0xff00) >> 2;
+	wm8753_write(codec, WM8753_CLOCK, clock);
+
+	srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x017f;
+	srate |= (pcm_c->hw_runtime. priv2 & 0xff) << 7;
+	wm8753_write(codec, WM8753_SRATE1, srate);
+	
+	/* clock inversion */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_INV_MASK) {
+		case SND_SOC_HWFMT_IB_IF:
+			voice |= 0x0090;
+			break;
+		case SND_SOC_HWFMT_IB_NF:
+			voice |= 0x0080;
+			break;
+		case SND_SOC_HWFMT_NB_IF:
+			voice |= 0x0010;
+			break;
+	}
+	wm8753_write(codec, WM8753_IOCTL, ioctl);
+	wm8753_write(codec, WM8753_PCM, voice);
+	return 0;
+}
+
+static int wm8753_pcm_hifi_prepare(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_codec *codec = pcm_c->codec;
+	u16 hifi = 0, ioctl, srate;
+	
+	/* enable PLL */
+	wm8753_config_pll(codec, 1, pcm_c->hw_runtime.priv1);
+	
+	/* set master/slave audio interface */
+	ioctl = wm8753_read_reg_cache(codec, WM8753_IOCTL) & 0x00f2;
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_CLOCK_MASK) {
+		case SND_SOC_HWFMT_CBM_CFM:
+			hifi |= 0x0040;
+			ioctl |= 0x0001;
+			break;
+		case SND_SOC_HWFMT_CBM_CFS:
+			hifi |= 0x0040;
+			break;
+	}
+	
+	/* interface format */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_FORMAT_MASK) {
+		case SND_SOC_HWFMT_I2S:
+			hifi |= 0x0002;
+			break;
+		case SND_SOC_HWFMT_RIGHT_J:
+			break;
+		case SND_SOC_HWFMT_LEFT_J:
+			hifi |= 0x0001;
+			break;
+		case SND_SOC_HWFMT_DSP_A:
+			hifi |= 0x0003;
+			break;
+		case SND_SOC_HWFMT_DSP_B:
+			hifi |= 0x0013;
+			break;
+	}
+	
+	/* bit size */
+	switch(pcm_c->hw_runtime.hbits) {
+		case SND_SOC_HWBITS(16):
+			break;
+		case SND_SOC_HWBITS(20):
+			hifi |= 0x0004;
+			break;
+		case SND_SOC_HWBITS(24):
+			hifi |= 0x0008;
+			break;
+		case SND_SOC_HWBITS(32):
+			hifi |= 0x000c;
+			break;
+	}
+	
+	/* set rate */
+	srate = wm8753_read_reg_cache(codec, WM8753_SRATE2) & 0x01c7;
+	switch ((pcm_c->hw_runtime.priv2 & 0xff00) >> 8){
+		case 1:
+			break;
+		case 2:
+			srate |= (0x1 << 3);
+			break;
+		case 4:
+			srate |= (0x2 << 3);
+			break;
+		case 8:
+			srate |= (0x3 << 3);
+			break;
+		case 16:
+			srate |= (0x4 << 3);
+			break;
+	}		
+	wm8753_write(codec, WM8753_SRATE2, srate);
+	srate = (pcm_c->hw_runtime. priv2 & 0xff) << 1;
+	wm8753_write(codec, WM8753_SRATE1, srate);
+	
+	/* clock inversion */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_INV_MASK) {
+		case SND_SOC_HWFMT_IB_IF:
+			hifi |= 0x0090;
+			break;
+		case SND_SOC_HWFMT_IB_NF:
+			hifi |= 0x0080;
+			break;
+		case SND_SOC_HWFMT_NB_IF:
+			hifi |= 0x0010;
+			break;
+	}
+	wm8753_write(codec, WM8753_IOCTL, ioctl | 0x0008);
+	wm8753_write(codec, WM8753_HIFI, hifi);
+	return 0;
+}
+
+static void wm8753_device_release(struct device * dev)
+{
+}
+
+static int wm8753_dpm_event(struct snd_soc_codec *codec, int event)
+{
+	u16 pwr_reg = wm8753_read_reg_cache(codec, WM8753_PWR1) & 0xfe3f;
+
+	switch (event) {
+		case SNDRV_CTL_POWER_D0: /* full On */
+			/* set vmid to 50k and unmute dac */
+			wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0);
+			break;
+		case SNDRV_CTL_POWER_D1: /* partial On */
+		case SNDRV_CTL_POWER_D2: /* partial On */
+			/* set vmid to 5k for quick power up */
+			wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x01c0);
+			break;
+		case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+			/* mute dac and set vmid to 500k, enable VREF */
+			wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x0140);
+			break;
+		case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+			wm8753_write(codec, WM8753_PWR1, 0x0001);
+			break;
+	}
+	
+	return 0;
+}
+
+static struct snd_soc_pcm_codec wm8753_pcm_clients[] = {
+{	.name = "WM8753 Voice",
+	.playback = {
+		.sname = "Voice Playback",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 1,},
+	.capture = {
+		.sname = "Voice Capture",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | 
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 |
+		SNDRV_PCM_RATE_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,},
+	.nplayback = 1,
+	.ncapture = 1,
+	.ops = {
+		.prepare = wm8753_pcm_voice_prepare,},
+	.hw = {
+		.num_hmodes = ARRAY_SIZE(wm8753_voice),
+		.hmodes = &wm8753_voice[0],
+		},},
+{	.name = "WM8753 HiFi",
+	.playback = {
+		.sname = "HiFi Playback",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 |
+		SNDRV_PCM_RATE_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,},
+	.nplayback = 1,
+	.ncapture = 1,
+	.ops = {
+		.prepare = wm8753_pcm_hifi_prepare,},
+	.hw = {
+		.num_hmodes = ARRAY_SIZE(wm8753_hifi),
+		.hmodes = &wm8753_hifi[0],},},
+};
+
+/*
+ * initialise the WM8753 driver
+ * register the mixer and dsp interfaces with the kernel 
+ */
+static int wm8753_probe(struct snd_soc_codec *codec)
+{
+	int reg;
+
+	wm8753_reset(codec);
+	
+	/* charge output caps */
+	wm8753_dpm_event(codec, SNDRV_CTL_POWER_D2);
+	
+	/* set the update bits */
+	reg = wm8753_read_reg_cache(codec, WM8753_LDAC);
+	wm8753_write(codec, WM8753_LDAC, reg | 0x0100);
+	reg = wm8753_read_reg_cache(codec, WM8753_RDAC);
+	wm8753_write(codec, WM8753_RDAC, reg | 0x0100);
+	reg = wm8753_read_reg_cache(codec, WM8753_LOUT1V);
+	wm8753_write(codec, WM8753_LOUT1V, reg | 0x0100);
+	reg = wm8753_read_reg_cache(codec, WM8753_ROUT1V);
+	wm8753_write(codec, WM8753_ROUT1V, reg | 0x0100);
+	reg = wm8753_read_reg_cache(codec, WM8753_LOUT2V);
+	wm8753_write(codec, WM8753_LOUT2V, reg | 0x0100);
+	reg = wm8753_read_reg_cache(codec, WM8753_ROUT2V);
+	wm8753_write(codec, WM8753_ROUT2V, reg | 0x0100);
+	reg = wm8753_read_reg_cache(codec, WM8753_LINVOL);
+	wm8753_write(codec, WM8753_LINVOL, reg | 0x0100);
+	reg = wm8753_read_reg_cache(codec, WM8753_RINVOL);
+	wm8753_write(codec, WM8753_RINVOL, reg | 0x0100);
+
+	wm8753_add_controls(codec);
+	wm8753_add_widgets(codec);
+	
+	/* wait for caps to finish charging - liam make this thread */
+	ssleep(1);
+	wm8753_dpm_event(codec, SNDRV_CTL_POWER_D3hot);
+
+	return 0;
+}
+
+/* 
+ * unregister interfaces and clean up
+ */
+static void wm8753_remove(struct snd_soc_codec *codec)
+{
+	/* power down chip */
+	wm8753_dpm_event(codec, SNDRV_CTL_POWER_D3cold);
+}
+
+/*
+ * Attach WM8753 2 wire client 
+ */
+static int wm8753_i2c_probe(struct i2c_adapter *adap, int addr, int kind)
+{		
+	int ret = 0;
+	struct snd_soc_codec *codec;
+	struct i2c_client *i2c;
+
+	client_template.adapter = adap;
+	client_template.addr = addr;
+	
+	if ((codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+ 
+	if ((i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL)) == NULL){
+		kfree(codec);
+		return -ENOMEM;
+	}
+	memcpy(i2c, &client_template, sizeof(struct i2c_client));
+	codec->control_data = i2c;
+	i2c->dev.platform_data = codec;
+
+	if((ret = i2c_attach_client(i2c)) < 0) {
+		err("failed to attach codec at addr %x\n", addr);
+		kfree(i2c);
+		kfree(codec);
+		return ret;
+	}
+
+	codec->name = "WM8753";
+	codec->longname = "WM8753 Portable Codec";
+	codec->owner = THIS_MODULE;
+	codec->pdev.name = "alsa-wm8753";
+	codec->pdev.id = -1;
+	codec->pdev.dev.release = wm8753_device_release;
+	codec->probe = wm8753_probe;
+	codec->remove = wm8753_remove;
+	codec->read = wm8753_read_reg_cache;
+	codec->write = wm8753_write;
+	codec->dpm_event = wm8753_dpm_event;
+	codec->pcms = wm8753_pcm_clients;
+	codec->npcms = 2;
+	if ((codec->reg_cache = 
+			kzalloc(sizeof(u16) * ARRAY_SIZE(wm8753_reg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	memcpy(codec->reg_cache, wm8753_reg, sizeof(u16) * ARRAY_SIZE(wm8753_reg));
+
+	if((ret = snd_soc_register_codec(codec)) < 0) {
+		err("can't register codec");
+		i2c_del_driver(&wm8753_i2c_driver);
+		kfree(i2c);
+		kfree(codec->reg_cache);
+		kfree(codec);
+	}
+
+	return ret;
+}
+
+static int wm8753_i2c_detach(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = 
+		(struct snd_soc_codec*)client->dev.platform_data;
+
+	i2c_detach_client(client);
+	snd_soc_unregister_codec(codec);
+	kfree(codec->reg_cache);
+	kfree(codec);
+	return 0;
+}
+
+static int wm8753_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, wm8753_i2c_probe);
+}
+
+/* WM8753 2 Wire layer */ 
+static struct i2c_driver wm8753_i2c_driver = {
+	.name =           "i2c wm8753 driver",
+	.id =             I2C_DRIVERID_WM8753,
+	.flags =          I2C_DF_NOTIFY,
+	.attach_adapter = wm8753_i2c_attach,
+	.detach_client =  wm8753_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "WM8753",
+	.flags =  I2C_CLIENT_ALLOW_USE,
+	.driver = &wm8753_i2c_driver,
+};
+
+static int __init wm8753_init(void)
+{	
+	int ret = 0;
+	
+	info("WM8753 Audio Codec %s", WM8753_VERSION);
+
+	if ((ret = i2c_add_driver(&wm8753_i2c_driver)) != 0)
+		err("can't add i2c driver");
+	
+	return ret;
+}
+
+static void __exit wm8753_exit(void)
+{
+	i2c_del_driver(&wm8753_i2c_driver);
+}
+
+module_init(wm8753_init);
+module_exit(wm8753_exit);
+
+MODULE_DESCRIPTION("Soc WM8753 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8753.h b/sound/soc/codecs/wm8753.h
new file mode 100644
index 0000000..c7f2a6b
--- /dev/null
+++ b/sound/soc/codecs/wm8753.h
@@ -0,0 +1,81 @@
+/*
+ * wm8753.h  --  audio driver for WM8753
+ *
+ * Copyright 2003 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef _WM8753_H
+#define _WM8753_H
+
+/* WM8753 register space */
+
+#define WM8753_DAC		0x01
+#define WM8753_ADC		0x02
+#define WM8753_PCM		0x03
+#define WM8753_HIFI		0x04
+#define WM8753_IOCTL	0x05
+#define WM8753_SRATE1	0x06
+#define WM8753_SRATE2	0x07
+#define WM8753_LDAC		0x08
+#define WM8753_RDAC		0x09
+#define WM8753_BASS		0x0a
+#define WM8753_TREBLE	0x0b
+#define WM8753_ALC1		0x0c
+#define WM8753_ALC2		0x0d
+#define WM8753_ALC3		0x0e
+#define WM8753_NGATE	0x0f
+#define WM8753_LADC		0x10
+#define WM8753_RADC		0x11
+#define WM8753_ADCTL1	0x12
+#define WM8753_3D		0x13
+#define WM8753_PWR1		0x14
+#define WM8753_PWR2		0x15
+#define WM8753_PWR3		0x16
+#define WM8753_PWR4		0x17
+#define WM8753_ID		0x18
+#define WM8753_INTPOL	0x19
+#define WM8753_INTEN	0x1a
+#define WM8753_GPIO1	0x1b
+#define WM8753_GPIO2	0x1c
+#define WM8753_RESET	0x1f
+#define WM8753_RECMIX1	0x20
+#define WM8753_RECMIX2	0x21
+#define WM8753_LOUTM1	0x22
+#define WM8753_LOUTM2	0x23
+#define WM8753_ROUTM1	0x24
+#define WM8753_ROUTM2	0x25
+#define WM8753_MOUTM1	0x26
+#define WM8753_MOUTM2	0x27
+#define WM8753_LOUT1V	0x28
+#define WM8753_ROUT1V	0x29
+#define WM8753_LOUT2V	0x2a
+#define WM8753_ROUT2V	0x2b
+#define WM8753_MOUTV	0x2c
+#define WM8753_OUTCTL	0x2d
+#define WM8753_ADCIN	0x2e
+#define WM8753_INCTL1	0x2f
+#define WM8753_INCTL2	0x30
+#define WM8753_LINVOL	0x31
+#define WM8753_RINVOL	0x32
+#define WM8753_MICBIAS	0x33
+#define WM8753_CLOCK	0x34
+#define WM8753_PLL1CTL1	0x35
+#define WM8753_PLL1CTL2	0x36
+#define WM8753_PLL1CTL3	0x37
+#define WM8753_PLL1CTL4	0x38
+#define WM8753_PLL2CTL1	0x39
+#define WM8753_PLL2CTL2	0x3a
+#define WM8753_PLL2CTL3	0x3b
+#define WM8753_PLL2CTL4	0x3c
+#define WM8753_BIASCTL	0x3d
+#define WM8753_ADCTL2	0x3f
+
+#endif
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
new file mode 100644
index 0000000..44ab8c2
--- /dev/null
+++ b/sound/soc/codecs/wm8971.c
@@ -0,0 +1,805 @@
+/*
+ * wm8971.c  --  WM8971 SoC Audio driver
+ *
+ * Copyright 2005 Lab126, Inc.
+ * 
+ * Author: Kenneth Kiraly <kiraly@lab126.com>
+ *
+ * Based on wm8753.c by Liam Girdwood
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpm.h>
+#include <sound/initval.h>
+
+#include "wm8971.h"
+
+#define AUDIO_NAME "wm8971"
+#define WM8971_VERSION "0.2"
+
+
+#define PFX AUDIO_NAME
+#undef	WM8971_DEBUG
+
+#ifdef WM8971_DEBUG
+#define dbg(format, arg...) printk(KERN_DEBUG PFX ": " format "\n" , ## arg)
+#else
+#define dbg(format, arg...) do {} while (0)
+#endif
+#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING PFX ": " format "\n" , ## arg)
+
+
+#define WM8971_2W_ADDR1			0x1a
+#define	WM8971_2W_ADDR2			0x1b
+
+#define I2C_DRIVERID_WM8971		0xfdfd		/* need to get a real ID */
+
+
+static unsigned short normal_i2c[] = { WM8971_2W_ADDR1, WM8971_2W_ADDR2, I2C_CLIENT_END };
+static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };	/* (required for kernel 2.6.10) */
+
+
+/* magic definition of all other variables and things */
+I2C_CLIENT_INSMOD;
+
+static struct i2c_driver wm8971_i2c_driver;
+static struct i2c_client client_template;
+
+
+#define	WM8971_REG_COUNT		43
+
+/*
+ * wm8971 register cache
+ * We can't read the WM8971 register space when we 
+ * are using 2 wire for device control, so we cache them instead. 
+ */
+static const u16 wm8971_reg[] = {
+	0x0097, 0x0097, 0x0079, 0x0079,  /*  0 */
+	0x0000, 0x0008, 0x0000, 0x000a,  /*  4 */
+	0x0000, 0x0000, 0x00ff, 0x00ff,  /*  8 */
+	0x000f, 0x000f, 0x0000, 0x0000,  /* 12 */
+	0x0000, 0x007b, 0x0000, 0x0032,  /* 16 */
+	0x0000, 0x00c3, 0x00c3, 0x00c0,  /* 20 */
+	0x0000, 0x0000, 0x0000, 0x0000,  /* 24 */
+	0x0000, 0x0000, 0x0000, 0x0000,  /* 28 */
+	0x0000, 0x0000, 0x0050, 0x0050,  /* 32 */
+	0x0050, 0x0050, 0x0050, 0x0050,  /* 36 */
+	0x0079, 0x0079, 0x0079,          /* 40 */
+};
+
+#define WM8971_HWFMT \
+	(SND_SOC_HWFMT_I2S | SND_SOC_HWFMT_LEFT_J | SND_SOC_HWFMT_RIGHT_J | \
+	SND_SOC_HWFMT_CBS_CFS | SND_SOC_HWFMT_CBM_CFM | \
+	SND_SOC_HWFMT_NB_NF | SND_SOC_HWFMT_NB_IF | SND_SOC_HWFMT_IB_NF | \
+	SND_SOC_HWFMT_IB_IF)
+
+/* priv1 is srate register setting */
+static struct snd_soc_hw_mode wm8971_audio[] = {
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 8000,   64, 0x000c},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 8000,   96, 0x000e},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 8000,  128, 0x004c},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 8000,  192, 0x004e},
+
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 11025, 64,  0x0030},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 11025, 96,  0x0032},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 11025, 128, 0x0070},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 11025, 192, 0x0072},
+
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 12000, 64,  0x0010},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 12000, 96,  0x0012},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 12000, 128, 0x0050},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 12000, 192, 0x0052},
+
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 16000, 64,  0x0014},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 16000, 96,  0x0016},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 16000, 128, 0x0054},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 16000, 192, 0x0056},
+
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 22050, 64,  0x0034},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 22050, 96,  0x0036},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 22050, 128, 0x0074},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 22050, 192, 0x0076},
+
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 24000, 64,  0x0038},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 24000, 96,  0x003a},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 24000, 128, 0x0078},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 24000, 192, 0x007a},
+
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 32000,  64, 0x0018},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 32000,  96, 0x001a},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 32000, 128, 0x0058},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 32000, 192, 0x005a},
+
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 44100,  64, 0x0020},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 44100,  96, 0x0022},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 44100, 128, 0x0060},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 44100, 192, 0x0062},
+
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 48000,  64, 0x0000},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 48000,  96, 0x0002},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 48000, 128, 0x0040},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 48000, 192, 0x0042},
+
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 88200,  32, 0x003c},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 88200,  48, 0x003e},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 88200,  64, 0x007c},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 88200,  96, 0x007e},
+
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 96000,  32, 0x001c},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 96000,  48, 0x001e},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 96000,  64, 0x005c},
+	{WM8971_HWFMT, SND_SOC_HWBITS(16), 96000,  96, 0x005e},
+};
+
+
+
+static inline unsigned int wm8971_read_reg_cache(struct snd_soc_codec * codec, 
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg < WM8971_REG_COUNT)
+		return cache[reg];
+
+	return -1;
+}
+
+static inline void wm8971_write_reg_cache(struct snd_soc_codec * codec, 
+	unsigned int reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg < WM8971_REG_COUNT)
+		cache[reg] = value;
+}
+
+static int wm8971_write(struct snd_soc_codec * codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+	struct i2c_client *i2c = (struct i2c_client*)codec->control_data;
+
+	/* D15..D9 is WM8971 register offset, D8..D0 is register data */
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	wm8971_write_reg_cache (codec, reg, value);
+
+	return (i2c_master_send(i2c, data, 2) == 2) ? 0 : -1;
+}
+
+#define wm8971_reset(c)	wm8971_write(c, WM8971_RESET, 0)
+
+/* WM8971 Controls */
+static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" };
+static const char *wm8971_bass_filter[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
+static const char *wm8971_treble[] = { "8kHz", "4kHz" };
+static const char *wm8971_alc_func[] = { "Off", "Right", "Left", "Stereo" };
+static const char *wm8971_ng_type[] = { "Constant PGA Gain", "Mute ADC Output" };
+static const char *wm8971_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
+static const char* wm8971_mono_mux[] = {"Stereo", "Mono (Left)", "Mono (Right)", "Digital Mono"};
+static const char *wm8971_dac_phase[] = { "Non Inverted", "Inverted" };
+static const char* wm8971_lline_mux[] = {"Line", "NC", "NC", "PGA", "Differential"};
+static const char* wm8971_rline_mux[] = {"Line", "Mic", "NC", "PGA", "Differential"};
+static const char* wm8971_lpga_sel[] = {"Line", "NC", "NC", "Differential"};
+static const char* wm8971_rpga_sel[] = {"Line", "Mic", "NC", "Differential"};
+static const char* wm8971_adcpol[] = {"Normal", "L Invert", "R Invert", "L + R Invert"};
+
+static const struct soc_enum wm8971_enum[] = {
+	SOC_ENUM_SINGLE(WM8971_BASS, 7, 2, wm8971_bass),			/* 0 */
+	SOC_ENUM_SINGLE(WM8971_BASS, 6, 2, wm8971_bass_filter),
+	SOC_ENUM_SINGLE(WM8971_TREBLE, 6, 2, wm8971_treble),
+	SOC_ENUM_SINGLE(WM8971_ALC1, 7, 4, wm8971_alc_func),
+	SOC_ENUM_SINGLE(WM8971_NGATE, 1, 2, wm8971_ng_type),        /* 4 */
+	SOC_ENUM_SINGLE(WM8971_ADCDAC, 1, 4, wm8971_deemp),
+	SOC_ENUM_SINGLE(WM8971_ADCTL1, 4, 4, wm8971_mono_mux),
+	SOC_ENUM_SINGLE(WM8971_ADCTL1, 1, 1, wm8971_dac_phase),
+	SOC_ENUM_SINGLE(WM8971_LOUTM1, 0, 5, wm8971_lline_mux),     /* 8 */
+	SOC_ENUM_SINGLE(WM8971_ROUTM1, 0, 5, wm8971_rline_mux),
+	SOC_ENUM_SINGLE(WM8971_LADCIN, 6, 4, wm8971_lpga_sel),
+	SOC_ENUM_SINGLE(WM8971_RADCIN, 6, 4, wm8971_rpga_sel),
+	SOC_ENUM_SINGLE(WM8971_ADCDAC, 5, 4, wm8971_adcpol),        /* 12 */
+	SOC_ENUM_SINGLE(WM8971_ADCIN, 6, 4, wm8971_mono_mux),
+};
+
+
+static const snd_kcontrol_new_t wm8971_snd_controls[] = {
+	SOC_SINGLE("Left PCM Volume", WM8971_LDAC, 0, 255, 0),
+	SOC_SINGLE("Right PCM Volume", WM8971_RDAC, 0, 255, 0),
+
+	SOC_SINGLE("Left ADC Capture Volume", WM8971_LADC, 0, 255, 0),
+	SOC_SINGLE("Right ADC Capture Volume", WM8971_RADC, 0, 255, 0),
+
+	SOC_SINGLE("Left Out1 Playback ZC Switch", WM8971_LOUT1V, 7, 1, 0),
+	SOC_SINGLE("Left Out2 Playback ZC Switch", WM8971_LOUT2V, 7, 1, 0),
+	
+	SOC_SINGLE("Right Out1 Playback ZC Switch", WM8971_ROUT1V, 7, 1, 0),
+	SOC_SINGLE("Right Out2 Playback ZC Switch", WM8971_ROUT2V, 7, 1, 0),
+
+	SOC_SINGLE("Left Bypass Left Playback Volume", WM8971_LOUTM1, 4, 7, 1),
+	SOC_SINGLE("Right Bypass Left Playback Volume", WM8971_LOUTM2, 4, 7, 1),
+
+	SOC_SINGLE("Left Bypass Right Playback Volume", WM8971_ROUTM1, 4, 7, 1),
+	SOC_SINGLE("Right Bypass Right Playback Volume", WM8971_ROUTM2, 4, 7, 1),
+
+	SOC_SINGLE("Left Bypass Mono Playback Volume", WM8971_MOUTM1, 4, 7, 1),
+	SOC_SINGLE("Right Bypass Mono Playback Volume", WM8971_MOUTM2, 4, 7, 1), 
+
+	SOC_SINGLE("Mono Playback ZC Switch", WM8971_MOUTV, 7, 1, 0),
+
+	SOC_ENUM("Bass Boost", wm8971_enum[0]),
+	SOC_ENUM("Bass Filter", wm8971_enum[1]),
+	SOC_SINGLE("Bass Volume", WM8971_BASS, 0, 7, 1),
+
+	SOC_SINGLE("Treble Volume", WM8971_TREBLE, 0, 7, 0),
+	SOC_ENUM("Treble Cut-off", wm8971_enum[2]),
+
+	SOC_SINGLE("Left Capture Volume", WM8971_LINVOL, 0, 63, 0),
+	SOC_SINGLE("Left Capture ZC Switch", WM8971_LINVOL, 6, 1, 0),
+	SOC_SINGLE("Left Capture Switch", WM8971_LINVOL, 7, 1, 1),
+	SOC_SINGLE("Right Capture Volume", WM8971_RINVOL, 0, 63, 0),
+	SOC_SINGLE("Right Capture ZC Switch", WM8971_RINVOL, 6, 1, 0),
+	SOC_SINGLE("Right Capture Switch", WM8971_RINVOL, 7, 1, 1),
+
+	SOC_SINGLE("Capture Filter Switch", WM8971_ADCDAC, 0, 1, 1),
+
+	SOC_SINGLE("ALC Target Volume", WM8971_ALC1, 0, 7, 0),
+	SOC_SINGLE("ALC Max Volume", WM8971_ALC1, 4, 7, 0),
+
+	SOC_SINGLE("ALC Capture Target Volume", WM8971_ALC1, 0, 7, 0),
+	SOC_SINGLE("ALC Capture Max Volume", WM8971_ALC1, 4, 7, 0),
+	SOC_ENUM("ALC Capture Function", wm8971_enum[3]),
+	SOC_SINGLE("ALC Capture ZC Switch", WM8971_ALC2, 7, 1, 0),
+	SOC_SINGLE("ALC Capture Hold Time", WM8971_ALC2, 0, 15, 0),
+	SOC_SINGLE("ALC Capture Decay Time", WM8971_ALC3, 4, 15, 0),
+	SOC_SINGLE("ALC Capture Attack Time", WM8971_ALC3, 0, 15, 0),
+	SOC_SINGLE("ALC Capture NG Threshold", WM8971_NGATE, 3, 31, 0),
+	SOC_ENUM("ALC Capture NG Type", wm8971_enum[4]),
+	SOC_SINGLE("ALC Capture NG Switch", WM8971_NGATE, 0, 1, 0),
+
+	SOC_SINGLE("Capture 6dB Attenuate", WM8971_ADCDAC, 8, 1, 0),
+	SOC_SINGLE("Playback 6dB Attenuate", WM8971_ADCDAC, 7, 1, 0),
+
+    SOC_ENUM("Playback De-emphasis", wm8971_enum[5]),
+	SOC_ENUM("Playback Function", wm8971_enum[6]),
+	SOC_ENUM("Playback Phase", wm8971_enum[7]),
+	
+	SOC_SINGLE("Left Mic Boost", WM8971_LADCIN, 4, 3, 0),
+	SOC_SINGLE("Right Mic Boost", WM8971_RADCIN, 4, 3, 0),
+};
+
+
+/* add non-DPM controls */
+static int wm8971_add_controls(struct snd_soc_codec *codec)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(wm8971_snd_controls); i++) {
+		if ((err = snd_ctl_add(codec->card, snd_soc_cnew(&wm8971_snd_controls[i],codec, NULL))) < 0) {
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * DPM Controls
+ */
+
+/* Left Mixer */
+static const snd_kcontrol_new_t wm8971_left_mixer_controls[] = {
+SOC_DPM_SINGLE("Playback Switch", WM8971_LOUTM1, 8, 1, 0),
+SOC_DPM_SINGLE("Left Bypass Switch", WM8971_LOUTM1, 7, 1, 0),
+SOC_DPM_SINGLE("Right Playback Switch", WM8971_LOUTM2, 8, 1, 0),
+SOC_DPM_SINGLE("Right Bypass Switch", WM8971_LOUTM2, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const snd_kcontrol_new_t wm8971_right_mixer_controls[] = {
+SOC_DPM_SINGLE("Left Playback Switch", WM8971_ROUTM1, 8, 1, 0),
+SOC_DPM_SINGLE("Left Bypass Switch", WM8971_ROUTM1, 7, 1, 0),
+SOC_DPM_SINGLE("Playback Switch", WM8971_ROUTM2, 8, 1, 0),
+SOC_DPM_SINGLE("Right Bypass Switch", WM8971_ROUTM2, 7, 1, 0),
+};
+
+/* Mono Mixer */
+static const snd_kcontrol_new_t wm8971_mono_mixer_controls[] = {
+SOC_DPM_SINGLE("Left Playback Switch", WM8971_MOUTM1, 8, 1, 0),
+SOC_DPM_SINGLE("Left Bypass Switch", WM8971_MOUTM1, 7, 1, 0),
+SOC_DPM_SINGLE("Right Playback Switch", WM8971_MOUTM2, 8, 1, 0),
+SOC_DPM_SINGLE("Right Bypass Switch", WM8971_MOUTM2, 7, 1, 0),
+};
+
+/* Left Line Mux */
+static const snd_kcontrol_new_t wm8971_left_line_controls =
+SOC_DPM_ENUM("Route", wm8971_enum[8]);
+
+/* Right Line Mux */
+static const snd_kcontrol_new_t wm8971_right_line_controls =
+SOC_DPM_ENUM("Route", wm8971_enum[9]);
+
+/* Left PGA Mux */
+static const snd_kcontrol_new_t wm8971_left_pga_controls =
+SOC_DPM_ENUM("Route", wm8971_enum[10]);
+
+/* Right PGA Mux */
+static const snd_kcontrol_new_t wm8971_right_pga_controls =
+SOC_DPM_ENUM("Route", wm8971_enum[11]);
+
+/* Mono ADC Mux */
+static const snd_kcontrol_new_t wm8971_monomux_controls =
+SOC_DPM_ENUM("Route", wm8971_enum[13]);
+
+/* LOUT 1 Volume */
+static const snd_kcontrol_new_t wm8971_lout1_vol_controls =
+SOC_DPM_SINGLE("Left Out1 Playback Volume", WM8971_LOUT1V, 0, 127, 0);
+
+/* ROUT 1 Volume */
+static const snd_kcontrol_new_t wm8971_rout1_vol_controls =
+SOC_DPM_SINGLE("Right Out1 Playback Volume", WM8971_ROUT1V, 0, 127, 0);
+
+/* LOUT 2 Volume */
+static const snd_kcontrol_new_t wm8971_lout2_vol_controls =
+SOC_DPM_SINGLE("Left Out2 Playback Volume", WM8971_LOUT2V, 0, 127, 0);
+
+/* ROUT 2 Volume */
+static const snd_kcontrol_new_t wm8971_rout2_vol_controls =
+SOC_DPM_SINGLE("Right Out2 Playback Volume", WM8971_ROUT2V, 0, 127, 0);
+
+/* Mono Volume */
+static const snd_kcontrol_new_t wm8971_mout_vol_controls =
+SOC_DPM_SINGLE("Mono Playback Volume", WM8971_MOUTV, 0, 127, 0);
+
+static const struct snd_soc_dpm_widget wm8971_dpm_widgets[] = {
+	SND_SOC_DPM_CLOCK("WM8971 Clock", WM8971_PWR1, 0, 1),
+	SND_SOC_DPM_MUTE("DAC Mute", WM8971_ADCDAC, 3, 0),
+	SND_SOC_DPM_MIXER("Left Mixer", -1, 0, 0, &wm8971_left_mixer_controls[0], ARRAY_SIZE(wm8971_left_mixer_controls)),
+	SND_SOC_DPM_MIXER("Right Mixer", -1, 0, 0, &wm8971_right_mixer_controls[0], ARRAY_SIZE(wm8971_right_mixer_controls)),
+	SND_SOC_DPM_MIXER("Mono Mixer", WM8971_PWR2, 2, 0, &wm8971_mono_mixer_controls[0], ARRAY_SIZE(wm8971_mono_mixer_controls)),
+
+	SND_SOC_DPM_VOLUME("Right Out 2", WM8971_PWR2, 3, 0, &wm8971_rout2_vol_controls, 1),
+	SND_SOC_DPM_VOLUME("Left Out 2", WM8971_PWR2, 4, 0, &wm8971_lout2_vol_controls, 1),
+	SND_SOC_DPM_VOLUME("Right Out 1", WM8971_PWR2, 5, 0, &wm8971_rout1_vol_controls, 1),
+	SND_SOC_DPM_VOLUME("Left Out 1", WM8971_PWR2, 6, 0, &wm8971_lout1_vol_controls, 1),
+	SND_SOC_DPM_DAC("Right DAC", "Right Playback", WM8971_PWR2, 7, 0, NULL, 0),
+	SND_SOC_DPM_DAC("Left DAC", "Left Playback", WM8971_PWR2, 8, 0, NULL, 0),
+	SND_SOC_DPM_VOLUME("Mono Out 1", WM8971_PWR2, 2, 0, &wm8971_mout_vol_controls, 1),
+	
+	SND_SOC_DPM_MICBIAS("Mic Bias", WM8971_PWR1, 1, 0),
+	SND_SOC_DPM_ADC("Right ADC", "Right Capture", WM8971_PWR1, 2, 0, NULL, 0),
+	SND_SOC_DPM_ADC("Left ADC", "Left Capture", WM8971_PWR1, 3, 0, NULL, 0),
+
+	SND_SOC_DPM_MUX("Left PGA Mux", WM8971_PWR1, 5, 0, &wm8971_left_pga_controls),
+	SND_SOC_DPM_MUX("Right PGA Mux", WM8971_PWR1, 4, 0, &wm8971_right_pga_controls),
+	SND_SOC_DPM_MUX("Left Line Mux", -1, 0, 0, &wm8971_left_line_controls),
+	SND_SOC_DPM_MUX("Right Line Mux", -1, 0, 0, &wm8971_right_line_controls),
+
+	SND_SOC_DPM_MUX("Left ADC Mux", -1, 0, 0, &wm8971_monomux_controls),
+	SND_SOC_DPM_MUX("Right ADC Mux", -1, 0, 0, &wm8971_monomux_controls),
+
+	SND_SOC_DPM_OUTPUT("LOUT1"),
+	SND_SOC_DPM_OUTPUT("ROUT1"),
+	SND_SOC_DPM_OUTPUT("LOUT2"),
+	SND_SOC_DPM_OUTPUT("ROUT2"),
+	SND_SOC_DPM_OUTPUT("MONO"),
+
+	SND_SOC_DPM_INPUT("LINPUT1"),
+	SND_SOC_DPM_INPUT("RINPUT1"),
+	SND_SOC_DPM_INPUT("MIC"),
+};
+
+static const char* intercon[][3] = {
+	/* left mixer */
+	{"Left Mixer", "Playback Switch", "Left DAC"},
+	{"Left Mixer", "Left Bypass Switch", "Left Line Mux"},
+	{"Left Mixer", "Right Playback Switch", "Right DAC"},
+	{"Left Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+	/* right mixer */
+	{"Right Mixer", "Left Playback Switch", "Left DAC"},
+	{"Right Mixer", "Left Bypass Switch", "Left Line Mux"},
+	{"Right Mixer", "Playback Switch", "Right DAC"},
+	{"Right Mixer", "Right Bypass Switch", "Right Line Mux"},
+
+	/* left out 1 */
+	{"Left Out 1", NULL, "Left Mixer"},
+	{"LOUT1", NULL, "Left Out 1"},
+
+	/* left out 2 */
+	{"Left Out 2", NULL, "Left Mixer"},
+	{"LOUT2", NULL, "Left Out 2"},
+	
+	/* right out 1 */
+	{"Right Out 1", NULL, "Right Mixer"},
+	{"ROUT1", NULL, "Right Out 1"},
+
+	/* right out 2 */
+	{"Right Out 2", NULL, "Right Mixer"},
+	{"ROUT2", NULL, "Right Out 2"},
+
+	/* mono mixer */
+	{"Mono Mixer", "Left Playback Switch", "Left DAC"},
+	{"Mono Mixer", "Left Bypass Switch", "Left Line Mux"},
+	{"Mono Mixer", "Right Playback Switch", "Right DAC"},
+	{"Mono Mixer", "Right Bypass Switch", "Right Line Mux"},
+	
+	/* mono out */
+	{"Mono Out", NULL, "Mono Mixer"},
+	{"MONO1", NULL, "Mono Out"},
+
+	/* Left Line Mux */
+	{"Left Line Mux", "Line", "LINPUT1"},
+	{"Left Line Mux", "PGA", "Left PGA Mux"},
+	{"Left Line Mux", "Differential", "Differential Mux"},
+
+	/* Right Line Mux */
+	{"Right Line Mux", "Line", "RINPUT1"},
+	{"Right Line Mux", "Mic", "MIC"},
+	{"Right Line Mux", "PGA", "Right PGA Mux"},
+	{"Right Line Mux", "Differential", "Differential Mux"},
+
+	/* Left PGA Mux */
+	{"Left PGA Mux", "Line", "LINPUT1"},
+	{"Left PGA Mux", "Differential", "Differential Mux"},
+
+	/* Right PGA Mux */
+	{"Right PGA Mux", "Line", "RINPUT1"},
+	{"Right PGA Mux", "Differential", "Differential Mux"},
+
+	/* Differential Mux */
+	{"Differential Mux", "Line", "LINPUT1"},
+	{"Differential Mux", "Line", "RINPUT1"},
+
+	/* Left ADC Mux */
+	{"Left ADC Mux", "Stereo", "Left PGA Mux"},
+	{"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},
+	{"Left ADC Mux", "Digital Mono", "Left PGA Mux"},
+
+	/* Right ADC Mux */
+	{"Right ADC Mux", "Stereo", "Right PGA Mux"},
+	{"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},
+	{"Right ADC Mux", "Digital Mono", "Right PGA Mux"},
+
+	/* ADC */
+	{"Left ADC", NULL, "Left ADC Mux"},
+	{"Right ADC", NULL, "Right ADC Mux"},
+
+	/* terminator */
+	{NULL, NULL, NULL},
+};
+
+static int wm8971_add_widgets(struct snd_soc_codec *codec)
+{
+	int i;
+	
+	for(i = 0; i < ARRAY_SIZE(wm8971_dpm_widgets); i++) {
+		snd_soc_dpm_new_control(codec, &wm8971_dpm_widgets[i]);
+	}
+	
+	/* set up audio path interconnects */
+	for(i = 0; intercon[i][0] != NULL; i++) {
+		snd_soc_dpm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]);
+	}
+	
+	snd_soc_dpm_sync(codec);
+	return 0;
+}
+
+static int wm8971_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_codec *codec = pcm_c->codec;
+	u16 iface = 0;
+	
+	/* set master/slave audio interface */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_CLOCK_MASK) {
+		case SND_SOC_HWFMT_CBM_CFM:
+			iface |= 0x0040;
+			break;
+		case SND_SOC_HWFMT_CBS_CFS:
+			break;
+	}
+	
+	/* interface format */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_FORMAT_MASK) {
+		case SND_SOC_HWFMT_I2S:
+			iface |= 0x0002;
+			break;
+		case SND_SOC_HWFMT_RIGHT_J:
+			break;
+		case SND_SOC_HWFMT_LEFT_J:
+			iface |= 0x0001;
+			break;
+		case SND_SOC_HWFMT_DSP_A:
+			iface |= 0x0003;
+			break;
+		case SND_SOC_HWFMT_DSP_B:
+			iface |= 0x0013;
+			break;
+	}
+		
+	/* bit size */
+	switch(pcm_c->hw_runtime.hbits) {
+		case SND_SOC_HWBITS(16):
+			break;
+		case SND_SOC_HWBITS(20):
+			iface |= 0x0004;
+			break;
+		case SND_SOC_HWBITS(24):
+			iface |= 0x0008;
+			break;
+		case SND_SOC_HWBITS(32):
+			iface |= 0x000c;
+			break;
+	}
+
+	/* clock inversion */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_INV_MASK) {
+		case SND_SOC_HWFMT_NB_NF:
+			break;
+		case SND_SOC_HWFMT_IB_IF:
+			iface |= 0x0090;
+			break;
+		case SND_SOC_HWFMT_IB_NF:
+			iface |= 0x0080;
+			break;
+		case SND_SOC_HWFMT_NB_IF:
+			iface |= 0x0010;
+			break;
+	}
+	
+	
+	/* set rate */
+	wm8971_write(codec, WM8971_SRATE, pcm_c->hw_runtime.priv1);
+	wm8971_write(codec, WM8971_AUDIO, iface);
+	
+	return 0;
+}
+
+static void wm8971_device_release(struct device * dev)
+{
+}
+
+static int wm8971_dpm_event(struct snd_soc_codec *codec, int event)
+{
+	u16 pwr_reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3f;
+
+	switch (event) {
+		case SNDRV_CTL_POWER_D0: /* full On */
+			/* set vmid to 50k and unmute dac */
+			wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x00c0);
+			break;
+		case SNDRV_CTL_POWER_D1: /* partial On */
+		case SNDRV_CTL_POWER_D2: /* partial On */
+			/* set vmid to 5k for quick power up */
+			wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x01c0);
+			break;
+		case SNDRV_CTL_POWER_D3hot: /* Off, with power */
+			/* mute dac and set vmid to 500k, enable VREF */
+			wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
+			break;
+		case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+			wm8971_write(codec, WM8971_PWR1, 0x0000);
+			break;
+	}
+	
+	return 0;
+}
+
+static struct snd_soc_pcm_codec wm8971_pcm_client[] = {
+	{
+	.name = "WM8971",
+	.playback = {
+		.sname = "Playback",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 |
+		SNDRV_PCM_RATE_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,
+	},
+	.nplayback = 1,
+	.capture = {
+		.sname = "Capture",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | 
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 |
+		SNDRV_PCM_RATE_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,
+	},
+		.ncapture = 1,
+		.ops = {
+			.prepare = wm8971_pcm_prepare,
+		},
+		.hw = {
+			.num_hmodes = ARRAY_SIZE(wm8971_audio),
+			.hmodes = &wm8971_audio[0],
+		},
+	},
+};
+
+static int wm8971_probe(struct snd_soc_codec *codec)
+{
+	int reg;
+
+	/* reset the WM8971 */
+	wm8971_reset(codec);
+
+	/* charge output caps */
+	wm8971_dpm_event(codec, SNDRV_CTL_POWER_D2);
+
+	/* set the update bits */
+	reg = wm8971_read_reg_cache(codec, WM8971_LDAC);
+	wm8971_write(codec, WM8971_LDAC, reg | 0x0100);
+	reg = wm8971_read_reg_cache(codec, WM8971_RDAC);
+	wm8971_write(codec, WM8971_RDAC, reg | 0x0100);
+
+	reg = wm8971_read_reg_cache(codec, WM8971_LOUT1V);
+	wm8971_write(codec, WM8971_LOUT1V, reg | 0x0100);
+	reg = wm8971_read_reg_cache(codec, WM8971_ROUT1V);
+	wm8971_write(codec, WM8971_ROUT1V, reg | 0x0100);
+
+	reg = wm8971_read_reg_cache(codec, WM8971_LOUT2V);
+	wm8971_write(codec, WM8971_LOUT2V, reg | 0x0100);
+	reg = wm8971_read_reg_cache(codec, WM8971_ROUT2V);
+	wm8971_write(codec, WM8971_ROUT2V, reg | 0x0100);
+
+	reg = wm8971_read_reg_cache(codec, WM8971_LINVOL);
+	wm8971_write(codec, WM8971_LINVOL, reg | 0x0100);
+	reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
+	wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
+
+	wm8971_add_controls(codec);
+	wm8971_add_widgets(codec);
+	
+	/* wait for caps to finish charging - liam make this thread */
+	ssleep(1);
+	wm8971_dpm_event(codec, SNDRV_CTL_POWER_D3hot);
+
+	return 0;
+}
+
+
+static void wm8971_remove(struct snd_soc_codec *codec)
+{
+	wm8971_dpm_event(codec, SNDRV_CTL_POWER_D3cold);
+}
+
+
+/*
+ * Attach WM8750 2 wire client 
+ */
+static int wm8971_i2c_probe(struct i2c_adapter *adap, int addr, int kind)
+{		
+	int ret = 0;
+	struct snd_soc_codec *codec;
+	struct i2c_client *i2c;
+		
+	client_template.adapter = adap;
+	client_template.addr = addr;
+	
+	if ((codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+ 
+	if ((i2c = kzalloc(sizeof(struct i2c_client), GFP_KERNEL)) == NULL){
+		kfree(codec);
+		return -ENOMEM;
+	}
+	memcpy(i2c, &client_template, sizeof(struct i2c_client));
+	codec->control_data = i2c;
+	i2c->dev.platform_data = codec;
+
+	if((ret = i2c_attach_client(i2c)) < 0) {
+		err("failed to attach codec at addr %x\n", addr);
+		kfree(i2c);
+		kfree(codec);
+		return ret;
+	}
+
+	codec->name = "WM8971";
+	codec->longname = "WM8971 Portable Codec";
+	codec->owner = THIS_MODULE;
+	codec->pdev.name = "alsa-wm8971";
+	codec->pdev.id = -1;
+	codec->pdev.dev.release = wm8971_device_release;
+	codec->probe = wm8971_probe;
+	codec->remove = wm8971_remove;
+	codec->read = wm8971_read_reg_cache;
+	codec->write = wm8971_write;
+	codec->dpm_event = wm8971_dpm_event;
+	codec->pcms = wm8971_pcm_client;
+	codec->npcms = 1;
+	if ((codec->reg_cache = 
+			kzalloc(sizeof(u16) * ARRAY_SIZE(wm8971_reg), GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+	memcpy(codec->reg_cache, wm8971_reg, sizeof(u16) * ARRAY_SIZE(wm8971_reg));
+
+	if((ret = snd_soc_register_codec(codec)) < 0) {
+		err("can't register codec");
+		i2c_del_driver(&wm8971_i2c_driver);
+		kfree(i2c);
+		kfree(codec->reg_cache);
+		kfree(codec);
+	}
+
+	return ret;
+}
+
+static int wm8971_i2c_detach(struct i2c_client *client)
+{
+	struct snd_soc_codec *codec = 
+		(struct snd_soc_codec*)client->dev.platform_data;
+	
+	i2c_detach_client(client);
+	snd_soc_unregister_codec(codec);
+	kfree(codec->control_data);
+	kfree(codec->reg_cache);
+	kfree(codec);
+	return 0;
+}
+
+static int wm8971_i2c_attach(struct i2c_adapter *adap)
+{
+	return i2c_probe(adap, &addr_data, wm8971_i2c_probe);
+}
+
+/* WM8971 2 Wire layer */ 
+static struct i2c_driver wm8971_i2c_driver = {
+	.name =           "i2c wm8971 driver",
+	.id =             I2C_DRIVERID_WM8971,
+	.flags =          I2C_DF_NOTIFY,
+	.attach_adapter = wm8971_i2c_attach,
+	.detach_client =  wm8971_i2c_detach,
+	.command =        NULL,
+};
+
+static struct i2c_client client_template = {
+	.name =   "WM8971",
+	.flags =  I2C_CLIENT_ALLOW_USE,
+	.driver = &wm8971_i2c_driver,
+};
+
+static int __init wm8971_init(void)
+{
+	int ret = 0;
+
+	info("WM8971 Audio Codec %s", WM8971_VERSION);
+
+	if ((ret = i2c_add_driver(&wm8971_i2c_driver)) != 0)
+		err("can't add i2c driver");
+
+	return ret;
+}
+
+
+static void __exit wm8971_exit(void)
+{
+	i2c_del_driver(&wm8971_i2c_driver);
+}
+
+
+module_init(wm8971_init);
+module_exit(wm8971_exit);
+
+MODULE_DESCRIPTION("SoC WM8971 driver");
+MODULE_AUTHOR("Lab126");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8971.h b/sound/soc/codecs/wm8971.h
new file mode 100644
index 0000000..c6fbe76
--- /dev/null
+++ b/sound/soc/codecs/wm8971.h
@@ -0,0 +1,55 @@
+/*
+ * wm8971.h  --  audio driver for WM8971
+ *
+ * Copyright 2005 Lab126, Inc.
+ *
+ * Author: Kenneth Kiraly <kiraly@lab126.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 _WM8971_H
+#define _WM8971_H
+
+#define WM8971_LINVOL	0x00
+#define WM8971_RINVOL	0x01
+#define WM8971_LOUT1V	0x02
+#define WM8971_ROUT1V	0x03
+#define WM8971_ADCDAC	0x05
+#define WM8971_AUDIO	0x07
+#define WM8971_SRATE	0x08
+#define WM8971_LDAC		0x0a
+#define WM8971_RDAC		0x0b
+#define WM8971_BASS		0x0c
+#define WM8971_TREBLE	0x0d
+#define WM8971_RESET	0x0f
+#define WM8971_ALC1		0x11
+#define	WM8971_ALC2		0x12
+#define	WM8971_ALC3		0x13
+#define WM8971_NGATE	0x14
+#define WM8971_LADC		0x15
+#define WM8971_RADC		0x16
+#define	WM8971_ADCTL1	0x17
+#define	WM8971_ADCTL2	0x18
+#define WM8971_PWR1		0x19
+#define WM8971_PWR2		0x1a
+#define	WM8971_ADCTL3	0x1b
+#define WM8971_ADCIN	0x1f
+#define	WM8971_LADCIN	0x20
+#define	WM8971_RADCIN	0x21
+#define WM8971_LOUTM1	0x22
+#define WM8971_LOUTM2	0x23
+#define WM8971_ROUTM1	0x24
+#define WM8971_ROUTM2	0x25
+#define WM8971_MOUTM1	0x26
+#define WM8971_MOUTM2	0x27
+#define WM8971_LOUT2V	0x28
+#define WM8971_ROUT2V	0x29
+#define WM8971_MOUTV	0x2A
+
+#endif
+
diff --git a/sound/soc/codecs/wm9713-voice.c b/sound/soc/codecs/wm9713-voice.c
new file mode 100644
index 0000000..2775e98
--- /dev/null
+++ b/sound/soc/codecs/wm9713-voice.c
@@ -0,0 +1,277 @@
+/*
+ * wm9713-voice.c  --  PXA WM9713 PCM audio driver 
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
+ *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
+ *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
+ *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
+ *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *  You should have received a copy of the  GNU General Public License along
+ *  with this program; if not, write  to the Free Software Foundation, Inc.,
+ *  675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Revision history
+ *    1st Aug 2005   Initial version.
+ * 
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/wm97xx.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+
+#define NAME		"wm9713-voice"
+#define VERSION		"0.1"
+
+/*
+ * Debug
+ */ 
+#if 0
+#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg)
+#else	/* 
+ */
+#define dbg(format, arg...)
+#endif	/* 
+ */
+#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg)
+
+#define WM9713_PCM_8K		(0xb << 8)
+#define WM9713_PCM_12K		(0x7 << 8)
+#define WM9713_PCM_16K		(0x5 << 8)
+#define WM9713_PCM_24K		(0x3 << 8)
+#define WM9713_PCM_32K		(0x2 << 8)
+#define WM9713_PCM_48K		(0x1 << 8)
+#define WM9713_PCM_96K		(0x0 << 8)
+
+#define WM9713_VOICE_HWFMT \
+	(SND_SOC_HWFMT_I2S | SND_SOC_HWFMT_RIGHT_J | SND_SOC_HWFMT_LEFT_J | \
+	SND_SOC_HWFMT_DSP_A | SND_SOC_HWFMT_DSP_B | SND_SOC_HWFMT_CBM_CFM | \
+	SND_SOC_HWFMT_CBM_CFS | SND_SOC_HWFMT_CBS_CFS | SND_SOC_HWFMT_NB_NF | \
+	SND_SOC_HWFMT_NB_IF | SND_SOC_HWFMT_IB_NF | SND_SOC_HWFMT_IB_IF)
+
+/*
+ * priv1 is pcm clock divider
+ */
+static struct snd_soc_hw_mode wm9713_voice[] = {
+	{WM9713_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		48000,	256,	WM9713_PCM_48K, },
+	{WM9713_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		32000,	384,	WM9713_PCM_32K,},
+	{WM9713_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		24000,	256,	WM9713_PCM_24K,},
+	{WM9713_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		16000,	256,	WM9713_PCM_16K,},
+	{WM9713_VOICE_HWFMT, 	SND_SOC_HWBITS(16),		8000,	256,	WM9713_PCM_8K,},
+};
+	
+struct wm9713_pcm {
+	int pcm_rate;
+	struct wm97xx *wm;
+	snd_pcm_t *pcm_voice;
+};
+
+static struct wm9713_pcm wm9713;
+
+static int wm9713_voice_startup(snd_pcm_substream_t *substream)
+{
+	int reg;
+
+	/* power up clock control and voice DAC */
+	reg = wm97xx_reg_read(wm9713.wm, AC97_POWERDOWN); 
+	wm97xx_reg_write(wm9713.wm, AC97_POWERDOWN, reg & ~0x2000);
+	reg = wm97xx_reg_read(wm9713.wm, AC97_EXTENDED_MID); 
+	wm97xx_reg_write(wm9713.wm, AC97_EXTENDED_MID, reg & ~0x1000);
+
+	return 0;
+}
+
+static void wm9713_voice_shutdown(snd_pcm_substream_t *substream)
+{
+}
+
+static int wm9713_voice_prepare(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	u16 reg = 0x8000;
+
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_CLOCK_MASK){
+		case SND_SOC_HWFMT_CBM_CFM:
+			reg |= 0x4000;
+			break;
+		case SND_SOC_HWFMT_CBM_CFS:
+			reg |= 0x6000;
+			break;
+		case SND_SOC_HWFMT_CBS_CFS:
+			reg |= 0x0200;
+			break;
+	}
+	/* clock inversion */
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_INV_MASK) {
+		case SND_SOC_HWFMT_IB_IF:
+			reg |= 0x00c0;
+			break;
+		case SND_SOC_HWFMT_IB_NF:
+			reg |= 0x0080;
+			break;
+		case SND_SOC_HWFMT_NB_IF:
+			reg |= 0x0040;
+			break;
+	}
+	
+	switch(pcm_c->hw_runtime.hformat & SND_SOC_FORMAT_MASK) {
+		case SND_SOC_HWFMT_I2S:
+			reg |= 0x0002;
+			break;
+		case SND_SOC_HWFMT_RIGHT_J:
+			break;
+		case SND_SOC_HWFMT_LEFT_J:
+			reg |= 0x0001;
+			break;
+		case SND_SOC_HWFMT_DSP_A:
+			reg |= 0x0003;
+			break;
+		case SND_SOC_HWFMT_DSP_B:
+			reg |= 0x0043;
+			break;
+	}
+	switch(pcm_c->hw_runtime.hbits) {
+		case SND_SOC_HWBITS(16):
+			break;
+		case SND_SOC_HWBITS(20):
+			reg |= 0x0004;
+			break;
+		case SND_SOC_HWBITS(24):
+			reg |= 0x0008;
+			break;
+		case SND_SOC_HWBITS(32):
+			reg |= 0x000c;
+			break;
+	}
+	/* enable PCM interface in master mode */
+	wm97xx_reg_write(wm9713.wm, AC97_CENTER_LFE_MASTER, reg);
+	
+	/* sample rate */
+	reg = wm97xx_reg_read(wm9713.wm, AC97_HANDSET_RATE) &0xe0ff;
+	wm97xx_reg_write(wm9713.wm, AC97_HANDSET_RATE, reg | pcm_c->hw_runtime.priv1);
+	return 0;
+}
+
+static struct snd_soc_pcm_codec wm9713_voice_client = {
+	.name = "WM9713 Voice",
+	.playback = {
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 1,},
+	.capture = {
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | 
+		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 |
+		SNDRV_PCM_RATE_96000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 1,},
+	.ops = {
+		.startup = wm9713_voice_startup,
+		.shutdown = wm9713_voice_shutdown,
+		.prepare = wm9713_voice_prepare,},
+	.hw = {
+		.num_hmodes = ARRAY_SIZE(wm9713_voice),
+		.hmodes = &wm9713_voice[0],
+		},
+};
+
+static void wm9713_device_release(struct device * dev)
+{
+}
+
+static struct snd_soc_codec wm9713_soc_codec;
+
+static int wm9713_soc_probe(struct snd_soc_codec *codec)
+{
+	int ret;
+
+	//ret = snd_soc_pcm_new(codec, 0, &wm9713_voice_client, 
+	//		&wm9713.pcm_voice, 1, 1, SND_SOC_SSP, 1);
+	return ret;
+}
+
+static void wm9713_soc_remove(struct snd_soc_codec *codec)
+{
+}
+
+static struct snd_soc_codec wm9713_soc_codec = {
+	.name = "WM9713 Voice",
+	.longname = "WM9713 Portable Voice Codec",
+	.owner = THIS_MODULE,
+	.pdev = {
+		.name		= "alsa-wm9713",
+		.id		= -1,
+		.dev		= {.release = wm9713_device_release},
+	},
+	.probe = wm9713_soc_probe,
+	.remove = wm9713_soc_remove,
+};
+
+static int wm97xx_wm9713_probe(struct wm97xx* wm)
+{	
+	if (wm->ac97 == NULL)
+		return -ENODEV;
+	
+	wm9713.wm = wm;
+	return snd_soc_register_codec(&wm9713_soc_codec);
+}
+
+static void wm97xx_wm9713_remove(struct wm97xx* wm)
+{
+	snd_soc_unregister_codec(&wm9713_soc_codec);
+	wm9713.wm = NULL;
+}
+
+static struct wm97xx_platform_drv pxa_wm9713 = {
+	.id = 0x4c13,
+	.mask = 0xffff,
+	.probe = 	wm97xx_wm9713_probe,
+	.remove = 	wm97xx_wm9713_remove,
+};
+
+static int __init pxa_wm9713_init(void)
+{
+	wm97xx_register_platform_drv(&pxa_wm9713);
+	return 0;
+}
+
+static void __exit pxa_wm9713_exit(void)
+{
+	wm97xx_unregister_platform_drv(&pxa_wm9713);
+}
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
+MODULE_DESCRIPTION("pxa2xx WM9713 PCM driver");
+MODULE_LICENSE("GPL");
+
+module_init(pxa_wm9713_init);
+module_exit(pxa_wm9713_exit);
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
new file mode 100644
index 0000000..aec10ec
--- /dev/null
+++ b/sound/soc/pxa/Kconfig
@@ -0,0 +1,46 @@
+config SND_PXA2xx_SOC
+	tristate "SoC Audio for the Intel PXA2xx chip"
+	depends on ARCH_PXA && SND
+	select SND_PCM
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the PXA2xx AC97, I2S or SSP interface. You will also need
+	  to select the audio interfaces to support below.
+
+config SND_PXA2xx_SOC_AC97
+	tristate "SoC AC97 driver for the Intel PXA2xx chip"
+	depends on SND_PXA2xx_SOC
+	select SND_AC97_CODEC
+	select SND_PCM
+	help
+	  Say Y or M if you want to add support for AC97 codecs attached to
+	  the PXA2xx AC97 interface.
+	  
+config SND_PXA2xx_SOC_I2S
+	tristate "SoC I2S driver for the Intel PXA2xx chip"
+	depends on SND_PXA2xx_SOC
+	select SND_PCM
+	help
+	  Say Y or M if you want to add support for I2S codecs attached to
+	  the PXA2xx I2S interface.
+	  
+config SND_PXA2xx_SOC_SSP
+	tristate "SoC SSP driver for the Intel PXA2xx chip"
+	depends on SND_PXA2xx_SOC
+	select SND_PCM
+	select PXA_SSP
+	help
+	  Say Y or M if you want to add support for PCM codecs attached to
+	  the PXA2xx SSP interface.
+
+# Supported machines
+menu "PXA2xx machine support"
+depends on SND_PXA2xx_SOC
+
+config SND_PXA2xx_SOC_MAINSTONE
+	tristate "SoC Audio support for Intel Mainstone"
+	depends on SND_PXA2xx_SOC
+	help
+	  Say Y or M if you want to add support for SoC audio on Mainstone.
+	  
+endmenu
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
new file mode 100644
index 0000000..f68febe
--- /dev/null
+++ b/sound/soc/pxa/Makefile
@@ -0,0 +1,13 @@
+
+snd-pxa2xx-soc-objs := pxa2xx-pcm.o 
+snd-pxa2xx-soc-ac97-objs := pxa2xx-ac97.o 
+snd-pxa2xx-soc-i2s-objs := pxa2xx-i2s.o 
+snd-pxa2xx-soc-ssp-objs := pxa2xx-ssp.o
+snd-pxa2xx-soc-mainstone-objs := mainstone.o
+
+obj-$(CONFIG_SND_PXA2xx_SOC) += snd-pxa2xx-soc.o
+obj-$(CONFIG_SND_PXA2xx_SOC_AC97) += snd-pxa2xx-soc-ac97.o
+obj-$(CONFIG_SND_PXA2xx_SOC_I2S) += snd-pxa2xx-soc-i2s.o
+obj-$(CONFIG_SND_PXA2xx_SOC_SSP) += snd-pxa2xx-soc-ssp.o
+
+obj-$(CONFIG_SND_PXA2xx_SOC_MAINSTONE) += snd-pxa2xx-soc-mainstone.o
\ No newline at end of file
diff --git a/sound/soc/pxa/mainstone.c b/sound/soc/pxa/mainstone.c
new file mode 100644
index 0000000..03bfced
--- /dev/null
+++ b/sound/soc/pxa/mainstone.c
@@ -0,0 +1,224 @@
+/*
+ * mainstone.c  --  SoC audio for Mainstone
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  Mainstone audio amplifier code taken from arch/arm/mach-pxa/mainstone.c
+ *  Copyright:	MontaVista Software Inc.
+ * 
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    30th Oct 2005   Initial version.
+ * 
+ */
+ 
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dpm.h>
+
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/mainstone.h>
+#include <asm/arch/audio.h>
+
+#include "../codecs/wm8753.h"
+
+static int mainstone_startup(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	
+	if(pcm_i->type == SND_SOC_SSP && pcm_i->id == 1) {
+		/* enable USB on the go MUX so we can use SSPFRM2 */
+		MST_MSCWR2 |= MST_MSCWR2_USB_OTG_SEL;
+		MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_RST;
+	}
+	return 0;
+}
+
+static void mainstone_shutdown(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+			
+	if(pcm_i->type == SND_SOC_SSP && pcm_i->id == 1) {
+		/* disable USB on the go MUX so we can use ttyS0 */
+		MST_MSCWR2 &= ~MST_MSCWR2_USB_OTG_SEL;
+		MST_MSCWR2 |= MST_MSCWR2_USB_OTG_RST;
+	}
+}
+
+static struct snd_soc_ops mainstone_ops = {
+	.startup = mainstone_startup,
+	.shutdown = mainstone_shutdown,
+};
+
+static long mst_audio_suspend_mask;
+
+static int mainstone_suspend(struct snd_soc_machine *machine, pm_message_t state)
+{
+	mst_audio_suspend_mask = MST_MSCWR2;
+	MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
+	return 0;
+}
+
+static int mainstone_resume(struct snd_soc_machine *machine)
+{
+	MST_MSCWR2 &= mst_audio_suspend_mask | ~MST_MSCWR2_AC97_SPKROFF;
+	return 0;
+}
+
+static int mainstone_probe(struct snd_soc_machine *machine)
+{
+	MST_MSCWR2 &= ~MST_MSCWR2_AC97_SPKROFF;
+	return 0;
+}
+
+static int mainstone_remove(struct snd_soc_machine *machine)
+{
+	MST_MSCWR2 |= MST_MSCWR2_AC97_SPKROFF;
+	return 0;
+}
+
+/* example external dpm event callback */
+int external_amp_event(struct snd_soc_codec *codec, int event)
+{
+	return 0;
+}
+
+/* example machine dpm widgets */
+static const struct snd_soc_dpm_widget wm8753_dpm_widgets[] = {
+SND_SOC_DPM_HP("Headphone Jack"),
+SND_SOC_DPM_MIC("Mic1 Jack"),
+SND_SOC_DPM_AMP("Ext Amp", external_amp_event),
+};
+
+/* example machine interconnections */
+static const char* intercon[][3] = {
+	
+	/* headphone connected to LOUT1, ROUT1 */
+	{"Headphone Jack", NULL, "LOUT1"},
+	{"Headphone Jack", NULL, "ROUT1"},
+	
+	/* ext speaker connected to LOUT2, ROUT2 via amp */
+	{"Ext Amp", NULL, "LOUT2"},
+	{"Ext Amp", NULL, "ROUT2"},
+	
+	/* mic is connected to mic1 - with bias */
+	{"MIC1", NULL, "Mic Bias"},
+	{"MIC1N", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Mic1 Jack"},
+	{"Mic Bias", NULL, "Mic1 Jack"},
+	
+	{"ACIN", NULL, "ACOP"},
+	{NULL, NULL, NULL},
+};
+
+/* example unconnected codec pins */
+static const struct snd_soc_dpm_pin wm8753_mainstone_pins[] = {
+	{"RXP", 0},
+	{"RXN", 0},
+	{"MIC2", 0},
+};
+
+/* headphone detect support on my board */
+static const char * hp_pol[] = {"Headphone", "Speaker"};
+static const struct soc_enum wm8753_enum =
+	SOC_ENUM_SINGLE(WM8753_OUTCTL, 1, 2, hp_pol);
+
+static const snd_kcontrol_new_t wm8753_mainstone_controls[] = {
+	SOC_SINGLE("Headphone Detect Switch", WM8753_OUTCTL, 6, 1, 0),
+	SOC_ENUM("Headphone Detect Polarity", wm8753_enum),
+};
+
+/*
+ * This is an example machine initialisation for a wm8753 connected to a 
+ * Mainstone II. It is missing logic to detect hp/mic insertions and logic
+ * to re-route the audio in such an event.
+ */
+
+static int mainstone_wm8753_init(struct snd_soc_codec *codec)
+{
+	int i, err;
+	
+	/* set up mainstone codec pins */
+	for (i = 0; i < ARRAY_SIZE(wm8753_mainstone_pins); i++)
+		snd_soc_dpm_set_connection(codec, &wm8753_mainstone_pins[i]);
+	
+	/* add mainstone specific controls */
+	for (i = 0; i < ARRAY_SIZE(wm8753_mainstone_controls); i++) {
+		if ((err = snd_ctl_add(codec->card, snd_soc_cnew(&wm8753_mainstone_controls[i],codec, NULL))) < 0)
+			return err;
+	}
+	
+	/* add mainstone specific widgets */
+	for(i = 0; i < ARRAY_SIZE(wm8753_dpm_widgets); i++) {
+		snd_soc_dpm_new_control(codec, &wm8753_dpm_widgets[i]);
+	}
+	
+	/* set up mainstone specific audio path interconnects */
+	for(i = 0; intercon[i][0] != NULL; i++) {
+		snd_soc_dpm_connect_input(codec, intercon[i][0], intercon[i][1], intercon[i][2]);
+	}
+	snd_soc_dpm_sync(codec);
+	
+	return 0;
+}
+
+/* I've added all my different configs here atm for easier testing on ms II */
+static struct snd_soc_machine_config codecs[] = {
+{
+	.name = "WM8753", 
+	.sname = "WM8753 HiFi", 
+	.interface = SND_SOC_I2S, 
+	.int_num = 0,
+	.init = mainstone_wm8753_init,
+},
+#if 0
+	{.name = "WM8971", .sname = "WM8971", SND_SOC_I2S, 0},
+	{.name = "WM8750", .sname = "WM8750", SND_SOC_I2S, 0},
+	{.name = "WM8731", .sname = "WM8731", SND_SOC_I2S, 0},
+	{.name = "WM8753", .sname = "WM8753 Voice", SND_SOC_SSP, 1},
+	{.name = "AC97", .sname = "AC97 Audio", SND_SOC_AC97, 0},
+#endif
+};
+
+static struct snd_soc_machine mainstone = {
+	.name = "Mainstone",
+	.probe = mainstone_probe,
+	.remove = mainstone_remove,
+	.suspend = mainstone_suspend,
+	.resume = mainstone_resume,
+	.ops = &mainstone_ops,
+	.config = codecs,
+	.nconfigs = ARRAY_SIZE(codecs),
+};
+
+static int __init snd_soc_init(void) 
+{
+	return snd_soc_register_machine(&mainstone);
+}
+
+static void __exit snd_soc_exit(void) 
+{
+	snd_soc_unregister_machine(&mainstone);
+}
+
+module_init(snd_soc_init); 
+module_exit(snd_soc_exit);
+
+/* Module information */ 
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("ALSA SoC Mainstone");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
new file mode 100644
index 0000000..235e866
--- /dev/null
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -0,0 +1,396 @@
+/*
+ * linux/sound/pxa2xx-ac97.c -- AC97 support for the Intel PXA2xx chip.
+ *
+ * Author:	Nicolas Pitre
+ * Created:	Dec 02, 2004
+ * Copyright:	MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/irq.h>
+#include <asm/semaphore.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/audio.h>
+
+#include "pxa2xx-pcm.h"
+
+static DECLARE_MUTEX(car_mutex);
+static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
+static volatile long gsr_bits;
+
+/*
+ * Beware PXA27x bugs:
+ *
+ *   o Slot 12 read from modem space will hang controller.
+ *   o CDONE, SDONE interrupt fails after any slot 12 IO.
+ *
+ * We therefore have an hybrid approach for waiting on SDONE (interrupt or
+ * 1 jiffy timeout if interrupt never comes).
+ */ 
+
+static unsigned short pxa2xx_ac97_read(ac97_t *ac97, unsigned short reg)
+{
+	unsigned short val = -1;
+	volatile unsigned long *reg_addr;
+
+	down(&car_mutex);
+
+	/* set up primary or secondary codec/modem space */
+#ifdef CONFIG_PXA27x
+	reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
+#else
+	if (reg == AC97_GPIO_STATUS)
+		reg_addr = (ac97->num & 1) ? &SMC_REG_BASE : &PMC_REG_BASE;
+	else
+		reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
+#endif
+	reg_addr += (reg >> 1);
+
+#ifndef CONFIG_PXA27x
+	if (reg == AC97_GPIO_STATUS) {
+		/* read from controller cache */
+		val = *reg_addr;
+		goto out;
+	}
+#endif
+	
+	/* start read access across the ac97 link */
+	GSR = GSR_CDONE | GSR_SDONE;
+	gsr_bits = 0;
+	val = *reg_addr;
+
+	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
+	if (!((GSR | gsr_bits) & GSR_SDONE)) {
+		printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
+				__FUNCTION__, reg, GSR | gsr_bits);
+		val = -1;
+		goto out;
+	}
+
+	/* valid data now */
+	GSR = GSR_CDONE | GSR_SDONE;
+	gsr_bits = 0;
+	val = *reg_addr;			
+	/* but we've just started another cycle... */
+	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
+
+out:	up(&car_mutex);
+	return val;
+}
+
+static void pxa2xx_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+	volatile unsigned long *reg_addr;
+
+	down(&car_mutex);
+
+	/* set up primary or secondary codec/modem space */
+#ifdef CONFIG_PXA27x
+	reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
+#else
+	if (reg == AC97_GPIO_STATUS)
+		reg_addr = (ac97->num & 1) ? &SMC_REG_BASE : &PMC_REG_BASE;
+	else
+		reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
+#endif
+	reg_addr += (reg >> 1);
+
+	GSR = GSR_CDONE | GSR_SDONE;
+	gsr_bits = 0;
+	*reg_addr = val;
+	wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1);
+	if (!((GSR | gsr_bits) & GSR_CDONE))
+		printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
+				__FUNCTION__, reg, GSR | gsr_bits);
+
+	up(&car_mutex);
+}
+
+static void pxa2xx_ac97_reset(ac97_t *ac97)
+{
+	/* First, try cold reset */
+	GCR &=  GCR_COLD_RST;  /* clear everything but nCRST */
+	GCR &= ~GCR_COLD_RST;  /* then assert nCRST */
+
+	gsr_bits = 0;
+#ifdef CONFIG_PXA27x
+	/* PXA27x Developers Manual section 13.5.2.2.1 */
+	pxa_set_cken(1 << 31, 1);
+	udelay(5);
+	pxa_set_cken(1 << 31, 0);
+	GCR = GCR_COLD_RST;
+	udelay(50);
+#else
+	GCR = GCR_COLD_RST;
+	GCR |= GCR_CDONE_IE|GCR_SDONE_IE;
+	wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+#endif
+
+	if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR))) {
+		printk(KERN_INFO "%s: cold reset timeout (GSR=%#lx)\n",
+				 __FUNCTION__, gsr_bits);
+
+		/* let's try warm reset */
+		gsr_bits = 0;
+#ifdef CONFIG_PXA27x
+		/* warm reset broken on Bulverde,
+		   so manually keep AC97 reset high */
+		pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); 
+		udelay(10);
+		GCR |= GCR_WARM_RST;
+		pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+		udelay(500);
+#else
+		GCR |= GCR_WARM_RST|GCR_PRIRDY_IEN|GCR_SECRDY_IEN;
+		wait_event_timeout(gsr_wq, gsr_bits & (GSR_PCR | GSR_SCR), 1);
+#endif			
+
+		if (!((GSR | gsr_bits) & (GSR_PCR | GSR_SCR)))
+			printk(KERN_INFO "%s: warm reset timeout (GSR=%#lx)\n",
+					 __FUNCTION__, gsr_bits);
+	}
+
+	GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN);
+	GCR |= GCR_SDONE_IE|GCR_CDONE_IE;
+}
+
+static irqreturn_t pxa2xx_ac97_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	long status;
+
+	status = GSR;
+	if (status) {
+		GSR = status;
+		gsr_bits |= status;
+		wake_up(&gsr_wq);
+
+#ifdef CONFIG_PXA27x
+		/* Although we don't use those we still need to clear them
+		   since they tend to spuriously trigger when MMC is used
+		   (hardware bug? go figure)... */
+		MISR = MISR_EOC;
+		PISR = PISR_EOC;
+		MCSR = MCSR_EOC;
+#endif
+
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static ac97_bus_ops_t pxa2xx_ac97_ops = {
+	.read	= pxa2xx_ac97_read,
+	.write	= pxa2xx_ac97_write,
+	.reset	= pxa2xx_ac97_reset,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ac97_pcm_stereo_out = {
+	.name			= "AC97 PCM Stereo out",
+	.dev_addr		= __PREG(PCDR),
+	.drcmr			= &DRCMRTXPCDR,
+	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ac97_pcm_stereo_in = {
+	.name			= "AC97 PCM Stereo in",
+	.dev_addr		= __PREG(PCDR),
+	.drcmr			= &DRCMRRXPCDR,
+	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ac97_pcm_mono_out = {
+	.name			= "AC97 PCM Mono out",
+	.dev_addr		= __PREG(PCDR),
+	.drcmr			= &DRCMRTXPCDR,
+	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ac97_pcm_mono_in = {
+	.name			= "AC97 PCM Mono in",
+	.dev_addr		= __PREG(PCDR),
+	.drcmr			= &DRCMRRXPCDR,
+	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+#ifdef CONFIG_PM
+
+static int pxa2xx_ac97_suspend(struct snd_soc_pcm_interface *interface, pm_message_t state)
+{
+	snd_card_t *card = interface->card;
+	
+	if (card->power_state != SNDRV_CTL_POWER_D3cold) {
+		pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
+		
+		if (platform_ops && platform_ops->suspend)
+			platform_ops->suspend(platform_ops->priv);
+		GCR |= GCR_ACLINK_OFF;
+		pxa_set_cken(CKEN2_AC97, 0);
+	}
+	
+	return 0;
+}
+
+static int pxa2xx_ac97_resume(struct snd_soc_pcm_interface *interface)
+{
+	snd_card_t *card = interface->card;
+
+	if (card->power_state != SNDRV_CTL_POWER_D0) {
+		pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
+		pxa_set_cken(CKEN2_AC97, 1);
+		if (platform_ops && platform_ops->resume)
+			platform_ops->resume(platform_ops->priv);
+	}
+	return 0;
+}
+
+#else
+#define pxa2xx_ac97_suspend	NULL
+#define pxa2xx_ac97_resume	NULL
+#endif
+
+static int pxa2xx_ac97_probe(struct snd_soc_pcm_interface *interface)
+{
+	ac97_bus_t *ac97_bus;
+	ac97_template_t ac97_template;
+	int ret;
+
+	ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, 0, "AC97", NULL);
+	if (ret < 0)
+		goto err;
+
+	pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
+	pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD);
+	pxa_gpio_mode(GPIO28_BITCLK_AC97_MD);
+	pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+#ifdef CONFIG_PXA27x
+	/* Use GPIO 113 as AC97 Reset on Bulverde */
+	pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
+#endif
+	pxa_set_cken(CKEN2_AC97, 1);
+
+	ret = snd_ac97_bus(interface->card, 0, &pxa2xx_ac97_ops, NULL, &ac97_bus);
+	if (ret)
+		return ret;
+	memset(&ac97_template, 0, sizeof(ac97_template));
+	ret = snd_ac97_mixer(ac97_bus, &ac97_template, &interface->ac97);
+
+	return 0;
+
+ err:
+	if (CKEN & CKEN2_AC97) {
+		GCR |= GCR_ACLINK_OFF;
+		free_irq(IRQ_AC97, NULL);
+		pxa_set_cken(CKEN2_AC97, 0);
+	}
+	return ret;
+}
+
+static void pxa2xx_ac97_remove(struct snd_soc_pcm_interface *interface)
+{
+	GCR |= GCR_ACLINK_OFF;
+	free_irq(IRQ_AC97, NULL);
+	pxa_set_cken(CKEN2_AC97, 0);
+}
+
+static int pxa2xx_ac97_prepare(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	
+	int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+		  AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
+	return snd_ac97_set_rate(pcm_i->ac97, reg, runtime->rate);
+}
+
+static int pxa2xx_ac97_hw_params(snd_pcm_substream_t *substream,
+				snd_pcm_hw_params_t *params)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (params_channels(params) == 2)
+			pcm_i->platform_data = &pxa2xx_ac97_pcm_stereo_out;
+		else
+			pcm_i->platform_data = &pxa2xx_ac97_pcm_mono_out;
+	} else {
+		if (params_channels(params) == 2)
+			pcm_i->platform_data = &pxa2xx_ac97_pcm_stereo_in;
+		else
+			pcm_i->platform_data = &pxa2xx_ac97_pcm_mono_in;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_pcm_interface pxa_ac97_interface[] = {
+	{	.name = "pxa2xx-ac97",
+		.id = 0,
+		.type = SND_SOC_AC97,
+		.probe = pxa2xx_ac97_probe,
+		.remove = pxa2xx_ac97_remove,
+		.suspend = pxa2xx_ac97_suspend,
+		.resume = pxa2xx_ac97_resume,
+		.playback = {
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | 
+			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 2,
+			.channels_max = 2,},
+		.capture = {
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | 
+			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 2,
+			.channels_max = 2,},
+		.ops = {
+			.prepare = pxa2xx_ac97_prepare,
+			.hw_params = pxa2xx_ac97_hw_params,},
+	},
+};
+
+static int __init pxa2xx_ac97_init(void)
+{
+	return snd_soc_register_interface(pxa_ac97_interface);
+}
+
+static void __exit pxa2xx_ac97_exit(void)
+{
+	snd_soc_unregister_interface(pxa_ac97_interface);
+}
+
+EXPORT_SYMBOL_GPL(pxa_ac97_interface);
+
+module_init(pxa2xx_ac97_init);
+module_exit(pxa2xx_ac97_exit);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("AC97 driver for the Intel PXA2xx chip");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
new file mode 100644
index 0000000..21f27c3
--- /dev/null
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -0,0 +1,300 @@
+/*
+ * pxa2xx-i2s.c  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    12th Aug 2005   Initial version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/audio.h>
+
+#include "pxa2xx-pcm.h"
+
+#define PXA_I2S_HWFMT \
+	(SND_SOC_HWFMT_I2S | SND_SOC_HWFMT_LEFT_J  | SND_SOC_HWFMT_CBS_CFS | \
+	SND_SOC_HWFMT_CBM_CFS | SND_SOC_HWFMT_NB_NF)
+	
+#define PXA_I2S_HWFMT_96K \
+	(SND_SOC_HWFMT_I2S | SND_SOC_HWFMT_LEFT_J  | \
+	SND_SOC_HWFMT_CBM_CFS | SND_SOC_HWFMT_NB_NF)
+
+static DECLARE_MUTEX(pxa_i2s_sem);
+static int i2s_open = 0;
+
+/*
+ * priv1 is sadiv
+ */
+static struct snd_soc_hw_mode pxa2xx_i2s[] = {
+	{PXA_I2S_HWFMT, 	SND_SOC_HWBITS(16),		8000,	64,		0x00000048},
+	{PXA_I2S_HWFMT, 	SND_SOC_HWBITS(16),		11025,	64,		0x00000034},
+	{PXA_I2S_HWFMT, 	SND_SOC_HWBITS(16),		16000,	64,		0x00000024},
+	{PXA_I2S_HWFMT, 	SND_SOC_HWBITS(16),		22050,	64,		0x0000001a},
+	{PXA_I2S_HWFMT, 	SND_SOC_HWBITS(16),		44100,	64,		0x0000000d},
+	{PXA_I2S_HWFMT, 	SND_SOC_HWBITS(16),		48000,	64,		0x0000000c},
+	/* 96k is not in yellow book - ymmv */
+	{PXA_I2S_HWFMT_96K, 	SND_SOC_HWBITS(16),	96000,	64,		0x00000006},
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_i2s_pcm_stereo_out = {
+	.name			= "I2S PCM Stereo out",
+	.dev_addr		= __PREG(SADR),
+	.drcmr			= &DRCMRTXSADR,
+	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_i2s_pcm_stereo_in = {
+	.name			= "I2S PCM Stereo in",
+	.dev_addr		= __PREG(SADR),
+	.drcmr			= &DRCMRRXSADR,
+	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_i2s_pcm_mono_out = {
+	.name			= "I2S PCM Mono out",
+	.dev_addr		= __PREG(SADR),
+	.drcmr			= &DRCMRTXSADR,
+	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
+				  DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_i2s_pcm_mono_in = {
+	.name			= "I2S PCM Mono in",
+	.dev_addr		= __PREG(SADR),
+	.drcmr			= &DRCMRRXSADR,
+	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
+				  DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static pxa2xx_gpio_t gpio_bus[] = {
+	{ /* I2S SoC Slave */
+		.rx = GPIO29_SDATA_IN_I2S_MD,
+		.tx = GPIO30_SDATA_OUT_I2S_MD,
+		.clk = GPIO28_BITCLK_IN_I2S_MD,
+		.frm = GPIO31_SYNC_I2S_MD,
+	},
+	{ /* I2S SoC Master */
+		.rx = GPIO29_SDATA_IN_I2S_MD,
+		.tx = GPIO30_SDATA_OUT_I2S_MD,
+		.clk = GPIO28_BITCLK_OUT_I2S_MD,
+		.frm = GPIO31_SYNC_I2S_MD,
+	},
+};
+
+static u32 get_sadiv(int rate)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(pxa2xx_i2s); i++) {
+		if (pxa2xx_i2s[i].rate == rate)
+			break;
+	}
+	return pxa2xx_i2s[i].priv1;
+};
+
+static int pxa2xx_i2s_startup(snd_pcm_substream_t *substream)
+{	
+	down(&pxa_i2s_sem);
+	if(!i2s_open) {
+		SACR0 |= SACR0_RST;
+		SACR0 = 0;
+	}
+	i2s_open++;
+	up(&pxa_i2s_sem);
+	return 0;
+}
+
+/* wait for I2S controller to be ready */
+static int pxa_i2s_wait(void)
+{
+	int i;
+	/* flush the Rx FIFO */
+	for(i = 0; i < 16; i++)
+		SADR;
+	return 0;
+}
+
+static int pxa2xx_i2s_hw_params(snd_pcm_substream_t *substream,
+				snd_pcm_hw_params_t *params)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	int master = 0;
+	
+	if(pcm_i->hw_runtime.hformat & SND_SOC_HWFMT_CBS_CFS)
+		master = 1;
+		
+	pxa_gpio_mode(gpio_bus[master].rx);
+	pxa_gpio_mode(gpio_bus[master].tx);
+	pxa_gpio_mode(gpio_bus[master].frm);
+	pxa_gpio_mode(gpio_bus[master].clk);
+	pxa_set_cken(CKEN8_I2S, 1);
+	pxa_i2s_wait();
+	
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (params_channels(params) == 2)
+			pcm_i->platform_data = &pxa2xx_i2s_pcm_stereo_out;
+		else
+			pcm_i->platform_data = &pxa2xx_i2s_pcm_mono_out;
+	} else {
+		if (params_channels(params) == 2)
+			pcm_i->platform_data = &pxa2xx_i2s_pcm_stereo_in;
+		else
+			pcm_i->platform_data = &pxa2xx_i2s_pcm_mono_in;
+	}
+	
+	/* is port used by another stream */
+	if (SACR0 & SACR0_ENB) {
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			SACR1 &= ~SACR1_DRPL;
+			SAIMR |= SAIMR_TFS;
+		} else {
+			SACR1 &= ~SACR1_DREC;
+			SAIMR |= SAIMR_RFS;
+		}
+	} else {
+		
+		SACR0 = 0;
+		SACR1 = 0;
+		SAIMR = SACR1_DRPL | SACR1_DREC;
+		if (master)
+			SACR0 |= SACR0_BCKD;
+		
+		SACR0 |= SACR0_RFTH(14) | SACR0_TFTH(1);
+	
+		if (pcm_i->hw_runtime.hformat & SND_SOC_HWFMT_LEFT_J)
+			SACR1 |= SACR1_AMSL;
+	
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			SACR1 &= ~SACR1_DRPL;
+			SAIMR |= SAIMR_TFS;
+		} else {
+			SACR1 &= ~SACR1_DREC;
+			SAIMR |= SAIMR_RFS;
+		}
+	}
+	
+	SADIV = get_sadiv(pcm_i->hw_runtime.rate);
+	SACR0 |= SACR0_ENB;
+	
+	return 0;
+}
+
+static void pxa2xx_i2s_shutdown(snd_pcm_substream_t *substream)
+{
+	down(&pxa_i2s_sem);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		SACR1 |= SACR1_DRPL;
+		SAIMR &= ~SAIMR_TFS;
+	} else {
+		SACR1 |= SACR1_DREC;
+		SAIMR &= ~SAIMR_RFS;
+	}
+
+	if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
+		pxa_i2s_wait();
+		SACR0 &= ~SACR0_ENB;
+		pxa_set_cken(CKEN8_I2S, 0);
+	}
+	i2s_open--;
+	up(&pxa_i2s_sem);
+}
+
+#ifdef CONFIG_PM
+static int pxa2xx_i2s_suspend(struct snd_soc_pcm_interface *interface, pm_message_t state)
+{
+	int ret = 0;
+
+	//if (interface->card && level == SUSPEND_DISABLE)
+		//ret = pxa2xx_ac97_do_suspend(card, SNDRV_CTL_POWER_D3cold);
+
+	return ret;
+}
+
+static int pxa2xx_i2s_resume(struct snd_soc_pcm_interface *interface)
+{
+	int ret = 0;
+
+	//if (interface->card && level == RESUME_ENABLE)
+		//ret = pxa2xx_ac97_do_resume(card, SNDRV_CTL_POWER_D0);
+
+	return ret;
+}
+
+#else
+#define pxa2xx_i2s_suspend	NULL
+#define pxa2xx_i2s_resume	NULL
+#endif
+
+static struct snd_soc_pcm_interface pxa_i2s_interface[] = {
+	{	.name = "pxa2xx-i2s",
+		.id = 0,
+		.type = SND_SOC_I2S,
+		.suspend = pxa2xx_i2s_suspend,
+		.resume = pxa2xx_i2s_resume,
+		.playback = {
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | 
+			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_96000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 2,
+			.channels_max = 2,},
+		.capture = {
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | 
+			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_96000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 2,
+			.channels_max = 2,},
+		.ops = {
+			.startup = pxa2xx_i2s_startup,
+			.shutdown = pxa2xx_i2s_shutdown,
+			.hw_params = pxa2xx_i2s_hw_params,},
+		.hw = {
+			.num_hmodes = ARRAY_SIZE(pxa2xx_i2s),
+			.hmodes = &pxa2xx_i2s[0],},
+	},
+};
+
+static int __init pxa2xx_soc_i2s_init(void)
+{
+	snd_soc_register_interface(pxa_i2s_interface);
+	return 0;
+}
+
+static void __exit pxa2xx_soc_i2s_exit(void)
+{
+	snd_soc_unregister_interface(pxa_i2s_interface);
+}
+
+EXPORT_SYMBOL_GPL(pxa_i2s_interface);
+
+module_init(pxa2xx_soc_i2s_init);
+module_exit(pxa2xx_soc_i2s_exit);
+
+/* Module information */ 
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("pxa2xx I2S SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
new file mode 100644
index 0000000..2b8f7fa
--- /dev/null
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -0,0 +1,395 @@
+/*
+ * linux/sound/arm/pxa2xx-pcm.c -- ALSA PCM interface for the Intel PXA2xx chip
+ *
+ * Author:	Nicolas Pitre
+ * Created:	Nov 30, 2004
+ * Copyright:	(C) 2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/audio.h>
+
+#include "pxa2xx-pcm.h"
+
+static const snd_pcm_hardware_t pxa2xx_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_MMAP |
+				  SNDRV_PCM_INFO_MMAP_VALID |
+				  SNDRV_PCM_INFO_INTERLEAVED |
+				  SNDRV_PCM_INFO_PAUSE,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.period_bytes_min	= 32,
+	.period_bytes_max	= 8192 - 32,
+	.periods_min		= 1,
+	.periods_max		= PAGE_SIZE/sizeof(pxa_dma_desc),
+	.buffer_bytes_max	= 128 * 1024,
+	.fifo_size		= 32,
+};
+
+struct pxa2xx_runtime_data {
+	int dma_ch;
+	pxa2xx_pcm_dma_params_t *params;
+	pxa_dma_desc *dma_desc_array;
+	dma_addr_t dma_desc_array_phys;
+};
+
+static void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id, struct pt_regs *regs)
+{
+	snd_pcm_substream_t *substream = dev_id;
+	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+	int dcsr;
+	
+	dcsr = DCSR(dma_ch);
+	DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
+
+	if (dcsr & DCSR_ENDINTR) {
+		snd_pcm_period_elapsed(substream);
+	} else {
+		printk( KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
+			rtd->params->name, dma_ch, dcsr );
+	}
+}
+
+static int pxa2xx_pcm_hw_params(snd_pcm_substream_t *substream,
+				snd_pcm_hw_params_t *params)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct pxa2xx_runtime_data *rtd = runtime->private_data;
+	size_t totsize = params_buffer_bytes(params);
+	size_t period = params_period_bytes(params);
+	pxa2xx_pcm_dma_params_t *dma = 
+		(pxa2xx_pcm_dma_params_t*)pcm_c->pcm_i->platform_data;
+	pxa_dma_desc *dma_desc;
+	dma_addr_t dma_buff_phys, next_desc_phys;
+	int ret;
+
+	if(rtd->params == NULL) {
+		rtd->params = dma;
+		if((ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW,
+			      pxa2xx_pcm_dma_irq, substream)) < 0)
+			return ret;
+		rtd->dma_ch = ret;
+	} else if(rtd->params != dma) {
+		pxa_free_dma(rtd->dma_ch);
+		rtd->params = dma;
+		if((ret = pxa_request_dma(rtd->params->name, DMA_PRIO_LOW,
+			      pxa2xx_pcm_dma_irq, substream)) < 0)
+			return ret;
+		rtd->dma_ch = ret;
+	}
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	runtime->dma_bytes = totsize;
+
+	dma_desc = rtd->dma_desc_array;
+	next_desc_phys = rtd->dma_desc_array_phys;
+	dma_buff_phys = runtime->dma_addr;
+	do {
+		next_desc_phys += sizeof(pxa_dma_desc);
+		dma_desc->ddadr = next_desc_phys;
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			dma_desc->dsadr = dma_buff_phys;
+			dma_desc->dtadr = rtd->params->dev_addr;
+		} else {
+			dma_desc->dsadr = rtd->params->dev_addr;
+			dma_desc->dtadr = dma_buff_phys;
+		}
+		if (period > totsize)
+			period = totsize;
+		dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN;
+		dma_desc++;
+		dma_buff_phys += period;
+	} while (totsize -= period);
+	dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
+
+	return 0;
+}
+
+static int pxa2xx_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+ 
+	if(rtd && rtd->params)
+		*rtd->params->drcmr = 0;
+	
+	if(rtd->dma_ch) {
+		snd_pcm_set_runtime_buffer(substream, NULL);
+		pxa_free_dma(rtd->dma_ch);
+		rtd->dma_ch = 0;
+	}
+	
+	return 0;
+}
+
+static int pxa2xx_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct pxa2xx_runtime_data *rtd = runtime->private_data;
+
+	DCSR(rtd->dma_ch) &= ~DCSR_RUN;
+	DCSR(rtd->dma_ch) = 0;
+	DCMD(rtd->dma_ch) = 0;
+	*rtd->params->drcmr = rtd->dma_ch | DRCMR_MAPVLD;
+
+	return 0;
+}
+
+static int pxa2xx_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		DDADR(rtd->dma_ch) = rtd->dma_desc_array_phys;
+		DCSR(rtd->dma_ch) = DCSR_RUN;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		DCSR(rtd->dma_ch) &= ~DCSR_RUN;
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		DCSR(rtd->dma_ch) |= DCSR_RUN;
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t pxa2xx_pcm_pointer(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct pxa2xx_runtime_data *rtd = runtime->private_data;
+	dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+			 DSADR(rtd->dma_ch) : DTADR(rtd->dma_ch);
+	snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+
+	if (x == runtime->buffer_size)
+		x = 0;
+	return x;
+}
+
+static int
+pxa2xx_pcm_hw_rule_mult32(snd_pcm_hw_params_t *params, snd_pcm_hw_rule_t *rule)
+{
+	snd_interval_t *i = hw_param_interval(params, rule->var);
+	int changed = 0;
+
+	if (i->min & 31) {
+		i->min = (i->min & ~31) + 32;
+		i->openmin = 0;
+		changed = 1;
+	}
+
+	if (i->max & 31) {
+		i->max &= ~31;
+		i->openmax = 0;
+		changed = 1;
+	}
+
+	return changed;
+}
+
+static int pxa2xx_pcm_open(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct pxa2xx_runtime_data *rtd;
+	int ret;
+
+	snd_soc_set_runtime_hw(substream, &pxa2xx_pcm_hardware);
+
+	/*
+	 * For mysterious reasons (and despite what the manual says)
+	 * playback samples are lost if the DMA count is not a multiple
+	 * of the DMA burst size.  Let's add a rule to enforce that.
+	 */
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+				  pxa2xx_pcm_hw_rule_mult32, NULL,
+				  SNDRV_PCM_HW_PARAM_PERIOD_BYTES, -1);
+	if (ret)
+		goto out;
+
+	ret = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+				  pxa2xx_pcm_hw_rule_mult32, NULL,
+				  SNDRV_PCM_HW_PARAM_BUFFER_BYTES, -1);
+	if (ret)
+		goto out;
+
+	ret = -ENOMEM;
+	rtd = kmalloc(sizeof(*rtd), GFP_KERNEL);
+	if (!rtd)
+		goto out;
+	memset(rtd, 0, sizeof(struct pxa2xx_runtime_data));
+	rtd->dma_desc_array =
+		dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+				       &rtd->dma_desc_array_phys, GFP_KERNEL);
+	if (!rtd->dma_desc_array)
+		goto err1;
+
+	runtime->private_data = rtd;
+	return 0;
+
+ err1:
+	kfree(rtd);
+ out:
+	return ret;
+}
+
+static int pxa2xx_pcm_close(snd_pcm_substream_t *substream)
+{
+	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
+	dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
+			      rtd->dma_desc_array, rtd->dma_desc_array_phys);
+	kfree(rtd);
+	return 0;
+}
+
+static int
+pxa2xx_pcm_mmap(snd_pcm_substream_t *substream, struct vm_area_struct *vma)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+				     runtime->dma_area,
+				     runtime->dma_addr,
+				     runtime->dma_bytes);
+}
+
+snd_pcm_ops_t pxa2xx_pcm_ops = {
+	.open		= pxa2xx_pcm_open,
+	.close		= pxa2xx_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= pxa2xx_pcm_hw_params,
+	.hw_free	= pxa2xx_pcm_hw_free,
+	.prepare	= pxa2xx_pcm_prepare,
+	.trigger	= pxa2xx_pcm_trigger,
+	.pointer	= pxa2xx_pcm_pointer,
+	.mmap		= pxa2xx_pcm_mmap,
+};
+
+static int pxa2xx_pcm_preallocate_dma_buffer(snd_pcm_t *pcm, int stream)
+{
+	snd_pcm_substream_t *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = pxa2xx_pcm_hardware.buffer_bytes_max;
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+					   &buf->addr, GFP_KERNEL);
+	if (!buf->area)
+		return -ENOMEM;
+	buf->bytes = size;
+	return 0;
+}
+
+static void pxa2xx_pcm_free_dma_buffers(snd_pcm_t *pcm)
+{
+	snd_pcm_substream_t *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+			
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		
+		dma_free_writecombine(pcm->card->dev, buf->bytes,
+				      buf->area, buf->addr);
+		buf->area = NULL;	
+	}
+}
+
+static u64 pxa2xx_pcm_dmamask = 0xffffffff;
+
+int pxa2xx_pcm_new(snd_card_t *card, struct snd_soc_pcm_codec *pcm_c, snd_pcm_t *pcm)
+{
+	int ret = 0;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &pxa2xx_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (pcm_c->nplayback) {
+		ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto out;
+	}
+
+	if (pcm_c->ncapture) {
+		ret = pxa2xx_pcm_preallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto out;
+	}
+ out:
+	return ret;
+}
+
+static struct snd_soc_platform pxa2xx_soc_platform;
+
+static int pxa2xx_soc_probe(struct device *dev)
+{
+	return 0;
+}
+
+static int pxa2xx_soc_remove(struct device *dev)
+{
+	return 0;
+}
+
+static int __init pxa2xx_soc_init(void)
+{
+	return snd_soc_register_platform(&pxa2xx_soc_platform);
+}
+
+static void __exit pxa2xx_soc_exit(void)
+{
+	snd_soc_unregister_platform(&pxa2xx_soc_platform);
+}
+
+static struct snd_soc_platform pxa2xx_soc_platform = {
+	.name		= "pxa2xx-audio",
+	.bus		= &platform_bus_type,
+	.probe		= pxa2xx_soc_probe,
+	.remove		= pxa2xx_soc_remove,
+	.pcm_ops 	= &pxa2xx_pcm_ops,
+	.pcm_new	= pxa2xx_pcm_new,
+	.pcm_free	= pxa2xx_pcm_free_dma_buffers,
+};
+
+module_init(pxa2xx_soc_init);
+module_exit(pxa2xx_soc_exit);
+
+EXPORT_SYMBOL(pxa2xx_pcm_new);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("Intel PXA2xx PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/pxa2xx-pcm.h b/sound/soc/pxa/pxa2xx-pcm.h
new file mode 100644
index 0000000..eebe005
--- /dev/null
+++ b/sound/soc/pxa/pxa2xx-pcm.h
@@ -0,0 +1,26 @@
+/*
+ * linux/sound/arm/pxa2xx-pcm.h -- ALSA PCM interface for the Intel PXA2xx chip
+ *
+ * Author:	Nicolas Pitre
+ * Created:	Nov 30, 2004
+ * Copyright:	MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+typedef struct {
+	char *name;			/* stream identifier */
+	u32 dcmd;			/* DMA descriptor dcmd field */
+	volatile unsigned long *drcmr;		/* the DMA request channel to use */
+	u32 dev_addr;			/* device physical address for DMA */
+} pxa2xx_pcm_dma_params_t;
+
+typedef struct {
+	u32	rx;
+	u32 tx;
+	u32 clk;
+	u32 frm;
+} pxa2xx_gpio_t;
+
diff --git a/sound/soc/pxa/pxa2xx-ssp.c b/sound/soc/pxa/pxa2xx-ssp.c
new file mode 100644
index 0000000..17efb2c
--- /dev/null
+++ b/sound/soc/pxa/pxa2xx-ssp.c
@@ -0,0 +1,430 @@
+/*
+ * pxa2xx-ssp.c  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    12th Aug 2005   Initial version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/audio.h>
+#include <asm/arch/ssp.h>
+
+#include "pxa2xx-pcm.h"
+
+/*
+ * SSP GPIO's
+ */
+#define GPIO26_SSP1RX_MD	(26 | GPIO_ALT_FN_1_IN)
+#define GPIO25_SSP1TX_MD	(25 | GPIO_ALT_FN_2_OUT)
+#define GPIO23_SSP1CLKS_MD	(23 | GPIO_ALT_FN_2_IN)
+#define GPIO24_SSP1FRMS_MD	(24 | GPIO_ALT_FN_2_IN)
+#define GPIO23_SSP1CLKM_MD	(23 | GPIO_ALT_FN_2_OUT)
+#define GPIO24_SSP1FRMM_MD	(24 | GPIO_ALT_FN_2_OUT) 
+ 
+#define GPIO11_SSP2RX_MD	(11 | GPIO_ALT_FN_2_IN)
+#define GPIO13_SSP2TX_MD	(13 | GPIO_ALT_FN_1_OUT)
+#define GPIO22_SSP2CLKS_MD	(22 | GPIO_ALT_FN_3_IN)
+#define GPIO88_SSP2FRMS_MD	(88 | GPIO_ALT_FN_3_IN)
+#define GPIO22_SSP2CLKM_MD	(22 | GPIO_ALT_FN_3_OUT)
+#define GPIO88_SSP2FRMM_MD	(88 | GPIO_ALT_FN_3_OUT)
+
+#define GPIO82_SSP3RX_MD	(82 | GPIO_ALT_FN_1_IN)
+#define GPIO81_SSP3TX_MD	(81 | GPIO_ALT_FN_1_OUT)
+#define GPIO84_SSP3CLKS_MD	(84 | GPIO_ALT_FN_1_IN)
+#define GPIO83_SSP3FRMS_MD	(83 | GPIO_ALT_FN_1_IN)
+#define GPIO84_SSP3CLKM_MD	(84 | GPIO_ALT_FN_1_OUT)
+#define GPIO83_SSP3FRMM_MD	(83 | GPIO_ALT_FN_1_OUT)
+
+#define PXA_SSP_HWFMT \
+	(SND_SOC_HWFMT_DSP_B  |SND_SOC_HWFMT_CBM_CFM | SND_SOC_HWFMT_CBM_CFS | \
+	SND_SOC_HWFMT_CBS_CFM | SND_SOC_HWFMT_CBS_CFS | SND_SOC_HWFMT_NB_NF | \
+	SND_SOC_HWFMT_NB_IF)
+
+/*
+ * priv1 is scr divider when in master mode
+ * -- liam, need some method of "doesn't matter" for fs, maybe 0  
+ */
+static struct snd_soc_hw_mode pxa2xx_ssp[] = {
+	{PXA_SSP_HWFMT, 	SND_SOC_HWBITS(16),		8000,	256,	1626},
+	{PXA_SSP_HWFMT, 	SND_SOC_HWBITS(16),		8000,	384,	1626}, 
+	{PXA_SSP_HWFMT, 	SND_SOC_HWBITS(16),		11025,	256,	1180},
+	{PXA_SSP_HWFMT, 	SND_SOC_HWBITS(16),		16000,	256,	813},
+	{PXA_SSP_HWFMT, 	SND_SOC_HWBITS(16),		22050,	256,	590},
+	{PXA_SSP_HWFMT, 	SND_SOC_HWBITS(16),		44100,	256,	295},
+	{PXA_SSP_HWFMT, 	SND_SOC_HWBITS(16),		48000,	256,	271},
+};
+
+static struct ssp_dev ssp[3];
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp1_pcm_mono_out = {
+	.name			= "SSP1 PCM Mono out",
+	.dev_addr		= __PREG(SSDR_P1),
+	.drcmr			= &DRCMRTXSSDR,
+	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
+				  DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp1_pcm_mono_in = {
+	.name			= "SSP1 PCM Mono in",
+	.dev_addr		= __PREG(SSDR_P1),
+	.drcmr			= &DRCMRRXSSDR,
+	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
+				  DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp1_pcm_stereo_out = {
+	.name			= "SSP1 PCM Stereo out",
+	.dev_addr		= __PREG(SSDR_P1),
+	.drcmr			= &DRCMRTXSSDR,
+	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp1_pcm_stereo_in = {
+	.name			= "SSP1 PCM Stereo in",
+	.dev_addr		= __PREG(SSDR_P1),
+	.drcmr			= &DRCMRRXSSDR,
+	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp2_pcm_mono_out = {
+	.name			= "SSP2 PCM Mono out",
+	.dev_addr		= __PREG(SSDR_P2),
+	.drcmr			= &DRCMRTXSS2DR,
+	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
+				  DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp2_pcm_mono_in = {
+	.name			= "SSP2 PCM Mono in",
+	.dev_addr		= __PREG(SSDR_P2),
+	.drcmr			= &DRCMRRXSS2DR,
+	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
+				  DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp2_pcm_stereo_out = {
+	.name			= "SSP2 PCM Stereo out",
+	.dev_addr		= __PREG(SSDR_P2),
+	.drcmr			= &DRCMRTXSS2DR,
+	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp2_pcm_stereo_in = {
+	.name			= "SSP2 PCM Stereo in",
+	.dev_addr		= __PREG(SSDR_P2),
+	.drcmr			= &DRCMRRXSS2DR,
+	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp3_pcm_mono_out = {
+	.name			= "SSP3 PCM Mono out",
+	.dev_addr		= __PREG(SSDR_P3),
+	.drcmr			= &DRCMRTXSS3DR,
+	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
+				  DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp3_pcm_mono_in = {
+	.name			= "SSP3 PCM Mono in",
+	.dev_addr		= __PREG(SSDR_P3),
+	.drcmr			= &DRCMRRXSS3DR,
+	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
+				  DCMD_BURST16 | DCMD_WIDTH2,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp3_pcm_stereo_out = {
+	.name			= "SSP3 PCM Stereo out",
+	.dev_addr		= __PREG(SSDR_P3),
+	.drcmr			= &DRCMRTXSS3DR,
+	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t pxa2xx_ssp3_pcm_stereo_in = {
+	.name			= "SSP3 PCM Stereo in",
+	.dev_addr		= __PREG(SSDR_P3),
+	.drcmr			= &DRCMRRXSS3DR,
+	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
+				  DCMD_BURST32 | DCMD_WIDTH4,
+};
+
+static pxa2xx_pcm_dma_params_t *ssp_dma_params[3][4] = {
+	{&pxa2xx_ssp1_pcm_mono_out, &pxa2xx_ssp1_pcm_mono_in,
+	&pxa2xx_ssp1_pcm_stereo_out,&pxa2xx_ssp1_pcm_stereo_in,},
+	{&pxa2xx_ssp2_pcm_mono_out, &pxa2xx_ssp2_pcm_mono_in,
+	&pxa2xx_ssp2_pcm_stereo_out, &pxa2xx_ssp2_pcm_stereo_in,},
+	{&pxa2xx_ssp3_pcm_mono_out, &pxa2xx_ssp3_pcm_mono_in,
+	&pxa2xx_ssp3_pcm_stereo_out,&pxa2xx_ssp3_pcm_stereo_in,},
+};
+
+static pxa2xx_gpio_t ssp_gpios[3][2] = {
+	{{ /* SSP1 Slave */	
+		.rx = GPIO26_SSP1RX_MD,
+		.tx = GPIO25_SSP1TX_MD,
+		.clk = GPIO23_SSP1CLKS_MD,
+		.frm = GPIO24_SSP1FRMS_MD,
+	},
+	{ /* SSP1 Master */	
+		.rx = GPIO26_SSP1RX_MD,
+		.tx = GPIO25_SSP1TX_MD,
+		.clk = GPIO23_SSP1CLKM_MD,
+		.frm = GPIO24_SSP1FRMM_MD,
+	}},
+	{{ /* SSP2 Slave */	
+		.rx = GPIO11_SSP2RX_MD,
+		.tx = GPIO13_SSP2TX_MD,
+		.clk = GPIO22_SSP2CLKS_MD,
+		.frm = GPIO88_SSP2FRMS_MD,
+	},
+	{ /* SSP2 Master */	
+		.rx = GPIO11_SSP2RX_MD,
+		.tx = GPIO13_SSP2TX_MD,
+		.clk = GPIO22_SSP2CLKM_MD,
+		.frm = GPIO88_SSP2FRMM_MD,
+	}},
+	{{ /* SSP3 Slave */	
+		.rx = GPIO82_SSP3RX_MD,
+		.tx = GPIO81_SSP3TX_MD,
+		.clk = GPIO84_SSP3CLKS_MD,
+		.frm = GPIO83_SSP3FRMS_MD,
+	},
+	{ /* SSP3 Master */	
+		.rx = GPIO82_SSP3RX_MD,
+		.tx = GPIO81_SSP3TX_MD,
+		.clk = GPIO84_SSP3CLKM_MD,
+		.frm = GPIO83_SSP3FRMM_MD,
+	}},
+};
+
+static int pxa2xx_ssp_startup(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	int ret = 0;
+	
+	if((ret = ssp_init (&ssp[pcm_i->id], pcm_i->id + 1, SSP_NO_IRQ)) < 0)
+		return ret;
+	
+	ssp_disable(&ssp[pcm_i->id]);
+	return ret;
+}
+
+static void pxa2xx_ssp_shutdown(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+
+	ssp_disable(&ssp[pcm_i->id]);
+	ssp_exit(&ssp[pcm_i->id]);
+}
+
+#ifdef CONFIG_PM
+static int pxa2xx_ssp_suspend(struct snd_soc_pcm_interface *interface, pm_message_t state)
+{
+	int ret = 0;
+
+	//if (interface->card && level == SUSPEND_DISABLE)
+		//ret = pxa2xx_ac97_do_suspend(card, SNDRV_CTL_POWER_D3cold);
+
+	return ret;
+}
+
+static int pxa2xx_ssp_resume(struct snd_soc_pcm_interface *interface)
+{
+	int ret = 0;
+
+	//if (interface->card && level == RESUME_ENABLE)
+		//ret = pxa2xx_ac97_do_resume(card, SNDRV_CTL_POWER_D0);
+
+	return ret;
+}
+
+#else
+#define pxa2xx_ssp_suspend	NULL
+#define pxa2xx_ssp_resume	NULL
+#endif
+
+static int pxa2xx_ssp_hw_params(snd_pcm_substream_t *substream,
+				snd_pcm_hw_params_t *params)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	int master = 0, dma = 0;
+	u32 ssp_mode = 0, ssp_setup = 0, psp_mode = 0;
+	int bits = generic_ffs(pcm_i->hw_runtime.hbits);
+	
+	if(pcm_i->hw_runtime.hformat & SND_SOC_HWFMT_CBS_CFS)
+		master = 1;
+	
+	pxa_gpio_mode(ssp_gpios[pcm_i->id][master].rx);
+	pxa_gpio_mode(ssp_gpios[pcm_i->id][master].tx);
+	pxa_gpio_mode(ssp_gpios[pcm_i->id][master].frm);
+	pxa_gpio_mode(ssp_gpios[pcm_i->id][master].clk);
+	
+	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+		dma = 1;
+	if (params_channels(params) == 2)
+		dma += 2;
+	pcm_i->platform_data = ssp_dma_params[pcm_i->id][dma];
+	
+	ssp_mode = SSCR0_PSP | SSCR0_DataSize(bits);
+	ssp_setup = SSCR1_RxTresh(14) | SSCR1_TxTresh(1) | SSCR1_TRAIL |
+				SSCR1_TSRE | SSCR1_RSRE | SSCR1_TIE | SSCR1_RIE;
+	
+	switch(pcm_i->hw_runtime.hformat & SND_SOC_CLOCK_MASK) {
+		case SND_SOC_HWFMT_CBM_CFM:
+			ssp_setup |= (SSCR1_SCLKDIR | SSCR1_SFRMDIR);
+			break;
+		case SND_SOC_HWFMT_CBM_CFS:
+			ssp_setup |= SSCR1_SCLKDIR;
+			break;
+		case SND_SOC_HWFMT_CBS_CFM:
+			ssp_setup |= SSCR1_SFRMDIR;
+			break;
+	}
+	
+	switch(pcm_i->hw_runtime.hformat & SND_SOC_INV_MASK){
+		case SND_SOC_HWFMT_NB_NF:
+			psp_mode |= SSPSP_SFRMP;
+			break;
+	}
+	if (pcm_i->hw_runtime.hformat & SND_SOC_HWFMT_DSP_A)
+		psp_mode |= SSPSP_SCMODE(2);
+	if (pcm_i->hw_runtime.hformat & SND_SOC_HWFMT_DSP_B)
+		psp_mode |= SSPSP_SCMODE(3);
+
+	ssp_disable(&ssp[pcm_i->id]);
+	ssp_config(&ssp[pcm_i->id], ssp_mode, ssp_setup, psp_mode,
+					SSCR0_SerClkDiv(pcm_i->hw_runtime.priv1));
+	ssp_enable(&ssp[pcm_i->id]);
+
+	return 0;
+}
+
+static struct snd_soc_pcm_interface pxa_ssp_interfaces[] = {
+	{	.name = "pxa2xx-ssp1",
+		.id = 0,
+		.type = SND_SOC_SSP,
+		.suspend = pxa2xx_ssp_suspend,
+		.resume = pxa2xx_ssp_resume,
+		.playback = {
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,},
+		.capture = {
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,},
+		.ops = {
+			.startup = pxa2xx_ssp_startup,
+			.shutdown = pxa2xx_ssp_shutdown,
+			.hw_params = pxa2xx_ssp_hw_params,},
+		.hw = {
+			.hmodes = &pxa2xx_ssp[0],
+			.num_hmodes = ARRAY_SIZE(pxa2xx_ssp),},
+	},
+	{	.name = "pxa2xx-ssp2",
+		.id = 1,
+		.type = SND_SOC_SSP,
+		.suspend = pxa2xx_ssp_suspend,
+		.resume = pxa2xx_ssp_resume,
+		.playback = {
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,},
+		.capture = {
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,},
+		.ops = {
+			.startup = pxa2xx_ssp_startup,
+			.shutdown = pxa2xx_ssp_shutdown,
+			.hw_params = pxa2xx_ssp_hw_params,},
+		.hw = {
+			.hmodes = &pxa2xx_ssp[0],
+			.num_hmodes = ARRAY_SIZE(pxa2xx_ssp),},
+	},
+	{	.name = "pxa2xx-ssp3",
+		.id = 2,
+		.type = SND_SOC_SSP,
+		.suspend = pxa2xx_ssp_suspend,
+		.resume = pxa2xx_ssp_resume,
+		.playback = {
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,},
+		.capture = {
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,},
+		.ops = {
+			.startup = pxa2xx_ssp_startup,
+			.shutdown = pxa2xx_ssp_shutdown,
+			.hw_params = pxa2xx_ssp_hw_params,},
+		.hw = {
+			.hmodes = &pxa2xx_ssp[0],
+			.num_hmodes = ARRAY_SIZE(pxa2xx_ssp),},
+	},
+};
+
+static int __init pxa2xx_soc_ssp_init(void)
+{
+	snd_soc_register_interface(&pxa_ssp_interfaces[0]);
+	snd_soc_register_interface(&pxa_ssp_interfaces[1]);
+	snd_soc_register_interface(&pxa_ssp_interfaces[2]);
+	return 0;
+}
+
+static void __exit pxa2xx_soc_ssp_exit(void)
+{
+	snd_soc_unregister_interface(&pxa_ssp_interfaces[2]);
+	snd_soc_unregister_interface(&pxa_ssp_interfaces[1]);
+	snd_soc_unregister_interface(&pxa_ssp_interfaces[0]);
+}
+
+EXPORT_SYMBOL_GPL(pxa_ssp_interfaces);
+
+module_init(pxa2xx_soc_ssp_init);
+module_exit(pxa2xx_soc_ssp_exit);
+
+/* Module information */ 
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("pxa2xx SSP/PCM SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
new file mode 100644
index 0000000..5f3bf78
--- /dev/null
+++ b/sound/soc/soc-core.c
@@ -0,0 +1,1055 @@
+/*
+ * soc-core.c  --  ALSA Soc Audio Layer
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    12th Aug 2005   Initial version.
+ *    25th Oct 2005   Working Codec, Interface and Platform registration.
+ *
+ *  TODO:
+ *   o Finish machine layer - move machine stuff out pxa2xx-pcm.c
+ *   o Make codec info const and copy on registration (e.g. kcontrols)
+ *   o Change away from ALSA pm callbacks and types for 2.6.15
+ *   o Add hw rules to enforce rates, etc.
+ *   o More testing with other codecs/machines.
+ *   o Add more codecs and platforms to ensure good API coverage.
+ *   o Support TDM on PCM and I2S
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpm.h>
+#include <sound/initval.h>
+
+#define SND_SOC_VERSION "0.7"
+
+#define iface_name(x) \
+	(((x) == SND_SOC_AC97) ? "ac97" : \
+	((x) == SND_SOC_I2S) ? "i2s" : \
+	((x) == SND_SOC_SSP) ? "ssp" : NULL)
+
+static DECLARE_MUTEX(soc_sem);
+static LIST_HEAD(codecs);
+static LIST_HEAD(interfaces);
+static struct snd_soc_platform *platform = NULL;
+static struct snd_soc_machine *machine = NULL;
+
+
+int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
+				unsigned short mask, unsigned short value)
+{
+	int change;
+	unsigned short old, new;
+
+	old = snd_soc_read(codec, reg);
+	new = (old & ~mask) | value;
+	change = old != new;
+	if (change)
+		snd_soc_write(codec, reg, new);
+
+	return change;
+}
+
+int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
+				unsigned short mask, unsigned short value)
+{
+	int change;
+	unsigned short old, new;
+
+	old = snd_soc_read(codec, reg);
+	new = (old & ~mask) | value;
+	change = old != new;
+
+	return change;
+}
+
+static int soc_match_hw(struct snd_soc_hw_mode *c, struct snd_soc_hw_mode *i, 
+	int rate, int fs)
+{
+	if (c->rate != i->rate)
+		return 0;
+	
+	if (c->rate != rate)
+		return 0;
+	
+	if (c->fs != i->fs)
+		return 0;
+	
+	if (!(c->hformat & i->hformat & SND_SOC_FORMAT_MASK))
+		return 0;
+	
+	if (!(c->hformat & i->hformat & SND_SOC_CLOCK_MASK))
+		return 0;
+	
+	if (!(c->hformat & i->hformat & SND_SOC_INV_MASK))
+		return 0;
+	
+	if (i->fs < fs)
+		return i->fs;
+	return 0;
+}
+
+/* Check that common hw capabilities exist between the codec and interface for
+ * the given hw params */
+static int soc_check_hw_params(snd_pcm_substream_t *substream, 
+	snd_pcm_hw_params_t *params)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	int fs = 2048, i, j, match = 0;	
+	
+	/* try and match interface <--> codec table entries */
+	for (i = 0; i < pcm_c->hw.num_hmodes; i++) {
+		for (j = 0; j < pcm_i->hw.num_hmodes; j++) {
+			if (soc_match_hw(&pcm_c->hw.hmodes[i], &pcm_i->hw.hmodes[j], 
+					params_rate(params), fs)) {
+				pcm_c->hw_runtime = pcm_c->hw.hmodes[i];
+				pcm_i->hw_runtime = pcm_i->hw.hmodes[j];
+				fs = pcm_c->hw.hmodes[i].fs;
+				match = 1;
+			}
+		}
+	}
+	
+	if (!match) {
+		printk(KERN_ERR "soc: can't match codec and interface capabilities\n");
+		return -EINVAL;
+	}
+	
+	pcm_i->hw_runtime.hformat =
+		1 << (generic_ffs(pcm_i->hw_runtime.hformat & pcm_c->hw_runtime.hformat & SND_SOC_FORMAT_MASK) -1) | 
+		1 << (generic_ffs(pcm_i->hw_runtime.hformat & pcm_c->hw_runtime.hformat & SND_SOC_CLOCK_MASK) - 1) |
+		1 << (generic_ffs(pcm_i->hw_runtime.hformat & pcm_c->hw_runtime.hformat & SND_SOC_INV_MASK) - 1);
+
+	pcm_i->hw_runtime.hbits = pcm_i->hw_runtime.hbits & pcm_c->hw_runtime.hbits;
+	pcm_c->hw_runtime.hformat = pcm_i->hw_runtime.hformat;
+	pcm_c->hw_runtime.hbits = pcm_i->hw_runtime.hbits;
+	
+	/* for debug atm */
+	printk(KERN_INFO "soc: codec rate %d fs %d format %x\n", pcm_c->hw_runtime.rate, 
+		pcm_c->hw_runtime.fs, pcm_c->hw_runtime.hformat);
+	printk(KERN_INFO "soc: iface rate %d fs %d format %x\n", pcm_i->hw_runtime.rate, 
+		pcm_i->hw_runtime.fs, pcm_i->hw_runtime.hformat);
+	printk(KERN_INFO "soc: audio rate %d chn %d\n", params_rate(params), params_channels(params));	
+	
+	return 0;
+}
+
+/*
+ * Initialize the runtime->hw record, private data can be allocated
+ */
+static int soc_pcm_open(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int ret = 0;
+
+	if(!machine || !platform)
+		return -ENODEV;
+	
+	/* Check that the codec and SoC audio interface's are compatible */
+	if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		runtime->hw.rates = pcm_c->playback.rates & pcm_i->playback.rates;
+		runtime->hw.formats = pcm_c->playback.formats & pcm_i->playback.formats;
+		runtime->hw.rate_min = max(pcm_c->playback.rate_min, pcm_i->playback.rate_min);
+		runtime->hw.rate_max = min(pcm_c->playback.rate_max, pcm_i->playback.rate_max);
+		runtime->hw.channels_min = max(pcm_c->playback.channels_min, pcm_i->playback.channels_min);
+		runtime->hw.channels_max = min(pcm_c->playback.channels_max, pcm_i->playback.channels_max);
+	} else {
+		runtime->hw.rates = pcm_c->capture.rates & pcm_i->capture.rates;
+		runtime->hw.formats = pcm_c->capture.formats & pcm_i->capture.formats;
+		runtime->hw.rate_min = max(pcm_c->capture.rate_min, pcm_i->capture.rate_min);
+		runtime->hw.rate_max = min(pcm_c->capture.rate_max, pcm_i->capture.rate_max);
+		runtime->hw.channels_min = max(pcm_c->capture.channels_min, pcm_i->capture.channels_min);
+		runtime->hw.channels_max = min(pcm_c->capture.channels_max, pcm_i->capture.channels_max);
+	}
+	
+	/* will the codec and interface work together ? */
+	snd_pcm_limit_hw_rates(runtime);
+	if (!runtime->hw.rates) {
+		printk(KERN_ERR "soc: %s <-> %s No matching rates\n", pcm_c->name, pcm_i->name);
+		return -ENODEV;
+	}
+	if(!runtime->hw.formats) {
+		printk(KERN_ERR "soc: %s <-> %s No matching formats\n", pcm_c->name, pcm_i->name);
+		return -ENODEV;
+	}
+	if(!runtime->hw.channels_min || !runtime->hw.channels_max) {
+		printk(KERN_ERR "soc: %s <-> %s No matching channels\n", pcm_c->name, pcm_i->name);
+		return -ENODEV;
+	}
+	printk(KERN_INFO "soc: %s <-> %s info:\n", pcm_c->name, pcm_i->name);
+	printk(KERN_INFO "soc: rate mask 0x%x \nsoc: min ch %d max ch %d\nsoc: min rate %d max rate %d\n",
+		runtime->hw.rates, runtime->hw.channels_min,
+		runtime->hw.channels_max, runtime->hw.rate_min, runtime->hw.rate_max);
+
+	if(pcm_i->ops.startup) {
+		if((ret = pcm_i->ops.startup(substream)) < 0) {
+			printk(KERN_ERR "soc: can't open interface %s\n", pcm_i->name);
+			goto ops_err;
+		}
+	}
+
+	if(platform->pcm_ops->open) {
+		if((ret = platform->pcm_ops->open(substream)) < 0) {
+			printk(KERN_ERR "soc: can't open platform %s\n", platform->name);
+			goto platform_err;
+		}
+	}
+	
+	if(machine->ops->startup) {
+		if((ret = machine->ops->startup(substream)) < 0) {
+			printk(KERN_ERR "soc: %s startup failed\n", machine->name);
+			goto machine_err;
+		}
+	}
+
+	if(pcm_c->ops.startup) {
+		if((ret = pcm_c->ops.startup(substream)) < 0) {
+			printk(KERN_ERR "soc: can't open codec %s\n", pcm_c->name);
+			goto pcm_err;
+		}
+	}
+
+	return ret;
+
+pcm_err:
+	if(pcm_c->ops.shutdown)
+		pcm_c->ops.shutdown(substream);
+	
+machine_err:
+	if(machine->ops->shutdown)
+		machine->ops->shutdown(substream);
+	
+platform_err:
+	if(platform->pcm_ops->close)
+		platform->pcm_ops->close(substream);
+
+ops_err:
+	if(pcm_i->ops.shutdown)
+		pcm_i->ops.shutdown(substream);	
+	
+	return ret;
+}
+
+/* private data can be freed here */
+static int soc_pcm_close(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+
+	if(pcm_i->ops.shutdown)
+		pcm_i->ops.shutdown(substream);
+	
+	if(pcm_c->ops.shutdown)
+		pcm_c->ops.shutdown(substream);
+	
+	if(machine->ops->shutdown)
+		machine->ops->shutdown(substream);
+	
+	if(platform->pcm_ops->close)
+		platform->pcm_ops->close(substream);
+
+	return 0;
+}
+
+/* called when pcm is prepared, can set format, sample rate; 
+ * atomic, can be called multiple times, can refer to runtime info*/
+static int soc_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	int ret = 0;
+
+	if(platform->pcm_ops->prepare)
+		if((ret = platform->pcm_ops->prepare(substream)) < 0)
+			return ret;
+	
+	if(pcm_c->ops.prepare)
+		if((ret = pcm_c->ops.prepare(substream)) < 0)
+			return ret;
+	
+	if(pcm_i->ops.prepare)
+		if((ret = pcm_i->ops.prepare(substream)) < 0)
+			return ret;
+
+	return ret;
+}
+
+static int soc_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	int ret = 0;
+
+	if(pcm_c->ops.trigger)
+		if((ret = pcm_c->ops.trigger(substream, cmd)) < 0)
+			return ret;
+	
+	if(pcm_i->ops.trigger)
+		if((ret = pcm_i->ops.trigger(substream, cmd)) < 0)
+			return ret;
+	
+	if(platform->pcm_ops->trigger)
+		if((ret = platform->pcm_ops->trigger(substream, cmd)) < 0)
+			return ret;
+
+	return ret;
+}
+
+/* called when hw params is set by app, can be called multiple times
+ * alloc buffers (using snd_pcm_lib_* ), non-atomic, hw setup
+ */
+static int soc_pcm_hw_params(snd_pcm_substream_t *substream,
+				snd_pcm_hw_params_t *params)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	struct snd_soc_codec *codec = (struct snd_soc_codec*)pcm_i->card->private_data;
+	int ret = 0;
+
+	/* prepare hw non AC97 only */
+	if(pcm_i->type != SND_SOC_AC97) {
+		if((ret = soc_check_hw_params(substream, params)) < 0)
+			return ret;
+	}
+	
+	if(pcm_c->ops.hw_params) {
+		if((ret = pcm_c->ops.hw_params(substream, params)) < 0) {
+			printk(KERN_ERR "soc: can't set codec %s hw params\n", pcm_c->name);
+			goto codec_err;
+		}
+	}
+	
+	if(pcm_i->ops.hw_params) {
+		if((ret = pcm_i->ops.hw_params(substream, params)) < 0) {
+			printk(KERN_ERR "soc: can't set interface %s hw params\n", pcm_i->name);
+			goto interface_err;
+		}
+	}
+	
+	if(platform->pcm_ops->hw_params) {
+		if((ret = platform->pcm_ops->hw_params(substream, params)) < 0) {
+			printk(KERN_ERR "soc: can't set platform %s hw params\n", platform->name);
+			goto platform_err;
+		}
+	}
+	
+	if(codec->dpm_state == SNDRV_CTL_POWER_D0)
+		return 0;
+	
+	if(codec->dpm_event)
+		codec->dpm_event(codec, SNDRV_CTL_POWER_D1);
+	
+	snd_soc_dpm_codec_event(codec, SNDRV_CTL_POWER_D1);
+	if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		snd_soc_dpm_stream_event(codec, pcm_c->playback.sname, SND_SOC_DPM_STREAM_START);
+	else
+		snd_soc_dpm_stream_event(codec, pcm_c->capture.sname, SND_SOC_DPM_STREAM_START);
+		
+	if(codec->dpm_event)
+		codec->dpm_event(codec, SNDRV_CTL_POWER_D0);
+	snd_soc_dpm_codec_mute(codec, 0);
+	codec->dpm_state = SNDRV_CTL_POWER_D0;
+	return ret;
+	
+platform_err:
+	if(platform->pcm_ops->hw_free)
+		platform->pcm_ops->hw_free(substream);
+	
+interface_err:
+	if(pcm_i->ops.hw_free)
+		pcm_i->ops.hw_free(substream);
+	
+codec_err:
+	if(pcm_c->ops.hw_free)
+		pcm_c->ops.hw_free(substream);
+
+	return ret;
+}
+
+/* free buffers, can be called multiple times */
+static int soc_pcm_hw_free(snd_pcm_substream_t *substream)
+{
+	struct snd_soc_pcm_codec *pcm_c = substream->private_data;
+	struct snd_soc_pcm_interface *pcm_i = pcm_c->pcm_i;
+	struct snd_soc_codec *codec = (struct snd_soc_codec*)pcm_i->card->private_data;
+
+	if(pcm_c->ops.hw_free)
+		pcm_c->ops.hw_free(substream);
+	
+	if(pcm_i->ops.hw_free)
+		pcm_i->ops.hw_free(substream);
+	
+	if(platform->pcm_ops->hw_free)
+		platform->pcm_ops->hw_free(substream);
+	
+	if(codec->dpm_state == SNDRV_CTL_POWER_D3hot)
+		return 0;
+	
+	snd_soc_dpm_codec_mute(codec, 1);
+	snd_soc_dpm_codec_event(codec, SNDRV_CTL_POWER_D3hot);
+		
+	if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		snd_soc_dpm_stream_event(codec, pcm_c->playback.sname, SND_SOC_DPM_STREAM_STOP);
+	else
+		snd_soc_dpm_stream_event(codec, pcm_c->capture.sname, SND_SOC_DPM_STREAM_STOP);
+	
+	if(codec->dpm_event)
+		codec->dpm_event(codec, SNDRV_CTL_POWER_D3hot);
+	
+	codec->dpm_state = SNDRV_CTL_POWER_D3hot;
+	return 0;
+}
+
+static snd_pcm_ops_t soc_pcm_ops = {
+	.open		= soc_pcm_open,
+	.close		= soc_pcm_close,
+	.hw_params	= soc_pcm_hw_params,
+	.hw_free	= soc_pcm_hw_free,
+	.prepare	= soc_pcm_prepare,
+	.trigger	= soc_pcm_trigger,
+};
+
+#ifdef CONFIG_PM
+/*
+ * SUSPEND_NOTIFY - mute outputs
+ * SUSPEND_DISABLE - power down chip
+ */
+static int soc_suspend(snd_card_t *card, pm_message_t state)
+{
+	struct snd_soc_codec *c = card->pm_private_data;
+	//snd_soc_pcm_interface_t *i = c->interface;
+	
+	//snd_pcm_suspend_all(c->pcm);
+	if(c->suspend)
+		c->suspend(c, state);
+	//if(i->suspend)
+	//	i->suspend(i, state);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
+	
+	return 0;
+}
+
+static int soc_resume(snd_card_t * card)
+{
+	struct snd_soc_codec *c = card->pm_private_data;
+	//snd_soc_pcm_interface_t *i = c->interface;
+	
+	if(c->resume)
+		c->resume(c);
+	//if(i->resume)
+	//	i->resume(i);
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+#else
+#define soc_suspend	NULL
+#define soc_resume	NULL
+#endif
+
+static int soc_register_pcm(int nconfig)
+{
+	int i, reg = 1;
+
+	/* register when all pcm are created for codec */
+	for(i = 0; i < machine->nconfigs; i++) {
+		
+		if(strcmp(machine->config[i].name, machine->config[nconfig].name))
+			continue;
+		
+		if(!machine->config[i].connect || machine->config[i].card_reg)
+			reg = 0;
+	}
+	
+	if (reg) {	
+		snd_soc_pcm_register(machine, machine->config[nconfig].name);
+		for(i = 0; i < machine->nconfigs; i++) {
+			if(!strcmp(machine->config[i].name, machine->config[nconfig].name))
+				machine->config[i].card_reg = 1;
+		}
+	}
+	
+	return reg;
+}
+
+static int soc_sync_objects(void)
+{
+	int i, ret = 0;
+	
+	/* only match codecs/interfaces when we have a machine and platform */
+	if(machine == NULL || platform == NULL)
+		return ret;
+
+	/* check machine config */
+	for(i = 0; i < machine->nconfigs; i++) {
+		if(machine->config[i].connect)
+			continue;
+	
+		if((ret = snd_soc_pcm_connect(machine, i)) == 0) {
+			machine->config[i].connect = 1;
+			soc_register_pcm(i);
+		}
+	}
+	
+	return 0;
+}
+
+static int soc_probe_codec(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+	
+	/* register a sound card */
+	codec->card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, codec->owner, 0);
+	if (!codec->card) {
+		printk(KERN_ERR "soc: can't create sound card for codec %s\n", codec->name);
+		return -ENODEV;
+	}
+
+	platform_device_register(&codec->pdev);
+	codec->card->dev = &codec->pdev.dev;
+	codec->card->private_data = codec;
+	strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
+
+	if(codec->probe && (ret = codec->probe(codec)) < 0)
+		goto err;
+	codec->probed = 1;
+	codec->dpm_state = SNDRV_CTL_POWER_D3hot;
+	soc_sync_objects();
+	return 0;
+	
+err:
+	snd_card_free(codec->card);
+	platform_device_unregister(&codec->pdev);
+	codec->card = NULL;
+	return ret;
+}
+
+static void soc_free_dpm_widgets(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dpm_widget *w, *lw = NULL;
+	struct snd_soc_dpm_path *p, *lp = NULL;
+	struct list_head *l;
+	
+	list_for_each(l, &codec->dpm_widgets)
+	{
+		w =  list_entry(l, struct snd_soc_dpm_widget, list);
+		if (lw)
+			kfree(lw);
+		lw = w;
+	}
+	kfree(lw);
+	
+	list_for_each(l, &codec->dpm_paths)
+	{
+		p =  list_entry(l, struct snd_soc_dpm_path, list);
+		if(p->long_name)
+			kfree(p->long_name);
+		if (lp)
+			kfree(lp);
+		lp = p;
+	}
+	kfree(lp);
+}
+
+static int soc_remove_codec(struct snd_soc_codec *codec)
+{	
+	if(!codec->probed)
+		return 0;
+	
+	if (codec->card) {
+		snd_soc_dpm_sys_remove(&codec->pdev.dev);
+		snd_card_free(codec->card);
+		platform_device_unregister(&codec->pdev);
+		codec->card = NULL;
+	}
+	if(codec->remove)
+		codec->remove(codec);
+
+	/* free any DPM objects */
+	soc_free_dpm_widgets(codec);
+	codec->probed = 0;
+
+	return 0;
+}
+
+int snd_soc_register_codec(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+
+	down(&soc_sem);
+	INIT_LIST_HEAD(&codec->list);
+	INIT_LIST_HEAD(&codec->dpm_widgets);
+	INIT_LIST_HEAD(&codec->dpm_paths);
+	list_add(&codec->list, &codecs);
+	
+	if((ret = soc_probe_codec(codec)) < 0) {
+		printk(KERN_ERR "soc: probe of codec %s failed\n", codec->name);
+		list_del_init(&codec->list);
+		up(&soc_sem);
+		return ret;
+	}
+
+	up(&soc_sem);
+	return ret;
+}
+
+void snd_soc_unregister_codec(struct snd_soc_codec *codec)
+{
+	down(&soc_sem); 
+	list_del_init(&codec->list);
+	soc_remove_codec(codec);	
+	up(&soc_sem);
+}
+
+int snd_soc_register_interface(struct snd_soc_pcm_interface *iface)
+{	
+	int ret = 0;
+	
+	down(&soc_sem);	
+	if(iface->probe && (ret = iface->probe(iface)) < 0) {
+		printk(KERN_ERR "soc: probe of interface %s failed\n", iface->name);
+		up(&soc_sem);
+		return ret;
+	}		
+
+	INIT_LIST_HEAD(&iface->list);
+	list_add(&iface->list, &interfaces);
+	soc_sync_objects();
+	up(&soc_sem);
+	return 0;
+}
+
+void snd_soc_unregister_interface(struct snd_soc_pcm_interface *iface)
+{
+	down(&soc_sem);
+	list_del_init(&iface->list);
+	if (iface->remove) 
+		iface->remove(iface);
+	up(&soc_sem);	
+}
+
+static struct device_driver soc_pdrv;
+
+int snd_soc_register_platform(struct snd_soc_platform *p)
+{
+	int ret = 0;
+	
+	down(&soc_sem);
+	if(platform) {
+		up(&soc_sem);
+		return -EBUSY;
+	}
+
+	platform = p;
+	soc_pcm_ops.mmap = p->pcm_ops->mmap;
+	soc_pcm_ops.pointer = p->pcm_ops->pointer;
+	soc_pcm_ops.ioctl = p->pcm_ops->ioctl;
+	soc_pdrv.bus = p->bus;
+	soc_pdrv.name = p->name;
+	soc_pdrv.probe = p->probe;
+	soc_pdrv.remove = p->remove;
+
+	if((ret = driver_register(&soc_pdrv)) < 0) {
+		printk(KERN_ERR "soc: failed to register platform %s\n", p->name);
+		up(&soc_sem);
+		return ret;
+	}
+	
+	soc_sync_objects();
+	up(&soc_sem);
+	return ret;
+}
+
+void snd_soc_unregister_platform(struct snd_soc_platform *p)
+{
+	struct list_head *l;
+	struct snd_soc_codec *codec = NULL;
+	
+	down(&soc_sem);
+	list_for_each(l, &codecs) {
+		codec = list_entry(l, struct snd_soc_codec, list);
+		if (codec->card) {
+			snd_soc_dpm_sys_remove(&codec->pdev.dev);
+			snd_card_free(codec->card);
+			platform_device_unregister(&codec->pdev);
+			codec->card = NULL;
+		}
+	}
+	
+	driver_unregister(&soc_pdrv);
+	platform = NULL;
+	up(&soc_sem);	
+}
+
+int snd_soc_register_machine(struct snd_soc_machine *m)
+{
+	int ret = 0;
+
+	down(&soc_sem);
+	if(m->probe) {
+		if((ret = m->probe(m)) < 0) {
+			up(&soc_sem);
+			return ret;
+		}
+	}
+	machine = m;
+	soc_sync_objects();
+	up(&soc_sem);
+	return 0;
+}
+
+void snd_soc_unregister_machine(struct snd_soc_machine *m)
+{
+	down(&soc_sem);
+	if(machine->remove)
+		machine->remove(m);
+	machine = NULL;
+	up(&soc_sem);
+}
+
+/* find codec from pcm name */
+static struct snd_soc_codec * soc_get_codec_from_pcm(char *pcm_name, 
+	struct snd_soc_pcm_codec **pcm_c)
+{
+	struct list_head *l;
+	struct snd_soc_codec *c = NULL;
+
+	list_for_each(l, &codecs) {
+		int i = 0;
+		c = list_entry(l, struct snd_soc_codec, list);
+
+		if(c->npcms == 0)
+			continue;
+
+		for(;i < c->npcms; i++) {
+			if(!strcmp(pcm_name, c->pcms[i].name)) {
+				if(pcm_c)
+					*pcm_c = &c->pcms[i];
+				return c;
+			}
+		}
+	}
+	
+	return NULL;
+}
+
+static struct snd_soc_codec* soc_get_codec(char *name)
+{
+	struct list_head *l;
+	struct snd_soc_codec *c = NULL;
+
+	list_for_each(l, &codecs) {
+		c = list_entry(l, struct snd_soc_codec, list);
+			
+		if(!strcmp(name, c->name))
+			return c;
+	}
+	
+	return NULL;
+}
+
+static struct snd_soc_pcm_interface* soc_get_interface(int iface_type, int num)
+{
+	struct list_head *l;
+	struct snd_soc_pcm_interface *i = NULL;
+	
+	list_for_each(l, &interfaces) {
+		i = list_entry(l, struct snd_soc_pcm_interface, list);
+		if (i->type == iface_type && i->id == num){
+			if (i->pcm_c)
+				return NULL;
+			else
+				return i;
+		}
+	}
+	return NULL;
+}
+
+int snd_soc_pcm_connect(struct snd_soc_machine *machine, int nconfig)
+{
+	snd_pcm_t *pcm;
+	struct snd_soc_pcm_codec *pcm_c;
+	int ret = 0;
+	struct snd_soc_pcm_interface *iface;
+	struct snd_soc_codec *codec;
+	int iface_type = machine->config[nconfig].interface;
+	int num = machine->config[nconfig].int_num;
+	char *name = machine->config[nconfig].sname;
+	char new_name[32];
+
+	if((codec = soc_get_codec_from_pcm(name, &pcm_c)) == NULL)
+		return -ENODEV;
+	
+	if((iface = soc_get_interface(iface_type, num)) == NULL)
+		return -ENODEV;
+	
+	/* check client and interface hw capabilities */
+	sprintf(new_name, "%s-%s-%d", pcm_c->name, iface_name(iface_type), num);
+	if((ret = snd_pcm_new(codec->card, new_name, codec->pcm_devs++, pcm_c->nplayback, pcm_c->ncapture, &pcm)) < 0) {
+		printk(KERN_ERR "soc: can't create pcm for codec %s\n", codec->name);
+		goto err;
+	}
+	pcm_c->pcm_i = iface;
+	pcm_c->codec = codec;
+	iface->card = codec->card;
+	iface->pcm_c = pcm_c;
+	pcm->private_data = pcm_c;
+		
+	if (pcm_c->nplayback)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &soc_pcm_ops);
+
+	if (pcm_c->ncapture)
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &soc_pcm_ops);
+
+	if((ret = platform->pcm_new(codec->card, pcm_c, pcm)) < 0) {
+		printk(KERN_ERR "soc: platform pcm constructor failed\n");
+		goto err;
+	}
+	pcm->private_free = platform->pcm_free;
+	if(machine->config[nconfig].init)
+		machine->config[nconfig].init(codec);
+	
+	printk(KERN_INFO "soc: %s <-> %s mapping ok\n", pcm_c->name, iface->name);
+	return ret;
+	
+err:
+	pcm_c->pcm_i = NULL;
+	iface->card = NULL;
+	iface->pcm_c = NULL;
+	return ret;
+}
+
+int snd_soc_pcm_register(struct snd_soc_machine *machine, char *name)
+{
+	struct snd_soc_codec* codec;
+	int ret = 0;
+
+	if((codec = soc_get_codec(name)) == NULL)
+		return -ENODEV;
+
+	snprintf(codec->card->shortname, sizeof(codec->card->shortname),
+		 "%s", codec->name);
+	snprintf(codec->card->longname, sizeof(codec->card->longname),
+		 "%s (%s)", codec->longname, "SoC");
+	
+	snd_card_set_pm_callback(codec->card, soc_suspend, soc_resume, codec);
+	
+	if((ret = snd_card_register(codec->card)) < 0) {
+		printk(KERN_ERR "soc: failed to register soundcard for codec %s\n", codec->name);
+		return ret;
+	}
+	
+	codec->pdev.dev.driver_data = codec;
+	snd_soc_dpm_sys_add(&codec->pdev.dev);
+
+	return ret;	
+}
+
+int snd_soc_set_runtime_hw(snd_pcm_substream_t *substream, const snd_pcm_hardware_t *hw)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	runtime->hw.info = hw->info;
+	runtime->hw.formats = hw->formats;
+	runtime->hw.period_bytes_min = hw->period_bytes_min;
+	runtime->hw.period_bytes_max = hw->period_bytes_max;
+	runtime->hw.periods_min = hw->periods_min;
+	runtime->hw.periods_max = hw->periods_max;
+	runtime->hw.buffer_bytes_max = hw->buffer_bytes_max;
+	runtime->hw.fifo_size = hw->fifo_size;
+	return 0;
+}
+
+snd_kcontrol_t *snd_soc_cnew(const snd_kcontrol_new_t *_template, 
+	void *data, char *long_name)
+{
+	snd_kcontrol_new_t template;
+	
+	memcpy(&template, _template, sizeof(template));
+	if(long_name)
+		template.name = long_name;
+	snd_runtime_check(!template.index, return NULL);
+	//template.index = codec->num;
+	return snd_ctl_new1(&template, data);
+}
+
+/*
+ * Controls
+ */
+
+int snd_soc_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
+	uinfo->value.enumerated.items = e->mask;
+	
+	if (uinfo->value.enumerated.item > e->mask - 1)
+		uinfo->value.enumerated.item = e->mask - 1;
+	strcpy(uinfo->value.enumerated.name, e->texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+int snd_soc_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned short val, bitmask;
+	
+	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+		;
+	val = snd_soc_read(codec, e->reg);
+	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
+	if (e->shift_l != e->shift_r)
+		ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (bitmask - 1);
+
+	return 0;
+}
+
+int snd_soc_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned short val;
+	unsigned short mask, bitmask;
+	
+	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+		;
+	if (ucontrol->value.enumerated.item[0] > e->mask - 1)
+		return -EINVAL;
+	val = ucontrol->value.enumerated.item[0] << e->shift_l;
+	mask = (bitmask - 1) << e->shift_l;
+	if (e->shift_l != e->shift_r) {
+		if (ucontrol->value.enumerated.item[1] > e->mask - 1)
+			return -EINVAL;
+		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
+		mask |= (bitmask - 1) << e->shift_r;
+	}
+	
+	return snd_soc_update_bits(codec, e->reg, mask, val);
+}
+
+/* volume and switch controls */
+int snd_soc_info_volsw(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0x0f;
+	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+
+	uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = shift == rshift ? 1 : 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = mask;
+	return 0;
+}
+
+int snd_soc_get_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0x0f;
+	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0x01;
+
+	ucontrol->value.integer.value[0] = (snd_soc_read(codec, reg) >> shift) & mask;
+	if (shift != rshift)
+		ucontrol->value.integer.value[1] = (snd_soc_read(codec, reg) >> rshift) & mask;
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		if (shift != rshift)
+			ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+
+	return 0;
+}
+
+int snd_soc_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0x0f;
+	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0x01;
+	int err;
+	unsigned short val, val2, val_mask;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val_mask = mask << shift;
+	val = val << shift;
+	if (shift != rshift) {
+		val2 = (ucontrol->value.integer.value[1] & mask);
+		if (invert)
+			val2 = mask - val2;
+		val_mask |= mask << rshift;
+		val |= val2 << rshift;
+	}
+	err = snd_soc_update_bits(codec, reg, val_mask, val);
+	return err;
+}
+
+static int __init snd_soc_init(void) 
+{
+	printk(KERN_INFO "soc: version %s liam.girdwood@wolfsonmicro.com\n", SND_SOC_VERSION);
+	return 0;
+}
+
+static void __exit snd_soc_exit(void) 
+{
+}
+
+module_init(snd_soc_init); 
+module_exit(snd_soc_exit);
+
+EXPORT_SYMBOL_GPL(snd_soc_register_codec);
+EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
+EXPORT_SYMBOL_GPL(snd_soc_register_platform);
+EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
+EXPORT_SYMBOL_GPL(snd_soc_pcm_connect);
+EXPORT_SYMBOL_GPL(snd_soc_register_interface);
+EXPORT_SYMBOL_GPL(snd_soc_unregister_interface);
+EXPORT_SYMBOL_GPL(snd_soc_set_runtime_hw);
+EXPORT_SYMBOL_GPL(snd_soc_cnew);
+EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
+EXPORT_SYMBOL_GPL(snd_soc_update_bits);
+EXPORT_SYMBOL_GPL(snd_soc_test_bits);
+EXPORT_SYMBOL_GPL(snd_soc_register_machine);
+EXPORT_SYMBOL_GPL(snd_soc_unregister_machine);
+EXPORT_SYMBOL_GPL(snd_soc_pcm_register);
+
+/* Module information */ 
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("ALSA SoC Core");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/soc-dpm.c b/sound/soc/soc-dpm.c
new file mode 100644
index 0000000..4a8f3f6
--- /dev/null
+++ b/sound/soc/soc-dpm.c
@@ -0,0 +1,953 @@
+/*
+ * soc-dpm.c  --  ALSA SoC Dynamic Power Management
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    12th Aug 2005   Initial version.
+ *    25th Oct 2005   Implemented path power domain.
+ *
+ *  Features:
+ *    o Changes power status of internal codec blocks depending on the 
+ *      dynamic configuration of codec internal audio paths and active
+ *      DAC's/ADC's.
+ * 
+ *  Todo:
+ *    o Platform power domain - could this be tied into machine layer ???
+ *    o DPM power change sequencing - allow for configurable sequences
+ *    o Support for analogue bias optimisation.
+ *    o Support for reduced codec oversampling rates.
+ *    o Support for reduced codec bias currents.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dpm.h>
+#include <sound/initval.h>
+
+
+static int dpm_status = 1;
+module_param(dpm_status, int, 0);
+MODULE_PARM_DESC(dpm_status, "enable DPM sysfs entries");
+
+static struct snd_soc_dpm_widget *dpm_cnew_widget(const struct snd_soc_dpm_widget *_widget)
+{
+	struct snd_soc_dpm_widget* widget;
+	widget = kmalloc(sizeof(struct snd_soc_dpm_widget), GFP_KERNEL);
+	if (!widget)
+		return NULL;
+	
+	memcpy(widget, _widget, sizeof(struct snd_soc_dpm_widget));
+	return widget;
+}
+
+/* set up initial codec paths */  
+static void dpm_set_path_status(struct snd_soc_dpm_widget *w, 
+	struct snd_soc_dpm_path *p, int i)
+{	
+	switch (w->id){
+	case snd_soc_dpm_mixer: {
+		int val;
+		int reg = w->kcontrols[i].private_value & 0xff;
+		int shift = (w->kcontrols[i].private_value >> 8) & 0x0f;
+		int mask = (w->kcontrols[i].private_value >> 16) & 0xff;
+		int invert = (w->kcontrols[i].private_value >> 24) & 0x01;
+		
+		val = snd_soc_read(w->codec, reg);
+		val = (reg >> shift) & mask;
+	
+		if((invert && !val) || (!invert && val))
+			p->connect = 1;
+		else
+			p->connect = 0;
+	}
+	break;
+	case snd_soc_dpm_mux: {
+		struct soc_enum *e = (struct soc_enum *)w->kcontrols[i].private_value;
+		int val, item, bitmask;
+		
+		for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+		;
+		val = snd_soc_read(w->codec, e->reg);
+		item = (val >> e->shift_l) & (bitmask - 1);
+		
+		p->connect = 0;
+		for (i = 0; i < e->mask; i++) {
+			if (!(strcmp(p->name, e->texts[i])) && item == i)
+				p->connect = 1;
+		}
+	}
+	break;
+	/* does not effect routing - always connected */
+	case snd_soc_dpm_volume:
+	case snd_soc_dpm_output:
+	case snd_soc_dpm_adc:
+	case snd_soc_dpm_input:
+	case snd_soc_dpm_dac:
+	case snd_soc_dpm_clock:
+	case snd_soc_dpm_vref:	
+	case snd_soc_dpm_amp:
+	case snd_soc_dpm_micbias:
+		p->connect = 1;
+	case snd_soc_dpm_mute:
+	/* does effect routing - dynamically connected */
+	case snd_soc_dpm_hp:
+	case snd_soc_dpm_mic:			
+		p->connect = 0;
+	break;
+	}
+}	
+
+static int dpm_connect_mux(struct snd_soc_codec *codec, struct snd_soc_dpm_widget *src, 
+	struct snd_soc_dpm_widget *dest, struct snd_soc_dpm_path *path, 
+	const char *control_name, const snd_kcontrol_new_t *kcontrol)
+{
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	int i;
+	
+	for (i = 0; i < e->mask; i++) {
+		if (!(strcmp(control_name, e->texts[i]))) {
+			list_add(&path->list, &codec->dpm_paths);
+			list_add(&path->list_out, &dest->inputs);
+			list_add(&path->list_in, &src->outputs);
+			path->name = (char*)e->texts[i];
+			dpm_set_path_status(dest, path, 0);
+			return 0;
+		}
+	}
+
+	return -ENODEV;
+}
+
+static int dpm_connect_mixer(struct snd_soc_codec *codec, struct snd_soc_dpm_widget *src, 
+	struct snd_soc_dpm_widget *dest, struct snd_soc_dpm_path *path, const char *control_name)
+{
+	int i;
+	
+	/* search for mixer kcontrol */
+	for (i = 0; i < dest->num_kcontrols; i++) {		
+		if(!strcmp(control_name, dest->kcontrols[i].name)) {
+			list_add(&path->list, &codec->dpm_paths);
+			list_add(&path->list_out, &dest->inputs);
+			list_add(&path->list_in, &src->outputs);
+			path->name = dest->kcontrols[i].name;
+			dpm_set_path_status(dest, path, i);
+			return 0;
+		}			
+	}
+	return -ENODEV;
+}
+
+int snd_soc_dpm_connect_input(struct snd_soc_codec *codec, const char *dest_name, 
+	const char * control_name, const char *src_name)
+{
+	struct snd_soc_dpm_path *path;
+	struct snd_soc_dpm_widget *src = NULL, *dest = NULL, *w;
+	struct list_head *l;
+	int ret = 0;
+
+	/* find src and dest widgets */
+	list_for_each(l, &codec->dpm_widgets)
+	{
+		w = list_entry(l, struct snd_soc_dpm_widget, list);
+		
+		if(!(strcmp(w->name, dest_name))) {
+			dest = w;
+			continue;
+		}
+		if(!(strcmp(w->name, src_name))) {
+			src = w;
+		}				
+	}
+
+	if(src == NULL || dest == NULL)
+		return -ENODEV;
+	
+	path = kzalloc(sizeof(struct snd_soc_dpm_path), GFP_KERNEL);
+	if (!path)
+		return -ENOMEM;	
+
+	path->in = src;
+	path->out = dest;
+	INIT_LIST_HEAD(&path->list);
+	INIT_LIST_HEAD(&path->list_in);
+	INIT_LIST_HEAD(&path->list_out);
+
+	/* check for external widgets */
+	if(dest->id == snd_soc_dpm_input) {	
+		if (src->id == snd_soc_dpm_micbias || src->id == snd_soc_dpm_mic)
+			dest->ext = 1;
+	}
+	if(src->id == snd_soc_dpm_output) {	
+		if (dest->id == snd_soc_dpm_amp || dest->id == snd_soc_dpm_hp)
+			src->ext = 1;
+	}
+	
+	if(control_name == NULL) {
+		list_add(&path->list, &codec->dpm_paths);
+		list_add(&path->list_out, &dest->inputs);
+		list_add(&path->list_in, &src->outputs);
+		path->connect = 1;
+		return 0;
+	}
+
+	switch(dest->id) {
+	case snd_soc_dpm_adc:
+	case snd_soc_dpm_dac:
+	case snd_soc_dpm_volume:
+	case snd_soc_dpm_input:
+	case snd_soc_dpm_output:
+	case snd_soc_dpm_clock:
+	case snd_soc_dpm_vref:
+	case snd_soc_dpm_micbias:
+	case snd_soc_dpm_mute:
+	case snd_soc_dpm_amp:
+		list_add(&path->list, &codec->dpm_paths);
+		list_add(&path->list_out, &dest->inputs);
+		list_add(&path->list_in, &src->outputs);
+		path->connect = 1;
+		return 0;
+	case snd_soc_dpm_mux:
+		if((ret = dpm_connect_mux(codec, src, dest, path, control_name, &dest->kcontrols[0])) != 0)
+			goto err;
+		break;
+	case snd_soc_dpm_mixer:
+		if((ret = dpm_connect_mixer(codec, src, dest, path, control_name)) != 0)
+			goto err;
+		break;
+	case snd_soc_dpm_hp:
+	case snd_soc_dpm_mic:
+		list_add(&path->list, &codec->dpm_paths);
+		list_add(&path->list_out, &dest->inputs);
+		list_add(&path->list_in, &src->outputs);
+		path->connect = 0;
+		return 0;
+	}
+	return 0;
+
+err:
+	printk(KERN_WARNING "soc: no dpm match for %s --> %s --> %s\n", src_name, 
+		control_name, dest_name); 
+	kfree(path);
+	return ret;
+}
+
+static int dpm_update_bits(struct snd_soc_dpm_widget *widget, int power)
+{
+	int change;
+	unsigned short old, new;
+	struct snd_soc_codec *codec = widget->codec;
+	
+	if(widget->reg < 0 || widget->id == snd_soc_dpm_input ||
+		widget->id == snd_soc_dpm_output)
+		return 0;
+	
+	if(power)
+		power = 1;
+	
+	old = snd_soc_read(codec, widget->reg);
+	new = (old & ~(0x1 << widget->shift)) | (power << widget->shift);
+	
+	change = old != new;
+	if (change) {
+		snd_soc_write(codec, widget->reg, new);
+		if (!widget->invert)
+			widget->power = power;
+		else
+			widget->power = power ? 0:1;
+	}
+
+	return change;
+}
+
+/* ramps the volume up or down to minimise pops before or after a power event */
+static int dpm_set_volume(struct snd_soc_dpm_widget *widget, int power)
+{
+	const snd_kcontrol_new_t *k = widget->kcontrols;
+	
+	if(widget->muted && !power)
+		return 0;
+	if(!widget->muted && power)
+		return 0;
+	
+	if(widget->num_kcontrols && k) {
+		int reg = k->private_value & 0xff;
+		int shift = (k->private_value >> 8) & 0x0f;
+		int mask = (k->private_value >> 16) & 0xff;
+		int invert = (k->private_value >> 24) & 0x01;
+		
+		if (power) {
+			int i;
+			/* power up has happended, increase volume to last level */
+			if(invert) {
+				for (i = mask; i > widget->value; i--)
+					snd_soc_update_bits(widget->codec, reg, mask, i);
+			} else {
+				for (i = 0; i < widget->value; i++)
+					snd_soc_update_bits(widget->codec, reg, mask, i);
+			}
+			widget->muted = 0;
+		} else {
+			/* power down is about to occur, decrease volume to mute */
+			int val = snd_soc_read(widget->codec, reg);
+			int i = widget->value = (val >> shift) & mask;
+			if(invert) {
+				for (; i < mask; i++)
+					snd_soc_update_bits(widget->codec, reg, mask, i);
+			} else {
+				for (; i > 0; i--)
+					snd_soc_update_bits(widget->codec, reg, mask, i);
+			}
+			widget->muted = 1;
+		}
+	}
+	return 0;
+}
+
+static int dpm_sync_mixer(struct snd_soc_codec *codec, struct snd_soc_dpm_widget *w)
+{
+	int i, ret = 0;
+	char name[32];
+	struct list_head *lp;
+	struct snd_soc_dpm_path *path;
+	
+	/* add kcontrol */
+	for (i = 0; i < w->num_kcontrols; i++) {
+			
+		/* match name */
+		list_for_each(lp, &w->inputs)
+		{
+			path = list_entry(lp, struct snd_soc_dpm_path, list_out);
+			
+			/* mixer/mux paths name must match control name */
+			if (path->name != (char*)w->kcontrols[i].name) 
+				continue;
+				
+			/* add dpm control with long name */
+			snprintf(name, 32, "%s %s", w->name, w->kcontrols[i].name);
+			path->long_name = kstrdup (name, GFP_KERNEL);
+			if(path->long_name == NULL)
+				return -ENOMEM;
+			
+			path->kcontrol = snd_soc_cnew(&w->kcontrols[i], w, path->long_name);
+			if ((ret = snd_ctl_add(codec->card, path->kcontrol)) < 0) {
+				printk(KERN_ERR "soc: failed to add dpm kcontrol %s\n", path->long_name);
+				kfree(path->long_name);
+				path->long_name = NULL;
+				return ret;
+			}
+		}
+	}
+	return ret;
+}
+
+static int dpm_sync_mux(struct snd_soc_codec *codec, struct snd_soc_dpm_widget *w)
+{
+	struct list_head *lp;
+	struct snd_soc_dpm_path *path = NULL;
+	snd_kcontrol_t *kcontrol;
+	int ret = 0;
+	
+	if(!w->num_kcontrols) {
+		printk(KERN_ERR "soc: mux %s has no controls\n", w->name);
+		return -EINVAL;
+	}
+	
+	kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
+	if ((ret = snd_ctl_add(codec->card, kcontrol)) < 0) {
+		printk(KERN_ERR "soc: failed to add kcontrol %s\n", w->name);
+		return ret;
+	}
+
+	list_for_each(lp, &w->inputs)
+	{
+		path = list_entry(lp, struct snd_soc_dpm_path, list_out);
+		path->kcontrol = kcontrol;
+	}
+	
+	return ret;
+}
+
+static int dpm_sync_volume(struct snd_soc_codec *codec, struct snd_soc_dpm_widget *w)
+{
+	snd_kcontrol_t *kcontrol;
+	int ret = 0;
+	
+	if(!w->num_kcontrols)
+		return -EINVAL;
+	
+	kcontrol = snd_soc_cnew(&w->kcontrols[0], w, w->name);
+	if ((ret = snd_ctl_add(codec->card, kcontrol)) < 0) {
+		printk(KERN_ERR "soc: failed to add kcontrol %s\n", w->name);
+		return ret;
+	}
+	
+	return ret;
+}
+
+/*
+ * Reset 'walked' bit for each path
+ */
+static void dpm_clear_walk(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dpm_path *p = NULL;
+	struct list_head *l = NULL;
+		
+	list_for_each(l, &codec->dpm_paths)
+	{
+		p = list_entry(l, struct snd_soc_dpm_path, list);
+		p->walked = 0;		
+	}
+}
+
+/*
+ * Check for a completed path to an active or physically connected output widget
+ */
+static int is_connected_output_ep(struct snd_soc_dpm_widget *widget)
+{
+	struct list_head *lp;
+	struct snd_soc_dpm_path *path = NULL;
+	int con = 0;
+
+	if(widget->id == snd_soc_dpm_adc && widget->active)
+		return 1;
+	if(widget->id == snd_soc_dpm_output && widget->connected && !widget->ext)
+		return 1;
+	if(widget->id == snd_soc_dpm_hp && widget->connected)
+		return 1;
+	if(widget->id == snd_soc_dpm_amp && widget->connected)
+		return 1;
+	
+	list_for_each(lp, &widget->outputs)
+	{
+		path = list_entry(lp, struct snd_soc_dpm_path, list_in);
+		if(path->walked)
+			continue;
+	
+		if (path->out && path->connect) {
+			path->walked = 1;
+			con += is_connected_output_ep(path->out);
+		}
+	}
+	
+	return con;
+}
+
+/*
+ * Check for a completed path to an active or physically connected input widget
+ */
+static int is_connected_input_ep(struct snd_soc_dpm_widget *widget)
+{
+	struct list_head *lp;
+	struct snd_soc_dpm_path *path = NULL;
+	int con = 0;
+	
+	if(widget->id == snd_soc_dpm_dac && widget->active)
+		return 1;
+	if(widget->id == snd_soc_dpm_input && widget->connected && !widget->ext)
+		return 1;
+	if(widget->id == snd_soc_dpm_mic && widget->connected)
+		return 1;
+	
+	list_for_each(lp, &widget->inputs)
+	{
+		path = list_entry(lp, struct snd_soc_dpm_path, list_out);
+		if(path->walked)
+			continue;
+	
+		if (path->in && path->connect) {
+			path->walked = 1;
+			con += is_connected_input_ep(path->in);
+		}
+	}
+	
+	return con;
+}
+
+static int dpm_power_widgets(struct snd_soc_codec *codec, int event)
+{
+	struct snd_soc_dpm_widget *w;
+	struct list_head *l;
+	int in, out, pm = 0, i, c = 1, *seq = NULL;
+	int up_seq[] = {snd_soc_dpm_dac, snd_soc_dpm_mixer, snd_soc_dpm_volume, snd_soc_dpm_mute};
+	int down_seq[] = {snd_soc_dpm_volume, snd_soc_dpm_mixer, snd_soc_dpm_dac, snd_soc_dpm_mute};
+
+	if(event == SND_SOC_DPM_STREAM_START) {
+		c = ARRAY_SIZE(up_seq);
+		seq = up_seq;
+	} else if (event == SND_SOC_DPM_STREAM_STOP) {
+		c = ARRAY_SIZE(down_seq);
+		seq = down_seq;
+	}
+	
+	for(i = 0; i < c; i++){
+		list_for_each(l, &codec->dpm_widgets)
+		{
+			w = list_entry(l, struct snd_soc_dpm_widget, list);
+			
+			if(seq && seq[i] && w->id != seq[i])
+				continue;
+			
+			if(w->id == snd_soc_dpm_vref || w->id == snd_soc_dpm_clock ||
+				w->id == snd_soc_dpm_mute)
+				continue;
+			
+			if(w->id == snd_soc_dpm_adc && w->active) {
+				in = is_connected_input_ep(w);
+				dpm_clear_walk(w->codec);
+				pm = (in != 0) ? 1 : 0;
+				if(w->invert)
+					pm = (pm ? 0:1);
+				dpm_update_bits(w, pm);
+				continue;
+			}
+			
+			if(w->id == snd_soc_dpm_dac && w->active) {
+				out = is_connected_output_ep(w);
+				dpm_clear_walk(w->codec);
+				pm = (out != 0) ? 1 : 0;
+				if(w->invert)
+					pm = (pm ? 0:1);
+				dpm_update_bits(w, pm);
+				continue;
+			}
+			
+			if(w->id == snd_soc_dpm_volume) {
+				int on;
+				in = is_connected_input_ep(w);
+				dpm_clear_walk(w->codec);
+				out = is_connected_output_ep(w);
+				dpm_clear_walk(w->codec);
+				pm = on = (out != 0 && in != 0) ? 1 : 0;
+				
+				if(!on)
+					dpm_set_volume(w, on); /* lower volume to reduce pops */
+				
+				if(w->invert)
+					pm = (on ? 0:1);
+				dpm_update_bits(w, pm);
+				
+				if(on) 
+					dpm_set_volume(w, on); /* restore volume from zero */
+				
+				continue;
+			}
+			in = is_connected_input_ep(w);
+			dpm_clear_walk(w->codec);
+			out = is_connected_output_ep(w);
+			dpm_clear_walk(w->codec);
+			pm = (out != 0 && in != 0) ? 1 : 0;
+			if(w->invert)
+					pm = (pm ? 0:1);
+			dpm_update_bits(w, pm);
+			if(w->ext_event)
+				w->ext_event(w->codec, pm);
+			/*printk("widget %s\n in %d out %d pm %s ext %d con %d\n", 
+				w->name, in, out, pm ? "On":"Off", w->ext, w->connected);*/	
+		}
+	}
+	
+	return 0;
+}
+
+static int dpm_mux_update_power(struct snd_soc_dpm_widget *widget, 
+	snd_kcontrol_t *kcontrol, int mask, int val, struct soc_enum* e)
+{
+	struct list_head *l;
+	struct snd_soc_dpm_path *path;
+	
+	if(widget->id != snd_soc_dpm_mux)
+		return -ENODEV; 
+
+	if (!snd_soc_test_bits(widget->codec, e->reg, mask, val))
+		return 0;
+	
+	/* find dpm widget path assoc with kcontrol */
+	list_for_each(l, &widget->codec->dpm_paths)
+	{
+		path = list_entry(l, struct snd_soc_dpm_path, list);
+		if (path->kcontrol != kcontrol)
+			continue;
+
+		if(!path->name || ! e->texts[val])
+			continue;
+
+		/* we now need to match the string in the enum to the path */
+		if (!(strcmp(path->name, e->texts[val])))
+			path->connect = 1; /* new connection */
+		else
+			path->connect = 0; /* old connection must be powered down */	
+	}
+	
+	dpm_power_widgets(widget->codec, 0);			
+	return 0;
+}
+
+static int dpm_mixer_update_power(struct snd_soc_dpm_widget *widget, 
+	snd_kcontrol_t *kcontrol, int reg, int val_mask, int val, int invert)
+{
+	struct list_head *l;
+	struct snd_soc_dpm_path *path;
+
+	if(widget->id != snd_soc_dpm_mixer)
+		return -ENODEV; 
+	
+	if (!snd_soc_test_bits(widget->codec, reg, val_mask, val))
+		return 0;
+	
+	/* find dpm widget path assoc with kcontrol */
+	list_for_each(l, &widget->codec->dpm_paths)
+	{
+		path = list_entry(l, struct snd_soc_dpm_path, list);
+		if (path->kcontrol != kcontrol)
+			continue;
+			
+		/* found, now check type */
+		if (val)
+			path->connect = invert ? 0:1; /* new connection */
+		else
+			path->connect = invert ? 1:0; /* old connection must be powered down */
+		break;
+	}
+	
+	dpm_power_widgets(widget->codec, 0);
+	return 0;
+}
+
+static ssize_t dpm_status_show(struct device *dev, struct device_attribute *attr, char *buf)   \
+{
+	struct snd_soc_codec *codec = (struct snd_soc_codec*)dev->driver_data;
+	struct snd_soc_dpm_widget *w = NULL;
+	struct list_head *l = NULL;
+	int count = 0; 
+
+	list_for_each(l, &codec->dpm_widgets)
+	{
+		w = list_entry(l, struct snd_soc_dpm_widget, list);
+		if (w->name && w->reg > 0 && 
+			w->id != snd_soc_dpm_input && w->id != snd_soc_dpm_output)
+			count += sprintf(buf + count, "%s: %s\n", w->name, w->power ? "On":"Off");
+	}
+	
+	return count;
+}
+
+static DEVICE_ATTR(dpm, 0444, dpm_status_show, NULL);
+		     
+int snd_soc_dpm_sys_add(struct device *dev) 
+{
+	int ret = 0;
+
+	if (dpm_status)
+		ret = device_create_file(dev, &dev_attr_dpm);
+	return ret;
+}	     
+
+void snd_soc_dpm_sys_remove(struct device *dev) 
+{
+	if (dpm_status)
+		device_remove_file(dev, &dev_attr_dpm);
+} 
+
+int snd_soc_dpm_sync(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dpm_widget *w = NULL;
+	struct list_head *lw;
+	
+	list_for_each(lw, &codec->dpm_widgets)
+	{
+		w = list_entry(lw, struct snd_soc_dpm_widget, list);
+		
+		if(w->sync)
+			continue;
+		
+		switch(w->id) {
+		case snd_soc_dpm_mixer:
+			dpm_sync_mixer(codec, w);
+			break;
+		case snd_soc_dpm_mux:
+			dpm_sync_mux(codec, w);
+			break;
+		case snd_soc_dpm_adc:
+		case snd_soc_dpm_dac:
+		case snd_soc_dpm_volume:
+			dpm_sync_volume(codec, w);
+			break;
+		case snd_soc_dpm_input:
+		case snd_soc_dpm_output:
+		case snd_soc_dpm_clock:
+		case snd_soc_dpm_vref:
+		case snd_soc_dpm_micbias:
+		case snd_soc_dpm_mute:
+		case snd_soc_dpm_amp:
+		case snd_soc_dpm_hp:
+		case snd_soc_dpm_mic:  				
+			break;
+		}
+		w->sync = 1;
+	}
+	
+	dpm_power_widgets(codec, 0);
+	return 0;
+}
+
+int snd_soc_dpm_get_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_soc_dpm_widget *widget = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0x0f;
+	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0x01;
+
+	if(widget->id == snd_soc_dpm_volume && !widget->power) {
+		ucontrol->value.integer.value[0] = widget->value;
+		return 0;
+	}
+	
+	ucontrol->value.integer.value[0] = (snd_soc_read(widget->codec, reg) >> shift) & mask;
+	if (shift != rshift)
+		ucontrol->value.integer.value[1] = (snd_soc_read(widget->codec, reg) >> rshift) & mask;
+	if (invert) {
+		ucontrol->value.integer.value[0] = mask - ucontrol->value.integer.value[0];
+		if (shift != rshift)
+			ucontrol->value.integer.value[1] = mask - ucontrol->value.integer.value[1];
+	}
+
+	return 0;
+}
+
+int snd_soc_dpm_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_soc_dpm_widget *widget = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int shift = (kcontrol->private_value >> 8) & 0x0f;
+	int rshift = (kcontrol->private_value >> 12) & 0x0f;
+	int mask = (kcontrol->private_value >> 16) & 0xff;
+	int invert = (kcontrol->private_value >> 24) & 0x01;
+	unsigned short val, val2, val_mask;
+	
+	val = (ucontrol->value.integer.value[0] & mask);
+	if (invert)
+		val = mask - val;
+	val_mask = mask << shift;
+	val = val << shift;
+	if (shift != rshift) {
+		val2 = (ucontrol->value.integer.value[1] & mask);
+		if (invert)
+			val2 = mask - val2;
+		val_mask |= mask << rshift;
+		val |= val2 << rshift;
+	}
+	
+	if(widget->id == snd_soc_dpm_volume && !widget->power) {
+		widget->value = val;
+		return 1;
+	}
+	
+	dpm_mixer_update_power(widget, kcontrol, reg, val_mask, val, invert);
+	return snd_soc_update_bits(widget->codec, reg, val_mask, val);
+}
+
+int snd_soc_dpm_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_soc_dpm_widget *widget = snd_kcontrol_chip(kcontrol);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned short val, bitmask;
+	
+	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+		;
+	val = snd_soc_read(widget->codec, e->reg);
+	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1);
+	if (e->shift_l != e->shift_r)
+		ucontrol->value.enumerated.item[1] = (val >> e->shift_r) & (bitmask - 1);
+
+	return 0;
+}
+
+int snd_soc_dpm_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	struct snd_soc_dpm_widget *widget = snd_kcontrol_chip(kcontrol);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned short val, mux;
+	unsigned short mask, bitmask;
+	
+	for (bitmask = 1; bitmask < e->mask; bitmask <<= 1)
+		;
+	if (ucontrol->value.enumerated.item[0] > e->mask - 1)
+		return -EINVAL;
+	mux = ucontrol->value.enumerated.item[0];
+	val = mux << e->shift_l;
+	mask = (bitmask - 1) << e->shift_l;
+	if (e->shift_l != e->shift_r) {
+		if (ucontrol->value.enumerated.item[1] > e->mask - 1)
+			return -EINVAL;
+		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
+		mask |= (bitmask - 1) << e->shift_r;
+	}
+
+	dpm_mux_update_power(widget, kcontrol, mask, mux, e);
+	return snd_soc_update_bits(widget->codec, e->reg, mask, val);
+}
+
+int snd_soc_dpm_new_control(struct snd_soc_codec *codec, const struct snd_soc_dpm_widget *widget)
+{
+	struct snd_soc_dpm_widget *w;
+
+	if ((w = dpm_cnew_widget(widget)) == NULL)
+		return -ENOMEM;
+
+	w->codec = codec;
+	INIT_LIST_HEAD(&w->inputs);
+	INIT_LIST_HEAD(&w->outputs);
+	INIT_LIST_HEAD(&w->list);
+	list_add(&w->list, &codec->dpm_widgets);
+	
+	/* machine layer set ups unconnected pins and insertions */
+	w->connected = 1;
+	return 0;
+}
+
+/* stream event notification */
+int snd_soc_dpm_stream_event(struct snd_soc_codec *codec, 
+	const char * stream, int event)
+{
+	struct snd_soc_dpm_widget *w;
+	struct list_head *l = NULL;	
+	
+	list_for_each(l, &codec->dpm_widgets)
+	{
+		w = list_entry(l, struct snd_soc_dpm_widget, list);
+
+		if (!w->sname)
+			continue;
+		
+		if (strstr(w->sname, stream)) {
+			switch(event) {
+			case SND_SOC_DPM_STREAM_START:
+				w->active = 1;
+				break;
+			case SND_SOC_DPM_STREAM_STOP:
+			case SND_SOC_DPM_STREAM_SUSPEND:	
+				w->active = 0;
+				break;
+			case SND_SOC_DPM_STREAM_PAUSE_PUSH:
+				break;
+			case SND_SOC_DPM_STREAM_PAUSE_RELEASE:
+				break;
+			}
+		}					
+	}
+	
+	dpm_power_widgets(codec, event);
+	return 0;
+}
+
+/* mute/unmute codec DAC mute bits - used to reduce pops */
+int snd_soc_dpm_codec_mute(struct snd_soc_codec *codec, int mute)
+{
+	struct snd_soc_dpm_widget *w;
+	struct list_head *l = NULL;	
+
+	list_for_each(l, &codec->dpm_widgets)
+	{
+		w = list_entry(l, struct snd_soc_dpm_widget, list);
+
+		if(w->id == snd_soc_dpm_mute)
+			dpm_update_bits(w, mute);
+	}
+	
+	return 0;
+}
+
+/* handle codec level dpm events - i.e. clocks */ 
+int snd_soc_dpm_codec_event(struct snd_soc_codec *codec, int event)
+{
+	struct snd_soc_dpm_widget *w;
+	struct list_head *l = NULL;	
+	int pm = 0, active = 0;
+
+	if(event == SNDRV_CTL_POWER_D1) {
+		if(codec->active_streams)
+			return 0;
+		pm = 1;
+		active = 1;
+	
+	} else if(event == SNDRV_CTL_POWER_D3hot){
+		if(!codec->active_streams)
+			return 0;
+		pm = 0;
+		active = -1;
+	}
+		
+	list_for_each(l, &codec->dpm_widgets)
+	{
+		int tpm = pm;
+		w = list_entry(l, struct snd_soc_dpm_widget, list);
+		if(w->id == snd_soc_dpm_vref || w->id == snd_soc_dpm_clock) {
+			codec->active_streams += active;
+			
+			if(w->invert)
+				tpm = (pm ? 0:1);
+			
+			dpm_update_bits(w, tpm);
+			//printk("active %d name %s state %d\n", codec->active_streams, w->name, tpm);	
+		}
+	}
+
+	return 0;
+}
+
+/* update codec dpm status with connected pins/inputs */
+int snd_soc_dpm_set_connection(struct snd_soc_codec *codec, 
+	const struct snd_soc_dpm_pin *pin)
+{
+	struct snd_soc_dpm_widget *w;
+	struct list_head *l = NULL;	
+
+	list_for_each(l, &codec->dpm_widgets)
+	{
+		w = list_entry(l, struct snd_soc_dpm_widget, list);
+			
+		if(!strcmp(w->name, pin->name)) {
+			w->connected = pin->connected;
+		}
+	}
+	
+	return 0;	
+}
+
+EXPORT_SYMBOL_GPL(snd_soc_dpm_put_volsw);
+EXPORT_SYMBOL_GPL(snd_soc_dpm_get_volsw);
+EXPORT_SYMBOL_GPL(snd_soc_dpm_connect_input);
+EXPORT_SYMBOL_GPL(snd_soc_dpm_new_control);
+EXPORT_SYMBOL_GPL(snd_soc_dpm_sync);
+EXPORT_SYMBOL_GPL(snd_soc_dpm_put_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_dpm_get_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_dpm_stream_event);
+EXPORT_SYMBOL_GPL(snd_soc_dpm_codec_event);
+EXPORT_SYMBOL_GPL(snd_soc_dpm_codec_mute);
+EXPORT_SYMBOL_GPL(snd_soc_dpm_set_connection);
+
+/* Module information */ 
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("ALSA SoC DPM");
+MODULE_LICENSE("GPL");
diff --git a/sound/Makefile b/sound/Makefile
index f352bb2..213e267 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -4,7 +4,7 @@
 obj-$(CONFIG_SOUND) += soundcore.o
 obj-$(CONFIG_SOUND_PRIME) += oss/
 obj-$(CONFIG_DMASOUND) += oss/
-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/
+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
 
 ifeq ($(CONFIG_SND),y)
   obj-y += last.o
diff --git a/sound/Kconfig b/sound/Kconfig
index b65ee47..76cfa2f 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -74,6 +74,8 @@ source "sound/sparc/Kconfig"
 
 source "sound/parisc/Kconfig"
 
+source "sound/soc/Kconfig"
+
 endmenu
 
 menu "Open Sound System"
diff --git a/include/sound/soc-dpm.h b/include/sound/soc-dpm.h
new file mode 100644
index 0000000..1a3a588
--- /dev/null
+++ b/include/sound/soc-dpm.h
@@ -0,0 +1,192 @@
+/*
+ * linux/sound/soc-dpm.h -- ALSA SoC Dynamic Power Management
+ *
+ * Author:		Liam Girdwood
+ * Created:		Aug 11th 2005
+ * Copyright:	Wolfson Microelectronics. PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+ 
+#ifndef __LINUX_SND_SOC_DPM_H
+#define __LINUX_SND_SOC_DPM_H
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+
+/*
+ * SoC dynamic power managment
+ *
+ * We can have upto 4 power domains
+ * 	1. Codec domain - VREF, VMID
+ *     Usually controlled at codec probe/remove, although can be set
+ *     at stream time if power is not needed for sidetone, etc.
+ *  2. Platform/Machine domain - physically connected inputs and outputs
+ *     Is platform/machine and user action specific, is set in the .asoundrc file and
+ *     by userspace e.g when HP are inserted
+ *  3. Path domain - Internal codec path mixers
+ *     Are automatically set when mixer and mux settings are
+ *     changed by the user.
+ *  4. Stream domain - DAC's and ADC's.
+ *     Enabled when stream playback/capture is started.  
+ */
+
+/* codec domain - vmid, vref handled by the codec driver */  
+#define SND_SOC_DPM_CLOCK(wname, preg, pshift, pinvert) \
+{.id = snd_soc_dpm_clock, .name = wname, .reg = preg, .shift = pshift, \
+ .invert = pinvert, .kcontrols = NULL, .num_kcontrols = 0}
+/* not a true power register, but is used by dpm to minimise pops */ 
+#define SND_SOC_DPM_MUTE(wname, preg, pshift, pinvert) \
+{.id = snd_soc_dpm_mute, .name = wname, .reg = preg, .shift = pshift, \
+ .invert = pinvert, .kcontrols = NULL, .num_kcontrols = 0}
+ 
+/* platform domain */
+#define SND_SOC_DPM_INPUT(wname) \
+{.id = snd_soc_dpm_input, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0}
+#define SND_SOC_DPM_OUTPUT(wname) \
+{.id = snd_soc_dpm_output, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0}
+#define SND_SOC_DPM_MIC(wname) \
+{.id = snd_soc_dpm_mic, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0}
+#define SND_SOC_DPM_HP(wname) \
+{.id = snd_soc_dpm_hp, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0}
+#define SND_SOC_DPM_AMP(wname, wext_event) \
+{.id = snd_soc_dpm_amp, .name = wname, .kcontrols = NULL, \
+ .num_kcontrols = 0, .ext_event = wext_event}
+ 
+/* path domain */
+#define SND_SOC_DPM_VOLUME(wname, preg, pshift, pinvert, wcontrols, wncontrols) \
+{.id = snd_soc_dpm_volume, .name = wname, .reg = preg, .shift = pshift, \
+ .invert = pinvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
+#define SND_SOC_DPM_MUX(wname, preg, pshift, pinvert, wcontrols) \
+{.id = snd_soc_dpm_mux, .name = wname, .reg = preg, .shift = pshift, \
+ .invert = pinvert,	.kcontrols = wcontrols, .num_kcontrols = 1} 
+#define SND_SOC_DPM_MIXER(wname, preg, pshift, pinvert, wcontrols, wncontrols) \
+{.id = snd_soc_dpm_mixer, .name = wname, .reg = preg, .shift = pshift, \
+ .invert = pinvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols} 
+#define SND_SOC_DPM_MICBIAS(wname, preg, pshift, pinvert) \
+{.id = snd_soc_dpm_micbias, .name = wname, .reg = preg, .shift = pshift, \
+ .invert = pinvert, .kcontrols = NULL, .num_kcontrols = 0} 
+
+/* stream domain */
+#define SND_SOC_DPM_DAC(wname, stname, preg, pshift, pinvert, wcontrols, wncontrols) \
+{.id = snd_soc_dpm_dac, .name = wname, .sname = stname, .reg = preg, .shift = pshift, \
+ .invert = pinvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
+#define SND_SOC_DPM_ADC(wname, stname, preg, pshift, pinvert, wcontrols, wncontrols) \
+{.id = snd_soc_dpm_adc, .name = wname, .sname = stname, .reg = preg, .shift = pshift, \
+ .invert = pinvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
+
+#define SOC_DPM_SINGLE(xname, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_soc_info_volsw, \
+  .get = snd_soc_dpm_get_volsw, .put = snd_soc_dpm_put_volsw, \
+  .private_value =  SOC_SINGLE_VALUE(reg, shift, mask, invert) }
+#define SOC_DPM_DOUBLE(xname, reg, shift_left, shift_right, mask, invert, power) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_soc_info_volsw, \
+  .get = snd_soc_dpm_get_volsw, .put = snd_soc_dpm_put_volsw, \
+  .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) }
+#define SOC_DPM_ENUM(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_soc_info_enum_double, \
+  .get = snd_soc_dpm_get_enum_double, .put = snd_soc_dpm_put_enum_double, \
+  .private_value = (unsigned long)&xenum }
+
+#define SND_SOC_DPM_STREAM_START		0x1
+#define SND_SOC_DPM_STREAM_STOP			0x2
+#define SND_SOC_DPM_STREAM_SUSPEND		0x4
+#define SND_SOC_DPM_STREAM_PAUSE_PUSH	0x8
+#define SND_SOC_DPM_STREAM_PAUSE_RELEASE	0x10  
+
+struct snd_soc_dpm_widget;
+enum snd_soc_dpm_type;
+struct snd_soc_dpm_path;
+struct snd_soc_dpm_pin;
+	
+int snd_soc_dpm_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_soc_dpm_get_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_soc_dpm_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_soc_dpm_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_soc_dpm_new_control(struct snd_soc_codec *codec, const struct snd_soc_dpm_widget *widget);
+int snd_soc_dpm_connect_input(struct snd_soc_codec *codec, const char *target_name, 
+	const char *control_name, const char *src_name);
+int snd_soc_dpm_sync(struct snd_soc_codec *codec);
+int snd_soc_dpm_stream_event(struct snd_soc_codec *codec, const char * stream, int event);
+int snd_soc_dpm_codec_event(struct snd_soc_codec *codec, int event);
+void snd_soc_dpm_sys_remove(struct device *dev);
+int snd_soc_dpm_sys_add(struct device *dev);
+int snd_soc_dpm_codec_mute(struct snd_soc_codec *codec, int mute);
+int snd_soc_dpm_set_connection(struct snd_soc_codec *codec, const struct snd_soc_dpm_pin *pin);
+
+enum snd_soc_dpm_type {
+	snd_soc_dpm_input = 0,
+	snd_soc_dpm_output,
+	snd_soc_dpm_mux,
+	snd_soc_dpm_mixer,
+	snd_soc_dpm_volume,
+	snd_soc_dpm_adc,
+	snd_soc_dpm_dac,
+	snd_soc_dpm_clock,
+	snd_soc_dpm_vref,
+	snd_soc_dpm_micbias,
+	snd_soc_dpm_mute,
+	snd_soc_dpm_mic,
+	snd_soc_dpm_hp,
+	snd_soc_dpm_amp,
+};
+
+struct snd_soc_dpm_path {
+	char *name;
+	char *long_name;
+	struct snd_soc_dpm_widget *in;
+	struct snd_soc_dpm_widget *out;
+	snd_kcontrol_t *kcontrol;
+	
+	u8 connect:1;	/* applies to widget inputs only */
+	u8 walked:1;	/* path has been walked */
+	
+	struct list_head list_in;
+	struct list_head list_out;
+	struct list_head list;
+};
+
+struct snd_soc_dpm_widget {
+	enum snd_soc_dpm_type id;
+	char *name;
+	char *sname;
+	struct snd_soc_codec *codec;
+	struct list_head list;
+	
+	/* dpm control */
+	short reg;						/* negative reg = no direct dpm */
+	unsigned char shift;
+	unsigned int value;				/* widget saved value */ 
+	unsigned char power:1;			/* block power status */
+	unsigned char invert:1;
+	unsigned char active:1;			/* active stream on DAC, ADC's */
+	unsigned char connected:1;		/* connected codec pin */
+	unsigned char sync:1;			/* sync complete */
+	unsigned char ext:1;			/* has external widgets */
+	unsigned char muted;			/* muted for pop reduction */
+	int (*ext_event)(struct snd_soc_codec*, int);
+	
+	/* kcontrols that relate to this widget */
+	int num_kcontrols;
+	const snd_kcontrol_new_t * kcontrols;
+	
+	/* widget input and outputs */
+	struct list_head inputs;
+	struct list_head outputs;
+};
+
+/* codec pin connections */
+struct snd_soc_dpm_pin {
+	char *name;
+	int connected; /* this is dynamic for insertion/removal events e.g. hp, mic */
+};
+
+#endif
diff --git a/include/sound/soc.h b/include/sound/soc.h
new file mode 100644
index 0000000..7163558
--- /dev/null
+++ b/include/sound/soc.h
@@ -0,0 +1,339 @@
+/*
+ * linux/sound/soc.h -- ALSA SoC Layer
+ *
+ * Author:		Liam Girdwood
+ * Created:		Aug 11th 2005
+ * Copyright:	Wolfson Microelectronics. PLC.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+ 
+#ifndef __LINUX_SND_SOC_H
+#define __LINUX_SND_SOC_H
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/ac97_codec.h>
+
+/*
+ * Convenience kcontrol builders
+ */
+
+#define SOC_SINGLE_VALUE(reg,shift,mask,invert) ((reg) | ((shift) << 8) | ((shift) << 12) | ((mask) << 16) | ((invert) << 24))
+#define SOC_SINGLE(xname, reg, shift, mask, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_soc_info_volsw, \
+  .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
+  .private_value =  SOC_SINGLE_VALUE(reg, shift, mask, invert) }
+#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert, power) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_soc_info_volsw, \
+  .get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
+  .private_value = (reg) | ((shift_left) << 8) | ((shift_right) << 12) | ((mask) << 16) | ((invert) << 24) }
+#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \
+{ .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
+  .mask = xmask, .texts = xtexts }
+#define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts) \
+	SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmask, xtexts)
+#define SOC_ENUM(xname, xenum) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_soc_info_enum_double, \
+  .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
+  .private_value = (unsigned long)&xenum }
+
+ 
+/*
+ * Audio interface types
+ */
+#define SND_SOC_AC97		0x1
+#define SND_SOC_I2S			0x2
+#define SND_SOC_SSP			0x4
+
+/*
+ * hw interface formats
+ */
+#define SND_SOC_HWFMT_I2S			(1 << 0)	/* I2S mode */
+#define SND_SOC_HWFMT_RIGHT_J		(1 << 1)	/* Right justified mode */
+#define SND_SOC_HWFMT_LEFT_J		(1 << 2)	/* Left Justified mode */
+#define SND_SOC_HWFMT_DSP_A			(1 << 3)	/* L data msb after FRM */
+#define SND_SOC_HWFMT_DSP_B			(1 << 4)	/* L data msb during FRM */
+
+/*
+ * hw signal inversions
+ */
+#define SND_SOC_HWFMT_NB_NF			(1 << 24)	/* normal bit clock + frame */
+#define SND_SOC_HWFMT_NB_IF			(1 << 25)	/* normal bclk + inv frm */
+#define SND_SOC_HWFMT_IB_NF			(1 << 26)	/* invert bclk + nor frm */
+#define SND_SOC_HWFMT_IB_IF			(1 << 27)	/* invert bclc + frm */
+
+/*
+ * hw clock masters
+ * This is wrt the codec, the inverse is true for the interface
+ * i.e. if the codec is clk and frm master then the interface is 
+ * clk and frame slave.
+ */
+#define SND_SOC_HWFMT_CBM_CFM		(1 << 28)	/* codec clk & frm master */
+#define SND_SOC_HWFMT_CBS_CFM		(1 << 29)	/* codec clk slave & frm master */
+#define SND_SOC_HWFMT_CBM_CFS		(1 << 30)	/* codec clk master & frame slave */
+#define SND_SOC_HWFMT_CBS_CFS		(1 << 31)	/* codec clk & frm slave */
+
+
+#define SND_SOC_FORMAT_MASK		0x000000ff
+#define SND_SOC_INV_MASK		0x0f000000
+#define SND_SOC_CLOCK_MASK		0xf0000000
+
+#define SND_SOC_HWBITS(x)		(1 << (x - 1))	/* sample size in bits */
+
+/*
+ * Codec IO
+ */
+#define snd_soc_read(codec, reg) codec->read(codec, reg)
+#define snd_soc_write(codec, reg, value) codec->write(codec, reg, value)
+
+struct snd_soc_pcm_stream;
+struct snd_soc_ops;
+struct snd_soc_hw_mode;
+struct snd_soc_hw_bus;
+struct snd_soc_pcm_codec;
+struct snd_soc_pcm_interface;
+struct snd_soc_codec;
+struct snd_soc_platform;
+struct snd_soc_machine_config;
+struct snd_soc_machine;
+struct soc_enum;
+
+/*
+ * SoC registration objects:-
+ * 
+ *  o Codec (n)
+ *  o Platform (1)
+ *  o Interface (n)
+ *  o Machine (1)
+ * 
+ * Only 1 Platform and Machine may be registered with the SoC layer. However there is
+ * no limit on the number of codecs and interfaces that can be registered.
+ */
+int snd_soc_register_codec(struct snd_soc_codec *codec);
+void snd_soc_unregister_codec(struct snd_soc_codec *codec);
+
+int snd_soc_register_platform(struct snd_soc_platform *platform);
+void snd_soc_unregister_platform(struct snd_soc_platform *platform);
+
+int snd_soc_register_interface(struct snd_soc_pcm_interface *iface);
+void snd_soc_unregister_interface(struct snd_soc_pcm_interface *iface);
+
+int snd_soc_register_machine(struct snd_soc_machine *machine);
+void snd_soc_unregister_machine(struct snd_soc_machine *machine);
+
+/* pcm <-> interface connect */
+int snd_soc_pcm_connect(struct snd_soc_machine *machine, int nconfig);
+int snd_soc_pcm_register(struct snd_soc_machine *machine, char *name);
+
+/* set runtime hw params */
+int snd_soc_set_runtime_hw(snd_pcm_substream_t *substream, 
+	const snd_pcm_hardware_t *hw);
+
+/* codec register bit access */
+int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
+				unsigned short mask, unsigned short value);
+				
+int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
+				unsigned short mask, unsigned short value);
+
+/*
+ *Controls
+ */
+snd_kcontrol_t *snd_soc_cnew(const snd_kcontrol_new_t *_template, 
+	void *data, char *long_name);
+
+int snd_soc_info_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_soc_get_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_soc_put_enum_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_soc_info_volsw(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_soc_get_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_soc_put_volsw(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+	
+/* SoC supported stream types */
+struct snd_soc_pcm_stream {
+	char *sname;
+	unsigned int rates;		/* SNDRV_PCM_RATE_* */
+	u64 formats;			/* SNDRV_PCM_FMTBIT_* */
+	unsigned int rate_min;		/* min rate */
+	unsigned int rate_max;		/* max rate */
+	unsigned int channels_min;	/* min channels */
+	unsigned int channels_max;	/* max channels */
+};
+
+/* SoC audio ops */
+struct snd_soc_ops {
+	int (*startup)(snd_pcm_substream_t *);
+	void (*shutdown)(snd_pcm_substream_t *);
+	int (*hw_params)(snd_pcm_substream_t *, snd_pcm_hw_params_t *);
+	int (*hw_free)(snd_pcm_substream_t *);
+	int (*prepare)(snd_pcm_substream_t *);
+	int (*trigger)(snd_pcm_substream_t *, int);
+};
+
+/* SoC hardware mode */
+struct snd_soc_hw_mode {
+	u32 hformat;	/* SND_SOC_HWFMT_* */
+	u32 hbits;		/* SND_SOC_HWBITS() */
+	u32 rate;		/* sample rate */
+	u32 fs;			/* ratio of rate:bclk */ 
+	long priv1;		/* private mode data 1 */
+	long priv2;		/* private mode data 2 */
+	long priv3;		/* private mode data 3 */
+};
+
+/* SoC hardware bus and all modes */
+struct snd_soc_hw_bus {
+	int num_hmodes;				/* number of hw modes */
+	struct snd_soc_hw_mode *hmodes;	/* array of supported audio hw modes */	
+};
+
+/* SoC Codec pcm stream */
+struct snd_soc_pcm_codec {
+	char *name;
+
+	/* codec pcm capabilities */
+	struct snd_soc_pcm_stream playback;
+	struct snd_soc_pcm_stream capture;
+	struct snd_soc_hw_bus hw;
+	int nplayback;
+	int ncapture;
+	
+	/* runtime info */
+	struct snd_soc_hw_mode hw_runtime;
+	struct snd_soc_ops ops;
+	struct snd_soc_pcm_interface *pcm_i;
+	struct snd_soc_codec *codec;
+	
+	/* private data */
+	void *priv;
+	void (*private_free)(snd_pcm_runtime_t *runtime);
+};
+
+/* SoC platform audio port interface */
+struct snd_soc_pcm_interface {
+	
+	/* interface description */
+	char *name;
+	unsigned int id;
+	unsigned char type;
+	
+	int probed;
+	int (*probe)(struct snd_soc_pcm_interface *interface);
+	void (*remove)(struct snd_soc_pcm_interface *interface);
+	int (*suspend)(struct snd_soc_pcm_interface *interface, pm_message_t state);
+	int (*resume)(struct snd_soc_pcm_interface *interface);
+	
+	/* interface pcm capabilities */
+	struct snd_soc_pcm_stream capture;
+	struct snd_soc_pcm_stream playback;
+	struct snd_soc_hw_bus hw;
+	
+	/* runtime info */
+	struct snd_soc_hw_mode hw_runtime;
+	struct snd_soc_pcm_codec *pcm_c;
+	struct snd_soc_ops ops;
+	snd_card_t *card;
+	ac97_t *ac97;
+	
+	/* private data */
+	void *platform_data;
+	
+	struct list_head list;
+};
+
+/* SoC Codec interface */
+struct snd_soc_codec {
+	char *name;
+	char *longname;
+	struct module *owner;
+	
+	/* callbacks */
+	int (*probe)(struct snd_soc_codec *codec);
+	void (*remove)(struct snd_soc_codec *codec);
+	int (*suspend)(struct snd_soc_codec *codec, pm_message_t state);
+	int (*resume)(struct snd_soc_codec *codec);
+	int (*dpm_event)(struct snd_soc_codec *codec, int event);
+	
+	/* runtime */
+	snd_card_t *card;
+	struct platform_device pdev;
+	void *private_data;
+	void *machine_data;
+	int active_streams;
+	int pcm_devs;
+	int probed;
+	int dpm_state;
+	
+	/* codec IO */
+	void *control_data; /* codec control (i2c/3wire) data */
+	unsigned int (*read)(struct snd_soc_codec *, unsigned int);
+	int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
+	void *reg_cache;
+	
+	struct list_head list;
+	struct list_head dpm_widgets;
+	struct list_head dpm_paths;
+	
+	/* codec pcms */
+	struct snd_soc_pcm_codec *pcms;
+	int	npcms;
+};
+	
+/* SoC platform interface */
+struct snd_soc_platform {
+	char *name;
+	
+	int	(*probe)(struct device * dev);
+	int	(*remove)(struct device * dev);
+	struct bus_type	* bus;
+	int (*pcm_new)(snd_card_t *, struct snd_soc_pcm_codec *, snd_pcm_t *);
+	void (*pcm_free)(snd_pcm_t *);
+	snd_pcm_ops_t *pcm_ops;
+};
+
+/* SoC machine configuration */
+struct snd_soc_machine_config {
+	char *name;			/* Codec name */
+	char *sname;		/* stream name */
+	int interface;		/* interface type */
+	int int_num;		/* interface number */
+	u32 connect:1;		/* interface and codec have been connected */
+	u32 card_reg:1;		/* codec card registered */
+	u32 flags;			/* preference flags */
+	
+	/* codec/machine specific init - e.g. add machine controls */
+	int (*init)(struct snd_soc_codec *codec); 
+};
+
+/* SoC machine interface */
+struct snd_soc_machine {
+	char *name;
+	
+	int	(*probe)(struct snd_soc_machine *machine);
+	int	(*remove)(struct snd_soc_machine *machine);
+	int (*suspend)(struct snd_soc_machine *machine, pm_message_t state);
+	int (*resume)(struct snd_soc_machine *machine);
+	
+	struct snd_soc_ops *ops;
+	struct snd_soc_machine_config *config;
+	int nconfigs;
+};
+
+/* enamerated kcontrol */
+struct soc_enum {
+	unsigned short reg;
+	unsigned char shift_l;
+	unsigned char shift_r;
+	unsigned int mask;
+	const char **texts;
+	void *dpm;
+};
+
+#endif
diff --git a/Documentation/sound/soc/codec.txt b/Documentation/sound/soc/codec.txt
new file mode 100644
index 0000000..b8bfeaf
--- /dev/null
+++ b/Documentation/sound/soc/codec.txt
@@ -0,0 +1,91 @@
+ASoC Codec driver
+=================
+
+** Still in progress **
+
+Writting an ASoC codec driver is relatively straight forward and can be broken down into the following sections.
+
+1. Audio Interface Capabilities
+
+   The codec audio interface(s) capabilities have to be defined and registered with the core. The definition describes the physical audio interface capabilities and includes format, speed, signal master/slave, signal inversion, etc. The physical codec interface is usually well described in the codec data sheet. 
+
+Codec digital audio link capabilities is described using the following struct :-
+
+/* SoC hardware mode */
+struct snd_soc_hw_mode {
+	u32 hformat;	/* SND_SOC_HWFMT_* */
+	u32 hbits;		/* SND_SOC_HWBITS() */
+	u32 rate;		/* sample rate */
+	u32 fs;			/* ratio of rate:bclk */ 
+	long priv1;		/* private mode data 1 */
+	long priv2;		/* private mode data 2 */
+	long priv3;		/* private mode data 3 */
+};
+
+This example is from the WM8750 and shows the supported options for 16bit pcm data at a 8k sample rate. Note that priv data can be used within the struct for codec setup.
+
+#define WM8750_HWFMT \
+	(SND_SOC_HWFMT_I2S | SND_SOC_HWFMT_LEFT_J | SND_SOC_HWFMT_RIGHT_J | \
+	SND_SOC_HWFMT_CBS_CFS | SND_SOC_HWFMT_CBM_CFM |\
+	SND_SOC_HWFMT_NB_NF | SND_SOC_HWFMT_NB_IF | SND_SOC_HWFMT_IB_NF | \
+	SND_SOC_HWFMT_IB_IF)
+
+/* priv1 is srate register setting */
+static struct snd_soc_hw_mode wm8750_hwfmt[] = {
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 8000,   64, 0x000c},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 8000,   96, 0x000e},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 8000,  128, 0x004c},
+	{WM8750_HWFMT, SND_SOC_HWBITS(16), 8000,  192, 0x004e},
+}
+
+2. Audio controls
+
+   Audio controls are added in the usual ALSA way. There are convenience macros to help with this. i.e.
+
+#define SOC_SINGLE(xname, reg, shift, mask, invert)
+#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert, power)
+#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts)
+#define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts)
+#define SOC_ENUM(xname, xenum)
+
+
+3. Dynamic Power Managment
+
+   Most portable audio codecs have fine grained power control. This enables the codec to use the minimum power possible per function. DPM automatically sets the lowest power state based upon internal audio routing and stream usage. 
+
+ We can have upto 4 codec power domains
+  i. Codec domain - VREF, VMID
+     Usually controlled at codec probe/remove, although can be set
+     at stream time if power is not needed for sidetone, etc.
+ ii. Platform/Machine domain - physically connected inputs and outputs
+     Is platform/machine and user action specific, is set in the .asoundrc file and
+     by userspace e.g when HP are inserted
+iii. Path domain - Internal codec path mixers
+     Are automatically set when mixer and mux settings are
+     changed by the user.
+ iv. Stream domain - DAC's and ADC's.
+     Enabled when stream playback/capture is started.  
+
+The machine domain (ii) is set outwith the codec driver.
+
+3.1 DPM Widgets
+
+   Each codec power block is controlled by a DPM widget. There are many different types of widget, each represents a different internal function within the codec. A dpm widget must be defined for every power block within the codec. i.e.
+
+	SND_SOC_DPM_DAC("Right DAC", "Right Playback", WM8750_PWR2, 7, 0, NULL, 0),
+	SND_SOC_DPM_DAC("Left DAC", "Left Playback", WM8750_PWR2, 8, 0, NULL, 0),
+
+3.2 Widget Interconnects
+
+   Widgets are connected to match the internal codec audio paths. 
+
+3.3 Codec dpm events
+
+
+4. Codec control interface
+
+
+5. Initialisation
+
+
+6. hw params

