sound: asoc: Added codec and machine drivers for cg29xx.

A codec driver and a machine driver have been added for cg29xx.
The platform driver for ux500 has been cleaned up. Directory
u8500 has changed name to ux500.

Signed-off-by: Roger Nilsson <roger.xr.nilsson@stericsson.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/707
Reviewed-by: Ola LILJA <ola.o.lilja@stericsson.com>
Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
Change-Id: I3e8d18f6182159aecc41ed352f8d7866cc70c39e
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/2843
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 6bd9819..ce2e511 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -35,7 +35,7 @@
 source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
 source "sound/soc/txx9/Kconfig"
-source "sound/soc/u8500/Kconfig"
+source "sound/soc/ux500/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index beefeec..f3384c0 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -14,4 +14,4 @@
 obj-$(CONFIG_SND_SOC)	+= s6000/
 obj-$(CONFIG_SND_SOC)	+= sh/
 obj-$(CONFIG_SND_SOC)	+= txx9/
-obj-$(CONFIG_SND_SOC)	+= u8500/
+obj-$(CONFIG_SND_SOC)	+= ux500/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index d6e4b41..a6d7b57 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -67,6 +67,8 @@
 	select SND_SOC_WM9705 if SND_SOC_AC97_BUS
 	select SND_SOC_WM9712 if SND_SOC_AC97_BUS
 	select SND_SOC_WM9713 if SND_SOC_AC97_BUS
+	select SND_SOC_AB3550
+	select SND_SOC_CG29XX
         help
           Normally ASoC codec drivers are only built if a machine driver which
           uses them is also built since they are only usable with a machine
@@ -261,11 +263,10 @@
 	tristate
 
 config SND_SOC_AB3550
-	tristate "ST-Ericsson AB3550 Mixed Signal Circuit CODEC"
-	depends on SND_SOC_U8500 && AB3550_CORE
-	default m
-	help
-	  Select this to support the AB3550 codec block.
+	tristate
+
+config SND_SOC_CG29XX
+	tristate
 
 # Amp
 config SND_SOC_MAX9877
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 962bfe1..7baa53f 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -9,6 +9,7 @@
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-ak4642-objs := ak4642.o
 snd-soc-ak4671-objs := ak4671.o
+snd-soc-cg29xx-objs := cg29xx.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
@@ -72,6 +73,7 @@
 obj-$(CONFIG_SND_SOC_AK4535)	+= snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_AK4642)	+= snd-soc-ak4642.o
 obj-$(CONFIG_SND_SOC_AK4671)	+= snd-soc-ak4671.o
+obj-$(CONFIG_SND_SOC_CG29XX)	+= snd-soc-cg29xx.o
 obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o
diff --git a/sound/soc/codecs/ab3550.c b/sound/soc/codecs/ab3550.c
index 5ee0867..de51ca9 100755
--- a/sound/soc/codecs/ab3550.c
+++ b/sound/soc/codecs/ab3550.c
@@ -1,7 +1,9 @@
 /*
  * Copyright (C) ST-Ericsson SA 2010
  *
- * Author: Xie Xiaolei <xie.xiaolei@etericsson.com>
+ * Author: Xie Xiaolei <xie.xiaolei@etericsson.com>,
+ *         Ola Lilja ola.o.lilja@stericsson.com,
+ *         Roger Nilsson 	roger.xr.nilsson@stericsson.com
  *         for ST-Ericsson.
  *
  * License terms:
@@ -25,34 +27,102 @@
 #include <sound/soc-dapm.h>
 #include <linux/mfd/abx500.h>
 #include <linux/bitops.h>
+#include <asm/atomic.h>
 #include "ab3550.h"
 
-#include "../u8500/u8500_pcm.h"
-#include "../u8500/u8500_msp_dai.h"
 
 #define I2C_BANK 0
 
 static struct device *ab3550_dev = NULL;
 
-#define SET_REG(reg, val) abx500_set_register_interruptible( \
-		ab3550_dev, I2C_BANK, (reg), (val))
+// Registers -----------------------------------------------------------------------------------------------------------------------
 
-#define MASK_SET_REG(reg, mask, val) \
-	abx500_mask_and_set_register_interruptible( \
-		ab3550_dev, I2C_BANK, (reg), (mask), (val))
+struct ab3550_register {
+	const char* name;
+	u8 address;
+};
+
+struct ab3550_register ab3550_registers[] = {
+	{"IDX_MIC_BIAS1",				0x31},
+	{"IDX_MIC_BIAS2",				0x32},
+	{"IDX_MIC_BIAS2_VAD",			0x33},
+	{"IDX_MIC1_GAIN",				0x34},
+	{"IDX_MIC2_GAIN",				0x35},
+	{"IDX_MIC1_INPUT_SELECT",		0x36},
+	{"IDX_MIC2_INPUT_SELECT",		0x37},
+	{"IDX_MIC1_VMID_SELECT",		0x38},
+	{"IDX_MIC2_VMID_SELECT",		0x39},
+	{"IDX_MIC2_TO_MIC1",			0x3A},
+	{"IDX_ANALOG_LOOP_PGA1",		0x3B},
+	{"IDX_ANALOG_LOOP_PGA2",		0x3C},
+	{"IDX_APGA_VMID_SELECT",		0x3D},
+	{"IDX_EAR",						0x3E},
+	{"IDX_AUXO1",					0x3F},
+	{"IDX_AUXO2",					0x40},
+	{"IDX_AUXO_PWR_MODE",			0x41},
+	{"IDX_OFFSET_CANCEL",			0x42},
+	{"IDX_SPKR",					0x43},
+	{"IDX_LINE1",					0x44},
+	{"IDX_LINE2",					0x45},
+	{"IDX_APGA1_ADDER",				0x46},
+	{"IDX_APGA2_ADDER",				0x47},
+	{"IDX_EAR_ADDER",				0x48},
+	{"IDX_AUXO1_ADDER",				0x49},
+	{"IDX_AUXO2_ADDER",				0x4A},
+	{"IDX_SPKR_ADDER",				0x4B},
+	{"IDX_LINE1_ADDER",				0x4C},
+	{"IDX_LINE2_ADDER",				0x4D},
+	{"IDX_EAR_TO_MIC2",				0x4E},
+	{"IDX_SPKR_TO_MIC2",			0x4F},
+	{"IDX_NEGATIVE_CHARGE_PUMP",	0x50},
+	{"IDX_TX1",						0x51},
+	{"IDX_TX2",						0x52},
+	{"IDX_RX1",						0x53},
+	{"IDX_RX2",						0x54},
+	{"IDX_RX3",						0x55},
+	{"IDX_TX_DIGITAL_PGA1",			0X56},
+	{"IDX_TX_DIGITAL_PGA2",			0X57},
+	{"IDX_RX1_DIGITAL_PGA",			0x58},
+	{"IDX_RX2_DIGITAL_PGA",			0x59},
+	{"IDX_RX3_DIGITAL_PGA",			0x5A},
+	{"IDX_SIDETONE1_PGA",			0x5B},
+	{"IDX_SIDETONE2_PGA",			0x5C},
+	{"IDX_CLOCK",					0x5D},
+	{"IDX_INTERFACE0",				0x5E},
+	{"IDX_INTERFACE1",				0x60},
+	{"IDX_INTERFACE0_DATA",			0x5F},
+	{"IDX_INTERFACE1_DATA",			0x61},
+	{"IDX_INTERFACE_LOOP",			0x62},
+	{"IDX_INTERFACE_SWAP",			0x63}
+};
+
+#define SET_REG(reg, val) abx500_set_register_interruptible( \
+		ab3550_dev, I2C_BANK, (reg), (val)); \
+		printk(KERN_DEBUG "REG = 0x%02x, VAL = 0x%02x.\n", (reg), (val))
+
+#define MASK_SET_REG(reg, mask, val) abx500_mask_and_set_register_interruptible( \
+		ab3550_dev, I2C_BANK, (reg), (mask), (val)); \
+		printk(KERN_DEBUG "REG = 0x%02x, MASK = 0x%02x, VAL = 0x%02x.\n", (reg), (mask), (val))
 
 #define GET_REG(reg, val) abx500_get_register_interruptible( \
 		ab3550_dev, I2C_BANK, (reg), (val))
 
-#define AB3550_SUPPORTED_RATE (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
-			       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-
-#define AB3550_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE | \
-			      SNDRV_PCM_FMTBIT_S24_LE)
-
-static const u8 outamp_reg[] = {
-	LINE1, LINE2, SPKR, EAR, AUXO1, AUXO2
+static enum enum_register outamp_indices[] = {
+	IDX_LINE1, IDX_LINE2, IDX_SPKR, IDX_EAR, IDX_AUXO1, IDX_AUXO2
 };
+
+static enum enum_control outamp_gain_controls[] = {
+	IDX_LINE1_Gain, IDX_LINE2_Gain, IDX_SPKR_Gain, IDX_EAR_Gain, IDX_AUXO1_Gain, IDX_AUXO2_Gain
+};
+
+static enum enum_register outamp_adder_indices[] = {
+	IDX_LINE1_ADDER, IDX_LINE2_ADDER, IDX_SPKR_ADDER, IDX_EAR_ADDER, IDX_AUXO1_ADDER, IDX_AUXO2_ADDER
+};
+
+//static const char* outamp_name[] = {
+//	"LINE1", "LINE2", "SPKR", "EAR", "AUXO1", "AUXO2"
+//};
+
 static const u8 outamp_gain_shift[] = {
 	LINEx_GAIN_SHIFT, LINEx_GAIN_SHIFT,
 	SPKR_GAIN_SHIFT, EAR_GAIN_SHIFT,
@@ -74,402 +144,268 @@
 	AUXOx_PWR_MASK, AUXOx_PWR_MASK
 };
 
-static int ab3550_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai* dai);
-static int ab3550_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params, struct snd_soc_dai* dai);
-static void ab3550_pcm_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai* dai);
-
-static int ab3550_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
-				 unsigned int freq, int dir);
-static int ab3550_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt);
-
-struct snd_soc_dai ab3550_codec_dai[] = {
-	{
-		.name = "ab3550_0",
-		.playback = {
-			.stream_name = "ab3550_0",
-			.channels_min = 2,
-			.channels_max = 2,
-			.rates = AB3550_SUPPORTED_RATE,
-			.formats = AB3550_SUPPORTED_FMT,
-		},
-		.capture = {
-			.stream_name = "ab3550_0",
-			.channels_min = 2,
-			.channels_max = 2,
-			.rates = AB3550_SUPPORTED_RATE,
-			.formats = AB3550_SUPPORTED_FMT,
-		},
-		.ops = {
-			.prepare = ab3550_pcm_prepare,
-			.hw_params = ab3550_pcm_hw_params,
-			.shutdown = ab3550_pcm_shutdown,
-			.set_sysclk = ab3550_set_dai_sysclk,
-			.set_fmt = ab3550_set_dai_fmt
-		},
-	},
-	{
-		.name = "ab3550_1",
-		.playback = {
-			.stream_name = "ab3550_1",
-			.channels_min = 2,
-			.channels_max = 2,
-			.rates = AB3550_SUPPORTED_RATE,
-			.formats = AB3550_SUPPORTED_FMT,
-		},
-		.capture = {
-			.stream_name = "ab3550_1",
-			.channels_min = 2,
-			.channels_max = 2,
-			.rates = AB3550_SUPPORTED_RATE,
-			.formats = AB3550_SUPPORTED_FMT,
-		},
-		.ops = {
-			.prepare = ab3550_pcm_prepare,
-			.hw_params = ab3550_pcm_hw_params,
-			.shutdown = ab3550_pcm_shutdown,
-			.set_sysclk = ab3550_set_dai_sysclk,
-			.set_fmt = ab3550_set_dai_fmt
-		},
-	}
+struct codec_dai_private {
+	atomic_t substream_count;
+} privates[] = {
+	{ATOMIC_INIT(0)},
+	{ATOMIC_INIT(0)},
 };
-EXPORT_SYMBOL_GPL(ab3550_codec_dai);
+
+/* TODO: Add a clock use count.
+   Turn off the clock is there is no audio activity in the system.
+ */
+
+
+
+// Controls -----------------------------------------------------------------------------------------------------------------------
 
 struct ab3550_control {
 	const char *name;
 	u8 value;
-	int is_enum;
+	enum enum_register reg_idx;
+	u8 reg_mask;
+	u8 reg_shift;
+	bool is_enum;
+	bool changed;
 };
 
-/* When AB3550 starts up, these registers are initialized to 0 */
 struct ab3550_control ab3550_ctl[] = {
-	{"RX2 Select", 0, 1},
-	{"DAC1 Routing", 0, 0},
-	{"DAC2 Routing", 0, 0},
-	{"DAC3 Routing", 0, 0},
-	{"MIC1 Input Select", 0, 0},
-	{"MIC2 Input Select", 0, 0},
-	{"I2S0 TX", 0, 1},
-	{"I2S1 TX", 0, 1},
-	{"APGA1 Source", 0, 1},
-	{"APGA2 Source", 0, 1},
-	{"APGA1 Destination", 0, 0},
-	{"APGA2 Destination", 0, 0},
-	{"DAC1 Side Tone", 0, 1},
-	{"DAC2 Side Tone", 0, 1},
-	{"RX-DPGA1 Gain", 0x3D, 0},
-	{"RX-DPGA2 Gain", 0x3D, 0},
-	{"RX-DPGA3 Gain", 0x3D, 0},
-	{"LINE1 Gain", 0x0A, 0},
-	{"LINE2 Gain", 0x0A, 0},
-	{"SPKR Gain", 0x0A, 0},
-	{"EAR Gain", 0x0A, 0},
-	{"AUXO1 Gain", 0x0A, 0},
-	{"AUXO2 Gain", 0x0A, 0},
-	{"MIC1 Gain", 0, 0},
-	{"MIC2 Gain", 0, 0},
-	{"TX-DPGA1 Gain", 0, 0},
-	{"TX-DPGA2 Gain", 0, 0},
-	{"ST-PGA1 Gain", 0, 0},
-	{"ST-PGA2 Gain", 0, 0},
-	{"APGA1 Gain", 0, 0},
-	{"APGA2 Gain", 0, 0},
-	{"DAC1 Power Mode", 0, 1},
-	{"DAC2 Power Mode", 0, 1},
-	{"DAC3 Power Mode", 0, 1},
-	{"EAR Power Mode", 0, 1},
-	{"AUXO Power Mode", 0, 1},
-	{"LINE1 Inverse", 0, 0},
-	{"LINE2 Inverse", 0, 0},
-	{"AUXO1 Inverse", 0, 0},
-	{"AUXO2 Inverse", 0, 0},
-	{"AUXO1 Pulldown", 0, 0},
-	{"AUXO2 Pulldown", 0, 0},
-	{"VMID1", 0, 0},
-	{"VMID2", 0, 0},
-	{"MIC1-1 MBias", 0, 1},
-	{"MIC1-2 MBias", 0, 1},
-	{"MIC2-1 MBias", 0, 1},
-	{"MIC2-2 MBias", 0, 1},
-	{"MBIAS1 HiZ Option", 0, 1},
-	{"MBIAS2 HiZ Option", 0, 1},
-	{"MBIAS2 Output Voltage", 0, 1},
-	{"MBIAS2 Internal Resistor", 0, 1},
-	{"MIC1 Input Impedance", 0, 1},
-	{"MIC2 Input Impedance", 0, 1},
-	{"TX1 HP Filter", 0, 1},
-	{"TX2 HP Filter", 0, 1},
-	{"LINEIN1 Pre-charge", 0, 0},
-	{"LINEIN2 Pre-charge", 0, 0},
-	{"MIC1P1 Pre-charge", 0, 0},
-	{"MIC1P2 Pre-charge", 0, 0},
-	{"MIC1N1 Pre-charge", 0, 0},
-	{"MIC1N2 Pre-charge", 0, 0},
-	{"MIC2P1 Pre-charge", 0, 0},
-	{"MIC2P2 Pre-charge", 0, 0},
-	{"MIC2N1 Pre-charge", 0, 0},
-	{"MIC2N2 Pre-charge", 0, 0},
-	{"ST1 HP Filter", 0, 1},
-	{"ST2 HP Filter", 0, 1},
-	{"I2S0 Word Length", 0, 1},
-	{"I2S1 Word Length", 0, 1},
-	{"I2S0 Mode", 0, 1},
-	{"I2S1 Mode", 0, 1},
-	{"I2S0 Tri-state", 0, 1},
-	{"I2S1 Tri-state", 0, 1},
-	{"I2S0 Pulldown", 0, 1},
-	{"I2S1 Pulldown", 0, 1},
-	{"I2S0 Sample Rate", 0, 1},
-	{"I2S1 Sample Rate", 0, 1},
-	{"Interface Loop", 0, 0},
-	{"Interface Swap", 0, 0},
-	{"Commit", 0, 0},
+//    name						value	reg_idx					reg_mask				reg_shift			is_enum		changed
+	{"RX2 Select",				0,		IDX_RX2,				RX2_IF_SELECT_MASK,		RX2_IF_SELECT_SHIFT,true,		false},
+	{"DAC1 Routing",			0x05,	IDX_RX1,				DACx_PWR_MASK,			DACx_PWR_SHIFT,		false,		false}, // DAC1 -> AUXO1, SPKR
+	{"DAC2 Routing",			0x06,	IDX_RX2,				DACx_PWR_MASK,			DACx_PWR_SHIFT,		false,		false}, // DAC2 -> AUXO1, SPKR
+	{"DAC3 Routing",			0x00,	IDX_RX3,				DACx_PWR_MASK,			DACx_PWR_SHIFT,		false,		false},
+	{"MIC1 Input Select",		0x30,	IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"MIC2 Input Select",		0x00,	IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"I2S0 Input Select",		0x11,	IDX_TX1,				0x00,					0x00,				true,		false},
+	{"I2S1 Input Select",		0x22,	IDX_TX2,				0x00,					0x00,				true,		false},
+	{"APGA1 Source",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"APGA2 Source",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"APGA1 Destination",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"APGA2 Destination",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"DAC1 Side Tone",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"DAC2 Side Tone",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"RX-DPGA1 Gain",			0x3A,	IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"RX-DPGA2 Gain",			0x3A,	IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"RX-DPGA3 Gain",			0x00,	IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"LINE1 Gain",				0x4,	IDX_UNKNOWN,			0x00,					0x00,				false,		false}, // -6 dB
+	{"LINE2 Gain",				0x4,	IDX_UNKNOWN,			0x00,					0x00,				false,		false}, // -6 dB
+	{"SPKR Gain",				0x4,	IDX_UNKNOWN,			0x00,					0x00,				false,		false}, // -6 dB
+	{"EAR Gain",				0x4,	IDX_UNKNOWN,			0x00,					0x00,				false,		false}, // -6 dB
+	{"AUXO1 Gain",				0x4,	IDX_UNKNOWN,			0x00,					0x00,				false,		false}, // -6 dB
+	{"AUXO2 Gain",				0x4,	IDX_UNKNOWN,			0x00,					0x00,				false,		false}, // -6 dB
+	{"MIC1 Gain",				0x2,	IDX_MIC1_GAIN,			MICx_GAIN_MASK,			MICx_GAIN_SHIFT,	false,		false},
+	{"MIC2 Gain",				0x2,	IDX_MIC2_GAIN,			MICx_GAIN_MASK,			MICx_GAIN_SHIFT,	false,		false},
+	{"TX-DPGA1 Gain",			0x06,	IDX_TX_DIGITAL_PGA1,	TXDPGAx_MASK,			TXDPGAx_SHIFT,		false,		false},
+	{"TX-DPGA2 Gain",			0x06,	IDX_TX_DIGITAL_PGA2,	TXDPGAx_MASK,			TXDPGAx_SHIFT,		false,		false},
+	{"ST-PGA1 Gain",			0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"ST-PGA2 Gain",			0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"APGA1 Gain",				0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"APGA2 Gain",				0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"DAC1 Power Mode",			0x0,	IDX_UNKNOWN,			0x00,					0x00,				true,		false}, // 100%
+	{"DAC2 Power Mode",			0x0,	IDX_UNKNOWN,			0x00,					0x00,				true,		false}, // 100%
+	{"DAC3 Power Mode",			0x3,	IDX_UNKNOWN,			0x00,					0x00,				true,		false}, // Do not use
+	{"EAR Power Mode",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"AUXO Power Mode",			0x4,	IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"LINE1 Inverse",			0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"LINE2 Inverse",			0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"AUXO1 Inverse",			0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"AUXO2 Inverse",			0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"AUXO1 Pulldown",			0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"AUXO2 Pulldown",			0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"VMID1",					0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"VMID2",					0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"MIC1 MBias",				0x01,	IDX_UNKNOWN,			0x00,					0x00,				true,		false}, // MBias 1 On
+	{"MIC2 MBias",				0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"MBIAS1 HiZ Option",		0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"MBIAS2 HiZ Option",		0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"MBIAS2 Output Voltage",	0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"MBIAS2 Internal Resistor",0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"MIC1 Input Impedance",	0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"MIC2 Input Impedance",	0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"TX1 HP Filter",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"TX2 HP Filter",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"LINEIN1 Pre-charge",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"LINEIN2 Pre-charge",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"MIC1P1 Pre-charge",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"MIC1P2 Pre-charge",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"MIC1N1 Pre-charge",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"MIC1N2 Pre-charge",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"MIC2P1 Pre-charge",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"MIC2P2 Pre-charge",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"MIC2N1 Pre-charge",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"MIC2N2 Pre-charge",		0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"ST1 HP Filter",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"ST2 HP Filter",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"I2S0 Word Length",		0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"I2S1 Word Length",		0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"I2S0 Mode",				0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"I2S1 Mode",				0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"I2S0 Tri-state",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"I2S1 Tri-state",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"I2S0 Pulldown",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"I2S1 Pulldown",			0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"I2S0 Sample Rate",		0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"I2S1 Sample Rate",		0,		IDX_UNKNOWN,			0x00,					0x00,				true,		false},
+	{"Interface Loop",			0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"Interface Swap",			0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"Voice Call",				0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false},
+	{"Commit",					0,		IDX_UNKNOWN,			0x00,					0x00,				false,		false}
 };
 
 /* control No. 0 correpsonds to bit No. 0 in this array.
    If a control value has been changed, but not commited
    to the AB3550. Its corresponding bit is set.
  */
-static unsigned long control_changed[] = {
-	0, 0, 0, 0
-};
+//static unsigned long changed_controls[BIT_WORD(ARRAY_SIZE(ab3550_ctl)) + 1];
 
 static const char *enum_rx2_select[] = {"I2S0", "I2S1"};
-static const char *enum_i2s_tx[] =
-{"TX1", "TX2", "tri-state", "mute"};
+static const char *enum_i2s_input_select[] = {"tri-state", "MIC1", "MIC2", "mute"};
+static const char *enum_apga1_source[] = {"None", "LINEIN1", "MIC1", "MIC2"};
+static const char *enum_apga2_source[] = {"None", "LINEIN2", "MIC1", "MIC2"};
+static const char *enum_dac_side_tone[] = {"None", "TX1", "TX2"};
+static const char *enum_dac_power_mode[] = {"100%", "75%", "50%"};
+static const char *enum_ear_power_mode[] = {"100%", "75%"};
+static const char *enum_auxo_power_mode[] = {"100%", "75%", "50%", "25%"};
+static const char *enum_mbias[] = {"Off", "On"};
+static const char *enum_mbias_hiz_option[] = {"GND", "HiZ"};
+static const char *enum_mbias2_output_voltage[] = {"2.0v", "2.2v"};
+static const char *enum_mbias2_internal_resistor[] = {"connected", "bypassed"};
+static const char *enum_mic_input_impedance[] = {"12.5 kohm", "25 kohm", "50 kohm"};
+static const char *enum_hp_filter[] = {"HP3", "HP1", "bypass"};
+static const char *enum_i2s_word_length[] = {"16 bits", "24 bits"};
+static const char *enum_i2s_mode[] = {"Master Mode", "Slave Mode"};
+static const char *enum_i2s_tristate[] = {"Normal", "Tri-state"};
+static const char *enum_i2s_pulldown[] = {"disconnected", "connected"};
+static const char *enum_i2s_sample_rate[] = {"8 kHz", "16 kHz", "44.1 kHz", "48 kHz"};
 
-static const char *enum_apga1_source[] =
-{"None", "LINEIN1", "MIC1", "MIC2"};
-
-static const char *enum_apga2_source[] =
-{"None", "LINEIN2", "MIC1", "MIC2"};
-
-static const char *enum_dac1_side_tone[] =
-{"None", "TX1", "TX2"};
-
-static const char *enum_dac1_power_mode[] =
-{"100%", "75%", "50%"};
-
-static const char *enum_ear_power_mode[] =
-{"100%", "75%"};
-
-static const char *enum_auxo_power_mode[] =
-{"100%", "75%", "50%", "25%"};
-
-static const char *enum_mic_bias_connection[] =
-{"MIC Bias 1", "MIC Bias 2"};
-
-static const char *enum_mbias1_hiz_option[] =
-{"GND", "HiZ"};
-
-static const char *enum_mbias2_output_voltage[] =
-{"2.0v", "2.2v"};
-
-static const char *enum_mbias2_internal_resistor[] =
-{"connected", "bypassed"};
-
-static const char *enum_mic1_input_impedance[] =
-{"12.5 kohm", "25 kohm", "50 kohm"};
-
-static const char *enum_tx1_hp_filter[] =
-{"HP3", "HP1", "bypass"};
-
-static const char *enum_i2s_word_length[] =
-{"16 bits", "24 bits"};
-
-static const char *enum_i2s_mode[] =
-{"Master Mode", "Slave Mode"};
-
-static const char *enum_i2s_tristate[] =
-{"Normal", "Tri-state"};
-
-static const char *enum_i2s_pulldown[] =
-{"disconnected", "connected"};
-
-static const char *enum_i2s_sample_rate[] =
-{"8 kHz", "16 kHz", "44.1 kHz", "48 kHz"};
-
-
-static struct soc_enum ab3550_enum[] = {
-	/* RX2 Select */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_rx2_select), enum_rx2_select),
-	/* I2S0 TX */
-	SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_tx), enum_i2s_tx),
-	/* I2S1 TX */
-	SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_tx), enum_i2s_tx),
-	/* APGA1 Source */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga1_source),
-			enum_apga1_source),
-	/* APGA2 Source */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga2_source),
-			enum_apga2_source),
-	/* DAC1 Side Tone */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_side_tone),
-			enum_dac1_side_tone),
-	/* DAC2 Side Tone */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_side_tone),
-			enum_dac1_side_tone),
-	/* DAC1 Power Mode */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_power_mode),
-			enum_dac1_power_mode),
-	/* DAC2 Power Mode */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_power_mode),
-			enum_dac1_power_mode),
-	/* DAC3 Power Mode */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac1_power_mode),
-			enum_dac1_power_mode),
-	/* EAR Power Mode */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_ear_power_mode),
-			enum_ear_power_mode),
-	/* AUXO Power Mode */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_auxo_power_mode),
-			enum_auxo_power_mode),
-	/* MIC Bias Connection */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection),
-			enum_mic_bias_connection),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection),
-			enum_mic_bias_connection),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection),
-			enum_mic_bias_connection),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_bias_connection),
-			enum_mic_bias_connection),
-	/* MBIAS1 HiZ Option */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias1_hiz_option),
-			enum_mbias1_hiz_option),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias1_hiz_option),
-			enum_mbias1_hiz_option),
-	/* MBIAS2 Output voltage */
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_output_voltage),
-			enum_mbias2_output_voltage),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_internal_resistor),
-			enum_mbias2_internal_resistor),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic1_input_impedance),
-			enum_mic1_input_impedance),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic1_input_impedance),
-			enum_mic1_input_impedance),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter),
-			enum_tx1_hp_filter),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter),
-			enum_tx1_hp_filter),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter),
-			enum_tx1_hp_filter),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_tx1_hp_filter),
-			enum_tx1_hp_filter),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length),
-			enum_i2s_word_length),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length),
-			enum_i2s_word_length),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode),
-			enum_i2s_mode),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode),
-			enum_i2s_mode),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate),
-			enum_i2s_tristate),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate),
-			enum_i2s_tristate),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown),
-			enum_i2s_pulldown),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown),
-			enum_i2s_pulldown),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate),
-			enum_i2s_sample_rate),
-	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate),
-			enum_i2s_sample_rate),
-
-};
+static struct soc_enum soc_enum_rx2_select = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_rx2_select), enum_rx2_select); // RX2 Select
+static struct soc_enum soc_enum_i2s0_input_select = SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_input_select), enum_i2s_input_select); // I2S0 Input Select
+static struct soc_enum soc_enum_i2s1_input_select = SOC_ENUM_DOUBLE(0, 0, 4, ARRAY_SIZE(enum_i2s_input_select), enum_i2s_input_select); // I2S1 Input Select
+static struct soc_enum soc_enum_apga1_source = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga1_source), enum_apga1_source); // APGA1 Source
+static struct soc_enum soc_enum_apga2_source = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_apga2_source), enum_apga2_source); // APGA2 Source
+static struct soc_enum soc_enum_dac1_side_tone = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_side_tone), enum_dac_side_tone); // DAC1 Side Tone
+static struct soc_enum soc_enum_dac2_side_tone = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_side_tone), enum_dac_side_tone); // DAC2 Side Tone
+static struct soc_enum soc_enum_dac1_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_power_mode), enum_dac_power_mode); // DAC1 Power Mode
+static struct soc_enum soc_enum_dac2_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_power_mode), enum_dac_power_mode); // DAC2 Power Mode
+static struct soc_enum soc_enum_dac3_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_dac_power_mode), enum_dac_power_mode); // DAC3 Power Mode
+static struct soc_enum soc_enum_ear_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_ear_power_mode), enum_ear_power_mode); // EAR Power Mode
+static struct soc_enum soc_enum_auxo_power_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_auxo_power_mode), enum_auxo_power_mode); // AUXO Power Mode
+static struct soc_enum soc_enum_mic1_mbias = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias), enum_mbias); // MIC Bias Connection
+static struct soc_enum soc_enum_mic2_mbias = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias), enum_mbias); // MIC Bias Connection
+static struct soc_enum soc_enum_mbias1_hiz_option = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias_hiz_option), enum_mbias_hiz_option); // MBIAS1 HiZ Option
+static struct soc_enum soc_enum_mbias2_hiz_option = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias_hiz_option), enum_mbias_hiz_option); // MBIAS1 HiZ Option
+static struct soc_enum soc_enum_mbias2_output_voltage = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_output_voltage), enum_mbias2_output_voltage); // MBIAS2 Output voltage
+static struct soc_enum soc_enum_mbias2_internal_resistor = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mbias2_internal_resistor), enum_mbias2_internal_resistor); //
+static struct soc_enum soc_enum_mic1_input_impedance = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_input_impedance), enum_mic_input_impedance); //
+static struct soc_enum soc_enum_mic2_input_impedance = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_mic_input_impedance), enum_mic_input_impedance); //
+static struct soc_enum soc_enum_tx1_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); //
+static struct soc_enum soc_enum_tx2_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); //
+static struct soc_enum soc_enum_st1_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); //
+static struct soc_enum soc_enum_st2_hp_filter = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_hp_filter), enum_hp_filter); //
+static struct soc_enum soc_enum_i2s0_word_length = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length), enum_i2s_word_length); //
+static struct soc_enum soc_enum_i2s1_word_length = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_word_length), enum_i2s_word_length); //
+static struct soc_enum soc_enum_i2s0_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode), enum_i2s_mode); //
+static struct soc_enum soc_enum_i2s1_mode = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_mode), enum_i2s_mode); //
+static struct soc_enum soc_enum_i2s0_tristate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate), enum_i2s_tristate); //
+static struct soc_enum soc_enum_i2s1_tristate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_tristate), enum_i2s_tristate); //
+static struct soc_enum soc_enum_i2s0_pulldown = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown), enum_i2s_pulldown); //
+static struct soc_enum soc_enum_i2s1_pulldown = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_pulldown), enum_i2s_pulldown); //
+static struct soc_enum soc_enum_i2s0_sample_rate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate), enum_i2s_sample_rate); //
+static struct soc_enum soc_enum_i2s1_sample_rate = SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(enum_i2s_sample_rate), enum_i2s_sample_rate); //
 
 static struct snd_kcontrol_new ab3550_snd_controls[] = {
 	/* RX Routing */
-	SOC_ENUM("RX2 Select", ab3550_enum[0]),
-	SOC_SINGLE("DAC1 Routing", 0, 0, 0x3f, 0),
-	SOC_SINGLE("DAC2 Routing", 0, 0, 0x3f, 0),
-	SOC_SINGLE("DAC3 Routing", 0, 0, 0x3f, 0),
+	SOC_ENUM(	"RX2 Select",			soc_enum_rx2_select),
+	SOC_SINGLE(	"DAC1 Routing",			0, 0, 0x3f, 0),
+	SOC_SINGLE(	"DAC2 Routing",			0, 0, 0x3f, 0),
+	SOC_SINGLE(	"DAC3 Routing",			0, 0, 0x3f, 0),
 	/* TX Routing */
-	SOC_SINGLE("MIC1 Input Select", 0, 0, 0xff, 0),
-	SOC_SINGLE("MIC2 Input Select", 0, 0, 0x3f, 0),
-	SOC_ENUM("I2S0 TX", ab3550_enum[1]),
-	SOC_ENUM("I2S1 TX", ab3550_enum[2]),
+	SOC_SINGLE(	"MIC1 Input Select",	0, 0, 0xff, 0),
+	SOC_SINGLE(	"MIC2 Input Select",	0, 0, 0x3f, 0),
+	SOC_ENUM(	"I2S0 Input Select",	soc_enum_i2s0_input_select),
+	SOC_ENUM(	"I2S1 Input Select",	soc_enum_i2s1_input_select),
 	/* Routing of Side Tone and Analop Loop */
-	SOC_ENUM("APGA1 Source", ab3550_enum[3]),
-	SOC_ENUM("APGA2 Source", ab3550_enum[4]),
-	SOC_SINGLE("APGA1 Destination", 0, 0, 0x3f, 0),
-	SOC_SINGLE("APGA2 Destination", 0, 0, 0x3f, 0),
-	SOC_ENUM("DAC1 Side Tone", ab3550_enum[5]),
-	SOC_ENUM("DAC2 Side Tone", ab3550_enum[5]),
+	SOC_ENUM(	"APGA1 Source",			soc_enum_apga1_source),
+	SOC_ENUM(	"APGA2 Source",			soc_enum_apga2_source),
+	SOC_SINGLE(	"APGA1 Destination",	0, 0, 0x3f, 0),
+	SOC_SINGLE(	"APGA2 Destination",	0, 0, 0x3f, 0),
+	SOC_ENUM(	"DAC1 Side Tone",		soc_enum_dac1_side_tone),
+	SOC_ENUM(	"DAC2 Side Tone",		soc_enum_dac2_side_tone),
 	/* RX Volume Control */
-	SOC_SINGLE("RX-DPGA1 Gain", 0, 0, 66, 0),
-	SOC_SINGLE("RX-DPGA2 Gain", 0, 0, 66, 0),
-	SOC_SINGLE("RX-DPGA3 Gain", 0, 0, 66, 0),
-	SOC_SINGLE("LINE1 Gain", 0, 0, 10, 0),
-	SOC_SINGLE("LINE2 Gain", 0, 0, 10, 0),
-	SOC_SINGLE("SPKR Gain", 0, 0, 22, 0),
-	SOC_SINGLE("EAR Gain", 0, 0, 14, 0),
-	SOC_SINGLE("AUXO1 Gain", 0, 0, 12, 0),
-	SOC_SINGLE("AUXO2 Gain", 0, 0, 12, 0),
+	SOC_SINGLE(	"RX-DPGA1 Gain",		0, 0, 66, 0),
+	SOC_SINGLE(	"RX-DPGA2 Gain",		0, 0, 66, 0),
+	SOC_SINGLE(	"RX-DPGA3 Gain",		0, 0, 66, 0),
+	SOC_SINGLE(	"LINE1 Gain",			0, 0, 10, 0),
+	SOC_SINGLE(	"LINE2 Gain",			0, 0, 10, 0),
+	SOC_SINGLE(	"SPKR Gain",			0, 0, 22, 0),
+	SOC_SINGLE(	"EAR Gain",				0, 0, 14, 0),
+	SOC_SINGLE(	"AUXO1 Gain",			0, 0, 12, 0),
+	SOC_SINGLE(	"AUXO2 Gain",			0, 0, 12, 0),
 	/* TX Volume Control */
-	SOC_SINGLE("MIC1 Gain", 0, 0, 10, 0),
-	SOC_SINGLE("MIC2 Gain", 0, 0, 10, 0),
-	SOC_SINGLE("TX-DPGA1 Gain", 0, 0, 15, 0),
-	SOC_SINGLE("TX-DPGA2 Gain", 0, 0, 15, 0),
+	SOC_SINGLE(	"MIC1 Gain",			0, 0, 10, 0),
+	SOC_SINGLE(	"MIC2 Gain",			0, 0, 10, 0),
+	SOC_SINGLE(	"TX-DPGA1 Gain",		0, 0, 15, 0),
+	SOC_SINGLE(	"TX-DPGA2 Gain",		0, 0, 15, 0),
 	/* Volume Control of Side Tone and Analog Loop */
-	SOC_SINGLE("ST-PGA1 Gain", 0, 0, 10, 0),
-	SOC_SINGLE("ST-PGA2 Gain", 0, 0, 10, 0),
-	SOC_SINGLE("APGA1 Gain", 0, 0, 28, 0),
-	SOC_SINGLE("APGA2 Gain", 0, 0, 28, 0),
+	SOC_SINGLE(	"ST-PGA1 Gain",			0, 0, 10, 0),
+	SOC_SINGLE(	"ST-PGA2 Gain",			0, 0, 10, 0),
+	SOC_SINGLE(	"APGA1 Gain",			0, 0, 28, 0),
+	SOC_SINGLE(	"APGA2 Gain",			0, 0, 28, 0),
 	/* RX Properties */
-	SOC_ENUM("DAC1 Power Mode", ab3550_enum[6]),
-	SOC_ENUM("DAC2 Power Mode", ab3550_enum[7]),
-	SOC_ENUM("DAC3 Power Mode", ab3550_enum[8]),
-	SOC_ENUM("EAR Power Mode", ab3550_enum[9]),
-	SOC_ENUM("AUXO Power Mode", ab3550_enum[10]),
-	SOC_SINGLE("LINE1 Inverse", 0, 0, 1, 0),
-	SOC_SINGLE("LINE2 Inverse", 0, 0, 1, 0),
-	SOC_SINGLE("AUXO1 Inverse", 0, 0, 1, 0),
-	SOC_SINGLE("AUXO2 Inverse", 0, 0, 1, 0),
-	SOC_SINGLE("AUXO1 Pulldown", 0, 0, 1, 0),
-	SOC_SINGLE("AUXO2 Pulldown", 0, 0, 1, 0),
+	SOC_ENUM(	"DAC1 Power Mode",		soc_enum_dac1_power_mode),
+	SOC_ENUM(	"DAC2 Power Mode",		soc_enum_dac2_power_mode),
+	SOC_ENUM(	"DAC3 Power Mode",		soc_enum_dac3_power_mode),
+	SOC_ENUM(	"EAR Power Mode",		soc_enum_ear_power_mode),
+	SOC_ENUM(	"AUXO Power Mode",		soc_enum_auxo_power_mode),
+	SOC_SINGLE(	"LINE1 Inverse",		0, 0, 1, 0),
+	SOC_SINGLE(	"LINE2 Inverse",		0, 0, 1, 0),
+	SOC_SINGLE(	"AUXO1 Inverse",		0, 0, 1, 0),
+	SOC_SINGLE(	"AUXO2 Inverse",		0, 0, 1, 0),
+	SOC_SINGLE(	"AUXO1 Pulldown",		0, 0, 1, 0),
+	SOC_SINGLE(	"AUXO2 Pulldown",		0, 0, 1, 0),
 	/* TX Properties */
-	SOC_SINGLE("VMID1", 0, 0, 0xff, 0),
-	SOC_SINGLE("VMID2", 0, 0, 0xff, 0),
-	SOC_ENUM("MIC1-1 MBias", ab3550_enum[11]),
-	SOC_ENUM("MIC1-2 MBias", ab3550_enum[12]),
-	SOC_ENUM("MIC2-1 MBias", ab3550_enum[13]),
-	SOC_ENUM("MIC2-2 MBias", ab3550_enum[14]),
-	SOC_ENUM("MBIAS1 HiZ Option", ab3550_enum[15]),
-	SOC_ENUM("MBIAS2 HiZ Option", ab3550_enum[16]),
-	SOC_ENUM("MBIAS2 Output Voltage", ab3550_enum[17]),
-	SOC_ENUM("MBIAS2 Internal Resistor", ab3550_enum[18]),
-	SOC_ENUM("MIC1 Input Impedance", ab3550_enum[19]),
-	SOC_ENUM("MIC2 Input Impedance", ab3550_enum[20]),
-	SOC_ENUM("TX1 HP Filter", ab3550_enum[21]),
-	SOC_ENUM("TX2 HP Filter", ab3550_enum[22]),
-	SOC_SINGLE("LINEIN1 Pre-charge", 0, 0, 100, 0),
-	SOC_SINGLE("LINEIN2 Pre-charge", 0, 0, 100, 0),
-	SOC_SINGLE("MIC1P1 Pre-charge", 0, 0, 100, 0),
-	SOC_SINGLE("MIC1P2 Pre-charge", 0, 0, 100, 0),
-	SOC_SINGLE("MIC1N1 Pre-charge", 0, 0, 100, 0),
-	SOC_SINGLE("MIC1N2 Pre-charge", 0, 0, 100, 0),
-	SOC_SINGLE("MIC2P1 Pre-charge", 0, 0, 100, 0),
-	SOC_SINGLE("MIC2P2 Pre-charge", 0, 0, 100, 0),
-	SOC_SINGLE("MIC2N1 Pre-charge", 0, 0, 100, 0),
-	SOC_SINGLE("MIC2N2 Pre-charge", 0, 0, 100, 0),
+	SOC_SINGLE(	"VMID1",				0, 0, 0xff, 0),
+	SOC_SINGLE(	"VMID2",				0, 0, 0xff, 0),
+	SOC_ENUM(	"MIC1 MBias",			soc_enum_mic1_mbias),
+	SOC_ENUM(	"MIC2 MBias",			soc_enum_mic2_mbias),
+	SOC_ENUM(	"MBIAS1 HiZ Option",	soc_enum_mbias1_hiz_option),
+	SOC_ENUM(	"MBIAS2 HiZ Option",	soc_enum_mbias2_hiz_option),
+	SOC_ENUM(	"MBIAS2 Output Voltage",	soc_enum_mbias2_output_voltage),
+	SOC_ENUM(	"MBIAS2 Internal Resistor", soc_enum_mbias2_internal_resistor),
+	SOC_ENUM(	"MIC1 Input Impedance",		soc_enum_mic1_input_impedance),
+	SOC_ENUM(	"MIC2 Input Impedance",		soc_enum_mic2_input_impedance),
+	SOC_ENUM(	"TX1 HP Filter",		soc_enum_tx1_hp_filter),
+	SOC_ENUM(	"TX2 HP Filter",		soc_enum_tx2_hp_filter),
+	SOC_SINGLE(	"LINEIN1 Pre-charge",	0, 0, 100, 0),
+	SOC_SINGLE(	"LINEIN2 Pre-charge",	0, 0, 100, 0),
+	SOC_SINGLE(	"MIC1P1 Pre-charge",	0, 0, 100, 0),
+	SOC_SINGLE(	"MIC1P2 Pre-charge",	0, 0, 100, 0),
+	SOC_SINGLE(	"MIC1N1 Pre-charge",	0, 0, 100, 0),
+	SOC_SINGLE(	"MIC1N2 Pre-charge",	0, 0, 100, 0),
+	SOC_SINGLE(	"MIC2P1 Pre-charge",	0, 0, 100, 0),
+	SOC_SINGLE(	"MIC2P2 Pre-charge",	0, 0, 100, 0),
+	SOC_SINGLE(	"MIC2N1 Pre-charge",	0, 0, 100, 0),
+	SOC_SINGLE(	"MIC2N2 Pre-charge",	0, 0, 100, 0),
 	/* Side Tone and Analog Loop Properties */
-	SOC_ENUM("ST1 HP Filter", ab3550_enum[23]),
-	SOC_ENUM("ST2 HP Filter", ab3550_enum[24]),
+	SOC_ENUM(	"ST1 HP Filter",		soc_enum_st1_hp_filter),
+	SOC_ENUM(	"ST2 HP Filter",		soc_enum_st2_hp_filter),
 	/* I2S Interface Properties */
-	SOC_ENUM("I2S0 Word Length", ab3550_enum[25]),
-	SOC_ENUM("I2S1 Word Length", ab3550_enum[26]),
-	SOC_ENUM("I2S0 Mode", ab3550_enum[27]),
-	SOC_ENUM("I2S1 Mode", ab3550_enum[28]),
-	SOC_ENUM("I2S0 tri-state", ab3550_enum[29]),
-	SOC_ENUM("I2S1 tri-state", ab3550_enum[30]),
-	SOC_ENUM("I2S0 pulldown", ab3550_enum[31]),
-	SOC_ENUM("I2S1 pulldown", ab3550_enum[32]),
-	SOC_ENUM("I2S0 Sample Rate", ab3550_enum[33]),
-	SOC_ENUM("I2S1 Sample Rate", ab3550_enum[34]),
-	SOC_SINGLE("Interface Loop", 0, 0, 0x0f, 0),
-	SOC_SINGLE("Interface Swap", 0, 0, 0x1f, 0),
+	SOC_ENUM(	"I2S0 Word Length",		soc_enum_i2s0_word_length),
+	SOC_ENUM(	"I2S1 Word Length",		soc_enum_i2s1_word_length),
+	SOC_ENUM(	"I2S0 Mode",			soc_enum_i2s0_mode),
+	SOC_ENUM(	"I2S1 Mode",			soc_enum_i2s1_mode),
+	SOC_ENUM(	"I2S0 tri-state",		soc_enum_i2s0_tristate),
+	SOC_ENUM(	"I2S1 tri-state",		soc_enum_i2s1_tristate),
+	SOC_ENUM(	"I2S0 pulldown",		soc_enum_i2s0_pulldown),
+	SOC_ENUM(	"I2S1 pulldown",		soc_enum_i2s1_pulldown),
+	SOC_ENUM(	"I2S0 Sample Rate",		soc_enum_i2s0_sample_rate),
+	SOC_ENUM(	"I2S1 Sample Rate",		soc_enum_i2s1_sample_rate),
+	SOC_SINGLE(	"Interface Loop",		0, 0, 0x0f, 0),
+	SOC_SINGLE(	"Interface Swap",		0, 0, 0x1f, 0),
 	/* Miscellaneous */
-	SOC_SINGLE("Commit", 0, 0, 1, 0)
+	SOC_SINGLE(	"Voice Call",			0, 0, 1, 0),
+	SOC_SINGLE(	"Commit",				0, 0, 1, 0)
 };
 
 static const struct snd_soc_dapm_widget ab3550_dapm_widgets[] = {
@@ -478,11 +414,12 @@
 static const struct snd_soc_dapm_route intercon[] = {
 };
 
-static unsigned int ab3550_get_control_value(struct snd_soc_codec *codec,
-					     unsigned int ctl)
+static unsigned int ab3550_get_control_value(struct snd_soc_codec *codec, unsigned int ctl)
 {
 	if (ctl >= ARRAY_SIZE(ab3550_ctl))
 		return -EINVAL;
+	//printk(KERN_DEBUG "%s: returning value of control %s: 0x%02x.\n",
+	//      __func__, ab3550_ctl[ctl].name, ab3550_ctl[ctl].value);
 	return ab3550_ctl[ctl].value;
 }
 
@@ -498,25 +435,27 @@
 
 static unsigned int get_control_value_by_name(const char *name)
 {
-	int i, n;
-	for(i = 0, n = ARRAY_SIZE(ab3550_ctl); i < n; i++) {
-		if (strcmp(ab3550_ctl[i].name, name) == 0)
-			break;
-	}
+	int i = get_control_index(name);
 	return ab3550_ctl[i].value;
 }
 
+static unsigned int get_control_changed_by_name(const char *name)
+{
+	int i = get_control_index(name);
+	return ab3550_ctl[i].changed;
+}
+
 static int gain_ramp(u8 reg, u8 target, int shift, u8 mask)
 {
-	u8 curr, step;
+//	u8 curr;
 	int ret = 0;
 
 //	printk(KERN_DEBUG "%s invoked.\n", __func__);
-	if ((ret = GET_REG(reg, &curr))) {
-		return ret;
-	}
-	if (curr == target)
-		return 0;
+//	if ((ret = GET_REG(reg, &curr))) {
+//		return ret;
+//	}
+//	if (curr == target)
+//		return 0;
 //	step = curr < target ? 1 : -1;
 //	while (!ret && curr != target) {
 /*		At least 15ms are required, so I take 16ms :-) */
@@ -527,125 +466,79 @@
 	return ret;
 }
 
-static int ab3550_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
-				unsigned int freq, int dir)
-{
-	printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__);
-	return 0;
-}
-
-static int ab3550_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
-{
-	printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
-	return 0;
-}
-
-static int ab3550_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai* dai)
-{
-	u8 val;
-	printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
-	MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 1 << CLOCK_ENABLE_SHIFT);
-	/* Turn on the master generator */
-	MASK_SET_REG(INTERFACE0, MASTER_GENx_PWR_MASK,
-		     1 << MASTER_GENx_PWR_SHIFT);
-	GET_REG(CLOCK, &val);
-	printk(KERN_DEBUG "%s: CLOCK = %02x.\n", __func__, val);
-	GET_REG(INTERFACE0, &val);
-	printk(KERN_DEBUG "%s: INTERFACE0 = %02x.\n", __func__, val);
-	GET_REG(RX1, &val);
-	printk(KERN_DEBUG "%s: RX1 = %02x.\n", __func__, val);
-	GET_REG(RX2, &val);
-	printk(KERN_DEBUG "%s: RX2 = %02x.\n", __func__, val);
-	GET_REG(SPKR_ADDER, &val);
-	printk(KERN_DEBUG "%s: SPKR_ADDER = %02x.\n", __func__, val);
-	GET_REG(AUXO1_ADDER, &val);
-	printk(KERN_DEBUG "%s: AUXO1_ADDER = %02x.\n", __func__, val);
-	GET_REG(AUXO2_ADDER, &val);
-	printk(KERN_DEBUG "%s: AUXO2_ADDER = %02x.\n", __func__, val);
-	GET_REG(SPKR, &val);
-	printk(KERN_DEBUG "%s: SPKR = %02x.\n", __func__, val);
-	GET_REG(AUXO1, &val);
-	printk(KERN_DEBUG "%s: AUXO1 = %02x.\n", __func__, val);
-	GET_REG(AUXO2, &val);
-	printk(KERN_DEBUG "%s: AUXO2 = %02x.\n", __func__, val);
-	GET_REG(RX1_DIGITAL_PGA, &val);
-	printk(KERN_DEBUG "%s: RX1_DIGITAL_PGA = %02x.\n", __func__, val);
-	GET_REG(RX2_DIGITAL_PGA, &val);
-	printk(KERN_DEBUG "%s: RX2_DIGITAL_PGA = %02x.\n", __func__, val);
-	return 0;
-}
-
-static void ab3550_pcm_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai* dai)
+static void ab3550_print_registers(int stream_dir)
 {
 	int i;
-	printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
-	MASK_SET_REG(INTERFACE0, MASTER_GENx_PWR_MASK, 0);
-	MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 0);
-	for (i = 0; i < ARRAY_SIZE(outamp_reg); i++) {
-		MASK_SET_REG(outamp_reg[i], outamp_pwr_mask[i], 0);
+	u8 val;
+	for (i = 0; i < ARRAY_SIZE(ab3550_registers); i++) {
+		GET_REG(ab3550_registers[i].address, &val);
+		printk(KERN_DEBUG "REGISTERS: %s = 0x%02x\n", ab3550_registers[i].name, val);
 	}
 }
 
-static int set_up_speaker_playback(void)
-{
-	printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
-	MASK_SET_REG(CLOCK, CLOCK_REF_SELECT_MASK,
-		     1 << CLOCK_REF_SELECT_SHIFT);
+static void ab3550_toggle_output_amp(int idx_amp, u8 val) {
+	struct ab3550_register* reg = &(ab3550_registers[(int)outamp_indices[idx_amp]]);
+	MASK_SET_REG(reg->address,
+				 outamp_pwr_mask[idx_amp],
+				 val << outamp_pwr_shift[idx_amp]);
 
-	/* Sample rate 48 kHz */
-	SET_REG(INTERFACE0, 0x03);
-	printk(KERN_DEBUG "%s: interface 0 is setup", __func__);
-
-	MASK_SET_REG(RX2, RX2_IF_SELECT_MASK, 0);
-	SET_REG(SPKR_ADDER, 1 << DAC1_TO_ADDER_SHIFT |
-		1 << DAC2_TO_ADDER_SHIFT);
-/* 	SET_REG(AUXO1_ADDER, 1 << DAC1_TO_ADDER_SHIFT); */
-/* 	SET_REG(AUXO2_ADDER, 1 << DAC2_TO_ADDER_SHIFT); */
-	printk(KERN_DEBUG "%s: Routes to the speaker and the AUXO are setup.\n",
-	       __func__);
-
-	/* Mute the outamps first */
-	MASK_SET_REG(RX1_DIGITAL_PGA, RXx_PGA_GAIN_MASK, 0);
-	MASK_SET_REG(RX2_DIGITAL_PGA, RXx_PGA_GAIN_MASK, 0);
-/* 	MASK_SET_REG(AUXO1, AUXOx_GAIN_MASK, 0); */
-/* 	MASK_SET_REG(AUXO2, AUXOx_GAIN_MASK, 0); */
-	MASK_SET_REG(SPKR, SPKR_GAIN_MASK, 0);
-
-	MASK_SET_REG(RX1, RX1_PWR_MASK | DAC1_PWR_MASK | DAC1_PWR_MODE_MASK,
-		1 << RX1_PWR_SHIFT | 1 << DAC1_PWR_SHIFT |
-		1 << DAC1_PWR_MODE_SHIFT);
-	MASK_SET_REG(RX2, RX2_PWR_MASK | DAC2_PWR_MASK | DAC2_PWR_MODE_MASK,
-		     1 << RX2_PWR_SHIFT | 1 << DAC2_PWR_SHIFT |
-		     1 << DAC2_PWR_MODE_SHIFT);
-	printk(KERN_DEBUG "%s: DAC1, DAC2 and RX2 are now powered up.\n",
-	       __func__);
-
-	MASK_SET_REG(SPKR, SPKR_PWR_MASK, 1 << SPKR_PWR_SHIFT);
-/* 	MASK_SET_REG(AUXO1, AUXOx_PWR_MASK, 1 << AUXOx_PWR_SHIFT); */
-/* 	MASK_SET_REG(AUXO2, AUXOx_PWR_MASK, 1 << AUXOx_PWR_SHIFT); */
-	printk(KERN_DEBUG "%s: The speaker and the AUXO are now powered up.\n",
-	       __func__);
-
-	gain_ramp(RX1_DIGITAL_PGA, get_control_value_by_name("RX-DPGA1 Gain"),
-		  RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
-
-	gain_ramp(RX2_DIGITAL_PGA, get_control_value_by_name("RX-DPGA2 Gain"),
-		  RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
-	gain_ramp(SPKR, get_control_value_by_name("SPKR Gain"),
-		  SPKR_GAIN_SHIFT, SPKR_GAIN_MASK);
-/* 	gain_ramp(AUXO1, get_control_value_by_name("AUXO1 Gain"), */
-/* 		  AUXOx_GAIN_SHIFT, AUXOx_GAIN_MASK); */
-/* 	gain_ramp(AUXO2, get_control_value_by_name("AUXO1 Gain"), */
-/* 		  AUXOx_GAIN_SHIFT, AUXOx_GAIN_MASK); */
-	printk(KERN_DEBUG "%s: return 0.\n", __func__);
-	return 0;
+	printk(KERN_DEBUG "DORIANO: %s turned %s.\n", reg->name, val == 0 ? "off" : "on");
 }
 
-static int ab3550_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params, struct snd_soc_dai* dai)
+static void ab3550_toggle_mic_amp(int idx, u8 val) {
+	enum enum_register idx_reg_mic_amp[] = {IDX_MIC1_GAIN, IDX_MIC2_GAIN}; // PWR is in located in the gain regs.
+	struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_mic_amp[idx]]);
+	MASK_SET_REG(reg->address,
+				 MICx_PWR_MASK,
+				 val << MICx_PWR_SHIFT);
+
+	printk(KERN_DEBUG "DORIANO: %s turned %s.\n", reg->name, val == 0 ? "off" : "on");
+}
+
+static void ab3550_toggle_DAC(int idx_DAC, u8 val)
 {
-	printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
-	set_up_speaker_playback();
-	return 0;
+	enum enum_register idx_reg_DAC[] = {IDX_RX1, IDX_RX2, IDX_RX3};
+	struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_DAC[idx_DAC]]);
+	MASK_SET_REG(reg->address,
+				 DACx_PWR_MASK | RXx_PWR_MASK,
+				 (val << RXx_PWR_SHIFT | val << DACx_PWR_SHIFT));
+
+	printk(KERN_DEBUG "DORIANO: DAC%d turned %s.\n", idx_DAC + 1, val == 0 ? "off" : "on");
+}
+
+static void	ab3550_toggle_output_adder(int idx_adder, u8 val)
+{
+	int i;
+	struct ab3550_register* reg = &(ab3550_registers[(int)outamp_adder_indices[idx_adder]]);
+
+	MASK_SET_REG(reg->address,
+				 DAC1_TO_ADDER_MASK | DAC2_TO_ADDER_MASK | DAC3_TO_ADDER_MASK,
+				 val);
+
+	for (i = 0; i < 3; i++)
+		printk(KERN_DEBUG "DORIANO: DAC%d -> %s: %s.\n", i + 1, reg->name, (val & 1 << i) ? "on" : "off");
+}
+
+static void ab3550_toggle_ADC(int idx_ADC, u8 val)
+{
+	enum enum_register idx_reg_ADC[] = {IDX_TX1, IDX_TX2};
+	struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_ADC[idx_ADC]]);
+	MASK_SET_REG(reg->address,
+				 ADCx_PWR_MASK | TXx_PWR_MASK,
+				 ( val << ADCx_PWR_SHIFT | val << TXx_PWR_SHIFT));
+
+	printk(KERN_DEBUG "DORIANO: ADC%d turned %s.\n", idx_ADC + 1, val == 0 ? "off" : "on");
+}
+
+static void ab3550_set_ADC_filter(int idx_ADC, u8 val)
+{
+	enum enum_register idx_reg_ADC[] = {IDX_TX1, IDX_TX2};
+	struct ab3550_register* reg = &(ab3550_registers[(int)idx_reg_ADC[idx_ADC]]);
+	MASK_SET_REG(reg->address,
+				 TXx_HP_FILTER_MASK,
+				 val << TXx_HP_FILTER_SHIFT);
+
+	printk(KERN_DEBUG "DORIANO: ADC%d filter set to %s.\n", idx_ADC + 1, enum_hp_filter[val]);
 }
 
 static int ab3550_add_widgets(struct snd_soc_codec *codec)
@@ -659,35 +552,150 @@
 	return 0;
 }
 
+
+
+static void ab3550_init_registers_startup(int ifSel)
+{
+	u8 val;
+	u8 iface = (ifSel == 0) ? INTERFACE0 : INTERFACE1;
+
+	// Configure CLOCK register
+	val = 1 << CLOCK_REF_SELECT_SHIFT |	// 32kHz RTC
+		  1 << CLOCK_ENABLE_SHIFT; // Enable clock
+	SET_REG(CLOCK, val);
+
+	// Enable Master Generator I2Sx
+	MASK_SET_REG(iface, MASTER_GENx_PWR_MASK | I2Sx_TRISTATE_MASK | I2Sx_PULLDOWN_MASK, 1 << MASTER_GENx_PWR_SHIFT);
+
+	// Configure INTERFACEx_DATA register
+	val = 1 << I2Sx_L_DATA_SHIFT |// TX1 to I2Sx_L
+		  1 << I2Sx_R_DATA_SHIFT; // TX1 to I2Sx_R
+	SET_REG(ifSel == 0 ? INTERFACE0_DATA : INTERFACE1_DATA, val);
+
+	GET_REG(CLOCK, &val);
+	printk(KERN_DEBUG "AB: CLOCK = 0x%02x\n", val);
+	GET_REG(iface, &val);
+	printk(KERN_DEBUG "AB: INTERFACE%d = 0x%02x\n", ifSel, val);
+	GET_REG(ifSel == 0 ? INTERFACE0_DATA : INTERFACE1_DATA, &val);
+	printk(KERN_DEBUG "AB: INTERFACE%d_DATA = 0x%02x\n", ifSel, val);
+}
+
+static void ab3550_init_registers_playback(int ifSel) {
+
+	int i, routing_left, routing_right, idx_RX_left, idx_RX_right;
+	u8 val;
+
+	// Get indices to active DACs
+	idx_RX_left = idx_RX_right = -1;
+	val = get_control_value_by_name("RX2 Select");
+	if (ifSel == 0) {
+		if (!(val & RX2_IF_SELECT_MASK))
+			idx_RX_right = 1;
+		idx_RX_left = 0;
+	} else {
+		if (val & RX2_IF_SELECT_MASK)
+			idx_RX_right = 1;
+		idx_RX_left = 2;
+	}
+printk(KERN_DEBUG "DORIANO: idx_RX_left = %d, idx_RX_right = %d\n", idx_RX_left, idx_RX_right);
+
+	// Toggle DACs
+	routing_left = ab3550_ctl[IDX_DAC1_Routing].value;
+printk(KERN_DEBUG "DORIANO: routing_left = %d\n", routing_left);
+	if (routing_left)
+		ab3550_toggle_DAC(idx_RX_left, 1);
+	routing_right = 0;
+	if (idx_RX_right > -1)
+	{
+		routing_right = ab3550_ctl[IDX_DAC2_Routing].value;
+printk(KERN_DEBUG "DORIANO: routing_right = %d\n", routing_right);
+		if (routing_right)
+			ab3550_toggle_DAC(idx_RX_right, 1);
+	}
+
+	// Toggle adders
+	for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) {
+		val = 0;
+		val |= (routing_left & 1 << i) ? 1 << idx_RX_left : 0;
+		if (idx_RX_right > -1)
+			val |= (routing_right & 1 << i) ? 1 << idx_RX_right : 0;
+		ab3550_toggle_output_adder(i, val);
+	}
+
+	/* TODO: DC cancellation */
+
+	// Toggle amplifiers
+	val = routing_left | routing_right;
+	for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) {
+printk(KERN_DEBUG "DORIANO: val & 1 << i = %d\n", val & 1 << i);
+		if (val & 1 << i)
+			ab3550_toggle_output_amp(i, 1);
+	}
+}
+
+static void ab3550_init_registers_capture(int ifSel)
+{
+	enum enum_control idx = (ifSel == 0) ? IDX_I2S0_Input_Select : IDX_I2S1_Input_Select;
+
+printk(KERN_DEBUG "DORIANO: ab3550_ctl[idx].value = 0x%02x\n", ab3550_ctl[idx].value);
+
+	// Toggle ADC (RXx) and amplifiers (MICx_GAIN)
+	if ((ab3550_ctl[idx].value & 0x0F) < 3) { // value = 3 => mute
+		int idx_ADC = (ab3550_ctl[idx].value & 0x0F) - 1; // value = 0xX1 => MIC1, value = 0xX2 => MIC2
+		ab3550_toggle_ADC(idx_ADC, 1);
+		ab3550_toggle_mic_amp(idx_ADC, 1);
+	}
+}
+
+
+
+// Commit ---------------------------------------------------------------------------------------------------------------
+
+static void printCommit(int idx) {
+	printk(KERN_DEBUG "%s: %s committed to 0x%02x.\n", __func__, ab3550_ctl[idx].name, ab3550_ctl[idx].value);
+}
+
+static bool commit_if_changed(enum enum_control idx_ctl)
+{
+	bool changed = false;
+
+	if (ab3550_ctl[idx_ctl].changed) {
+		MASK_SET_REG(ab3550_registers[ab3550_ctl[idx_ctl].reg_idx].address,
+					 ab3550_ctl[idx_ctl].reg_mask,
+					 ab3550_ctl[idx_ctl].value << ab3550_ctl[idx_ctl].reg_shift);
+		ab3550_ctl[idx_ctl].changed = false;
+		changed = true;
+
+		printCommit(idx_ctl);
+	}
+
+	return changed;
+}
+
 static int commit_outamps_routing(void)
 {
-	unsigned long idx;
-	int adjust = 0;
 	static const char *control_names[] = {
 		"DAC1 Routing", "DAC2 Routing", "DAC3 Routing",
 		"APGA1 Destination", "APGA2 Destination"
 	};
-	static const u8 outamp_adder_reg[] = {
-		LINE1_ADDER, LINE2_ADDER, SPKR_ADDER,
-		EAR_ADDER, AUXO1_ADDER, AUXO2_ADDER
-	};
+
 	static const u8 outamp_0db_gain[] = {
 		0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C
 	};
 
-	for_each_bit(idx, control_changed, ARRAY_SIZE(ab3550_ctl)) {
-		if (strcmp(ab3550_ctl[idx].name, "DAC1 Routing") == 0 ||
-		    strcmp(ab3550_ctl[idx].name, "DAC2 Routing") == 0 ||
-		    strcmp(ab3550_ctl[idx].name, "DAC3 Routing") == 0 ||
-		    strcmp(ab3550_ctl[idx].name, "APGA1 Destination") == 0 ||
-		    strcmp(ab3550_ctl[idx].name, "APGA2 Destination") == 0
-			) {
-			adjust = 1;
-		}
-	}
-	if (adjust) {
+	struct ab3550_register* reg;
+
+	printk(KERN_DEBUG "%s: Enter.\n", __func__);
+
+	if ((get_control_changed_by_name("DAC1 Routing")) ||
+		(get_control_changed_by_name("DAC2 Routing")) ||
+		(get_control_changed_by_name("DAC3 Routing")) ||
+		(get_control_changed_by_name("APGA1 Destination")) ||
+		(get_control_changed_by_name("APGA2 Destination"))) {
+
 		int i;
-		for (i = 0; i < ARRAY_SIZE(outamp_reg); i++) {
+
+		for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) {
 			unsigned int connect = 0;
 			int j;
 			for (j = 0; j < ARRAY_SIZE(control_names); j++) {
@@ -700,17 +708,19 @@
 			   nothing is to be disconnected to it */
 			if (!connect) {
 				u8 val = 0;
-				GET_REG(outamp_reg[i], &val);
+				reg = &(ab3550_registers[(int)outamp_indices[i]]);
+				GET_REG(reg->address, &val);
 				if (! (val & 1 << outamp_pwr_shift[i]))
 					continue;
-				gain_ramp(outamp_reg[i], 0,
+				gain_ramp(reg->address, 0,
 					  outamp_gain_shift[i],
 					  outamp_gain_mask[i]);
-				MASK_SET_REG(outamp_reg[i],
+				MASK_SET_REG(reg->address,
 					     outamp_pwr_mask[i], 0);
 				continue;
 			}
-			MASK_SET_REG(outamp_adder_reg[i],
+			reg = &(ab3550_registers[(int)outamp_adder_indices[i]]);
+			MASK_SET_REG(reg->address,
 				     DAC3_TO_ADDER_MASK |
 				     DAC2_TO_ADDER_MASK |
 				     DAC3_TO_ADDER_MASK,
@@ -718,88 +728,270 @@
 			if (connect & 1 << 3) {
 				/* Connect APGA1 */
 				MASK_SET_REG(APGA1_ADDER,
-					     1 << (ARRAY_SIZE(outamp_reg) - i),
-					     1 << (ARRAY_SIZE(outamp_reg) - i));
+					     1 << (ARRAY_SIZE(outamp_indices) - i),
+					     1 << (ARRAY_SIZE(outamp_indices) - i));
 			}
 			if (connect & 1 << 4) {
 				/* Connect APGA2 */
 				MASK_SET_REG(APGA2_ADDER,
-					     1 << (ARRAY_SIZE(outamp_reg) - i),
-					     1 << (ARRAY_SIZE(outamp_reg) - i));
+					     1 << (ARRAY_SIZE(outamp_indices) - i),
+					     1 << (ARRAY_SIZE(outamp_indices) - i));
 			}
-			MASK_SET_REG(outamp_reg[i],
+			reg = &(ab3550_registers[(int)outamp_indices[i]]);
+			MASK_SET_REG(reg->address,
 				     outamp_pwr_mask[i],
 				     1 << outamp_pwr_shift[i]);
-			gain_ramp(outamp_reg[i], outamp_0db_gain[i],
+			gain_ramp(reg->address, outamp_0db_gain[i],
 				  outamp_gain_shift[i],
 				  outamp_gain_mask[i]);
 		}
+
+		for (i = 0; i < ARRAY_SIZE(control_names); i++)
+			ab3550_ctl[get_control_index(control_names[i])].changed = 0;
 	}
 	return 0;
 }
 
-static int commit_gain(void)
+static int commit_rx_routing(void)
 {
-	unsigned long idx;
-	for_each_bit(idx, control_changed, ARRAY_SIZE(ab3550_ctl)) {
-		if (strcmp(ab3550_ctl[idx].name, "LINE1 Gain") == 0 ||
-		    strcmp(ab3550_ctl[idx].name, "LINE2 Gain") == 0 ||
-		    strcmp(ab3550_ctl[idx].name, "SPKR Gain") == 0 ||
-		    strcmp(ab3550_ctl[idx].name, "EAR Gain") == 0 ||
-		    strcmp(ab3550_ctl[idx].name, "AUXO1 Gain") == 0 ||
-		    strcmp(ab3550_ctl[idx].name, "AUXO2 Gain") == 0
-			) {
-			/* Ramping is needed if powered up */
-			int i = idx - get_control_index("LINE1 Gain");
-			u8 val = GET_REG(outamp_reg[i], &val);
-			if (val & 1 << outamp_pwr_shift[i]) {
-				gain_ramp(outamp_reg[i], ab3550_ctl[idx].value,
-					  outamp_gain_shift[i],
-					  outamp_gain_mask[i]);
-				continue;
-			}
-			/* Othwise set directly */
-			MASK_SET_REG(outamp_reg[i], outamp_gain_mask[i],
-				     ab3550_ctl[idx].value);
-		} else if (strcmp(ab3550_ctl[idx].name, "RX-DPGA1 Gain") == 0) {
-			gain_ramp(RX1_DIGITAL_PGA,
-				  get_control_value_by_name("RX-DPGA1 Gain"),
-				  RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+	int idx;
 
-		} else if (strcmp(ab3550_ctl[idx].name, "RX-DPGA2 Gain") == 0) {
-			gain_ramp(RX2_DIGITAL_PGA,
-				  get_control_value_by_name("RX-DPGA2 Gain"),
-				  RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+	printk(KERN_DEBUG "%s: Enter.\n", __func__);
 
-		} else if (strcmp(ab3550_ctl[idx].name, "RX-DPGA3 Gain") == 0) {
-			gain_ramp(RX3_DIGITAL_PGA,
-				  get_control_value_by_name("RX-DPGA3 Gain"),
-				  RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+	idx = get_control_index("RX2 Select");
+	if (ab3550_ctl[idx].changed) {
+		u8 val = ab3550_ctl[idx].value;
+		if (val & RXx_PWR_MASK)
+			MASK_SET_REG(RX2, RXx_PWR_MASK, 0);
+		MASK_SET_REG(RX2, RX2_IF_SELECT_MASK, ab3550_ctl[idx].value);
+		if (val & RXx_PWR_MASK)
+			MASK_SET_REG(RX2, RXx_PWR_MASK, 1 << RXx_PWR_SHIFT);
+		ab3550_ctl[idx].changed = false;
 
+		printCommit(idx);
+	}
+
+	commit_outamps_routing();
+
+	return 0;
+}
+
+static int commit_rx_gain(void)
+{
+	int i, idx_ctl;
+	//u8 val;
+	struct ab3550_register* reg;
+
+	printk(KERN_DEBUG "%s: Enter.\n", __func__);
+
+	for (i = 0; i < ARRAY_SIZE(outamp_gain_controls); i++) {
+		idx_ctl = (int)outamp_gain_controls[i];
+		reg = &(ab3550_registers[ab3550_ctl[idx_ctl].reg_idx]);
+		if (ab3550_ctl[idx_ctl].changed) {
+			//val = GET_REG(reg->address, &val);
+		//	if (ab3550_ctl[idx_ctl].value & 1 << outamp_pwr_shift[i]) // Ramping is needed if amp is powered up
+		//		gain_ramp(outamp_reg[i], ab3550_ctl[idx_ctl].value, outamp_gain_shift[i], outamp_gain_mask[i]);
+		//	else // Set directly if amp is not powered up
+			MASK_SET_REG(reg->address, outamp_gain_mask[i], ab3550_ctl[idx_ctl].value);
+			ab3550_ctl[idx_ctl].changed = false;
+
+			printCommit(idx_ctl);
 		}
 	}
+
+	idx_ctl = get_control_index("RX-DPGA1 Gain");
+	if (ab3550_ctl[idx_ctl].changed) {
+		gain_ramp(RX1_DIGITAL_PGA, ab3550_ctl[idx_ctl].value, RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+		ab3550_ctl[idx_ctl].changed = false;
+
+		printCommit(idx_ctl);
+
+	}
+
+	idx_ctl = get_control_index("RX-DPGA2 Gain");
+	if (ab3550_ctl[idx_ctl].changed) {
+		gain_ramp(RX2_DIGITAL_PGA, ab3550_ctl[idx_ctl].value, RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+		ab3550_ctl[idx_ctl].changed = false;
+
+		printCommit(idx_ctl);
+	}
+
+	idx_ctl = get_control_index("RX-DPGA3 Gain");
+	if (ab3550_ctl[idx_ctl].changed) {
+		gain_ramp(RX3_DIGITAL_PGA, ab3550_ctl[idx_ctl].value, RXx_PGA_GAIN_SHIFT, RXx_PGA_GAIN_MASK);
+		ab3550_ctl[idx_ctl].changed = false;
+
+		printCommit(idx_ctl);
+	}
+
 	return 0;
 }
 
+static int commit_tx_routing(void)
+{
+	int idx;
+
+	printk(KERN_DEBUG "%s: Enter.\n", __func__);
+
+	idx = get_control_index("MIC1 Input Select");
+	if (ab3550_ctl[idx].changed) {
+		MASK_SET_REG(MIC1_INPUT_SELECT,
+		             MICxP1_SEL_MASK | MICxN1_SEL_MASK | MICxP2_SEL_MASK | MICxN2_SEL_MASK | LINEIN_SEL_MASK,
+				     ab3550_ctl[idx].value);
+		MASK_SET_REG(MIC2_TO_MIC1,
+		             MIC2P1_MIC1P_SEL_MASK | MIC2N1_MIC1N_SEL_MASK,
+				     ab3550_ctl[idx].value >> 2);
+		ab3550_ctl[idx].changed = false;
+
+		printCommit(idx);
+	}
+
+	idx = get_control_index("MIC2 Input Select");
+	if (ab3550_ctl[idx].changed) {
+		MASK_SET_REG(MIC2_INPUT_SELECT,
+				     MICxP1_SEL_MASK | MICxN1_SEL_MASK | MICxP2_SEL_MASK | MICxN2_SEL_MASK | LINEIN_SEL_MASK,
+				     ab3550_ctl[idx].value);
+		ab3550_ctl[idx].changed = false;
+
+		printCommit(idx);
+	}
+
+	idx = get_control_index("I2S0 Input Select");
+	if (ab3550_ctl[idx].changed) {
+		MASK_SET_REG(INTERFACE0_DATA,
+					 I2Sx_L_DATA_MASK,
+				     ab3550_ctl[idx].value << I2Sx_L_DATA_SHIFT);
+		MASK_SET_REG(INTERFACE0_DATA,
+					 I2Sx_R_DATA_MASK,
+				     ab3550_ctl[idx].value << I2Sx_R_DATA_SHIFT);
+		ab3550_ctl[idx].changed = false;
+
+		printCommit(idx);
+	}
+
+	idx = get_control_index("I2S1 Input Select");
+	if (ab3550_ctl[idx].changed) {
+		MASK_SET_REG(INTERFACE1_DATA,
+					 I2Sx_L_DATA_MASK,
+					 ab3550_ctl[idx].value << I2Sx_L_DATA_SHIFT);
+		MASK_SET_REG(INTERFACE1_DATA,
+					 I2Sx_R_DATA_MASK,
+					 ab3550_ctl[idx].value << I2Sx_R_DATA_SHIFT);
+		ab3550_ctl[idx].changed = false;
+
+		printCommit(idx);
+	}
+
+	return 0;
+}
+
+static int commit_tx_gain(void)
+{
+	printk(KERN_DEBUG "%s: Enter.\n", __func__);
+
+	commit_if_changed(IDX_MIC1_Gain);
+	commit_if_changed(IDX_MIC2_Gain);
+	commit_if_changed(IDX_TX_DPGA1_Gain);
+	commit_if_changed(IDX_TX_DPGA2_Gain);
+
+	return 0;
+}
+
+static int commit_others(void)
+{
+	int idx;
+
+	printk(KERN_DEBUG "%s: Enter.\n", __func__);
+
+	idx = get_control_index("I2S0 Sample Rate");
+	if (ab3550_ctl[idx].changed) {
+		// TODO?
+		ab3550_ctl[idx].changed = false;
+	}
+
+	idx = get_control_index("I2S1 Sample Rate");
+	if (ab3550_ctl[idx].changed) {
+		// TODO?
+		ab3550_ctl[idx].changed = false;
+	}
+
+	idx = get_control_index("MIC1 MBias");
+	if (ab3550_ctl[idx].changed) {
+		MASK_SET_REG(MIC_BIAS1,
+					 MBIAS_PWR_MASK,
+					 ab3550_ctl[idx].value << MBIAS_PWR_SHIFT);
+		ab3550_ctl[idx].changed = false;
+	}
+
+	idx = get_control_index("MIC1 MBias");
+	if (ab3550_ctl[idx].changed) {
+		MASK_SET_REG(MIC_BIAS2,
+					 MBIAS_PWR_MASK,
+					 ab3550_ctl[idx].value << MBIAS_PWR_SHIFT);
+		ab3550_ctl[idx].changed = false;
+	}
+
+	idx = get_control_index("TX1 HP Filter");
+	if (ab3550_ctl[idx].changed) {
+		ab3550_set_ADC_filter(0, ab3550_ctl[idx].value);
+		ab3550_ctl[idx].changed = false;
+	}
+
+	idx = get_control_index("TX2 HP Filter");
+	if (ab3550_ctl[idx].changed) {
+		ab3550_set_ADC_filter(1, ab3550_ctl[idx].value);
+		ab3550_ctl[idx].changed = false;
+	}
+
+	idx = get_control_index("Voice Call");
+	if (ab3550_ctl[idx].changed) {
+		printk(KERN_DEBUG "%s: commiting 'Voice Call'.\n", __func__);
+		if (ab3550_ctl[idx].value) {
+			MASK_SET_REG(MIC_BIAS1,
+						 MBIAS_PWR_MASK,
+					     1 << MBIAS_PWR_SHIFT);
+			MASK_SET_REG(MIC_BIAS2,
+						 MBIAS_PWR_MASK,
+					     1 << MBIAS_PWR_SHIFT);
+	//turn_on_interface_tx(INTERFACE1);
+			MASK_SET_REG(INTERFACE1,
+						 MASTER_GENx_PWR_MASK,
+						 1 << MASTER_GENx_PWR_SHIFT);
+
+			printCommit(idx);
+		}
+		ab3550_ctl[idx].changed = false;
+	}
+
+	return 0;
+}
+
+void commit_all(void)
+{
+	commit_rx_routing();
+	commit_rx_gain();
+	commit_tx_routing();
+	commit_tx_gain();
+	commit_others();
+}
+
 static int ab3550_set_control_value(struct snd_soc_codec *codec,
 				    unsigned int ctl, unsigned int value)
 {
-	int ret = 0;
-
 	if (ctl >= ARRAY_SIZE(ab3550_ctl))
 		return -EINVAL;
-	if (ab3550_ctl[ctl].value == value)
-		return 0;
-	ab3550_ctl[ctl].value = value;
-	if (strcmp(ab3550_ctl[ctl].name, "Commit") != 0)
-		return 0;
-	/* Commit the changes */
-	commit_outamps_routing();
-	commit_gain();
-	memset(control_changed, 0, sizeof(control_changed));
 
+	if (strcmp(ab3550_ctl[ctl].name, "Commit") == 0) {
+		commit_all();
 
-	return ret;
+	} else if (ab3550_ctl[ctl].value != value) {
+		printk(KERN_DEBUG "%s: Ctl %d changed from 0x%02x to 0x%02x\n", __func__, ctl, ab3550_ctl[ctl].value, value);
+
+		ab3550_ctl[ctl].value = value;
+		ab3550_ctl[ctl].changed = true;
+	}
+
+	return 0;
 }
 
 static int ab3550_add_controls(struct snd_soc_codec *codec)
@@ -810,11 +1002,11 @@
 	for (i = 0; i < n; i++) {
 		/* Initialize the control indice */
 		if (! ab3550_ctl[i].is_enum) {
-			ab3550_snd_controls[i].private_value |= i;
+			((struct soc_mixer_control *)
+			 ab3550_snd_controls[i].private_value)->reg = i;
 		} else {
-			struct soc_enum *p = (struct soc_enum *)
-				ab3550_snd_controls[i].private_value;
-			p->reg = i;
+			((struct soc_enum *)
+			 ab3550_snd_controls[i].private_value)->reg = i;
 		}
 		printk(KERN_DEBUG "%s: %s.reg = %d\n", __func__,
 		       ab3550_ctl[i].name, i);
@@ -831,6 +1023,232 @@
 	return err;
 }
 
+
+
+// snd_soc_dai ---------------------------------------------------------------------------------------------------------
+
+static int ab3550_pcm_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai* dai)
+{
+	struct codec_dai_private *priv = (struct codec_dai_private*)dai->private_data;
+
+	printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
+	/* TODO: consult the clock use count */
+
+
+	// Configure registers on startup
+	if (atomic_read(&priv->substream_count) == 0)
+		ab3550_init_registers_startup(dai->id);
+
+	atomic_inc(&priv->substream_count);
+
+	// Configure registers for either playback or capture
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ab3550_init_registers_playback(dai->id);
+	else
+		ab3550_init_registers_capture(dai->id);
+
+	ab3550_print_registers(substream->stream);
+
+	return 0;
+}
+
+static int ab3550_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *hw_params,
+				struct snd_soc_dai* dai)
+{
+
+  u8 val;
+	const char *reg_name;
+
+  printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__);
+
+  reg_name = dai->id == 0 ? "I2S0 Sample Rate" : "I2S1 Sample Rate";
+
+  switch(params_rate(hw_params))
+  {
+    case 8000:
+      val=0;
+      break;
+
+    case 16000:
+      val=1;
+      break;
+
+    case 44100:
+      val=2;
+      break;
+
+    case 48000:
+      val=3;
+      break;
+
+    default:
+      return -EINVAL;
+  }
+
+  ab3550_ctl[get_control_index(reg_name)].value = val;
+
+  MASK_SET_REG(dai->id == 0 ? INTERFACE0 : INTERFACE1,
+						   I2Sx_SR_MASK,
+						   val);
+
+  return 0;
+}
+
+static void ab3550_pcm_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai* dai)
+{
+	int i;
+	u8 iface;
+	const char *mode_ctl_name;
+	printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
+	if (dai->id == 0) {
+		iface = INTERFACE0;
+		mode_ctl_name = "I2S0 Mode";
+	} else {
+		iface = INTERFACE1;
+		mode_ctl_name = "I2S1 Mode";
+	}
+	if (get_control_value_by_name(mode_ctl_name) == 0) {
+		struct codec_dai_private *priv =
+			(struct codec_dai_private *)dai->private_data;
+		if (atomic_dec_and_test(&priv->substream_count))
+			MASK_SET_REG(iface, MASTER_GENx_PWR_MASK, 0);
+	}
+	/*TODO: consult the clock use count */
+/* 	MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 0); */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		struct ab3550_register* reg;
+		/* TODO: Turn off the outamps only used by this I2S */
+		for (i = 0; i < ARRAY_SIZE(outamp_indices); i++) {
+			reg = &(ab3550_registers[(int)outamp_indices[i]]);
+			MASK_SET_REG(reg->address, outamp_pwr_mask[i], 0);
+		}
+	} else {
+		/* TODO: Turn off the inactive components */
+	}
+}
+
+static int ab3550_set_dai_sysclk(struct snd_soc_dai* dai, int clk_id,
+				unsigned int freq, int dir)
+{
+	/*
+  u8 val;
+	const char *reg_name;
+	printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__);
+	switch(freq) {
+	case 8000:
+		val = 0;
+		break;
+	case 16000:
+		val = 1;
+		break;
+	case 44100:
+		val = 2;
+		break;
+	case 48000:
+		val = 3;
+		break;
+	default:
+		printk(KERN_ERR "%s: invalid frequency %u.\n",
+		       __func__, freq);
+		return -EINVAL;
+	}
+
+	reg_name = dai->id == 0 ? "I2S0 Sample Rate" : "I2S1 Sample Rate";
+	ab3550_ctl[get_control_index(reg_name)].value = val;
+	return MASK_SET_REG(dai->id == 0 ? INTERFACE0 : INTERFACE1,
+						I2Sx_SR_MASK,
+						val << I2Sx_SR_SHIFT);
+  */
+  return 0;
+}
+
+static int ab3550_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+  u8 iface = (codec_dai->id == 0) ? INTERFACE0 : INTERFACE1;
+  u8 val=0;
+  printk(KERN_DEBUG "%s: %s called\n", __FILE__, __func__);
+
+  switch (fmt & (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+
+	  case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+	    val |= 1 << I2Sx_MODE_SHIFT;
+      break;
+
+    case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+      break;
+
+	  default:
+		  printk(KERN_WARNING "AB3550_dai: unsupported DAI format 0x%x\n",fmt);
+		  return -EINVAL;
+	}
+
+  return MASK_SET_REG(iface,I2Sx_MODE_MASK | I2Sx_WORDLENGTH_MASK, val);
+}
+
+struct snd_soc_dai ab3550_codec_dai[] = {
+	{
+		.name = "ab3550_0",
+		.id = 0,
+		.playback = {
+			.stream_name = "ab3550_0",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = AB3550_SUPPORTED_RATE,
+			.formats = AB3550_SUPPORTED_FMT,
+		},
+		.capture = {
+			.stream_name = "ab3550_0",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = AB3550_SUPPORTED_RATE,
+			.formats = AB3550_SUPPORTED_FMT,
+		},
+		.ops = {
+			.prepare = ab3550_pcm_prepare,
+			.hw_params = ab3550_pcm_hw_params,
+			.shutdown = ab3550_pcm_shutdown,
+			.set_sysclk = ab3550_set_dai_sysclk,
+			.set_fmt = ab3550_set_dai_fmt
+		},
+		.private_data = &privates[0]
+	},
+	{
+		.name = "ab3550_1",
+		.id = 1,
+		.playback = {
+			.stream_name = "ab3550_1",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = AB3550_SUPPORTED_RATE,
+			.formats = AB3550_SUPPORTED_FMT,
+		},
+		.capture = {
+			.stream_name = "ab3550_1",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = AB3550_SUPPORTED_RATE,
+			.formats = AB3550_SUPPORTED_FMT,
+		},
+		.ops = {
+			.prepare = ab3550_pcm_prepare,
+			.hw_params = ab3550_pcm_hw_params,
+			.shutdown = ab3550_pcm_shutdown,
+			.set_sysclk = ab3550_set_dai_sysclk,
+			.set_fmt = ab3550_set_dai_fmt
+		},
+		.private_data = &privates[1]
+	}
+};
+EXPORT_SYMBOL_GPL(ab3550_codec_dai);
+
+
+
+
+
+// snd_soc_codec_device ---------------------------------------------------------------------------------------------------
+
 static int ab3550_codec_probe(struct platform_device *pdev)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
@@ -843,7 +1261,6 @@
 		return -ENOMEM;
 	codec->name = "AB3550";
 	codec->owner = THIS_MODULE;
-	/* TODO: add codec dai */
 	codec->dai = ab3550_codec_dai;
 	codec->num_dai = 2;
 	codec->read = ab3550_get_control_value;
@@ -911,38 +1328,31 @@
 
 EXPORT_SYMBOL_GPL(soc_codec_dev_ab3550);
 
-/* struct snd_soc_dai ab3550_codec_dai = { */
-/* 	.name = "AB3550", */
-/* 	.playback = { */
-/* 		.stream_name = "Playback", */
-/* 		.channels_min = 1, */
-/* 		.channels_max = 2, */
-/* 		.rates = AB3550_RATES, */
-/* 		.formats = AB3550_FORMATS,}, */
-/* 	.capture = { */
-/* 		.stream_name = "Capture", */
-/* 		.channels_min = 1, */
-/* 		.channels_max = 2, */
-/* 		.rates = AB3550_RATES, */
-/* 		.formats = AB3550_FORMATS,}, */
-/* 	.ops = { */
-/* 		.prepare = ab3550_pcm_prepare, */
-/* 		.hw_params = ab3550_hw_params, */
-/* 		.shutdown = ab3550_shutdown, */
-/* 	}, */
-/* 	.dai_ops = { */
-/* 		.digital_mute = ab3550_mute, */
-/* 		.set_sysclk = ab3550_set_dai_sysclk, */
-/* 		.set_fmt = ab3550_set_dai_fmt, */
-/* 	} */
-/* }; */
-/* EXPORT_SYMBOL_GPL(ab3550_codec_dai); */
 static int ab3550_platform_probe(struct platform_device *pdev)
 {
 	int ret = 0;
-
+	u8 reg, val;
+	unsigned long i;
 	printk(KERN_DEBUG "%s invoked with pdev = %p.\n", __func__, pdev);
 	ab3550_dev = &pdev->dev;
+
+	/* Initialize the codec registers */
+	for (reg = MIC_BIAS1; reg < INTERFACE_SWAP; reg++) {
+		SET_REG(reg, 0);
+	}
+	MASK_SET_REG(CLOCK, CLOCK_REF_SELECT_MASK,
+		     1 << CLOCK_REF_SELECT_SHIFT);
+	MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK,
+		     1 << CLOCK_ENABLE_SHIFT);
+	val = GET_REG(CLOCK, &val);
+	printk(KERN_DEBUG "%s: CLOCK = 0x%02x.\n", __func__, val);
+
+	// Init all registers from default control values
+	for (i = 0; i < ARRAY_SIZE(ab3550_ctl); i++)
+		if (ab3550_ctl[i].value)
+			ab3550_ctl[i].changed = true;
+	commit_all();
+
 	return ret;
 }
 
@@ -950,6 +1360,7 @@
 {
 	int ret;
 	printk(KERN_DEBUG "%s called.\n", __func__);
+	MASK_SET_REG(CLOCK, CLOCK_ENABLE_MASK, 0);
 	if ((ret = soc_codec_dev_ab3550.remove(NULL)))
 		printk(KERN_ERR "%s failed with error code %d.\n",
 		       __func__, ret);
@@ -991,18 +1402,47 @@
 
 static int __devinit ab3550_init(void)
 {
-	int ret;
+	int i, ret1;
+	int ret2 = 0;
+
 	printk(KERN_DEBUG "%s called.\n", __func__);
-	ret= platform_driver_register(&ab3550_platform_driver);
-	printk(KERN_DEBUG "%s finished with error code %d.\n",
-	       __func__, ret);
-	return ret;
+
+	// Register codec platform driver.
+	ret1 = platform_driver_register(&ab3550_platform_driver);
+	if (ret1 < 0) {
+		printk(KERN_DEBUG "%s: Error %d: Failed to register codec platform driver.\n", __func__, ret1);
+		ret2 = -1;
+	}
+
+	// Register codec-dai.
+	printk(KERN_DEBUG "%s: Register codec-dai.\n", __func__);
+
+	for (i = 0; i < ARRAY_SIZE(ab3550_codec_dai); i++) {
+		printk(KERN_DEBUG "%s: Register codec-dai %d.\n", __func__, i);
+		ret1 = snd_soc_register_dai(&ab3550_codec_dai[i]);
+		if (ret1 < 0) {
+		  printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to register codec-dai %d.\n", i);
+		  ret2 = -1;
+		}
+	}
+
+	return ret2;
 }
 
 static void ab3550_exit(void)
 {
-	printk(KERN_DEBUG "%s called.\n", __func__);
+	int i;
+
+	printk(KERN_DEBUG "u8500_ab3550_init: Enter.\n");
+
+	// Register codec platform driver.
+	printk(KERN_DEBUG "%s: Un-register codec platform driver.\n", __func__);
 	platform_driver_unregister(&ab3550_platform_driver);
+
+	for (i = 0; i < ARRAY_SIZE(ab3550_codec_dai); i++) {
+		printk(KERN_DEBUG "%s: Un-register codec-dai %d.\n", __func__, i);
+    snd_soc_unregister_dai(&ab3550_codec_dai[i]);
+	}
 }
 
 module_init(ab3550_init);
diff --git a/sound/soc/codecs/ab3550.h b/sound/soc/codecs/ab3550.h
index 943d9e4..00c2fc4 100755
--- a/sound/soc/codecs/ab3550.h
+++ b/sound/soc/codecs/ab3550.h
@@ -13,6 +13,15 @@
 #ifndef AB3550_CODEC_REGISTERS_H
 #define AB3550_CODEC_REGISTERS_H
 
+extern struct snd_soc_dai ab3550_codec_dai[2];
+extern struct snd_soc_codec_device soc_codec_dev_ab3550;
+
+#define AB3550_SUPPORTED_RATE (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+			       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define AB3550_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE | \
+			      SNDRV_PCM_FMTBIT_S24_LE)
+
 /* MIC BIAS */
 #define MIC_BIAS1 0X31
 #define MIC_BIAS2 0X32
@@ -150,7 +159,7 @@
 #define APGA1_ADDER 0x46
 #define APGA2_ADDER 0x47
 #define APGAx_TO_LINE1_MASK 0x20
-#define APGAx_TO_LINE1_SHIFT 5
+#define APGAx_TO_LINE1_SHIFT 5F
 #define APGAx_TO_LINE2_MASK 0x10
 #define APGAx_TO_LINE2_SHIFT 4
 #define APGAx_TO_SPKR_MASK 0x08
@@ -201,30 +210,16 @@
 #define ADCx_PWR_SHIFT 0
 
 #define RX1 0x53
-#define RX1_PWR_MASK 0x08
-#define RX1_PWR_SHIFT 3
-#define DAC1_PWR_MASK 0x04
-#define DAC1_PWR_SHIFT 2
-#define DAC1_PWR_MODE_MASK 0x03
-#define DAC1_PWR_MODE_SHIFT 0
-
 #define RX2 0x54
 #define RX2_IF_SELECT_MASK 0x10
 #define RX2_IF_SELECT_SHIFT 4
-#define RX2_PWR_MASK 0x08
-#define RX2_PWR_SHIFT 3
-#define DAC2_PWR_MASK 0x04
-#define DAC2_PWR_SHIFT 2
-#define DAC2_PWR_MODE_MASK 0x03
-#define DAC2_PWR_MODE_SHIFT 0
-
 #define RX3 0x55
-#define RX3_PWR_MASK 0x08
-#define RX3_PWR_SHIFT 3
-#define DAC3_PWR_MASK 0x04
-#define DAC3_PWR_SHIFT 2
-#define DAC3_PWR_MODE_MASK 0x03
-#define DAC3_PWR_MODE_SHIFT 0
+#define RXx_PWR_MASK 0x08
+#define RXx_PWR_SHIFT 3
+#define DACx_PWR_MASK 0x04
+#define DACx_PWR_SHIFT 2
+#define DACx_PWR_MODE_MASK 0x03
+#define DACx_PWR_MODE_SHIFT 0
 
 #define TX_DIGITAL_PGA1 0X56
 #define TX_DIGITAL_PGA2 0X57
@@ -300,4 +295,145 @@
 #define IO_SWAP1_MASK 0x01
 #define IO_SWAP1_SHIFT 0
 
+enum enum_register {
+	IDX_UNKNOWN = -1,
+	IDX_MIC_BIAS1 = 0,
+	IDX_MIC_BIAS2 = 1,
+	IDX_MIC_BIAS2_VAD = 2,
+	IDX_MIC1_GAIN = 3,
+	IDX_MIC2_GAIN = 4,
+	IDX_MIC1_INPUT_SELECT = 5,
+	IDX_MIC2_INPUT_SELECT = 6,
+	IDX_MIC1_VMID_SELECT = 7,
+	IDX_MIC2_VMID_SELECT = 8,
+	IDX_MIC2_TO_MIC1 = 9,
+	IDX_ANALOG_LOOP_PGA1 = 10,
+	IDX_ANALOG_LOOP_PGA2 = 11,
+	IDX_APGA_VMID_SELECT = 12,
+	IDX_EAR = 13,
+	IDX_AUXO1 = 14,
+	IDX_AUXO2 = 15,
+	IDX_AUXO_PWR_MODE = 16,
+	IDX_OFFSET_CANCEL = 17,
+	IDX_SPKR = 18,
+	IDX_LINE1 = 19,
+	IDX_LINE2 = 20,
+	IDX_APGA1_ADDER = 21,
+	IDX_APGA2_ADDER = 22,
+	IDX_EAR_ADDER = 23,
+	IDX_AUXO1_ADDER = 24,
+	IDX_AUXO2_ADDER = 25,
+	IDX_SPKR_ADDER = 26,
+	IDX_LINE1_ADDER = 27,
+	IDX_LINE2_ADDER = 28,
+	IDX_EAR_TO_MIC2 = 29,
+	IDX_SPKR_TO_MIC2 = 30,
+	IDX_NEGATIVE_CHARGE_PUMP = 31,
+	IDX_TX1 = 32,
+	IDX_TX2 = 33,
+	IDX_RX1 = 34,
+	IDX_RX2 = 35,
+	IDX_RX3 = 36,
+	IDX_TX_DIGITAL_PGA1 = 37,
+	IDX_TX_DIGITAL_PGA2 = 38,
+	IDX_RX1_DIGITAL_PGA = 39,
+	IDX_RX2_DIGITAL_PGA = 40,
+	IDX_RX3_DIGITAL_PGA = 41,
+	IDX_SIDETONE1_PGA = 42,
+	IDX_SIDETONE2_PGA = 43,
+	IDX_CLOCK = 44,
+	IDX_INTERFACE0 = 45,
+	IDX_INTERFACE1 = 46,
+	IDX_INTERFACE0_DATA = 47,
+	IDX_INTERFACE1_DATA = 48,
+	IDX_INTERFACE_LOOP = 49,
+	IDX_INTERFACE_SWAP = 50
+};
+
+enum enum_control {
+	IDX_RX2_Select = 0,
+	IDX_DAC1_Routing,
+	IDX_DAC2_Routing,
+	IDX_DAC3_Routing,
+	IDX_MIC1_Input_Select,
+	IDX_MIC2_Input_Select,
+	IDX_I2S0_Input_Select,
+	IDX_I2S1_Input_Select,
+	IDX_APGA1_Source,
+	IDX_APGA2_Source,
+	IDX_APGA1_Destination,
+	IDX_APGA2_Destination,
+	IDX_DAC1_Side_Tone,
+	IDX_DAC2_Side_Tone,
+	IDX_RX_DPGA1_Gain,
+	IDX_RX_DPGA2_Gain,
+	IDX_RX_DPGA3_Gain,
+	IDX_LINE1_Gain,
+	IDX_LINE2_Gain,
+	IDX_SPKR_Gain,
+	IDX_EAR_Gain,
+	IDX_AUXO1_Gain,
+	IDX_AUXO2_Gain,
+	IDX_MIC1_Gain,
+	IDX_MIC2_Gain,
+	IDX_TX_DPGA1_Gain,
+	IDX_TX_DPGA2_Gain,
+	IDX_ST_PGA1_Gain,
+	IDX_ST_PGA2_Gain,
+	IDX_APGA1_Gain,
+	IDX_APGA2_Gain,
+	IDX_DAC1_Power_Mode,
+	IDX_DAC2_Power_Mode,
+	IDX_DAC3_Power_Mode,
+	IDX_EAR_Power_Mode,
+	IDX_AUXO_Power_Mode,
+	IDX_LINE1_Inverse,
+	IDX_LINE2_Inverse,
+	IDX_AUXO1_Inverse,
+	IDX_AUXO2_Inverse,
+	IDX_AUXO1_Pulldown,
+	IDX_AUXO2_Pulldown,
+	IDX_VMID1,
+	IDX_VMID2,
+	IDX_MIC1_MBias,
+	IDX_MIC2_MBias,
+	IDX_MBIAS1_HiZ_Option,
+	IDX_MBIAS2_HiZ_Option,
+	IDX_MBIAS2_Output_Voltage,
+	IDX_MBIAS2_Internal_Resistor,
+	IDX_MIC1_Input_Impedance,
+	IDX_MIC2_Input_Impedance,
+	IDX_TX1_HP_Filter,
+	IDX_TX2_HP_Filter,
+	IDX_LINEIN1_Pre_charge,
+	IDX_LINEIN2_Pre_charge,
+	IDX_MIC1P1_Pre_charge,
+	IDX_MIC1P2_Pre_charge,
+	IDX_MIC1N1_Pre_charge,
+	IDX_MIC1N2_Pre_charge,
+	IDX_MIC2P1_Pre_charge,
+	IDX_MIC2P2_Pre_charge,
+	IDX_MIC2N1_Pre_charge,
+	IDX_MIC2N2_Pre_charge,
+	IDX_ST1_HP_Filter,
+	IDX_ST2_HP_Filter,
+	IDX_I2S0_Word_Length,
+	IDX_I2S1_Word_Length,
+	IDX_I2S0_Mode,
+	IDX_I2S1_Mode,
+	IDX_I2S0_Tri_state,
+	IDX_I2S1_Tri_state,
+	IDX_I2S0_Pulldown,
+	IDX_I2S1_Pulldown,
+	IDX_I2S0_Sample_Rate,
+	IDX_I2S1_Sample_Rate,
+	IDX_Interface_Loop,
+	IDX_Interface_Swap,
+	IDX_Voice_Call,
+	IDX_Commit
+};
+
 #endif /* AB3550_CODEC_REGISTERS_H */
+
+
+
diff --git a/sound/soc/codecs/cg29xx.c b/sound/soc/codecs/cg29xx.c
new file mode 100755
index 0000000..eab26b4
--- /dev/null
+++ b/sound/soc/codecs/cg29xx.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/bitops.h>
+#include "cg29xx.h"
+
+
+#define CG29XX_SUPPORTED_RATE (SNDRV_PCM_RATE_8000 | \
+	SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define CG29XX_SUPPORTED_FMT (SNDRV_PCM_FMTBIT_S16_LE | \
+			      SNDRV_PCM_FMTBIT_S24_LE)
+
+static int cg29xx_pcm_prepare(
+	struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai);
+
+static int cg29xx_pcm_hw_params(
+	struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *hw_params,
+	struct snd_soc_dai *dai);
+
+static void cg29xx_pcm_shutdown(
+	struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai);
+
+static int cg29xx_set_dai_sysclk(
+	struct snd_soc_dai *codec_dai,
+	int clk_id,
+	unsigned int freq, int dir);
+
+static int cg29xx_set_dai_fmt(
+	struct snd_soc_dai *codec_dai,
+	unsigned int fmt);
+
+struct snd_soc_dai cg29xx_codec_dai[] = {
+	{
+		.name = "nullcodec_0",
+		.playback = {
+			.stream_name = "nullcodec_0",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = CG29XX_SUPPORTED_RATE,
+			.formats = CG29XX_SUPPORTED_FMT,
+		},
+		.capture = {
+			.stream_name = "nullcodec_0",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = CG29XX_SUPPORTED_RATE,
+			.formats = CG29XX_SUPPORTED_FMT,
+		},
+		.ops = {
+			.prepare = cg29xx_pcm_prepare,
+			.hw_params = cg29xx_pcm_hw_params,
+			.shutdown = cg29xx_pcm_shutdown,
+			.set_sysclk = cg29xx_set_dai_sysclk,
+			.set_fmt = cg29xx_set_dai_fmt
+		},
+	}
+};
+EXPORT_SYMBOL_GPL(cg29xx_codec_dai);
+
+static int cg29xx_set_dai_sysclk(
+	struct snd_soc_dai *codec_dai,
+	int clk_id,
+	unsigned int freq, int dir)
+{
+	printk(
+		KERN_DEBUG
+		"%s: %s called\n",
+		__FILE__,
+		__func__);
+	return 0;
+}
+
+static int cg29xx_set_dai_fmt(
+	struct snd_soc_dai *codec_dai,
+	unsigned int fmt)
+{
+	printk(
+		KERN_DEBUG
+		"%s: %s called.\n",
+		__FILE__,
+		__func__);
+	return 0;
+}
+
+static int cg29xx_pcm_prepare(
+	struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	printk(KERN_DEBUG "%s: %s called.\n",
+		__FILE__,
+		__func__);
+	return 0;
+}
+
+static void cg29xx_pcm_shutdown(
+	struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	printk(KERN_DEBUG "%s: %s called.\n",
+		__FILE__,
+		__func__);
+}
+
+static int cg29xx_pcm_hw_params(
+	struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *hw_params,
+	struct snd_soc_dai *dai)
+{
+	printk(KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
+	return 0;
+}
+
+static unsigned int nullcodec_codec_read(
+	struct snd_soc_codec *codec,
+	unsigned int ctl)
+{
+	printk(
+		KERN_DEBUG
+		"%s: %s called.\n",
+		__FILE__,
+		__func__);
+	return 0;
+}
+
+static int nullcodec_codec_write(
+	struct snd_soc_codec *codec,
+	unsigned int ctl,
+	unsigned int value)
+{
+	printk(
+		KERN_DEBUG "%s: %s called.\n", __FILE__, __func__);
+	return 0;
+}
+
+static int nullcodec_codec_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret;
+
+	printk(KERN_DEBUG "%s called. pdev = %p.\n", __func__, pdev);
+
+	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (codec == NULL)
+		return -ENOMEM;
+	codec->name = "NullCodec";
+	codec->owner = THIS_MODULE;
+	codec->dai = &cg29xx_codec_dai[0];
+	codec->num_dai = 1;
+	codec->read = nullcodec_codec_read;
+	codec->write = nullcodec_codec_write;
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+
+	mutex_init(&codec->mutex);
+
+	socdev->codec = codec;
+
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		printk(
+			KERN_ERR
+			"%s: Error: to create new PCMs. error %d\n",
+			__func__,
+			ret);
+		goto err1;
+	}
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		printk(
+			KERN_ERR
+			"%s: Error:"
+			" Failed to register card.  error %d.\n",
+			__func__,
+			ret);
+		goto err2;
+	}
+
+	return 0;
+
+err2:
+	snd_soc_free_pcms(socdev);
+err1:
+	kfree(codec);
+	return ret;
+}
+
+static int nullcodec_codec_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->codec;
+
+	if (!codec)
+		return 0;
+
+	snd_soc_free_pcms(socdev);
+	kfree(socdev->codec);
+
+	printk(KERN_DEBUG "%s : pdev=%p.\n", __func__, pdev);
+	return 0;
+}
+
+static int nullcodec_codec_suspend(struct platform_device *pdev,
+				pm_message_t state)
+{
+	printk(KERN_DEBUG "%s : pdev=%p.\n", __func__, pdev);
+	return 0;
+}
+
+static int nullcodec_codec_resume(struct platform_device *pdev)
+{
+	printk(KERN_DEBUG "%s : pdev=%p.\n", __func__, pdev);
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_cg29xx = {
+	.probe =	nullcodec_codec_probe,
+	.remove =	nullcodec_codec_remove,
+	.suspend =	nullcodec_codec_suspend,
+	.resume =	nullcodec_codec_resume
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_cg29xx);
+
+static int __devinit cg29xx_init(void)
+{
+	int ret1;
+	int ret2 = 0;
+
+	printk(
+		KERN_DEBUG
+		"%s called.\n",
+		__func__);
+
+	printk(
+		KERN_DEBUG
+		"%s: Register codec-dai.\n",
+		__func__);
+	ret1 = snd_soc_register_dai(&cg29xx_codec_dai[0]);
+	if (ret1 < 0) {
+		printk(
+			KERN_DEBUG
+			"%s: Error %d: Failed to register codec-dai.\n",
+			__func__,
+			ret1);
+		ret2 = -1;
+	}
+
+	return ret2;
+}
+
+static void cg29xx_exit(void)
+{
+	printk(KERN_DEBUG "%s called.\n", __func__);
+
+	printk(
+		KERN_DEBUG
+		"%s: Un-register codec-dai.\n",
+		__func__);
+	snd_soc_unregister_dai(&cg29xx_codec_dai[0]);
+}
+
+module_init(cg29xx_init);
+module_exit(cg29xx_exit);
+
+MODULE_DESCRIPTION("CG29xx driver");
+MODULE_AUTHOR("www.stericsson.com");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cg29xx.h b/sound/soc/codecs/cg29xx.h
new file mode 100755
index 0000000..d515cd1
--- /dev/null
+++ b/sound/soc/codecs/cg29xx.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Roger Nilsson 	roger.xr.nilsson@stericsson.com
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 CG29XX_CODEC_H
+#define CG29XX_CODEC_H
+
+extern struct snd_soc_dai cg29xx_codec_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_cg29xx;
+
+#endif /* CG29XX_CODEC_H */
+
+
+
diff --git a/sound/soc/u8500/Kconfig b/sound/soc/u8500/Kconfig
deleted file mode 100755
index 2e008a7..0000000
--- a/sound/soc/u8500/Kconfig
+++ /dev/null
@@ -1,13 +0,0 @@
-#
-# U8500 SoC audio configuration
-#
-
-config SND_SOC_U8500
-	bool "U8500"
-	depends on SND_SOC
-	default y
-	help
-	  Support for sound devices specific to ARM architectures.
-	  Drivers that are implemented on ASoC can be found in
-	  "ALSA for SoC audio support" section.
-
diff --git a/sound/soc/u8500/Makefile b/sound/soc/u8500/Makefile
deleted file mode 100755
index 438c58f..0000000
--- a/sound/soc/u8500/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-# U8500 Platform Support
-
-snd-soc-u8500-objs := u8500_pcm.o u8500_msp_dai.o
-snd-soc-u8500-ab3550-objs := mop500_ab3550.o
-
-obj-$(CONFIG_SND_SOC_U8500) += snd-soc-u8500.o
-obj-$(CONFIG_SND_SOC_AB3550) += snd-soc-u8500-ab3550.o
-
-
-
-
-
diff --git a/sound/soc/u8500/mop500_ab3550.c b/sound/soc/u8500/mop500_ab3550.c
deleted file mode 100755
index 743206a..0000000
--- a/sound/soc/u8500/mop500_ab3550.c
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Ola Lilja ola.o.lilja@stericsson.com,
- *         Roger Nilsson 	roger.xr.nilsson@stericsson.com
- *         for ST-Ericsson.
- *
- * License terms:
- *
- * 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/device.h>
-#include <linux/io.h>
-
-// SOC framework
-#include <sound/soc.h>
-
-// Platform driver
-#include "u8500_pcm.h"
-#include "u8500_msp_dai.h"
-
-// AB3550 codec
-extern struct snd_soc_dai ab3550_codec_dai;
-extern struct snd_soc_codec_device soc_codec_dev_ab3550;
-
-
-
-
-// Create the snd_soc_dai_link struct ---------------------------------------
-
-static int u8500_ab3550_startup(struct snd_pcm_substream *substream)
-{
-  printk(KERN_DEBUG "MOP500_AB3550: u8500_ab3550_startup\n");
-
-  return 0;
-}
-
-static void u8500_ab3550_shutdown(struct snd_pcm_substream *substream)
-{
-  printk(KERN_DEBUG "MOP500_AB3550: u8500_ab3550_shutdown\n");
-}
-
-static int u8500_ab3550_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params)
-{
-	printk(KERN_DEBUG "MOP500_AB3550: u8500_ab3550_hw_params\n");
-
-	return 0;
-}
-
-static struct snd_soc_ops u8500_ab3550_ops = {
-  .startup = u8500_ab3550_startup,
-  .shutdown = u8500_ab3550_shutdown,
-  .hw_params = u8500_ab3550_hw_params,
-};
-
-struct snd_soc_dai_link u8500_ab3550_dai[2] = {
-  {
-    .name = "ab3550_0",
-    .stream_name = "ab3550_0",
-    .cpu_dai = &u8500_msp_dai[0],
-    .codec_dai = &ab3550_codec_dai,
-    .init = NULL,
-    .ops = &u8500_ab3550_ops,
-  },
-  {
-    .name = "ab3550_1",
-    .stream_name = "ab3550_1",
-    .cpu_dai = &u8500_msp_dai[1],
-    .codec_dai = &ab3550_codec_dai,
-    .init = NULL,
-    .ops = &u8500_ab3550_ops,
-  },
-};
-EXPORT_SYMBOL(u8500_ab3550_dai);
-
-
-
-// Create the snd_soc_device struct ---------------------------------------
-
-static struct snd_soc_card u8500_ab3550 = {
-  .name = "u8500-ab3550",
-  .probe = NULL,
-  .dai_link = u8500_ab3550_dai,
-  .num_links = 2,
-  .platform = &u8500_soc_platform,
-};
-
-struct snd_soc_device u8500_ab3550_snd_devdata = {
-  .card = &u8500_ab3550,
-  .codec_dev = &soc_codec_dev_ab3550,
-};
-EXPORT_SYMBOL(u8500_ab3550_snd_devdata);
-
-
-
-// Machine driver init and exit --------------------------------------------
-
-static struct platform_device *u8500_ab3550_snd_device;
-
-static int __init u8500_ab3550_init(void)
-{
-  int ret, i;
-  struct snd_soc_device *socdev;
-  struct snd_soc_codec *codec;
-
-  printk(KERN_DEBUG "MOP500_AB3550: u8500_ab3550_init\n");
-
-  // Register platform
-  printk(KERN_DEBUG "MOP500_AB3550: Register platform.\n");
-  ret = snd_soc_register_platform(&u8500_soc_platform);
-  if (ret < 0)
-	  printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to register platform.\n");
-
-  printk(KERN_DEBUG "MOP500_AB3550: Register codec dai.\n");
-  snd_soc_register_dai(&ab3550_codec_dai);
-  if (ret < 0)
-	  printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to register codec dai.\n");
-
-  for (i = 0; i < U8500_NBR_OF_DAI; i++) {
-	printk(KERN_DEBUG "MOP500_AB3550: Register MSP dai %d.\n", i);
-	ret = snd_soc_register_dai(&u8500_msp_dai[i]);
-	if (ret < 0)
-	  printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to register MSP dai %d.\n", i);
-  }
-
-  // Allocate platform device
-  printk(KERN_DEBUG "MOP500_AB3550: Allocate platform device.\n");
-  u8500_ab3550_snd_device = platform_device_alloc("soc-audio", -1);
-  if (!u8500_ab3550_snd_device)
-      return -ENOMEM;
-
-  // Set platform drvdata
-  printk(KERN_DEBUG "MOP500_AB3550: Set platform drvdata.\n");
-  platform_set_drvdata(u8500_ab3550_snd_device, &u8500_ab3550_snd_devdata);
-  if (ret < 0)
-	  printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to set drvdata.\n");
-
-  // Add platform device
-  printk(KERN_DEBUG "MOP500_AB3550: Add device.\n");
-  u8500_ab3550_snd_devdata.dev = &u8500_ab3550_snd_device->dev;
-  ret = platform_device_add(u8500_ab3550_snd_device);
-  if (ret) {
-	printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to add platform device.\n");
-    platform_device_put(u8500_ab3550_snd_device);
-  }
-
-  // Register IS2-driver
-  printk(KERN_DEBUG "MOP500_AB3550: Register I2S-driver.\n");
-  ret = u8500_platform_registerI2S();
-  if (ret < 0)
-    printk(KERN_DEBUG "MOP500_AB3550: Error: Failed to register I2S-driver.\n");
-
-  return ret;
-}
-
-
-
-static void __exit u8500_ab3550_exit(void)
-{
-  printk(KERN_ALERT "MOP500_AB35500: u8500_ab3550_exit\n");
-
-  platform_device_unregister(u8500_ab3550_snd_device);
-}
-
-module_init(u8500_ab3550_init);
-module_exit(u8500_ab3550_exit);
-
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/u8500/u8500_msp_dai.c b/sound/soc/u8500/u8500_msp_dai.c
deleted file mode 100755
index c560066..0000000
--- a/sound/soc/u8500/u8500_msp_dai.c
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Ola Lilja ola.o.lilja@stericsson.com,
- *         Roger Nilsson 	roger.xr.nilsson@stericsson.com
- *         for ST-Ericsson.
- *
- * License terms:
- *
- * 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 <sound/soc.h>
-#include <sound/soc-dai.h>
-#include <asm/dma.h>
-#include "u8500_msp_dai.h"
-#include "u8500_pcm.h"
-
-#include <mach/msp.h>
-#include <linux/i2s/i2s.h>
-
-struct u8500_dai_private_t{
-	spinlock_t lock;
-	struct i2s_device *i2s;
-	u8 rx_active;
-	u8 tx_active;
-};
-
-struct u8500_dai_private_t msp_dai_private[U8500_NBR_OF_DAI] =
-{
-	/* DAI 0 */
-	{
-		.lock = SPIN_LOCK_UNLOCKED,
-		.i2s = NULL,
-		.tx_active = 0,
-		.rx_active = 0,
-	},
-	/* DAI 1  */
-	{
-		.lock = SPIN_LOCK_UNLOCKED,
-		.i2s = NULL,
-		.tx_active = 0,
-		.rx_active = 0,
-	},
-};
-
-static void compile_msp_config(struct msp_generic_config *msp_config,struct snd_pcm_substream *substream)
-{
-  int channels;
-
-  msp_config->tx_clock_sel = 0;
-	msp_config->rx_clock_sel = 0;
-	msp_config->tx_frame_sync_sel = 0;
-	msp_config->rx_frame_sync_sel = 0;
-	msp_config->input_clock_freq = MSP_INPUT_FREQ_48MHZ;
-	msp_config->srg_clock_sel = 0;
-  msp_config->rx_frame_sync_pol = RX_FIFO_SYNC_HI;
-	msp_config->tx_frame_sync_pol = TX_FIFO_SYNC_HI;
-  msp_config->rx_fifo_config = RX_FIFO_ENABLE;
-	msp_config->tx_fifo_config = TX_FIFO_ENABLE;
-  msp_config->spi_clk_mode = SPI_CLK_MODE_NORMAL;
-	msp_config->spi_burst_mode = 0;
-  msp_config->handler = u8500_pcm_dma_eot_handler;
-	msp_config->tx_callback_data =  substream;
-	msp_config->tx_data_enable = 0;
-	msp_config->rx_callback_data =  substream;
-	msp_config->loopback_enable = 0;
-	msp_config->multichannel_configured = 0;
-	msp_config->def_elem_len = 0;
-  msp_config->default_protocol_desc = 0;
-  msp_config->protocol_desc.rx_phase_mode = MSP_SINGLE_PHASE;
-  msp_config->protocol_desc.tx_phase_mode = MSP_SINGLE_PHASE;
-  msp_config->protocol_desc.rx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
-  msp_config->protocol_desc.tx_phase2_start_mode = MSP_PHASE2_START_MODE_IMEDIATE;
-  msp_config->protocol_desc.rx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
-  msp_config->protocol_desc.tx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
-  msp_config->protocol_desc. rx_frame_length_1 = MSP_FRAME_LENGTH_2;
-  msp_config->protocol_desc. rx_frame_length_2 = MSP_FRAME_LENGTH_2;
-  msp_config->protocol_desc.tx_frame_length_1 = MSP_FRAME_LENGTH_2;
-  msp_config->protocol_desc.tx_frame_length_2 = MSP_FRAME_LENGTH_2;
-  msp_config->protocol_desc. rx_element_length_1 = MSP_ELEM_LENGTH_32;
-  msp_config->protocol_desc.rx_element_length_2 = MSP_ELEM_LENGTH_32;
-  msp_config->protocol_desc.tx_element_length_1 = MSP_ELEM_LENGTH_32;
-  msp_config->protocol_desc.tx_element_length_2 = MSP_ELEM_LENGTH_32;
-  msp_config->protocol_desc.rx_data_delay = MSP_DELAY_0;
-  msp_config->protocol_desc.tx_data_delay = MSP_DELAY_0;
-  msp_config->protocol_desc.rx_clock_pol = MSP_FALLING_EDGE;
-  msp_config->protocol_desc.tx_clock_pol = MSP_RISING_EDGE;
-  msp_config->protocol_desc. rx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
-  msp_config->protocol_desc.tx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
-  msp_config->protocol_desc.rx_half_word_swap = MSP_HWS_NO_SWAP;
-  msp_config->protocol_desc.tx_half_word_swap = MSP_HWS_NO_SWAP;
-  msp_config->protocol_desc.compression_mode = MSP_COMPRESS_MODE_LINEAR;
-  msp_config->protocol_desc.expansion_mode = MSP_EXPAND_MODE_LINEAR;
-  msp_config->protocol_desc.spi_clk_mode = MSP_SPI_CLOCK_MODE_NON_SPI;
-  msp_config->protocol_desc.spi_burst_mode = MSP_SPI_BURST_MODE_DISABLE;
-  msp_config->protocol_desc.frame_sync_ignore = MSP_FRAME_SYNC_IGNORE;
-  msp_config->protocol_desc.frame_period = 63;
-  msp_config->protocol_desc.frame_width = 31;
-	msp_config->protocol_desc.total_clocks_for_one_frame = 64;
-  msp_config->protocol = MSP_I2S_PROTOCOL; //MSP_PCM_PROTOCOL
-	msp_config->direction = MSP_BOTH_T_R_MODE;
-	msp_config->frame_size = 0;
-	msp_config->frame_freq = 48000;
-	channels = 2;
-	msp_config->data_size = (channels == 1) ? MSP_DATA_SIZE_16BIT : MSP_DATA_SIZE_32BIT;
-	msp_config->work_mode = MSP_DMA_MODE;
-  msp_config->multichannel_configured = 1;
-	msp_config->multichannel_config.tx_multichannel_enable = 1;
-	msp_config->multichannel_config.tx_channel_0_enable = 0x0000003; //Channel 1 and channel 2
-	msp_config->multichannel_config.tx_channel_1_enable = 0x0000000; //Channel 1 and channel 2
-	msp_config->multichannel_config.tx_channel_2_enable = 0x0000000; //Channel 1 and channel 2
-	msp_config->multichannel_config.tx_channel_3_enable = 0x0000000; //Channel 1 and channel 2
-	msp_config->multichannel_config.rx_multichannel_enable = 1;
-	msp_config->multichannel_config.rx_channel_0_enable = 0x0000003; //Channel 1 and channel 2
-	msp_config->multichannel_config.rx_channel_1_enable = 0x0000000; //Channel 1 and channel 2
-	msp_config->multichannel_config.rx_channel_2_enable = 0x0000000; //Channel 1 and channel 2
-	msp_config->multichannel_config.rx_channel_3_enable = 0x0000000; //Channel 1 and channel 2
-}
-
-static void u8500_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *msp_dai)
-{
-	int ret = 0;
-	unsigned long flags;
-	struct u8500_dai_private_t *dai_private;
-
-  printk(KERN_ALERT "STW4500: u8500_dai_shutdown\n");
-
-	if (!(dai_private = msp_dai->private_data))
-		return;
-
-	spin_lock_irqsave(&dai_private->lock, flags);
-
-	/* Mark the stopped direction as inactive */
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		dai_private->tx_active = 0;
-	} else {
-		dai_private->rx_active = 0;
-	}
-
-	if (0 == dai_private->tx_active && 0 == dai_private->rx_active) {
-		msp_dai->private_data = NULL;
-
-
-    ret = i2s_cleanup(dai_private->i2s->controller, DISABLE_ALL);
-
-    if (ret)
-			printk(KERN_WARNING "Error closing i2s\n");
-
-	} else {
-
-    if(dai_private->tx_active)
-      ret = i2s_cleanup(dai_private->i2s->controller, DISABLE_RECEIVE);
-    else
-      ret = i2s_cleanup(dai_private->i2s->controller, DISABLE_TRANSMIT);
-
-    if (ret)
-			printk(KERN_WARNING "Error closing i2s\n");
-  }
-
-  spin_unlock_irqrestore(&dai_private->lock, flags);
-
-  return;
-}
-
-static int u8500_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *msp_dai)
-{
-	struct u8500_dai_private_t *dai_private;
-
-  printk(KERN_ALERT "STW4500: u8500_dai_startup\n");
-
-	dai_private=&msp_dai_private[msp_dai->id];
-	/* Store pointer to private data */
-	msp_dai->private_data = dai_private;
-
-  if (dai_private->i2s == NULL)
-	{
-		printk(KERN_ALERT "u8500_pcm_open: Error: i2sdrv.i2s == NULL.\n");
-		return -1;
-	}
-	if (dai_private->i2s->controller == NULL)
-	{
-		printk(KERN_ALERT "u8500_pcm_open: Error: i2sdrv.i2s->controller == NULL.\n");
-		return -1;
-	}
-
-
-	return 0;
-}
-
-static int u8500_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *msp_dai)
-{
-	int ret = 0;
-	unsigned long flags_private;
-	struct u8500_dai_private_t *dai_private;
-	struct msp_generic_config msp_config;
-
-  printk(KERN_ALERT "STW4500: u8500_dai_prepare\n");
-
-	dai_private = msp_dai->private_data;
-
-	spin_lock_irqsave(&dai_private->lock, flags_private);
-
-  /* Is the other direction already active? */
-	if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
-	     dai_private->rx_active) ||
-	    (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
-	     dai_private->tx_active)) {
-		/* TODO: Check if sample rate match ! */
-    /*
-    if (  != runtime->rate) {
-			printk(KERN_ERR "CPU_DAI configuration mismatch with already opened stream!\n");
-			ret = -EBUSY;
-			goto cleanup;
-		}
-    */
-  } else if (!dai_private->rx_active && !dai_private->tx_active) {
-		ret = 0;
-
-    compile_msp_config(&msp_config,substream);
-
-    ret = i2s_setup(dai_private->i2s->controller, &msp_config);
-	  if (ret < 0) {
-		  printk(KERN_ALERT "u8500_dai_prepare: i2s_setup failed! ret = %d\n", ret);
-		  goto cleanup;
-	  }
-	}
-
-	/* Mark the newly started stream as active */
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		dai_private->tx_active = 1;
-	} else {
-		dai_private->rx_active = 1;
-	}
-
-cleanup:
-	spin_unlock_irqrestore(&dai_private->lock, flags_private);
-	return ret;
-}
-
-static int u8500_dai_hw_params(struct snd_pcm_substream *substream,
-			      struct snd_pcm_hw_params *params, struct snd_soc_dai *msp_dai)
-{
-  printk(KERN_ALERT "STW4500: u8500_dai_hw_params\n");
-
-	return 0;
-}
-
-static int u8500_dai_set_dai_fmt(struct snd_soc_dai *msp_dai, unsigned int fmt)
-{
-  printk(KERN_ALERT "STW4500: u8500_dai_set_dai_fmt\n");
-	return 0;
-}
-
-static int u8500_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *msp_dai)
-{
-	unsigned long flags;
-	int ret = 0;
-	struct u8500_dai_private_t *u8500_dai_private = msp_dai->private_data;
-
-  printk(KERN_ALERT "STW4500: u8500_dai_trigger\n");
-
-	spin_lock_irqsave(&u8500_dai_private->lock, flags);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		ret = 0;
-		break;
-	case SNDRV_PCM_TRIGGER_RESUME:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		ret = 0;
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		ret = 0;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		ret = 0;
-		break;
-	default:
-		ret = -EINVAL;
-		break;
-	}
-
-	spin_unlock_irqrestore(&u8500_dai_private->lock, flags);
-	return ret;
-}
-
-int u8500_i2s_send_data(void *data, size_t bytes, int dai_idx)
-{
-  unsigned long flags;
-  struct u8500_dai_private_t *dai_private;
-  struct i2s_message message;
-  struct i2s_device *i2s_dev;
-  int ret = 0;
-  dai_private=&msp_dai_private[dai_idx];
-
-  spin_lock_irqsave(&dai_private->lock, flags);
-
-  i2s_dev=dai_private->i2s;
-
-
-  if(!dai_private->tx_active)
-  {
-    printk(KERN_ERR "u8500_i2s_send_data: I2S controller not available\n");
-    goto cleanup;
-  }
-
-	message.txbytes = bytes;
-	message.txdata = data;
-	message.rxbytes = 0;
-	message.rxdata = NULL;
-	message.dma_flag = 1;
-
-  ret = i2s_transfer(i2s_dev->controller, &message);
-	if(ret < 0)
-	{
-		printk(KERN_ERR "u8500_i2s_send_data: Error: i2s_transfer failed!\n");
-		goto cleanup;
-	}
-
-cleanup:
-	spin_unlock_irqrestore(&dai_private->lock, flags);
-	return ret;
-}
-
-int u8500_i2s_receive_data(void *data, size_t bytes, int dai_idx)
-{
-	unsigned long flags;
-  struct u8500_dai_private_t *dai_private;
-  struct i2s_message message;
-  struct i2s_device *i2s_dev;
-  int ret = 0;
-  dai_private=&msp_dai_private[dai_idx];
-
-  spin_lock_irqsave(&dai_private->lock, flags);
-
-  i2s_dev=dai_private->i2s;
-
-
-  if(!dai_private->rx_active)
-  {
-    printk(KERN_ERR "u8500_i2s_receive_data: I2S controller not available\n");
-    goto cleanup;
-  }
-
-  message.rxbytes = bytes;
-	message.rxdata = data;
-	message.txbytes = 0;
-	message.txdata = NULL;
-	message.dma_flag = 1;
-
-  ret = i2s_transfer(i2s_dev->controller, &message);
-	if(ret < 0)
-	{
-		printk(KERN_ERR "u8500_i2s_receive_data: Error: i2s_transfer failed!\n");
-		goto cleanup;
-	}
-
-cleanup:
-	spin_unlock_irqrestore(&dai_private->lock, flags);
-	return ret;
-}
-
-#define u8500_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |	\
-			SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
-#define u8500_I2S_FORMATS ( SNDRV_PCM_FMTBIT_S16_LE )
-
-#define dai_suspend NULL
-#define dai_resume NULL
-#define u8500_dai_set_dai_sysclk NULL
-
-struct snd_soc_dai_ops u8500_msp_dai_ops_0 =
-{
-	.set_sysclk=u8500_dai_set_dai_sysclk,
-	.set_fmt=u8500_dai_set_dai_fmt,
-	.startup = u8500_dai_startup,
-	.shutdown = u8500_dai_shutdown,
-	.prepare = u8500_dai_prepare,
-	.trigger = u8500_dai_trigger,
-	.hw_params = u8500_dai_hw_params,
-};
-
-struct snd_soc_dai_ops u8500_msp_dai_ops_1 =
-{
-	.set_sysclk=u8500_dai_set_dai_sysclk,
-	.set_fmt=u8500_dai_set_dai_fmt,
-	.startup = u8500_dai_startup,
-	.shutdown = u8500_dai_shutdown,
-	.prepare = u8500_dai_prepare,
-	.trigger = u8500_dai_trigger,
-	.hw_params = u8500_dai_hw_params,
-};
-
-struct snd_soc_dai u8500_msp_dai[U8500_NBR_OF_DAI] =
-{
-	{
-		.name = "u8500_i2s-0",
-		.id = 0,
-		//.type = SND_SOC_DAI_I2S,
-		.suspend = dai_suspend,
-		.resume = dai_resume,
-		.playback = {
-			.channels_min = 2,
-			.channels_max = 2,
-			.rates = u8500_I2S_RATES,
-			.formats = u8500_I2S_FORMATS,},
-		.capture = {
-			.channels_min = 2,
-			.channels_max = 2,
-			.rates = u8500_I2S_RATES,
-			.formats = u8500_I2S_FORMATS,},
-		.ops = &u8500_msp_dai_ops_0,
-		.private_data = &msp_dai_private[0],
-	},
-	{
-		.name = "u8500_i2s-1",
-		.id = 1,
-		//.type = SND_SOC_DAI_I2S,
-		.suspend = dai_suspend,
-		.resume = dai_resume,
-		.playback = {
-			.channels_min = 2,
-			.channels_max = 2,
-			.rates = u8500_I2S_RATES,
-			.formats = u8500_I2S_FORMATS,},
-		.capture = {
-			.channels_min = 2,
-			.channels_max = 2,
-			.rates = u8500_I2S_RATES,
-			.formats = u8500_I2S_FORMATS,},
-		.ops = &u8500_msp_dai_ops_1,
-		.private_data = &msp_dai_private[1],
-	},
-};
-
-EXPORT_SYMBOL(u8500_msp_dai);
-
-
-
-static int i2sdrv_probe(struct i2s_device *i2s)
-{
-	unsigned long flags;
-  struct u8500_dai_private_t *u8500_dai_private;
-
-  u8500_dai_private=&msp_dai_private[0];
-  printk(KERN_DEBUG "i2sdrv_probe: Enter.\n");
-
-  spin_lock_irqsave(&u8500_dai_private->lock, flags);
-  u8500_dai_private->i2s = i2s;
-  spin_unlock_irqrestore(&u8500_dai_private->lock, flags);
-
-	try_module_get(i2s->controller->dev.parent->driver->owner);
-	i2s_set_drvdata(i2s, (void*)u8500_dai_private);
-
-	return 0;
-}
-
-
-
-static int i2sdrv_remove(struct i2s_device *i2s)
-{
-  unsigned long flags;
-  struct u8500_dai_private_t *u8500_dai_private = i2s_get_drvdata(i2s);
-
-  printk(KERN_DEBUG "U8500_PCM: i2sdrv_remove\n");
-
-  spin_lock_irqsave(&u8500_dai_private->lock, flags);
-
-  u8500_dai_private->i2s = NULL;
-  i2s_set_drvdata(i2s, NULL);
-  spin_unlock_irqrestore(&u8500_dai_private->lock, flags);
-
-
-  printk(KERN_ALERT "i2sdrv_remove: module_put\n");
-  module_put(i2s->controller->dev.parent->driver->owner);
-
-  return 0;
-}
-
-
-
-static const struct i2s_device_id dev_id_table[] = {
-	        { "i2s_device.0", 0, 0 },
-		        { },
-};
-MODULE_DEVICE_TABLE(i2s, acodec_id_table);
-
-
-
-static struct i2s_driver i2sdrv_i2s = {
-  .driver = {
-    .name = "nomadik_acodec",
-    .owner = THIS_MODULE,
-  },
-  .probe = i2sdrv_probe,
-  .remove = __devexit_p(i2sdrv_remove),
-  .id_table = dev_id_table,
-};
-
-int u8500_platform_registerI2S(void)
-{
-  int ret;
-
-  printk(KERN_ALERT "u8500_platform_registerI2S: Register I2S-driver.\n");
-  ret = i2s_register_driver(&i2sdrv_i2s);
-  if (ret < 0) {
-    printk(KERN_ALERT "u8500_platform_registerI2S: Unable to register I2S-driver\n");
-  }
-
-  return ret;
-}
diff --git a/sound/soc/u8500/u8500_msp_dai.h b/sound/soc/u8500/u8500_msp_dai.h
deleted file mode 100755
index 077f1c9..0000000
--- a/sound/soc/u8500/u8500_msp_dai.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Ola Lilja ola.o.lilja@stericsson.com,
- *         Roger Nilsson 	roger.xr.nilsson@stericsson.com
- *         for ST-Ericsson.
- *
- * License terms:
- *
- * 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 U8500_msp_dai_H
-#define U8500_msp_dai_H
-
-#include <linux/types.h>
-#include <linux/spinlock.h>
-
-#define U8500_NBR_OF_DAI 2
-
-extern struct snd_soc_dai u8500_msp_dai[U8500_NBR_OF_DAI];
-
-int u8500_platform_registerI2S(void);
-int u8500_i2s_send_data(void *data, size_t bytes, int dai_idx);
-int u8500_i2s_receive_data(void *data, size_t bytes, int dai_idx);
-
-#endif
diff --git a/sound/soc/u8500/u8500_pcm.c b/sound/soc/u8500/u8500_pcm.c
deleted file mode 100755
index edd22be..0000000
--- a/sound/soc/u8500/u8500_pcm.c
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Ola Lilja ola.o.lilja@stericsson.com,
- *         Roger Nilsson 	roger.xr.nilsson@stericsson.com
- *         for ST-Ericsson.
- *
- * License terms:
- *
- * 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 <asm/page.h>
-
-#include <linux/dma-mapping.h>
-#include <linux/dmaengine.h>
-
-#include <linux/dma-mapping.h>
-
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include "u8500_pcm.h"
-#include "u8500_msp_dai.h"
-
-
-
-// Local variables -------------------------------------------------------------------
-
-// struct dma_chan
-struct u8500_pcm_transfer {
-	spinlock_t lock;
-	dma_addr_t addr;
-	u32 dma_chan_id;
-	u32 period;
-	struct dma_chan *dma_chan;
-};
-
-static struct snd_pcm_hardware u8500_pcm_hw_playback = {
-	.info =
-	    (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
-	     SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE),
-	.formats =
-	    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |
-	    SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U16_BE,
-	.rates = SNDRV_PCM_RATE_KNOT,
-	.rate_min = U8500_PLATFORM_MIN_RATE_PLAYBACK,
-	.rate_max = U8500_PLATFORM_MAX_RATE_PLAYBACK,
-	.channels_min = 1,
-	.channels_max = 2,
-	.buffer_bytes_max = NMDK_BUFFER_SIZE,
-	.period_bytes_min = 128,
-	.period_bytes_max = PAGE_SIZE,
-	.periods_min = NMDK_BUFFER_SIZE / PAGE_SIZE,
-	.periods_max = NMDK_BUFFER_SIZE / 128
-};
-
-static struct snd_pcm_hardware u8500_pcm_hw_capture = {
-	.info =
-	    (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
-	     SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE),
-	.formats =
-	    SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |
-	    SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U16_BE,
-	.rates = SNDRV_PCM_RATE_KNOT,
-	.rate_min = U8500_PLATFORM_MIN_RATE_CAPTURE,
-	.rate_max = U8500_PLATFORM_MAX_RATE_CAPTURE,
-	.channels_min = 1,
-	.channels_max = 2,
-	.buffer_bytes_max = NMDK_BUFFER_SIZE,
-	.period_bytes_min = 128,
-	.period_bytes_max = PAGE_SIZE,
-	.periods_min = NMDK_BUFFER_SIZE / PAGE_SIZE,
-	.periods_max = NMDK_BUFFER_SIZE / 128
-};
-
-static struct u8500_pcm_private_t u8500_pcm_private;
-
-
-
-// DMA operations -----------------------------------------------------------------------------
-
-/**
- * nomadik_alsa_dma_start - used to transmit or recive a dma chunk
- * @stream - specifies the playback/record stream structure
- */
-static void u8500_pcm_dma_start(struct snd_pcm_substream *substream)
-{
-	unsigned int offset, dma_size;
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	int stream_id = substream->pstr->stream;
-	u8500_pcm_stream_t* stream_p = &u8500_pcm_private.streams[stream_id];
-
-	printk(KERN_DEBUG "u8500_pcm_dma_start: Enter.\n");
-
-	dma_size = frames_to_bytes(runtime, runtime->period_size);
-	offset = dma_size * stream_p->period;
-	stream_p->old_offset = offset;
-	printk(KERN_DEBUG "u8500_pcm_dma_start: address = %x  size=%d\n", (runtime->dma_addr + offset), dma_size);
-
-	if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		u8500_i2s_send_data((void*)(runtime->dma_addr + offset), dma_size, 0);
-	}
-	else {
-		u8500_i2s_receive_data((void *)(runtime->dma_addr + offset), dma_size, 0);
-	}
-
-	stream_p->period++;
-	stream_p->period %= runtime->periods;
-	stream_p->periods++;
-}
-
-static void u8500_pcm_dma_hw_free(struct device *dev, struct snd_pcm_substream *substream)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_dma_buffer *buf = runtime->dma_buffer_p;
-
-	if (runtime->dma_area == NULL)
-		return;
-
-	if (buf != &substream->dma_buffer) {
-		dma_free_coherent(buf->dev.dev, buf->bytes, buf->area, buf->addr);
-		kfree(runtime->dma_buffer_p);
-	}
-
-	snd_pcm_set_runtime_buffer(substream, NULL);
-}
-
-/**
- * dma_eot_handler
- * @data - pointer to structure set in the dma callback handler
- * @event - specifies the DMA event: transfer complete/error
- *
- *  This is the PCM tasklet handler linked to a pipe, its role is to tell
- *  the PCM middler layer whene the buffer position goes across the prescribed
- *  period size.To inform of this the snd_pcm_period_elapsed is called.
- *
- * this callback will be called in case of DMA_EVENT_TC only
- */
-irqreturn_t u8500_pcm_dma_eot_handler(void *data, int irq)
-{
-	struct snd_pcm_substream *substream = data;
-	int stream_id = substream->pstr->stream;
-	u8500_pcm_stream_t* stream_p = &u8500_pcm_private.streams[stream_id];
-
-	/* snd_pcm_period_elapsed() is _not_ to be protected
-	 */
-	printk(KERN_DEBUG "u8500_pcm_dma_eot_handler: Enter.\n");
-
-	if (substream)
-		snd_pcm_period_elapsed(substream);
-	if(stream_p->state == ALSA_STATE_PAUSED)
-		return IRQ_HANDLED;
-	if (stream_p->active == 1) {
-		u8500_pcm_dma_start(substream);
-	}
-	return IRQ_HANDLED;
-}
-EXPORT_SYMBOL(u8500_pcm_dma_eot_handler);
-
-
-
-
-// snd_pcm_ops -----------------------------------------------------------------------------
-
-static int u8500_pcm_open(struct snd_pcm_substream *substream)
-{
-	int status;
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	int stream_id = substream->pstr->stream;
-	u8500_pcm_stream_t* stream_p = &u8500_pcm_private.streams[stream_id];
-
-	printk(KERN_DEBUG "u8500_pcm_open: Enter.\n");
-
-	status = 0;
-	printk(KERN_DEBUG "u8500_pcm_open: Setting HW-config.\n");
-	if (stream_id == SNDRV_PCM_STREAM_PLAYBACK)
-	{
-		runtime->hw = u8500_pcm_hw_playback;
-	}
-	else
-	{
-		runtime->hw = u8500_pcm_hw_capture;
-	}
-
-	init_MUTEX(&(stream_p->alsa_sem));
-	init_completion(&(stream_p->alsa_com));
-	stream_p->state = ALSA_STATE_RUNNING;
-	printk(KERN_ALERT "u8500_pcm_open: Exit.\n");
-
-	return 0;
-}
-
-static int u8500_pcm_close(struct snd_pcm_substream *substream)
-{
-	printk(KERN_DEBUG "u8500_pcm_close: Enter.\n");
-
-	return 0;
-}
-
-static int u8500_pcm_hw_params(struct snd_pcm_substream *substream,
-			      struct snd_pcm_hw_params *hw_params)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct snd_dma_buffer *buf = runtime->dma_buffer_p;
-	int ret = 0;
-	int size;
-
-	printk(KERN_DEBUG "u8500_pcm_hw_params: Enter.\n");
-
-	size = params_buffer_bytes(hw_params);
-
-	if (buf) {
-		if (buf->bytes >= size)
-			goto out;
-		u8500_pcm_dma_hw_free(NULL, substream);
-	}
-
-	if (substream->dma_buffer.area != NULL && substream->dma_buffer.bytes >= size) {
-		buf = &substream->dma_buffer;
-	} else {
-		buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL);
-		if (!buf)
-			goto nomem;
-
-		buf->dev.type = SNDRV_DMA_TYPE_DEV;
-		buf->dev.dev = NULL;
-		buf->area = dma_alloc_coherent(NULL, size, &buf->addr, GFP_KERNEL);
-		buf->bytes = size;
-		buf->private_data = NULL;
-
-		if (!buf->area)
-			goto free;
-	}
-	snd_pcm_set_runtime_buffer(substream, buf);
-	ret = 1;
- out:
-	runtime->dma_bytes = size;
-	return ret;
-
- free:
-	kfree(buf);
- nomem:
-	return -ENOMEM;
-}
-
-static int u8500_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-	printk(KERN_ALERT "u8500_pcm_hw_free: Enter.\n");
-
-	u8500_pcm_dma_hw_free(NULL, substream);
-
-	return 0;
-}
-
-static int u8500_pcm_prepare(struct snd_pcm_substream *substream)
-{
-	printk(KERN_DEBUG "u8500_pcm_prepare: Enter.\n");
-
-	return 0;
-}
-
-static int u8500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-	int stream_id = substream->pstr->stream;
-	u8500_pcm_stream_t* stream_p = &u8500_pcm_private.streams[stream_id];
-
-	printk(KERN_DEBUG "u8500_pcm_trigger: Enter.\n");
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		// Start DMA transfer
-		printk(KERN_DEBUG "u8500_pcm_trigger: TRIGGER START\n");
-		if (stream_p->active == 0) {
-			stream_p->active = 1;
-			u8500_pcm_dma_start(substream);
-			break;
-		}
-		printk(KERN_DEBUG "u8500_pcm_trigger: Error: H/w is busy\n");
-		return -EINVAL;
-
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		// Pause DMA transfer
-		printk(KERN_ALERT "u8500_pcm_trigger: SNDRV_PCM_TRIGGER_PAUSE\n");
-	if (stream_p->state == ALSA_STATE_RUNNING) {
-		stream_p->state = ALSA_STATE_PAUSED;
-	}
-
-		break;
-
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		// Resume DMA transfer
-		printk(KERN_DEBUG "u8500_pcm_trigger: SNDRV_PCM_TRIGGER_RESUME\n");
-		if (stream_p->state == ALSA_STATE_PAUSED) {
-			stream_p->state = ALSA_STATE_RUNNING;
-			u8500_pcm_dma_start(substream);
-		}
-		break;
-
-	case SNDRV_PCM_TRIGGER_STOP:
-		// Stop DMA transfer
-		printk(KERN_DEBUG "u8500_pcm_trigger: SNDRV_PCM_TRIGGER_STOP\n");
-		if (stream_p->active == 1) {
-			stream_p->active = 0;
-			stream_p->period = 0;
-		}
-		break;
-
-	default:
-		printk(KERN_ERR "u8500_pcm_trigger: Invalid command in pcm trigger.\n");
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static snd_pcm_uframes_t u8500_pcm_pointer(struct snd_pcm_substream *substream)
-{
-	unsigned int offset;
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	int stream_id = substream->pstr->stream;
-	u8500_pcm_stream_t* stream_p = &u8500_pcm_private.streams[stream_id];
-
-	printk(KERN_DEBUG "u8500_pcm_pointer: Enter.\n");
-
-	offset = bytes_to_frames(runtime, stream_p->old_offset);
-	if (offset < 0 || stream_p->old_offset < 0)
-		printk(KERN_DEBUG "u8500_pcm_pointer: Offset=%i %i\n", offset, stream_p->old_offset);
-
-	return offset;
-}
-
-static int u8500_pcm_mmap(struct snd_pcm_substream *substream,
-			 struct vm_area_struct *vma)
-{
-	struct snd_pcm_runtime *runtime = substream->runtime;
-
-	printk(KERN_DEBUG "u8500_pcm_mmap: Enter.\n");
-
-	return dma_mmap_coherent(NULL, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes);
-}
-
-static struct snd_pcm_ops u8500_pcm_ops = {
-	.open		= u8500_pcm_open,
-	.close		= u8500_pcm_close,
-	.ioctl		= snd_pcm_lib_ioctl,
-	.hw_params	= u8500_pcm_hw_params,
-	.hw_free	= u8500_pcm_hw_free,
-	.prepare	= u8500_pcm_prepare,
-	.trigger	= u8500_pcm_trigger,
-	.pointer	= u8500_pcm_pointer,
-	.mmap		= u8500_pcm_mmap
-};
-
-
-
-// snd_soc_platform -----------------------------------------------------------------------------
-
-int u8500_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm)
-{
-	int i;
-
-	printk(KERN_DEBUG "u8500_pcm_new: Enter.\n");
-
-	printk(KERN_DEBUG "u8500_pcm_new: pcm = %d\n", (int)pcm);
-
-	pcm->info_flags = 0;
-	strcpy(pcm->name, "nomadik_asoc");
-
-	for (i = 0; i < NO_OF_STREAMS; i++)
-	{
-		;
-		u8500_pcm_private.streams[i].active = 0;
-		u8500_pcm_private.streams[i].period = 0;
-		u8500_pcm_private.streams[i].periods = 0;
-		u8500_pcm_private.streams[i].old_offset = 0;
-	}
-
-	printk(KERN_DEBUG "u8500_pcm_new: pcm->name = %s\n", pcm->name);
-
-	return 0;
-}
-
-static void u8500_pcm_free(struct snd_pcm *pcm)
-{
-	printk(KERN_DEBUG "u8500_pcm_free: Enter.\n");
-
-	//u8500_pcm_dma_hw_free(NULL, substream);
-}
-
-static int u8500_pcm_suspend(struct snd_soc_dai *dai)
-{
-	printk(KERN_DEBUG "u8500_pcm_suspend: Enter.\n");
-
-	return 0;
-}
-
-static int u8500_pcm_resume(struct snd_soc_dai *dai)
-{
-	printk(KERN_DEBUG "u8500_pcm_resume: Enter.\n");
-
-	return 0;
-}
-
-struct snd_soc_platform u8500_soc_platform = {
-	.name           = "u8500-audio",
-	.pcm_ops        = &u8500_pcm_ops,
-	.pcm_new        = u8500_pcm_new,
-	.pcm_free       = u8500_pcm_free,
-	.suspend        = u8500_pcm_suspend,
-	.resume         = u8500_pcm_resume,
-};
-EXPORT_SYMBOL(u8500_soc_platform);
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/sound/soc/u8500/u8500_pcm.h b/sound/soc/u8500/u8500_pcm.h
deleted file mode 100755
index 5957871..0000000
--- a/sound/soc/u8500/u8500_pcm.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2010
- *
- * Author: Ola Lilja ola.o.lilja@stericsson.com,
- *         Roger Nilsson 	roger.xr.nilsson@stericsson.com
- *         for ST-Ericsson.
- *
- * License terms:
- *
- * 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 U8500_PCM_H
-#define U8500_PCM_H
-
-#include <mach/msp.h>
-
-#define NMDK_BUFFER_SIZE 	(64*1024)
-
-#define U8500_PLATFORM_MIN_RATE_PLAYBACK   48000
-#define U8500_PLATFORM_MAX_RATE_PLAYBACK   48000
-#define U8500_PLATFORM_MIN_RATE_CAPTURE    48000
-#define U8500_PLATFORM_MAX_RATE_CAPTURE    48000
-#define U8500_PLATFORM_MAX_NO_OF_RATES		1
-
-#define NUMBER_OUTPUT_DEVICE				5
-#define NUMBER_INPUT_DEVICE					10
-
-#define NO_OF_STREAMS 2
-
-enum alsa_state {
-	ALSA_STATE_PAUSED,
-	ALSA_STATE_RUNNING
-};
-
-extern struct snd_soc_platform u8500_soc_platform;
-
-typedef struct {
-	int stream_id;		/* stream identifier  */
-	int active;		/* we are using this stream for transfer now */
-	int period;		/* current transfer period */
-	int periods;		/* current count of periods registerd in the DMA engine */
-	enum alsa_state state;
-	unsigned int old_offset;
-	struct semaphore alsa_sem;
-	struct completion alsa_com;
-} u8500_pcm_stream_t;
-
-struct u8500_pcm_private_t {
-	u8500_pcm_stream_t streams[2];
-};
-
-irqreturn_t u8500_pcm_dma_eot_handler(void *data, int irq);
-
-#endif
diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig
new file mode 100755
index 0000000..90b28e5
--- /dev/null
+++ b/sound/soc/ux500/Kconfig
@@ -0,0 +1,26 @@
+#
+# Ux500 SoC audio configuration
+#
+
+config SND_SOC_UX500
+	bool "ux500"
+	depends on SND_SOC && STM_I2S && STM_MSP_I2S
+	default y
+	help
+	  Say Y if you want to add support for the codecs attached to
+	  the I2S of the ux500. You will also need
+	  to select the audio codecs to support below.
+
+config SND_SOC_UX500_AB3550
+	tristate "SoC Audio support for ux500 w break-out card AB3550"
+	depends on SND_SOC_UX500 && AB3550_CORE
+	select SND_SOC_AB3550
+  help
+	  Say Y if you want to add support for SoC audio on ux500 w break-out card AB3550.
+
+config SND_SOC_UX500_CG29XX
+	tristate "SoC Audio support for ux500 w break-out card CG29xx"
+	depends on SND_SOC_UX500 && !SND_SOC_UX500_AB3550
+	select SND_SOC_CG29XX
+	help
+	  Say Y if you want to add support for SoC audio on ux500 w break-out card CG29XX.
diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile
new file mode 100755
index 0000000..3eff08b
--- /dev/null
+++ b/sound/soc/ux500/Makefile
@@ -0,0 +1,9 @@
+# Ux500 Platform Support
+
+snd-soc-ux500-objs := ux500_pcm.o ux500_msp_dai.o
+snd-soc-ux500-ab3550-objs := ux500_ab3550.o
+snd-soc-ux500-cg29xx-objs := ux500_cg29xx.o
+
+obj-$(CONFIG_SND_SOC_UX500) += snd-soc-ux500.o
+obj-$(CONFIG_SND_SOC_UX500_AB3550) += snd-soc-ux500-ab3550.o
+obj-$(CONFIG_SND_SOC_UX500_CG29XX) += snd-soc-ux500-cg29xx.o
diff --git a/sound/soc/ux500/ux500_ab3550.c b/sound/soc/ux500/ux500_ab3550.c
new file mode 100755
index 0000000..f8132b2
--- /dev/null
+++ b/sound/soc/ux500/ux500_ab3550.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ *         Roger Nilsson roger.xr.nilsson@stericsson.com
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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/device.h>
+#include <linux/io.h>
+#include <sound/soc.h>
+
+#include "ux500_pcm.h"
+#include "ux500_msp_dai.h"
+#include "mach/hardware.h"
+
+#include "../codecs/ab3550.h"
+
+static struct platform_device *ux500_ab3550_platform_device;
+
+#define AB3550_DAI_FMT_I2S_M (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM)
+#define AB3550_DAI_FMT_I2S_S (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS)
+#define AB3550_DAI_FMT AB3550_DAI_FMT_I2S_S
+
+static int ux500_ab3550_startup(struct snd_pcm_substream *substream)
+{
+	dev_dbg(&ux500_ab3550_platform_device->dev,
+		"%s: Enter\n",
+		__func__);
+	return 0;
+}
+
+static void ux500_ab3550_shutdown(struct snd_pcm_substream *substream)
+{
+	dev_dbg(&ux500_ab3550_platform_device->dev,
+		"%s: Enter\n",
+		__func__);
+}
+
+static int ux500_ab3550_hw_params(
+	struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	int ifid, ret = 0;
+
+	dev_dbg(&ux500_ab3550_platform_device->dev,
+		"%s: Enter\n",
+		__func__);
+
+	dev_dbg(&ux500_ab3550_platform_device->dev,
+		"%s: substream->pcm->name = %s\n"
+		"substream->pcm->id = %s.\n"
+		"substream->name = %s.\n"
+		"substream->number = %d.\n",
+		__func__,
+		substream->pcm->name,
+		substream->pcm->id,
+		substream->name,
+		substream->number);
+
+	for (ifid = 0; ifid < ARRAY_SIZE(ab3550_codec_dai); ifid++) {
+		if (strcmp(codec_dai->name, ab3550_codec_dai[ifid].name) == 0)
+			break;
+	}
+
+	if (codec_dai->ops.set_fmt) {
+		ret = snd_soc_dai_set_fmt(codec_dai, AB3550_DAI_FMT);
+		if (ret < 0) {
+			dev_dbg(&ux500_ab3550_platform_device->dev,
+				"%s: snd_soc_dai_set_fmt failed with %d.\n",
+				__func__,
+				ret);
+			return ret;
+		}
+
+		ret = snd_soc_dai_set_fmt(cpu_dai, AB3550_DAI_FMT);
+
+		if (ret < 0) {
+			dev_dbg(&ux500_ab3550_platform_device->dev,
+				"%s: snd_soc_dai_set_fmt"
+				" failed with %d.\n", __func__, ret);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static struct snd_soc_ops ux500_ab3550_ops = {
+	.startup = ux500_ab3550_startup,
+	.shutdown = ux500_ab3550_shutdown,
+	.hw_params = ux500_ab3550_hw_params,
+};
+
+struct snd_soc_dai_link ux500_ab3550_dai_links[] = {
+	{
+		.name = "ab3550_0",
+		.stream_name = "ab3550_0",
+		.cpu_dai = &ux500_msp_dai[0],
+		.codec_dai = &ab3550_codec_dai[0],
+		.init = NULL,
+		.ops = &ux500_ab3550_ops,
+	},
+	{
+		.name = "ab3550_1",
+		.stream_name = "ab3550_1",
+		.cpu_dai = &ux500_msp_dai[1],
+		.codec_dai = &ab3550_codec_dai[1],
+		.init = NULL,
+		.ops = &ux500_ab3550_ops,
+	},
+};
+
+static struct snd_soc_card ux500_ab3550 = {
+	.name = "ab3550",
+	.probe = NULL,
+	.dai_link = ux500_ab3550_dai_links,
+	.num_links = ARRAY_SIZE(ux500_ab3550_dai_links),
+	.platform = &ux500_soc_platform,
+};
+
+struct snd_soc_device ux500_ab3550_drvdata = {
+	.card = &ux500_ab3550,
+	.codec_dev = &soc_codec_dev_ab3550,
+};
+
+static int __init mop500_ab3550_soc_init(void)
+{
+	int i;
+	int ret = 0;
+
+	pr_debug("%s: Enter\n",
+		__func__);
+	pr_debug("%s: Card name: %s\n",
+		__func__,
+		ux500_ab3550_drvdata.card->name);
+
+	for (i = 0; i < ARRAY_SIZE(ux500_ab3550_dai_links); i++) {
+		pr_debug("%s: DAI-link %d, name: %s\n",
+			__func__,
+			i,
+			ux500_ab3550_drvdata.card->dai_link[i].name);
+		pr_debug("%s: DAI-link %d, stream_name: %s\n",
+			__func__,
+			i,
+			ux500_ab3550_drvdata.card->dai_link[i].stream_name);
+	}
+
+	pr_debug("%s: Allocate platform device (%s)\n",
+		__func__,
+		ux500_ab3550_drvdata.card->name);
+	ux500_ab3550_platform_device = platform_device_alloc("soc-audio", -1);
+	if (!ux500_ab3550_platform_device)
+		return -ENOMEM;
+
+	dev_dbg(&ux500_ab3550_platform_device->dev,
+		"%s: Set platform drvdata (%s)\n",
+		__func__,
+		ux500_ab3550_drvdata.card->name);
+	platform_set_drvdata(
+		ux500_ab3550_platform_device,
+		&ux500_ab3550_drvdata);
+
+	dev_dbg(&ux500_ab3550_platform_device->dev,
+		"%s: Add platform device (%s)\n",
+		__func__,
+		ux500_ab3550_drvdata.card->name);
+	ux500_ab3550_drvdata.dev = &ux500_ab3550_platform_device->dev;
+
+	ret = platform_device_add(ux500_ab3550_platform_device);
+	if (ret) {
+		dev_dbg(&ux500_ab3550_platform_device->dev,
+			"%s: Error: Failed to add platform device (%s)\n",
+			__func__,
+			ux500_ab3550_drvdata.card->name);
+		platform_device_put(ux500_ab3550_platform_device);
+	}
+
+	return ret;
+}
+module_init(mop500_ab3550_soc_init);
+
+static void __exit mop500_ab3550_soc_exit(void)
+{
+	dev_dbg(&ux500_ab3550_platform_device->dev,
+		"%s: Enter.\n",
+		__func__);
+
+	dev_dbg(&ux500_ab3550_platform_device->dev,
+		"%s: Un-register platform device (%s)\n",
+		__func__,
+		ux500_ab3550_drvdata.card->name);
+	platform_device_unregister(ux500_ab3550_platform_device);
+}
+module_exit(mop500_ab3550_soc_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ux500/ux500_cg29xx.c b/sound/soc/ux500/ux500_cg29xx.c
new file mode 100755
index 0000000..f1eaa08
--- /dev/null
+++ b/sound/soc/ux500/ux500_cg29xx.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Roger Nilsson 	roger.xr.nilsson@stericsson.com
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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/device.h>
+#include <linux/io.h>
+#include <sound/soc.h>
+
+#include "ux500_pcm.h"
+#include "ux500_msp_dai.h"
+#include "mach/hardware.h"
+#include "../codecs/cg29xx.h"
+
+static struct platform_device *ux500_cg29xx_platform_device;
+
+static int ux500_cg29xx_hw_params(
+	struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	int ret = 0;
+
+	pr_debug("%s: Enter.\n", __func__);
+	pr_debug("%s: substream->pcm->name = %s.\n"
+		"substream->pcm->id = %s.\n"
+		"substream->name = %s.\n"
+		"substream->number = %d.\n",
+		__func__,
+		substream->pcm->name,
+		substream->pcm->id,
+		substream->name,
+		substream->number);
+
+	if (cpu_dai->ops.set_fmt) {
+		ret = snd_soc_dai_set_fmt(
+			cpu_dai,
+			SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS);
+		if (ret < 0) {
+			pr_debug("%s: snd_soc_dai_set_fmt"
+				" failed with %d.\n",
+				__func__,
+				ret);
+			return ret;
+		}
+	}
+  return ret;
+}
+
+static struct snd_soc_ops ux500_cg29xx_ops = {
+	.hw_params = ux500_cg29xx_hw_params,
+};
+
+static int ux500_hdmi_hw_params(
+	struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	int ret = 0;
+
+	pr_debug("%s: Enter.\n", __func__);
+
+	pr_debug("%s: substream->pcm->name = %s.\n"
+		"substream->pcm->id = %s.\n"
+		"substream->name = %s.\n"
+		"substream->number = %d.\n",
+		__func__,
+		substream->pcm->name,
+		substream->pcm->id,
+		substream->name,
+		substream->number);
+
+	if (cpu_dai->ops.set_fmt) {
+		ret = snd_soc_dai_set_fmt(
+			cpu_dai, SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_CBM_CFM);
+		if (ret < 0) {
+			pr_debug("%s: snd_soc_dai_set_fmt"
+				" failed with %d.\n",
+				__func__,
+				ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops ux500_hdmi_ops = {
+	.hw_params = ux500_hdmi_hw_params,
+};
+
+struct snd_soc_dai_link ux500_cg29xx_dai_links[] = {
+	{
+		.name = "cg29xx_0",
+		.stream_name = "cg29xx_0",
+		.cpu_dai = &ux500_msp_dai[0],
+		.codec_dai = &cg29xx_codec_dai[0],
+		.init = NULL,
+		.ops = &ux500_cg29xx_ops,
+	},
+	{
+		.name = "hdmi_0",
+		.stream_name = "hdmi_0",
+		.cpu_dai = &ux500_msp_dai[2],
+		.codec_dai = &cg29xx_codec_dai[0],
+		.init = NULL,
+		.ops = &ux500_hdmi_ops,
+	},
+};
+
+static struct snd_soc_card ux500_cg29xx = {
+	.name = "cg29xx",
+	.probe = NULL,
+	.dai_link = ux500_cg29xx_dai_links,
+	.num_links = ARRAY_SIZE(ux500_cg29xx_dai_links),
+	.platform = &ux500_soc_platform,
+};
+
+struct snd_soc_device ux500_cg29xx_drvdata = {
+	.card = &ux500_cg29xx,
+	.codec_dev = &soc_codec_dev_cg29xx,
+};
+
+static int __init ux500_cg29xx_soc_init(void)
+{
+	int i;
+	int ret = 0;
+
+	pr_debug("%s: Enter.\n", __func__);
+	pr_debug("%s: Card name: %s\n",
+		__func__,
+		ux500_cg29xx_drvdata.card->name);
+
+	for (i = 0; i < ARRAY_SIZE(ux500_cg29xx_dai_links); i++) {
+		pr_debug("%s: DAI-link %d, name: %s\n",
+			__func__,
+			i,
+			ux500_cg29xx_drvdata.card->dai_link[i].name);
+		pr_debug("%s: DAI-link %d, stream_name: %s\n",
+			__func__,
+			i,
+			ux500_cg29xx_drvdata.card->dai_link[i].stream_name);
+	}
+
+	pr_debug("%s: Allocate platform device (%s).\n",
+		__func__,
+		ux500_cg29xx_drvdata.card->name);
+	ux500_cg29xx_platform_device =
+		platform_device_alloc("soc-audio", -1);
+	if (!ux500_cg29xx_platform_device)
+		return -ENOMEM;
+	pr_debug("%s: Set platform drvdata (%s).\n",
+		__func__,
+		ux500_cg29xx_drvdata.card->name);
+	platform_set_drvdata(
+		ux500_cg29xx_platform_device,
+		&ux500_cg29xx_drvdata);
+
+	pr_debug("%s: Add platform device (%s).\n",
+		__func__,
+		ux500_cg29xx_drvdata.card->name);
+	ux500_cg29xx_drvdata.dev = &ux500_cg29xx_platform_device->dev;
+
+	ret = platform_device_add(ux500_cg29xx_platform_device);
+	if (ret) {
+		pr_debug("%s: Error: Failed to add platform device (%s).\n",
+			__func__,
+			ux500_cg29xx_drvdata.card->name);
+		platform_device_put(ux500_cg29xx_platform_device);
+	}
+
+	return ret;
+}
+module_init(ux500_cg29xx_soc_init);
+
+static void __exit ux500_cg29xx_soc_exit(void)
+{
+	pr_debug("%s: Enter.\n", __func__);
+
+	pr_debug("%s: Un-register platform device (%s).\n",
+		__func__,
+		ux500_cg29xx_drvdata.card->name);
+	platform_device_unregister(ux500_cg29xx_platform_device);
+}
+module_exit(ux500_cg29xx_soc_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
new file mode 100755
index 0000000..db9495d
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -0,0 +1,789 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ *         Roger Nilsson roger.xr.nilsson@stericsson.com
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <asm/dma.h>
+#include "ux500_msp_dai.h"
+#include "ux500_pcm.h"
+
+#include <mach/msp.h>
+#include <linux/i2s/i2s.h>
+
+static struct ux500_msp_dai_private_s
+	ux500_msp_dai_private[UX500_NBR_OF_DAI] =
+{
+	{
+		.lock = __SPIN_LOCK_UNLOCKED(ux500_msp_dai_private[0].lock),
+		.i2s = NULL,
+		.tx_active = 0,
+		.rx_active = 0,
+		.fmt = 0,
+		.rate = 0,
+	},
+	{
+		.lock = __SPIN_LOCK_UNLOCKED(ux500_msp_dai_private[1].lock),
+		.i2s = NULL,
+		.tx_active = 0,
+		.rx_active = 0,
+		.fmt = 0,
+		.rate = 0,
+	},
+	{
+		.lock = __SPIN_LOCK_UNLOCKED(ux500_msp_dai_private[2].lock),
+		.i2s = NULL,
+		.tx_active = 0,
+		.rx_active = 0,
+		.fmt = 0,
+		.rate = 0,
+	},
+};
+
+static int ux500_msp_dai_i2s_probe(struct i2s_device *i2s)
+{
+	unsigned long flags;
+
+	pr_debug("%s: Enter (chip_select = %d, i2s = %d).\n",
+		__func__,
+		(int)i2s->chip_select, (int)(i2s));
+
+	spin_lock_irqsave(
+		&ux500_msp_dai_private[i2s->chip_select].lock,
+		flags);
+	ux500_msp_dai_private[i2s->chip_select].i2s = i2s;
+	spin_unlock_irqrestore(
+		&ux500_msp_dai_private[i2s->chip_select].lock,
+		flags);
+	try_module_get(i2s->controller->dev.parent->driver->owner);
+	i2s_set_drvdata(
+		i2s,
+		(void *)&ux500_msp_dai_private[i2s->chip_select]);
+
+	return 0;
+}
+
+static int ux500_msp_dai_i2s_remove(struct i2s_device *i2s)
+{
+	unsigned long flags;
+	struct ux500_msp_dai_private_s *ux500_msp_dai_private =
+		i2s_get_drvdata(i2s);
+
+	pr_debug("%s: Enter (chip_select = %d).\n",
+		__func__,
+		(int)i2s->chip_select);
+
+	spin_lock_irqsave(&ux500_msp_dai_private->lock, flags);
+
+	ux500_msp_dai_private->i2s = NULL;
+	i2s_set_drvdata(i2s, NULL);
+	spin_unlock_irqrestore(
+		&ux500_msp_dai_private->lock,
+		flags);
+
+	pr_debug("%s: module_put\n",
+		__func__);
+	module_put(i2s->controller->dev.parent->driver->owner);
+
+	return 0;
+}
+
+static const struct i2s_device_id dev_id_table[] = {
+	{ "i2s_device.0", 0, 0 },
+	{ "i2s_device.1", 0, 0 },
+	{ "i2s_device.2", 0, 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2s, dev_id_table);
+
+static struct i2s_driver i2sdrv_i2s = {
+	.driver = {
+		.name = "ux500_asoc_i2s",
+		.owner = THIS_MODULE,
+	},
+	.probe = ux500_msp_dai_i2s_probe,
+	.remove = __devexit_p(ux500_msp_dai_i2s_remove),
+	.id_table = dev_id_table,
+};
+
+int ux500_msp_dai_i2s_send_data(
+	void *data,
+	size_t bytes,
+	int dai_idx)
+{
+	unsigned long flags;
+	struct ux500_msp_dai_private_s *dai_private =
+		&ux500_msp_dai_private[dai_idx];
+	struct i2s_message message;
+	struct i2s_device *i2s_dev;
+	int ret = 0;
+
+	pr_debug("%s: Enter MSP Index:%d bytes = %d).\n",
+		__func__,
+		dai_idx,
+		(int)bytes);
+	spin_lock_irqsave(&dai_private->lock, flags);
+
+	i2s_dev = dai_private->i2s;
+
+	if (!dai_private->tx_active) {
+		pr_err("%s: The I2S controller is not available."
+			"MSP index:%d\n",
+			__func__,
+			dai_idx);
+		goto cleanup;
+	}
+
+	message.txbytes = bytes;
+	message.txdata = data;
+	message.rxbytes = 0;
+	message.rxdata = NULL;
+	message.dma_flag = 1;
+	ret = i2s_transfer(i2s_dev->controller, &message);
+	if (ret < 0) {
+		pr_err("%s: Error: i2s_transfer failed. MSP index: %d\n",
+			__func__,
+			dai_idx);
+		goto cleanup;
+	}
+
+cleanup:
+	spin_unlock_irqrestore(&dai_private->lock, flags);
+	return ret;
+}
+
+int ux500_msp_dai_i2s_receive_data(
+	void *data,
+	size_t bytes,
+	int dai_idx)
+{
+	unsigned long flags;
+	struct ux500_msp_dai_private_s *dai_private =
+		&ux500_msp_dai_private[dai_idx];
+	struct i2s_message message;
+	struct i2s_device *i2s_dev;
+	int ret = 0;
+
+	pr_debug("%s: Enter MSP Index: %d, bytes = %d).\n",
+		__func__,
+		dai_idx,
+		(int)bytes);
+
+	spin_lock_irqsave(&dai_private->lock, flags);
+
+	i2s_dev = dai_private->i2s;
+
+	if (!dai_private->rx_active) {
+		pr_err("%s: The I2S controller is not available."
+			"MSP index: %d\n",
+			__func__,
+			dai_idx);
+		goto cleanup;
+	}
+
+	message.rxbytes = bytes;
+	message.rxdata = data;
+	message.txbytes = 0;
+	message.txdata = NULL;
+	message.dma_flag = 1;
+	ret = i2s_transfer(i2s_dev->controller, &message);
+	if (ret < 0) {
+		pr_err("%s: Error: i2s_transfer failed. Msp index: %d\n",
+			__func__,
+			dai_idx);
+		goto cleanup;
+	}
+
+cleanup:
+	spin_unlock_irqrestore(&dai_private->lock, flags);
+	return ret;
+}
+
+static void ux500_msp_dai_shutdown(
+	struct snd_pcm_substream *substream,
+	struct snd_soc_dai *msp_dai)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct ux500_msp_dai_private_s *dai_private =
+		msp_dai->private_data;
+
+	pr_debug("%s: Enter (stream = %s).\n",
+		__func__,
+		substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			"SNDRV_PCM_STREAM_PLAYBACK" :
+			"SNDRV_PCM_STREAM_CAPTURE");
+
+	if (dai_private == NULL)
+		return;
+
+	pr_info("%s: MSP Index: %d\n",
+		__func__,
+		(int)dai_private->i2s->chip_select);
+
+	spin_lock_irqsave(&dai_private->lock, flags);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dai_private->tx_active = 0;
+	else
+		dai_private->rx_active = 0;
+
+	if (0 == dai_private->tx_active &&
+		0 == dai_private->rx_active) {
+
+		msp_dai->private_data = NULL;
+		ret = i2s_cleanup(dai_private->i2s->controller, DISABLE_ALL);
+
+		if (ret) {
+			pr_err("%s: Failed to close the i2s controller."
+				"MSP index:%d\n",
+				__func__,
+				(int)dai_private->i2s->chip_select);
+		}
+	} else {
+
+		if (dai_private->tx_active)
+			ret = i2s_cleanup(
+				dai_private->i2s->controller,
+				DISABLE_RECEIVE);
+		else
+			ret = i2s_cleanup(
+				dai_private->i2s->controller,
+				DISABLE_TRANSMIT);
+
+		if (ret) {
+			pr_err("%s: Failed to close the i2s controller."
+				"MSP index:%d\n",
+				__func__,
+				(int)dai_private->i2s->chip_select);
+		}
+	}
+
+	spin_unlock_irqrestore(&dai_private->lock, flags);
+}
+
+static int ux500_msp_dai_startup(
+	struct snd_pcm_substream *substream,
+	struct snd_soc_dai *msp_dai)
+{
+	struct ux500_msp_dai_private_s *dai_private =
+		&ux500_msp_dai_private[msp_dai->id];
+
+	pr_info("%s: MSP Index: %d).\n",
+		__func__,
+		msp_dai->id);
+
+	msp_dai->private_data = dai_private;
+
+	if (dai_private->i2s == NULL) {
+		pr_err("%s: Error: MSP index: %d"
+			"i2sdrv.i2s == NULL\n",
+			__func__,
+			msp_dai->id);
+		return -1;
+	}
+
+	if (dai_private->i2s->controller == NULL) {
+		pr_err("%s: Error: MSP index: %d"
+			"i2sdrv.i2s->controller == NULL.\n",
+			__func__,
+			msp_dai->id);
+		return -1;
+	}
+
+	return 0;
+}
+
+static void ux500_msp_dai_compile_msp_config(
+	struct snd_pcm_substream *substream,
+    unsigned int fmt,
+    unsigned int rate,
+    struct msp_generic_config *msp_config)
+{
+	struct msp_protocol_desc *prot_desc;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	msp_config->input_clock_freq = UX500_MSP_INTERNAL_CLOCK_FREQ;
+	msp_config->rx_frame_sync_pol = RX_FIFO_SYNC_HI;
+	msp_config->tx_frame_sync_pol = TX_FIFO_SYNC_HI;
+	msp_config->rx_fifo_config = RX_FIFO_ENABLE;
+	msp_config->tx_fifo_config = TX_FIFO_ENABLE;
+	msp_config->spi_clk_mode = SPI_CLK_MODE_NORMAL;
+	msp_config->spi_burst_mode = 0;
+	msp_config->handler = ux500_pcm_dma_eot_handler;
+	msp_config->tx_callback_data =  substream;
+	msp_config->tx_data_enable = 0;
+	msp_config->rx_callback_data =  substream;
+	msp_config->loopback_enable = 0;
+	msp_config->def_elem_len = 1;
+	msp_config->direction = MSP_BOTH_T_R_MODE;
+	msp_config->frame_size = 0;
+	msp_config->data_size = MSP_DATA_SIZE_32BIT;
+	msp_config->work_mode = MSP_DMA_MODE;
+	msp_config->multichannel_configured = 0;
+	msp_config->multichannel_config.tx_multichannel_enable = 0;
+	msp_config->multichannel_config.rx_multichannel_enable = 0;
+
+	switch (fmt &
+		(SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+		pr_info("%s: SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS\n",
+			__func__);
+		msp_config->default_protocol_desc = 1;
+		msp_config->protocol = MSP_I2S_PROTOCOL;
+		msp_config->tx_clock_sel = TX_CLK_SEL_SRG;
+		msp_config->tx_frame_sync_sel = TX_SYNC_SRG_PROG;
+		msp_config->rx_clock_sel = RX_CLK_SEL_SRG;
+		msp_config->rx_frame_sync_sel = RX_SYNC_SRG;
+		msp_config->srg_clock_sel = 1 << SCKSEL_BIT;
+		msp_config->frame_freq = rate;
+		break;
+
+	default:
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+		pr_info("%s: SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM\n",
+			__func__);
+		msp_config->data_size = MSP_DATA_SIZE_16BIT;
+		msp_config->default_protocol_desc = 0;
+		msp_config->protocol = MSP_I2S_PROTOCOL;
+
+		prot_desc = &msp_config->protocol_desc;
+
+		prot_desc->rx_phase_mode = MSP_DUAL_PHASE;
+		prot_desc->tx_phase_mode = MSP_DUAL_PHASE;
+		prot_desc->rx_phase2_start_mode =
+			MSP_PHASE2_START_MODE_FRAME_SYNC;
+		prot_desc->tx_phase2_start_mode =
+			MSP_PHASE2_START_MODE_FRAME_SYNC;
+		prot_desc->rx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
+		prot_desc->tx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
+		prot_desc->rx_frame_length_1 = MSP_FRAME_LENGTH_1;
+		prot_desc->rx_frame_length_2 = MSP_FRAME_LENGTH_1;
+		prot_desc->tx_frame_length_1 = MSP_FRAME_LENGTH_1;
+		prot_desc->tx_frame_length_2 = MSP_FRAME_LENGTH_1;
+		prot_desc->rx_element_length_1 = MSP_ELEM_LENGTH_16;
+		prot_desc->rx_element_length_2 = MSP_ELEM_LENGTH_16;
+		prot_desc->tx_element_length_1 = MSP_ELEM_LENGTH_16;
+		prot_desc->tx_element_length_2 = MSP_ELEM_LENGTH_16;
+		prot_desc->rx_data_delay = MSP_DELAY_0;
+		prot_desc->tx_data_delay = MSP_DELAY_0;
+		prot_desc->rx_clock_pol = MSP_RISING_EDGE;
+		prot_desc->tx_clock_pol = MSP_RISING_EDGE;
+		prot_desc->rx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
+		prot_desc->tx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
+		prot_desc->rx_half_word_swap =
+			MSP_HWS_BYTE_SWAP_IN_EACH_HALF_WORD;
+		prot_desc->tx_half_word_swap = MSP_HWS_NO_SWAP;
+		prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
+		prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
+		prot_desc->spi_clk_mode = MSP_SPI_CLOCK_MODE_NON_SPI;
+		prot_desc->spi_burst_mode = MSP_SPI_BURST_MODE_DISABLE;
+		prot_desc->frame_sync_ignore = MSP_FRAME_SYNC_IGNORE;
+
+		msp_config->tx_clock_sel = 0;
+		msp_config->tx_frame_sync_sel = 0;
+		msp_config->rx_clock_sel = 0;
+		msp_config->rx_frame_sync_sel = 0;
+		msp_config->srg_clock_sel = 0;
+		msp_config->frame_freq = rate;
+		break;
+	case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
+		prot_desc = &msp_config->protocol_desc;
+		pr_info("%s: SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS\n",
+			__func__);
+		pr_info("%s: rate: %u channels: %d\n",
+			__func__,
+			rate,
+			runtime->channels);
+		msp_config->data_size = MSP_DATA_SIZE_16BIT;
+		msp_config->default_protocol_desc = 0;
+		msp_config->protocol = MSP_PCM_PROTOCOL;
+
+		prot_desc->rx_phase_mode = MSP_SINGLE_PHASE;
+		prot_desc->tx_phase_mode = MSP_SINGLE_PHASE;
+		prot_desc->rx_phase2_start_mode =
+			MSP_PHASE2_START_MODE_IMEDIATE;
+		prot_desc->tx_phase2_start_mode =
+			MSP_PHASE2_START_MODE_IMEDIATE;
+		prot_desc->rx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
+		prot_desc->tx_bit_transfer_format = MSP_BTF_MS_BIT_FIRST;
+		prot_desc->rx_frame_length_1 = MSP_FRAME_LENGTH_1;
+		prot_desc->rx_frame_length_2 = MSP_FRAME_LENGTH_1;
+		prot_desc->tx_frame_length_1 = MSP_FRAME_LENGTH_1;
+		prot_desc->tx_frame_length_2 = MSP_FRAME_LENGTH_1;
+		prot_desc->rx_element_length_1 = MSP_ELEM_LENGTH_16;
+		prot_desc->rx_element_length_2 = MSP_ELEM_LENGTH_16;
+		prot_desc->tx_element_length_1 = MSP_ELEM_LENGTH_16;
+		prot_desc->tx_element_length_2 = MSP_ELEM_LENGTH_16;
+		prot_desc->rx_data_delay = MSP_DELAY_0;
+		prot_desc->tx_data_delay = MSP_DELAY_0;
+		prot_desc->rx_clock_pol = MSP_FALLING_EDGE;
+		prot_desc->tx_clock_pol = MSP_FALLING_EDGE;
+		prot_desc->rx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
+		prot_desc->tx_frame_sync_pol = MSP_FRAME_SYNC_POL_ACTIVE_HIGH;
+		prot_desc->rx_half_word_swap = MSP_HWS_NO_SWAP;
+		prot_desc->tx_half_word_swap = MSP_HWS_NO_SWAP;
+		prot_desc->compression_mode = MSP_COMPRESS_MODE_LINEAR;
+		prot_desc->expansion_mode = MSP_EXPAND_MODE_LINEAR;
+		prot_desc->spi_clk_mode = MSP_SPI_CLOCK_MODE_NON_SPI;
+		prot_desc->spi_burst_mode = MSP_SPI_BURST_MODE_DISABLE;
+		prot_desc->frame_sync_ignore = MSP_FRAME_SYNC_IGNORE;
+
+		prot_desc->frame_width = 0;
+		switch (rate) {
+		case 8000:
+			prot_desc->frame_period = 249;
+			break;
+		case 16000:
+			prot_desc->frame_period = 124;
+			break;
+		case 44100:
+			prot_desc->frame_period = 63;
+			break;
+		case 48000:
+		default:
+			prot_desc->frame_period = 49;
+			break;
+		}
+		prot_desc->total_clocks_for_one_frame =
+			prot_desc->frame_period+1;
+
+		msp_config->tx_clock_sel = TX_CLK_SEL_SRG;
+		msp_config->tx_frame_sync_sel = TX_SYNC_SRG_PROG;
+		msp_config->rx_clock_sel = RX_CLK_SEL_SRG;
+		msp_config->rx_frame_sync_sel = RX_SYNC_SRG;
+		msp_config->srg_clock_sel = 1 << SCKSEL_BIT;
+		msp_config->frame_freq = rate;
+		break;
+	}
+}
+
+static int ux500_msp_dai_prepare(
+	struct snd_pcm_substream *substream,
+	struct snd_soc_dai *msp_dai)
+{
+	int ret = 0;
+	unsigned long flags_private;
+	struct ux500_msp_dai_private_s *dai_private = msp_dai->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msp_generic_config msp_config;
+
+	pr_info("%s: Enter (stream = %s, chip_select = %d).\n",
+		__func__,
+		substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			"SNDRV_PCM_STREAM_PLAYBACK" :
+			"SNDRV_PCM_STREAM_CAPTURE",
+		(int)dai_private->i2s->chip_select);
+
+	spin_lock_irqsave(&dai_private->lock, flags_private);
+
+	if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+		dai_private->rx_active) ||
+		(substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+		dai_private->tx_active)) {
+
+		if (dai_private->rate != runtime->rate) {
+			pr_err(
+				"%s: Rate mismatch. index:%d"
+				"rate:%d requested rate:%d\n",
+				__func__,
+				msp_dai->id,
+				dai_private->rate,
+				runtime->rate);
+			ret = -EBUSY;
+			goto cleanup;
+		}
+	} else if (!dai_private->rx_active && !dai_private->tx_active) {
+		ret = 0;
+		ux500_msp_dai_compile_msp_config(substream,
+			dai_private->fmt,
+			runtime->rate,
+			&msp_config);
+
+		ret = i2s_setup(dai_private->i2s->controller, &msp_config);
+		if (ret < 0) {
+			pr_err("%s: i2s_setup failed."
+				" MSP index: %d return: %d\n",
+				__func__,
+				msp_dai->id,
+				ret);
+			goto cleanup;
+		}
+		dai_private->rate = runtime->rate;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dai_private->tx_active = 1;
+	else
+		dai_private->rx_active = 1;
+
+cleanup:
+	spin_unlock_irqrestore(&dai_private->lock, flags_private);
+	return ret;
+}
+
+static int ux500_msp_dai_hw_params(
+	struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *msp_dai)
+{
+	struct ux500_msp_dai_private_s *dai_private = msp_dai->private_data;
+
+	pr_debug("%s: Enter stream: %s, MSP index: %d\n",
+			__func__,
+			substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+				"SNDRV_PCM_STREAM_PLAYBACK" :
+				"SNDRV_PCM_STREAM_CAPTURE",
+			(int)dai_private->i2s->chip_select);
+
+	switch (dai_private->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		if (params_channels(params) != 2) {
+			pr_debug("%s: MSP index: %d:"
+				"I2S format requires that channels: %d"
+				" is set to two.\n",
+				__func__,
+				msp_dai->id,
+				params_channels(params));
+			return -EINVAL;
+		}
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		if (params_channels(params) != 1) {
+			pr_debug("%s: MSP index: %d:"
+				" PCM format requires that channels: %d"
+				" is set to one.\n",
+				__func__,
+				msp_dai->id,
+				params_channels(params));
+			return -EINVAL;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int ux500_msp_dai_set_dai_fmt(
+	struct snd_soc_dai *msp_dai,
+	unsigned int fmt)
+{
+	struct ux500_msp_dai_private_s *dai_private =
+		msp_dai->private_data;
+
+	pr_debug("%s: MSP index: %d: Enter\n",
+		__func__,
+		(int)dai_private->i2s->chip_select);
+
+	switch (fmt &
+		(SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_MASTER_MASK)) {
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS:
+	case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM:
+	case SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBS_CFS:
+		break;
+
+	default:
+		pr_notice("Msp_dai: unsupported DAI format 0x%x\n",
+			fmt);
+		return -EINVAL;
+	}
+
+	dai_private->fmt = fmt;
+	return 0;
+}
+
+static int ux500_msp_dai_trigger(
+	struct snd_pcm_substream *substream,
+	int cmd,
+	struct snd_soc_dai *msp_dai)
+{
+	unsigned long flags;
+	int ret = 0;
+	struct ux500_msp_dai_private_s *dai_private =
+		msp_dai->private_data;
+
+	pr_debug("%s: Enter (stream = %s,"
+		" chip_select = %d).\n",
+		__func__,
+		substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			"SNDRV_PCM_STREAM_PLAYBACK" :
+			"SNDRV_PCM_STREAM_CAPTURE",
+		(int)dai_private->i2s->chip_select);
+
+	spin_lock_irqsave(&dai_private->lock, flags);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		ret = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ret = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		ret = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ret = 0;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	spin_unlock_irqrestore(&dai_private->lock, flags);
+	return ret;
+}
+
+struct snd_soc_dai ux500_msp_dai[UX500_NBR_OF_DAI] =
+{
+	{
+		.name = "ux500_i2s-0",
+		.id = 0,
+		.suspend = NULL,
+		.resume = NULL,
+		.playback = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.capture = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.ops = {
+			.set_sysclk = NULL,
+			.set_fmt = ux500_msp_dai_set_dai_fmt,
+			.startup = ux500_msp_dai_startup,
+			.shutdown = ux500_msp_dai_shutdown,
+			.prepare = ux500_msp_dai_prepare,
+			.trigger = ux500_msp_dai_trigger,
+			.hw_params = ux500_msp_dai_hw_params,
+		},
+		.private_data = &ux500_msp_dai_private[0],
+	},
+	{
+		.name = "ux500_i2s-1",
+		.id = 1,
+		.suspend = NULL,
+		.resume = NULL,
+		.playback = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.capture = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.ops = {
+			.set_sysclk = NULL,
+			.set_fmt = ux500_msp_dai_set_dai_fmt,
+			.startup = ux500_msp_dai_startup,
+			.shutdown = ux500_msp_dai_shutdown,
+			.prepare = ux500_msp_dai_prepare,
+			.trigger = ux500_msp_dai_trigger,
+			.hw_params = ux500_msp_dai_hw_params,
+		},
+		.private_data = &ux500_msp_dai_private[1],
+	},
+	{
+		.name = "ux500_i2s-2",
+		.id = 2,
+		.suspend = NULL,
+		.resume = NULL,
+		.playback = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.capture = {
+			.channels_min = UX500_MSP_MIN_CHANNELS,
+			.channels_max = UX500_MSP_MAX_CHANNELS,
+			.rates = UX500_I2S_RATES,
+			.formats = UX500_I2S_FORMATS,
+		},
+		.ops = {
+			.set_sysclk = NULL,
+			.set_fmt = ux500_msp_dai_set_dai_fmt,
+			.startup = ux500_msp_dai_startup,
+			.shutdown = ux500_msp_dai_shutdown,
+			.prepare = ux500_msp_dai_prepare,
+			.trigger = ux500_msp_dai_trigger,
+			.hw_params = ux500_msp_dai_hw_params,
+		},
+		.private_data = &ux500_msp_dai_private[2],
+	},
+};
+EXPORT_SYMBOL(ux500_msp_dai);
+
+static int __init ux500_msp_dai_init(void)
+{
+	int i;
+	int ret = 0;
+
+	pr_debug("%s: Enter.\n", __func__);
+
+	ret = i2s_register_driver(&i2sdrv_i2s);
+	if (ret < 0) {
+		pr_err("%s: Unable to register I2S-driver\n",
+			__func__);
+		return ret;
+	}
+
+	for (i = 0; i < UX500_NBR_OF_DAI; i++) {
+		pr_debug("%s: Register MSP dai %d.\n",
+			__func__,
+			i);
+		ret = snd_soc_register_dai(&ux500_msp_dai[i]);
+		if (ret < 0) {
+			pr_err("MOP500_AB3550:"
+				" Error: Failed to register MSP dai %d.\n",
+				i);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+module_init(ux500_msp_dai_init);
+
+static void __exit ux500_msp_dai_exit(void)
+{
+	int i;
+
+	pr_debug("%s: Enter.\n", __func__);
+
+	i2s_unregister_driver(&i2sdrv_i2s);
+
+	for (i = 0; i < UX500_NBR_OF_DAI; i++) {
+		pr_debug("%s: Un-register MSP dai %d.\n",
+			__func__,
+			i);
+		snd_soc_unregister_dai(&ux500_msp_dai[i]);
+	}
+}
+module_exit(ux500_msp_dai_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ux500/ux500_msp_dai.h b/sound/soc/ux500/ux500_msp_dai.h
new file mode 100755
index 0000000..082a4bb
--- /dev/null
+++ b/sound/soc/ux500/ux500_msp_dai.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ *         Roger Nilsson roger.xr.nilsson@stericsson.com
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 UX500_msp_dai_H
+#define UX500_msp_dai_H
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+
+#define UX500_NBR_OF_DAI 3
+
+#define UX500_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |	\
+			SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+#define UX500_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+#define UX500_MSP_INTERNAL_CLOCK_FREQ 40000000;
+#define UX500_MSP_MIN_CHANNELS		1
+#define UX500_MSP_MAX_CHANNELS 		2
+
+struct ux500_msp_dai_private_s {
+	spinlock_t lock;
+	struct i2s_device *i2s;
+	u8 rx_active;
+	u8 tx_active;
+	unsigned int fmt;
+	unsigned int rate;
+};
+
+extern struct snd_soc_dai ux500_msp_dai[UX500_NBR_OF_DAI];
+
+int ux500_msp_dai_i2s_send_data(void *data, size_t bytes, int dai_idx);
+int ux500_msp_dai_i2s_receive_data(void *data, size_t bytes, int dai_idx);
+
+#endif
diff --git a/sound/soc/ux500/ux500_pcm.c b/sound/soc/ux500/ux500_pcm.c
new file mode 100755
index 0000000..880ca87
--- /dev/null
+++ b/sound/soc/ux500/ux500_pcm.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ *         Roger Nilsson roger.xr.nilsson@stericsson.com
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 <asm/page.h>
+
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "ux500_pcm.h"
+#include "ux500_msp_dai.h"
+
+static struct snd_pcm_hardware ux500_pcm_hw_playback = {
+	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+			SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE),
+	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |
+			SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U16_BE,
+	.rates = SNDRV_PCM_RATE_KNOT,
+	.rate_min = UX500_PLATFORM_MIN_RATE_PLAYBACK,
+	.rate_max = UX500_PLATFORM_MAX_RATE_PLAYBACK,
+	.channels_min = UX500_PLATFORM_MIN_CHANNELS,
+	.channels_max = UX500_PLATFORM_MAX_CHANNELS,
+	.buffer_bytes_max = UX500_PLATFORM_BUFFER_SIZE,
+	.period_bytes_min = UX500_PLATFORM_MIN_PERIOD_BYTES,
+	.period_bytes_max = PAGE_SIZE,
+	.periods_min = UX500_PLATFORM_BUFFER_SIZE / PAGE_SIZE,
+	.periods_max = UX500_PLATFORM_BUFFER_SIZE /
+			UX500_PLATFORM_MIN_PERIOD_BYTES
+};
+
+static struct snd_pcm_hardware ux500_pcm_hw_capture = {
+	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+			SNDRV_PCM_INFO_RESUME | SNDRV_PCM_INFO_PAUSE),
+	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE |
+			SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U16_BE,
+	.rates = SNDRV_PCM_RATE_KNOT,
+	.rate_min = UX500_PLATFORM_MIN_RATE_CAPTURE,
+	.rate_max = UX500_PLATFORM_MAX_RATE_CAPTURE,
+	.channels_min = UX500_PLATFORM_MIN_CHANNELS,
+	.channels_max = UX500_PLATFORM_MAX_CHANNELS,
+	.buffer_bytes_max = UX500_PLATFORM_BUFFER_SIZE,
+	.period_bytes_min = UX500_PLATFORM_MIN_PERIOD_BYTES,
+	.period_bytes_max = PAGE_SIZE,
+	.periods_min = UX500_PLATFORM_BUFFER_SIZE / PAGE_SIZE,
+	.periods_max = UX500_PLATFORM_BUFFER_SIZE /
+			UX500_PLATFORM_MIN_PERIOD_BYTES
+};
+
+static void ux500_pcm_dma_start(struct snd_pcm_substream *substream)
+{
+	unsigned int offset, dma_size;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ux500_pcm_private_s *private = substream->runtime->private_data;
+
+	pr_debug("%s: Enter MSP Index: %d\n",
+			__func__,
+			private->msp_id);
+
+	dma_size = frames_to_bytes(runtime, runtime->period_size);
+	offset = dma_size * private->period;
+	private->old_offset = offset;
+	pr_debug("%s: address = %x  size = %d\n",
+			__func__,
+			(runtime->dma_addr + offset), dma_size);
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ux500_msp_dai_i2s_send_data(
+			(void *)(runtime->dma_addr + offset),
+			dma_size,
+			private->msp_id);
+	} else{
+		ux500_msp_dai_i2s_receive_data(
+			(void *)(runtime->dma_addr + offset),
+			dma_size,
+			private->msp_id);
+	}
+
+	private->period++;
+	private->period %= runtime->periods;
+	private->periods++;
+}
+
+static void ux500_pcm_dma_hw_free(struct device *dev,
+		struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dma_buffer *buf = runtime->dma_buffer_p;
+
+	if (runtime->dma_area == NULL)
+		return;
+
+	if (buf != &substream->dma_buffer) {
+		dma_free_coherent(
+			buf->dev.dev,
+			buf->bytes,
+			buf->area,
+			buf->addr);
+		kfree(runtime->dma_buffer_p);
+	}
+
+	snd_pcm_set_runtime_buffer(substream, NULL);
+}
+
+irqreturn_t ux500_pcm_dma_eot_handler(void *data, int irq)
+{
+	struct snd_pcm_substream *substream = data;
+	struct ux500_pcm_private_s *private =
+		substream->runtime->private_data;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	if (substream)
+		snd_pcm_period_elapsed(substream);
+	if (private->state == ALSA_STATE_PAUSED)
+		return IRQ_HANDLED;
+	if (private->active == 1)
+		ux500_pcm_dma_start(substream);
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(ux500_pcm_dma_eot_handler);
+
+static int ux500_pcm_open(struct snd_pcm_substream *substream)
+{
+	int stream_id = substream->pstr->stream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ux500_pcm_private_s *private;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	private = kzalloc(sizeof(struct ux500_pcm_private_s), GFP_KERNEL);
+	if (private == NULL)
+		return -ENOMEM;
+
+	init_MUTEX(&(private->alsa_sem));
+	init_completion(&(private->alsa_com));
+	private->state = ALSA_STATE_RUNNING;
+	private->active = 0;
+	private->msp_id = rtd->dai->cpu_dai->id;
+	runtime->private_data = private;
+
+	pr_debug("%s: Setting HW-config\n", __func__);
+	runtime->hw = (stream_id == SNDRV_PCM_STREAM_PLAYBACK) ?
+		ux500_pcm_hw_playback : ux500_pcm_hw_capture;
+
+	return 0;
+}
+
+static int ux500_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct ux500_pcm_private_s *private = substream->runtime->private_data;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	kfree(private);
+
+	return 0;
+}
+
+static int ux500_pcm_hw_params(
+	struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dma_buffer *buf = runtime->dma_buffer_p;
+	int ret = 0;
+	int size;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	size = params_buffer_bytes(hw_params);
+
+	if (buf) {
+		if (buf->bytes >= size)
+			goto out;
+		ux500_pcm_dma_hw_free(NULL, substream);
+	}
+
+	if (substream->dma_buffer.area != NULL &&
+		substream->dma_buffer.bytes >= size) {
+		buf = &substream->dma_buffer;
+	} else {
+		buf = kmalloc(sizeof(struct snd_dma_buffer), GFP_KERNEL);
+		if (!buf)
+			goto nomem;
+
+		buf->dev.type = SNDRV_DMA_TYPE_DEV;
+		buf->dev.dev = NULL;
+		buf->area = dma_alloc_coherent(
+			NULL,
+			size,
+			&buf->addr,
+			GFP_KERNEL);
+		buf->bytes = size;
+		buf->private_data = NULL;
+
+		if (!buf->area)
+			goto free;
+	}
+	snd_pcm_set_runtime_buffer(substream, buf);
+	ret = 1;
+ out:
+	runtime->dma_bytes = size;
+	return ret;
+
+ free:
+	kfree(buf);
+ nomem:
+	return -ENOMEM;
+}
+
+static int ux500_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	pr_debug("%s: Enter\n", __func__);
+
+	ux500_pcm_dma_hw_free(NULL, substream);
+
+	return 0;
+}
+
+static int ux500_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	pr_debug("%s: Enter\n", __func__);
+
+	return 0;
+}
+
+static int ux500_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct ux500_pcm_private_s *private = substream->runtime->private_data;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		pr_debug("%s: TRIGGER START\n", __func__);
+		if (private->active == 0) {
+			private->active = 1;
+			ux500_pcm_dma_start(substream);
+			break;
+		}
+		pr_debug("%s: Error: H/w is busy\n", __func__);
+		return -EINVAL;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		pr_debug("%s: SNDRV_PCM_TRIGGER_PAUSE\n", __func__);
+		if (private->state == ALSA_STATE_RUNNING)
+			private->state = ALSA_STATE_PAUSED;
+
+		break;
+
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		pr_debug("%s: SNDRV_PCM_TRIGGER_RESUME\n", __func__);
+		if (private->state == ALSA_STATE_PAUSED) {
+			private->state = ALSA_STATE_RUNNING;
+			ux500_pcm_dma_start(substream);
+		}
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
+		if (private->active == 1) {
+			private->active = 0;
+			private->period = 0;
+		}
+		break;
+
+	default:
+		pr_err("%s: Invalid command in pcm trigger\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t ux500_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	unsigned int offset;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ux500_pcm_private_s *private = substream->runtime->private_data;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	offset = bytes_to_frames(runtime, private->old_offset);
+	if (offset < 0 || private->old_offset < 0)
+		pr_debug("%s: Offset=%i %i\n",
+			__func__,
+			offset,
+			private->old_offset);
+
+	return offset;
+}
+
+static int ux500_pcm_mmap(struct snd_pcm_substream *substream,
+			 struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	pr_debug("%s: Enter.\n", __func__);
+
+	return dma_mmap_coherent(
+		NULL,
+		vma,
+		runtime->dma_area,
+		runtime->dma_addr,
+		runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops ux500_pcm_ops = {
+	.open		= ux500_pcm_open,
+	.close		= ux500_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= ux500_pcm_hw_params,
+	.hw_free	= ux500_pcm_hw_free,
+	.prepare	= ux500_pcm_prepare,
+	.trigger	= ux500_pcm_trigger,
+	.pointer	= ux500_pcm_pointer,
+	.mmap		= ux500_pcm_mmap
+};
+
+int ux500_pcm_new(
+		struct snd_card *card,
+		struct snd_soc_dai *dai,
+		struct snd_pcm *pcm)
+{
+	pr_debug("%s: pcm = %d\n", __func__, (int)pcm);
+
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "UX500_PCM");
+
+	pr_debug("%s: pcm->name = %s\n", __func__, pcm->name);
+
+	return 0;
+}
+
+static void ux500_pcm_free(struct snd_pcm *pcm)
+{
+	pr_debug("%s: Enter\n", __func__);
+}
+
+static int ux500_pcm_suspend(struct snd_soc_dai *dai)
+{
+	pr_debug("%s: Enter\n", __func__);
+
+	return 0;
+}
+
+static int ux500_pcm_resume(struct snd_soc_dai *dai)
+{
+	pr_debug("%s: Enter\n", __func__);
+
+	return 0;
+}
+
+struct snd_soc_platform ux500_soc_platform = {
+	.name           = "ux500-audio",
+	.pcm_ops        = &ux500_pcm_ops,
+	.pcm_new        = ux500_pcm_new,
+	.pcm_free       = ux500_pcm_free,
+	.suspend        = ux500_pcm_suspend,
+	.resume         = ux500_pcm_resume,
+};
+EXPORT_SYMBOL(ux500_soc_platform);
+
+static int __init ux500_pcm_init(void)
+{
+	int ret;
+
+	pr_debug("%s: Register platform\n", __func__);
+	ret = snd_soc_register_platform(&ux500_soc_platform);
+	if (ret < 0)
+		pr_debug("%s: Error: Failed to register platform\n",
+			__func__);
+
+	return 0;
+}
+module_init(ux500_pcm_init);
+
+static void __exit ux500_pcm_exit(void)
+{
+	snd_soc_unregister_platform(&ux500_soc_platform);
+}
+module_exit(ux500_pcm_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/ux500/ux500_pcm.h b/sound/soc/ux500/ux500_pcm.h
new file mode 100755
index 0000000..8320df7
--- /dev/null
+++ b/sound/soc/ux500/ux500_pcm.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Ola Lilja ola.o.lilja@stericsson.com,
+ *         Roger Nilsson roger.xr.nilsson@stericsson.com
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 UX500_PCM_H
+#define UX500_PCM_H
+
+#include <mach/msp.h>
+
+#define UX500_PLATFORM_BUFFER_SIZE 	(64*1024)
+
+#define UX500_PLATFORM_MIN_RATE_PLAYBACK	8000
+#define UX500_PLATFORM_MAX_RATE_PLAYBACK	48000
+#define UX500_PLATFORM_MIN_RATE_CAPTURE		8000
+#define UX500_PLATFORM_MAX_RATE_CAPTURE		48000
+#define UX500_PLATFORM_MIN_CHANNELS   		1
+#define UX500_PLATFORM_MAX_CHANNELS   		2
+#define UX500_PLATFORM_MIN_PERIOD_BYTES		128
+
+enum alsa_state {
+	ALSA_STATE_PAUSED,
+	ALSA_STATE_RUNNING
+};
+
+extern struct snd_soc_platform ux500_soc_platform;
+
+struct ux500_pcm_private_s {
+	int msp_id;
+	int stream_id;
+	int active;
+	int period;
+	int periods;
+	enum alsa_state state;
+	unsigned int old_offset;
+	struct semaphore alsa_sem;
+	struct completion alsa_com;
+};
+
+irqreturn_t ux500_pcm_dma_eot_handler(void *data, int irq);
+
+#endif