Merge branch 'for-2.6.32' into mxc

Conflicts:
	sound/soc/Makefile
diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c
index fbf5c93..586965f 100644
--- a/sound/aoa/fabrics/layout.c
+++ b/sound/aoa/fabrics/layout.c
@@ -1037,7 +1037,7 @@
 	}
 	ldev->selfptr_headphone.ptr = ldev;
 	ldev->selfptr_lineout.ptr = ldev;
-	sdev->ofdev.dev.driver_data = ldev;
+	dev_set_drvdata(&sdev->ofdev.dev, ldev);
 	list_add(&ldev->list, &layouts_list);
 	layouts_list_items++;
 
@@ -1081,7 +1081,7 @@
 
 static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
 {
-	struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+	struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
 	int i;
 
 	for (i=0; i<MAX_CODECS_PER_BUS; i++) {
@@ -1114,7 +1114,7 @@
 #ifdef CONFIG_PM
 static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state)
 {
-	struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+	struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
 
 	if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
 		ldev->gpio.methods->all_amps_off(&ldev->gpio);
@@ -1124,7 +1124,7 @@
 
 static int aoa_fabric_layout_resume(struct soundbus_dev *sdev)
 {
-	struct layout_dev *ldev = sdev->ofdev.dev.driver_data;
+	struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
 
 	if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
 		ldev->gpio.methods->all_amps_restore(&ldev->gpio);
diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c
index 418c84c..4e3b819 100644
--- a/sound/aoa/soundbus/i2sbus/core.c
+++ b/sound/aoa/soundbus/i2sbus/core.c
@@ -358,14 +358,14 @@
 		return -ENODEV;
 	}
 
-	dev->ofdev.dev.driver_data = control;
+	dev_set_drvdata(&dev->ofdev.dev, control);
 
 	return 0;
 }
 
 static int i2sbus_remove(struct macio_dev* dev)
 {
-	struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+	struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
 	struct i2sbus_dev *i2sdev, *tmp;
 
 	list_for_each_entry_safe(i2sdev, tmp, &control->list, item)
@@ -377,7 +377,7 @@
 #ifdef CONFIG_PM
 static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
 {
-	struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+	struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
 	struct codec_info_item *cii;
 	struct i2sbus_dev* i2sdev;
 	int err, ret = 0;
@@ -407,7 +407,7 @@
 
 static int i2sbus_resume(struct macio_dev* dev)
 {
-	struct i2sbus_control *control = dev->ofdev.dev.driver_data;
+	struct i2sbus_control *control = dev_get_drvdata(&dev->ofdev.dev);
 	struct codec_info_item *cii;
 	struct i2sbus_dev* i2sdev;
 	int err, ret = 0;
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index 5c48e36..dc78272 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -1089,7 +1089,7 @@
 		goto out;
 	}
 
-	aaci->base = ioremap(dev->res.start, SZ_4K);
+	aaci->base = ioremap(dev->res.start, resource_size(&dev->res));
 	if (!aaci->base) {
 		ret = -ENOMEM;
 		goto out;
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index c570ebd..6c00ea4 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -170,6 +170,13 @@
 	struct snd_ac97_bus *ac97_bus;
 	struct snd_ac97_template ac97_template;
 	int ret;
+	pxa2xx_audio_ops_t *pdata = dev->dev.platform_data;
+
+	if (dev->id >= 0) {
+		dev_err(&dev->dev, "PXA2xx has only one AC97 port.\n");
+		ret = -ENXIO;
+		goto err_dev;
+	}
 
 	ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
 			      THIS_MODULE, 0, &card);
@@ -200,6 +207,8 @@
 	snprintf(card->longname, sizeof(card->longname),
 		 "%s (%s)", dev->dev.driver->name, card->mixername);
 
+	if (pdata && pdata->codec_data)
+		snd_ac97_dev_add_pdata(ac97_bus->codec[0], pdata->codec_pdata);
 	snd_card_set_dev(card, &dev->dev);
 	ret = snd_card_register(card);
 	if (ret == 0) {
@@ -212,6 +221,7 @@
 err:
 	if (card)
 		snd_card_free(card);
+err_dev:
 	return ret;
 }
 
diff --git a/sound/arm/pxa2xx-pcm-lib.c b/sound/arm/pxa2xx-pcm-lib.c
index 108b643..6205f37 100644
--- a/sound/arm/pxa2xx-pcm-lib.c
+++ b/sound/arm/pxa2xx-pcm-lib.c
@@ -75,7 +75,7 @@
 {
 	struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
 
-	if (rtd && rtd->params)
+	if (rtd && rtd->params && rtd->params->drcmr)
 		*rtd->params->drcmr = 0;
 
 	snd_pcm_set_runtime_buffer(substream, NULL);
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index 7bbdda0..6061fb5 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -205,3 +205,5 @@
 
 config SND_VMASTER
 	bool
+
+source "sound/core/seq/Kconfig"
diff --git a/sound/core/init.c b/sound/core/init.c
index fd56afe..d5d40d7 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -152,15 +152,8 @@
 	card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
 	if (!card)
 		return -ENOMEM;
-	if (xid) {
-		if (!snd_info_check_reserved_words(xid)) {
-			snd_printk(KERN_ERR
-				   "given id string '%s' is reserved.\n", xid);
-			err = -EBUSY;
-			goto __error;
-		}
+	if (xid)
 		strlcpy(card->id, xid, sizeof(card->id));
-	}
 	err = 0;
 	mutex_lock(&snd_card_mutex);
 	if (idx < 0) {
@@ -483,22 +476,28 @@
 
 EXPORT_SYMBOL(snd_card_free);
 
-static void choose_default_id(struct snd_card *card)
+static void snd_card_set_id_no_lock(struct snd_card *card, const char *nid)
 {
 	int i, len, idx_flag = 0, loops = SNDRV_CARDS;
-	char *id, *spos;
+	const char *spos, *src;
+	char *id;
 	
-	id = spos = card->shortname;	
-	while (*id != '\0') {
-		if (*id == ' ')
-			spos = id + 1;
-		id++;
+	if (nid == NULL) {
+		id = card->shortname;
+		spos = src = id;
+		while (*id != '\0') {
+			if (*id == ' ')
+				spos = id + 1;
+			id++;
+		}
+	} else {
+		spos = src = nid;
 	}
 	id = card->id;
 	while (*spos != '\0' && !isalnum(*spos))
 		spos++;
 	if (isdigit(*spos))
-		*id++ = isalpha(card->shortname[0]) ? card->shortname[0] : 'D';
+		*id++ = isalpha(src[0]) ? src[0] : 'D';
 	while (*spos != '\0' && (size_t)(id - card->id) < sizeof(card->id) - 1) {
 		if (isalnum(*spos))
 			*id++ = *spos;
@@ -513,7 +512,7 @@
 
 	while (1) {
 	      	if (loops-- == 0) {
-      			snd_printk(KERN_ERR "unable to choose default card id (%s)\n", id);
+			snd_printk(KERN_ERR "unable to set card id (%s)\n", id);
       			strcpy(card->id, card->proc_root->name);
       			return;
       		}
@@ -539,14 +538,33 @@
 			spos = id + len - 2;
 			if ((size_t)len <= sizeof(card->id) - 2)
 				spos++;
-			*spos++ = '_';
-			*spos++ = '1';
-			*spos++ = '\0';
+			*(char *)spos++ = '_';
+			*(char *)spos++ = '1';
+			*(char *)spos++ = '\0';
 			idx_flag++;
 		}
 	}
 }
 
+/**
+ *  snd_card_set_id - set card identification name
+ *  @card: soundcard structure
+ *  @nid: new identification string
+ *
+ *  This function sets the card identification and checks for name
+ *  collisions.
+ */
+void snd_card_set_id(struct snd_card *card, const char *nid)
+{
+	/* check if user specified own card->id */
+	if (card->id[0] != '\0')
+		return;
+	mutex_lock(&snd_card_mutex);
+	snd_card_set_id_no_lock(card, nid);
+	mutex_unlock(&snd_card_mutex);
+}
+EXPORT_SYMBOL(snd_card_set_id);
+
 #ifndef CONFIG_SYSFS_DEPRECATED
 static ssize_t
 card_id_show_attr(struct device *dev,
@@ -640,8 +658,7 @@
 		mutex_unlock(&snd_card_mutex);
 		return 0;
 	}
-	if (card->id[0] == '\0')
-		choose_default_id(card);
+	snd_card_set_id_no_lock(card, card->id[0] == '\0' ? NULL : card->id);
 	snd_cards[card->number] = card;
 	mutex_unlock(&snd_card_mutex);
 	init_info_for_card(card);
diff --git a/sound/core/jack.c b/sound/core/jack.c
index d54d1a0..f705eec 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -63,7 +63,7 @@
 
 	/* Default to the sound card device. */
 	if (!jack->input_dev->dev.parent)
-		jack->input_dev->dev.parent = card->dev;
+		jack->input_dev->dev.parent = snd_card_get_device_link(card);
 
 	err = input_register_device(jack->input_dev);
 	if (err == 0)
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index dda000b..dbe406b 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -31,6 +31,7 @@
 #include <linux/time.h>
 #include <linux/vmalloc.h>
 #include <linux/moduleparam.h>
+#include <linux/math64.h>
 #include <linux/string.h>
 #include <sound/core.h>
 #include <sound/minors.h>
@@ -617,9 +618,7 @@
 #else
 	{
 		u64 bsize = (u64)runtime->oss.buffer_bytes * (u64)bytes;
-		u32 rem;
-		div64_32(&bsize, buffer_size, &rem);
-		return (long)bsize;
+		return div_u64(bsize, buffer_size);
 	}
 #endif
 }
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index d659995..333e4dd 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -22,6 +22,7 @@
 
 #include <linux/slab.h>
 #include <linux/time.h>
+#include <linux/math64.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/info.h>
@@ -126,24 +127,37 @@
 }
 
 #ifdef CONFIG_SND_PCM_XRUN_DEBUG
-#define xrun_debug(substream)	((substream)->pstr->xrun_debug)
+#define xrun_debug(substream, mask)	((substream)->pstr->xrun_debug & (mask))
 #else
-#define xrun_debug(substream)	0
+#define xrun_debug(substream, mask)	0
 #endif
 
-#define dump_stack_on_xrun(substream) do {	\
-		if (xrun_debug(substream) > 1)	\
-			dump_stack();		\
+#define dump_stack_on_xrun(substream) do {		\
+		if (xrun_debug(substream, 2))		\
+			dump_stack();			\
 	} while (0)
 
+static void pcm_debug_name(struct snd_pcm_substream *substream,
+			   char *name, size_t len)
+{
+	snprintf(name, len, "pcmC%dD%d%c:%d",
+		 substream->pcm->card->number,
+		 substream->pcm->device,
+		 substream->stream ? 'c' : 'p',
+		 substream->number);
+}
+
 static void xrun(struct snd_pcm_substream *substream)
 {
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+		snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
 	snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-	if (xrun_debug(substream)) {
-		snd_printd(KERN_DEBUG "XRUN: pcmC%dD%d%c\n",
-			   substream->pcm->card->number,
-			   substream->pcm->device,
-			   substream->stream ? 'c' : 'p');
+	if (xrun_debug(substream, 1)) {
+		char name[16];
+		pcm_debug_name(substream, name, sizeof(name));
+		snd_printd(KERN_DEBUG "XRUN: %s\n", name);
 		dump_stack_on_xrun(substream);
 	}
 }
@@ -154,16 +168,16 @@
 {
 	snd_pcm_uframes_t pos;
 
-	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
-		snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
 	pos = substream->ops->pointer(substream);
 	if (pos == SNDRV_PCM_POS_XRUN)
 		return pos; /* XRUN */
 	if (pos >= runtime->buffer_size) {
 		if (printk_ratelimit()) {
-			snd_printd(KERN_ERR  "BUG: stream = %i, pos = 0x%lx, "
+			char name[16];
+			pcm_debug_name(substream, name, sizeof(name));
+			snd_printd(KERN_ERR  "BUG: %s, pos = 0x%lx, "
 				   "buffer size = 0x%lx, period size = 0x%lx\n",
-				   substream->stream, pos, runtime->buffer_size,
+				   name, pos, runtime->buffer_size,
 				   runtime->period_size);
 		}
 		pos = 0;
@@ -197,7 +211,7 @@
 
 #define hw_ptr_error(substream, fmt, args...)				\
 	do {								\
-		if (xrun_debug(substream)) {				\
+		if (xrun_debug(substream, 1)) {				\
 			if (printk_ratelimit()) {			\
 				snd_printd("PCM: " fmt, ##args);	\
 			}						\
@@ -251,7 +265,7 @@
 	}
 
 	/* Do jiffies check only in xrun_debug mode */
-	if (!xrun_debug(substream))
+	if (!xrun_debug(substream, 4))
 		goto no_jiffies_check;
 
 	/* Skip the jiffies check for hardwares with BATCH flag.
@@ -261,6 +275,9 @@
 	if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)
 		goto no_jiffies_check;
 	hdelta = new_hw_ptr - old_hw_ptr;
+	if (hdelta < runtime->delay)
+		goto no_jiffies_check;
+	hdelta -= runtime->delay;
 	jdelta = jiffies - runtime->hw_ptr_jiffies;
 	if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
 		delta = jdelta /
@@ -294,14 +311,20 @@
 		hw_ptr_interrupt =
 			new_hw_ptr - new_hw_ptr % runtime->period_size;
 	}
+	runtime->hw_ptr_interrupt = hw_ptr_interrupt;
+
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
 	    runtime->silence_size > 0)
 		snd_pcm_playback_silence(substream, new_hw_ptr);
 
+	if (runtime->status->hw_ptr == new_hw_ptr)
+		return 0;
+
 	runtime->hw_ptr_base = hw_base;
 	runtime->status->hw_ptr = new_hw_ptr;
 	runtime->hw_ptr_jiffies = jiffies;
-	runtime->hw_ptr_interrupt = hw_ptr_interrupt;
+	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+		snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
 
 	return snd_pcm_update_hw_ptr_post(substream, runtime);
 }
@@ -342,8 +365,12 @@
 		new_hw_ptr = hw_base + pos;
 	}
 	/* Do jiffies check only in xrun_debug mode */
-	if (xrun_debug(substream) &&
-	    ((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
+	if (!xrun_debug(substream, 4))
+		goto no_jiffies_check;
+	if (delta < runtime->delay)
+		goto no_jiffies_check;
+	delta -= runtime->delay;
+	if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
 		hw_ptr_error(substream,
 			     "hw_ptr skipping! "
 			     "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",
@@ -352,13 +379,19 @@
 			     ((delta * HZ) / runtime->rate));
 		return 0;
 	}
+ no_jiffies_check:
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
 	    runtime->silence_size > 0)
 		snd_pcm_playback_silence(substream, new_hw_ptr);
 
+	if (runtime->status->hw_ptr == new_hw_ptr)
+		return 0;
+
 	runtime->hw_ptr_base = hw_base;
 	runtime->status->hw_ptr = new_hw_ptr;
 	runtime->hw_ptr_jiffies = jiffies;
+	if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+		snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
 
 	return snd_pcm_update_hw_ptr_post(substream, runtime);
 }
@@ -452,7 +485,7 @@
 		*r = 0;
 		return UINT_MAX;
 	}
-	div64_32(&n, c, r);
+	n = div_u64_rem(n, c, r);
 	if (n >= UINT_MAX) {
 		*r = 0;
 		return UINT_MAX;
@@ -1524,6 +1557,23 @@
 	return 0;
 }
 
+static int snd_pcm_lib_ioctl_fifo_size(struct snd_pcm_substream *substream,
+				       void *arg)
+{
+	struct snd_pcm_hw_params *params = arg;
+	snd_pcm_format_t format;
+	int channels, width;
+
+	params->fifo_size = substream->runtime->hw.fifo_size;
+	if (!(substream->runtime->hw.info & SNDRV_PCM_INFO_FIFO_IN_FRAMES)) {
+		format = params_format(params);
+		channels = params_channels(params);
+		width = snd_pcm_format_physical_width(format);
+		params->fifo_size /= width * channels;
+	}
+	return 0;
+}
+
 /**
  * snd_pcm_lib_ioctl - a generic PCM ioctl callback
  * @substream: the pcm substream instance
@@ -1545,6 +1595,8 @@
 		return snd_pcm_lib_ioctl_reset(substream, arg);
 	case SNDRV_PCM_IOCTL1_CHANNEL_INFO:
 		return snd_pcm_lib_ioctl_channel_info(substream, arg);
+	case SNDRV_PCM_IOCTL1_FIFO_SIZE:
+		return snd_pcm_lib_ioctl_fifo_size(substream, arg);
 	}
 	return -ENXIO;
 }
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index b5da656..ac2150e 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -312,9 +312,18 @@
 
 	hw = &substream->runtime->hw;
 	if (!params->info)
-		params->info = hw->info;
-	if (!params->fifo_size)
-		params->fifo_size = hw->fifo_size;
+		params->info = hw->info & ~SNDRV_PCM_INFO_FIFO_IN_FRAMES;
+	if (!params->fifo_size) {
+		if (snd_mask_min(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT]) ==
+		    snd_mask_max(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT]) &&
+                    snd_mask_min(&params->masks[SNDRV_PCM_HW_PARAM_CHANNELS]) ==
+                    snd_mask_max(&params->masks[SNDRV_PCM_HW_PARAM_CHANNELS])) {
+			changed = substream->ops->ioctl(substream,
+					SNDRV_PCM_IOCTL1_FIFO_SIZE, params);
+			if (changed < 0)
+				return changed;
+		}
+	}
 	params->rmask = 0;
 	return 0;
 }
@@ -587,14 +596,15 @@
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		status->avail = snd_pcm_playback_avail(runtime);
 		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING ||
-		    runtime->status->state == SNDRV_PCM_STATE_DRAINING)
+		    runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
 			status->delay = runtime->buffer_size - status->avail;
-		else
+			status->delay += runtime->delay;
+		} else
 			status->delay = 0;
 	} else {
 		status->avail = snd_pcm_capture_avail(runtime);
 		if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
-			status->delay = status->avail;
+			status->delay = status->avail + runtime->delay;
 		else
 			status->delay = 0;
 	}
@@ -2410,6 +2420,7 @@
 			n = snd_pcm_playback_hw_avail(runtime);
 		else
 			n = snd_pcm_capture_avail(runtime);
+		n += runtime->delay;
 		break;
 	case SNDRV_PCM_STATE_XRUN:
 		err = -EPIPE;
diff --git a/sound/core/seq/Kconfig b/sound/core/seq/Kconfig
new file mode 100644
index 0000000..b851fd8
--- /dev/null
+++ b/sound/core/seq/Kconfig
@@ -0,0 +1,16 @@
+# define SND_XXX_SEQ to min(SND_SEQUENCER,SND_XXX)
+
+config SND_RAWMIDI_SEQ
+	def_tristate SND_SEQUENCER && SND_RAWMIDI
+
+config SND_OPL3_LIB_SEQ
+	def_tristate SND_SEQUENCER && SND_OPL3_LIB
+
+config SND_OPL4_LIB_SEQ
+	def_tristate SND_SEQUENCER && SND_OPL4_LIB
+
+config SND_SBAWE_SEQ
+	def_tristate SND_SEQUENCER && SND_SBAWE
+
+config SND_EMU10K1_SEQ
+	def_tristate SND_SEQUENCER && SND_EMU10K1
diff --git a/sound/core/seq/Makefile b/sound/core/seq/Makefile
index 0695937..1bcb360 100644
--- a/sound/core/seq/Makefile
+++ b/sound/core/seq/Makefile
@@ -17,14 +17,6 @@
 snd-seq-dummy-objs := seq_dummy.o
 snd-seq-virmidi-objs := seq_virmidi.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 obj-$(CONFIG_SND_SEQUENCER) += snd-seq.o snd-seq-device.o
 ifeq ($(CONFIG_SND_SEQUENCER_OSS),y)
 obj-$(CONFIG_SND_SEQUENCER) += snd-seq-midi-event.o
@@ -33,8 +25,8 @@
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_VIRMIDI) += snd-seq-virmidi.o snd-seq-midi-event.o
-obj-$(call sequencer,$(CONFIG_SND_RAWMIDI)) += snd-seq-midi.o snd-seq-midi-event.o
-obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
-obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-seq-midi-event.o snd-seq-midi-emul.o
-obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-seq-midi-emul.o snd-seq-virmidi.o
-obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-seq-midi-emul.o snd-seq-virmidi.o
+obj-$(CONFIG_SND_RAWMIDI_SEQ) += snd-seq-midi.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
+obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-seq-midi-event.o snd-seq-midi-emul.o
+obj-$(CONFIG_SND_SBAWE_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
+obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-seq-midi-emul.o snd-seq-virmidi.o
diff --git a/sound/core/seq/seq_midi_event.c b/sound/core/seq/seq_midi_event.c
index 8284f17..b5d6ea4 100644
--- a/sound/core/seq/seq_midi_event.c
+++ b/sound/core/seq/seq_midi_event.c
@@ -504,10 +504,10 @@
 	if (dev->nostat && count < 12)
 		return -ENOMEM;
 	cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f);
-	bytes[0] = ev->data.control.param & 0x007f;
-	bytes[1] = (ev->data.control.param & 0x3f80) >> 7;
-	bytes[2] = ev->data.control.value & 0x007f;
-	bytes[3] = (ev->data.control.value & 0x3f80) >> 7;
+	bytes[0] = (ev->data.control.param & 0x3f80) >> 7;
+	bytes[1] = ev->data.control.param & 0x007f;
+	bytes[2] = (ev->data.control.value & 0x3f80) >> 7;
+	bytes[3] = ev->data.control.value & 0x007f;
 	if (cmd != dev->lastcmd && !dev->nostat) {
 		if (count < 9)
 			return -ENOMEM;
diff --git a/sound/drivers/opl3/Makefile b/sound/drivers/opl3/Makefile
index 19767a6..7f2c2a1 100644
--- a/sound/drivers/opl3/Makefile
+++ b/sound/drivers/opl3/Makefile
@@ -7,14 +7,6 @@
 snd-opl3-synth-y := opl3_seq.o opl3_midi.o opl3_drums.o
 snd-opl3-synth-$(CONFIG_SND_SEQUENCER_OSS) += opl3_oss.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 obj-$(CONFIG_SND_OPL3_LIB) += snd-opl3-lib.o
 obj-$(CONFIG_SND_OPL4_LIB) += snd-opl3-lib.o
-obj-$(call sequencer,$(CONFIG_SND_OPL3_LIB)) += snd-opl3-synth.o
+obj-$(CONFIG_SND_OPL3_LIB_SEQ) += snd-opl3-synth.o
diff --git a/sound/drivers/opl4/Makefile b/sound/drivers/opl4/Makefile
index d178b39..b94009b 100644
--- a/sound/drivers/opl4/Makefile
+++ b/sound/drivers/opl4/Makefile
@@ -6,13 +6,5 @@
 snd-opl4-lib-objs := opl4_lib.o opl4_mixer.o opl4_proc.o
 snd-opl4-synth-objs := opl4_seq.o opl4_synth.o yrw801.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 obj-$(CONFIG_SND_OPL4_LIB) += snd-opl4-lib.o
-obj-$(call sequencer,$(CONFIG_SND_OPL4_LIB)) += snd-opl4-synth.o
+obj-$(CONFIG_SND_OPL4_LIB_SEQ) += snd-opl4-synth.o
diff --git a/sound/drivers/pcsp/pcsp.h b/sound/drivers/pcsp/pcsp.h
index cdef266..174dd2f 100644
--- a/sound/drivers/pcsp/pcsp.h
+++ b/sound/drivers/pcsp/pcsp.h
@@ -10,6 +10,7 @@
 #define __PCSP_H__
 
 #include <linux/hrtimer.h>
+#include <linux/timex.h>
 #if defined(CONFIG_MIPS) || defined(CONFIG_X86)
 /* Use the global PIT lock ! */
 #include <asm/i8253.h>
diff --git a/sound/isa/Kconfig b/sound/isa/Kconfig
index c6942a4..51a7e37 100644
--- a/sound/isa/Kconfig
+++ b/sound/isa/Kconfig
@@ -177,15 +177,18 @@
 	  will be called snd-es18xx.
 
 config SND_SC6000
-	tristate "Gallant SC-6000, Audio Excel DSP 16"
+	tristate "Gallant SC-6000/6600/7000 and Audio Excel DSP 16"
 	depends on HAS_IOPORT
 	select SND_WSS_LIB
 	select SND_OPL3_LIB
 	select SND_MPU401_UART
 	help
-	  Say Y here to include support for Gallant SC-6000 card and clones:
+	  Say Y here to include support for Gallant SC-6000, SC-6600, SC-7000
+	  cards and clones:
 	  Audio Excel DSP 16 and Zoltrix AV302.
 
+	  These cards are based on CompuMedia ASC-9308 or ASC-9408 chips.
+
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-sc6000.
 
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
index 442b081..07df201 100644
--- a/sound/isa/es1688/es1688.c
+++ b/sound/isa/es1688/es1688.c
@@ -193,7 +193,7 @@
 static struct isa_driver snd_es1688_driver = {
 	.match		= snd_es1688_match,
 	.probe		= snd_es1688_probe,
-	.remove		= snd_es1688_remove,
+	.remove		= __devexit_p(snd_es1688_remove),
 #if 0	/* FIXME */
 	.suspend	= snd_es1688_suspend,
 	.resume		= snd_es1688_resume,
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
index 180a8de..65e4b18 100644
--- a/sound/isa/gus/gusextreme.c
+++ b/sound/isa/gus/gusextreme.c
@@ -348,7 +348,7 @@
 static struct isa_driver snd_gusextreme_driver = {
 	.match		= snd_gusextreme_match,
 	.probe		= snd_gusextreme_probe,
-	.remove		= snd_gusextreme_remove,
+	.remove		= __devexit_p(snd_gusextreme_remove),
 #if 0	/* FIXME */
 	.suspend	= snd_gusextreme_suspend,
 	.resume		= snd_gusextreme_resume,
diff --git a/sound/isa/sb/Makefile b/sound/isa/sb/Makefile
index 1098a56..faeffceb 100644
--- a/sound/isa/sb/Makefile
+++ b/sound/isa/sb/Makefile
@@ -13,14 +13,6 @@
 snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
 snd-es968-objs := es968.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
 obj-$(CONFIG_SND_SB16_DSP) += snd-sb16-dsp.o
@@ -33,4 +25,4 @@
   obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
   obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
 endif
-obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emu8000-synth.o
+obj-$(CONFIG_SND_SBAWE_SEQ) += snd-emu8000-synth.o
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index 7820106..9a8bbf6 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -2,6 +2,8 @@
  *  Driver for Gallant SC-6000 soundcard. This card is also known as
  *  Audio Excel DSP 16 or Zoltrix AV302.
  *  These cards use CompuMedia ASC-9308 chip + AD1848 codec.
+ *  SC-6600 and SC-7000 cards are also supported. They are based on
+ *  CompuMedia ASC-9408 chip and CS4231 codec.
  *
  *  Copyright (C) 2007 Krzysztof Helt <krzysztof.h1@wp.pl>
  *
@@ -54,6 +56,7 @@
 						/* 0x300, 0x310, 0x320, 0x330 */
 static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;	/* 5, 7, 9, 10, 0 */
 static int dma[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;	/* 0, 1, 3 */
+static bool joystick[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = false };
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for sc-6000 based soundcard.");
@@ -73,6 +76,8 @@
 MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver.");
 module_param_array(dma, int, NULL, 0444);
 MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver.");
+module_param_array(joystick, bool, NULL, 0444);
+MODULE_PARM_DESC(joystick, "Enable gameport.");
 
 /*
  * Commands of SC6000's DSP (SBPRO+special).
@@ -191,7 +196,7 @@
 	return val;
 }
 
-static __devinit int sc6000_wait_data(char __iomem *vport)
+static int sc6000_wait_data(char __iomem *vport)
 {
 	int loop = 1000;
 	unsigned char val = 0;
@@ -206,7 +211,7 @@
 	return -EAGAIN;
 }
 
-static __devinit int sc6000_read(char __iomem *vport)
+static int sc6000_read(char __iomem *vport)
 {
 	if (sc6000_wait_data(vport))
 		return -EBUSY;
@@ -215,7 +220,7 @@
 
 }
 
-static __devinit int sc6000_write(char __iomem *vport, int cmd)
+static int sc6000_write(char __iomem *vport, int cmd)
 {
 	unsigned char val;
 	int loop = 500000;
@@ -276,8 +281,33 @@
 }
 
 /* detection and initialization */
-static int __devinit sc6000_cfg_write(char __iomem *vport,
-				      unsigned char softcfg)
+static int __devinit sc6000_hw_cfg_write(char __iomem *vport, const int *cfg)
+{
+	if (sc6000_write(vport, COMMAND_6C) < 0) {
+		snd_printk(KERN_WARNING "CMD 0x%x: failed!\n", COMMAND_6C);
+		return -EIO;
+	}
+	if (sc6000_write(vport, COMMAND_5C) < 0) {
+		snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_5C);
+		return -EIO;
+	}
+	if (sc6000_write(vport, cfg[0]) < 0) {
+		snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[0]);
+		return -EIO;
+	}
+	if (sc6000_write(vport, cfg[1]) < 0) {
+		snd_printk(KERN_ERR "DATA 0x%x: failed!\n", cfg[1]);
+		return -EIO;
+	}
+	if (sc6000_write(vport, COMMAND_C5) < 0) {
+		snd_printk(KERN_ERR "CMD 0x%x: failed!\n", COMMAND_C5);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int sc6000_cfg_write(char __iomem *vport, unsigned char softcfg)
 {
 
 	if (sc6000_write(vport, WRITE_MDIRQ_CFG)) {
@@ -291,7 +321,7 @@
 	return 0;
 }
 
-static int __devinit sc6000_setup_board(char __iomem *vport, int config)
+static int sc6000_setup_board(char __iomem *vport, int config)
 {
 	int loop = 10;
 
@@ -334,16 +364,39 @@
 	return 0;
 }
 
-static int __devinit sc6000_init_board(char __iomem *vport, int irq, int dma,
-					char __iomem *vmss_port, int mpu_irq)
+static void __devinit sc6000_hw_cfg_encode(char __iomem *vport, int *cfg,
+					   long xport, long xmpu,
+					   long xmss_port, int joystick)
+{
+	cfg[0] = 0;
+	cfg[1] = 0;
+	if (xport == 0x240)
+		cfg[0] |= 1;
+	if (xmpu != SNDRV_AUTO_PORT) {
+		cfg[0] |= (xmpu & 0x30) >> 2;
+		cfg[1] |= 0x20;
+	}
+	if (xmss_port == 0xe80)
+		cfg[0] |= 0x10;
+	cfg[0] |= 0x40;		/* always set */
+	if (!joystick)
+		cfg[0] |= 0x02;
+	cfg[1] |= 0x80;		/* enable WSS system */
+	cfg[1] &= ~0x40;	/* disable IDE */
+	snd_printd("hw cfg %x, %x\n", cfg[0], cfg[1]);
+}
+
+static int __devinit sc6000_init_board(char __iomem *vport,
+					char __iomem *vmss_port, int dev)
 {
 	char answer[15];
 	char version[2];
-	int mss_config = sc6000_irq_to_softcfg(irq) |
-			 sc6000_dma_to_softcfg(dma);
+	int mss_config = sc6000_irq_to_softcfg(irq[dev]) |
+			 sc6000_dma_to_softcfg(dma[dev]);
 	int config = mss_config |
-		     sc6000_mpu_irq_to_softcfg(mpu_irq);
+		     sc6000_mpu_irq_to_softcfg(mpu_irq[dev]);
 	int err;
+	int old = 0;
 
 	err = sc6000_dsp_reset(vport);
 	if (err < 0) {
@@ -360,7 +413,6 @@
 	/*
 	 * My SC-6000 card return "SC-6000" in DSPCopyright, so
 	 * if we have something different, we have to be warned.
-	 * Mine returns "SC-6000A " - KH
 	 */
 	if (strncmp("SC-6000", answer, 7))
 		snd_printk(KERN_WARNING "Warning: non SC-6000 audio card!\n");
@@ -372,13 +424,32 @@
 	printk(KERN_INFO PFX "Detected model: %s, DSP version %d.%d\n",
 		answer, version[0], version[1]);
 
-	/*
-	 * 0x0A == (IRQ 7, DMA 1, MIRQ 0)
-	 */
-	err = sc6000_cfg_write(vport, 0x0a);
+	/* set configuration */
+	sc6000_write(vport, COMMAND_5C);
+	if (sc6000_read(vport) < 0)
+		old = 1;
+
+	if (!old) {
+		int cfg[2];
+		sc6000_hw_cfg_encode(vport, &cfg[0], port[dev], mpu_port[dev],
+				     mss_port[dev], joystick[dev]);
+		if (sc6000_hw_cfg_write(vport, cfg) < 0) {
+			snd_printk(KERN_ERR "sc6000_hw_cfg_write: failed!\n");
+			return -EIO;
+		}
+	}
+	err = sc6000_setup_board(vport, config);
 	if (err < 0) {
-		snd_printk(KERN_ERR "sc6000_cfg_write: failed!\n");
-		return -EFAULT;
+		snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
+		return -ENODEV;
+	}
+
+	sc6000_dsp_reset(vport);
+
+	if (!old) {
+		sc6000_write(vport, COMMAND_60);
+		sc6000_write(vport, 0x02);
+		sc6000_dsp_reset(vport);
 	}
 
 	err = sc6000_setup_board(vport, config);
@@ -386,10 +457,9 @@
 		snd_printk(KERN_ERR "sc6000_setup_board: failed!\n");
 		return -ENODEV;
 	}
-
 	err = sc6000_init_mss(vport, config, vmss_port, mss_config);
 	if (err < 0) {
-		snd_printk(KERN_ERR "Can not initialize "
+		snd_printk(KERN_ERR "Cannot initialize "
 			   "Microsoft Sound System mode.\n");
 		return -ENODEV;
 	}
@@ -485,14 +555,16 @@
 	struct snd_card *card;
 	struct snd_wss *chip;
 	struct snd_opl3 *opl3;
-	char __iomem *vport;
+	char __iomem **vport;
 	char __iomem *vmss_port;
 
 
-	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+	err = snd_card_create(index[dev], id[dev], THIS_MODULE, sizeof(vport),
+				&card);
 	if (err < 0)
 		return err;
 
+	vport = card->private_data;
 	if (xirq == SNDRV_AUTO_IRQ) {
 		xirq = snd_legacy_find_free_irq(possible_irqs);
 		if (xirq < 0) {
@@ -517,8 +589,8 @@
 		err = -EBUSY;
 		goto err_exit;
 	}
-	vport = devm_ioport_map(devptr, port[dev], 0x10);
-	if (!vport) {
+	*vport = devm_ioport_map(devptr, port[dev], 0x10);
+	if (*vport == NULL) {
 		snd_printk(KERN_ERR PFX
 			   "I/O port cannot be iomaped.\n");
 		err = -EBUSY;
@@ -533,7 +605,7 @@
 		goto err_unmap1;
 	}
 	vmss_port = devm_ioport_map(devptr, mss_port[dev], 4);
-	if (!vport) {
+	if (!vmss_port) {
 		snd_printk(KERN_ERR PFX
 			   "MSS port I/O cannot be iomaped.\n");
 		err = -EBUSY;
@@ -544,7 +616,7 @@
 		   port[dev], xirq, xdma,
 		   mpu_irq[dev] == SNDRV_AUTO_IRQ ? 0 : mpu_irq[dev]);
 
-	err = sc6000_init_board(vport, xirq, xdma, vmss_port, mpu_irq[dev]);
+	err = sc6000_init_board(*vport, vmss_port, dev);
 	if (err < 0)
 		goto err_unmap2;
 
@@ -552,7 +624,6 @@
 			     WSS_HW_DETECT, 0, &chip);
 	if (err < 0)
 		goto err_unmap2;
-	card->private_data = chip;
 
 	err = snd_wss_pcm(chip, 0, NULL);
 	if (err < 0) {
@@ -608,6 +679,7 @@
 	return 0;
 
 err_unmap2:
+	sc6000_setup_board(*vport, 0);
 	release_region(mss_port[dev], 4);
 err_unmap1:
 	release_region(port[dev], 0x10);
@@ -618,11 +690,17 @@
 
 static int __devexit snd_sc6000_remove(struct device *devptr, unsigned int dev)
 {
+	struct snd_card *card = dev_get_drvdata(devptr);
+	char __iomem **vport = card->private_data;
+
+	if (sc6000_setup_board(*vport, 0) < 0)
+		snd_printk(KERN_WARNING "sc6000_setup_board failed on exit!\n");
+
 	release_region(port[dev], 0x10);
 	release_region(mss_port[dev], 4);
 
-	snd_card_free(dev_get_drvdata(devptr));
 	dev_set_drvdata(devptr, NULL);
+	snd_card_free(card);
 	return 0;
 }
 
diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c
index 66f3b48..e497525 100644
--- a/sound/mips/sgio2audio.c
+++ b/sound/mips/sgio2audio.c
@@ -619,8 +619,7 @@
 /* hw_free callback */
 static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
 {
-	if (substream->runtime->dma_area)
-		vfree(substream->runtime->dma_area);
+	vfree(substream->runtime->dma_area);
 	substream->runtime->dma_area = NULL;
 	return 0;
 }
diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig
index 1ca7427..bcf2a06 100644
--- a/sound/oss/Kconfig
+++ b/sound/oss/Kconfig
@@ -561,7 +561,7 @@
 
 config SOUND_SH_DAC_AUDIO
 	tristate "SuperH DAC audio support"
-	depends on CPU_SH3
+	depends on CPU_SH3 && HIGH_RES_TIMERS
 
 config SOUND_SH_DAC_AUDIO_CHANNEL
 	int "DAC channel"
diff --git a/sound/oss/msnd.c b/sound/oss/msnd.c
index e4282d9..21eb6dc 100644
--- a/sound/oss/msnd.c
+++ b/sound/oss/msnd.c
@@ -100,7 +100,7 @@
 int msnd_fifo_alloc(msnd_fifo *f, size_t n)
 {
 	msnd_fifo_free(f);
-	f->data = (char *)vmalloc(n);
+	f->data = vmalloc(n);
 	f->n = n;
 	f->tail = 0;
 	f->head = 0;
diff --git a/sound/oss/pas2_pcm.c b/sound/oss/pas2_pcm.c
index 36c3ea6..8f7d175 100644
--- a/sound/oss/pas2_pcm.c
+++ b/sound/oss/pas2_pcm.c
@@ -17,7 +17,7 @@
 
 #include <linux/init.h>
 #include <linux/spinlock.h>
-#include <asm/timex.h>
+#include <linux/timex.h>
 #include "sound_config.h"
 
 #include "pas2.h"
diff --git a/sound/oss/sh_dac_audio.c b/sound/oss/sh_dac_audio.c
index 78cfb66..b2ed875 100644
--- a/sound/oss/sh_dac_audio.c
+++ b/sound/oss/sh_dac_audio.c
@@ -18,47 +18,36 @@
 #include <linux/sound.h>
 #include <linux/soundcard.h>
 #include <linux/interrupt.h>
+#include <linux/hrtimer.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/irq.h>
 #include <asm/delay.h>
 #include <asm/clock.h>
-#include <asm/cpu/dac.h>
-#include <asm/cpu/timer.h>
+#include <cpu/dac.h>
 #include <asm/machvec.h>
 #include <mach/hp6xx.h>
 #include <asm/hd64461.h>
 
 #define MODNAME "sh_dac_audio"
 
-#define TMU_TOCR_INIT	0x00
-
-#define TMU1_TCR_INIT	0x0020	/* Clock/4, rising edge; interrupt on */
-#define TMU1_TSTR_INIT  0x02	/* Bit to turn on TMU1 */
-
 #define BUFFER_SIZE 48000
 
 static int rate;
 static int empty;
 static char *data_buffer, *buffer_begin, *buffer_end;
 static int in_use, device_major;
+static struct hrtimer hrtimer;
+static ktime_t wakeups_per_second;
 
 static void dac_audio_start_timer(void)
 {
-	u8 tstr;
-
-	tstr = ctrl_inb(TMU_TSTR);
-	tstr |= TMU1_TSTR_INIT;
-	ctrl_outb(tstr, TMU_TSTR);
+	hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL);
 }
 
 static void dac_audio_stop_timer(void)
 {
-	u8 tstr;
-
-	tstr = ctrl_inb(TMU_TSTR);
-	tstr &= ~TMU1_TSTR_INIT;
-	ctrl_outb(tstr, TMU_TSTR);
+	hrtimer_cancel(&hrtimer);
 }
 
 static void dac_audio_reset(void)
@@ -77,38 +66,30 @@
 static void dac_audio_start(void)
 {
 	if (mach_is_hp6xx()) {
-		u16 v = inw(HD64461_GPADR);
+		u16 v = __raw_readw(HD64461_GPADR);
 		v &= ~HD64461_GPADR_SPEAKER;
-		outw(v, HD64461_GPADR);
+		__raw_writew(v, HD64461_GPADR);
 	}
 
 	sh_dac_enable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
-	ctrl_outw(TMU1_TCR_INIT, TMU1_TCR);
 }
 static void dac_audio_stop(void)
 {
 	dac_audio_stop_timer();
 
 	if (mach_is_hp6xx()) {
-		u16 v = inw(HD64461_GPADR);
+		u16 v = __raw_readw(HD64461_GPADR);
 		v |= HD64461_GPADR_SPEAKER;
-		outw(v, HD64461_GPADR);
+		__raw_writew(v, HD64461_GPADR);
 	}
 
- 	sh_dac_output(0, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
+	sh_dac_output(0, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
 	sh_dac_disable(CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
 }
 
 static void dac_audio_set_rate(void)
 {
-	unsigned long interval;
- 	struct clk *clk;
-
- 	clk = clk_get(NULL, "module_clk");
- 	interval = (clk_get_rate(clk) / 4) / rate;
- 	clk_put(clk);
-	ctrl_outl(interval, TMU1_TCOR);
-	ctrl_outl(interval, TMU1_TCNT);
+	wakeups_per_second = ktime_set(0, 1000000000 / rate);
 }
 
 static int dac_audio_ioctl(struct inode *inode, struct file *file,
@@ -265,32 +246,26 @@
       .release =	dac_audio_release,
 };
 
-static irqreturn_t timer1_interrupt(int irq, void *dev)
+static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
 {
-	unsigned long timer_status;
-
-	timer_status = ctrl_inw(TMU1_TCR);
-	timer_status &= ~0x100;
-	ctrl_outw(timer_status, TMU1_TCR);
-
 	if (!empty) {
 		sh_dac_output(*buffer_begin, CONFIG_SOUND_SH_DAC_AUDIO_CHANNEL);
 		buffer_begin++;
 
 		if (buffer_begin == data_buffer + BUFFER_SIZE)
 			buffer_begin = data_buffer;
-		if (buffer_begin == buffer_end) {
+		if (buffer_begin == buffer_end)
 			empty = 1;
-			dac_audio_stop_timer();
-		}
 	}
-	return IRQ_HANDLED;
+
+	if (!empty)
+		hrtimer_start(&hrtimer, wakeups_per_second, HRTIMER_MODE_REL);
+
+	return HRTIMER_NORESTART;
 }
 
 static int __init dac_audio_init(void)
 {
-	int retval;
-
 	if ((device_major = register_sound_dsp(&dac_audio_fops, -1)) < 0) {
 		printk(KERN_ERR "Cannot register dsp device");
 		return device_major;
@@ -306,21 +281,25 @@
 	rate = 8000;
 	dac_audio_set_rate();
 
-	retval =
-	    request_irq(TIMER1_IRQ, timer1_interrupt, IRQF_DISABLED, MODNAME, 0);
-	if (retval < 0) {
-		printk(KERN_ERR "sh_dac_audio: IRQ %d request failed\n",
-		       TIMER1_IRQ);
-		return retval;
-	}
+	/* Today: High Resolution Timer driven DAC playback.
+	 * The timer callback gets called once per sample. Ouch.
+	 *
+	 * Future: A much better approach would be to use the
+	 * SH7720 CMT+DMAC+DAC hardware combination like this:
+	 * - Program sample rate using CMT0 or CMT1
+	 * - Program DMAC to use CMT for timing and output to DAC
+	 * - Play sound using DMAC, let CPU sleep.
+	 * - While at it, rewrite this driver to use ALSA.
+	 */
+
+	hrtimer_init(&hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	hrtimer.function = sh_dac_audio_timer;
 
 	return 0;
 }
 
 static void __exit dac_audio_exit(void)
 {
-	free_irq(TIMER1_IRQ, 0);
-
 	unregister_sound_dsp(device_major);
 	kfree((void *)data_buffer);
 }
diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c
index 6055fd6..e924492 100644
--- a/sound/parisc/harmony.c
+++ b/sound/parisc/harmony.c
@@ -935,7 +935,7 @@
 	h->iobase = ioremap_nocache(padev->hpa.start, HARMONY_SIZE);
 	if (h->iobase == NULL) {
 		printk(KERN_ERR PFX "unable to remap hpa 0x%lx\n",
-		       padev->hpa.start);
+		       (unsigned long)padev->hpa.start);
 		err = -EBUSY;
 		goto free_and_ret;
 	}
@@ -1020,7 +1020,7 @@
 	.name = "harmony",
 	.id_table = snd_harmony_devtable,
 	.probe = snd_harmony_probe,
-	.remove = snd_harmony_remove,
+	.remove = __devexit_p(snd_harmony_remove),
 };
 
 static int __init 
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 93422e3..748f6b7 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -275,6 +275,16 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-cs5535audio.
 
+config SND_CTXFI
+	tristate "Creative Sound Blaster X-Fi"
+	select SND_PCM
+	help
+	  If you want to use soundcards based on Creative Sound Blastr X-Fi
+	  boards with 20k1 or 20k2 chips, say Y here.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-ctxfi.
+
 config SND_DARLA20
 	tristate "(Echoaudio) Darla20"
 	select FW_LOADER
@@ -532,6 +542,9 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-hdsp.
 
+comment "Don't forget to add built-in firmwares for HDSP driver"
+	depends on SND_HDSP=y
+
 config SND_HDSPM
 	tristate "RME Hammerfall DSP MADI"
 	select SND_HWDEP
@@ -622,6 +635,16 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-korg1212.
 
+config SND_LX6464ES
+	tristate "Digigram LX6464ES"
+	select SND_PCM
+	help
+	  Say Y here to include support for Digigram LX6464ES boards.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-lx6464es.
+
+
 config SND_MAESTRO3
 	tristate "ESS Allegro/Maestro3"
 	select SND_AC97_CODEC
@@ -764,8 +787,8 @@
 	select SND_OXYGEN_LIB
 	help
 	  Say Y here to include support for sound cards based on the
-	  Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, and
-	  Essence STX.
+	  Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X,
+	  Essence ST (Deluxe), and Essence STX.
 	  Support for the HDAV1.3 (Deluxe) is very experimental.
 
 	  To compile this driver as a module, choose M here: the module
diff --git a/sound/pci/Makefile b/sound/pci/Makefile
index 65b25d2..ecfc609 100644
--- a/sound/pci/Makefile
+++ b/sound/pci/Makefile
@@ -59,9 +59,11 @@
 	ali5451/ \
 	au88x0/ \
 	aw2/ \
+	ctxfi/ \
 	ca0106/ \
 	cs46xx/ \
 	cs5535audio/ \
+	lx6464es/ \
 	echoaudio/ \
 	emu10k1/ \
 	hda/ \
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 3906f5a..23f49f3 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -1255,8 +1255,8 @@
 	int temp;
 
 	temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2));
-	temp = (dma->period_virt * dma->period_bytes) + (temp & POS_MASK);
-	return (temp);
+	temp = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1));
+	return temp;
 }
 
 static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma)
@@ -1504,8 +1504,7 @@
 	int temp;
 
 	temp = hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2));
-	//temp = (temp & POS_MASK) + (((temp>>WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK)*(dma->cfg0&POS_MASK));
-	temp = (temp & POS_MASK) + ((dma->period_virt) * (dma->period_bytes));
+	temp = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1));
 	return temp;
 }
 
@@ -2441,7 +2440,8 @@
 		spin_lock(&vortex->lock);
 		for (i = 0; i < NR_ADB; i++) {
 			if (vortex->dma_adb[i].fifo_status == FIFO_START) {
-				if (vortex_adbdma_bufshift(vortex, i)) ;
+				if (!vortex_adbdma_bufshift(vortex, i))
+					continue;
 				spin_unlock(&vortex->lock);
 				snd_pcm_period_elapsed(vortex->dma_adb[i].
 						       substream);
diff --git a/sound/pci/aw2/aw2-saa7146.c b/sound/pci/aw2/aw2-saa7146.c
index 6a3891a..296123a 100644
--- a/sound/pci/aw2/aw2-saa7146.c
+++ b/sound/pci/aw2/aw2-saa7146.c
@@ -108,7 +108,7 @@
 #endif
 	/* WS0_CTRL, WS0_SYNC: input TSL1, I2S */
 
-	/* At initialization WS1 and WS2 are disbaled (configured as input */
+	/* At initialization WS1 and WS2 are disabled (configured as input) */
 	acon1 |= 0 * WS1_CTRL;
 	acon1 |= 0 * WS2_CTRL;
 
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index ce3f2e9..24585c6 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -810,6 +810,8 @@
 	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x107d, 0x6606, GENERIC),
 	/* Voodoo TV 200 */
 	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x121a, 0x3000, GENERIC),
+	/* Askey Computer Corp. MagicTView'99 */
+	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x144f, 0x3000, GENERIC),
 	/* AVerMedia Studio No. 103, 203, ...? */
 	BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x1461, 0x0003, AVPHONE98),
 	/* Prolink PixelView PV-M4900 */
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index bfac30f..57b992a 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1319,7 +1319,6 @@
         }
 
 	pcm->info_flags = 0;
-	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
 	strcpy(pcm->name, "CA0106");
 
 	for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index c111efe..c8c6f43 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -739,7 +739,7 @@
 	} while (0)
 
 static __devinitdata
-DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 50, 1);
+DECLARE_TLV_DB_SCALE(snd_ca0106_master_db_scale, -6375, 25, 1);
 
 static char *slave_vols[] __devinitdata = {
 	"Analog Front Playback Volume",
@@ -841,6 +841,9 @@
 					      snd_ca0106_master_db_scale);
 	if (!vmaster)
 		return -ENOMEM;
+	err = snd_ctl_add(card, vmaster);
+	if (err < 0)
+		return err;
 	add_slaves(card, vmaster, slave_vols);
 
 	if (emu->details->spi_dac == 1) {
@@ -848,8 +851,13 @@
 						      NULL);
 		if (!vmaster)
 			return -ENOMEM;
+		err = snd_ctl_add(card, vmaster);
+		if (err < 0)
+			return err;
 		add_slaves(card, vmaster, slave_sws);
 	}
+
+	strcpy(card->mixername, "CA0106");
         return 0;
 }
 
diff --git a/sound/pci/ctxfi/Makefile b/sound/pci/ctxfi/Makefile
new file mode 100644
index 0000000..15075f8
--- /dev/null
+++ b/sound/pci/ctxfi/Makefile
@@ -0,0 +1,5 @@
+snd-ctxfi-objs := xfi.o ctatc.o ctvmem.o ctpcm.o ctmixer.o ctresource.o \
+	ctsrc.o ctamixer.o ctdaio.o ctimap.o cthardware.o cttimer.o \
+	cthw20k2.o cthw20k1.o
+
+obj-$(CONFIG_SND_CTXFI) += snd-ctxfi.o
diff --git a/sound/pci/ctxfi/ct20k1reg.h b/sound/pci/ctxfi/ct20k1reg.h
new file mode 100644
index 0000000..f2e34e3
--- /dev/null
+++ b/sound/pci/ctxfi/ct20k1reg.h
@@ -0,0 +1,636 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#ifndef CT20K1REG_H
+#define CT20k1REG_H
+
+/* 20k1 registers */
+#define 	DSPXRAM_START 			0x000000
+#define 	DSPXRAM_END 			0x013FFC
+#define 	DSPAXRAM_START 			0x020000
+#define 	DSPAXRAM_END 			0x023FFC
+#define 	DSPYRAM_START 			0x040000
+#define 	DSPYRAM_END 			0x04FFFC
+#define 	DSPAYRAM_START 			0x020000
+#define 	DSPAYRAM_END 			0x063FFC
+#define 	DSPMICRO_START 			0x080000
+#define 	DSPMICRO_END 			0x0B3FFC
+#define 	DSP0IO_START 			0x100000
+#define 	DSP0IO_END	 		0x101FFC
+#define 	AUDIORINGIPDSP0_START 		0x100000
+#define 	AUDIORINGIPDSP0_END 		0x1003FC
+#define 	AUDIORINGOPDSP0_START 		0x100400
+#define 	AUDIORINGOPDSP0_END 		0x1007FC
+#define 	AUDPARARINGIODSP0_START 	0x100800
+#define 	AUDPARARINGIODSP0_END	 	0x100BFC
+#define 	DSP0LOCALHWREG_START 		0x100C00
+#define 	DSP0LOCALHWREG_END	 	0x100C3C
+#define 	DSP0XYRAMAGINDEX_START 		0x100C40
+#define 	DSP0XYRAMAGINDEX_END	 	0x100C5C
+#define 	DSP0XYRAMAGMDFR_START 		0x100C60
+#define 	DSP0XYRAMAGMDFR_END	 	0x100C7C
+#define 	DSP0INTCONTLVEC_START 		0x100C80
+#define 	DSP0INTCONTLVEC_END	 	0x100CD8
+#define 	INTCONTLGLOBALREG_START 	0x100D1C
+#define 	INTCONTLGLOBALREG_END	 	0x100D3C
+#define		HOSTINTFPORTADDRCONTDSP0	0x100D40
+#define		HOSTINTFPORTDATADSP0		0x100D44
+#define		TIME0PERENBDSP0			0x100D60
+#define		TIME0COUNTERDSP0		0x100D64
+#define		TIME1PERENBDSP0			0x100D68
+#define		TIME1COUNTERDSP0		0x100D6C
+#define		TIME2PERENBDSP0			0x100D70
+#define		TIME2COUNTERDSP0		0x100D74
+#define		TIME3PERENBDSP0			0x100D78
+#define		TIME3COUNTERDSP0		0x100D7C
+#define 	XRAMINDOPERREFNOUP_STARTDSP0 	0x100D80
+#define 	XRAMINDOPERREFNOUP_ENDDSP0	0x100D9C
+#define 	XRAMINDOPERREFUP_STARTDSP0	0x100DA0
+#define 	XRAMINDOPERREFUP_ENDDSP0	0x100DBC
+#define 	YRAMINDOPERREFNOUP_STARTDSP0 	0x100DC0
+#define 	YRAMINDOPERREFNOUP_ENDDSP0 	0x100DDC
+#define 	YRAMINDOPERREFUP_STARTDSP0	0x100DE0
+#define 	YRAMINDOPERREFUP_ENDDSP0 	0x100DFC
+#define 	DSP0CONDCODE 			0x100E00
+#define 	DSP0STACKFLAG 			0x100E04
+#define 	DSP0PROGCOUNTSTACKPTREG 	0x100E08
+#define 	DSP0PROGCOUNTSTACKDATAREG 	0x100E0C
+#define 	DSP0CURLOOPADDRREG 		0x100E10
+#define 	DSP0CURLOOPCOUNT 		0x100E14
+#define 	DSP0TOPLOOPCOUNTSTACK 		0x100E18
+#define 	DSP0TOPLOOPADDRSTACK 		0x100E1C
+#define 	DSP0LOOPSTACKPTR 		0x100E20
+#define 	DSP0STASSTACKDATAREG 		0x100E24
+#define 	DSP0STASSTACKPTR 		0x100E28
+#define 	DSP0PROGCOUNT 			0x100E2C
+#define  	GLOBDSPDEBGREG			0x100E30
+#define  	GLOBDSPBREPTRREG		0x100E30
+#define 	DSP0XYRAMBASE_START 		0x100EA0
+#define 	DSP0XYRAMBASE_END 		0x100EBC
+#define 	DSP0XYRAMLENG_START 		0x100EC0
+#define 	DSP0XYRAMLENG_END 		0x100EDC
+#define		SEMAPHOREREGDSP0		0x100EE0
+#define		DSP0INTCONTMASKREG		0x100EE4
+#define		DSP0INTCONTPENDREG		0x100EE8
+#define		DSP0INTCONTSERVINT		0x100EEC
+#define		DSPINTCONTEXTINTMODREG		0x100EEC
+#define		GPIODSP0			0x100EFC
+#define 	DMADSPBASEADDRREG_STARTDSP0	0x100F00
+#define 	DMADSPBASEADDRREG_ENDDSP0	0x100F1C
+#define 	DMAHOSTBASEADDRREG_STARTDSP0	0x100F20
+#define 	DMAHOSTBASEADDRREG_ENDDSP0	0x100F3C
+#define 	DMADSPCURADDRREG_STARTDSP0	0x100F40
+#define 	DMADSPCURADDRREG_ENDDSP0	0x100F5C
+#define 	DMAHOSTCURADDRREG_STARTDSP0	0x100F60
+#define 	DMAHOSTCURADDRREG_ENDDSP0	0x100F7C
+#define 	DMATANXCOUNTREG_STARTDSP0	0x100F80
+#define 	DMATANXCOUNTREG_ENDDSP0		0x100F9C
+#define 	DMATIMEBUGREG_STARTDSP0		0x100FA0
+#define 	DMATIMEBUGREG_ENDDSP0		0x100FAC
+#define 	DMACNTLMODFREG_STARTDSP0	0x100FA0
+#define 	DMACNTLMODFREG_ENDDSP0		0x100FAC
+
+#define 	DMAGLOBSTATSREGDSP0		0x100FEC
+#define 	DSP0XGPRAM_START 		0x101000
+#define 	DSP0XGPRAM_END 			0x1017FC
+#define 	DSP0YGPRAM_START 		0x101800
+#define 	DSP0YGPRAM_END 			0x101FFC
+
+
+
+
+#define 	AUDIORINGIPDSP1_START 		0x102000
+#define 	AUDIORINGIPDSP1_END	 	0x1023FC
+#define 	AUDIORINGOPDSP1_START 		0x102400
+#define 	AUDIORINGOPDSP1_END	 	0x1027FC
+#define 	AUDPARARINGIODSP1_START 	0x102800
+#define 	AUDPARARINGIODSP1_END	 	0x102BFC
+#define 	DSP1LOCALHWREG_START 		0x102C00
+#define 	DSP1LOCALHWREG_END	 	0x102C3C
+#define 	DSP1XYRAMAGINDEX_START 		0x102C40
+#define 	DSP1XYRAMAGINDEX_END	 	0x102C5C
+#define 	DSP1XYRAMAGMDFR_START 		0x102C60
+#define 	DSP1XYRAMAGMDFR_END	 	0x102C7C
+#define 	DSP1INTCONTLVEC_START 		0x102C80
+#define 	DSP1INTCONTLVEC_END	 	0x102CD8
+#define		HOSTINTFPORTADDRCONTDSP1	0x102D40
+#define		HOSTINTFPORTDATADSP1		0x102D44
+#define		TIME0PERENBDSP1			0x102D60
+#define		TIME0COUNTERDSP1		0x102D64
+#define		TIME1PERENBDSP1			0x102D68
+#define		TIME1COUNTERDSP1		0x102D6C
+#define		TIME2PERENBDSP1			0x102D70
+#define		TIME2COUNTERDSP1		0x102D74
+#define		TIME3PERENBDSP1			0x102D78
+#define		TIME3COUNTERDSP1		0x102D7C
+#define 	XRAMINDOPERREFNOUP_STARTDSP1 	0x102D80
+#define 	XRAMINDOPERREFNOUP_ENDDSP1	0x102D9C
+#define 	XRAMINDOPERREFUP_STARTDSP1	0x102DA0
+#define 	XRAMINDOPERREFUP_ENDDSP1	0x102DBC
+#define 	YRAMINDOPERREFNOUP_STARTDSP1 	0x102DC0
+#define 	YRAMINDOPERREFNOUP_ENDDSP1	0x102DDC
+#define 	YRAMINDOPERREFUP_STARTDSP1	0x102DE0
+#define 	YRAMINDOPERREFUP_ENDDSP1	0x102DFC
+
+#define 	DSP1CONDCODE 			0x102E00
+#define 	DSP1STACKFLAG 			0x102E04
+#define 	DSP1PROGCOUNTSTACKPTREG 	0x102E08
+#define 	DSP1PROGCOUNTSTACKDATAREG 	0x102E0C
+#define 	DSP1CURLOOPADDRREG 		0x102E10
+#define 	DSP1CURLOOPCOUNT 		0x102E14
+#define 	DSP1TOPLOOPCOUNTSTACK 		0x102E18
+#define 	DSP1TOPLOOPADDRSTACK 		0x102E1C
+#define 	DSP1LOOPSTACKPTR 		0x102E20
+#define 	DSP1STASSTACKDATAREG 		0x102E24
+#define 	DSP1STASSTACKPTR 		0x102E28
+#define 	DSP1PROGCOUNT 			0x102E2C
+#define 	DSP1XYRAMBASE_START 		0x102EA0
+#define 	DSP1XYRAMBASE_END 		0x102EBC
+#define 	DSP1XYRAMLENG_START 		0x102EC0
+#define 	DSP1XYRAMLENG_END 		0x102EDC
+#define		SEMAPHOREREGDSP1		0x102EE0
+#define		DSP1INTCONTMASKREG		0x102EE4
+#define		DSP1INTCONTPENDREG		0x102EE8
+#define		DSP1INTCONTSERVINT		0x102EEC
+#define		GPIODSP1			0x102EFC
+#define 	DMADSPBASEADDRREG_STARTDSP1	0x102F00
+#define 	DMADSPBASEADDRREG_ENDDSP1	0x102F1C
+#define 	DMAHOSTBASEADDRREG_STARTDSP1	0x102F20
+#define 	DMAHOSTBASEADDRREG_ENDDSP1	0x102F3C
+#define 	DMADSPCURADDRREG_STARTDSP1	0x102F40
+#define 	DMADSPCURADDRREG_ENDDSP1	0x102F5C
+#define 	DMAHOSTCURADDRREG_STARTDSP1	0x102F60
+#define 	DMAHOSTCURADDRREG_ENDDSP1	0x102F7C
+#define 	DMATANXCOUNTREG_STARTDSP1	0x102F80
+#define 	DMATANXCOUNTREG_ENDDSP1		0x102F9C
+#define 	DMATIMEBUGREG_STARTDSP1		0x102FA0
+#define 	DMATIMEBUGREG_ENDDSP1		0x102FAC
+#define 	DMACNTLMODFREG_STARTDSP1	0x102FA0
+#define 	DMACNTLMODFREG_ENDDSP1		0x102FAC
+
+#define 	DMAGLOBSTATSREGDSP1		0x102FEC
+#define 	DSP1XGPRAM_START 		0x103000
+#define 	DSP1XGPRAM_END 			0x1033FC
+#define 	DSP1YGPRAM_START 		0x103400
+#define 	DSP1YGPRAM_END 			0x1037FC
+
+
+
+#define 	AUDIORINGIPDSP2_START 		0x104000
+#define 	AUDIORINGIPDSP2_END	 	0x1043FC
+#define 	AUDIORINGOPDSP2_START 		0x104400
+#define 	AUDIORINGOPDSP2_END	 	0x1047FC
+#define 	AUDPARARINGIODSP2_START 	0x104800
+#define 	AUDPARARINGIODSP2_END	 	0x104BFC
+#define 	DSP2LOCALHWREG_START 		0x104C00
+#define 	DSP2LOCALHWREG_END	 	0x104C3C
+#define 	DSP2XYRAMAGINDEX_START 		0x104C40
+#define 	DSP2XYRAMAGINDEX_END	 	0x104C5C
+#define 	DSP2XYRAMAGMDFR_START 		0x104C60
+#define 	DSP2XYRAMAGMDFR_END		0x104C7C
+#define 	DSP2INTCONTLVEC_START 		0x104C80
+#define 	DSP2INTCONTLVEC_END	 	0x104CD8
+#define		HOSTINTFPORTADDRCONTDSP2	0x104D40
+#define		HOSTINTFPORTDATADSP2		0x104D44
+#define		TIME0PERENBDSP2			0x104D60
+#define		TIME0COUNTERDSP2		0x104D64
+#define		TIME1PERENBDSP2			0x104D68
+#define		TIME1COUNTERDSP2		0x104D6C
+#define		TIME2PERENBDSP2			0x104D70
+#define		TIME2COUNTERDSP2		0x104D74
+#define		TIME3PERENBDSP2			0x104D78
+#define		TIME3COUNTERDSP2		0x104D7C
+#define 	XRAMINDOPERREFNOUP_STARTDSP2 	0x104D80
+#define 	XRAMINDOPERREFNOUP_ENDDSP2	0x104D9C
+#define 	XRAMINDOPERREFUP_STARTDSP2	0x104DA0
+#define 	XRAMINDOPERREFUP_ENDDSP2	0x104DBC
+#define 	YRAMINDOPERREFNOUP_STARTDSP2 	0x104DC0
+#define 	YRAMINDOPERREFNOUP_ENDDSP2	0x104DDC
+#define 	YRAMINDOPERREFUP_STARTDSP2	0x104DE0
+#define 	YRAMINDOPERREFUP_ENDDSP2 	0x104DFC
+#define 	DSP2CONDCODE 			0x104E00
+#define 	DSP2STACKFLAG 			0x104E04
+#define 	DSP2PROGCOUNTSTACKPTREG 	0x104E08
+#define 	DSP2PROGCOUNTSTACKDATAREG 	0x104E0C
+#define 	DSP2CURLOOPADDRREG 		0x104E10
+#define 	DSP2CURLOOPCOUNT 		0x104E14
+#define 	DSP2TOPLOOPCOUNTSTACK 		0x104E18
+#define 	DSP2TOPLOOPADDRSTACK 		0x104E1C
+#define 	DSP2LOOPSTACKPTR 		0x104E20
+#define 	DSP2STASSTACKDATAREG 		0x104E24
+#define 	DSP2STASSTACKPTR 		0x104E28
+#define 	DSP2PROGCOUNT 			0x104E2C
+#define 	DSP2XYRAMBASE_START 		0x104EA0
+#define 	DSP2XYRAMBASE_END 		0x104EBC
+#define 	DSP2XYRAMLENG_START 		0x104EC0
+#define 	DSP2XYRAMLENG_END 		0x104EDC
+#define		SEMAPHOREREGDSP2		0x104EE0
+#define		DSP2INTCONTMASKREG		0x104EE4
+#define		DSP2INTCONTPENDREG		0x104EE8
+#define		DSP2INTCONTSERVINT		0x104EEC
+#define		GPIODSP2			0x104EFC
+#define 	DMADSPBASEADDRREG_STARTDSP2	0x104F00
+#define 	DMADSPBASEADDRREG_ENDDSP2	0x104F1C
+#define 	DMAHOSTBASEADDRREG_STARTDSP2	0x104F20
+#define 	DMAHOSTBASEADDRREG_ENDDSP2	0x104F3C
+#define 	DMADSPCURADDRREG_STARTDSP2	0x104F40
+#define 	DMADSPCURADDRREG_ENDDSP2	0x104F5C
+#define 	DMAHOSTCURADDRREG_STARTDSP2	0x104F60
+#define 	DMAHOSTCURADDRREG_ENDDSP2	0x104F7C
+#define 	DMATANXCOUNTREG_STARTDSP2	0x104F80
+#define 	DMATANXCOUNTREG_ENDDSP2		0x104F9C
+#define 	DMATIMEBUGREG_STARTDSP2		0x104FA0
+#define 	DMATIMEBUGREG_ENDDSP2		0x104FAC
+#define 	DMACNTLMODFREG_STARTDSP2	0x104FA0
+#define 	DMACNTLMODFREG_ENDDSP2		0x104FAC
+
+#define 	DMAGLOBSTATSREGDSP2		0x104FEC
+#define 	DSP2XGPRAM_START 		0x105000
+#define 	DSP2XGPRAM_END 			0x1051FC
+#define 	DSP2YGPRAM_START 		0x105800
+#define 	DSP2YGPRAM_END 			0x1059FC
+
+
+
+#define 	AUDIORINGIPDSP3_START 		0x106000
+#define 	AUDIORINGIPDSP3_END	 	0x1063FC
+#define 	AUDIORINGOPDSP3_START 		0x106400
+#define 	AUDIORINGOPDSP3_END	 	0x1067FC
+#define 	AUDPARARINGIODSP3_START 	0x106800
+#define 	AUDPARARINGIODSP3_END	 	0x106BFC
+#define 	DSP3LOCALHWREG_START 		0x106C00
+#define 	DSP3LOCALHWREG_END	 	0x106C3C
+#define 	DSP3XYRAMAGINDEX_START 		0x106C40
+#define 	DSP3XYRAMAGINDEX_END	 	0x106C5C
+#define 	DSP3XYRAMAGMDFR_START 		0x106C60
+#define 	DSP3XYRAMAGMDFR_END		0x106C7C
+#define 	DSP3INTCONTLVEC_START 		0x106C80
+#define 	DSP3INTCONTLVEC_END	 	0x106CD8
+#define		HOSTINTFPORTADDRCONTDSP3	0x106D40
+#define		HOSTINTFPORTDATADSP3		0x106D44
+#define		TIME0PERENBDSP3			0x106D60
+#define		TIME0COUNTERDSP3		0x106D64
+#define		TIME1PERENBDSP3			0x106D68
+#define		TIME1COUNTERDSP3		0x106D6C
+#define		TIME2PERENBDSP3			0x106D70
+#define		TIME2COUNTERDSP3		0x106D74
+#define		TIME3PERENBDSP3			0x106D78
+#define		TIME3COUNTERDSP3		0x106D7C
+#define 	XRAMINDOPERREFNOUP_STARTDSP3 	0x106D80
+#define 	XRAMINDOPERREFNOUP_ENDDSP3	0x106D9C
+#define 	XRAMINDOPERREFUP_STARTDSP3	0x106DA0
+#define 	XRAMINDOPERREFUP_ENDDSP3	0x106DBC
+#define 	YRAMINDOPERREFNOUP_STARTDSP3 	0x106DC0
+#define 	YRAMINDOPERREFNOUP_ENDDSP3	0x106DDC
+#define 	YRAMINDOPERREFUP_STARTDSP3	0x106DE0
+#define 	YRAMINDOPERREFUP_ENDDSP3	0x100DFC
+
+#define 	DSP3CONDCODE 			0x106E00
+#define 	DSP3STACKFLAG 			0x106E04
+#define 	DSP3PROGCOUNTSTACKPTREG 	0x106E08
+#define 	DSP3PROGCOUNTSTACKDATAREG 	0x106E0C
+#define 	DSP3CURLOOPADDRREG 		0x106E10
+#define 	DSP3CURLOOPCOUNT 		0x106E14
+#define 	DSP3TOPLOOPCOUNTSTACK 		0x106E18
+#define 	DSP3TOPLOOPADDRSTACK 		0x106E1C
+#define 	DSP3LOOPSTACKPTR 		0x106E20
+#define 	DSP3STASSTACKDATAREG 		0x106E24
+#define 	DSP3STASSTACKPTR 		0x106E28
+#define 	DSP3PROGCOUNT 			0x106E2C
+#define 	DSP3XYRAMBASE_START 		0x106EA0
+#define 	DSP3XYRAMBASE_END 		0x106EBC
+#define 	DSP3XYRAMLENG_START 		0x106EC0
+#define 	DSP3XYRAMLENG_END 		0x106EDC
+#define		SEMAPHOREREGDSP3		0x106EE0
+#define		DSP3INTCONTMASKREG		0x106EE4
+#define		DSP3INTCONTPENDREG		0x106EE8
+#define		DSP3INTCONTSERVINT		0x106EEC
+#define		GPIODSP3			0x106EFC
+#define 	DMADSPBASEADDRREG_STARTDSP3	0x106F00
+#define 	DMADSPBASEADDRREG_ENDDSP3	0x106F1C
+#define 	DMAHOSTBASEADDRREG_STARTDSP3	0x106F20
+#define 	DMAHOSTBASEADDRREG_ENDDSP3	0x106F3C
+#define 	DMADSPCURADDRREG_STARTDSP3	0x106F40
+#define 	DMADSPCURADDRREG_ENDDSP3	0x106F5C
+#define 	DMAHOSTCURADDRREG_STARTDSP3	0x106F60
+#define 	DMAHOSTCURADDRREG_ENDDSP3	0x106F7C
+#define 	DMATANXCOUNTREG_STARTDSP3	0x106F80
+#define 	DMATANXCOUNTREG_ENDDSP3		0x106F9C
+#define 	DMATIMEBUGREG_STARTDSP3		0x106FA0
+#define 	DMATIMEBUGREG_ENDDSP3		0x106FAC
+#define 	DMACNTLMODFREG_STARTDSP3	0x106FA0
+#define 	DMACNTLMODFREG_ENDDSP3		0x106FAC
+
+#define 	DMAGLOBSTATSREGDSP3		0x106FEC
+#define 	DSP3XGPRAM_START 		0x107000
+#define 	DSP3XGPRAM_END 			0x1071FC
+#define 	DSP3YGPRAM_START 		0x107800
+#define 	DSP3YGPRAM_END 			0x1079FC
+
+/* end of DSP reg definitions */
+
+#define  	DSPAIMAP_START			0x108000
+#define  	DSPAIMAP_END			0x1083FC
+#define  	DSPPIMAP_START			0x108400
+#define  	DSPPIMAP_END			0x1087FC
+#define  	DSPPOMAP_START			0x108800
+#define  	DSPPOMAP_END			0x108BFC
+#define  	DSPPOCTL			0x108C00
+#define 	TKCTL_START			0x110000
+#define 	TKCTL_END			0x110FFC
+#define 	TKCC_START			0x111000
+#define 	TKCC_END			0x111FFC
+#define 	TKIMAP_START			0x112000
+#define 	TKIMAP_END			0x112FFC
+#define		TKDCTR16			0x113000
+#define		TKPB16				0x113004
+#define		TKBS16				0x113008
+#define		TKDCTR32			0x11300C
+#define		TKPB32				0x113010
+#define		TKBS32				0x113014
+#define		ICDCTR16			0x113018
+#define		ITBS16				0x11301C
+#define		ICDCTR32			0x113020
+#define		ITBS32				0x113024
+#define		ITSTART				0x113028
+#define		TKSQ				0x11302C
+
+#define		TKSCCTL_START			0x114000
+#define		TKSCCTL_END			0x11403C
+#define		TKSCADR_START			0x114100
+#define		TKSCADR_END			0x11413C
+#define		TKSCDATAX_START			0x114800
+#define		TKSCDATAX_END			0x1149FC
+#define		TKPCDATAX_START			0x120000
+#define		TKPCDATAX_END			0x12FFFC
+
+#define		MALSA				0x130000
+#define		MAPPHA				0x130004
+#define		MAPPLA				0x130008
+#define		MALSB				0x130010
+#define		MAPPHB				0x130014
+#define		MAPPLB				0x130018
+
+#define 	TANSPORTMAPABREGS_START		0x130020
+#define 	TANSPORTMAPABREGS_END		0x13A2FC
+
+#define		PTPAHX				0x13B000
+#define		PTPALX				0x13B004
+
+#define		TANSPPAGETABLEPHYADDR015_START	0x13B008
+#define		TANSPPAGETABLEPHYADDR015_END	0x13B07C
+#define		TRNQADRX_START			0x13B100
+#define		TRNQADRX_END			0x13B13C
+#define		TRNQTIMX_START			0x13B200
+#define		TRNQTIMX_END			0x13B23C
+#define		TRNQAPARMX_START		0x13B300
+#define		TRNQAPARMX_END			0x13B33C
+
+#define		TRNQCNT				0x13B400
+#define		TRNCTL				0x13B404
+#define		TRNIS				0x13B408
+#define		TRNCURTS			0x13B40C
+
+#define		AMOP_START			0x140000
+#define		AMOPLO				0x140000
+#define		AMOPHI				0x140004
+#define		AMOP_END			0x147FFC
+#define		PMOP_START			0x148000
+#define		PMOPLO				0x148000
+#define		PMOPHI				0x148004
+#define		PMOP_END			0x14FFFC
+#define		PCURR_START			0x150000
+#define		PCURR_END			0x153FFC
+#define		PTRAG_START			0x154000
+#define		PTRAG_END			0x157FFC
+#define		PSR_START			0x158000
+#define		PSR_END				0x15BFFC
+
+#define		PFSTAT4SEG_START		0x160000
+#define		PFSTAT4SEG_END			0x160BFC
+#define		PFSTAT2SEG_START		0x160C00
+#define		PFSTAT2SEG_END			0x1617FC
+#define		PFTARG4SEG_START		0x164000
+#define		PFTARG4SEG_END			0x164BFC
+#define		PFTARG2SEG_START		0x164C00
+#define		PFTARG2SEG_END			0x1657FC
+#define		PFSR4SEG_START			0x168000
+#define		PFSR4SEG_END			0x168BFC
+#define		PFSR2SEG_START			0x168C00
+#define		PFSR2SEG_END			0x1697FC
+#define		PCURRMS4SEG_START		0x16C000
+#define		PCURRMS4SEG_END			0x16CCFC
+#define		PCURRMS2SEG_START		0x16CC00
+#define		PCURRMS2SEG_END			0x16D7FC
+#define		PTARGMS4SEG_START		0x170000
+#define		PTARGMS4SEG_END			0x172FFC
+#define		PTARGMS2SEG_START		0x173000
+#define		PTARGMS2SEG_END			0x1747FC
+#define		PSRMS4SEG_START			0x170000
+#define		PSRMS4SEG_END			0x172FFC
+#define		PSRMS2SEG_START			0x173000
+#define		PSRMS2SEG_END			0x1747FC
+
+#define		PRING_LO_START			0x190000
+#define		PRING_LO_END			0x193FFC
+#define		PRING_HI_START			0x194000
+#define		PRING_HI_END			0x197FFC
+#define		PRING_LO_HI_START		0x198000
+#define		PRING_LO_HI			0x198000
+#define		PRING_LO_HI_END			0x19BFFC
+
+#define		PINTFIFO			0x1A0000
+#define		SRCCTL				0x1B0000
+#define		SRCCCR				0x1B0004
+#define		SRCIMAP				0x1B0008
+#define		SRCODDC				0x1B000C
+#define		SRCCA				0x1B0010
+#define		SRCCF				0x1B0014
+#define		SRCSA				0x1B0018
+#define		SRCLA				0x1B001C
+#define		SRCCTLSWR			0x1B0020
+
+/* SRC HERE */
+#define		SRCALBA				0x1B002C
+#define		SRCMCTL				0x1B012C
+#define		SRCCERR				0x1B022C
+#define		SRCITB				0x1B032C
+#define		SRCIPM				0x1B082C
+#define		SRCIP				0x1B102C
+#define		SRCENBSTAT			0x1B202C
+#define		SRCENBLO			0x1B212C
+#define		SRCENBHI			0x1B222C
+#define		SRCENBS				0x1B232C
+#define		SRCENB				0x1B282C
+#define		SRCENB07			0x1B282C
+#define		SRCENBS07			0x1B302C
+
+#define		SRCDN0Z				0x1B0030
+#define		SRCDN0Z0			0x1B0030
+#define		SRCDN0Z1			0x1B0034
+#define		SRCDN0Z2			0x1B0038
+#define		SRCDN0Z3			0x1B003C
+#define		SRCDN1Z				0x1B0040
+#define		SRCDN1Z0			0x1B0040
+#define		SRCDN1Z1			0x1B0044
+#define		SRCDN1Z2			0x1B0048
+#define		SRCDN1Z3			0x1B004C
+#define		SRCDN1Z4			0x1B0050
+#define		SRCDN1Z5			0x1B0054
+#define		SRCDN1Z6			0x1B0058
+#define		SRCDN1Z7			0x1B005C
+#define		SRCUPZ				0x1B0060
+#define		SRCUPZ0				0x1B0060
+#define		SRCUPZ1				0x1B0064
+#define		SRCUPZ2				0x1B0068
+#define		SRCUPZ3				0x1B006C
+#define		SRCUPZ4				0x1B0070
+#define		SRCUPZ5				0x1B0074
+#define		SRCUPZ6				0x1B0078
+#define		SRCUPZ7				0x1B007C
+#define		SRCCD0				0x1B0080
+#define		SRCCD1				0x1B0084
+#define		SRCCD2				0x1B0088
+#define		SRCCD3				0x1B008C
+#define		SRCCD4				0x1B0090
+#define		SRCCD5				0x1B0094
+#define		SRCCD6				0x1B0098
+#define		SRCCD7				0x1B009C
+#define		SRCCD8				0x1B00A0
+#define		SRCCD9				0x1B00A4
+#define		SRCCDA				0x1B00A8
+#define		SRCCDB				0x1B00AC
+#define		SRCCDC				0x1B00B0
+#define		SRCCDD				0x1B00B4
+#define		SRCCDE				0x1B00B8
+#define		SRCCDF				0x1B00BC
+#define		SRCCD10				0x1B00C0
+#define		SRCCD11				0x1B00C4
+#define		SRCCD12				0x1B00C8
+#define		SRCCD13				0x1B00CC
+#define		SRCCD14				0x1B00D0
+#define		SRCCD15				0x1B00D4
+#define		SRCCD16				0x1B00D8
+#define		SRCCD17				0x1B00DC
+#define		SRCCD18				0x1B00E0
+#define		SRCCD19				0x1B00E4
+#define		SRCCD1A				0x1B00E8
+#define		SRCCD1B				0x1B00EC
+#define		SRCCD1C				0x1B00F0
+#define		SRCCD1D				0x1B00F4
+#define		SRCCD1E				0x1B00F8
+#define		SRCCD1F				0x1B00FC
+
+#define		SRCCONTRBLOCK_START		0x1B0100
+#define		SRCCONTRBLOCK_END		0x1BFFFC
+#define		FILTOP_START	0x1C0000
+#define		FILTOP_END	0x1C05FC
+#define		FILTIMAP_START	0x1C0800
+#define		FILTIMAP_END	0x1C0DFC
+#define		FILTZ1_START	0x1C1000
+#define		FILTZ1_END	0x1C15FC
+#define		FILTZ2_START	0x1C1800
+#define		FILTZ2_END	0x1C1DFC
+#define		DAOIMAP_START	0x1C5000
+#define		DAOIMAP		0x1C5000
+#define		DAOIMAP_END	0x1C5124
+
+#define		AC97D		0x1C5400
+#define		AC97A		0x1C5404
+#define		AC97CTL		0x1C5408
+#define		I2SCTL		0x1C5420
+
+#define		SPOS		0x1C5440
+#define		SPOSA		0x1C5440
+#define		SPOSB		0x1C5444
+#define		SPOSC		0x1C5448
+#define		SPOSD		0x1C544C
+
+#define		SPISA		0x1C5450
+#define		SPISB		0x1C5454
+#define		SPISC		0x1C5458
+#define		SPISD		0x1C545C
+
+#define		SPFSCTL		0x1C5460
+
+#define		SPFS0		0x1C5468
+#define		SPFS1		0x1C546C
+#define		SPFS2		0x1C5470
+#define		SPFS3		0x1C5474
+#define		SPFS4		0x1C5478
+#define		SPFS5		0x1C547C
+
+#define		SPOCTL		0x1C5480
+#define		SPICTL		0x1C5484
+#define		SPISTS		0x1C5488
+#define		SPINTP		0x1C548C
+#define		SPINTE		0x1C5490
+#define		SPUTCTLAB	0x1C5494
+#define		SPUTCTLCD	0x1C5498
+
+#define		SRTSPA		0x1C54C0
+#define		SRTSPB		0x1C54C4
+#define		SRTSPC		0x1C54C8
+#define		SRTSPD		0x1C54CC
+
+#define		SRTSCTL		0x1C54D0
+#define		SRTSCTLA	0x1C54D0
+#define		SRTSCTLB	0x1C54D4
+#define		SRTSCTLC	0x1C54D8
+#define		SRTSCTLD	0x1C54DC
+
+#define		SRTI2S		0x1C54E0
+#define		SRTICTL		0x1C54F0
+
+#define		WC		0x1C6000
+#define		TIMR		0x1C6004
+# define	TIMR_IE		(1<<15)
+# define	TIMR_IP		(1<<14)
+
+#define		GIP		0x1C6010
+#define		GIE		0x1C6014
+#define		DIE		0x1C6018
+#define		DIC		0x1C601C
+#define		GPIO		0x1C6020
+#define		GPIOCTL		0x1C6024
+#define		GPIP		0x1C6028
+#define		GPIE		0x1C602C
+#define		DSPINT0		0x1C6030
+#define		DSPEIOC		0x1C6034
+#define		MUADAT		0x1C6040
+#define		MUACMD		0x1C6044
+#define 	MUASTAT		0x1C6044
+#define		MUBDAT		0x1C6048
+#define		MUBCMD		0x1C604C
+#define		MUBSTAT		0x1C604C
+#define		UARTCMA		0x1C6050
+#define		UARTCMB		0x1C6054
+#define		UARTIP		0x1C6058
+#define		UARTIE		0x1C605C
+#define		PLLCTL		0x1C6060
+#define		PLLDCD		0x1C6064
+#define		GCTL		0x1C6070
+#define		ID0		0x1C6080
+#define		ID1		0x1C6084
+#define		ID2		0x1C6088
+#define		ID3		0x1C608C
+#define		SDRCTL		0x1C7000
+
+
+#define I2SA_L    0x0L
+#define I2SA_R    0x1L
+#define I2SB_L    0x8L
+#define I2SB_R    0x9L
+#define I2SC_L    0x10L
+#define I2SC_R    0x11L
+#define I2SD_L    0x18L
+#define I2SD_R    0x19L
+
+#endif /* CT20K1REG_H */
+
+
diff --git a/sound/pci/ctxfi/ct20k2reg.h b/sound/pci/ctxfi/ct20k2reg.h
new file mode 100644
index 0000000..2d07986
--- /dev/null
+++ b/sound/pci/ctxfi/ct20k2reg.h
@@ -0,0 +1,85 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#ifndef _20K2REGISTERS_H_
+#define _20K2REGISTERS_H_
+
+
+/* Timer Registers */
+#define TIMER_TIMR          0x1B7004
+#define INTERRUPT_GIP       0x1B7010
+#define INTERRUPT_GIE       0x1B7014
+
+/* I2C Registers */
+#define I2C_IF_ADDRESS   0x1B9000
+#define I2C_IF_WDATA     0x1B9004
+#define I2C_IF_RDATA     0x1B9008
+#define I2C_IF_STATUS    0x1B900C
+#define I2C_IF_WLOCK     0x1B9010
+
+/* Global Control Registers */
+#define GLOBAL_CNTL_GCTL    0x1B7090
+
+/* PLL Registers */
+#define PLL_CTL 		0x1B7080
+#define PLL_STAT		0x1B7084
+#define PLL_ENB			0x1B7088
+
+/* SRC Registers */
+#define SRC_CTL             0x1A0000 /* 0x1A0000 + (256 * Chn) */
+#define SRC_CCR             0x1A0004 /* 0x1A0004 + (256 * Chn) */
+#define SRC_IMAP            0x1A0008 /* 0x1A0008 + (256 * Chn) */
+#define SRC_CA              0x1A0010 /* 0x1A0010 + (256 * Chn) */
+#define SRC_CF              0x1A0014 /* 0x1A0014 + (256 * Chn) */
+#define SRC_SA              0x1A0018 /* 0x1A0018 + (256 * Chn) */
+#define SRC_LA              0x1A001C /* 0x1A001C + (256 * Chn) */
+#define SRC_CTLSWR	    0x1A0020 /* 0x1A0020 + (256 * Chn) */
+#define SRC_CD		    0x1A0080 /* 0x1A0080 + (256 * Chn) + (4 * Regn) */
+#define SRC_MCTL		0x1A012C
+#define SRC_IP			0x1A102C /* 0x1A102C + (256 * Regn) */
+#define SRC_ENB			0x1A282C /* 0x1A282C + (256 * Regn) */
+#define SRC_ENBSTAT		0x1A202C
+#define SRC_ENBSA		0x1A232C
+#define SRC_DN0Z		0x1A0030
+#define SRC_DN1Z		0x1A0040
+#define SRC_UPZ			0x1A0060
+
+/* GPIO Registers */
+#define GPIO_DATA           0x1B7020
+#define GPIO_CTRL           0x1B7024
+
+/* Virtual memory registers */
+#define VMEM_PTPAL          0x1C6300 /* 0x1C6300 + (16 * Chn) */
+#define VMEM_PTPAH          0x1C6304 /* 0x1C6304 + (16 * Chn) */
+#define VMEM_CTL            0x1C7000
+
+/* Transport Registers */
+#define TRANSPORT_ENB       0x1B6000
+#define TRANSPORT_CTL       0x1B6004
+#define TRANSPORT_INT       0x1B6008
+
+/* Audio IO */
+#define AUDIO_IO_AIM        0x1B5000 /* 0x1B5000 + (0x04 * Chn) */
+#define AUDIO_IO_TX_CTL     0x1B5400 /* 0x1B5400 + (0x40 * Chn) */
+#define AUDIO_IO_TX_CSTAT_L 0x1B5408 /* 0x1B5408 + (0x40 * Chn) */
+#define AUDIO_IO_TX_CSTAT_H 0x1B540C /* 0x1B540C + (0x40 * Chn) */
+#define AUDIO_IO_RX_CTL     0x1B5410 /* 0x1B5410 + (0x40 * Chn) */
+#define AUDIO_IO_RX_SRT_CTL 0x1B5420 /* 0x1B5420 + (0x40 * Chn) */
+#define AUDIO_IO_MCLK       0x1B5600
+#define AUDIO_IO_TX_BLRCLK  0x1B5604
+#define AUDIO_IO_RX_BLRCLK  0x1B5608
+
+/* Mixer */
+#define MIXER_AMOPLO		0x130000 /* 0x130000 + (8 * Chn) [4095 : 0] */
+#define MIXER_AMOPHI		0x130004 /* 0x130004 + (8 * Chn) [4095 : 0] */
+#define MIXER_PRING_LO_HI	0x188000 /* 0x188000 + (4 * Chn) [4095 : 0] */
+#define MIXER_PMOPLO		0x138000 /* 0x138000 + (8 * Chn) [4095 : 0] */
+#define MIXER_PMOPHI		0x138004 /* 0x138004 + (8 * Chn) [4095 : 0] */
+#define MIXER_AR_ENABLE		0x19000C
+
+#endif
diff --git a/sound/pci/ctxfi/ctamixer.c b/sound/pci/ctxfi/ctamixer.c
new file mode 100644
index 0000000..a1db51b3
--- /dev/null
+++ b/sound/pci/ctxfi/ctamixer.c
@@ -0,0 +1,488 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctamixer.c
+ *
+ * @Brief
+ * This file contains the implementation of the Audio Mixer
+ * resource management object.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 21 2008
+ *
+ */
+
+#include "ctamixer.h"
+#include "cthardware.h"
+#include <linux/slab.h>
+
+#define AMIXER_RESOURCE_NUM	256
+#define SUM_RESOURCE_NUM	256
+
+#define AMIXER_Y_IMMEDIATE	1
+
+#define BLANK_SLOT		4094
+
+static int amixer_master(struct rsc *rsc)
+{
+	rsc->conj = 0;
+	return rsc->idx = container_of(rsc, struct amixer, rsc)->idx[0];
+}
+
+static int amixer_next_conj(struct rsc *rsc)
+{
+	rsc->conj++;
+	return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
+}
+
+static int amixer_index(const struct rsc *rsc)
+{
+	return container_of(rsc, struct amixer, rsc)->idx[rsc->conj];
+}
+
+static int amixer_output_slot(const struct rsc *rsc)
+{
+	return (amixer_index(rsc) << 4) + 0x4;
+}
+
+static struct rsc_ops amixer_basic_rsc_ops = {
+	.master		= amixer_master,
+	.next_conj	= amixer_next_conj,
+	.index		= amixer_index,
+	.output_slot	= amixer_output_slot,
+};
+
+static int amixer_set_input(struct amixer *amixer, struct rsc *rsc)
+{
+	struct hw *hw;
+
+	hw = amixer->rsc.hw;
+	hw->amixer_set_mode(amixer->rsc.ctrl_blk, AMIXER_Y_IMMEDIATE);
+	amixer->input = rsc;
+	if (NULL == rsc)
+		hw->amixer_set_x(amixer->rsc.ctrl_blk, BLANK_SLOT);
+	else
+		hw->amixer_set_x(amixer->rsc.ctrl_blk,
+					rsc->ops->output_slot(rsc));
+
+	return 0;
+}
+
+/* y is a 14-bit immediate constant */
+static int amixer_set_y(struct amixer *amixer, unsigned int y)
+{
+	struct hw *hw;
+
+	hw = amixer->rsc.hw;
+	hw->amixer_set_y(amixer->rsc.ctrl_blk, y);
+
+	return 0;
+}
+
+static int amixer_set_invalid_squash(struct amixer *amixer, unsigned int iv)
+{
+	struct hw *hw;
+
+	hw = amixer->rsc.hw;
+	hw->amixer_set_iv(amixer->rsc.ctrl_blk, iv);
+
+	return 0;
+}
+
+static int amixer_set_sum(struct amixer *amixer, struct sum *sum)
+{
+	struct hw *hw;
+
+	hw = amixer->rsc.hw;
+	amixer->sum = sum;
+	if (NULL == sum) {
+		hw->amixer_set_se(amixer->rsc.ctrl_blk, 0);
+	} else {
+		hw->amixer_set_se(amixer->rsc.ctrl_blk, 1);
+		hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
+					sum->rsc.ops->index(&sum->rsc));
+	}
+
+	return 0;
+}
+
+static int amixer_commit_write(struct amixer *amixer)
+{
+	struct hw *hw;
+	unsigned int index;
+	int i;
+	struct rsc *input;
+	struct sum *sum;
+
+	hw = amixer->rsc.hw;
+	input = amixer->input;
+	sum = amixer->sum;
+
+	/* Program master and conjugate resources */
+	amixer->rsc.ops->master(&amixer->rsc);
+	if (NULL != input)
+		input->ops->master(input);
+
+	if (NULL != sum)
+		sum->rsc.ops->master(&sum->rsc);
+
+	for (i = 0; i < amixer->rsc.msr; i++) {
+		hw->amixer_set_dirty_all(amixer->rsc.ctrl_blk);
+		if (NULL != input) {
+			hw->amixer_set_x(amixer->rsc.ctrl_blk,
+						input->ops->output_slot(input));
+			input->ops->next_conj(input);
+		}
+		if (NULL != sum) {
+			hw->amixer_set_sadr(amixer->rsc.ctrl_blk,
+						sum->rsc.ops->index(&sum->rsc));
+			sum->rsc.ops->next_conj(&sum->rsc);
+		}
+		index = amixer->rsc.ops->output_slot(&amixer->rsc);
+		hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk);
+		amixer->rsc.ops->next_conj(&amixer->rsc);
+	}
+	amixer->rsc.ops->master(&amixer->rsc);
+	if (NULL != input)
+		input->ops->master(input);
+
+	if (NULL != sum)
+		sum->rsc.ops->master(&sum->rsc);
+
+	return 0;
+}
+
+static int amixer_commit_raw_write(struct amixer *amixer)
+{
+	struct hw *hw;
+	unsigned int index;
+
+	hw = amixer->rsc.hw;
+	index = amixer->rsc.ops->output_slot(&amixer->rsc);
+	hw->amixer_commit_write(hw, index, amixer->rsc.ctrl_blk);
+
+	return 0;
+}
+
+static int amixer_get_y(struct amixer *amixer)
+{
+	struct hw *hw;
+
+	hw = amixer->rsc.hw;
+	return hw->amixer_get_y(amixer->rsc.ctrl_blk);
+}
+
+static int amixer_setup(struct amixer *amixer, struct rsc *input,
+			unsigned int scale, struct sum *sum)
+{
+	amixer_set_input(amixer, input);
+	amixer_set_y(amixer, scale);
+	amixer_set_sum(amixer, sum);
+	amixer_commit_write(amixer);
+	return 0;
+}
+
+static struct amixer_rsc_ops amixer_ops = {
+	.set_input		= amixer_set_input,
+	.set_invalid_squash	= amixer_set_invalid_squash,
+	.set_scale		= amixer_set_y,
+	.set_sum		= amixer_set_sum,
+	.commit_write		= amixer_commit_write,
+	.commit_raw_write	= amixer_commit_raw_write,
+	.setup			= amixer_setup,
+	.get_scale		= amixer_get_y,
+};
+
+static int amixer_rsc_init(struct amixer *amixer,
+			   const struct amixer_desc *desc,
+			   struct amixer_mgr *mgr)
+{
+	int err;
+
+	err = rsc_init(&amixer->rsc, amixer->idx[0],
+			AMIXER, desc->msr, mgr->mgr.hw);
+	if (err)
+		return err;
+
+	/* Set amixer specific operations */
+	amixer->rsc.ops = &amixer_basic_rsc_ops;
+	amixer->ops = &amixer_ops;
+	amixer->input = NULL;
+	amixer->sum = NULL;
+
+	amixer_setup(amixer, NULL, 0, NULL);
+
+	return 0;
+}
+
+static int amixer_rsc_uninit(struct amixer *amixer)
+{
+	amixer_setup(amixer, NULL, 0, NULL);
+	rsc_uninit(&amixer->rsc);
+	amixer->ops = NULL;
+	amixer->input = NULL;
+	amixer->sum = NULL;
+	return 0;
+}
+
+static int get_amixer_rsc(struct amixer_mgr *mgr,
+			  const struct amixer_desc *desc,
+			  struct amixer **ramixer)
+{
+	int err, i;
+	unsigned int idx;
+	struct amixer *amixer;
+	unsigned long flags;
+
+	*ramixer = NULL;
+
+	/* Allocate mem for amixer resource */
+	amixer = kzalloc(sizeof(*amixer), GFP_KERNEL);
+	if (NULL == amixer) {
+		err = -ENOMEM;
+		return err;
+	}
+
+	/* Check whether there are sufficient
+	 * amixer resources to meet request. */
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	for (i = 0; i < desc->msr; i++) {
+		err = mgr_get_resource(&mgr->mgr, 1, &idx);
+		if (err)
+			break;
+
+		amixer->idx[i] = idx;
+	}
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	if (err) {
+		printk(KERN_ERR "ctxfi: Can't meet AMIXER resource request!\n");
+		goto error;
+	}
+
+	err = amixer_rsc_init(amixer, desc, mgr);
+	if (err)
+		goto error;
+
+	*ramixer = amixer;
+
+	return 0;
+
+error:
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	for (i--; i >= 0; i--)
+		mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	kfree(amixer);
+	return err;
+}
+
+static int put_amixer_rsc(struct amixer_mgr *mgr, struct amixer *amixer)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	for (i = 0; i < amixer->rsc.msr; i++)
+		mgr_put_resource(&mgr->mgr, 1, amixer->idx[i]);
+
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	amixer_rsc_uninit(amixer);
+	kfree(amixer);
+
+	return 0;
+}
+
+int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr)
+{
+	int err;
+	struct amixer_mgr *amixer_mgr;
+
+	*ramixer_mgr = NULL;
+	amixer_mgr = kzalloc(sizeof(*amixer_mgr), GFP_KERNEL);
+	if (NULL == amixer_mgr)
+		return -ENOMEM;
+
+	err = rsc_mgr_init(&amixer_mgr->mgr, AMIXER, AMIXER_RESOURCE_NUM, hw);
+	if (err)
+		goto error;
+
+	spin_lock_init(&amixer_mgr->mgr_lock);
+
+	amixer_mgr->get_amixer = get_amixer_rsc;
+	amixer_mgr->put_amixer = put_amixer_rsc;
+
+	*ramixer_mgr = amixer_mgr;
+
+	return 0;
+
+error:
+	kfree(amixer_mgr);
+	return err;
+}
+
+int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr)
+{
+	rsc_mgr_uninit(&amixer_mgr->mgr);
+	kfree(amixer_mgr);
+	return 0;
+}
+
+/* SUM resource management */
+
+static int sum_master(struct rsc *rsc)
+{
+	rsc->conj = 0;
+	return rsc->idx = container_of(rsc, struct sum, rsc)->idx[0];
+}
+
+static int sum_next_conj(struct rsc *rsc)
+{
+	rsc->conj++;
+	return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
+}
+
+static int sum_index(const struct rsc *rsc)
+{
+	return container_of(rsc, struct sum, rsc)->idx[rsc->conj];
+}
+
+static int sum_output_slot(const struct rsc *rsc)
+{
+	return (sum_index(rsc) << 4) + 0xc;
+}
+
+static struct rsc_ops sum_basic_rsc_ops = {
+	.master		= sum_master,
+	.next_conj	= sum_next_conj,
+	.index		= sum_index,
+	.output_slot	= sum_output_slot,
+};
+
+static int sum_rsc_init(struct sum *sum,
+			const struct sum_desc *desc,
+			struct sum_mgr *mgr)
+{
+	int err;
+
+	err = rsc_init(&sum->rsc, sum->idx[0], SUM, desc->msr, mgr->mgr.hw);
+	if (err)
+		return err;
+
+	sum->rsc.ops = &sum_basic_rsc_ops;
+
+	return 0;
+}
+
+static int sum_rsc_uninit(struct sum *sum)
+{
+	rsc_uninit(&sum->rsc);
+	return 0;
+}
+
+static int get_sum_rsc(struct sum_mgr *mgr,
+		       const struct sum_desc *desc,
+		       struct sum **rsum)
+{
+	int err, i;
+	unsigned int idx;
+	struct sum *sum;
+	unsigned long flags;
+
+	*rsum = NULL;
+
+	/* Allocate mem for sum resource */
+	sum = kzalloc(sizeof(*sum), GFP_KERNEL);
+	if (NULL == sum) {
+		err = -ENOMEM;
+		return err;
+	}
+
+	/* Check whether there are sufficient sum resources to meet request. */
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	for (i = 0; i < desc->msr; i++) {
+		err = mgr_get_resource(&mgr->mgr, 1, &idx);
+		if (err)
+			break;
+
+		sum->idx[i] = idx;
+	}
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	if (err) {
+		printk(KERN_ERR "ctxfi: Can't meet SUM resource request!\n");
+		goto error;
+	}
+
+	err = sum_rsc_init(sum, desc, mgr);
+	if (err)
+		goto error;
+
+	*rsum = sum;
+
+	return 0;
+
+error:
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	for (i--; i >= 0; i--)
+		mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
+
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	kfree(sum);
+	return err;
+}
+
+static int put_sum_rsc(struct sum_mgr *mgr, struct sum *sum)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	for (i = 0; i < sum->rsc.msr; i++)
+		mgr_put_resource(&mgr->mgr, 1, sum->idx[i]);
+
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	sum_rsc_uninit(sum);
+	kfree(sum);
+
+	return 0;
+}
+
+int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr)
+{
+	int err;
+	struct sum_mgr *sum_mgr;
+
+	*rsum_mgr = NULL;
+	sum_mgr = kzalloc(sizeof(*sum_mgr), GFP_KERNEL);
+	if (NULL == sum_mgr)
+		return -ENOMEM;
+
+	err = rsc_mgr_init(&sum_mgr->mgr, SUM, SUM_RESOURCE_NUM, hw);
+	if (err)
+		goto error;
+
+	spin_lock_init(&sum_mgr->mgr_lock);
+
+	sum_mgr->get_sum = get_sum_rsc;
+	sum_mgr->put_sum = put_sum_rsc;
+
+	*rsum_mgr = sum_mgr;
+
+	return 0;
+
+error:
+	kfree(sum_mgr);
+	return err;
+}
+
+int sum_mgr_destroy(struct sum_mgr *sum_mgr)
+{
+	rsc_mgr_uninit(&sum_mgr->mgr);
+	kfree(sum_mgr);
+	return 0;
+}
+
diff --git a/sound/pci/ctxfi/ctamixer.h b/sound/pci/ctxfi/ctamixer.h
new file mode 100644
index 0000000..cc49e5a
--- /dev/null
+++ b/sound/pci/ctxfi/ctamixer.h
@@ -0,0 +1,96 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctamixer.h
+ *
+ * @Brief
+ * This file contains the definition of the Audio Mixer
+ * resource management object.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 21 2008
+ *
+ */
+
+#ifndef CTAMIXER_H
+#define CTAMIXER_H
+
+#include "ctresource.h"
+#include <linux/spinlock.h>
+
+/* Define the descriptor of a summation node resource */
+struct sum {
+	struct rsc rsc;		/* Basic resource info */
+	unsigned char idx[8];
+};
+
+/* Define sum resource request description info */
+struct sum_desc {
+	unsigned int msr;
+};
+
+struct sum_mgr {
+	struct rsc_mgr mgr;	/* Basic resource manager info */
+	spinlock_t mgr_lock;
+
+	 /* request one sum resource */
+	int (*get_sum)(struct sum_mgr *mgr,
+			const struct sum_desc *desc, struct sum **rsum);
+	/* return one sum resource */
+	int (*put_sum)(struct sum_mgr *mgr, struct sum *sum);
+};
+
+/* Constructor and destructor of daio resource manager */
+int sum_mgr_create(void *hw, struct sum_mgr **rsum_mgr);
+int sum_mgr_destroy(struct sum_mgr *sum_mgr);
+
+/* Define the descriptor of a amixer resource */
+struct amixer_rsc_ops;
+
+struct amixer {
+	struct rsc rsc;		/* Basic resource info */
+	unsigned char idx[8];
+	struct rsc *input;	/* pointer to a resource acting as source */
+	struct sum *sum;	/* Put amixer output to this summation node */
+	struct amixer_rsc_ops *ops;	/* AMixer specific operations */
+};
+
+struct amixer_rsc_ops {
+	int (*set_input)(struct amixer *amixer, struct rsc *rsc);
+	int (*set_scale)(struct amixer *amixer, unsigned int scale);
+	int (*set_invalid_squash)(struct amixer *amixer, unsigned int iv);
+	int (*set_sum)(struct amixer *amixer, struct sum *sum);
+	int (*commit_write)(struct amixer *amixer);
+	/* Only for interleaved recording */
+	int (*commit_raw_write)(struct amixer *amixer);
+	int (*setup)(struct amixer *amixer, struct rsc *input,
+			unsigned int scale, struct sum *sum);
+	int (*get_scale)(struct amixer *amixer);
+};
+
+/* Define amixer resource request description info */
+struct amixer_desc {
+	unsigned int msr;
+};
+
+struct amixer_mgr {
+	struct rsc_mgr mgr;	/* Basic resource manager info */
+	spinlock_t mgr_lock;
+
+	 /* request one amixer resource */
+	int (*get_amixer)(struct amixer_mgr *mgr,
+			  const struct amixer_desc *desc,
+			  struct amixer **ramixer);
+	/* return one amixer resource */
+	int (*put_amixer)(struct amixer_mgr *mgr, struct amixer *amixer);
+};
+
+/* Constructor and destructor of amixer resource manager */
+int amixer_mgr_create(void *hw, struct amixer_mgr **ramixer_mgr);
+int amixer_mgr_destroy(struct amixer_mgr *amixer_mgr);
+
+#endif /* CTAMIXER_H */
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
new file mode 100644
index 0000000..a49c766
--- /dev/null
+++ b/sound/pci/ctxfi/ctatc.c
@@ -0,0 +1,1713 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File    ctatc.c
+ *
+ * @Brief
+ * This file contains the implementation of the device resource management
+ * object.
+ *
+ * @Author Liu Chun
+ * @Date Mar 28 2008
+ */
+
+#include "ctatc.h"
+#include "ctpcm.h"
+#include "ctmixer.h"
+#include "cthardware.h"
+#include "ctsrc.h"
+#include "ctamixer.h"
+#include "ctdaio.h"
+#include "cttimer.h"
+#include <linux/delay.h>
+#include <sound/pcm.h>
+#include <sound/control.h>
+#include <sound/asoundef.h>
+
+#define MONO_SUM_SCALE	0x19a8	/* 2^(-0.5) in 14-bit floating format */
+#define DAIONUM		7
+#define MAX_MULTI_CHN	8
+
+#define IEC958_DEFAULT_CON ((IEC958_AES0_NONAUDIO \
+			    | IEC958_AES0_CON_NOT_COPYRIGHT) \
+			    | ((IEC958_AES1_CON_MIXER \
+			    | IEC958_AES1_CON_ORIGINAL) << 8) \
+			    | (0x10 << 16) \
+			    | ((IEC958_AES3_CON_FS_48000) << 24))
+
+static struct snd_pci_quirk __devinitdata subsys_20k1_list[] = {
+	SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0022, "SB055x", CTSB055X),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x002f, "SB055x", CTSB055X),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0029, "SB073x", CTSB073X),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, 0x0031, "SB073x", CTSB073X),
+	SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000, 0x6000,
+			   "UAA", CTUAA),
+	{ } /* terminator */
+};
+
+static struct snd_pci_quirk __devinitdata subsys_20k2_list[] = {
+	SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB0760,
+		      "SB0760", CTSB0760),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08801,
+		      "SB0880", CTSB0880),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08802,
+		      "SB0880", CTSB0880),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_CREATIVE, PCI_SUBDEVICE_ID_CREATIVE_SB08803,
+		      "SB0880", CTSB0880),
+	SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_CREATIVE, 0xf000,
+			   PCI_SUBDEVICE_ID_CREATIVE_HENDRIX, "HENDRIX",
+			   CTHENDRIX),
+	{ } /* terminator */
+};
+
+static const char *ct_subsys_name[NUM_CTCARDS] = {
+	/* 20k1 models */
+	[CTSB055X]	= "SB055x",
+	[CTSB073X]	= "SB073x",
+	[CTUAA]		= "UAA",
+	[CT20K1_UNKNOWN] = "Unknown",
+	/* 20k2 models */
+	[CTSB0760]	= "SB076x",
+	[CTHENDRIX]	= "Hendrix",
+	[CTSB0880]	= "SB0880",
+	[CT20K2_UNKNOWN] = "Unknown",
+};
+
+static struct {
+	int (*create)(struct ct_atc *atc,
+			enum CTALSADEVS device, const char *device_name);
+	int (*destroy)(void *alsa_dev);
+	const char *public_name;
+} alsa_dev_funcs[NUM_CTALSADEVS] = {
+	[FRONT]		= { .create = ct_alsa_pcm_create,
+			    .destroy = NULL,
+			    .public_name = "Front/WaveIn"},
+	[SURROUND]	= { .create = ct_alsa_pcm_create,
+			    .destroy = NULL,
+			    .public_name = "Surround"},
+	[CLFE]		= { .create = ct_alsa_pcm_create,
+			    .destroy = NULL,
+			    .public_name = "Center/LFE"},
+	[SIDE]		= { .create = ct_alsa_pcm_create,
+			    .destroy = NULL,
+			    .public_name = "Side"},
+	[IEC958]	= { .create = ct_alsa_pcm_create,
+			    .destroy = NULL,
+			    .public_name = "IEC958 Non-audio"},
+
+	[MIXER]		= { .create = ct_alsa_mix_create,
+			    .destroy = NULL,
+			    .public_name = "Mixer"}
+};
+
+typedef int (*create_t)(void *, void **);
+typedef int (*destroy_t)(void *);
+
+static struct {
+	int (*create)(void *hw, void **rmgr);
+	int (*destroy)(void *mgr);
+} rsc_mgr_funcs[NUM_RSCTYP] = {
+	[SRC] 		= { .create 	= (create_t)src_mgr_create,
+			    .destroy 	= (destroy_t)src_mgr_destroy	},
+	[SRCIMP] 	= { .create 	= (create_t)srcimp_mgr_create,
+			    .destroy 	= (destroy_t)srcimp_mgr_destroy	},
+	[AMIXER]	= { .create	= (create_t)amixer_mgr_create,
+			    .destroy	= (destroy_t)amixer_mgr_destroy	},
+	[SUM]		= { .create	= (create_t)sum_mgr_create,
+			    .destroy	= (destroy_t)sum_mgr_destroy	},
+	[DAIO]		= { .create	= (create_t)daio_mgr_create,
+			    .destroy	= (destroy_t)daio_mgr_destroy	}
+};
+
+static int
+atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+
+/* *
+ * Only mono and interleaved modes are supported now.
+ * Always allocates a contiguous channel block.
+ * */
+
+static int ct_map_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct snd_pcm_runtime *runtime;
+	struct ct_vm *vm;
+
+	if (NULL == apcm->substream)
+		return 0;
+
+	runtime = apcm->substream->runtime;
+	vm = atc->vm;
+
+	apcm->vm_block = vm->map(vm, apcm->substream, runtime->dma_bytes);
+
+	if (NULL == apcm->vm_block)
+		return -ENOENT;
+
+	return 0;
+}
+
+static void ct_unmap_audio_buffer(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct ct_vm *vm;
+
+	if (NULL == apcm->vm_block)
+		return;
+
+	vm = atc->vm;
+
+	vm->unmap(vm, apcm->vm_block);
+
+	apcm->vm_block = NULL;
+}
+
+static unsigned long atc_get_ptp_phys(struct ct_atc *atc, int index)
+{
+	struct ct_vm *vm;
+	void *kvirt_addr;
+	unsigned long phys_addr;
+
+	vm = atc->vm;
+	kvirt_addr = vm->get_ptp_virt(vm, index);
+	if (kvirt_addr == NULL)
+		phys_addr = (~0UL);
+	else
+		phys_addr = virt_to_phys(kvirt_addr);
+
+	return phys_addr;
+}
+
+static unsigned int convert_format(snd_pcm_format_t snd_format)
+{
+	switch (snd_format) {
+	case SNDRV_PCM_FORMAT_U8:
+		return SRC_SF_U8;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		return SRC_SF_S16;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		return SRC_SF_S24;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		return SRC_SF_S32;
+	case SNDRV_PCM_FORMAT_FLOAT_LE:
+		return SRC_SF_F32;
+	default:
+		printk(KERN_ERR "ctxfi: not recognized snd format is %d \n",
+			snd_format);
+		return SRC_SF_S16;
+	}
+}
+
+static unsigned int
+atc_get_pitch(unsigned int input_rate, unsigned int output_rate)
+{
+	unsigned int pitch;
+	int b;
+
+	/* get pitch and convert to fixed-point 8.24 format. */
+	pitch = (input_rate / output_rate) << 24;
+	input_rate %= output_rate;
+	input_rate /= 100;
+	output_rate /= 100;
+	for (b = 31; ((b >= 0) && !(input_rate >> b)); )
+		b--;
+
+	if (b >= 0) {
+		input_rate <<= (31 - b);
+		input_rate /= output_rate;
+		b = 24 - (31 - b);
+		if (b >= 0)
+			input_rate <<= b;
+		else
+			input_rate >>= -b;
+
+		pitch |= input_rate;
+	}
+
+	return pitch;
+}
+
+static int select_rom(unsigned int pitch)
+{
+	if ((pitch > 0x00428f5c) && (pitch < 0x01b851ec)) {
+		/* 0.26 <= pitch <= 1.72 */
+		return 1;
+	} else if ((0x01d66666 == pitch) || (0x01d66667 == pitch)) {
+		/* pitch == 1.8375 */
+		return 2;
+	} else if (0x02000000 == pitch) {
+		/* pitch == 2 */
+		return 3;
+	} else if ((pitch >= 0x0) && (pitch <= 0x08000000)) {
+		/* 0 <= pitch <= 8 */
+		return 0;
+	} else {
+		return -ENOENT;
+	}
+}
+
+static int atc_pcm_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+	struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+	struct src_desc desc = {0};
+	struct amixer_desc mix_dsc = {0};
+	struct src *src;
+	struct amixer *amixer;
+	int err;
+	int n_amixer = apcm->substream->runtime->channels, i = 0;
+	int device = apcm->substream->pcm->device;
+	unsigned int pitch;
+
+	/* first release old resources */
+	atc_pcm_release_resources(atc, apcm);
+
+	/* Get SRC resource */
+	desc.multi = apcm->substream->runtime->channels;
+	desc.msr = atc->msr;
+	desc.mode = MEMRD;
+	err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src);
+	if (err)
+		goto error1;
+
+	pitch = atc_get_pitch(apcm->substream->runtime->rate,
+						(atc->rsr * atc->msr));
+	src = apcm->src;
+	src->ops->set_pitch(src, pitch);
+	src->ops->set_rom(src, select_rom(pitch));
+	src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+	src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
+
+	/* Get AMIXER resource */
+	n_amixer = (n_amixer < 2) ? 2 : n_amixer;
+	apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+	if (NULL == apcm->amixers) {
+		err = -ENOMEM;
+		goto error1;
+	}
+	mix_dsc.msr = atc->msr;
+	for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) {
+		err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc,
+					(struct amixer **)&apcm->amixers[i]);
+		if (err)
+			goto error1;
+
+		apcm->n_amixer++;
+	}
+
+	/* Set up device virtual mem map */
+	err = ct_map_audio_buffer(atc, apcm);
+	if (err < 0)
+		goto error1;
+
+	/* Connect resources */
+	src = apcm->src;
+	for (i = 0; i < n_amixer; i++) {
+		amixer = apcm->amixers[i];
+		mutex_lock(&atc->atc_mutex);
+		amixer->ops->setup(amixer, &src->rsc,
+					INIT_VOL, atc->pcm[i+device*2]);
+		mutex_unlock(&atc->atc_mutex);
+		src = src->ops->next_interleave(src);
+		if (NULL == src)
+			src = apcm->src;
+	}
+
+	ct_timer_prepare(apcm->timer);
+
+	return 0;
+
+error1:
+	atc_pcm_release_resources(atc, apcm);
+	return err;
+}
+
+static int
+atc_pcm_release_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+	struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+	struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+	struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM];
+	struct srcimp *srcimp;
+	int i;
+
+	if (NULL != apcm->srcimps) {
+		for (i = 0; i < apcm->n_srcimp; i++) {
+			srcimp = apcm->srcimps[i];
+			srcimp->ops->unmap(srcimp);
+			srcimp_mgr->put_srcimp(srcimp_mgr, srcimp);
+			apcm->srcimps[i] = NULL;
+		}
+		kfree(apcm->srcimps);
+		apcm->srcimps = NULL;
+	}
+
+	if (NULL != apcm->srccs) {
+		for (i = 0; i < apcm->n_srcc; i++) {
+			src_mgr->put_src(src_mgr, apcm->srccs[i]);
+			apcm->srccs[i] = NULL;
+		}
+		kfree(apcm->srccs);
+		apcm->srccs = NULL;
+	}
+
+	if (NULL != apcm->amixers) {
+		for (i = 0; i < apcm->n_amixer; i++) {
+			amixer_mgr->put_amixer(amixer_mgr, apcm->amixers[i]);
+			apcm->amixers[i] = NULL;
+		}
+		kfree(apcm->amixers);
+		apcm->amixers = NULL;
+	}
+
+	if (NULL != apcm->mono) {
+		sum_mgr->put_sum(sum_mgr, apcm->mono);
+		apcm->mono = NULL;
+	}
+
+	if (NULL != apcm->src) {
+		src_mgr->put_src(src_mgr, apcm->src);
+		apcm->src = NULL;
+	}
+
+	if (NULL != apcm->vm_block) {
+		/* Undo device virtual mem map */
+		ct_unmap_audio_buffer(atc, apcm);
+		apcm->vm_block = NULL;
+	}
+
+	return 0;
+}
+
+static int atc_pcm_playback_start(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	unsigned int max_cisz;
+	struct src *src = apcm->src;
+
+	if (apcm->started)
+		return 0;
+	apcm->started = 1;
+
+	max_cisz = src->multi * src->rsc.msr;
+	max_cisz = 0x80 * (max_cisz < 8 ? max_cisz : 8);
+
+	src->ops->set_sa(src, apcm->vm_block->addr);
+	src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size);
+	src->ops->set_ca(src, apcm->vm_block->addr + max_cisz);
+	src->ops->set_cisz(src, max_cisz);
+
+	src->ops->set_bm(src, 1);
+	src->ops->set_state(src, SRC_STATE_INIT);
+	src->ops->commit_write(src);
+
+	ct_timer_start(apcm->timer);
+	return 0;
+}
+
+static int atc_pcm_stop(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct src *src;
+	int i;
+
+	ct_timer_stop(apcm->timer);
+
+	src = apcm->src;
+	src->ops->set_bm(src, 0);
+	src->ops->set_state(src, SRC_STATE_OFF);
+	src->ops->commit_write(src);
+
+	if (NULL != apcm->srccs) {
+		for (i = 0; i < apcm->n_srcc; i++) {
+			src = apcm->srccs[i];
+			src->ops->set_bm(src, 0);
+			src->ops->set_state(src, SRC_STATE_OFF);
+			src->ops->commit_write(src);
+		}
+	}
+
+	apcm->started = 0;
+
+	return 0;
+}
+
+static int
+atc_pcm_playback_position(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct src *src = apcm->src;
+	u32 size, max_cisz;
+	int position;
+
+	if (!src)
+		return 0;
+	position = src->ops->get_ca(src);
+
+	size = apcm->vm_block->size;
+	max_cisz = src->multi * src->rsc.msr;
+	max_cisz = 128 * (max_cisz < 8 ? max_cisz : 8);
+
+	return (position + size - max_cisz - apcm->vm_block->addr) % size;
+}
+
+struct src_node_conf_t {
+	unsigned int pitch;
+	unsigned int msr:8;
+	unsigned int mix_msr:8;
+	unsigned int imp_msr:8;
+	unsigned int vo:1;
+};
+
+static void setup_src_node_conf(struct ct_atc *atc, struct ct_atc_pcm *apcm,
+				struct src_node_conf_t *conf, int *n_srcc)
+{
+	unsigned int pitch;
+
+	/* get pitch and convert to fixed-point 8.24 format. */
+	pitch = atc_get_pitch((atc->rsr * atc->msr),
+				apcm->substream->runtime->rate);
+	*n_srcc = 0;
+
+	if (1 == atc->msr) {
+		*n_srcc = apcm->substream->runtime->channels;
+		conf[0].pitch = pitch;
+		conf[0].mix_msr = conf[0].imp_msr = conf[0].msr = 1;
+		conf[0].vo = 1;
+	} else if (2 == atc->msr) {
+		if (0x8000000 < pitch) {
+			/* Need two-stage SRCs, SRCIMPs and
+			 * AMIXERs for converting format */
+			conf[0].pitch = (atc->msr << 24);
+			conf[0].msr = conf[0].mix_msr = 1;
+			conf[0].imp_msr = atc->msr;
+			conf[0].vo = 0;
+			conf[1].pitch = atc_get_pitch(atc->rsr,
+					apcm->substream->runtime->rate);
+			conf[1].msr = conf[1].mix_msr = conf[1].imp_msr = 1;
+			conf[1].vo = 1;
+			*n_srcc = apcm->substream->runtime->channels * 2;
+		} else if (0x1000000 < pitch) {
+			/* Need one-stage SRCs, SRCIMPs and
+			 * AMIXERs for converting format */
+			conf[0].pitch = pitch;
+			conf[0].msr = conf[0].mix_msr
+				    = conf[0].imp_msr = atc->msr;
+			conf[0].vo = 1;
+			*n_srcc = apcm->substream->runtime->channels;
+		}
+	}
+}
+
+static int
+atc_pcm_capture_get_resources(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+	struct srcimp_mgr *srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+	struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+	struct sum_mgr *sum_mgr = atc->rsc_mgrs[SUM];
+	struct src_desc src_dsc = {0};
+	struct src *src;
+	struct srcimp_desc srcimp_dsc = {0};
+	struct srcimp *srcimp;
+	struct amixer_desc mix_dsc = {0};
+	struct sum_desc sum_dsc = {0};
+	unsigned int pitch;
+	int multi, err, i;
+	int n_srcimp, n_amixer, n_srcc, n_sum;
+	struct src_node_conf_t src_node_conf[2] = {{0} };
+
+	/* first release old resources */
+	atc_pcm_release_resources(atc, apcm);
+
+	/* The numbers of converting SRCs and SRCIMPs should be determined
+	 * by pitch value. */
+
+	multi = apcm->substream->runtime->channels;
+
+	/* get pitch and convert to fixed-point 8.24 format. */
+	pitch = atc_get_pitch((atc->rsr * atc->msr),
+				apcm->substream->runtime->rate);
+
+	setup_src_node_conf(atc, apcm, src_node_conf, &n_srcc);
+	n_sum = (1 == multi) ? 1 : 0;
+	n_amixer = n_sum * 2 + n_srcc;
+	n_srcimp = n_srcc;
+	if ((multi > 1) && (0x8000000 >= pitch)) {
+		/* Need extra AMIXERs and SRCIMPs for special treatment
+		 * of interleaved recording of conjugate channels */
+		n_amixer += multi * atc->msr;
+		n_srcimp += multi * atc->msr;
+	} else {
+		n_srcimp += multi;
+	}
+
+	if (n_srcc) {
+		apcm->srccs = kzalloc(sizeof(void *)*n_srcc, GFP_KERNEL);
+		if (NULL == apcm->srccs)
+			return -ENOMEM;
+	}
+	if (n_amixer) {
+		apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+		if (NULL == apcm->amixers) {
+			err = -ENOMEM;
+			goto error1;
+		}
+	}
+	apcm->srcimps = kzalloc(sizeof(void *)*n_srcimp, GFP_KERNEL);
+	if (NULL == apcm->srcimps) {
+		err = -ENOMEM;
+		goto error1;
+	}
+
+	/* Allocate SRCs for sample rate conversion if needed */
+	src_dsc.multi = 1;
+	src_dsc.mode = ARCRW;
+	for (i = 0, apcm->n_srcc = 0; i < n_srcc; i++) {
+		src_dsc.msr = src_node_conf[i/multi].msr;
+		err = src_mgr->get_src(src_mgr, &src_dsc,
+					(struct src **)&apcm->srccs[i]);
+		if (err)
+			goto error1;
+
+		src = apcm->srccs[i];
+		pitch = src_node_conf[i/multi].pitch;
+		src->ops->set_pitch(src, pitch);
+		src->ops->set_rom(src, select_rom(pitch));
+		src->ops->set_vo(src, src_node_conf[i/multi].vo);
+
+		apcm->n_srcc++;
+	}
+
+	/* Allocate AMIXERs for routing SRCs of conversion if needed */
+	for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) {
+		if (i < (n_sum*2))
+			mix_dsc.msr = atc->msr;
+		else if (i < (n_sum*2+n_srcc))
+			mix_dsc.msr = src_node_conf[(i-n_sum*2)/multi].mix_msr;
+		else
+			mix_dsc.msr = 1;
+
+		err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc,
+					(struct amixer **)&apcm->amixers[i]);
+		if (err)
+			goto error1;
+
+		apcm->n_amixer++;
+	}
+
+	/* Allocate a SUM resource to mix all input channels together */
+	sum_dsc.msr = atc->msr;
+	err = sum_mgr->get_sum(sum_mgr, &sum_dsc, (struct sum **)&apcm->mono);
+	if (err)
+		goto error1;
+
+	pitch = atc_get_pitch((atc->rsr * atc->msr),
+				apcm->substream->runtime->rate);
+	/* Allocate SRCIMP resources */
+	for (i = 0, apcm->n_srcimp = 0; i < n_srcimp; i++) {
+		if (i < (n_srcc))
+			srcimp_dsc.msr = src_node_conf[i/multi].imp_msr;
+		else if (1 == multi)
+			srcimp_dsc.msr = (pitch <= 0x8000000) ? atc->msr : 1;
+		else
+			srcimp_dsc.msr = 1;
+
+		err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc, &srcimp);
+		if (err)
+			goto error1;
+
+		apcm->srcimps[i] = srcimp;
+		apcm->n_srcimp++;
+	}
+
+	/* Allocate a SRC for writing data to host memory */
+	src_dsc.multi = apcm->substream->runtime->channels;
+	src_dsc.msr = 1;
+	src_dsc.mode = MEMWR;
+	err = src_mgr->get_src(src_mgr, &src_dsc, (struct src **)&apcm->src);
+	if (err)
+		goto error1;
+
+	src = apcm->src;
+	src->ops->set_pitch(src, pitch);
+
+	/* Set up device virtual mem map */
+	err = ct_map_audio_buffer(atc, apcm);
+	if (err < 0)
+		goto error1;
+
+	return 0;
+
+error1:
+	atc_pcm_release_resources(atc, apcm);
+	return err;
+}
+
+static int atc_pcm_capture_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct src *src;
+	struct amixer *amixer;
+	struct srcimp *srcimp;
+	struct ct_mixer *mixer = atc->mixer;
+	struct sum *mono;
+	struct rsc *out_ports[8] = {NULL};
+	int err, i, j, n_sum, multi;
+	unsigned int pitch;
+	int mix_base = 0, imp_base = 0;
+
+	atc_pcm_release_resources(atc, apcm);
+
+	/* Get needed resources. */
+	err = atc_pcm_capture_get_resources(atc, apcm);
+	if (err)
+		return err;
+
+	/* Connect resources */
+	mixer->get_output_ports(mixer, MIX_PCMO_FRONT,
+				&out_ports[0], &out_ports[1]);
+
+	multi = apcm->substream->runtime->channels;
+	if (1 == multi) {
+		mono = apcm->mono;
+		for (i = 0; i < 2; i++) {
+			amixer = apcm->amixers[i];
+			amixer->ops->setup(amixer, out_ports[i],
+						MONO_SUM_SCALE, mono);
+		}
+		out_ports[0] = &mono->rsc;
+		n_sum = 1;
+		mix_base = n_sum * 2;
+	}
+
+	for (i = 0; i < apcm->n_srcc; i++) {
+		src = apcm->srccs[i];
+		srcimp = apcm->srcimps[imp_base+i];
+		amixer = apcm->amixers[mix_base+i];
+		srcimp->ops->map(srcimp, src, out_ports[i%multi]);
+		amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL);
+		out_ports[i%multi] = &amixer->rsc;
+	}
+
+	pitch = atc_get_pitch((atc->rsr * atc->msr),
+				apcm->substream->runtime->rate);
+
+	if ((multi > 1) && (pitch <= 0x8000000)) {
+		/* Special connection for interleaved
+		 * recording with conjugate channels */
+		for (i = 0; i < multi; i++) {
+			out_ports[i]->ops->master(out_ports[i]);
+			for (j = 0; j < atc->msr; j++) {
+				amixer = apcm->amixers[apcm->n_srcc+j*multi+i];
+				amixer->ops->set_input(amixer, out_ports[i]);
+				amixer->ops->set_scale(amixer, INIT_VOL);
+				amixer->ops->set_sum(amixer, NULL);
+				amixer->ops->commit_raw_write(amixer);
+				out_ports[i]->ops->next_conj(out_ports[i]);
+
+				srcimp = apcm->srcimps[apcm->n_srcc+j*multi+i];
+				srcimp->ops->map(srcimp, apcm->src,
+							&amixer->rsc);
+			}
+		}
+	} else {
+		for (i = 0; i < multi; i++) {
+			srcimp = apcm->srcimps[apcm->n_srcc+i];
+			srcimp->ops->map(srcimp, apcm->src, out_ports[i]);
+		}
+	}
+
+	ct_timer_prepare(apcm->timer);
+
+	return 0;
+}
+
+static int atc_pcm_capture_start(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct src *src;
+	struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+	int i, multi;
+
+	if (apcm->started)
+		return 0;
+
+	apcm->started = 1;
+	multi = apcm->substream->runtime->channels;
+	/* Set up converting SRCs */
+	for (i = 0; i < apcm->n_srcc; i++) {
+		src = apcm->srccs[i];
+		src->ops->set_pm(src, ((i%multi) != (multi-1)));
+		src_mgr->src_disable(src_mgr, src);
+	}
+
+	/*  Set up recording SRC */
+	src = apcm->src;
+	src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+	src->ops->set_sa(src, apcm->vm_block->addr);
+	src->ops->set_la(src, apcm->vm_block->addr + apcm->vm_block->size);
+	src->ops->set_ca(src, apcm->vm_block->addr);
+	src_mgr->src_disable(src_mgr, src);
+
+	/* Disable relevant SRCs firstly */
+	src_mgr->commit_write(src_mgr);
+
+	/* Enable SRCs respectively */
+	for (i = 0; i < apcm->n_srcc; i++) {
+		src = apcm->srccs[i];
+		src->ops->set_state(src, SRC_STATE_RUN);
+		src->ops->commit_write(src);
+		src_mgr->src_enable_s(src_mgr, src);
+	}
+	src = apcm->src;
+	src->ops->set_bm(src, 1);
+	src->ops->set_state(src, SRC_STATE_RUN);
+	src->ops->commit_write(src);
+	src_mgr->src_enable_s(src_mgr, src);
+
+	/* Enable relevant SRCs synchronously */
+	src_mgr->commit_write(src_mgr);
+
+	ct_timer_start(apcm->timer);
+	return 0;
+}
+
+static int
+atc_pcm_capture_position(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct src *src = apcm->src;
+
+	if (!src)
+		return 0;
+	return src->ops->get_ca(src) - apcm->vm_block->addr;
+}
+
+static int spdif_passthru_playback_get_resources(struct ct_atc *atc,
+						 struct ct_atc_pcm *apcm)
+{
+	struct src_mgr *src_mgr = atc->rsc_mgrs[SRC];
+	struct amixer_mgr *amixer_mgr = atc->rsc_mgrs[AMIXER];
+	struct src_desc desc = {0};
+	struct amixer_desc mix_dsc = {0};
+	struct src *src;
+	int err;
+	int n_amixer = apcm->substream->runtime->channels, i;
+	unsigned int pitch, rsr = atc->pll_rate;
+
+	/* first release old resources */
+	atc_pcm_release_resources(atc, apcm);
+
+	/* Get SRC resource */
+	desc.multi = apcm->substream->runtime->channels;
+	desc.msr = 1;
+	while (apcm->substream->runtime->rate > (rsr * desc.msr))
+		desc.msr <<= 1;
+
+	desc.mode = MEMRD;
+	err = src_mgr->get_src(src_mgr, &desc, (struct src **)&apcm->src);
+	if (err)
+		goto error1;
+
+	pitch = atc_get_pitch(apcm->substream->runtime->rate, (rsr * desc.msr));
+	src = apcm->src;
+	src->ops->set_pitch(src, pitch);
+	src->ops->set_rom(src, select_rom(pitch));
+	src->ops->set_sf(src, convert_format(apcm->substream->runtime->format));
+	src->ops->set_pm(src, (src->ops->next_interleave(src) != NULL));
+	src->ops->set_bp(src, 1);
+
+	/* Get AMIXER resource */
+	n_amixer = (n_amixer < 2) ? 2 : n_amixer;
+	apcm->amixers = kzalloc(sizeof(void *)*n_amixer, GFP_KERNEL);
+	if (NULL == apcm->amixers) {
+		err = -ENOMEM;
+		goto error1;
+	}
+	mix_dsc.msr = desc.msr;
+	for (i = 0, apcm->n_amixer = 0; i < n_amixer; i++) {
+		err = amixer_mgr->get_amixer(amixer_mgr, &mix_dsc,
+					(struct amixer **)&apcm->amixers[i]);
+		if (err)
+			goto error1;
+
+		apcm->n_amixer++;
+	}
+
+	/* Set up device virtual mem map */
+	err = ct_map_audio_buffer(atc, apcm);
+	if (err < 0)
+		goto error1;
+
+	return 0;
+
+error1:
+	atc_pcm_release_resources(atc, apcm);
+	return err;
+}
+
+static int atc_pll_init(struct ct_atc *atc, int rate)
+{
+	struct hw *hw = atc->hw;
+	int err;
+	err = hw->pll_init(hw, rate);
+	atc->pll_rate = err ? 0 : rate;
+	return err;
+}
+
+static int
+spdif_passthru_playback_setup(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct dao *dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
+	unsigned int rate = apcm->substream->runtime->rate;
+	unsigned int status;
+	int err = 0;
+	unsigned char iec958_con_fs;
+
+	switch (rate) {
+	case 48000:
+		iec958_con_fs = IEC958_AES3_CON_FS_48000;
+		break;
+	case 44100:
+		iec958_con_fs = IEC958_AES3_CON_FS_44100;
+		break;
+	case 32000:
+		iec958_con_fs = IEC958_AES3_CON_FS_32000;
+		break;
+	default:
+		return -ENOENT;
+	}
+
+	mutex_lock(&atc->atc_mutex);
+	dao->ops->get_spos(dao, &status);
+	if (((status >> 24) & IEC958_AES3_CON_FS) != iec958_con_fs) {
+		status &= ((~IEC958_AES3_CON_FS) << 24);
+		status |= (iec958_con_fs << 24);
+		dao->ops->set_spos(dao, status);
+		dao->ops->commit_write(dao);
+	}
+	if ((rate != atc->pll_rate) && (32000 != rate))
+		err = atc_pll_init(atc, rate);
+	mutex_unlock(&atc->atc_mutex);
+
+	return err;
+}
+
+static int
+spdif_passthru_playback_prepare(struct ct_atc *atc, struct ct_atc_pcm *apcm)
+{
+	struct src *src;
+	struct amixer *amixer;
+	struct dao *dao;
+	int err;
+	int i;
+
+	atc_pcm_release_resources(atc, apcm);
+
+	/* Configure SPDIFOO and PLL to passthrough mode;
+	 * determine pll_rate. */
+	err = spdif_passthru_playback_setup(atc, apcm);
+	if (err)
+		return err;
+
+	/* Get needed resources. */
+	err = spdif_passthru_playback_get_resources(atc, apcm);
+	if (err)
+		return err;
+
+	/* Connect resources */
+	src = apcm->src;
+	for (i = 0; i < apcm->n_amixer; i++) {
+		amixer = apcm->amixers[i];
+		amixer->ops->setup(amixer, &src->rsc, INIT_VOL, NULL);
+		src = src->ops->next_interleave(src);
+		if (NULL == src)
+			src = apcm->src;
+	}
+	/* Connect to SPDIFOO */
+	mutex_lock(&atc->atc_mutex);
+	dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
+	amixer = apcm->amixers[0];
+	dao->ops->set_left_input(dao, &amixer->rsc);
+	amixer = apcm->amixers[1];
+	dao->ops->set_right_input(dao, &amixer->rsc);
+	mutex_unlock(&atc->atc_mutex);
+
+	ct_timer_prepare(apcm->timer);
+
+	return 0;
+}
+
+static int atc_select_line_in(struct ct_atc *atc)
+{
+	struct hw *hw = atc->hw;
+	struct ct_mixer *mixer = atc->mixer;
+	struct src *src;
+
+	if (hw->is_adc_source_selected(hw, ADC_LINEIN))
+		return 0;
+
+	mixer->set_input_left(mixer, MIX_MIC_IN, NULL);
+	mixer->set_input_right(mixer, MIX_MIC_IN, NULL);
+
+	hw->select_adc_source(hw, ADC_LINEIN);
+
+	src = atc->srcs[2];
+	mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc);
+	src = atc->srcs[3];
+	mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc);
+
+	return 0;
+}
+
+static int atc_select_mic_in(struct ct_atc *atc)
+{
+	struct hw *hw = atc->hw;
+	struct ct_mixer *mixer = atc->mixer;
+	struct src *src;
+
+	if (hw->is_adc_source_selected(hw, ADC_MICIN))
+		return 0;
+
+	mixer->set_input_left(mixer, MIX_LINE_IN, NULL);
+	mixer->set_input_right(mixer, MIX_LINE_IN, NULL);
+
+	hw->select_adc_source(hw, ADC_MICIN);
+
+	src = atc->srcs[2];
+	mixer->set_input_left(mixer, MIX_MIC_IN, &src->rsc);
+	src = atc->srcs[3];
+	mixer->set_input_right(mixer, MIX_MIC_IN, &src->rsc);
+
+	return 0;
+}
+
+static int atc_have_digit_io_switch(struct ct_atc *atc)
+{
+	struct hw *hw = atc->hw;
+
+	return hw->have_digit_io_switch(hw);
+}
+
+static int atc_select_digit_io(struct ct_atc *atc)
+{
+	struct hw *hw = atc->hw;
+
+	if (hw->is_adc_source_selected(hw, ADC_NONE))
+		return 0;
+
+	hw->select_adc_source(hw, ADC_NONE);
+
+	return 0;
+}
+
+static int atc_daio_unmute(struct ct_atc *atc, unsigned char state, int type)
+{
+	struct daio_mgr *daio_mgr = atc->rsc_mgrs[DAIO];
+
+	if (state)
+		daio_mgr->daio_enable(daio_mgr, atc->daios[type]);
+	else
+		daio_mgr->daio_disable(daio_mgr, atc->daios[type]);
+
+	daio_mgr->commit_write(daio_mgr);
+
+	return 0;
+}
+
+static int
+atc_dao_get_status(struct ct_atc *atc, unsigned int *status, int type)
+{
+	struct dao *dao = container_of(atc->daios[type], struct dao, daio);
+	return dao->ops->get_spos(dao, status);
+}
+
+static int
+atc_dao_set_status(struct ct_atc *atc, unsigned int status, int type)
+{
+	struct dao *dao = container_of(atc->daios[type], struct dao, daio);
+
+	dao->ops->set_spos(dao, status);
+	dao->ops->commit_write(dao);
+	return 0;
+}
+
+static int atc_line_front_unmute(struct ct_atc *atc, unsigned char state)
+{
+	return atc_daio_unmute(atc, state, LINEO1);
+}
+
+static int atc_line_surround_unmute(struct ct_atc *atc, unsigned char state)
+{
+	return atc_daio_unmute(atc, state, LINEO4);
+}
+
+static int atc_line_clfe_unmute(struct ct_atc *atc, unsigned char state)
+{
+	return atc_daio_unmute(atc, state, LINEO3);
+}
+
+static int atc_line_rear_unmute(struct ct_atc *atc, unsigned char state)
+{
+	return atc_daio_unmute(atc, state, LINEO2);
+}
+
+static int atc_line_in_unmute(struct ct_atc *atc, unsigned char state)
+{
+	return atc_daio_unmute(atc, state, LINEIM);
+}
+
+static int atc_spdif_out_unmute(struct ct_atc *atc, unsigned char state)
+{
+	return atc_daio_unmute(atc, state, SPDIFOO);
+}
+
+static int atc_spdif_in_unmute(struct ct_atc *atc, unsigned char state)
+{
+	return atc_daio_unmute(atc, state, SPDIFIO);
+}
+
+static int atc_spdif_out_get_status(struct ct_atc *atc, unsigned int *status)
+{
+	return atc_dao_get_status(atc, status, SPDIFOO);
+}
+
+static int atc_spdif_out_set_status(struct ct_atc *atc, unsigned int status)
+{
+	return atc_dao_set_status(atc, status, SPDIFOO);
+}
+
+static int atc_spdif_out_passthru(struct ct_atc *atc, unsigned char state)
+{
+	struct dao_desc da_dsc = {0};
+	struct dao *dao;
+	int err;
+	struct ct_mixer *mixer = atc->mixer;
+	struct rsc *rscs[2] = {NULL};
+	unsigned int spos = 0;
+
+	mutex_lock(&atc->atc_mutex);
+	dao = container_of(atc->daios[SPDIFOO], struct dao, daio);
+	da_dsc.msr = state ? 1 : atc->msr;
+	da_dsc.passthru = state ? 1 : 0;
+	err = dao->ops->reinit(dao, &da_dsc);
+	if (state) {
+		spos = IEC958_DEFAULT_CON;
+	} else {
+		mixer->get_output_ports(mixer, MIX_SPDIF_OUT,
+					&rscs[0], &rscs[1]);
+		dao->ops->set_left_input(dao, rscs[0]);
+		dao->ops->set_right_input(dao, rscs[1]);
+		/* Restore PLL to atc->rsr if needed. */
+		if (atc->pll_rate != atc->rsr)
+			err = atc_pll_init(atc, atc->rsr);
+	}
+	dao->ops->set_spos(dao, spos);
+	dao->ops->commit_write(dao);
+	mutex_unlock(&atc->atc_mutex);
+
+	return err;
+}
+
+static int atc_release_resources(struct ct_atc *atc)
+{
+	int i;
+	struct daio_mgr *daio_mgr = NULL;
+	struct dao *dao = NULL;
+	struct dai *dai = NULL;
+	struct daio *daio = NULL;
+	struct sum_mgr *sum_mgr = NULL;
+	struct src_mgr *src_mgr = NULL;
+	struct srcimp_mgr *srcimp_mgr = NULL;
+	struct srcimp *srcimp = NULL;
+	struct ct_mixer *mixer = NULL;
+
+	/* disconnect internal mixer objects */
+	if (NULL != atc->mixer) {
+		mixer = atc->mixer;
+		mixer->set_input_left(mixer, MIX_LINE_IN, NULL);
+		mixer->set_input_right(mixer, MIX_LINE_IN, NULL);
+		mixer->set_input_left(mixer, MIX_MIC_IN, NULL);
+		mixer->set_input_right(mixer, MIX_MIC_IN, NULL);
+		mixer->set_input_left(mixer, MIX_SPDIF_IN, NULL);
+		mixer->set_input_right(mixer, MIX_SPDIF_IN, NULL);
+	}
+
+	if (NULL != atc->daios) {
+		daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
+		for (i = 0; i < atc->n_daio; i++) {
+			daio = atc->daios[i];
+			if (daio->type < LINEIM) {
+				dao = container_of(daio, struct dao, daio);
+				dao->ops->clear_left_input(dao);
+				dao->ops->clear_right_input(dao);
+			} else {
+				dai = container_of(daio, struct dai, daio);
+				/* some thing to do for dai ... */
+			}
+			daio_mgr->put_daio(daio_mgr, daio);
+		}
+		kfree(atc->daios);
+		atc->daios = NULL;
+	}
+
+	if (NULL != atc->pcm) {
+		sum_mgr = atc->rsc_mgrs[SUM];
+		for (i = 0; i < atc->n_pcm; i++)
+			sum_mgr->put_sum(sum_mgr, atc->pcm[i]);
+
+		kfree(atc->pcm);
+		atc->pcm = NULL;
+	}
+
+	if (NULL != atc->srcs) {
+		src_mgr = atc->rsc_mgrs[SRC];
+		for (i = 0; i < atc->n_src; i++)
+			src_mgr->put_src(src_mgr, atc->srcs[i]);
+
+		kfree(atc->srcs);
+		atc->srcs = NULL;
+	}
+
+	if (NULL != atc->srcimps) {
+		srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+		for (i = 0; i < atc->n_srcimp; i++) {
+			srcimp = atc->srcimps[i];
+			srcimp->ops->unmap(srcimp);
+			srcimp_mgr->put_srcimp(srcimp_mgr, atc->srcimps[i]);
+		}
+		kfree(atc->srcimps);
+		atc->srcimps = NULL;
+	}
+
+	return 0;
+}
+
+static int ct_atc_destroy(struct ct_atc *atc)
+{
+	int i = 0;
+
+	if (NULL == atc)
+		return 0;
+
+	if (atc->timer) {
+		ct_timer_free(atc->timer);
+		atc->timer = NULL;
+	}
+
+	atc_release_resources(atc);
+
+	/* Destroy internal mixer objects */
+	if (NULL != atc->mixer)
+		ct_mixer_destroy(atc->mixer);
+
+	for (i = 0; i < NUM_RSCTYP; i++) {
+		if ((NULL != rsc_mgr_funcs[i].destroy) &&
+		    (NULL != atc->rsc_mgrs[i]))
+			rsc_mgr_funcs[i].destroy(atc->rsc_mgrs[i]);
+
+	}
+
+	if (NULL != atc->hw)
+		destroy_hw_obj((struct hw *)atc->hw);
+
+	/* Destroy device virtual memory manager object */
+	if (NULL != atc->vm) {
+		ct_vm_destroy(atc->vm);
+		atc->vm = NULL;
+	}
+
+	kfree(atc);
+
+	return 0;
+}
+
+static int atc_dev_free(struct snd_device *dev)
+{
+	struct ct_atc *atc = dev->device_data;
+	return ct_atc_destroy(atc);
+}
+
+static int __devinit atc_identify_card(struct ct_atc *atc)
+{
+	const struct snd_pci_quirk *p;
+	const struct snd_pci_quirk *list;
+
+	switch (atc->chip_type) {
+	case ATC20K1:
+		atc->chip_name = "20K1";
+		list = subsys_20k1_list;
+		break;
+	case ATC20K2:
+		atc->chip_name = "20K2";
+		list = subsys_20k2_list;
+		break;
+	default:
+		return -ENOENT;
+	}
+	p = snd_pci_quirk_lookup(atc->pci, list);
+	if (p) {
+		if (p->value < 0) {
+			printk(KERN_ERR "ctxfi: "
+			       "Device %04x:%04x is black-listed\n",
+			       atc->pci->subsystem_vendor,
+			       atc->pci->subsystem_device);
+			return -ENOENT;
+		}
+		atc->model = p->value;
+	} else {
+		if (atc->chip_type == ATC20K1)
+			atc->model = CT20K1_UNKNOWN;
+		else
+			atc->model = CT20K2_UNKNOWN;
+	}
+	atc->model_name = ct_subsys_name[atc->model];
+	snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n",
+		   atc->chip_name, atc->model_name,
+		   atc->pci->subsystem_vendor,
+		   atc->pci->subsystem_device);
+	return 0;
+}
+
+int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc)
+{
+	enum CTALSADEVS i;
+	int err;
+
+	alsa_dev_funcs[MIXER].public_name = atc->chip_name;
+
+	for (i = 0; i < NUM_CTALSADEVS; i++) {
+		if (NULL == alsa_dev_funcs[i].create)
+			continue;
+
+		err = alsa_dev_funcs[i].create(atc, i,
+				alsa_dev_funcs[i].public_name);
+		if (err) {
+			printk(KERN_ERR "ctxfi: "
+			       "Creating alsa device %d failed!\n", i);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static int __devinit atc_create_hw_devs(struct ct_atc *atc)
+{
+	struct hw *hw;
+	struct card_conf info = {0};
+	int i, err;
+
+	err = create_hw_obj(atc->pci, atc->chip_type, atc->model, &hw);
+	if (err) {
+		printk(KERN_ERR "Failed to create hw obj!!!\n");
+		return err;
+	}
+	atc->hw = hw;
+
+	/* Initialize card hardware. */
+	info.rsr = atc->rsr;
+	info.msr = atc->msr;
+	info.vm_pgt_phys = atc_get_ptp_phys(atc, 0);
+	err = hw->card_init(hw, &info);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < NUM_RSCTYP; i++) {
+		if (NULL == rsc_mgr_funcs[i].create)
+			continue;
+
+		err = rsc_mgr_funcs[i].create(atc->hw, &atc->rsc_mgrs[i]);
+		if (err) {
+			printk(KERN_ERR "ctxfi: "
+			       "Failed to create rsc_mgr %d!!!\n", i);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static int atc_get_resources(struct ct_atc *atc)
+{
+	struct daio_desc da_desc = {0};
+	struct daio_mgr *daio_mgr;
+	struct src_desc src_dsc = {0};
+	struct src_mgr *src_mgr;
+	struct srcimp_desc srcimp_dsc = {0};
+	struct srcimp_mgr *srcimp_mgr;
+	struct sum_desc sum_dsc = {0};
+	struct sum_mgr *sum_mgr;
+	int err, i;
+
+	atc->daios = kzalloc(sizeof(void *)*(DAIONUM), GFP_KERNEL);
+	if (NULL == atc->daios)
+		return -ENOMEM;
+
+	atc->srcs = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+	if (NULL == atc->srcs)
+		return -ENOMEM;
+
+	atc->srcimps = kzalloc(sizeof(void *)*(2*2), GFP_KERNEL);
+	if (NULL == atc->srcimps)
+		return -ENOMEM;
+
+	atc->pcm = kzalloc(sizeof(void *)*(2*4), GFP_KERNEL);
+	if (NULL == atc->pcm)
+		return -ENOMEM;
+
+	daio_mgr = (struct daio_mgr *)atc->rsc_mgrs[DAIO];
+	da_desc.msr = atc->msr;
+	for (i = 0, atc->n_daio = 0; i < DAIONUM-1; i++) {
+		da_desc.type = i;
+		err = daio_mgr->get_daio(daio_mgr, &da_desc,
+					(struct daio **)&atc->daios[i]);
+		if (err) {
+			printk(KERN_ERR "ctxfi: Failed to get DAIO "
+					"resource %d!!!\n", i);
+			return err;
+		}
+		atc->n_daio++;
+	}
+	if (atc->model == CTSB073X)
+		da_desc.type = SPDIFI1;
+	else
+		da_desc.type = SPDIFIO;
+	err = daio_mgr->get_daio(daio_mgr, &da_desc,
+				(struct daio **)&atc->daios[i]);
+	if (err) {
+		printk(KERN_ERR "ctxfi: Failed to get S/PDIF-in resource!!!\n");
+		return err;
+	}
+	atc->n_daio++;
+
+	src_mgr = atc->rsc_mgrs[SRC];
+	src_dsc.multi = 1;
+	src_dsc.msr = atc->msr;
+	src_dsc.mode = ARCRW;
+	for (i = 0, atc->n_src = 0; i < (2*2); i++) {
+		err = src_mgr->get_src(src_mgr, &src_dsc,
+					(struct src **)&atc->srcs[i]);
+		if (err)
+			return err;
+
+		atc->n_src++;
+	}
+
+	srcimp_mgr = atc->rsc_mgrs[SRCIMP];
+	srcimp_dsc.msr = 8; /* SRCIMPs for S/PDIFIn SRT */
+	for (i = 0, atc->n_srcimp = 0; i < (2*1); i++) {
+		err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
+					(struct srcimp **)&atc->srcimps[i]);
+		if (err)
+			return err;
+
+		atc->n_srcimp++;
+	}
+	srcimp_dsc.msr = 8; /* SRCIMPs for LINE/MICIn SRT */
+	for (i = 0; i < (2*1); i++) {
+		err = srcimp_mgr->get_srcimp(srcimp_mgr, &srcimp_dsc,
+				(struct srcimp **)&atc->srcimps[2*1+i]);
+		if (err)
+			return err;
+
+		atc->n_srcimp++;
+	}
+
+	sum_mgr = atc->rsc_mgrs[SUM];
+	sum_dsc.msr = atc->msr;
+	for (i = 0, atc->n_pcm = 0; i < (2*4); i++) {
+		err = sum_mgr->get_sum(sum_mgr, &sum_dsc,
+					(struct sum **)&atc->pcm[i]);
+		if (err)
+			return err;
+
+		atc->n_pcm++;
+	}
+
+	return 0;
+}
+
+static void
+atc_connect_dai(struct src_mgr *src_mgr, struct dai *dai,
+		struct src **srcs, struct srcimp **srcimps)
+{
+	struct rsc *rscs[2] = {NULL};
+	struct src *src;
+	struct srcimp *srcimp;
+	int i = 0;
+
+	rscs[0] = &dai->daio.rscl;
+	rscs[1] = &dai->daio.rscr;
+	for (i = 0; i < 2; i++) {
+		src = srcs[i];
+		srcimp = srcimps[i];
+		srcimp->ops->map(srcimp, src, rscs[i]);
+		src_mgr->src_disable(src_mgr, src);
+	}
+
+	src_mgr->commit_write(src_mgr); /* Actually disable SRCs */
+
+	src = srcs[0];
+	src->ops->set_pm(src, 1);
+	for (i = 0; i < 2; i++) {
+		src = srcs[i];
+		src->ops->set_state(src, SRC_STATE_RUN);
+		src->ops->commit_write(src);
+		src_mgr->src_enable_s(src_mgr, src);
+	}
+
+	dai->ops->set_srt_srcl(dai, &(srcs[0]->rsc));
+	dai->ops->set_srt_srcr(dai, &(srcs[1]->rsc));
+
+	dai->ops->set_enb_src(dai, 1);
+	dai->ops->set_enb_srt(dai, 1);
+	dai->ops->commit_write(dai);
+
+	src_mgr->commit_write(src_mgr); /* Synchronously enable SRCs */
+}
+
+static void atc_connect_resources(struct ct_atc *atc)
+{
+	struct dai *dai;
+	struct dao *dao;
+	struct src *src;
+	struct sum *sum;
+	struct ct_mixer *mixer;
+	struct rsc *rscs[2] = {NULL};
+	int i, j;
+
+	mixer = atc->mixer;
+
+	for (i = MIX_WAVE_FRONT, j = LINEO1; i <= MIX_SPDIF_OUT; i++, j++) {
+		mixer->get_output_ports(mixer, i, &rscs[0], &rscs[1]);
+		dao = container_of(atc->daios[j], struct dao, daio);
+		dao->ops->set_left_input(dao, rscs[0]);
+		dao->ops->set_right_input(dao, rscs[1]);
+	}
+
+	dai = container_of(atc->daios[LINEIM], struct dai, daio);
+	atc_connect_dai(atc->rsc_mgrs[SRC], dai,
+			(struct src **)&atc->srcs[2],
+			(struct srcimp **)&atc->srcimps[2]);
+	src = atc->srcs[2];
+	mixer->set_input_left(mixer, MIX_LINE_IN, &src->rsc);
+	src = atc->srcs[3];
+	mixer->set_input_right(mixer, MIX_LINE_IN, &src->rsc);
+
+	dai = container_of(atc->daios[SPDIFIO], struct dai, daio);
+	atc_connect_dai(atc->rsc_mgrs[SRC], dai,
+			(struct src **)&atc->srcs[0],
+			(struct srcimp **)&atc->srcimps[0]);
+
+	src = atc->srcs[0];
+	mixer->set_input_left(mixer, MIX_SPDIF_IN, &src->rsc);
+	src = atc->srcs[1];
+	mixer->set_input_right(mixer, MIX_SPDIF_IN, &src->rsc);
+
+	for (i = MIX_PCMI_FRONT, j = 0; i <= MIX_PCMI_SURROUND; i++, j += 2) {
+		sum = atc->pcm[j];
+		mixer->set_input_left(mixer, i, &sum->rsc);
+		sum = atc->pcm[j+1];
+		mixer->set_input_right(mixer, i, &sum->rsc);
+	}
+}
+
+#ifdef CONFIG_PM
+static int atc_suspend(struct ct_atc *atc, pm_message_t state)
+{
+	int i;
+	struct hw *hw = atc->hw;
+
+	snd_power_change_state(atc->card, SNDRV_CTL_POWER_D3hot);
+
+	for (i = FRONT; i < NUM_PCMS; i++) {
+		if (!atc->pcms[i])
+			continue;
+
+		snd_pcm_suspend_all(atc->pcms[i]);
+	}
+
+	atc_release_resources(atc);
+
+	hw->suspend(hw, state);
+
+	return 0;
+}
+
+static int atc_hw_resume(struct ct_atc *atc)
+{
+	struct hw *hw = atc->hw;
+	struct card_conf info = {0};
+
+	/* Re-initialize card hardware. */
+	info.rsr = atc->rsr;
+	info.msr = atc->msr;
+	info.vm_pgt_phys = atc_get_ptp_phys(atc, 0);
+	return hw->resume(hw, &info);
+}
+
+static int atc_resources_resume(struct ct_atc *atc)
+{
+	struct ct_mixer *mixer;
+	int err = 0;
+
+	/* Get resources */
+	err = atc_get_resources(atc);
+	if (err < 0) {
+		atc_release_resources(atc);
+		return err;
+	}
+
+	/* Build topology */
+	atc_connect_resources(atc);
+
+	mixer = atc->mixer;
+	mixer->resume(mixer);
+
+	return 0;
+}
+
+static int atc_resume(struct ct_atc *atc)
+{
+	int err = 0;
+
+	/* Do hardware resume. */
+	err = atc_hw_resume(atc);
+	if (err < 0) {
+		printk(KERN_ERR "ctxfi: pci_enable_device failed, "
+		       "disabling device\n");
+		snd_card_disconnect(atc->card);
+		return err;
+	}
+
+	err = atc_resources_resume(atc);
+	if (err < 0)
+		return err;
+
+	snd_power_change_state(atc->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+#endif
+
+static struct ct_atc atc_preset __devinitdata = {
+	.map_audio_buffer = ct_map_audio_buffer,
+	.unmap_audio_buffer = ct_unmap_audio_buffer,
+	.pcm_playback_prepare = atc_pcm_playback_prepare,
+	.pcm_release_resources = atc_pcm_release_resources,
+	.pcm_playback_start = atc_pcm_playback_start,
+	.pcm_playback_stop = atc_pcm_stop,
+	.pcm_playback_position = atc_pcm_playback_position,
+	.pcm_capture_prepare = atc_pcm_capture_prepare,
+	.pcm_capture_start = atc_pcm_capture_start,
+	.pcm_capture_stop = atc_pcm_stop,
+	.pcm_capture_position = atc_pcm_capture_position,
+	.spdif_passthru_playback_prepare = spdif_passthru_playback_prepare,
+	.get_ptp_phys = atc_get_ptp_phys,
+	.select_line_in = atc_select_line_in,
+	.select_mic_in = atc_select_mic_in,
+	.select_digit_io = atc_select_digit_io,
+	.line_front_unmute = atc_line_front_unmute,
+	.line_surround_unmute = atc_line_surround_unmute,
+	.line_clfe_unmute = atc_line_clfe_unmute,
+	.line_rear_unmute = atc_line_rear_unmute,
+	.line_in_unmute = atc_line_in_unmute,
+	.spdif_out_unmute = atc_spdif_out_unmute,
+	.spdif_in_unmute = atc_spdif_in_unmute,
+	.spdif_out_get_status = atc_spdif_out_get_status,
+	.spdif_out_set_status = atc_spdif_out_set_status,
+	.spdif_out_passthru = atc_spdif_out_passthru,
+	.have_digit_io_switch = atc_have_digit_io_switch,
+#ifdef CONFIG_PM
+	.suspend = atc_suspend,
+	.resume = atc_resume,
+#endif
+};
+
+/**
+ *  ct_atc_create - create and initialize a hardware manager
+ *  @card: corresponding alsa card object
+ *  @pci: corresponding kernel pci device object
+ *  @ratc: return created object address in it
+ *
+ *  Creates and initializes a hardware manager.
+ *
+ *  Creates kmallocated ct_atc structure. Initializes hardware.
+ *  Returns 0 if suceeds, or negative error code if fails.
+ */
+
+int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
+			    unsigned int rsr, unsigned int msr,
+			    int chip_type, struct ct_atc **ratc)
+{
+	struct ct_atc *atc;
+	static struct snd_device_ops ops = {
+		.dev_free = atc_dev_free,
+	};
+	int err;
+
+	*ratc = NULL;
+
+	atc = kzalloc(sizeof(*atc), GFP_KERNEL);
+	if (NULL == atc)
+		return -ENOMEM;
+
+	/* Set operations */
+	*atc = atc_preset;
+
+	atc->card = card;
+	atc->pci = pci;
+	atc->rsr = rsr;
+	atc->msr = msr;
+	atc->chip_type = chip_type;
+
+	mutex_init(&atc->atc_mutex);
+
+	/* Find card model */
+	err = atc_identify_card(atc);
+	if (err < 0) {
+		printk(KERN_ERR "ctatc: Card not recognised\n");
+		goto error1;
+	}
+
+	/* Set up device virtual memory management object */
+	err = ct_vm_create(&atc->vm);
+	if (err < 0)
+		goto error1;
+
+	/* Create all atc hw devices */
+	err = atc_create_hw_devs(atc);
+	if (err < 0)
+		goto error1;
+
+	err = ct_mixer_create(atc, (struct ct_mixer **)&atc->mixer);
+	if (err) {
+		printk(KERN_ERR "ctxfi: Failed to create mixer obj!!!\n");
+		goto error1;
+	}
+
+	/* Get resources */
+	err = atc_get_resources(atc);
+	if (err < 0)
+		goto error1;
+
+	/* Build topology */
+	atc_connect_resources(atc);
+
+	atc->timer = ct_timer_new(atc);
+	if (!atc->timer)
+		goto error1;
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops);
+	if (err < 0)
+		goto error1;
+
+	snd_card_set_dev(card, &pci->dev);
+
+	*ratc = atc;
+	return 0;
+
+error1:
+	ct_atc_destroy(atc);
+	printk(KERN_ERR "ctxfi: Something wrong!!!\n");
+	return err;
+}
diff --git a/sound/pci/ctxfi/ctatc.h b/sound/pci/ctxfi/ctatc.h
new file mode 100644
index 0000000..9fd8a57
--- /dev/null
+++ b/sound/pci/ctxfi/ctatc.h
@@ -0,0 +1,154 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctatc.h
+ *
+ * @Brief
+ * This file contains the definition of the device resource management object.
+ *
+ * @Author	Liu Chun
+ * @Date 	Mar 28 2008
+ *
+ */
+
+#ifndef CTATC_H
+#define CTATC_H
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/pci.h>
+#include <linux/timer.h>
+#include <sound/core.h>
+
+#include "ctvmem.h"
+#include "ctresource.h"
+
+enum CTALSADEVS {		/* Types of alsa devices */
+	FRONT,
+	SURROUND,
+	CLFE,
+	SIDE,
+	IEC958,
+	MIXER,
+	NUM_CTALSADEVS		/* This should always be the last */
+};
+
+struct ct_atc_chip_sub_details {
+	u16 subsys;
+	const char *nm_model;
+};
+
+struct ct_atc_chip_details {
+	u16 vendor;
+	u16 device;
+	const struct ct_atc_chip_sub_details *sub_details;
+	const char *nm_card;
+};
+
+struct ct_atc;
+struct ct_timer;
+struct ct_timer_instance;
+
+/* alsa pcm stream descriptor */
+struct ct_atc_pcm {
+	struct snd_pcm_substream *substream;
+	void (*interrupt)(struct ct_atc_pcm *apcm);
+	struct ct_timer_instance *timer;
+	unsigned int started:1;
+
+	/* Only mono and interleaved modes are supported now. */
+	struct ct_vm_block *vm_block;
+	void *src;		/* SRC for interacting with host memory */
+	void **srccs;		/* SRCs for sample rate conversion */
+	void **srcimps;		/* SRC Input Mappers */
+	void **amixers;		/* AMIXERs for routing converted data */
+	void *mono;		/* A SUM resource for mixing chs to one */
+	unsigned char n_srcc;	/* Number of converting SRCs */
+	unsigned char n_srcimp;	/* Number of SRC Input Mappers */
+	unsigned char n_amixer;	/* Number of AMIXERs */
+};
+
+/* Chip resource management object */
+struct ct_atc {
+	struct pci_dev *pci;
+	struct snd_card *card;
+	unsigned int rsr; /* reference sample rate in Hz */
+	unsigned int msr; /* master sample rate in rsr */
+	unsigned int pll_rate; /* current rate of Phase Lock Loop */
+
+	int chip_type;
+	int model;
+	const char *chip_name;
+	const char *model_name;
+
+	struct ct_vm *vm; /* device virtual memory manager for this card */
+	int (*map_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+	void (*unmap_audio_buffer)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+	unsigned long (*get_ptp_phys)(struct ct_atc *atc, int index);
+
+	struct mutex atc_mutex;
+
+	int (*pcm_playback_prepare)(struct ct_atc *atc,
+				    struct ct_atc_pcm *apcm);
+	int (*pcm_playback_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+	int (*pcm_playback_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+	int (*pcm_playback_position)(struct ct_atc *atc,
+				     struct ct_atc_pcm *apcm);
+	int (*spdif_passthru_playback_prepare)(struct ct_atc *atc,
+					       struct ct_atc_pcm *apcm);
+	int (*pcm_capture_prepare)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+	int (*pcm_capture_start)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+	int (*pcm_capture_stop)(struct ct_atc *atc, struct ct_atc_pcm *apcm);
+	int (*pcm_capture_position)(struct ct_atc *atc,
+				    struct ct_atc_pcm *apcm);
+	int (*pcm_release_resources)(struct ct_atc *atc,
+				     struct ct_atc_pcm *apcm);
+	int (*select_line_in)(struct ct_atc *atc);
+	int (*select_mic_in)(struct ct_atc *atc);
+	int (*select_digit_io)(struct ct_atc *atc);
+	int (*line_front_unmute)(struct ct_atc *atc, unsigned char state);
+	int (*line_surround_unmute)(struct ct_atc *atc, unsigned char state);
+	int (*line_clfe_unmute)(struct ct_atc *atc, unsigned char state);
+	int (*line_rear_unmute)(struct ct_atc *atc, unsigned char state);
+	int (*line_in_unmute)(struct ct_atc *atc, unsigned char state);
+	int (*spdif_out_unmute)(struct ct_atc *atc, unsigned char state);
+	int (*spdif_in_unmute)(struct ct_atc *atc, unsigned char state);
+	int (*spdif_out_get_status)(struct ct_atc *atc, unsigned int *status);
+	int (*spdif_out_set_status)(struct ct_atc *atc, unsigned int status);
+	int (*spdif_out_passthru)(struct ct_atc *atc, unsigned char state);
+	int (*have_digit_io_switch)(struct ct_atc *atc);
+
+	/* Don't touch! Used for internal object. */
+	void *rsc_mgrs[NUM_RSCTYP]; /* chip resource managers */
+	void *mixer;		/* internal mixer object */
+	void *hw;		/* chip specific hardware access object */
+	void **daios;		/* digital audio io resources */
+	void **pcm;		/* SUMs for collecting all pcm stream */
+	void **srcs;		/* Sample Rate Converters for input signal */
+	void **srcimps;		/* input mappers for SRCs */
+	unsigned char n_daio;
+	unsigned char n_src;
+	unsigned char n_srcimp;
+	unsigned char n_pcm;
+
+	struct ct_timer *timer;
+
+#ifdef CONFIG_PM
+	int (*suspend)(struct ct_atc *atc, pm_message_t state);
+	int (*resume)(struct ct_atc *atc);
+#define NUM_PCMS (NUM_CTALSADEVS - 1)
+	struct snd_pcm *pcms[NUM_PCMS];
+#endif
+};
+
+
+int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
+			    unsigned int rsr, unsigned int msr, int chip_type,
+			    struct ct_atc **ratc);
+int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc);
+
+#endif /* CTATC_H */
diff --git a/sound/pci/ctxfi/ctdaio.c b/sound/pci/ctxfi/ctdaio.c
new file mode 100644
index 0000000..082e35c
--- /dev/null
+++ b/sound/pci/ctxfi/ctdaio.c
@@ -0,0 +1,769 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctdaio.c
+ *
+ * @Brief
+ * This file contains the implementation of Digital Audio Input Output
+ * resource management object.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 23 2008
+ *
+ */
+
+#include "ctdaio.h"
+#include "cthardware.h"
+#include "ctimap.h"
+#include <linux/slab.h>
+#include <linux/kernel.h>
+
+#define DAIO_RESOURCE_NUM	NUM_DAIOTYP
+#define DAIO_OUT_MAX		SPDIFOO
+
+union daio_usage {
+	struct {
+		unsigned short lineo1:1;
+		unsigned short lineo2:1;
+		unsigned short lineo3:1;
+		unsigned short lineo4:1;
+		unsigned short spdifoo:1;
+		unsigned short lineim:1;
+		unsigned short spdifio:1;
+		unsigned short spdifi1:1;
+	} bf;
+	unsigned short data;
+};
+
+struct daio_rsc_idx {
+	unsigned short left;
+	unsigned short right;
+};
+
+struct daio_rsc_idx idx_20k1[NUM_DAIOTYP] = {
+	[LINEO1] = {.left = 0x00, .right = 0x01},
+	[LINEO2] = {.left = 0x18, .right = 0x19},
+	[LINEO3] = {.left = 0x08, .right = 0x09},
+	[LINEO4] = {.left = 0x10, .right = 0x11},
+	[LINEIM] = {.left = 0x1b5, .right = 0x1bd},
+	[SPDIFOO] = {.left = 0x20, .right = 0x21},
+	[SPDIFIO] = {.left = 0x15, .right = 0x1d},
+	[SPDIFI1] = {.left = 0x95, .right = 0x9d},
+};
+
+struct daio_rsc_idx idx_20k2[NUM_DAIOTYP] = {
+	[LINEO1] = {.left = 0x40, .right = 0x41},
+	[LINEO2] = {.left = 0x70, .right = 0x71},
+	[LINEO3] = {.left = 0x50, .right = 0x51},
+	[LINEO4] = {.left = 0x60, .right = 0x61},
+	[LINEIM] = {.left = 0x45, .right = 0xc5},
+	[SPDIFOO] = {.left = 0x00, .right = 0x01},
+	[SPDIFIO] = {.left = 0x05, .right = 0x85},
+};
+
+static int daio_master(struct rsc *rsc)
+{
+	/* Actually, this is not the resource index of DAIO.
+	 * For DAO, it is the input mapper index. And, for DAI,
+	 * it is the output time-slot index. */
+	return rsc->conj = rsc->idx;
+}
+
+static int daio_index(const struct rsc *rsc)
+{
+	return rsc->conj;
+}
+
+static int daio_out_next_conj(struct rsc *rsc)
+{
+	return rsc->conj += 2;
+}
+
+static int daio_in_next_conj_20k1(struct rsc *rsc)
+{
+	return rsc->conj += 0x200;
+}
+
+static int daio_in_next_conj_20k2(struct rsc *rsc)
+{
+	return rsc->conj += 0x100;
+}
+
+static struct rsc_ops daio_out_rsc_ops = {
+	.master		= daio_master,
+	.next_conj	= daio_out_next_conj,
+	.index		= daio_index,
+	.output_slot	= NULL,
+};
+
+static struct rsc_ops daio_in_rsc_ops_20k1 = {
+	.master		= daio_master,
+	.next_conj	= daio_in_next_conj_20k1,
+	.index		= NULL,
+	.output_slot	= daio_index,
+};
+
+static struct rsc_ops daio_in_rsc_ops_20k2 = {
+	.master		= daio_master,
+	.next_conj	= daio_in_next_conj_20k2,
+	.index		= NULL,
+	.output_slot	= daio_index,
+};
+
+static unsigned int daio_device_index(enum DAIOTYP type, struct hw *hw)
+{
+	switch (hw->chip_type) {
+	case ATC20K1:
+		switch (type) {
+		case SPDIFOO:	return 0;
+		case SPDIFIO:	return 0;
+		case SPDIFI1:	return 1;
+		case LINEO1:	return 4;
+		case LINEO2:	return 7;
+		case LINEO3:	return 5;
+		case LINEO4:	return 6;
+		case LINEIM:	return 7;
+		default:	return -EINVAL;
+		}
+	case ATC20K2:
+		switch (type) {
+		case SPDIFOO:	return 0;
+		case SPDIFIO:	return 0;
+		case LINEO1:	return 4;
+		case LINEO2:	return 7;
+		case LINEO3:	return 5;
+		case LINEO4:	return 6;
+		case LINEIM:	return 4;
+		default:	return -EINVAL;
+		}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc);
+
+static int dao_spdif_get_spos(struct dao *dao, unsigned int *spos)
+{
+	((struct hw *)dao->hw)->dao_get_spos(dao->ctrl_blk, spos);
+	return 0;
+}
+
+static int dao_spdif_set_spos(struct dao *dao, unsigned int spos)
+{
+	((struct hw *)dao->hw)->dao_set_spos(dao->ctrl_blk, spos);
+	return 0;
+}
+
+static int dao_commit_write(struct dao *dao)
+{
+	((struct hw *)dao->hw)->dao_commit_write(dao->hw,
+		daio_device_index(dao->daio.type, dao->hw), dao->ctrl_blk);
+	return 0;
+}
+
+static int dao_set_left_input(struct dao *dao, struct rsc *input)
+{
+	struct imapper *entry;
+	struct daio *daio = &dao->daio;
+	int i;
+
+	entry = kzalloc((sizeof(*entry) * daio->rscl.msr), GFP_KERNEL);
+	if (NULL == entry)
+		return -ENOMEM;
+
+	/* Program master and conjugate resources */
+	input->ops->master(input);
+	daio->rscl.ops->master(&daio->rscl);
+	for (i = 0; i < daio->rscl.msr; i++, entry++) {
+		entry->slot = input->ops->output_slot(input);
+		entry->user = entry->addr = daio->rscl.ops->index(&daio->rscl);
+		dao->mgr->imap_add(dao->mgr, entry);
+		dao->imappers[i] = entry;
+
+		input->ops->next_conj(input);
+		daio->rscl.ops->next_conj(&daio->rscl);
+	}
+	input->ops->master(input);
+	daio->rscl.ops->master(&daio->rscl);
+
+	return 0;
+}
+
+static int dao_set_right_input(struct dao *dao, struct rsc *input)
+{
+	struct imapper *entry;
+	struct daio *daio = &dao->daio;
+	int i;
+
+	entry = kzalloc((sizeof(*entry) * daio->rscr.msr), GFP_KERNEL);
+	if (NULL == entry)
+		return -ENOMEM;
+
+	/* Program master and conjugate resources */
+	input->ops->master(input);
+	daio->rscr.ops->master(&daio->rscr);
+	for (i = 0; i < daio->rscr.msr; i++, entry++) {
+		entry->slot = input->ops->output_slot(input);
+		entry->user = entry->addr = daio->rscr.ops->index(&daio->rscr);
+		dao->mgr->imap_add(dao->mgr, entry);
+		dao->imappers[daio->rscl.msr + i] = entry;
+
+		input->ops->next_conj(input);
+		daio->rscr.ops->next_conj(&daio->rscr);
+	}
+	input->ops->master(input);
+	daio->rscr.ops->master(&daio->rscr);
+
+	return 0;
+}
+
+static int dao_clear_left_input(struct dao *dao)
+{
+	struct imapper *entry;
+	struct daio *daio = &dao->daio;
+	int i;
+
+	if (NULL == dao->imappers[0])
+		return 0;
+
+	entry = dao->imappers[0];
+	dao->mgr->imap_delete(dao->mgr, entry);
+	/* Program conjugate resources */
+	for (i = 1; i < daio->rscl.msr; i++) {
+		entry = dao->imappers[i];
+		dao->mgr->imap_delete(dao->mgr, entry);
+		dao->imappers[i] = NULL;
+	}
+
+	kfree(dao->imappers[0]);
+	dao->imappers[0] = NULL;
+
+	return 0;
+}
+
+static int dao_clear_right_input(struct dao *dao)
+{
+	struct imapper *entry;
+	struct daio *daio = &dao->daio;
+	int i;
+
+	if (NULL == dao->imappers[daio->rscl.msr])
+		return 0;
+
+	entry = dao->imappers[daio->rscl.msr];
+	dao->mgr->imap_delete(dao->mgr, entry);
+	/* Program conjugate resources */
+	for (i = 1; i < daio->rscr.msr; i++) {
+		entry = dao->imappers[daio->rscl.msr + i];
+		dao->mgr->imap_delete(dao->mgr, entry);
+		dao->imappers[daio->rscl.msr + i] = NULL;
+	}
+
+	kfree(dao->imappers[daio->rscl.msr]);
+	dao->imappers[daio->rscl.msr] = NULL;
+
+	return 0;
+}
+
+static struct dao_rsc_ops dao_ops = {
+	.set_spos		= dao_spdif_set_spos,
+	.commit_write		= dao_commit_write,
+	.get_spos		= dao_spdif_get_spos,
+	.reinit			= dao_rsc_reinit,
+	.set_left_input		= dao_set_left_input,
+	.set_right_input	= dao_set_right_input,
+	.clear_left_input	= dao_clear_left_input,
+	.clear_right_input	= dao_clear_right_input,
+};
+
+static int dai_set_srt_srcl(struct dai *dai, struct rsc *src)
+{
+	src->ops->master(src);
+	((struct hw *)dai->hw)->dai_srt_set_srcm(dai->ctrl_blk,
+						src->ops->index(src));
+	return 0;
+}
+
+static int dai_set_srt_srcr(struct dai *dai, struct rsc *src)
+{
+	src->ops->master(src);
+	((struct hw *)dai->hw)->dai_srt_set_srco(dai->ctrl_blk,
+						src->ops->index(src));
+	return 0;
+}
+
+static int dai_set_srt_msr(struct dai *dai, unsigned int msr)
+{
+	unsigned int rsr;
+
+	for (rsr = 0; msr > 1; msr >>= 1)
+		rsr++;
+
+	((struct hw *)dai->hw)->dai_srt_set_rsr(dai->ctrl_blk, rsr);
+	return 0;
+}
+
+static int dai_set_enb_src(struct dai *dai, unsigned int enb)
+{
+	((struct hw *)dai->hw)->dai_srt_set_ec(dai->ctrl_blk, enb);
+	return 0;
+}
+
+static int dai_set_enb_srt(struct dai *dai, unsigned int enb)
+{
+	((struct hw *)dai->hw)->dai_srt_set_et(dai->ctrl_blk, enb);
+	return 0;
+}
+
+static int dai_commit_write(struct dai *dai)
+{
+	((struct hw *)dai->hw)->dai_commit_write(dai->hw,
+		daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
+	return 0;
+}
+
+static struct dai_rsc_ops dai_ops = {
+	.set_srt_srcl		= dai_set_srt_srcl,
+	.set_srt_srcr		= dai_set_srt_srcr,
+	.set_srt_msr		= dai_set_srt_msr,
+	.set_enb_src		= dai_set_enb_src,
+	.set_enb_srt		= dai_set_enb_srt,
+	.commit_write		= dai_commit_write,
+};
+
+static int daio_rsc_init(struct daio *daio,
+			 const struct daio_desc *desc,
+			 void *hw)
+{
+	int err;
+	unsigned int idx_l, idx_r;
+
+	switch (((struct hw *)hw)->chip_type) {
+	case ATC20K1:
+		idx_l = idx_20k1[desc->type].left;
+		idx_r = idx_20k1[desc->type].right;
+		break;
+	case ATC20K2:
+		idx_l = idx_20k2[desc->type].left;
+		idx_r = idx_20k2[desc->type].right;
+		break;
+	default:
+		return -EINVAL;
+	}
+	err = rsc_init(&daio->rscl, idx_l, DAIO, desc->msr, hw);
+	if (err)
+		return err;
+
+	err = rsc_init(&daio->rscr, idx_r, DAIO, desc->msr, hw);
+	if (err)
+		goto error1;
+
+	/* Set daio->rscl/r->ops to daio specific ones */
+	if (desc->type <= DAIO_OUT_MAX) {
+		daio->rscl.ops = daio->rscr.ops = &daio_out_rsc_ops;
+	} else {
+		switch (((struct hw *)hw)->chip_type) {
+		case ATC20K1:
+			daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k1;
+			break;
+		case ATC20K2:
+			daio->rscl.ops = daio->rscr.ops = &daio_in_rsc_ops_20k2;
+			break;
+		default:
+			break;
+		}
+	}
+	daio->type = desc->type;
+
+	return 0;
+
+error1:
+	rsc_uninit(&daio->rscl);
+	return err;
+}
+
+static int daio_rsc_uninit(struct daio *daio)
+{
+	rsc_uninit(&daio->rscl);
+	rsc_uninit(&daio->rscr);
+
+	return 0;
+}
+
+static int dao_rsc_init(struct dao *dao,
+			const struct daio_desc *desc,
+			struct daio_mgr *mgr)
+{
+	struct hw *hw = mgr->mgr.hw;
+	unsigned int conf;
+	int err;
+
+	err = daio_rsc_init(&dao->daio, desc, mgr->mgr.hw);
+	if (err)
+		return err;
+
+	dao->imappers = kzalloc(sizeof(void *)*desc->msr*2, GFP_KERNEL);
+	if (NULL == dao->imappers) {
+		err = -ENOMEM;
+		goto error1;
+	}
+	dao->ops = &dao_ops;
+	dao->mgr = mgr;
+	dao->hw = hw;
+	err = hw->dao_get_ctrl_blk(&dao->ctrl_blk);
+	if (err)
+		goto error2;
+
+	hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
+			daio_device_index(dao->daio.type, hw));
+	hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+
+	conf = (desc->msr & 0x7) | (desc->passthru << 3);
+	hw->daio_mgr_dao_init(mgr->mgr.ctrl_blk,
+			daio_device_index(dao->daio.type, hw), conf);
+	hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
+			daio_device_index(dao->daio.type, hw));
+	hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+
+	return 0;
+
+error2:
+	kfree(dao->imappers);
+	dao->imappers = NULL;
+error1:
+	daio_rsc_uninit(&dao->daio);
+	return err;
+}
+
+static int dao_rsc_uninit(struct dao *dao)
+{
+	if (NULL != dao->imappers) {
+		if (NULL != dao->imappers[0])
+			dao_clear_left_input(dao);
+
+		if (NULL != dao->imappers[dao->daio.rscl.msr])
+			dao_clear_right_input(dao);
+
+		kfree(dao->imappers);
+		dao->imappers = NULL;
+	}
+	((struct hw *)dao->hw)->dao_put_ctrl_blk(dao->ctrl_blk);
+	dao->hw = dao->ctrl_blk = NULL;
+	daio_rsc_uninit(&dao->daio);
+
+	return 0;
+}
+
+static int dao_rsc_reinit(struct dao *dao, const struct dao_desc *desc)
+{
+	struct daio_mgr *mgr = dao->mgr;
+	struct daio_desc dsc = {0};
+
+	dsc.type = dao->daio.type;
+	dsc.msr = desc->msr;
+	dsc.passthru = desc->passthru;
+	dao_rsc_uninit(dao);
+	return dao_rsc_init(dao, &dsc, mgr);
+}
+
+static int dai_rsc_init(struct dai *dai,
+			const struct daio_desc *desc,
+			struct daio_mgr *mgr)
+{
+	int err;
+	struct hw *hw = mgr->mgr.hw;
+	unsigned int rsr, msr;
+
+	err = daio_rsc_init(&dai->daio, desc, mgr->mgr.hw);
+	if (err)
+		return err;
+
+	dai->ops = &dai_ops;
+	dai->hw = mgr->mgr.hw;
+	err = hw->dai_get_ctrl_blk(&dai->ctrl_blk);
+	if (err)
+		goto error1;
+
+	for (rsr = 0, msr = desc->msr; msr > 1; msr >>= 1)
+		rsr++;
+
+	hw->dai_srt_set_rsr(dai->ctrl_blk, rsr);
+	hw->dai_srt_set_drat(dai->ctrl_blk, 0);
+	/* default to disabling control of a SRC */
+	hw->dai_srt_set_ec(dai->ctrl_blk, 0);
+	hw->dai_srt_set_et(dai->ctrl_blk, 0); /* default to disabling SRT */
+	hw->dai_commit_write(hw,
+		daio_device_index(dai->daio.type, dai->hw), dai->ctrl_blk);
+
+	return 0;
+
+error1:
+	daio_rsc_uninit(&dai->daio);
+	return err;
+}
+
+static int dai_rsc_uninit(struct dai *dai)
+{
+	((struct hw *)dai->hw)->dai_put_ctrl_blk(dai->ctrl_blk);
+	dai->hw = dai->ctrl_blk = NULL;
+	daio_rsc_uninit(&dai->daio);
+	return 0;
+}
+
+static int daio_mgr_get_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
+{
+	if (((union daio_usage *)mgr->rscs)->data & (0x1 << type))
+		return -ENOENT;
+
+	((union daio_usage *)mgr->rscs)->data |= (0x1 << type);
+
+	return 0;
+}
+
+static int daio_mgr_put_rsc(struct rsc_mgr *mgr, enum DAIOTYP type)
+{
+	((union daio_usage *)mgr->rscs)->data &= ~(0x1 << type);
+
+	return 0;
+}
+
+static int get_daio_rsc(struct daio_mgr *mgr,
+			const struct daio_desc *desc,
+			struct daio **rdaio)
+{
+	int err;
+	struct dai *dai = NULL;
+	struct dao *dao = NULL;
+	unsigned long flags;
+
+	*rdaio = NULL;
+
+	/* Check whether there are sufficient daio resources to meet request. */
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	err = daio_mgr_get_rsc(&mgr->mgr, desc->type);
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	if (err) {
+		printk(KERN_ERR "Can't meet DAIO resource request!\n");
+		return err;
+	}
+
+	/* Allocate mem for daio resource */
+	if (desc->type <= DAIO_OUT_MAX) {
+		dao = kzalloc(sizeof(*dao), GFP_KERNEL);
+		if (NULL == dao) {
+			err = -ENOMEM;
+			goto error;
+		}
+		err = dao_rsc_init(dao, desc, mgr);
+		if (err)
+			goto error;
+
+		*rdaio = &dao->daio;
+	} else {
+		dai = kzalloc(sizeof(*dai), GFP_KERNEL);
+		if (NULL == dai) {
+			err = -ENOMEM;
+			goto error;
+		}
+		err = dai_rsc_init(dai, desc, mgr);
+		if (err)
+			goto error;
+
+		*rdaio = &dai->daio;
+	}
+
+	mgr->daio_enable(mgr, *rdaio);
+	mgr->commit_write(mgr);
+
+	return 0;
+
+error:
+	if (NULL != dao)
+		kfree(dao);
+	else if (NULL != dai)
+		kfree(dai);
+
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	daio_mgr_put_rsc(&mgr->mgr, desc->type);
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	return err;
+}
+
+static int put_daio_rsc(struct daio_mgr *mgr, struct daio *daio)
+{
+	unsigned long flags;
+
+	mgr->daio_disable(mgr, daio);
+	mgr->commit_write(mgr);
+
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	daio_mgr_put_rsc(&mgr->mgr, daio->type);
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+
+	if (daio->type <= DAIO_OUT_MAX) {
+		dao_rsc_uninit(container_of(daio, struct dao, daio));
+		kfree(container_of(daio, struct dao, daio));
+	} else {
+		dai_rsc_uninit(container_of(daio, struct dai, daio));
+		kfree(container_of(daio, struct dai, daio));
+	}
+
+	return 0;
+}
+
+static int daio_mgr_enb_daio(struct daio_mgr *mgr, struct daio *daio)
+{
+	struct hw *hw = mgr->mgr.hw;
+
+	if (DAIO_OUT_MAX >= daio->type) {
+		hw->daio_mgr_enb_dao(mgr->mgr.ctrl_blk,
+				daio_device_index(daio->type, hw));
+	} else {
+		hw->daio_mgr_enb_dai(mgr->mgr.ctrl_blk,
+				daio_device_index(daio->type, hw));
+	}
+	return 0;
+}
+
+static int daio_mgr_dsb_daio(struct daio_mgr *mgr, struct daio *daio)
+{
+	struct hw *hw = mgr->mgr.hw;
+
+	if (DAIO_OUT_MAX >= daio->type) {
+		hw->daio_mgr_dsb_dao(mgr->mgr.ctrl_blk,
+				daio_device_index(daio->type, hw));
+	} else {
+		hw->daio_mgr_dsb_dai(mgr->mgr.ctrl_blk,
+				daio_device_index(daio->type, hw));
+	}
+	return 0;
+}
+
+static int daio_map_op(void *data, struct imapper *entry)
+{
+	struct rsc_mgr *mgr = &((struct daio_mgr *)data)->mgr;
+	struct hw *hw = mgr->hw;
+
+	hw->daio_mgr_set_imaparc(mgr->ctrl_blk, entry->slot);
+	hw->daio_mgr_set_imapnxt(mgr->ctrl_blk, entry->next);
+	hw->daio_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr);
+	hw->daio_mgr_commit_write(mgr->hw, mgr->ctrl_blk);
+
+	return 0;
+}
+
+static int daio_imap_add(struct daio_mgr *mgr, struct imapper *entry)
+{
+	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&mgr->imap_lock, flags);
+	if ((0 == entry->addr) && (mgr->init_imap_added)) {
+		input_mapper_delete(&mgr->imappers, mgr->init_imap,
+							daio_map_op, mgr);
+		mgr->init_imap_added = 0;
+	}
+	err = input_mapper_add(&mgr->imappers, entry, daio_map_op, mgr);
+	spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+	return err;
+}
+
+static int daio_imap_delete(struct daio_mgr *mgr, struct imapper *entry)
+{
+	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&mgr->imap_lock, flags);
+	err = input_mapper_delete(&mgr->imappers, entry, daio_map_op, mgr);
+	if (list_empty(&mgr->imappers)) {
+		input_mapper_add(&mgr->imappers, mgr->init_imap,
+							daio_map_op, mgr);
+		mgr->init_imap_added = 1;
+	}
+	spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+	return err;
+}
+
+static int daio_mgr_commit_write(struct daio_mgr *mgr)
+{
+	struct hw *hw = mgr->mgr.hw;
+
+	hw->daio_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+	return 0;
+}
+
+int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr)
+{
+	int err, i;
+	struct daio_mgr *daio_mgr;
+	struct imapper *entry;
+
+	*rdaio_mgr = NULL;
+	daio_mgr = kzalloc(sizeof(*daio_mgr), GFP_KERNEL);
+	if (NULL == daio_mgr)
+		return -ENOMEM;
+
+	err = rsc_mgr_init(&daio_mgr->mgr, DAIO, DAIO_RESOURCE_NUM, hw);
+	if (err)
+		goto error1;
+
+	spin_lock_init(&daio_mgr->mgr_lock);
+	spin_lock_init(&daio_mgr->imap_lock);
+	INIT_LIST_HEAD(&daio_mgr->imappers);
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (NULL == entry) {
+		err = -ENOMEM;
+		goto error2;
+	}
+	entry->slot = entry->addr = entry->next = entry->user = 0;
+	list_add(&entry->list, &daio_mgr->imappers);
+	daio_mgr->init_imap = entry;
+	daio_mgr->init_imap_added = 1;
+
+	daio_mgr->get_daio = get_daio_rsc;
+	daio_mgr->put_daio = put_daio_rsc;
+	daio_mgr->daio_enable = daio_mgr_enb_daio;
+	daio_mgr->daio_disable = daio_mgr_dsb_daio;
+	daio_mgr->imap_add = daio_imap_add;
+	daio_mgr->imap_delete = daio_imap_delete;
+	daio_mgr->commit_write = daio_mgr_commit_write;
+
+	for (i = 0; i < 8; i++) {
+		((struct hw *)hw)->daio_mgr_dsb_dao(daio_mgr->mgr.ctrl_blk, i);
+		((struct hw *)hw)->daio_mgr_dsb_dai(daio_mgr->mgr.ctrl_blk, i);
+	}
+	((struct hw *)hw)->daio_mgr_commit_write(hw, daio_mgr->mgr.ctrl_blk);
+
+	*rdaio_mgr = daio_mgr;
+
+	return 0;
+
+error2:
+	rsc_mgr_uninit(&daio_mgr->mgr);
+error1:
+	kfree(daio_mgr);
+	return err;
+}
+
+int daio_mgr_destroy(struct daio_mgr *daio_mgr)
+{
+	unsigned long flags;
+
+	/* free daio input mapper list */
+	spin_lock_irqsave(&daio_mgr->imap_lock, flags);
+	free_input_mapper_list(&daio_mgr->imappers);
+	spin_unlock_irqrestore(&daio_mgr->imap_lock, flags);
+
+	rsc_mgr_uninit(&daio_mgr->mgr);
+	kfree(daio_mgr);
+
+	return 0;
+}
+
diff --git a/sound/pci/ctxfi/ctdaio.h b/sound/pci/ctxfi/ctdaio.h
new file mode 100644
index 0000000..0f52ce5
--- /dev/null
+++ b/sound/pci/ctxfi/ctdaio.h
@@ -0,0 +1,122 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctdaio.h
+ *
+ * @Brief
+ * This file contains the definition of Digital Audio Input Output
+ * resource management object.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 23 2008
+ *
+ */
+
+#ifndef CTDAIO_H
+#define CTDAIO_H
+
+#include "ctresource.h"
+#include "ctimap.h"
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+/* Define the descriptor of a daio resource */
+enum DAIOTYP {
+	LINEO1,
+	LINEO2,
+	LINEO3,
+	LINEO4,
+	SPDIFOO,	/* S/PDIF Out (Flexijack/Optical) */
+	LINEIM,
+	SPDIFIO,	/* S/PDIF In (Flexijack/Optical) on the card */
+	SPDIFI1,	/* S/PDIF In on internal Drive Bay */
+	NUM_DAIOTYP
+};
+
+struct dao_rsc_ops;
+struct dai_rsc_ops;
+struct daio_mgr;
+
+struct daio {
+	struct rsc rscl;	/* Basic resource info for left TX/RX */
+	struct rsc rscr;	/* Basic resource info for right TX/RX */
+	enum DAIOTYP type;
+};
+
+struct dao {
+	struct daio daio;
+	struct dao_rsc_ops *ops;	/* DAO specific operations */
+	struct imapper **imappers;
+	struct daio_mgr *mgr;
+	void *hw;
+	void *ctrl_blk;
+};
+
+struct dai {
+	struct daio daio;
+	struct dai_rsc_ops *ops;	/* DAI specific operations */
+	void *hw;
+	void *ctrl_blk;
+};
+
+struct dao_desc {
+	unsigned int msr:4;
+	unsigned int passthru:1;
+};
+
+struct dao_rsc_ops {
+	int (*set_spos)(struct dao *dao, unsigned int spos);
+	int (*commit_write)(struct dao *dao);
+	int (*get_spos)(struct dao *dao, unsigned int *spos);
+	int (*reinit)(struct dao *dao, const struct dao_desc *desc);
+	int (*set_left_input)(struct dao *dao, struct rsc *input);
+	int (*set_right_input)(struct dao *dao, struct rsc *input);
+	int (*clear_left_input)(struct dao *dao);
+	int (*clear_right_input)(struct dao *dao);
+};
+
+struct dai_rsc_ops {
+	int (*set_srt_srcl)(struct dai *dai, struct rsc *src);
+	int (*set_srt_srcr)(struct dai *dai, struct rsc *src);
+	int (*set_srt_msr)(struct dai *dai, unsigned int msr);
+	int (*set_enb_src)(struct dai *dai, unsigned int enb);
+	int (*set_enb_srt)(struct dai *dai, unsigned int enb);
+	int (*commit_write)(struct dai *dai);
+};
+
+/* Define daio resource request description info */
+struct daio_desc {
+	unsigned int type:4;
+	unsigned int msr:4;
+	unsigned int passthru:1;
+};
+
+struct daio_mgr {
+	struct rsc_mgr mgr;	/* Basic resource manager info */
+	spinlock_t mgr_lock;
+	spinlock_t imap_lock;
+	struct list_head imappers;
+	struct imapper *init_imap;
+	unsigned int init_imap_added;
+
+	 /* request one daio resource */
+	int (*get_daio)(struct daio_mgr *mgr,
+			const struct daio_desc *desc, struct daio **rdaio);
+	/* return one daio resource */
+	int (*put_daio)(struct daio_mgr *mgr, struct daio *daio);
+	int (*daio_enable)(struct daio_mgr *mgr, struct daio *daio);
+	int (*daio_disable)(struct daio_mgr *mgr, struct daio *daio);
+	int (*imap_add)(struct daio_mgr *mgr, struct imapper *entry);
+	int (*imap_delete)(struct daio_mgr *mgr, struct imapper *entry);
+	int (*commit_write)(struct daio_mgr *mgr);
+};
+
+/* Constructor and destructor of daio resource manager */
+int daio_mgr_create(void *hw, struct daio_mgr **rdaio_mgr);
+int daio_mgr_destroy(struct daio_mgr *daio_mgr);
+
+#endif /* CTDAIO_H */
diff --git a/sound/pci/ctxfi/cthardware.c b/sound/pci/ctxfi/cthardware.c
new file mode 100644
index 0000000..8e64f48
--- /dev/null
+++ b/sound/pci/ctxfi/cthardware.c
@@ -0,0 +1,91 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	cthardware.c
+ *
+ * @Brief
+ * This file contains the implementation of hardware access methord.
+ *
+ * @Author	Liu Chun
+ * @Date 	Jun 26 2008
+ *
+ */
+
+#include "cthardware.h"
+#include "cthw20k1.h"
+#include "cthw20k2.h"
+#include <linux/bug.h>
+
+int __devinit create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
+			    enum CTCARDS model, struct hw **rhw)
+{
+	int err;
+
+	switch (chip_type) {
+	case ATC20K1:
+		err = create_20k1_hw_obj(rhw);
+		break;
+	case ATC20K2:
+		err = create_20k2_hw_obj(rhw);
+		break;
+	default:
+		err = -ENODEV;
+		break;
+	}
+	if (err)
+		return err;
+
+	(*rhw)->pci = pci;
+	(*rhw)->chip_type = chip_type;
+	(*rhw)->model = model;
+
+	return 0;
+}
+
+int destroy_hw_obj(struct hw *hw)
+{
+	int err;
+
+	switch (hw->pci->device) {
+	case 0x0005:	/* 20k1 device */
+		err = destroy_20k1_hw_obj(hw);
+		break;
+	case 0x000B:	/* 20k2 device */
+		err = destroy_20k2_hw_obj(hw);
+		break;
+	default:
+		err = -ENODEV;
+		break;
+	}
+
+	return err;
+}
+
+unsigned int get_field(unsigned int data, unsigned int field)
+{
+	int i;
+
+	BUG_ON(!field);
+	/* @field should always be greater than 0 */
+	for (i = 0; !(field & (1 << i)); )
+		i++;
+
+	return (data & field) >> i;
+}
+
+void set_field(unsigned int *data, unsigned int field, unsigned int value)
+{
+	int i;
+
+	BUG_ON(!field);
+	/* @field should always be greater than 0 */
+	for (i = 0; !(field & (1 << i)); )
+		i++;
+
+	*data = (*data & (~field)) | ((value << i) & field);
+}
+
diff --git a/sound/pci/ctxfi/cthardware.h b/sound/pci/ctxfi/cthardware.h
new file mode 100644
index 0000000..af55405
--- /dev/null
+++ b/sound/pci/ctxfi/cthardware.h
@@ -0,0 +1,203 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	cthardware.h
+ *
+ * @Brief
+ * This file contains the definition of hardware access methord.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 13 2008
+ *
+ */
+
+#ifndef CTHARDWARE_H
+#define CTHARDWARE_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+
+enum CHIPTYP {
+	ATC20K1,
+	ATC20K2,
+	ATCNONE
+};
+
+enum CTCARDS {
+	/* 20k1 models */
+	CTSB055X,
+	CT20K1_MODEL_FIRST = CTSB055X,
+	CTSB073X,
+	CTUAA,
+	CT20K1_UNKNOWN,
+	/* 20k2 models */
+	CTSB0760,
+	CT20K2_MODEL_FIRST = CTSB0760,
+	CTHENDRIX,
+	CTSB0880,
+	CT20K2_UNKNOWN,
+	NUM_CTCARDS		/* This should always be the last */
+};
+
+/* Type of input source for ADC */
+enum ADCSRC{
+	ADC_MICIN,
+	ADC_LINEIN,
+	ADC_VIDEO,
+	ADC_AUX,
+	ADC_NONE	/* Switch to digital input */
+};
+
+struct card_conf {
+	/* device virtual mem page table page physical addr
+	 * (supporting one page table page now) */
+	unsigned long vm_pgt_phys;
+	unsigned int rsr;	/* reference sample rate in Hzs*/
+	unsigned int msr;	/* master sample rate in rsrs */
+};
+
+struct hw {
+	int (*card_init)(struct hw *hw, struct card_conf *info);
+	int (*card_stop)(struct hw *hw);
+	int (*pll_init)(struct hw *hw, unsigned int rsr);
+#ifdef CONFIG_PM
+	int (*suspend)(struct hw *hw, pm_message_t state);
+	int (*resume)(struct hw *hw, struct card_conf *info);
+#endif
+	int (*is_adc_source_selected)(struct hw *hw, enum ADCSRC source);
+	int (*select_adc_source)(struct hw *hw, enum ADCSRC source);
+	int (*have_digit_io_switch)(struct hw *hw);
+
+	/* SRC operations */
+	int (*src_rsc_get_ctrl_blk)(void **rblk);
+	int (*src_rsc_put_ctrl_blk)(void *blk);
+	int (*src_set_state)(void *blk, unsigned int state);
+	int (*src_set_bm)(void *blk, unsigned int bm);
+	int (*src_set_rsr)(void *blk, unsigned int rsr);
+	int (*src_set_sf)(void *blk, unsigned int sf);
+	int (*src_set_wr)(void *blk, unsigned int wr);
+	int (*src_set_pm)(void *blk, unsigned int pm);
+	int (*src_set_rom)(void *blk, unsigned int rom);
+	int (*src_set_vo)(void *blk, unsigned int vo);
+	int (*src_set_st)(void *blk, unsigned int st);
+	int (*src_set_ie)(void *blk, unsigned int ie);
+	int (*src_set_ilsz)(void *blk, unsigned int ilsz);
+	int (*src_set_bp)(void *blk, unsigned int bp);
+	int (*src_set_cisz)(void *blk, unsigned int cisz);
+	int (*src_set_ca)(void *blk, unsigned int ca);
+	int (*src_set_sa)(void *blk, unsigned int sa);
+	int (*src_set_la)(void *blk, unsigned int la);
+	int (*src_set_pitch)(void *blk, unsigned int pitch);
+	int (*src_set_clear_zbufs)(void *blk, unsigned int clear);
+	int (*src_set_dirty)(void *blk, unsigned int flags);
+	int (*src_set_dirty_all)(void *blk);
+	int (*src_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+	int (*src_get_ca)(struct hw *hw, unsigned int idx, void *blk);
+	unsigned int (*src_get_dirty)(void *blk);
+	unsigned int (*src_dirty_conj_mask)(void);
+	int (*src_mgr_get_ctrl_blk)(void **rblk);
+	int (*src_mgr_put_ctrl_blk)(void *blk);
+	/* syncly enable src @idx */
+	int (*src_mgr_enbs_src)(void *blk, unsigned int idx);
+	/* enable src @idx */
+	int (*src_mgr_enb_src)(void *blk, unsigned int idx);
+	/* disable src @idx */
+	int (*src_mgr_dsb_src)(void *blk, unsigned int idx);
+	int (*src_mgr_commit_write)(struct hw *hw, void *blk);
+
+	/* SRC Input Mapper operations */
+	int (*srcimp_mgr_get_ctrl_blk)(void **rblk);
+	int (*srcimp_mgr_put_ctrl_blk)(void *blk);
+	int (*srcimp_mgr_set_imaparc)(void *blk, unsigned int slot);
+	int (*srcimp_mgr_set_imapuser)(void *blk, unsigned int user);
+	int (*srcimp_mgr_set_imapnxt)(void *blk, unsigned int next);
+	int (*srcimp_mgr_set_imapaddr)(void *blk, unsigned int addr);
+	int (*srcimp_mgr_commit_write)(struct hw *hw, void *blk);
+
+	/* AMIXER operations */
+	int (*amixer_rsc_get_ctrl_blk)(void **rblk);
+	int (*amixer_rsc_put_ctrl_blk)(void *blk);
+	int (*amixer_mgr_get_ctrl_blk)(void **rblk);
+	int (*amixer_mgr_put_ctrl_blk)(void *blk);
+	int (*amixer_set_mode)(void *blk, unsigned int mode);
+	int (*amixer_set_iv)(void *blk, unsigned int iv);
+	int (*amixer_set_x)(void *blk, unsigned int x);
+	int (*amixer_set_y)(void *blk, unsigned int y);
+	int (*amixer_set_sadr)(void *blk, unsigned int sadr);
+	int (*amixer_set_se)(void *blk, unsigned int se);
+	int (*amixer_set_dirty)(void *blk, unsigned int flags);
+	int (*amixer_set_dirty_all)(void *blk);
+	int (*amixer_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+	int (*amixer_get_y)(void *blk);
+	unsigned int (*amixer_get_dirty)(void *blk);
+
+	/* DAIO operations */
+	int (*dai_get_ctrl_blk)(void **rblk);
+	int (*dai_put_ctrl_blk)(void *blk);
+	int (*dai_srt_set_srco)(void *blk, unsigned int src);
+	int (*dai_srt_set_srcm)(void *blk, unsigned int src);
+	int (*dai_srt_set_rsr)(void *blk, unsigned int rsr);
+	int (*dai_srt_set_drat)(void *blk, unsigned int drat);
+	int (*dai_srt_set_ec)(void *blk, unsigned int ec);
+	int (*dai_srt_set_et)(void *blk, unsigned int et);
+	int (*dai_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+	int (*dao_get_ctrl_blk)(void **rblk);
+	int (*dao_put_ctrl_blk)(void *blk);
+	int (*dao_set_spos)(void *blk, unsigned int spos);
+	int (*dao_commit_write)(struct hw *hw, unsigned int idx, void *blk);
+	int (*dao_get_spos)(void *blk, unsigned int *spos);
+
+	int (*daio_mgr_get_ctrl_blk)(struct hw *hw, void **rblk);
+	int (*daio_mgr_put_ctrl_blk)(void *blk);
+	int (*daio_mgr_enb_dai)(void *blk, unsigned int idx);
+	int (*daio_mgr_dsb_dai)(void *blk, unsigned int idx);
+	int (*daio_mgr_enb_dao)(void *blk, unsigned int idx);
+	int (*daio_mgr_dsb_dao)(void *blk, unsigned int idx);
+	int (*daio_mgr_dao_init)(void *blk, unsigned int idx,
+						unsigned int conf);
+	int (*daio_mgr_set_imaparc)(void *blk, unsigned int slot);
+	int (*daio_mgr_set_imapnxt)(void *blk, unsigned int next);
+	int (*daio_mgr_set_imapaddr)(void *blk, unsigned int addr);
+	int (*daio_mgr_commit_write)(struct hw *hw, void *blk);
+
+	int (*set_timer_irq)(struct hw *hw, int enable);
+	int (*set_timer_tick)(struct hw *hw, unsigned int tick);
+	unsigned int (*get_wc)(struct hw *hw);
+
+	void (*irq_callback)(void *data, unsigned int bit);
+	void *irq_callback_data;
+
+	struct pci_dev *pci;	/* the pci kernel structure of this card */
+	int irq;
+	unsigned long io_base;
+	unsigned long mem_base;
+
+	enum CHIPTYP chip_type;
+	enum CTCARDS model;
+};
+
+int create_hw_obj(struct pci_dev *pci, enum CHIPTYP chip_type,
+		  enum CTCARDS model, struct hw **rhw);
+int destroy_hw_obj(struct hw *hw);
+
+unsigned int get_field(unsigned int data, unsigned int field);
+void set_field(unsigned int *data, unsigned int field, unsigned int value);
+
+/* IRQ bits */
+#define	PLL_INT		(1 << 10) /* PLL input-clock out-of-range */
+#define FI_INT		(1 << 9)  /* forced interrupt */
+#define IT_INT		(1 << 8)  /* timer interrupt */
+#define PCI_INT		(1 << 7)  /* PCI bus error pending */
+#define URT_INT		(1 << 6)  /* UART Tx/Rx */
+#define GPI_INT		(1 << 5)  /* GPI pin */
+#define MIX_INT		(1 << 4)  /* mixer parameter segment FIFO channels */
+#define DAI_INT		(1 << 3)  /* DAI (SR-tracker or SPDIF-receiver) */
+#define TP_INT		(1 << 2)  /* transport priority queue */
+#define DSP_INT		(1 << 1)  /* DSP */
+#define SRC_INT		(1 << 0)  /* SRC channels */
+
+#endif /* CTHARDWARE_H */
diff --git a/sound/pci/ctxfi/cthw20k1.c b/sound/pci/ctxfi/cthw20k1.c
new file mode 100644
index 0000000..ad3e1d1
--- /dev/null
+++ b/sound/pci/ctxfi/cthw20k1.c
@@ -0,0 +1,2297 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	cthw20k1.c
+ *
+ * @Brief
+ * This file contains the implementation of hardware access methord for 20k1.
+ *
+ * @Author	Liu Chun
+ * @Date 	Jun 24 2008
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include "cthw20k1.h"
+#include "ct20k1reg.h"
+
+#if BITS_PER_LONG == 32
+#define CT_XFI_DMA_MASK		DMA_BIT_MASK(32) /* 32 bit PTE */
+#else
+#define CT_XFI_DMA_MASK		DMA_BIT_MASK(64) /* 64 bit PTE */
+#endif
+
+struct hw20k1 {
+	struct hw hw;
+	spinlock_t reg_20k1_lock;
+	spinlock_t reg_pci_lock;
+};
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg);
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data);
+static u32 hw_read_pci(struct hw *hw, u32 reg);
+static void hw_write_pci(struct hw *hw, u32 reg, u32 data);
+
+/*
+ * Type definition block.
+ * The layout of control structures can be directly applied on 20k2 chip.
+ */
+
+/*
+ * SRC control block definitions.
+ */
+
+/* SRC resource control block */
+#define SRCCTL_STATE	0x00000007
+#define SRCCTL_BM	0x00000008
+#define SRCCTL_RSR	0x00000030
+#define SRCCTL_SF	0x000001C0
+#define SRCCTL_WR	0x00000200
+#define SRCCTL_PM	0x00000400
+#define SRCCTL_ROM	0x00001800
+#define SRCCTL_VO	0x00002000
+#define SRCCTL_ST	0x00004000
+#define SRCCTL_IE	0x00008000
+#define SRCCTL_ILSZ	0x000F0000
+#define SRCCTL_BP	0x00100000
+
+#define SRCCCR_CISZ	0x000007FF
+#define SRCCCR_CWA	0x001FF800
+#define SRCCCR_D	0x00200000
+#define SRCCCR_RS	0x01C00000
+#define SRCCCR_NAL	0x3E000000
+#define SRCCCR_RA	0xC0000000
+
+#define SRCCA_CA	0x03FFFFFF
+#define SRCCA_RS	0x1C000000
+#define SRCCA_NAL	0xE0000000
+
+#define SRCSA_SA	0x03FFFFFF
+
+#define SRCLA_LA	0x03FFFFFF
+
+/* Mixer Parameter Ring ram Low and Hight register.
+ * Fixed-point value in 8.24 format for parameter channel */
+#define MPRLH_PITCH	0xFFFFFFFF
+
+/* SRC resource register dirty flags */
+union src_dirty {
+	struct {
+		u16 ctl:1;
+		u16 ccr:1;
+		u16 sa:1;
+		u16 la:1;
+		u16 ca:1;
+		u16 mpr:1;
+		u16 czbfs:1;	/* Clear Z-Buffers */
+		u16 rsv:9;
+	} bf;
+	u16 data;
+};
+
+struct src_rsc_ctrl_blk {
+	unsigned int	ctl;
+	unsigned int 	ccr;
+	unsigned int	ca;
+	unsigned int	sa;
+	unsigned int	la;
+	unsigned int	mpr;
+	union src_dirty	dirty;
+};
+
+/* SRC manager control block */
+union src_mgr_dirty {
+	struct {
+		u16 enb0:1;
+		u16 enb1:1;
+		u16 enb2:1;
+		u16 enb3:1;
+		u16 enb4:1;
+		u16 enb5:1;
+		u16 enb6:1;
+		u16 enb7:1;
+		u16 enbsa:1;
+		u16 rsv:7;
+	} bf;
+	u16 data;
+};
+
+struct src_mgr_ctrl_blk {
+	unsigned int		enbsa;
+	unsigned int		enb[8];
+	union src_mgr_dirty	dirty;
+};
+
+/* SRCIMP manager control block */
+#define SRCAIM_ARC	0x00000FFF
+#define SRCAIM_NXT	0x00FF0000
+#define SRCAIM_SRC	0xFF000000
+
+struct srcimap {
+	unsigned int srcaim;
+	unsigned int idx;
+};
+
+/* SRCIMP manager register dirty flags */
+union srcimp_mgr_dirty {
+	struct {
+		u16 srcimap:1;
+		u16 rsv:15;
+	} bf;
+	u16 data;
+};
+
+struct srcimp_mgr_ctrl_blk {
+	struct srcimap		srcimap;
+	union srcimp_mgr_dirty	dirty;
+};
+
+/*
+ * Function implementation block.
+ */
+
+static int src_get_rsc_ctrl_blk(void **rblk)
+{
+	struct src_rsc_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int src_put_rsc_ctrl_blk(void *blk)
+{
+	kfree((struct src_rsc_ctrl_blk *)blk);
+
+	return 0;
+}
+
+static int src_set_state(void *blk, unsigned int state)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_STATE, state);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_bm(void *blk, unsigned int bm)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_BM, bm);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_rsr(void *blk, unsigned int rsr)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_RSR, rsr);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_sf(void *blk, unsigned int sf)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_SF, sf);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_wr(void *blk, unsigned int wr)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_WR, wr);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_pm(void *blk, unsigned int pm)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_PM, pm);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_rom(void *blk, unsigned int rom)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_ROM, rom);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_vo(void *blk, unsigned int vo)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_VO, vo);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_st(void *blk, unsigned int st)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_ST, st);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_ie(void *blk, unsigned int ie)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_IE, ie);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_ilsz(void *blk, unsigned int ilsz)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_bp(void *blk, unsigned int bp)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_BP, bp);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_cisz(void *blk, unsigned int cisz)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ccr, SRCCCR_CISZ, cisz);
+	ctl->dirty.bf.ccr = 1;
+	return 0;
+}
+
+static int src_set_ca(void *blk, unsigned int ca)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ca, SRCCA_CA, ca);
+	ctl->dirty.bf.ca = 1;
+	return 0;
+}
+
+static int src_set_sa(void *blk, unsigned int sa)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->sa, SRCSA_SA, sa);
+	ctl->dirty.bf.sa = 1;
+	return 0;
+}
+
+static int src_set_la(void *blk, unsigned int la)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->la, SRCLA_LA, la);
+	ctl->dirty.bf.la = 1;
+	return 0;
+}
+
+static int src_set_pitch(void *blk, unsigned int pitch)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->mpr, MPRLH_PITCH, pitch);
+	ctl->dirty.bf.mpr = 1;
+	return 0;
+}
+
+static int src_set_clear_zbufs(void *blk, unsigned int clear)
+{
+	((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0);
+	return 0;
+}
+
+static int src_set_dirty(void *blk, unsigned int flags)
+{
+	((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+	return 0;
+}
+
+static int src_set_dirty_all(void *blk)
+{
+	((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+	return 0;
+}
+
+#define AR_SLOT_SIZE		4096
+#define AR_SLOT_BLOCK_SIZE	16
+#define AR_PTS_PITCH		6
+#define AR_PARAM_SRC_OFFSET	0x60
+
+static unsigned int src_param_pitch_mixer(unsigned int src_idx)
+{
+	return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE
+			- AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE;
+
+}
+
+static int src_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+	int i;
+
+	if (ctl->dirty.bf.czbfs) {
+		/* Clear Z-Buffer registers */
+		for (i = 0; i < 8; i++)
+			hw_write_20kx(hw, SRCUPZ+idx*0x100+i*0x4, 0);
+
+		for (i = 0; i < 4; i++)
+			hw_write_20kx(hw, SRCDN0Z+idx*0x100+i*0x4, 0);
+
+		for (i = 0; i < 8; i++)
+			hw_write_20kx(hw, SRCDN1Z+idx*0x100+i*0x4, 0);
+
+		ctl->dirty.bf.czbfs = 0;
+	}
+	if (ctl->dirty.bf.mpr) {
+		/* Take the parameter mixer resource in the same group as that
+		 * the idx src is in for simplicity. Unlike src, all conjugate
+		 * parameter mixer resources must be programmed for
+		 * corresponding conjugate src resources. */
+		unsigned int pm_idx = src_param_pitch_mixer(idx);
+		hw_write_20kx(hw, PRING_LO_HI+4*pm_idx, ctl->mpr);
+		hw_write_20kx(hw, PMOPLO+8*pm_idx, 0x3);
+		hw_write_20kx(hw, PMOPHI+8*pm_idx, 0x0);
+		ctl->dirty.bf.mpr = 0;
+	}
+	if (ctl->dirty.bf.sa) {
+		hw_write_20kx(hw, SRCSA+idx*0x100, ctl->sa);
+		ctl->dirty.bf.sa = 0;
+	}
+	if (ctl->dirty.bf.la) {
+		hw_write_20kx(hw, SRCLA+idx*0x100, ctl->la);
+		ctl->dirty.bf.la = 0;
+	}
+	if (ctl->dirty.bf.ca) {
+		hw_write_20kx(hw, SRCCA+idx*0x100, ctl->ca);
+		ctl->dirty.bf.ca = 0;
+	}
+
+	/* Write srccf register */
+	hw_write_20kx(hw, SRCCF+idx*0x100, 0x0);
+
+	if (ctl->dirty.bf.ccr) {
+		hw_write_20kx(hw, SRCCCR+idx*0x100, ctl->ccr);
+		ctl->dirty.bf.ccr = 0;
+	}
+	if (ctl->dirty.bf.ctl) {
+		hw_write_20kx(hw, SRCCTL+idx*0x100, ctl->ctl);
+		ctl->dirty.bf.ctl = 0;
+	}
+
+	return 0;
+}
+
+static int src_get_ca(struct hw *hw, unsigned int idx, void *blk)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	ctl->ca = hw_read_20kx(hw, SRCCA+idx*0x100);
+	ctl->dirty.bf.ca = 0;
+
+	return get_field(ctl->ca, SRCCA_CA);
+}
+
+static unsigned int src_get_dirty(void *blk)
+{
+	return ((struct src_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static unsigned int src_dirty_conj_mask(void)
+{
+	return 0x20;
+}
+
+static int src_mgr_enbs_src(void *blk, unsigned int idx)
+{
+	((struct src_mgr_ctrl_blk *)blk)->enbsa = ~(0x0);
+	((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1;
+	((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+	return 0;
+}
+
+static int src_mgr_enb_src(void *blk, unsigned int idx)
+{
+	((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+	((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+	return 0;
+}
+
+static int src_mgr_dsb_src(void *blk, unsigned int idx)
+{
+	((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32));
+	((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+	return 0;
+}
+
+static int src_mgr_commit_write(struct hw *hw, void *blk)
+{
+	struct src_mgr_ctrl_blk *ctl = blk;
+	int i;
+	unsigned int ret;
+
+	if (ctl->dirty.bf.enbsa) {
+		do {
+			ret = hw_read_20kx(hw, SRCENBSTAT);
+		} while (ret & 0x1);
+		hw_write_20kx(hw, SRCENBS, ctl->enbsa);
+		ctl->dirty.bf.enbsa = 0;
+	}
+	for (i = 0; i < 8; i++) {
+		if ((ctl->dirty.data & (0x1 << i))) {
+			hw_write_20kx(hw, SRCENB+(i*0x100), ctl->enb[i]);
+			ctl->dirty.data &= ~(0x1 << i);
+		}
+	}
+
+	return 0;
+}
+
+static int src_mgr_get_ctrl_blk(void **rblk)
+{
+	struct src_mgr_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int src_mgr_put_ctrl_blk(void *blk)
+{
+	kfree((struct src_mgr_ctrl_blk *)blk);
+
+	return 0;
+}
+
+static int srcimp_mgr_get_ctrl_blk(void **rblk)
+{
+	struct srcimp_mgr_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int srcimp_mgr_put_ctrl_blk(void *blk)
+{
+	kfree((struct srcimp_mgr_ctrl_blk *)blk);
+
+	return 0;
+}
+
+static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+	struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot);
+	ctl->dirty.bf.srcimap = 1;
+	return 0;
+}
+
+static int srcimp_mgr_set_imapuser(void *blk, unsigned int user)
+{
+	struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user);
+	ctl->dirty.bf.srcimap = 1;
+	return 0;
+}
+
+static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+	struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next);
+	ctl->dirty.bf.srcimap = 1;
+	return 0;
+}
+
+static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+	struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+	ctl->srcimap.idx = addr;
+	ctl->dirty.bf.srcimap = 1;
+	return 0;
+}
+
+static int srcimp_mgr_commit_write(struct hw *hw, void *blk)
+{
+	struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+	if (ctl->dirty.bf.srcimap) {
+		hw_write_20kx(hw, SRCIMAP+ctl->srcimap.idx*0x100,
+						ctl->srcimap.srcaim);
+		ctl->dirty.bf.srcimap = 0;
+	}
+
+	return 0;
+}
+
+/*
+ * AMIXER control block definitions.
+ */
+
+#define AMOPLO_M	0x00000003
+#define AMOPLO_X	0x0003FFF0
+#define AMOPLO_Y	0xFFFC0000
+
+#define AMOPHI_SADR	0x000000FF
+#define AMOPHI_SE	0x80000000
+
+/* AMIXER resource register dirty flags */
+union amixer_dirty {
+	struct {
+		u16 amoplo:1;
+		u16 amophi:1;
+		u16 rsv:14;
+	} bf;
+	u16 data;
+};
+
+/* AMIXER resource control block */
+struct amixer_rsc_ctrl_blk {
+	unsigned int		amoplo;
+	unsigned int		amophi;
+	union amixer_dirty	dirty;
+};
+
+static int amixer_set_mode(void *blk, unsigned int mode)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->amoplo, AMOPLO_M, mode);
+	ctl->dirty.bf.amoplo = 1;
+	return 0;
+}
+
+static int amixer_set_iv(void *blk, unsigned int iv)
+{
+	/* 20k1 amixer does not have this field */
+	return 0;
+}
+
+static int amixer_set_x(void *blk, unsigned int x)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->amoplo, AMOPLO_X, x);
+	ctl->dirty.bf.amoplo = 1;
+	return 0;
+}
+
+static int amixer_set_y(void *blk, unsigned int y)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->amoplo, AMOPLO_Y, y);
+	ctl->dirty.bf.amoplo = 1;
+	return 0;
+}
+
+static int amixer_set_sadr(void *blk, unsigned int sadr)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->amophi, AMOPHI_SADR, sadr);
+	ctl->dirty.bf.amophi = 1;
+	return 0;
+}
+
+static int amixer_set_se(void *blk, unsigned int se)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->amophi, AMOPHI_SE, se);
+	ctl->dirty.bf.amophi = 1;
+	return 0;
+}
+
+static int amixer_set_dirty(void *blk, unsigned int flags)
+{
+	((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+	return 0;
+}
+
+static int amixer_set_dirty_all(void *blk)
+{
+	((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+	return 0;
+}
+
+static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) {
+		hw_write_20kx(hw, AMOPLO+idx*8, ctl->amoplo);
+		ctl->dirty.bf.amoplo = 0;
+		hw_write_20kx(hw, AMOPHI+idx*8, ctl->amophi);
+		ctl->dirty.bf.amophi = 0;
+	}
+
+	return 0;
+}
+
+static int amixer_get_y(void *blk)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	return get_field(ctl->amoplo, AMOPLO_Y);
+}
+
+static unsigned int amixer_get_dirty(void *blk)
+{
+	return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static int amixer_rsc_get_ctrl_blk(void **rblk)
+{
+	struct amixer_rsc_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int amixer_rsc_put_ctrl_blk(void *blk)
+{
+	kfree((struct amixer_rsc_ctrl_blk *)blk);
+
+	return 0;
+}
+
+static int amixer_mgr_get_ctrl_blk(void **rblk)
+{
+	/*amixer_mgr_ctrl_blk_t *blk;*/
+
+	*rblk = NULL;
+	/*blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;*/
+
+	return 0;
+}
+
+static int amixer_mgr_put_ctrl_blk(void *blk)
+{
+	/*kfree((amixer_mgr_ctrl_blk_t *)blk);*/
+
+	return 0;
+}
+
+/*
+ * DAIO control block definitions.
+ */
+
+/* Receiver Sample Rate Tracker Control register */
+#define SRTCTL_SRCR	0x000000FF
+#define SRTCTL_SRCL	0x0000FF00
+#define SRTCTL_RSR	0x00030000
+#define SRTCTL_DRAT	0x000C0000
+#define SRTCTL_RLE	0x10000000
+#define SRTCTL_RLP	0x20000000
+#define SRTCTL_EC	0x40000000
+#define SRTCTL_ET	0x80000000
+
+/* DAIO Receiver register dirty flags */
+union dai_dirty {
+	struct {
+		u16 srtctl:1;
+		u16 rsv:15;
+	} bf;
+	u16 data;
+};
+
+/* DAIO Receiver control block */
+struct dai_ctrl_blk {
+	unsigned int	srtctl;
+	union dai_dirty	dirty;
+};
+
+/* S/PDIF Transmitter register dirty flags */
+union dao_dirty {
+	struct {
+		u16 spos:1;
+		u16 rsv:15;
+	} bf;
+	u16 data;
+};
+
+/* S/PDIF Transmitter control block */
+struct dao_ctrl_blk {
+	unsigned int 	spos; /* S/PDIF Output Channel Status Register */
+	union dao_dirty	dirty;
+};
+
+/* Audio Input Mapper RAM */
+#define AIM_ARC		0x00000FFF
+#define AIM_NXT		0x007F0000
+
+struct daoimap {
+	unsigned int aim;
+	unsigned int idx;
+};
+
+/* I2S Transmitter/Receiver Control register */
+#define I2SCTL_EA	0x00000004
+#define I2SCTL_EI	0x00000010
+
+/* S/PDIF Transmitter Control register */
+#define SPOCTL_OE	0x00000001
+#define SPOCTL_OS	0x0000000E
+#define SPOCTL_RIV	0x00000010
+#define SPOCTL_LIV	0x00000020
+#define SPOCTL_SR	0x000000C0
+
+/* S/PDIF Receiver Control register */
+#define SPICTL_EN	0x00000001
+#define SPICTL_I24	0x00000002
+#define SPICTL_IB	0x00000004
+#define SPICTL_SM	0x00000008
+#define SPICTL_VM	0x00000010
+
+/* DAIO manager register dirty flags */
+union daio_mgr_dirty {
+	struct {
+		u32 i2soctl:4;
+		u32 i2sictl:4;
+		u32 spoctl:4;
+		u32 spictl:4;
+		u32 daoimap:1;
+		u32 rsv:15;
+	} bf;
+	u32 data;
+};
+
+/* DAIO manager control block */
+struct daio_mgr_ctrl_blk {
+	unsigned int		i2sctl;
+	unsigned int		spoctl;
+	unsigned int		spictl;
+	struct daoimap		daoimap;
+	union daio_mgr_dirty	dirty;
+};
+
+static int dai_srt_set_srcr(void *blk, unsigned int src)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srtctl, SRTCTL_SRCR, src);
+	ctl->dirty.bf.srtctl = 1;
+	return 0;
+}
+
+static int dai_srt_set_srcl(void *blk, unsigned int src)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srtctl, SRTCTL_SRCL, src);
+	ctl->dirty.bf.srtctl = 1;
+	return 0;
+}
+
+static int dai_srt_set_rsr(void *blk, unsigned int rsr)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srtctl, SRTCTL_RSR, rsr);
+	ctl->dirty.bf.srtctl = 1;
+	return 0;
+}
+
+static int dai_srt_set_drat(void *blk, unsigned int drat)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srtctl, SRTCTL_DRAT, drat);
+	ctl->dirty.bf.srtctl = 1;
+	return 0;
+}
+
+static int dai_srt_set_ec(void *blk, unsigned int ec)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srtctl, SRTCTL_EC, ec ? 1 : 0);
+	ctl->dirty.bf.srtctl = 1;
+	return 0;
+}
+
+static int dai_srt_set_et(void *blk, unsigned int et)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srtctl, SRTCTL_ET, et ? 1 : 0);
+	ctl->dirty.bf.srtctl = 1;
+	return 0;
+}
+
+static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	if (ctl->dirty.bf.srtctl) {
+		if (idx < 4) {
+			/* S/PDIF SRTs */
+			hw_write_20kx(hw, SRTSCTL+0x4*idx, ctl->srtctl);
+		} else {
+			/* I2S SRT */
+			hw_write_20kx(hw, SRTICTL, ctl->srtctl);
+		}
+		ctl->dirty.bf.srtctl = 0;
+	}
+
+	return 0;
+}
+
+static int dai_get_ctrl_blk(void **rblk)
+{
+	struct dai_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int dai_put_ctrl_blk(void *blk)
+{
+	kfree((struct dai_ctrl_blk *)blk);
+
+	return 0;
+}
+
+static int dao_set_spos(void *blk, unsigned int spos)
+{
+	((struct dao_ctrl_blk *)blk)->spos = spos;
+	((struct dao_ctrl_blk *)blk)->dirty.bf.spos = 1;
+	return 0;
+}
+
+static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+	struct dao_ctrl_blk *ctl = blk;
+
+	if (ctl->dirty.bf.spos) {
+		if (idx < 4) {
+			/* S/PDIF SPOSx */
+			hw_write_20kx(hw, SPOS+0x4*idx, ctl->spos);
+		}
+		ctl->dirty.bf.spos = 0;
+	}
+
+	return 0;
+}
+
+static int dao_get_spos(void *blk, unsigned int *spos)
+{
+	*spos = ((struct dao_ctrl_blk *)blk)->spos;
+	return 0;
+}
+
+static int dao_get_ctrl_blk(void **rblk)
+{
+	struct dao_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int dao_put_ctrl_blk(void *blk)
+{
+	kfree((struct dao_ctrl_blk *)blk);
+
+	return 0;
+}
+
+static int daio_mgr_enb_dai(void *blk, unsigned int idx)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	if (idx < 4) {
+		/* S/PDIF input */
+		set_field(&ctl->spictl, SPICTL_EN << (idx*8), 1);
+		ctl->dirty.bf.spictl |= (0x1 << idx);
+	} else {
+		/* I2S input */
+		idx %= 4;
+		set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 1);
+		ctl->dirty.bf.i2sictl |= (0x1 << idx);
+	}
+	return 0;
+}
+
+static int daio_mgr_dsb_dai(void *blk, unsigned int idx)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	if (idx < 4) {
+		/* S/PDIF input */
+		set_field(&ctl->spictl, SPICTL_EN << (idx*8), 0);
+		ctl->dirty.bf.spictl |= (0x1 << idx);
+	} else {
+		/* I2S input */
+		idx %= 4;
+		set_field(&ctl->i2sctl, I2SCTL_EI << (idx*8), 0);
+		ctl->dirty.bf.i2sictl |= (0x1 << idx);
+	}
+	return 0;
+}
+
+static int daio_mgr_enb_dao(void *blk, unsigned int idx)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	if (idx < 4) {
+		/* S/PDIF output */
+		set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 1);
+		ctl->dirty.bf.spoctl |= (0x1 << idx);
+	} else {
+		/* I2S output */
+		idx %= 4;
+		set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 1);
+		ctl->dirty.bf.i2soctl |= (0x1 << idx);
+	}
+	return 0;
+}
+
+static int daio_mgr_dsb_dao(void *blk, unsigned int idx)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	if (idx < 4) {
+		/* S/PDIF output */
+		set_field(&ctl->spoctl, SPOCTL_OE << (idx*8), 0);
+		ctl->dirty.bf.spoctl |= (0x1 << idx);
+	} else {
+		/* I2S output */
+		idx %= 4;
+		set_field(&ctl->i2sctl, I2SCTL_EA << (idx*8), 0);
+		ctl->dirty.bf.i2soctl |= (0x1 << idx);
+	}
+	return 0;
+}
+
+static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	if (idx < 4) {
+		/* S/PDIF output */
+		switch ((conf & 0x7)) {
+		case 0:
+			set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 3);
+			break; /* CDIF */
+		case 1:
+			set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 0);
+			break;
+		case 2:
+			set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 1);
+			break;
+		case 4:
+			set_field(&ctl->spoctl, SPOCTL_SR << (idx*8), 2);
+			break;
+		default:
+			break;
+		}
+		set_field(&ctl->spoctl, SPOCTL_LIV << (idx*8),
+			  (conf >> 4) & 0x1); /* Non-audio */
+		set_field(&ctl->spoctl, SPOCTL_RIV << (idx*8),
+			  (conf >> 4) & 0x1); /* Non-audio */
+		set_field(&ctl->spoctl, SPOCTL_OS << (idx*8),
+			  ((conf >> 3) & 0x1) ? 2 : 2); /* Raw */
+
+		ctl->dirty.bf.spoctl |= (0x1 << idx);
+	} else {
+		/* I2S output */
+		/*idx %= 4; */
+	}
+	return 0;
+}
+
+static int daio_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->daoimap.aim, AIM_ARC, slot);
+	ctl->dirty.bf.daoimap = 1;
+	return 0;
+}
+
+static int daio_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->daoimap.aim, AIM_NXT, next);
+	ctl->dirty.bf.daoimap = 1;
+	return 0;
+}
+
+static int daio_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	ctl->daoimap.idx = addr;
+	ctl->dirty.bf.daoimap = 1;
+	return 0;
+}
+
+static int daio_mgr_commit_write(struct hw *hw, void *blk)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+	int i;
+
+	if (ctl->dirty.bf.i2sictl || ctl->dirty.bf.i2soctl) {
+		for (i = 0; i < 4; i++) {
+			if ((ctl->dirty.bf.i2sictl & (0x1 << i)))
+				ctl->dirty.bf.i2sictl &= ~(0x1 << i);
+
+			if ((ctl->dirty.bf.i2soctl & (0x1 << i)))
+				ctl->dirty.bf.i2soctl &= ~(0x1 << i);
+		}
+		hw_write_20kx(hw, I2SCTL, ctl->i2sctl);
+		mdelay(1);
+	}
+	if (ctl->dirty.bf.spoctl) {
+		for (i = 0; i < 4; i++) {
+			if ((ctl->dirty.bf.spoctl & (0x1 << i)))
+				ctl->dirty.bf.spoctl &= ~(0x1 << i);
+		}
+		hw_write_20kx(hw, SPOCTL, ctl->spoctl);
+		mdelay(1);
+	}
+	if (ctl->dirty.bf.spictl) {
+		for (i = 0; i < 4; i++) {
+			if ((ctl->dirty.bf.spictl & (0x1 << i)))
+				ctl->dirty.bf.spictl &= ~(0x1 << i);
+		}
+		hw_write_20kx(hw, SPICTL, ctl->spictl);
+		mdelay(1);
+	}
+	if (ctl->dirty.bf.daoimap) {
+		hw_write_20kx(hw, DAOIMAP+ctl->daoimap.idx*4,
+					ctl->daoimap.aim);
+		ctl->dirty.bf.daoimap = 0;
+	}
+
+	return 0;
+}
+
+static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
+{
+	struct daio_mgr_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	blk->i2sctl = hw_read_20kx(hw, I2SCTL);
+	blk->spoctl = hw_read_20kx(hw, SPOCTL);
+	blk->spictl = hw_read_20kx(hw, SPICTL);
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int daio_mgr_put_ctrl_blk(void *blk)
+{
+	kfree((struct daio_mgr_ctrl_blk *)blk);
+
+	return 0;
+}
+
+/* Timer interrupt */
+static int set_timer_irq(struct hw *hw, int enable)
+{
+	hw_write_20kx(hw, GIE, enable ? IT_INT : 0);
+	return 0;
+}
+
+static int set_timer_tick(struct hw *hw, unsigned int ticks)
+{
+	if (ticks)
+		ticks |= TIMR_IE | TIMR_IP;
+	hw_write_20kx(hw, TIMR, ticks);
+	return 0;
+}
+
+static unsigned int get_wc(struct hw *hw)
+{
+	return hw_read_20kx(hw, WC);
+}
+
+/* Card hardware initialization block */
+struct dac_conf {
+	unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct adc_conf {
+	unsigned int msr; 	/* master sample rate in rsrs */
+	unsigned char input; 	/* the input source of ADC */
+	unsigned char mic20db; 	/* boost mic by 20db if input is microphone */
+};
+
+struct daio_conf {
+	unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct trn_conf {
+	unsigned long vm_pgt_phys;
+};
+
+static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
+{
+	u32 i2sorg;
+	u32 spdorg;
+
+	/* Read I2S CTL.  Keep original value. */
+	/*i2sorg = hw_read_20kx(hw, I2SCTL);*/
+	i2sorg = 0x94040404; /* enable all audio out and I2S-D input */
+	/* Program I2S with proper master sample rate and enable
+	 * the correct I2S channel. */
+	i2sorg &= 0xfffffffc;
+
+	/* Enable S/PDIF-out-A in fixed 24-bit data
+	 * format and default to 48kHz. */
+	/* Disable all before doing any changes. */
+	hw_write_20kx(hw, SPOCTL, 0x0);
+	spdorg = 0x05;
+
+	switch (info->msr) {
+	case 1:
+		i2sorg |= 1;
+		spdorg |= (0x0 << 6);
+		break;
+	case 2:
+		i2sorg |= 2;
+		spdorg |= (0x1 << 6);
+		break;
+	case 4:
+		i2sorg |= 3;
+		spdorg |= (0x2 << 6);
+		break;
+	default:
+		i2sorg |= 1;
+		break;
+	}
+
+	hw_write_20kx(hw, I2SCTL, i2sorg);
+	hw_write_20kx(hw, SPOCTL, spdorg);
+
+	/* Enable S/PDIF-in-A in fixed 24-bit data format. */
+	/* Disable all before doing any changes. */
+	hw_write_20kx(hw, SPICTL, 0x0);
+	mdelay(1);
+	spdorg = 0x0a0a0a0a;
+	hw_write_20kx(hw, SPICTL, spdorg);
+	mdelay(1);
+
+	return 0;
+}
+
+/* TRANSPORT operations */
+static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
+{
+	u32 trnctl;
+	u32 ptp_phys_low, ptp_phys_high;
+
+	/* Set up device page table */
+	if ((~0UL) == info->vm_pgt_phys) {
+		printk(KERN_ERR "Wrong device page table page address!\n");
+		return -1;
+	}
+
+	trnctl = 0x13;  /* 32-bit, 4k-size page */
+	ptp_phys_low = (u32)info->vm_pgt_phys;
+	ptp_phys_high = upper_32_bits(info->vm_pgt_phys);
+	if (sizeof(void *) == 8) /* 64bit address */
+		trnctl |= (1 << 2);
+#if 0 /* Only 4k h/w pages for simplicitiy */
+#if PAGE_SIZE == 8192
+	trnctl |= (1<<5);
+#endif
+#endif
+	hw_write_20kx(hw, PTPALX, ptp_phys_low);
+	hw_write_20kx(hw, PTPAHX, ptp_phys_high);
+	hw_write_20kx(hw, TRNCTL, trnctl);
+	hw_write_20kx(hw, TRNIS, 0x200c01); /* realy needed? */
+
+	return 0;
+}
+
+/* Card initialization */
+#define GCTL_EAC	0x00000001
+#define GCTL_EAI	0x00000002
+#define GCTL_BEP	0x00000004
+#define GCTL_BES	0x00000008
+#define GCTL_DSP	0x00000010
+#define GCTL_DBP	0x00000020
+#define GCTL_ABP	0x00000040
+#define GCTL_TBP	0x00000080
+#define GCTL_SBP	0x00000100
+#define GCTL_FBP	0x00000200
+#define GCTL_XA		0x00000400
+#define GCTL_ET		0x00000800
+#define GCTL_PR		0x00001000
+#define GCTL_MRL	0x00002000
+#define GCTL_SDE	0x00004000
+#define GCTL_SDI	0x00008000
+#define GCTL_SM		0x00010000
+#define GCTL_SR		0x00020000
+#define GCTL_SD		0x00040000
+#define GCTL_SE		0x00080000
+#define GCTL_AID	0x00100000
+
+static int hw_pll_init(struct hw *hw, unsigned int rsr)
+{
+	unsigned int pllctl;
+	int i;
+
+	pllctl = (48000 == rsr) ? 0x1480a001 : 0x1480a731;
+	for (i = 0; i < 3; i++) {
+		if (hw_read_20kx(hw, PLLCTL) == pllctl)
+			break;
+
+		hw_write_20kx(hw, PLLCTL, pllctl);
+		mdelay(40);
+	}
+	if (i >= 3) {
+		printk(KERN_ALERT "PLL initialization failed!!!\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int hw_auto_init(struct hw *hw)
+{
+	unsigned int gctl;
+	int i;
+
+	gctl = hw_read_20kx(hw, GCTL);
+	set_field(&gctl, GCTL_EAI, 0);
+	hw_write_20kx(hw, GCTL, gctl);
+	set_field(&gctl, GCTL_EAI, 1);
+	hw_write_20kx(hw, GCTL, gctl);
+	mdelay(10);
+	for (i = 0; i < 400000; i++) {
+		gctl = hw_read_20kx(hw, GCTL);
+		if (get_field(gctl, GCTL_AID))
+			break;
+	}
+	if (!get_field(gctl, GCTL_AID)) {
+		printk(KERN_ALERT "Card Auto-init failed!!!\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int i2c_unlock(struct hw *hw)
+{
+	if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+		return 0;
+
+	hw_write_pci(hw, 0xcc, 0x8c);
+	hw_write_pci(hw, 0xcc, 0x0e);
+	if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+		return 0;
+
+	hw_write_pci(hw, 0xcc, 0xee);
+	hw_write_pci(hw, 0xcc, 0xaa);
+	if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+		return 0;
+
+	return -1;
+}
+
+static void i2c_lock(struct hw *hw)
+{
+	if ((hw_read_pci(hw, 0xcc) & 0xff) == 0xaa)
+		hw_write_pci(hw, 0xcc, 0x00);
+}
+
+static void i2c_write(struct hw *hw, u32 device, u32 addr, u32 data)
+{
+	unsigned int ret;
+
+	do {
+		ret = hw_read_pci(hw, 0xEC);
+	} while (!(ret & 0x800000));
+	hw_write_pci(hw, 0xE0, device);
+	hw_write_pci(hw, 0xE4, (data << 8) | (addr & 0xff));
+}
+
+/* DAC operations */
+
+static int hw_reset_dac(struct hw *hw)
+{
+	u32 i;
+	u16 gpioorg;
+	unsigned int ret;
+
+	if (i2c_unlock(hw))
+		return -1;
+
+	do {
+		ret = hw_read_pci(hw, 0xEC);
+	} while (!(ret & 0x800000));
+	hw_write_pci(hw, 0xEC, 0x05);  /* write to i2c status control */
+
+	/* To be effective, need to reset the DAC twice. */
+	for (i = 0; i < 2;  i++) {
+		/* set gpio */
+		mdelay(100);
+		gpioorg = (u16)hw_read_20kx(hw, GPIO);
+		gpioorg &= 0xfffd;
+		hw_write_20kx(hw, GPIO, gpioorg);
+		mdelay(1);
+		hw_write_20kx(hw, GPIO, gpioorg | 0x2);
+	}
+
+	i2c_write(hw, 0x00180080, 0x01, 0x80);
+	i2c_write(hw, 0x00180080, 0x02, 0x10);
+
+	i2c_lock(hw);
+
+	return 0;
+}
+
+static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
+{
+	u32 data;
+	u16 gpioorg;
+	unsigned int ret;
+
+	if (hw->model == CTSB055X) {
+		/* SB055x, unmute outputs */
+		gpioorg = (u16)hw_read_20kx(hw, GPIO);
+		gpioorg &= 0xffbf;	/* set GPIO6 to low */
+		gpioorg |= 2;		/* set GPIO1 to high */
+		hw_write_20kx(hw, GPIO, gpioorg);
+		return 0;
+	}
+
+	/* mute outputs */
+	gpioorg = (u16)hw_read_20kx(hw, GPIO);
+	gpioorg &= 0xffbf;
+	hw_write_20kx(hw, GPIO, gpioorg);
+
+	hw_reset_dac(hw);
+
+	if (i2c_unlock(hw))
+		return -1;
+
+	hw_write_pci(hw, 0xEC, 0x05);  /* write to i2c status control */
+	do {
+		ret = hw_read_pci(hw, 0xEC);
+	} while (!(ret & 0x800000));
+
+	switch (info->msr) {
+	case 1:
+		data = 0x24;
+		break;
+	case 2:
+		data = 0x25;
+		break;
+	case 4:
+		data = 0x26;
+		break;
+	default:
+		data = 0x24;
+		break;
+	}
+
+	i2c_write(hw, 0x00180080, 0x06, data);
+	i2c_write(hw, 0x00180080, 0x09, data);
+	i2c_write(hw, 0x00180080, 0x0c, data);
+	i2c_write(hw, 0x00180080, 0x0f, data);
+
+	i2c_lock(hw);
+
+	/* unmute outputs */
+	gpioorg = (u16)hw_read_20kx(hw, GPIO);
+	gpioorg = gpioorg | 0x40;
+	hw_write_20kx(hw, GPIO, gpioorg);
+
+	return 0;
+}
+
+/* ADC operations */
+
+static int is_adc_input_selected_SB055x(struct hw *hw, enum ADCSRC type)
+{
+	return 0;
+}
+
+static int is_adc_input_selected_SBx(struct hw *hw, enum ADCSRC type)
+{
+	u32 data;
+
+	data = hw_read_20kx(hw, GPIO);
+	switch (type) {
+	case ADC_MICIN:
+		data = ((data & (0x1<<7)) && (data & (0x1<<8)));
+		break;
+	case ADC_LINEIN:
+		data = (!(data & (0x1<<7)) && (data & (0x1<<8)));
+		break;
+	case ADC_NONE: /* Digital I/O */
+		data = (!(data & (0x1<<8)));
+		break;
+	default:
+		data = 0;
+	}
+	return data;
+}
+
+static int is_adc_input_selected_hendrix(struct hw *hw, enum ADCSRC type)
+{
+	u32 data;
+
+	data = hw_read_20kx(hw, GPIO);
+	switch (type) {
+	case ADC_MICIN:
+		data = (data & (0x1 << 7)) ? 1 : 0;
+		break;
+	case ADC_LINEIN:
+		data = (data & (0x1 << 7)) ? 0 : 1;
+		break;
+	default:
+		data = 0;
+	}
+	return data;
+}
+
+static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
+{
+	switch (hw->model) {
+	case CTSB055X:
+		return is_adc_input_selected_SB055x(hw, type);
+	case CTSB073X:
+		return is_adc_input_selected_hendrix(hw, type);
+	case CTUAA:
+		return is_adc_input_selected_hendrix(hw, type);
+	default:
+		return is_adc_input_selected_SBx(hw, type);
+	}
+}
+
+static int
+adc_input_select_SB055x(struct hw *hw, enum ADCSRC type, unsigned char boost)
+{
+	u32 data;
+
+	/*
+	 * check and set the following GPIO bits accordingly
+	 * ADC_Gain		= GPIO2
+	 * DRM_off		= GPIO3
+	 * Mic_Pwr_on		= GPIO7
+	 * Digital_IO_Sel	= GPIO8
+	 * Mic_Sw		= GPIO9
+	 * Aux/MicLine_Sw	= GPIO12
+	 */
+	data = hw_read_20kx(hw, GPIO);
+	data &= 0xec73;
+	switch (type) {
+	case ADC_MICIN:
+		data |= (0x1<<7) | (0x1<<8) | (0x1<<9) ;
+		data |= boost ? (0x1<<2) : 0;
+		break;
+	case ADC_LINEIN:
+		data |= (0x1<<8);
+		break;
+	case ADC_AUX:
+		data |= (0x1<<8) | (0x1<<12);
+		break;
+	case ADC_NONE:
+		data |= (0x1<<12);  /* set to digital */
+		break;
+	default:
+		return -1;
+	}
+
+	hw_write_20kx(hw, GPIO, data);
+
+	return 0;
+}
+
+
+static int
+adc_input_select_SBx(struct hw *hw, enum ADCSRC type, unsigned char boost)
+{
+	u32 data;
+	u32 i2c_data;
+	unsigned int ret;
+
+	if (i2c_unlock(hw))
+		return -1;
+
+	do {
+		ret = hw_read_pci(hw, 0xEC);
+	} while (!(ret & 0x800000)); /* i2c ready poll */
+	/* set i2c access mode as Direct Control */
+	hw_write_pci(hw, 0xEC, 0x05);
+
+	data = hw_read_20kx(hw, GPIO);
+	switch (type) {
+	case ADC_MICIN:
+		data |= ((0x1 << 7) | (0x1 << 8));
+		i2c_data = 0x1;  /* Mic-in */
+		break;
+	case ADC_LINEIN:
+		data &= ~(0x1 << 7);
+		data |= (0x1 << 8);
+		i2c_data = 0x2; /* Line-in */
+		break;
+	case ADC_NONE:
+		data &= ~(0x1 << 8);
+		i2c_data = 0x0; /* set to Digital */
+		break;
+	default:
+		i2c_lock(hw);
+		return -1;
+	}
+	hw_write_20kx(hw, GPIO, data);
+	i2c_write(hw, 0x001a0080, 0x2a, i2c_data);
+	if (boost) {
+		i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */
+		i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */
+	} else {
+		i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */
+		i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */
+	}
+
+	i2c_lock(hw);
+
+	return 0;
+}
+
+static int
+adc_input_select_hendrix(struct hw *hw, enum ADCSRC type, unsigned char boost)
+{
+	u32 data;
+	u32 i2c_data;
+	unsigned int ret;
+
+	if (i2c_unlock(hw))
+		return -1;
+
+	do {
+		ret = hw_read_pci(hw, 0xEC);
+	} while (!(ret & 0x800000)); /* i2c ready poll */
+	/* set i2c access mode as Direct Control */
+	hw_write_pci(hw, 0xEC, 0x05);
+
+	data = hw_read_20kx(hw, GPIO);
+	switch (type) {
+	case ADC_MICIN:
+		data |= (0x1 << 7);
+		i2c_data = 0x1;  /* Mic-in */
+		break;
+	case ADC_LINEIN:
+		data &= ~(0x1 << 7);
+		i2c_data = 0x2; /* Line-in */
+		break;
+	default:
+		i2c_lock(hw);
+		return -1;
+	}
+	hw_write_20kx(hw, GPIO, data);
+	i2c_write(hw, 0x001a0080, 0x2a, i2c_data);
+	if (boost) {
+		i2c_write(hw, 0x001a0080, 0x1c, 0xe7); /* +12dB boost */
+		i2c_write(hw, 0x001a0080, 0x1e, 0xe7); /* +12dB boost */
+	} else {
+		i2c_write(hw, 0x001a0080, 0x1c, 0xcf); /* No boost */
+		i2c_write(hw, 0x001a0080, 0x1e, 0xcf); /* No boost */
+	}
+
+	i2c_lock(hw);
+
+	return 0;
+}
+
+static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
+{
+	int state = type == ADC_MICIN;
+
+	switch (hw->model) {
+	case CTSB055X:
+		return adc_input_select_SB055x(hw, type, state);
+	case CTSB073X:
+		return adc_input_select_hendrix(hw, type, state);
+	case CTUAA:
+		return adc_input_select_hendrix(hw, type, state);
+	default:
+		return adc_input_select_SBx(hw, type, state);
+	}
+}
+
+static int adc_init_SB055x(struct hw *hw, int input, int mic20db)
+{
+	return adc_input_select_SB055x(hw, input, mic20db);
+}
+
+static int adc_init_SBx(struct hw *hw, int input, int mic20db)
+{
+	u16 gpioorg;
+	u16 input_source;
+	u32 adcdata;
+	unsigned int ret;
+
+	input_source = 0x100;  /* default to analog */
+	switch (input) {
+	case ADC_MICIN:
+		adcdata = 0x1;
+		input_source = 0x180;  /* set GPIO7 to select Mic */
+		break;
+	case ADC_LINEIN:
+		adcdata = 0x2;
+		break;
+	case ADC_VIDEO:
+		adcdata = 0x4;
+		break;
+	case ADC_AUX:
+		adcdata = 0x8;
+		break;
+	case ADC_NONE:
+		adcdata = 0x0;
+		input_source = 0x0;  /* set to Digital */
+		break;
+	default:
+		adcdata = 0x0;
+		break;
+	}
+
+	if (i2c_unlock(hw))
+		return -1;
+
+	do {
+		ret = hw_read_pci(hw, 0xEC);
+	} while (!(ret & 0x800000)); /* i2c ready poll */
+	hw_write_pci(hw, 0xEC, 0x05);  /* write to i2c status control */
+
+	i2c_write(hw, 0x001a0080, 0x0e, 0x08);
+	i2c_write(hw, 0x001a0080, 0x18, 0x0a);
+	i2c_write(hw, 0x001a0080, 0x28, 0x86);
+	i2c_write(hw, 0x001a0080, 0x2a, adcdata);
+
+	if (mic20db) {
+		i2c_write(hw, 0x001a0080, 0x1c, 0xf7);
+		i2c_write(hw, 0x001a0080, 0x1e, 0xf7);
+	} else {
+		i2c_write(hw, 0x001a0080, 0x1c, 0xcf);
+		i2c_write(hw, 0x001a0080, 0x1e, 0xcf);
+	}
+
+	if (!(hw_read_20kx(hw, ID0) & 0x100))
+		i2c_write(hw, 0x001a0080, 0x16, 0x26);
+
+	i2c_lock(hw);
+
+	gpioorg = (u16)hw_read_20kx(hw,  GPIO);
+	gpioorg &= 0xfe7f;
+	gpioorg |= input_source;
+	hw_write_20kx(hw, GPIO, gpioorg);
+
+	return 0;
+}
+
+static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
+{
+	if (hw->model == CTSB055X)
+		return adc_init_SB055x(hw, info->input, info->mic20db);
+	else
+		return adc_init_SBx(hw, info->input, info->mic20db);
+}
+
+static int hw_have_digit_io_switch(struct hw *hw)
+{
+	/* SB073x and Vista compatible cards have no digit IO switch */
+	return !(hw->model == CTSB073X || hw->model == CTUAA);
+}
+
+#define CTLBITS(a, b, c, d)	(((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+
+#define UAA_CFG_PWRSTATUS	0x44
+#define UAA_CFG_SPACE_FLAG	0xA0
+#define UAA_CORE_CHANGE		0x3FFC
+static int uaa_to_xfi(struct pci_dev *pci)
+{
+	unsigned int bar0, bar1, bar2, bar3, bar4, bar5;
+	unsigned int cmd, irq, cl_size, l_timer, pwr;
+	unsigned int is_uaa;
+	unsigned int data[4] = {0};
+	unsigned int io_base;
+	void *mem_base;
+	int i;
+	const u32 CTLX = CTLBITS('C', 'T', 'L', 'X');
+	const u32 CTL_ = CTLBITS('C', 'T', 'L', '-');
+	const u32 CTLF = CTLBITS('C', 'T', 'L', 'F');
+	const u32 CTLi = CTLBITS('C', 'T', 'L', 'i');
+	const u32 CTLA = CTLBITS('C', 'T', 'L', 'A');
+	const u32 CTLZ = CTLBITS('C', 'T', 'L', 'Z');
+	const u32 CTLL = CTLBITS('C', 'T', 'L', 'L');
+
+	/* By default, Hendrix card UAA Bar0 should be using memory... */
+	io_base = pci_resource_start(pci, 0);
+	mem_base = ioremap(io_base, pci_resource_len(pci, 0));
+	if (NULL == mem_base)
+		return -ENOENT;
+
+	/* Read current mode from Mode Change Register */
+	for (i = 0; i < 4; i++)
+		data[i] = readl(mem_base + UAA_CORE_CHANGE);
+
+	/* Determine current mode... */
+	if (data[0] == CTLA) {
+		is_uaa = ((data[1] == CTLZ && data[2] == CTLL
+			  && data[3] == CTLA) || (data[1] == CTLA
+			  && data[2] == CTLZ && data[3] == CTLL));
+	} else if (data[0] == CTLZ) {
+		is_uaa = (data[1] == CTLL
+				&& data[2] == CTLA && data[3] == CTLA);
+	} else if (data[0] == CTLL) {
+		is_uaa = (data[1] == CTLA
+				&& data[2] == CTLA && data[3] == CTLZ);
+	} else {
+		is_uaa = 0;
+	}
+
+	if (!is_uaa) {
+		/* Not in UAA mode currently. Return directly. */
+		iounmap(mem_base);
+		return 0;
+	}
+
+	pci_read_config_dword(pci, PCI_BASE_ADDRESS_0, &bar0);
+	pci_read_config_dword(pci, PCI_BASE_ADDRESS_1, &bar1);
+	pci_read_config_dword(pci, PCI_BASE_ADDRESS_2, &bar2);
+	pci_read_config_dword(pci, PCI_BASE_ADDRESS_3, &bar3);
+	pci_read_config_dword(pci, PCI_BASE_ADDRESS_4, &bar4);
+	pci_read_config_dword(pci, PCI_BASE_ADDRESS_5, &bar5);
+	pci_read_config_dword(pci, PCI_INTERRUPT_LINE, &irq);
+	pci_read_config_dword(pci, PCI_CACHE_LINE_SIZE, &cl_size);
+	pci_read_config_dword(pci, PCI_LATENCY_TIMER, &l_timer);
+	pci_read_config_dword(pci, UAA_CFG_PWRSTATUS, &pwr);
+	pci_read_config_dword(pci, PCI_COMMAND, &cmd);
+
+	/* Set up X-Fi core PCI configuration space. */
+	/* Switch to X-Fi config space with BAR0 exposed. */
+	pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x87654321);
+	/* Copy UAA's BAR5 into X-Fi BAR0 */
+	pci_write_config_dword(pci, PCI_BASE_ADDRESS_0, bar5);
+	/* Switch to X-Fi config space without BAR0 exposed. */
+	pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x12345678);
+	pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, bar1);
+	pci_write_config_dword(pci, PCI_BASE_ADDRESS_2, bar2);
+	pci_write_config_dword(pci, PCI_BASE_ADDRESS_3, bar3);
+	pci_write_config_dword(pci, PCI_BASE_ADDRESS_4, bar4);
+	pci_write_config_dword(pci, PCI_INTERRUPT_LINE, irq);
+	pci_write_config_dword(pci, PCI_CACHE_LINE_SIZE, cl_size);
+	pci_write_config_dword(pci, PCI_LATENCY_TIMER, l_timer);
+	pci_write_config_dword(pci, UAA_CFG_PWRSTATUS, pwr);
+	pci_write_config_dword(pci, PCI_COMMAND, cmd);
+
+	/* Switch to X-Fi mode */
+	writel(CTLX, (mem_base + UAA_CORE_CHANGE));
+	writel(CTL_, (mem_base + UAA_CORE_CHANGE));
+	writel(CTLF, (mem_base + UAA_CORE_CHANGE));
+	writel(CTLi, (mem_base + UAA_CORE_CHANGE));
+
+	iounmap(mem_base);
+
+	return 0;
+}
+
+static irqreturn_t ct_20k1_interrupt(int irq, void *dev_id)
+{
+	struct hw *hw = dev_id;
+	unsigned int status;
+
+	status = hw_read_20kx(hw, GIP);
+	if (!status)
+		return IRQ_NONE;
+
+	if (hw->irq_callback)
+		hw->irq_callback(hw->irq_callback_data, status);
+
+	hw_write_20kx(hw, GIP, status);
+	return IRQ_HANDLED;
+}
+
+static int hw_card_start(struct hw *hw)
+{
+	int err;
+	struct pci_dev *pci = hw->pci;
+
+	err = pci_enable_device(pci);
+	if (err < 0)
+		return err;
+
+	/* Set DMA transfer mask */
+	if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
+	    pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
+		printk(KERN_ERR "architecture does not support PCI "
+				"busmaster DMA with mask 0x%llx\n",
+		       CT_XFI_DMA_MASK);
+		err = -ENXIO;
+		goto error1;
+	}
+
+	if (!hw->io_base) {
+		err = pci_request_regions(pci, "XFi");
+		if (err < 0)
+			goto error1;
+
+		if (hw->model == CTUAA)
+			hw->io_base = pci_resource_start(pci, 5);
+		else
+			hw->io_base = pci_resource_start(pci, 0);
+
+	}
+
+	/* Switch to X-Fi mode from UAA mode if neeeded */
+	if (hw->model == CTUAA) {
+		err = uaa_to_xfi(pci);
+		if (err)
+			goto error2;
+
+	}
+
+	if (hw->irq < 0) {
+		err = request_irq(pci->irq, ct_20k1_interrupt, IRQF_SHARED,
+				  "ctxfi", hw);
+		if (err < 0) {
+			printk(KERN_ERR "XFi: Cannot get irq %d\n", pci->irq);
+			goto error2;
+		}
+		hw->irq = pci->irq;
+	}
+
+	pci_set_master(pci);
+
+	return 0;
+
+error2:
+	pci_release_regions(pci);
+	hw->io_base = 0;
+error1:
+	pci_disable_device(pci);
+	return err;
+}
+
+static int hw_card_stop(struct hw *hw)
+{
+	unsigned int data;
+
+	/* disable transport bus master and queueing of request */
+	hw_write_20kx(hw, TRNCTL, 0x00);
+
+	/* disable pll */
+	data = hw_read_20kx(hw, PLLCTL);
+	hw_write_20kx(hw, PLLCTL, (data & (~(0x0F<<12))));
+
+	/* TODO: Disable interrupt and so on... */
+	if (hw->irq >= 0)
+		synchronize_irq(hw->irq);
+	return 0;
+}
+
+static int hw_card_shutdown(struct hw *hw)
+{
+	if (hw->irq >= 0)
+		free_irq(hw->irq, hw);
+
+	hw->irq	= -1;
+
+	if (NULL != ((void *)hw->mem_base))
+		iounmap((void *)hw->mem_base);
+
+	hw->mem_base = (unsigned long)NULL;
+
+	if (hw->io_base)
+		pci_release_regions(hw->pci);
+
+	hw->io_base = 0;
+
+	pci_disable_device(hw->pci);
+
+	return 0;
+}
+
+static int hw_card_init(struct hw *hw, struct card_conf *info)
+{
+	int err;
+	unsigned int gctl;
+	u32 data;
+	struct dac_conf dac_info = {0};
+	struct adc_conf adc_info = {0};
+	struct daio_conf daio_info = {0};
+	struct trn_conf trn_info = {0};
+
+	/* Get PCI io port base address and do Hendrix switch if needed. */
+	err = hw_card_start(hw);
+	if (err)
+		return err;
+
+	/* PLL init */
+	err = hw_pll_init(hw, info->rsr);
+	if (err < 0)
+		return err;
+
+	/* kick off auto-init */
+	err = hw_auto_init(hw);
+	if (err < 0)
+		return err;
+
+	/* Enable audio ring */
+	gctl = hw_read_20kx(hw, GCTL);
+	set_field(&gctl, GCTL_EAC, 1);
+	set_field(&gctl, GCTL_DBP, 1);
+	set_field(&gctl, GCTL_TBP, 1);
+	set_field(&gctl, GCTL_FBP, 1);
+	set_field(&gctl, GCTL_ET, 1);
+	hw_write_20kx(hw, GCTL, gctl);
+	mdelay(10);
+
+	/* Reset all global pending interrupts */
+	hw_write_20kx(hw, GIE, 0);
+	/* Reset all SRC pending interrupts */
+	hw_write_20kx(hw, SRCIP, 0);
+	mdelay(30);
+
+	/* Detect the card ID and configure GPIO accordingly. */
+	switch (hw->model) {
+	case CTSB055X:
+		hw_write_20kx(hw, GPIOCTL, 0x13fe);
+		break;
+	case CTSB073X:
+		hw_write_20kx(hw, GPIOCTL, 0x00e6);
+		break;
+	case CTUAA:
+		hw_write_20kx(hw, GPIOCTL, 0x00c2);
+		break;
+	default:
+		hw_write_20kx(hw, GPIOCTL, 0x01e6);
+		break;
+	}
+
+	trn_info.vm_pgt_phys = info->vm_pgt_phys;
+	err = hw_trn_init(hw, &trn_info);
+	if (err < 0)
+		return err;
+
+	daio_info.msr = info->msr;
+	err = hw_daio_init(hw, &daio_info);
+	if (err < 0)
+		return err;
+
+	dac_info.msr = info->msr;
+	err = hw_dac_init(hw, &dac_info);
+	if (err < 0)
+		return err;
+
+	adc_info.msr = info->msr;
+	adc_info.input = ADC_LINEIN;
+	adc_info.mic20db = 0;
+	err = hw_adc_init(hw, &adc_info);
+	if (err < 0)
+		return err;
+
+	data = hw_read_20kx(hw, SRCMCTL);
+	data |= 0x1; /* Enables input from the audio ring */
+	hw_write_20kx(hw, SRCMCTL, data);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int hw_suspend(struct hw *hw, pm_message_t state)
+{
+	struct pci_dev *pci = hw->pci;
+
+	hw_card_stop(hw);
+
+	if (hw->model == CTUAA) {
+		/* Switch to UAA config space. */
+		pci_write_config_dword(pci, UAA_CFG_SPACE_FLAG, 0x0);
+	}
+
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	pci_set_power_state(pci, pci_choose_state(pci, state));
+
+	return 0;
+}
+
+static int hw_resume(struct hw *hw, struct card_conf *info)
+{
+	struct pci_dev *pci = hw->pci;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+
+	/* Re-initialize card hardware. */
+	return hw_card_init(hw, info);
+}
+#endif
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg)
+{
+	u32 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(
+		&container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+	outl(reg, hw->io_base + 0x0);
+	value = inl(hw->io_base + 0x4);
+	spin_unlock_irqrestore(
+		&container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+
+	return value;
+}
+
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(
+		&container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+	outl(reg, hw->io_base + 0x0);
+	outl(data, hw->io_base + 0x4);
+	spin_unlock_irqrestore(
+		&container_of(hw, struct hw20k1, hw)->reg_20k1_lock, flags);
+
+}
+
+static u32 hw_read_pci(struct hw *hw, u32 reg)
+{
+	u32 value;
+	unsigned long flags;
+
+	spin_lock_irqsave(
+		&container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+	outl(reg, hw->io_base + 0x10);
+	value = inl(hw->io_base + 0x14);
+	spin_unlock_irqrestore(
+		&container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+
+	return value;
+}
+
+static void hw_write_pci(struct hw *hw, u32 reg, u32 data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(
+		&container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+	outl(reg, hw->io_base + 0x10);
+	outl(data, hw->io_base + 0x14);
+	spin_unlock_irqrestore(
+		&container_of(hw, struct hw20k1, hw)->reg_pci_lock, flags);
+}
+
+static struct hw ct20k1_preset __devinitdata = {
+	.irq = -1,
+
+	.card_init = hw_card_init,
+	.card_stop = hw_card_stop,
+	.pll_init = hw_pll_init,
+	.is_adc_source_selected = hw_is_adc_input_selected,
+	.select_adc_source = hw_adc_input_select,
+	.have_digit_io_switch = hw_have_digit_io_switch,
+#ifdef CONFIG_PM
+	.suspend = hw_suspend,
+	.resume = hw_resume,
+#endif
+
+	.src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk,
+	.src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk,
+	.src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk,
+	.src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk,
+	.src_set_state = src_set_state,
+	.src_set_bm = src_set_bm,
+	.src_set_rsr = src_set_rsr,
+	.src_set_sf = src_set_sf,
+	.src_set_wr = src_set_wr,
+	.src_set_pm = src_set_pm,
+	.src_set_rom = src_set_rom,
+	.src_set_vo = src_set_vo,
+	.src_set_st = src_set_st,
+	.src_set_ie = src_set_ie,
+	.src_set_ilsz = src_set_ilsz,
+	.src_set_bp = src_set_bp,
+	.src_set_cisz = src_set_cisz,
+	.src_set_ca = src_set_ca,
+	.src_set_sa = src_set_sa,
+	.src_set_la = src_set_la,
+	.src_set_pitch = src_set_pitch,
+	.src_set_dirty = src_set_dirty,
+	.src_set_clear_zbufs = src_set_clear_zbufs,
+	.src_set_dirty_all = src_set_dirty_all,
+	.src_commit_write = src_commit_write,
+	.src_get_ca = src_get_ca,
+	.src_get_dirty = src_get_dirty,
+	.src_dirty_conj_mask = src_dirty_conj_mask,
+	.src_mgr_enbs_src = src_mgr_enbs_src,
+	.src_mgr_enb_src = src_mgr_enb_src,
+	.src_mgr_dsb_src = src_mgr_dsb_src,
+	.src_mgr_commit_write = src_mgr_commit_write,
+
+	.srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk,
+	.srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk,
+	.srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc,
+	.srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser,
+	.srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt,
+	.srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr,
+	.srcimp_mgr_commit_write = srcimp_mgr_commit_write,
+
+	.amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk,
+	.amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk,
+	.amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk,
+	.amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk,
+	.amixer_set_mode = amixer_set_mode,
+	.amixer_set_iv = amixer_set_iv,
+	.amixer_set_x = amixer_set_x,
+	.amixer_set_y = amixer_set_y,
+	.amixer_set_sadr = amixer_set_sadr,
+	.amixer_set_se = amixer_set_se,
+	.amixer_set_dirty = amixer_set_dirty,
+	.amixer_set_dirty_all = amixer_set_dirty_all,
+	.amixer_commit_write = amixer_commit_write,
+	.amixer_get_y = amixer_get_y,
+	.amixer_get_dirty = amixer_get_dirty,
+
+	.dai_get_ctrl_blk = dai_get_ctrl_blk,
+	.dai_put_ctrl_blk = dai_put_ctrl_blk,
+	.dai_srt_set_srco = dai_srt_set_srcr,
+	.dai_srt_set_srcm = dai_srt_set_srcl,
+	.dai_srt_set_rsr = dai_srt_set_rsr,
+	.dai_srt_set_drat = dai_srt_set_drat,
+	.dai_srt_set_ec = dai_srt_set_ec,
+	.dai_srt_set_et = dai_srt_set_et,
+	.dai_commit_write = dai_commit_write,
+
+	.dao_get_ctrl_blk = dao_get_ctrl_blk,
+	.dao_put_ctrl_blk = dao_put_ctrl_blk,
+	.dao_set_spos = dao_set_spos,
+	.dao_commit_write = dao_commit_write,
+	.dao_get_spos = dao_get_spos,
+
+	.daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk,
+	.daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk,
+	.daio_mgr_enb_dai = daio_mgr_enb_dai,
+	.daio_mgr_dsb_dai = daio_mgr_dsb_dai,
+	.daio_mgr_enb_dao = daio_mgr_enb_dao,
+	.daio_mgr_dsb_dao = daio_mgr_dsb_dao,
+	.daio_mgr_dao_init = daio_mgr_dao_init,
+	.daio_mgr_set_imaparc = daio_mgr_set_imaparc,
+	.daio_mgr_set_imapnxt = daio_mgr_set_imapnxt,
+	.daio_mgr_set_imapaddr = daio_mgr_set_imapaddr,
+	.daio_mgr_commit_write = daio_mgr_commit_write,
+
+	.set_timer_irq = set_timer_irq,
+	.set_timer_tick = set_timer_tick,
+	.get_wc = get_wc,
+};
+
+int __devinit create_20k1_hw_obj(struct hw **rhw)
+{
+	struct hw20k1 *hw20k1;
+
+	*rhw = NULL;
+	hw20k1 = kzalloc(sizeof(*hw20k1), GFP_KERNEL);
+	if (NULL == hw20k1)
+		return -ENOMEM;
+
+	spin_lock_init(&hw20k1->reg_20k1_lock);
+	spin_lock_init(&hw20k1->reg_pci_lock);
+
+	hw20k1->hw = ct20k1_preset;
+
+	*rhw = &hw20k1->hw;
+
+	return 0;
+}
+
+int destroy_20k1_hw_obj(struct hw *hw)
+{
+	if (hw->io_base)
+		hw_card_shutdown(hw);
+
+	kfree(container_of(hw, struct hw20k1, hw));
+	return 0;
+}
diff --git a/sound/pci/ctxfi/cthw20k1.h b/sound/pci/ctxfi/cthw20k1.h
new file mode 100644
index 0000000..02f72fb
--- /dev/null
+++ b/sound/pci/ctxfi/cthw20k1.h
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	cthw20k1.h
+ *
+ * @Brief
+ * This file contains the definition of hardware access methord.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 13 2008
+ *
+ */
+
+#ifndef CTHW20K1_H
+#define CTHW20K1_H
+
+#include "cthardware.h"
+
+int create_20k1_hw_obj(struct hw **rhw);
+int destroy_20k1_hw_obj(struct hw *hw);
+
+#endif /* CTHW20K1_H */
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
new file mode 100644
index 0000000..dec46d0
--- /dev/null
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -0,0 +1,2176 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	cthw20k2.c
+ *
+ * @Brief
+ * This file contains the implementation of hardware access methord for 20k2.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 14 2008
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include "cthw20k2.h"
+#include "ct20k2reg.h"
+
+#if BITS_PER_LONG == 32
+#define CT_XFI_DMA_MASK		DMA_BIT_MASK(32) /* 32 bit PTE */
+#else
+#define CT_XFI_DMA_MASK		DMA_BIT_MASK(64) /* 64 bit PTE */
+#endif
+
+struct hw20k2 {
+	struct hw hw;
+	/* for i2c */
+	unsigned char dev_id;
+	unsigned char addr_size;
+	unsigned char data_size;
+};
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg);
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data);
+
+/*
+ * Type definition block.
+ * The layout of control structures can be directly applied on 20k2 chip.
+ */
+
+/*
+ * SRC control block definitions.
+ */
+
+/* SRC resource control block */
+#define SRCCTL_STATE	0x00000007
+#define SRCCTL_BM	0x00000008
+#define SRCCTL_RSR	0x00000030
+#define SRCCTL_SF	0x000001C0
+#define SRCCTL_WR	0x00000200
+#define SRCCTL_PM	0x00000400
+#define SRCCTL_ROM	0x00001800
+#define SRCCTL_VO	0x00002000
+#define SRCCTL_ST	0x00004000
+#define SRCCTL_IE	0x00008000
+#define SRCCTL_ILSZ	0x000F0000
+#define SRCCTL_BP	0x00100000
+
+#define SRCCCR_CISZ	0x000007FF
+#define SRCCCR_CWA	0x001FF800
+#define SRCCCR_D	0x00200000
+#define SRCCCR_RS	0x01C00000
+#define SRCCCR_NAL	0x3E000000
+#define SRCCCR_RA	0xC0000000
+
+#define SRCCA_CA	0x0FFFFFFF
+#define SRCCA_RS	0xE0000000
+
+#define SRCSA_SA	0x0FFFFFFF
+
+#define SRCLA_LA	0x0FFFFFFF
+
+/* Mixer Parameter Ring ram Low and Hight register.
+ * Fixed-point value in 8.24 format for parameter channel */
+#define MPRLH_PITCH	0xFFFFFFFF
+
+/* SRC resource register dirty flags */
+union src_dirty {
+	struct {
+		u16 ctl:1;
+		u16 ccr:1;
+		u16 sa:1;
+		u16 la:1;
+		u16 ca:1;
+		u16 mpr:1;
+		u16 czbfs:1;	/* Clear Z-Buffers */
+		u16 rsv:9;
+	} bf;
+	u16 data;
+};
+
+struct src_rsc_ctrl_blk {
+	unsigned int	ctl;
+	unsigned int 	ccr;
+	unsigned int	ca;
+	unsigned int	sa;
+	unsigned int	la;
+	unsigned int	mpr;
+	union src_dirty	dirty;
+};
+
+/* SRC manager control block */
+union src_mgr_dirty {
+	struct {
+		u16 enb0:1;
+		u16 enb1:1;
+		u16 enb2:1;
+		u16 enb3:1;
+		u16 enb4:1;
+		u16 enb5:1;
+		u16 enb6:1;
+		u16 enb7:1;
+		u16 enbsa:1;
+		u16 rsv:7;
+	} bf;
+	u16 data;
+};
+
+struct src_mgr_ctrl_blk {
+	unsigned int		enbsa;
+	unsigned int		enb[8];
+	union src_mgr_dirty	dirty;
+};
+
+/* SRCIMP manager control block */
+#define SRCAIM_ARC	0x00000FFF
+#define SRCAIM_NXT	0x00FF0000
+#define SRCAIM_SRC	0xFF000000
+
+struct srcimap {
+	unsigned int srcaim;
+	unsigned int idx;
+};
+
+/* SRCIMP manager register dirty flags */
+union srcimp_mgr_dirty {
+	struct {
+		u16 srcimap:1;
+		u16 rsv:15;
+	} bf;
+	u16 data;
+};
+
+struct srcimp_mgr_ctrl_blk {
+	struct srcimap		srcimap;
+	union srcimp_mgr_dirty	dirty;
+};
+
+/*
+ * Function implementation block.
+ */
+
+static int src_get_rsc_ctrl_blk(void **rblk)
+{
+	struct src_rsc_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int src_put_rsc_ctrl_blk(void *blk)
+{
+	kfree(blk);
+
+	return 0;
+}
+
+static int src_set_state(void *blk, unsigned int state)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_STATE, state);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_bm(void *blk, unsigned int bm)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_BM, bm);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_rsr(void *blk, unsigned int rsr)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_RSR, rsr);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_sf(void *blk, unsigned int sf)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_SF, sf);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_wr(void *blk, unsigned int wr)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_WR, wr);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_pm(void *blk, unsigned int pm)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_PM, pm);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_rom(void *blk, unsigned int rom)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_ROM, rom);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_vo(void *blk, unsigned int vo)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_VO, vo);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_st(void *blk, unsigned int st)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_ST, st);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_ie(void *blk, unsigned int ie)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_IE, ie);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_ilsz(void *blk, unsigned int ilsz)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_ILSZ, ilsz);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_bp(void *blk, unsigned int bp)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ctl, SRCCTL_BP, bp);
+	ctl->dirty.bf.ctl = 1;
+	return 0;
+}
+
+static int src_set_cisz(void *blk, unsigned int cisz)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ccr, SRCCCR_CISZ, cisz);
+	ctl->dirty.bf.ccr = 1;
+	return 0;
+}
+
+static int src_set_ca(void *blk, unsigned int ca)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->ca, SRCCA_CA, ca);
+	ctl->dirty.bf.ca = 1;
+	return 0;
+}
+
+static int src_set_sa(void *blk, unsigned int sa)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->sa, SRCSA_SA, sa);
+	ctl->dirty.bf.sa = 1;
+	return 0;
+}
+
+static int src_set_la(void *blk, unsigned int la)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->la, SRCLA_LA, la);
+	ctl->dirty.bf.la = 1;
+	return 0;
+}
+
+static int src_set_pitch(void *blk, unsigned int pitch)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->mpr, MPRLH_PITCH, pitch);
+	ctl->dirty.bf.mpr = 1;
+	return 0;
+}
+
+static int src_set_clear_zbufs(void *blk, unsigned int clear)
+{
+	((struct src_rsc_ctrl_blk *)blk)->dirty.bf.czbfs = (clear ? 1 : 0);
+	return 0;
+}
+
+static int src_set_dirty(void *blk, unsigned int flags)
+{
+	((struct src_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+	return 0;
+}
+
+static int src_set_dirty_all(void *blk)
+{
+	((struct src_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+	return 0;
+}
+
+#define AR_SLOT_SIZE		4096
+#define AR_SLOT_BLOCK_SIZE	16
+#define AR_PTS_PITCH		6
+#define AR_PARAM_SRC_OFFSET	0x60
+
+static unsigned int src_param_pitch_mixer(unsigned int src_idx)
+{
+	return ((src_idx << 4) + AR_PTS_PITCH + AR_SLOT_SIZE
+			- AR_PARAM_SRC_OFFSET) % AR_SLOT_SIZE;
+
+}
+
+static int src_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+	int i;
+
+	if (ctl->dirty.bf.czbfs) {
+		/* Clear Z-Buffer registers */
+		for (i = 0; i < 8; i++)
+			hw_write_20kx(hw, SRC_UPZ+idx*0x100+i*0x4, 0);
+
+		for (i = 0; i < 4; i++)
+			hw_write_20kx(hw, SRC_DN0Z+idx*0x100+i*0x4, 0);
+
+		for (i = 0; i < 8; i++)
+			hw_write_20kx(hw, SRC_DN1Z+idx*0x100+i*0x4, 0);
+
+		ctl->dirty.bf.czbfs = 0;
+	}
+	if (ctl->dirty.bf.mpr) {
+		/* Take the parameter mixer resource in the same group as that
+		 * the idx src is in for simplicity. Unlike src, all conjugate
+		 * parameter mixer resources must be programmed for
+		 * corresponding conjugate src resources. */
+		unsigned int pm_idx = src_param_pitch_mixer(idx);
+		hw_write_20kx(hw, MIXER_PRING_LO_HI+4*pm_idx, ctl->mpr);
+		hw_write_20kx(hw, MIXER_PMOPLO+8*pm_idx, 0x3);
+		hw_write_20kx(hw, MIXER_PMOPHI+8*pm_idx, 0x0);
+		ctl->dirty.bf.mpr = 0;
+	}
+	if (ctl->dirty.bf.sa) {
+		hw_write_20kx(hw, SRC_SA+idx*0x100, ctl->sa);
+		ctl->dirty.bf.sa = 0;
+	}
+	if (ctl->dirty.bf.la) {
+		hw_write_20kx(hw, SRC_LA+idx*0x100, ctl->la);
+		ctl->dirty.bf.la = 0;
+	}
+	if (ctl->dirty.bf.ca) {
+		hw_write_20kx(hw, SRC_CA+idx*0x100, ctl->ca);
+		ctl->dirty.bf.ca = 0;
+	}
+
+	/* Write srccf register */
+	hw_write_20kx(hw, SRC_CF+idx*0x100, 0x0);
+
+	if (ctl->dirty.bf.ccr) {
+		hw_write_20kx(hw, SRC_CCR+idx*0x100, ctl->ccr);
+		ctl->dirty.bf.ccr = 0;
+	}
+	if (ctl->dirty.bf.ctl) {
+		hw_write_20kx(hw, SRC_CTL+idx*0x100, ctl->ctl);
+		ctl->dirty.bf.ctl = 0;
+	}
+
+	return 0;
+}
+
+static int src_get_ca(struct hw *hw, unsigned int idx, void *blk)
+{
+	struct src_rsc_ctrl_blk *ctl = blk;
+
+	ctl->ca = hw_read_20kx(hw, SRC_CA+idx*0x100);
+	ctl->dirty.bf.ca = 0;
+
+	return get_field(ctl->ca, SRCCA_CA);
+}
+
+static unsigned int src_get_dirty(void *blk)
+{
+	return ((struct src_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static unsigned int src_dirty_conj_mask(void)
+{
+	return 0x20;
+}
+
+static int src_mgr_enbs_src(void *blk, unsigned int idx)
+{
+	((struct src_mgr_ctrl_blk *)blk)->enbsa |= (0x1 << ((idx%128)/4));
+	((struct src_mgr_ctrl_blk *)blk)->dirty.bf.enbsa = 1;
+	((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+	return 0;
+}
+
+static int src_mgr_enb_src(void *blk, unsigned int idx)
+{
+	((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] |= (0x1 << (idx%32));
+	((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+	return 0;
+}
+
+static int src_mgr_dsb_src(void *blk, unsigned int idx)
+{
+	((struct src_mgr_ctrl_blk *)blk)->enb[idx/32] &= ~(0x1 << (idx%32));
+	((struct src_mgr_ctrl_blk *)blk)->dirty.data |= (0x1 << (idx/32));
+	return 0;
+}
+
+static int src_mgr_commit_write(struct hw *hw, void *blk)
+{
+	struct src_mgr_ctrl_blk *ctl = blk;
+	int i;
+	unsigned int ret;
+
+	if (ctl->dirty.bf.enbsa) {
+		do {
+			ret = hw_read_20kx(hw, SRC_ENBSTAT);
+		} while (ret & 0x1);
+		hw_write_20kx(hw, SRC_ENBSA, ctl->enbsa);
+		ctl->dirty.bf.enbsa = 0;
+	}
+	for (i = 0; i < 8; i++) {
+		if ((ctl->dirty.data & (0x1 << i))) {
+			hw_write_20kx(hw, SRC_ENB+(i*0x100), ctl->enb[i]);
+			ctl->dirty.data &= ~(0x1 << i);
+		}
+	}
+
+	return 0;
+}
+
+static int src_mgr_get_ctrl_blk(void **rblk)
+{
+	struct src_mgr_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int src_mgr_put_ctrl_blk(void *blk)
+{
+	kfree(blk);
+
+	return 0;
+}
+
+static int srcimp_mgr_get_ctrl_blk(void **rblk)
+{
+	struct srcimp_mgr_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int srcimp_mgr_put_ctrl_blk(void *blk)
+{
+	kfree(blk);
+
+	return 0;
+}
+
+static int srcimp_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+	struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srcimap.srcaim, SRCAIM_ARC, slot);
+	ctl->dirty.bf.srcimap = 1;
+	return 0;
+}
+
+static int srcimp_mgr_set_imapuser(void *blk, unsigned int user)
+{
+	struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srcimap.srcaim, SRCAIM_SRC, user);
+	ctl->dirty.bf.srcimap = 1;
+	return 0;
+}
+
+static int srcimp_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+	struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srcimap.srcaim, SRCAIM_NXT, next);
+	ctl->dirty.bf.srcimap = 1;
+	return 0;
+}
+
+static int srcimp_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+	((struct srcimp_mgr_ctrl_blk *)blk)->srcimap.idx = addr;
+	((struct srcimp_mgr_ctrl_blk *)blk)->dirty.bf.srcimap = 1;
+	return 0;
+}
+
+static int srcimp_mgr_commit_write(struct hw *hw, void *blk)
+{
+	struct srcimp_mgr_ctrl_blk *ctl = blk;
+
+	if (ctl->dirty.bf.srcimap) {
+		hw_write_20kx(hw, SRC_IMAP+ctl->srcimap.idx*0x100,
+						ctl->srcimap.srcaim);
+		ctl->dirty.bf.srcimap = 0;
+	}
+
+	return 0;
+}
+
+/*
+ * AMIXER control block definitions.
+ */
+
+#define AMOPLO_M	0x00000003
+#define AMOPLO_IV	0x00000004
+#define AMOPLO_X	0x0003FFF0
+#define AMOPLO_Y	0xFFFC0000
+
+#define AMOPHI_SADR	0x000000FF
+#define AMOPHI_SE	0x80000000
+
+/* AMIXER resource register dirty flags */
+union amixer_dirty {
+	struct {
+		u16 amoplo:1;
+		u16 amophi:1;
+		u16 rsv:14;
+	} bf;
+	u16 data;
+};
+
+/* AMIXER resource control block */
+struct amixer_rsc_ctrl_blk {
+	unsigned int		amoplo;
+	unsigned int		amophi;
+	union amixer_dirty	dirty;
+};
+
+static int amixer_set_mode(void *blk, unsigned int mode)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->amoplo, AMOPLO_M, mode);
+	ctl->dirty.bf.amoplo = 1;
+	return 0;
+}
+
+static int amixer_set_iv(void *blk, unsigned int iv)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->amoplo, AMOPLO_IV, iv);
+	ctl->dirty.bf.amoplo = 1;
+	return 0;
+}
+
+static int amixer_set_x(void *blk, unsigned int x)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->amoplo, AMOPLO_X, x);
+	ctl->dirty.bf.amoplo = 1;
+	return 0;
+}
+
+static int amixer_set_y(void *blk, unsigned int y)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->amoplo, AMOPLO_Y, y);
+	ctl->dirty.bf.amoplo = 1;
+	return 0;
+}
+
+static int amixer_set_sadr(void *blk, unsigned int sadr)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->amophi, AMOPHI_SADR, sadr);
+	ctl->dirty.bf.amophi = 1;
+	return 0;
+}
+
+static int amixer_set_se(void *blk, unsigned int se)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->amophi, AMOPHI_SE, se);
+	ctl->dirty.bf.amophi = 1;
+	return 0;
+}
+
+static int amixer_set_dirty(void *blk, unsigned int flags)
+{
+	((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = (flags & 0xffff);
+	return 0;
+}
+
+static int amixer_set_dirty_all(void *blk)
+{
+	((struct amixer_rsc_ctrl_blk *)blk)->dirty.data = ~(0x0);
+	return 0;
+}
+
+static int amixer_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	if (ctl->dirty.bf.amoplo || ctl->dirty.bf.amophi) {
+		hw_write_20kx(hw, MIXER_AMOPLO+idx*8, ctl->amoplo);
+		ctl->dirty.bf.amoplo = 0;
+		hw_write_20kx(hw, MIXER_AMOPHI+idx*8, ctl->amophi);
+		ctl->dirty.bf.amophi = 0;
+	}
+
+	return 0;
+}
+
+static int amixer_get_y(void *blk)
+{
+	struct amixer_rsc_ctrl_blk *ctl = blk;
+
+	return get_field(ctl->amoplo, AMOPLO_Y);
+}
+
+static unsigned int amixer_get_dirty(void *blk)
+{
+	return ((struct amixer_rsc_ctrl_blk *)blk)->dirty.data;
+}
+
+static int amixer_rsc_get_ctrl_blk(void **rblk)
+{
+	struct amixer_rsc_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int amixer_rsc_put_ctrl_blk(void *blk)
+{
+	kfree(blk);
+
+	return 0;
+}
+
+static int amixer_mgr_get_ctrl_blk(void **rblk)
+{
+	*rblk = NULL;
+
+	return 0;
+}
+
+static int amixer_mgr_put_ctrl_blk(void *blk)
+{
+	return 0;
+}
+
+/*
+ * DAIO control block definitions.
+ */
+
+/* Receiver Sample Rate Tracker Control register */
+#define SRTCTL_SRCO	0x000000FF
+#define SRTCTL_SRCM	0x0000FF00
+#define SRTCTL_RSR	0x00030000
+#define SRTCTL_DRAT	0x00300000
+#define SRTCTL_EC	0x01000000
+#define SRTCTL_ET	0x10000000
+
+/* DAIO Receiver register dirty flags */
+union dai_dirty {
+	struct {
+		u16 srt:1;
+		u16 rsv:15;
+	} bf;
+	u16 data;
+};
+
+/* DAIO Receiver control block */
+struct dai_ctrl_blk {
+	unsigned int	srt;
+	union dai_dirty	dirty;
+};
+
+/* Audio Input Mapper RAM */
+#define AIM_ARC		0x00000FFF
+#define AIM_NXT		0x007F0000
+
+struct daoimap {
+	unsigned int aim;
+	unsigned int idx;
+};
+
+/* Audio Transmitter Control and Status register */
+#define ATXCTL_EN	0x00000001
+#define ATXCTL_MODE	0x00000010
+#define ATXCTL_CD	0x00000020
+#define ATXCTL_RAW	0x00000100
+#define ATXCTL_MT	0x00000200
+#define ATXCTL_NUC	0x00003000
+#define ATXCTL_BEN	0x00010000
+#define ATXCTL_BMUX	0x00700000
+#define ATXCTL_B24	0x01000000
+#define ATXCTL_CPF	0x02000000
+#define ATXCTL_RIV	0x10000000
+#define ATXCTL_LIV	0x20000000
+#define ATXCTL_RSAT	0x40000000
+#define ATXCTL_LSAT	0x80000000
+
+/* XDIF Transmitter register dirty flags */
+union dao_dirty {
+	struct {
+		u16 atxcsl:1;
+		u16 rsv:15;
+	} bf;
+	u16 data;
+};
+
+/* XDIF Transmitter control block */
+struct dao_ctrl_blk {
+	/* XDIF Transmitter Channel Status Low Register */
+	unsigned int	atxcsl;
+	union dao_dirty	dirty;
+};
+
+/* Audio Receiver Control register */
+#define ARXCTL_EN	0x00000001
+
+/* DAIO manager register dirty flags */
+union daio_mgr_dirty {
+	struct {
+		u32 atxctl:8;
+		u32 arxctl:8;
+		u32 daoimap:1;
+		u32 rsv:15;
+	} bf;
+	u32 data;
+};
+
+/* DAIO manager control block */
+struct daio_mgr_ctrl_blk {
+	struct daoimap		daoimap;
+	unsigned int		txctl[8];
+	unsigned int		rxctl[8];
+	union daio_mgr_dirty	dirty;
+};
+
+static int dai_srt_set_srco(void *blk, unsigned int src)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srt, SRTCTL_SRCO, src);
+	ctl->dirty.bf.srt = 1;
+	return 0;
+}
+
+static int dai_srt_set_srcm(void *blk, unsigned int src)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srt, SRTCTL_SRCM, src);
+	ctl->dirty.bf.srt = 1;
+	return 0;
+}
+
+static int dai_srt_set_rsr(void *blk, unsigned int rsr)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srt, SRTCTL_RSR, rsr);
+	ctl->dirty.bf.srt = 1;
+	return 0;
+}
+
+static int dai_srt_set_drat(void *blk, unsigned int drat)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srt, SRTCTL_DRAT, drat);
+	ctl->dirty.bf.srt = 1;
+	return 0;
+}
+
+static int dai_srt_set_ec(void *blk, unsigned int ec)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srt, SRTCTL_EC, ec ? 1 : 0);
+	ctl->dirty.bf.srt = 1;
+	return 0;
+}
+
+static int dai_srt_set_et(void *blk, unsigned int et)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->srt, SRTCTL_ET, et ? 1 : 0);
+	ctl->dirty.bf.srt = 1;
+	return 0;
+}
+
+static int dai_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+	struct dai_ctrl_blk *ctl = blk;
+
+	if (ctl->dirty.bf.srt) {
+		hw_write_20kx(hw, AUDIO_IO_RX_SRT_CTL+0x40*idx, ctl->srt);
+		ctl->dirty.bf.srt = 0;
+	}
+
+	return 0;
+}
+
+static int dai_get_ctrl_blk(void **rblk)
+{
+	struct dai_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int dai_put_ctrl_blk(void *blk)
+{
+	kfree(blk);
+
+	return 0;
+}
+
+static int dao_set_spos(void *blk, unsigned int spos)
+{
+	((struct dao_ctrl_blk *)blk)->atxcsl = spos;
+	((struct dao_ctrl_blk *)blk)->dirty.bf.atxcsl = 1;
+	return 0;
+}
+
+static int dao_commit_write(struct hw *hw, unsigned int idx, void *blk)
+{
+	struct dao_ctrl_blk *ctl = blk;
+
+	if (ctl->dirty.bf.atxcsl) {
+		if (idx < 4) {
+			/* S/PDIF SPOSx */
+			hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+0x40*idx,
+							ctl->atxcsl);
+		}
+		ctl->dirty.bf.atxcsl = 0;
+	}
+
+	return 0;
+}
+
+static int dao_get_spos(void *blk, unsigned int *spos)
+{
+	*spos = ((struct dao_ctrl_blk *)blk)->atxcsl;
+	return 0;
+}
+
+static int dao_get_ctrl_blk(void **rblk)
+{
+	struct dao_ctrl_blk *blk;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int dao_put_ctrl_blk(void *blk)
+{
+	kfree(blk);
+
+	return 0;
+}
+
+static int daio_mgr_enb_dai(void *blk, unsigned int idx)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->rxctl[idx], ARXCTL_EN, 1);
+	ctl->dirty.bf.arxctl |= (0x1 << idx);
+	return 0;
+}
+
+static int daio_mgr_dsb_dai(void *blk, unsigned int idx)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->rxctl[idx], ARXCTL_EN, 0);
+
+	ctl->dirty.bf.arxctl |= (0x1 << idx);
+	return 0;
+}
+
+static int daio_mgr_enb_dao(void *blk, unsigned int idx)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->txctl[idx], ATXCTL_EN, 1);
+	ctl->dirty.bf.atxctl |= (0x1 << idx);
+	return 0;
+}
+
+static int daio_mgr_dsb_dao(void *blk, unsigned int idx)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->txctl[idx], ATXCTL_EN, 0);
+	ctl->dirty.bf.atxctl |= (0x1 << idx);
+	return 0;
+}
+
+static int daio_mgr_dao_init(void *blk, unsigned int idx, unsigned int conf)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	if (idx < 4) {
+		/* S/PDIF output */
+		switch ((conf & 0x7)) {
+		case 1:
+			set_field(&ctl->txctl[idx], ATXCTL_NUC, 0);
+			break;
+		case 2:
+			set_field(&ctl->txctl[idx], ATXCTL_NUC, 1);
+			break;
+		case 4:
+			set_field(&ctl->txctl[idx], ATXCTL_NUC, 2);
+			break;
+		case 8:
+			set_field(&ctl->txctl[idx], ATXCTL_NUC, 3);
+			break;
+		default:
+			break;
+		}
+		/* CDIF */
+		set_field(&ctl->txctl[idx], ATXCTL_CD, (!(conf & 0x7)));
+		/* Non-audio */
+		set_field(&ctl->txctl[idx], ATXCTL_LIV, (conf >> 4) & 0x1);
+		/* Non-audio */
+		set_field(&ctl->txctl[idx], ATXCTL_RIV, (conf >> 4) & 0x1);
+		set_field(&ctl->txctl[idx], ATXCTL_RAW,
+			  ((conf >> 3) & 0x1) ? 0 : 0);
+		ctl->dirty.bf.atxctl |= (0x1 << idx);
+	} else {
+		/* I2S output */
+		/*idx %= 4; */
+	}
+	return 0;
+}
+
+static int daio_mgr_set_imaparc(void *blk, unsigned int slot)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->daoimap.aim, AIM_ARC, slot);
+	ctl->dirty.bf.daoimap = 1;
+	return 0;
+}
+
+static int daio_mgr_set_imapnxt(void *blk, unsigned int next)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+
+	set_field(&ctl->daoimap.aim, AIM_NXT, next);
+	ctl->dirty.bf.daoimap = 1;
+	return 0;
+}
+
+static int daio_mgr_set_imapaddr(void *blk, unsigned int addr)
+{
+	((struct daio_mgr_ctrl_blk *)blk)->daoimap.idx = addr;
+	((struct daio_mgr_ctrl_blk *)blk)->dirty.bf.daoimap = 1;
+	return 0;
+}
+
+static int daio_mgr_commit_write(struct hw *hw, void *blk)
+{
+	struct daio_mgr_ctrl_blk *ctl = blk;
+	unsigned int data;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		if ((ctl->dirty.bf.atxctl & (0x1 << i))) {
+			data = ctl->txctl[i];
+			hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data);
+			ctl->dirty.bf.atxctl &= ~(0x1 << i);
+			mdelay(1);
+		}
+		if ((ctl->dirty.bf.arxctl & (0x1 << i))) {
+			data = ctl->rxctl[i];
+			hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data);
+			ctl->dirty.bf.arxctl &= ~(0x1 << i);
+			mdelay(1);
+		}
+	}
+	if (ctl->dirty.bf.daoimap) {
+		hw_write_20kx(hw, AUDIO_IO_AIM+ctl->daoimap.idx*4,
+						ctl->daoimap.aim);
+		ctl->dirty.bf.daoimap = 0;
+	}
+
+	return 0;
+}
+
+static int daio_mgr_get_ctrl_blk(struct hw *hw, void **rblk)
+{
+	struct daio_mgr_ctrl_blk *blk;
+	int i;
+
+	*rblk = NULL;
+	blk = kzalloc(sizeof(*blk), GFP_KERNEL);
+	if (NULL == blk)
+		return -ENOMEM;
+
+	for (i = 0; i < 8; i++) {
+		blk->txctl[i] = hw_read_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i));
+		blk->rxctl[i] = hw_read_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i));
+	}
+
+	*rblk = blk;
+
+	return 0;
+}
+
+static int daio_mgr_put_ctrl_blk(void *blk)
+{
+	kfree(blk);
+
+	return 0;
+}
+
+/* Card hardware initialization block */
+struct dac_conf {
+	unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct adc_conf {
+	unsigned int msr; 	/* master sample rate in rsrs */
+	unsigned char input; 	/* the input source of ADC */
+	unsigned char mic20db; 	/* boost mic by 20db if input is microphone */
+};
+
+struct daio_conf {
+	unsigned int msr; /* master sample rate in rsrs */
+};
+
+struct trn_conf {
+	unsigned long vm_pgt_phys;
+};
+
+static int hw_daio_init(struct hw *hw, const struct daio_conf *info)
+{
+	u32 data;
+	int i;
+
+	/* Program I2S with proper sample rate and enable the correct I2S
+	 * channel. ED(0/8/16/24): Enable all I2S/I2X master clock output */
+	if (1 == info->msr) {
+		hw_write_20kx(hw, AUDIO_IO_MCLK, 0x01010101);
+		hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x01010101);
+		hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
+	} else if (2 == info->msr) {
+		hw_write_20kx(hw, AUDIO_IO_MCLK, 0x11111111);
+		/* Specify all playing 96khz
+		 * EA [0]	- Enabled
+		 * RTA [4:5]	- 96kHz
+		 * EB [8]	- Enabled
+		 * RTB [12:13]	- 96kHz
+		 * EC [16]	- Enabled
+		 * RTC [20:21]	- 96kHz
+		 * ED [24]	- Enabled
+		 * RTD [28:29]	- 96kHz */
+		hw_write_20kx(hw, AUDIO_IO_TX_BLRCLK, 0x11111111);
+		hw_write_20kx(hw, AUDIO_IO_RX_BLRCLK, 0);
+	} else {
+		printk(KERN_ALERT "ctxfi: ERROR!!! Invalid sampling rate!!!\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < 8; i++) {
+		if (i <= 3) {
+			/* 1st 3 channels are SPDIFs (SB0960) */
+			if (i == 3)
+				data = 0x1001001;
+			else
+				data = 0x1000001;
+
+			hw_write_20kx(hw, (AUDIO_IO_TX_CTL+(0x40*i)), data);
+			hw_write_20kx(hw, (AUDIO_IO_RX_CTL+(0x40*i)), data);
+
+			/* Initialize the SPDIF Out Channel status registers.
+			 * The value specified here is based on the typical
+			 * values provided in the specification, namely: Clock
+			 * Accuracy of 1000ppm, Sample Rate of 48KHz,
+			 * unspecified source number, Generation status = 1,
+			 * Category code = 0x12 (Digital Signal Mixer),
+			 * Mode = 0, Emph = 0, Copy Permitted, AN = 0
+			 * (indicating that we're transmitting digital audio,
+			 * and the Professional Use bit is 0. */
+
+			hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_L+(0x40*i),
+					0x02109204); /* Default to 48kHz */
+
+			hw_write_20kx(hw, AUDIO_IO_TX_CSTAT_H+(0x40*i), 0x0B);
+		} else {
+			/* Next 5 channels are I2S (SB0960) */
+			data = 0x11;
+			hw_write_20kx(hw, AUDIO_IO_RX_CTL+(0x40*i), data);
+			if (2 == info->msr) {
+				/* Four channels per sample period */
+				data |= 0x1000;
+			}
+			hw_write_20kx(hw, AUDIO_IO_TX_CTL+(0x40*i), data);
+		}
+	}
+
+	return 0;
+}
+
+/* TRANSPORT operations */
+static int hw_trn_init(struct hw *hw, const struct trn_conf *info)
+{
+	u32 vmctl, data;
+	u32 ptp_phys_low, ptp_phys_high;
+	int i;
+
+	/* Set up device page table */
+	if ((~0UL) == info->vm_pgt_phys) {
+		printk(KERN_ALERT "ctxfi: "
+		       "Wrong device page table page address!!!\n");
+		return -1;
+	}
+
+	vmctl = 0x80000C0F;  /* 32-bit, 4k-size page */
+	ptp_phys_low = (u32)info->vm_pgt_phys;
+	ptp_phys_high = upper_32_bits(info->vm_pgt_phys);
+	if (sizeof(void *) == 8) /* 64bit address */
+		vmctl |= (3 << 8);
+	/* Write page table physical address to all PTPAL registers */
+	for (i = 0; i < 64; i++) {
+		hw_write_20kx(hw, VMEM_PTPAL+(16*i), ptp_phys_low);
+		hw_write_20kx(hw, VMEM_PTPAH+(16*i), ptp_phys_high);
+	}
+	/* Enable virtual memory transfer */
+	hw_write_20kx(hw, VMEM_CTL, vmctl);
+	/* Enable transport bus master and queueing of request */
+	hw_write_20kx(hw, TRANSPORT_CTL, 0x03);
+	hw_write_20kx(hw, TRANSPORT_INT, 0x200c01);
+	/* Enable transport ring */
+	data = hw_read_20kx(hw, TRANSPORT_ENB);
+	hw_write_20kx(hw, TRANSPORT_ENB, (data | 0x03));
+
+	return 0;
+}
+
+/* Card initialization */
+#define GCTL_AIE	0x00000001
+#define GCTL_UAA	0x00000002
+#define GCTL_DPC	0x00000004
+#define GCTL_DBP	0x00000008
+#define GCTL_ABP	0x00000010
+#define GCTL_TBP	0x00000020
+#define GCTL_SBP	0x00000040
+#define GCTL_FBP	0x00000080
+#define GCTL_ME		0x00000100
+#define GCTL_AID	0x00001000
+
+#define PLLCTL_SRC	0x00000007
+#define PLLCTL_SPE	0x00000008
+#define PLLCTL_RD	0x000000F0
+#define PLLCTL_FD	0x0001FF00
+#define PLLCTL_OD	0x00060000
+#define PLLCTL_B	0x00080000
+#define PLLCTL_AS	0x00100000
+#define PLLCTL_LF	0x03E00000
+#define PLLCTL_SPS	0x1C000000
+#define PLLCTL_AD	0x60000000
+
+#define PLLSTAT_CCS	0x00000007
+#define PLLSTAT_SPL	0x00000008
+#define PLLSTAT_CRD	0x000000F0
+#define PLLSTAT_CFD	0x0001FF00
+#define PLLSTAT_SL	0x00020000
+#define PLLSTAT_FAS	0x00040000
+#define PLLSTAT_B	0x00080000
+#define PLLSTAT_PD	0x00100000
+#define PLLSTAT_OCA	0x00200000
+#define PLLSTAT_NCA	0x00400000
+
+static int hw_pll_init(struct hw *hw, unsigned int rsr)
+{
+	unsigned int pllenb;
+	unsigned int pllctl;
+	unsigned int pllstat;
+	int i;
+
+	pllenb = 0xB;
+	hw_write_20kx(hw, PLL_ENB, pllenb);
+	pllctl = 0x20D00000;
+	set_field(&pllctl, PLLCTL_FD, 16 - 4);
+	hw_write_20kx(hw, PLL_CTL, pllctl);
+	mdelay(40);
+	pllctl = hw_read_20kx(hw, PLL_CTL);
+	set_field(&pllctl, PLLCTL_B, 0);
+	if (48000 == rsr) {
+		set_field(&pllctl, PLLCTL_FD, 16 - 2);
+		set_field(&pllctl, PLLCTL_RD, 1 - 1);
+	} else { /* 44100 */
+		set_field(&pllctl, PLLCTL_FD, 147 - 2);
+		set_field(&pllctl, PLLCTL_RD, 10 - 1);
+	}
+	hw_write_20kx(hw, PLL_CTL, pllctl);
+	mdelay(40);
+	for (i = 0; i < 1000; i++) {
+		pllstat = hw_read_20kx(hw, PLL_STAT);
+		if (get_field(pllstat, PLLSTAT_PD))
+			continue;
+
+		if (get_field(pllstat, PLLSTAT_B) !=
+					get_field(pllctl, PLLCTL_B))
+			continue;
+
+		if (get_field(pllstat, PLLSTAT_CCS) !=
+					get_field(pllctl, PLLCTL_SRC))
+			continue;
+
+		if (get_field(pllstat, PLLSTAT_CRD) !=
+					get_field(pllctl, PLLCTL_RD))
+			continue;
+
+		if (get_field(pllstat, PLLSTAT_CFD) !=
+					get_field(pllctl, PLLCTL_FD))
+			continue;
+
+		break;
+	}
+	if (i >= 1000) {
+		printk(KERN_ALERT "ctxfi: PLL initialization failed!!!\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int hw_auto_init(struct hw *hw)
+{
+	unsigned int gctl;
+	int i;
+
+	gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+	set_field(&gctl, GCTL_AIE, 0);
+	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+	set_field(&gctl, GCTL_AIE, 1);
+	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+	mdelay(10);
+	for (i = 0; i < 400000; i++) {
+		gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+		if (get_field(gctl, GCTL_AID))
+			break;
+	}
+	if (!get_field(gctl, GCTL_AID)) {
+		printk(KERN_ALERT "ctxfi: Card Auto-init failed!!!\n");
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+/* DAC operations */
+
+#define CS4382_MC1 		0x1
+#define CS4382_MC2 		0x2
+#define CS4382_MC3		0x3
+#define CS4382_FC		0x4
+#define CS4382_IC		0x5
+#define CS4382_XC1		0x6
+#define CS4382_VCA1 		0x7
+#define CS4382_VCB1 		0x8
+#define CS4382_XC2		0x9
+#define CS4382_VCA2 		0xA
+#define CS4382_VCB2 		0xB
+#define CS4382_XC3		0xC
+#define CS4382_VCA3		0xD
+#define CS4382_VCB3		0xE
+#define CS4382_XC4 		0xF
+#define CS4382_VCA4 		0x10
+#define CS4382_VCB4 		0x11
+#define CS4382_CREV 		0x12
+
+/* I2C status */
+#define STATE_LOCKED		0x00
+#define STATE_UNLOCKED		0xAA
+#define DATA_READY		0x800000    /* Used with I2C_IF_STATUS */
+#define DATA_ABORT		0x10000     /* Used with I2C_IF_STATUS */
+
+#define I2C_STATUS_DCM	0x00000001
+#define I2C_STATUS_BC	0x00000006
+#define I2C_STATUS_APD	0x00000008
+#define I2C_STATUS_AB	0x00010000
+#define I2C_STATUS_DR	0x00800000
+
+#define I2C_ADDRESS_PTAD	0x0000FFFF
+#define I2C_ADDRESS_SLAD	0x007F0000
+
+struct regs_cs4382 {
+	u32 mode_control_1;
+	u32 mode_control_2;
+	u32 mode_control_3;
+
+	u32 filter_control;
+	u32 invert_control;
+
+	u32 mix_control_P1;
+	u32 vol_control_A1;
+	u32 vol_control_B1;
+
+	u32 mix_control_P2;
+	u32 vol_control_A2;
+	u32 vol_control_B2;
+
+	u32 mix_control_P3;
+	u32 vol_control_A3;
+	u32 vol_control_B3;
+
+	u32 mix_control_P4;
+	u32 vol_control_A4;
+	u32 vol_control_B4;
+};
+
+static int hw20k2_i2c_unlock_full_access(struct hw *hw)
+{
+	u8 UnlockKeySequence_FLASH_FULLACCESS_MODE[2] =  {0xB3, 0xD4};
+
+	/* Send keys for forced BIOS mode */
+	hw_write_20kx(hw, I2C_IF_WLOCK,
+			UnlockKeySequence_FLASH_FULLACCESS_MODE[0]);
+	hw_write_20kx(hw, I2C_IF_WLOCK,
+			UnlockKeySequence_FLASH_FULLACCESS_MODE[1]);
+	/* Check whether the chip is unlocked */
+	if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_UNLOCKED)
+		return 0;
+
+	return -1;
+}
+
+static int hw20k2_i2c_lock_chip(struct hw *hw)
+{
+	/* Write twice */
+	hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED);
+	hw_write_20kx(hw, I2C_IF_WLOCK, STATE_LOCKED);
+	if (hw_read_20kx(hw, I2C_IF_WLOCK) == STATE_LOCKED)
+		return 0;
+
+	return -1;
+}
+
+static int hw20k2_i2c_init(struct hw *hw, u8 dev_id, u8 addr_size, u8 data_size)
+{
+	struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+	int err;
+	unsigned int i2c_status;
+	unsigned int i2c_addr;
+
+	err = hw20k2_i2c_unlock_full_access(hw);
+	if (err < 0)
+		return err;
+
+	hw20k2->addr_size = addr_size;
+	hw20k2->data_size = data_size;
+	hw20k2->dev_id = dev_id;
+
+	i2c_addr = 0;
+	set_field(&i2c_addr, I2C_ADDRESS_SLAD, dev_id);
+
+	hw_write_20kx(hw, I2C_IF_ADDRESS, i2c_addr);
+
+	i2c_status = hw_read_20kx(hw, I2C_IF_STATUS);
+
+	set_field(&i2c_status, I2C_STATUS_DCM, 1); /* Direct control mode */
+
+	hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
+
+	return 0;
+}
+
+static int hw20k2_i2c_uninit(struct hw *hw)
+{
+	unsigned int i2c_status;
+	unsigned int i2c_addr;
+
+	i2c_addr = 0;
+	set_field(&i2c_addr, I2C_ADDRESS_SLAD, 0x57); /* I2C id */
+
+	hw_write_20kx(hw, I2C_IF_ADDRESS, i2c_addr);
+
+	i2c_status = hw_read_20kx(hw, I2C_IF_STATUS);
+
+	set_field(&i2c_status, I2C_STATUS_DCM, 0); /* I2C mode */
+
+	hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
+
+	return hw20k2_i2c_lock_chip(hw);
+}
+
+static int hw20k2_i2c_wait_data_ready(struct hw *hw)
+{
+	int i = 0x400000;
+	unsigned int ret;
+
+	do {
+		ret = hw_read_20kx(hw, I2C_IF_STATUS);
+	} while ((!(ret & DATA_READY)) && --i);
+
+	return i;
+}
+
+static int hw20k2_i2c_read(struct hw *hw, u16 addr, u32 *datap)
+{
+	struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+	unsigned int i2c_status;
+
+	i2c_status = hw_read_20kx(hw, I2C_IF_STATUS);
+	set_field(&i2c_status, I2C_STATUS_BC,
+		  (4 == hw20k2->addr_size) ? 0 : hw20k2->addr_size);
+	hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
+	if (!hw20k2_i2c_wait_data_ready(hw))
+		return -1;
+
+	hw_write_20kx(hw, I2C_IF_WDATA, addr);
+	if (!hw20k2_i2c_wait_data_ready(hw))
+		return -1;
+
+	/* Force a read operation */
+	hw_write_20kx(hw, I2C_IF_RDATA, 0);
+	if (!hw20k2_i2c_wait_data_ready(hw))
+		return -1;
+
+	*datap = hw_read_20kx(hw, I2C_IF_RDATA);
+
+	return 0;
+}
+
+static int hw20k2_i2c_write(struct hw *hw, u16 addr, u32 data)
+{
+	struct hw20k2 *hw20k2 = (struct hw20k2 *)hw;
+	unsigned int i2c_data = (data << (hw20k2->addr_size * 8)) | addr;
+	unsigned int i2c_status;
+
+	i2c_status = hw_read_20kx(hw, I2C_IF_STATUS);
+
+	set_field(&i2c_status, I2C_STATUS_BC,
+		  (4 == (hw20k2->addr_size + hw20k2->data_size)) ?
+		  0 : (hw20k2->addr_size + hw20k2->data_size));
+
+	hw_write_20kx(hw, I2C_IF_STATUS, i2c_status);
+	hw20k2_i2c_wait_data_ready(hw);
+	/* Dummy write to trigger the write oprtation */
+	hw_write_20kx(hw, I2C_IF_WDATA, 0);
+	hw20k2_i2c_wait_data_ready(hw);
+
+	/* This is the real data */
+	hw_write_20kx(hw, I2C_IF_WDATA, i2c_data);
+	hw20k2_i2c_wait_data_ready(hw);
+
+	return 0;
+}
+
+static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
+{
+	int err;
+	u32 data;
+	int i;
+	struct regs_cs4382 cs_read = {0};
+	struct regs_cs4382 cs_def = {
+				   0x00000001,  /* Mode Control 1 */
+				   0x00000000,  /* Mode Control 2 */
+				   0x00000084,  /* Mode Control 3 */
+				   0x00000000,  /* Filter Control */
+				   0x00000000,  /* Invert Control */
+				   0x00000024,  /* Mixing Control Pair 1 */
+				   0x00000000,  /* Vol Control A1 */
+				   0x00000000,  /* Vol Control B1 */
+				   0x00000024,  /* Mixing Control Pair 2 */
+				   0x00000000,  /* Vol Control A2 */
+				   0x00000000,  /* Vol Control B2 */
+				   0x00000024,  /* Mixing Control Pair 3 */
+				   0x00000000,  /* Vol Control A3 */
+				   0x00000000,  /* Vol Control B3 */
+				   0x00000024,  /* Mixing Control Pair 4 */
+				   0x00000000,  /* Vol Control A4 */
+				   0x00000000   /* Vol Control B4 */
+				 };
+
+	/* Set DAC reset bit as output */
+	data = hw_read_20kx(hw, GPIO_CTRL);
+	data |= 0x02;
+	hw_write_20kx(hw, GPIO_CTRL, data);
+
+	err = hw20k2_i2c_init(hw, 0x18, 1, 1);
+	if (err < 0)
+		goto End;
+
+	for (i = 0; i < 2; i++) {
+		/* Reset DAC twice just in-case the chip
+		 * didn't initialized properly */
+		data = hw_read_20kx(hw, GPIO_DATA);
+		/* GPIO data bit 1 */
+		data &= 0xFFFFFFFD;
+		hw_write_20kx(hw, GPIO_DATA, data);
+		mdelay(10);
+		data |= 0x2;
+		hw_write_20kx(hw, GPIO_DATA, data);
+		mdelay(50);
+
+		/* Reset the 2nd time */
+		data &= 0xFFFFFFFD;
+		hw_write_20kx(hw, GPIO_DATA, data);
+		mdelay(10);
+		data |= 0x2;
+		hw_write_20kx(hw, GPIO_DATA, data);
+		mdelay(50);
+
+		if (hw20k2_i2c_read(hw, CS4382_MC1,  &cs_read.mode_control_1))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_MC2,  &cs_read.mode_control_2))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_MC3,  &cs_read.mode_control_3))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_FC,   &cs_read.filter_control))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_IC,   &cs_read.invert_control))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_XC1,  &cs_read.mix_control_P1))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_VCA1, &cs_read.vol_control_A1))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_VCB1, &cs_read.vol_control_B1))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_XC2,  &cs_read.mix_control_P2))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_VCA2, &cs_read.vol_control_A2))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_VCB2, &cs_read.vol_control_B2))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_XC3,  &cs_read.mix_control_P3))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_VCA3, &cs_read.vol_control_A3))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_VCB3, &cs_read.vol_control_B3))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_XC4,  &cs_read.mix_control_P4))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_VCA4, &cs_read.vol_control_A4))
+			continue;
+
+		if (hw20k2_i2c_read(hw, CS4382_VCB4, &cs_read.vol_control_B4))
+			continue;
+
+		if (memcmp(&cs_read, &cs_def, sizeof(cs_read)))
+			continue;
+		else
+			break;
+	}
+
+	if (i >= 2)
+		goto End;
+
+	/* Note: Every I2C write must have some delay.
+	 * This is not a requirement but the delay works here... */
+	hw20k2_i2c_write(hw, CS4382_MC1, 0x80);
+	hw20k2_i2c_write(hw, CS4382_MC2, 0x10);
+	if (1 == info->msr) {
+		hw20k2_i2c_write(hw, CS4382_XC1, 0x24);
+		hw20k2_i2c_write(hw, CS4382_XC2, 0x24);
+		hw20k2_i2c_write(hw, CS4382_XC3, 0x24);
+		hw20k2_i2c_write(hw, CS4382_XC4, 0x24);
+	} else if (2 == info->msr) {
+		hw20k2_i2c_write(hw, CS4382_XC1, 0x25);
+		hw20k2_i2c_write(hw, CS4382_XC2, 0x25);
+		hw20k2_i2c_write(hw, CS4382_XC3, 0x25);
+		hw20k2_i2c_write(hw, CS4382_XC4, 0x25);
+	} else {
+		hw20k2_i2c_write(hw, CS4382_XC1, 0x26);
+		hw20k2_i2c_write(hw, CS4382_XC2, 0x26);
+		hw20k2_i2c_write(hw, CS4382_XC3, 0x26);
+		hw20k2_i2c_write(hw, CS4382_XC4, 0x26);
+	}
+
+	return 0;
+End:
+
+	hw20k2_i2c_uninit(hw);
+	return -1;
+}
+
+/* ADC operations */
+#define MAKE_WM8775_ADDR(addr, data)	(u32)(((addr<<1)&0xFE)|((data>>8)&0x1))
+#define MAKE_WM8775_DATA(data)	(u32)(data&0xFF)
+
+#define WM8775_IC       0x0B
+#define WM8775_MMC      0x0C
+#define WM8775_AADCL    0x0E
+#define WM8775_AADCR    0x0F
+#define WM8775_ADCMC    0x15
+#define WM8775_RESET    0x17
+
+static int hw_is_adc_input_selected(struct hw *hw, enum ADCSRC type)
+{
+	u32 data;
+
+	data = hw_read_20kx(hw, GPIO_DATA);
+	switch (type) {
+	case ADC_MICIN:
+		data = (data & (0x1 << 14)) ? 1 : 0;
+		break;
+	case ADC_LINEIN:
+		data = (data & (0x1 << 14)) ? 0 : 1;
+		break;
+	default:
+		data = 0;
+	}
+	return data;
+}
+
+static int hw_adc_input_select(struct hw *hw, enum ADCSRC type)
+{
+	u32 data;
+
+	data = hw_read_20kx(hw, GPIO_DATA);
+	switch (type) {
+	case ADC_MICIN:
+		data |= (0x1 << 14);
+		hw_write_20kx(hw, GPIO_DATA, data);
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
+				MAKE_WM8775_DATA(0x101)); /* Mic-in */
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
+				MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
+				MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+		break;
+	case ADC_LINEIN:
+		data &= ~(0x1 << 14);
+		hw_write_20kx(hw, GPIO_DATA, data);
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
+				MAKE_WM8775_DATA(0x102)); /* Line-in */
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
+				MAKE_WM8775_DATA(0xCF)); /* No boost */
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
+				MAKE_WM8775_DATA(0xCF)); /* No boost */
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int hw_adc_init(struct hw *hw, const struct adc_conf *info)
+{
+	int err;
+	u32 mux = 2, data, ctl;
+
+	/*  Set ADC reset bit as output */
+	data = hw_read_20kx(hw, GPIO_CTRL);
+	data |= (0x1 << 15);
+	hw_write_20kx(hw, GPIO_CTRL, data);
+
+	/* Initialize I2C */
+	err = hw20k2_i2c_init(hw, 0x1A, 1, 1);
+	if (err < 0) {
+		printk(KERN_ALERT "ctxfi: Failure to acquire I2C!!!\n");
+		goto error;
+	}
+
+	/* Make ADC in normal operation */
+	data = hw_read_20kx(hw, GPIO_DATA);
+	data &= ~(0x1 << 15);
+	mdelay(10);
+	data |= (0x1 << 15);
+	hw_write_20kx(hw, GPIO_DATA, data);
+	mdelay(50);
+
+	/* Set the master mode (256fs) */
+	if (1 == info->msr) {
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x02),
+						MAKE_WM8775_DATA(0x02));
+	} else if (2 == info->msr) {
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_MMC, 0x0A),
+						MAKE_WM8775_DATA(0x0A));
+	} else {
+		printk(KERN_ALERT "ctxfi: Invalid master sampling "
+				  "rate (msr %d)!!!\n", info->msr);
+		err = -EINVAL;
+		goto error;
+	}
+
+	/* Configure GPIO bit 14 change to line-in/mic-in */
+	ctl = hw_read_20kx(hw, GPIO_CTRL);
+	ctl |= 0x1 << 14;
+	hw_write_20kx(hw, GPIO_CTRL, ctl);
+
+	/* Check using Mic-in or Line-in */
+	data = hw_read_20kx(hw, GPIO_DATA);
+
+	if (mux == 1) {
+		/* Configures GPIO data to select Mic-in */
+		data |= 0x1 << 14;
+		hw_write_20kx(hw, GPIO_DATA, data);
+
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x101),
+				MAKE_WM8775_DATA(0x101)); /* Mic-in */
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xE7),
+				MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xE7),
+				MAKE_WM8775_DATA(0xE7)); /* +12dB boost */
+	} else if (mux == 2) {
+		/* Configures GPIO data to select Line-in */
+		data &= ~(0x1 << 14);
+		hw_write_20kx(hw, GPIO_DATA, data);
+
+		/* Setup ADC */
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_ADCMC, 0x102),
+				MAKE_WM8775_DATA(0x102)); /* Line-in */
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCL, 0xCF),
+				MAKE_WM8775_DATA(0xCF)); /* No boost */
+		hw20k2_i2c_write(hw, MAKE_WM8775_ADDR(WM8775_AADCR, 0xCF),
+				MAKE_WM8775_DATA(0xCF)); /* No boost */
+	} else {
+		printk(KERN_ALERT "ctxfi: ERROR!!! Invalid input mux!!!\n");
+		err = -EINVAL;
+		goto error;
+	}
+
+	return 0;
+
+error:
+	hw20k2_i2c_uninit(hw);
+	return err;
+}
+
+static int hw_have_digit_io_switch(struct hw *hw)
+{
+	return 0;
+}
+
+static int hw_card_start(struct hw *hw)
+{
+	int err = 0;
+	struct pci_dev *pci = hw->pci;
+	unsigned int gctl;
+
+	err = pci_enable_device(pci);
+	if (err < 0)
+		return err;
+
+	/* Set DMA transfer mask */
+	if (pci_set_dma_mask(pci, CT_XFI_DMA_MASK) < 0 ||
+	    pci_set_consistent_dma_mask(pci, CT_XFI_DMA_MASK) < 0) {
+		printk(KERN_ERR "ctxfi: architecture does not support PCI "
+		"busmaster DMA with mask 0x%llx\n", CT_XFI_DMA_MASK);
+		err = -ENXIO;
+		goto error1;
+	}
+
+	if (!hw->io_base) {
+		err = pci_request_regions(pci, "XFi");
+		if (err < 0)
+			goto error1;
+
+		hw->io_base = pci_resource_start(hw->pci, 2);
+		hw->mem_base = (unsigned long)ioremap(hw->io_base,
+					pci_resource_len(hw->pci, 2));
+		if (NULL == (void *)hw->mem_base) {
+			err = -ENOENT;
+			goto error2;
+		}
+	}
+
+	/* Switch to 20k2 mode from UAA mode. */
+	gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+	set_field(&gctl, GCTL_UAA, 0);
+	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+
+	/*if ((err = request_irq(pci->irq, ct_atc_interrupt, IRQF_SHARED,
+				atc->chip_details->nm_card, hw))) {
+		goto error3;
+	}
+	hw->irq = pci->irq;
+	*/
+
+	pci_set_master(pci);
+
+	return 0;
+
+/*error3:
+	iounmap((void *)hw->mem_base);
+	hw->mem_base = (unsigned long)NULL;*/
+error2:
+	pci_release_regions(pci);
+	hw->io_base = 0;
+error1:
+	pci_disable_device(pci);
+	return err;
+}
+
+static int hw_card_stop(struct hw *hw)
+{
+	unsigned int data;
+
+	/* disable transport bus master and queueing of request */
+	hw_write_20kx(hw, TRANSPORT_CTL, 0x00);
+
+	/* disable pll */
+	data = hw_read_20kx(hw, PLL_ENB);
+	hw_write_20kx(hw, PLL_ENB, (data & (~0x07)));
+
+	/* TODO: Disable interrupt and so on... */
+	return 0;
+}
+
+static int hw_card_shutdown(struct hw *hw)
+{
+	if (hw->irq >= 0)
+		free_irq(hw->irq, hw);
+
+	hw->irq	= -1;
+
+	if (NULL != ((void *)hw->mem_base))
+		iounmap((void *)hw->mem_base);
+
+	hw->mem_base = (unsigned long)NULL;
+
+	if (hw->io_base)
+		pci_release_regions(hw->pci);
+
+	hw->io_base = 0;
+
+	pci_disable_device(hw->pci);
+
+	return 0;
+}
+
+static int hw_card_init(struct hw *hw, struct card_conf *info)
+{
+	int err;
+	unsigned int gctl;
+	u32 data = 0;
+	struct dac_conf dac_info = {0};
+	struct adc_conf adc_info = {0};
+	struct daio_conf daio_info = {0};
+	struct trn_conf trn_info = {0};
+
+	/* Get PCI io port/memory base address and
+	 * do 20kx core switch if needed. */
+	err = hw_card_start(hw);
+	if (err)
+		return err;
+
+	/* PLL init */
+	err = hw_pll_init(hw, info->rsr);
+	if (err < 0)
+		return err;
+
+	/* kick off auto-init */
+	err = hw_auto_init(hw);
+	if (err < 0)
+		return err;
+
+	gctl = hw_read_20kx(hw, GLOBAL_CNTL_GCTL);
+	set_field(&gctl, GCTL_DBP, 1);
+	set_field(&gctl, GCTL_TBP, 1);
+	set_field(&gctl, GCTL_FBP, 1);
+	set_field(&gctl, GCTL_DPC, 0);
+	hw_write_20kx(hw, GLOBAL_CNTL_GCTL, gctl);
+
+	/* Reset all global pending interrupts */
+	hw_write_20kx(hw, INTERRUPT_GIE, 0);
+	/* Reset all SRC pending interrupts */
+	hw_write_20kx(hw, SRC_IP, 0);
+
+	/* TODO: detect the card ID and configure GPIO accordingly. */
+	/* Configures GPIO (0xD802 0x98028) */
+	/*hw_write_20kx(hw, GPIO_CTRL, 0x7F07);*/
+	/* Configures GPIO (SB0880) */
+	/*hw_write_20kx(hw, GPIO_CTRL, 0xFF07);*/
+	hw_write_20kx(hw, GPIO_CTRL, 0xD802);
+
+	/* Enable audio ring */
+	hw_write_20kx(hw, MIXER_AR_ENABLE, 0x01);
+
+	trn_info.vm_pgt_phys = info->vm_pgt_phys;
+	err = hw_trn_init(hw, &trn_info);
+	if (err < 0)
+		return err;
+
+	daio_info.msr = info->msr;
+	err = hw_daio_init(hw, &daio_info);
+	if (err < 0)
+		return err;
+
+	dac_info.msr = info->msr;
+	err = hw_dac_init(hw, &dac_info);
+	if (err < 0)
+		return err;
+
+	adc_info.msr = info->msr;
+	adc_info.input = ADC_LINEIN;
+	adc_info.mic20db = 0;
+	err = hw_adc_init(hw, &adc_info);
+	if (err < 0)
+		return err;
+
+	data = hw_read_20kx(hw, SRC_MCTL);
+	data |= 0x1; /* Enables input from the audio ring */
+	hw_write_20kx(hw, SRC_MCTL, data);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int hw_suspend(struct hw *hw, pm_message_t state)
+{
+	struct pci_dev *pci = hw->pci;
+
+	hw_card_stop(hw);
+
+	pci_disable_device(pci);
+	pci_save_state(pci);
+	pci_set_power_state(pci, pci_choose_state(pci, state));
+
+	return 0;
+}
+
+static int hw_resume(struct hw *hw, struct card_conf *info)
+{
+	struct pci_dev *pci = hw->pci;
+
+	pci_set_power_state(pci, PCI_D0);
+	pci_restore_state(pci);
+
+	/* Re-initialize card hardware. */
+	return hw_card_init(hw, info);
+}
+#endif
+
+static u32 hw_read_20kx(struct hw *hw, u32 reg)
+{
+	return readl((void *)(hw->mem_base + reg));
+}
+
+static void hw_write_20kx(struct hw *hw, u32 reg, u32 data)
+{
+	writel(data, (void *)(hw->mem_base + reg));
+}
+
+static struct hw ct20k2_preset __devinitdata = {
+	.irq = -1,
+
+	.card_init = hw_card_init,
+	.card_stop = hw_card_stop,
+	.pll_init = hw_pll_init,
+	.is_adc_source_selected = hw_is_adc_input_selected,
+	.select_adc_source = hw_adc_input_select,
+	.have_digit_io_switch = hw_have_digit_io_switch,
+#ifdef CONFIG_PM
+	.suspend = hw_suspend,
+	.resume = hw_resume,
+#endif
+
+	.src_rsc_get_ctrl_blk = src_get_rsc_ctrl_blk,
+	.src_rsc_put_ctrl_blk = src_put_rsc_ctrl_blk,
+	.src_mgr_get_ctrl_blk = src_mgr_get_ctrl_blk,
+	.src_mgr_put_ctrl_blk = src_mgr_put_ctrl_blk,
+	.src_set_state = src_set_state,
+	.src_set_bm = src_set_bm,
+	.src_set_rsr = src_set_rsr,
+	.src_set_sf = src_set_sf,
+	.src_set_wr = src_set_wr,
+	.src_set_pm = src_set_pm,
+	.src_set_rom = src_set_rom,
+	.src_set_vo = src_set_vo,
+	.src_set_st = src_set_st,
+	.src_set_ie = src_set_ie,
+	.src_set_ilsz = src_set_ilsz,
+	.src_set_bp = src_set_bp,
+	.src_set_cisz = src_set_cisz,
+	.src_set_ca = src_set_ca,
+	.src_set_sa = src_set_sa,
+	.src_set_la = src_set_la,
+	.src_set_pitch = src_set_pitch,
+	.src_set_dirty = src_set_dirty,
+	.src_set_clear_zbufs = src_set_clear_zbufs,
+	.src_set_dirty_all = src_set_dirty_all,
+	.src_commit_write = src_commit_write,
+	.src_get_ca = src_get_ca,
+	.src_get_dirty = src_get_dirty,
+	.src_dirty_conj_mask = src_dirty_conj_mask,
+	.src_mgr_enbs_src = src_mgr_enbs_src,
+	.src_mgr_enb_src = src_mgr_enb_src,
+	.src_mgr_dsb_src = src_mgr_dsb_src,
+	.src_mgr_commit_write = src_mgr_commit_write,
+
+	.srcimp_mgr_get_ctrl_blk = srcimp_mgr_get_ctrl_blk,
+	.srcimp_mgr_put_ctrl_blk = srcimp_mgr_put_ctrl_blk,
+	.srcimp_mgr_set_imaparc = srcimp_mgr_set_imaparc,
+	.srcimp_mgr_set_imapuser = srcimp_mgr_set_imapuser,
+	.srcimp_mgr_set_imapnxt = srcimp_mgr_set_imapnxt,
+	.srcimp_mgr_set_imapaddr = srcimp_mgr_set_imapaddr,
+	.srcimp_mgr_commit_write = srcimp_mgr_commit_write,
+
+	.amixer_rsc_get_ctrl_blk = amixer_rsc_get_ctrl_blk,
+	.amixer_rsc_put_ctrl_blk = amixer_rsc_put_ctrl_blk,
+	.amixer_mgr_get_ctrl_blk = amixer_mgr_get_ctrl_blk,
+	.amixer_mgr_put_ctrl_blk = amixer_mgr_put_ctrl_blk,
+	.amixer_set_mode = amixer_set_mode,
+	.amixer_set_iv = amixer_set_iv,
+	.amixer_set_x = amixer_set_x,
+	.amixer_set_y = amixer_set_y,
+	.amixer_set_sadr = amixer_set_sadr,
+	.amixer_set_se = amixer_set_se,
+	.amixer_set_dirty = amixer_set_dirty,
+	.amixer_set_dirty_all = amixer_set_dirty_all,
+	.amixer_commit_write = amixer_commit_write,
+	.amixer_get_y = amixer_get_y,
+	.amixer_get_dirty = amixer_get_dirty,
+
+	.dai_get_ctrl_blk = dai_get_ctrl_blk,
+	.dai_put_ctrl_blk = dai_put_ctrl_blk,
+	.dai_srt_set_srco = dai_srt_set_srco,
+	.dai_srt_set_srcm = dai_srt_set_srcm,
+	.dai_srt_set_rsr = dai_srt_set_rsr,
+	.dai_srt_set_drat = dai_srt_set_drat,
+	.dai_srt_set_ec = dai_srt_set_ec,
+	.dai_srt_set_et = dai_srt_set_et,
+	.dai_commit_write = dai_commit_write,
+
+	.dao_get_ctrl_blk = dao_get_ctrl_blk,
+	.dao_put_ctrl_blk = dao_put_ctrl_blk,
+	.dao_set_spos = dao_set_spos,
+	.dao_commit_write = dao_commit_write,
+	.dao_get_spos = dao_get_spos,
+
+	.daio_mgr_get_ctrl_blk = daio_mgr_get_ctrl_blk,
+	.daio_mgr_put_ctrl_blk = daio_mgr_put_ctrl_blk,
+	.daio_mgr_enb_dai = daio_mgr_enb_dai,
+	.daio_mgr_dsb_dai = daio_mgr_dsb_dai,
+	.daio_mgr_enb_dao = daio_mgr_enb_dao,
+	.daio_mgr_dsb_dao = daio_mgr_dsb_dao,
+	.daio_mgr_dao_init = daio_mgr_dao_init,
+	.daio_mgr_set_imaparc = daio_mgr_set_imaparc,
+	.daio_mgr_set_imapnxt = daio_mgr_set_imapnxt,
+	.daio_mgr_set_imapaddr = daio_mgr_set_imapaddr,
+	.daio_mgr_commit_write = daio_mgr_commit_write,
+};
+
+int __devinit create_20k2_hw_obj(struct hw **rhw)
+{
+	struct hw20k2 *hw20k2;
+
+	*rhw = NULL;
+	hw20k2 = kzalloc(sizeof(*hw20k2), GFP_KERNEL);
+	if (!hw20k2)
+		return -ENOMEM;
+
+	hw20k2->hw = ct20k2_preset;
+	*rhw = &hw20k2->hw;
+
+	return 0;
+}
+
+int destroy_20k2_hw_obj(struct hw *hw)
+{
+	if (hw->io_base)
+		hw_card_shutdown(hw);
+
+	kfree(hw);
+	return 0;
+}
diff --git a/sound/pci/ctxfi/cthw20k2.h b/sound/pci/ctxfi/cthw20k2.h
new file mode 100644
index 0000000..d2b7daa
--- /dev/null
+++ b/sound/pci/ctxfi/cthw20k2.h
@@ -0,0 +1,26 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	cthw20k2.h
+ *
+ * @Brief
+ * This file contains the definition of hardware access methord.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 13 2008
+ *
+ */
+
+#ifndef CTHW20K2_H
+#define CTHW20K2_H
+
+#include "cthardware.h"
+
+int create_20k2_hw_obj(struct hw **rhw);
+int destroy_20k2_hw_obj(struct hw *hw);
+
+#endif /* CTHW20K2_H */
diff --git a/sound/pci/ctxfi/ctimap.c b/sound/pci/ctxfi/ctimap.c
new file mode 100644
index 0000000..0b73368
--- /dev/null
+++ b/sound/pci/ctxfi/ctimap.c
@@ -0,0 +1,112 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctimap.c
+ *
+ * @Brief
+ * This file contains the implementation of generic input mapper operations
+ * for input mapper management.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 23 2008
+ *
+ */
+
+#include "ctimap.h"
+#include <linux/slab.h>
+
+int input_mapper_add(struct list_head *mappers, struct imapper *entry,
+		     int (*map_op)(void *, struct imapper *), void *data)
+{
+	struct list_head *pos, *pre, *head;
+	struct imapper *pre_ent, *pos_ent;
+
+	head = mappers;
+
+	if (list_empty(head)) {
+		entry->next = entry->addr;
+		map_op(data, entry);
+		list_add(&entry->list, head);
+		return 0;
+	}
+
+	list_for_each(pos, head) {
+		pos_ent = list_entry(pos, struct imapper, list);
+		if (pos_ent->slot > entry->slot) {
+			/* found a position in list */
+			break;
+		}
+	}
+
+	if (pos != head) {
+		pre = pos->prev;
+		if (pre == head)
+			pre = head->prev;
+
+		__list_add(&entry->list, pos->prev, pos);
+	} else {
+		pre = head->prev;
+		pos = head->next;
+		list_add_tail(&entry->list, head);
+	}
+
+	pre_ent = list_entry(pre, struct imapper, list);
+	pos_ent = list_entry(pos, struct imapper, list);
+
+	entry->next = pos_ent->addr;
+	map_op(data, entry);
+	pre_ent->next = entry->addr;
+	map_op(data, pre_ent);
+
+	return 0;
+}
+
+int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
+		     int (*map_op)(void *, struct imapper *), void *data)
+{
+	struct list_head *next, *pre, *head;
+	struct imapper *pre_ent, *next_ent;
+
+	head = mappers;
+
+	if (list_empty(head))
+		return 0;
+
+	pre = (entry->list.prev == head) ? head->prev : entry->list.prev;
+	next = (entry->list.next == head) ? head->next : entry->list.next;
+
+	if (pre == &entry->list) {
+		/* entry is the only one node in mappers list */
+		entry->next = entry->addr = entry->user = entry->slot = 0;
+		map_op(data, entry);
+		list_del(&entry->list);
+		return 0;
+	}
+
+	pre_ent = list_entry(pre, struct imapper, list);
+	next_ent = list_entry(next, struct imapper, list);
+
+	pre_ent->next = next_ent->addr;
+	map_op(data, pre_ent);
+	list_del(&entry->list);
+
+	return 0;
+}
+
+void free_input_mapper_list(struct list_head *head)
+{
+	struct imapper *entry;
+	struct list_head *pos;
+
+	while (!list_empty(head)) {
+		pos = head->next;
+		list_del(pos);
+		entry = list_entry(pos, struct imapper, list);
+		kfree(entry);
+	}
+}
+
diff --git a/sound/pci/ctxfi/ctimap.h b/sound/pci/ctxfi/ctimap.h
new file mode 100644
index 0000000..53ccf9b
--- /dev/null
+++ b/sound/pci/ctxfi/ctimap.h
@@ -0,0 +1,40 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctimap.h
+ *
+ * @Brief
+ * This file contains the definition of generic input mapper operations
+ * for input mapper management.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 23 2008
+ *
+ */
+
+#ifndef CTIMAP_H
+#define CTIMAP_H
+
+#include <linux/list.h>
+
+struct imapper {
+	unsigned short slot; /* the id of the slot containing input data */
+	unsigned short user; /* the id of the user resource consuming data */
+	unsigned short addr; /* the input mapper ram id */
+	unsigned short next; /* the next input mapper ram id */
+	struct list_head	list;
+};
+
+int input_mapper_add(struct list_head *mappers, struct imapper *entry,
+		     int (*map_op)(void *, struct imapper *), void *data);
+
+int input_mapper_delete(struct list_head *mappers, struct imapper *entry,
+		     int (*map_op)(void *, struct imapper *), void *data);
+
+void free_input_mapper_list(struct list_head *mappers);
+
+#endif /* CTIMAP_H */
diff --git a/sound/pci/ctxfi/ctmixer.c b/sound/pci/ctxfi/ctmixer.c
new file mode 100644
index 0000000..f26d7cd
--- /dev/null
+++ b/sound/pci/ctxfi/ctmixer.c
@@ -0,0 +1,1157 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctmixer.c
+ *
+ * @Brief
+ * This file contains the implementation of alsa mixer device functions.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 28 2008
+ *
+ */
+
+
+#include "ctmixer.h"
+#include "ctamixer.h"
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/asoundef.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+
+enum CT_SUM_CTL {
+	SUM_IN_F,
+	SUM_IN_R,
+	SUM_IN_C,
+	SUM_IN_S,
+	SUM_IN_F_C,
+
+	NUM_CT_SUMS
+};
+
+enum CT_AMIXER_CTL {
+	/* volume control mixers */
+	AMIXER_MASTER_F,
+	AMIXER_MASTER_R,
+	AMIXER_MASTER_C,
+	AMIXER_MASTER_S,
+	AMIXER_PCM_F,
+	AMIXER_PCM_R,
+	AMIXER_PCM_C,
+	AMIXER_PCM_S,
+	AMIXER_SPDIFI,
+	AMIXER_LINEIN,
+	AMIXER_MIC,
+	AMIXER_SPDIFO,
+	AMIXER_WAVE_F,
+	AMIXER_WAVE_R,
+	AMIXER_WAVE_C,
+	AMIXER_WAVE_S,
+	AMIXER_MASTER_F_C,
+	AMIXER_PCM_F_C,
+	AMIXER_SPDIFI_C,
+	AMIXER_LINEIN_C,
+	AMIXER_MIC_C,
+
+	/* this should always be the last one */
+	NUM_CT_AMIXERS
+};
+
+enum CTALSA_MIXER_CTL {
+	/* volume control mixers */
+	MIXER_MASTER_P,
+	MIXER_PCM_P,
+	MIXER_LINEIN_P,
+	MIXER_MIC_P,
+	MIXER_SPDIFI_P,
+	MIXER_SPDIFO_P,
+	MIXER_WAVEF_P,
+	MIXER_WAVER_P,
+	MIXER_WAVEC_P,
+	MIXER_WAVES_P,
+	MIXER_MASTER_C,
+	MIXER_PCM_C,
+	MIXER_LINEIN_C,
+	MIXER_MIC_C,
+	MIXER_SPDIFI_C,
+
+	/* switch control mixers */
+	MIXER_PCM_C_S,
+	MIXER_LINEIN_C_S,
+	MIXER_MIC_C_S,
+	MIXER_SPDIFI_C_S,
+	MIXER_LINEIN_P_S,
+	MIXER_SPDIFO_P_S,
+	MIXER_SPDIFI_P_S,
+	MIXER_WAVEF_P_S,
+	MIXER_WAVER_P_S,
+	MIXER_WAVEC_P_S,
+	MIXER_WAVES_P_S,
+	MIXER_DIGITAL_IO_S,
+	MIXER_IEC958_MASK,
+	MIXER_IEC958_DEFAULT,
+	MIXER_IEC958_STREAM,
+
+	/* this should always be the last one */
+	NUM_CTALSA_MIXERS
+};
+
+#define VOL_MIXER_START		MIXER_MASTER_P
+#define VOL_MIXER_END		MIXER_SPDIFI_C
+#define VOL_MIXER_NUM		(VOL_MIXER_END - VOL_MIXER_START + 1)
+#define SWH_MIXER_START		MIXER_PCM_C_S
+#define SWH_MIXER_END		MIXER_DIGITAL_IO_S
+#define SWH_CAPTURE_START	MIXER_PCM_C_S
+#define SWH_CAPTURE_END		MIXER_SPDIFI_C_S
+
+#define CHN_NUM		2
+
+struct ct_kcontrol_init {
+	unsigned char ctl;
+	char *name;
+};
+
+static struct ct_kcontrol_init
+ct_kcontrol_init_table[NUM_CTALSA_MIXERS] = {
+	[MIXER_MASTER_P] = {
+		.ctl = 1,
+		.name = "Master Playback Volume",
+	},
+	[MIXER_MASTER_C] = {
+		.ctl = 1,
+		.name = "Master Capture Volume",
+	},
+	[MIXER_PCM_P] = {
+		.ctl = 1,
+		.name = "PCM Playback Volume",
+	},
+	[MIXER_PCM_C] = {
+		.ctl = 1,
+		.name = "PCM Capture Volume",
+	},
+	[MIXER_LINEIN_P] = {
+		.ctl = 1,
+		.name = "Line-in Playback Volume",
+	},
+	[MIXER_LINEIN_C] = {
+		.ctl = 1,
+		.name = "Line-in Capture Volume",
+	},
+	[MIXER_MIC_P] = {
+		.ctl = 1,
+		.name = "Mic Playback Volume",
+	},
+	[MIXER_MIC_C] = {
+		.ctl = 1,
+		.name = "Mic Capture Volume",
+	},
+	[MIXER_SPDIFI_P] = {
+		.ctl = 1,
+		.name = "S/PDIF-in Playback Volume",
+	},
+	[MIXER_SPDIFI_C] = {
+		.ctl = 1,
+		.name = "S/PDIF-in Capture Volume",
+	},
+	[MIXER_SPDIFO_P] = {
+		.ctl = 1,
+		.name = "S/PDIF-out Playback Volume",
+	},
+	[MIXER_WAVEF_P] = {
+		.ctl = 1,
+		.name = "Front Playback Volume",
+	},
+	[MIXER_WAVES_P] = {
+		.ctl = 1,
+		.name = "Side Playback Volume",
+	},
+	[MIXER_WAVEC_P] = {
+		.ctl = 1,
+		.name = "Center/LFE Playback Volume",
+	},
+	[MIXER_WAVER_P] = {
+		.ctl = 1,
+		.name = "Surround Playback Volume",
+	},
+
+	[MIXER_PCM_C_S] = {
+		.ctl = 1,
+		.name = "PCM Capture Switch",
+	},
+	[MIXER_LINEIN_C_S] = {
+		.ctl = 1,
+		.name = "Line-in Capture Switch",
+	},
+	[MIXER_MIC_C_S] = {
+		.ctl = 1,
+		.name = "Mic Capture Switch",
+	},
+	[MIXER_SPDIFI_C_S] = {
+		.ctl = 1,
+		.name = "S/PDIF-in Capture Switch",
+	},
+	[MIXER_LINEIN_P_S] = {
+		.ctl = 1,
+		.name = "Line-in Playback Switch",
+	},
+	[MIXER_SPDIFO_P_S] = {
+		.ctl = 1,
+		.name = "S/PDIF-out Playback Switch",
+	},
+	[MIXER_SPDIFI_P_S] = {
+		.ctl = 1,
+		.name = "S/PDIF-in Playback Switch",
+	},
+	[MIXER_WAVEF_P_S] = {
+		.ctl = 1,
+		.name = "Front Playback Switch",
+	},
+	[MIXER_WAVES_P_S] = {
+		.ctl = 1,
+		.name = "Side Playback Switch",
+	},
+	[MIXER_WAVEC_P_S] = {
+		.ctl = 1,
+		.name = "Center/LFE Playback Switch",
+	},
+	[MIXER_WAVER_P_S] = {
+		.ctl = 1,
+		.name = "Surround Playback Switch",
+	},
+	[MIXER_DIGITAL_IO_S] = {
+		.ctl = 0,
+		.name = "Digit-IO Playback Switch",
+	},
+};
+
+static void
+ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
+
+static void
+ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type);
+
+static struct snd_kcontrol *kctls[2] = {NULL};
+
+static enum CT_AMIXER_CTL get_amixer_index(enum CTALSA_MIXER_CTL alsa_index)
+{
+	switch (alsa_index) {
+	case MIXER_MASTER_P:	return AMIXER_MASTER_F;
+	case MIXER_MASTER_C:	return AMIXER_MASTER_F_C;
+	case MIXER_PCM_P:	return AMIXER_PCM_F;
+	case MIXER_PCM_C:
+	case MIXER_PCM_C_S:	return AMIXER_PCM_F_C;
+	case MIXER_LINEIN_P:	return AMIXER_LINEIN;
+	case MIXER_LINEIN_C:
+	case MIXER_LINEIN_C_S:	return AMIXER_LINEIN_C;
+	case MIXER_MIC_P:	return AMIXER_MIC;
+	case MIXER_MIC_C:
+	case MIXER_MIC_C_S:	return AMIXER_MIC_C;
+	case MIXER_SPDIFI_P:	return AMIXER_SPDIFI;
+	case MIXER_SPDIFI_C:
+	case MIXER_SPDIFI_C_S:	return AMIXER_SPDIFI_C;
+	case MIXER_SPDIFO_P:	return AMIXER_SPDIFO;
+	case MIXER_WAVEF_P:	return AMIXER_WAVE_F;
+	case MIXER_WAVES_P:	return AMIXER_WAVE_S;
+	case MIXER_WAVEC_P:	return AMIXER_WAVE_C;
+	case MIXER_WAVER_P:	return AMIXER_WAVE_R;
+	default:		return NUM_CT_AMIXERS;
+	}
+}
+
+static enum CT_AMIXER_CTL get_recording_amixer(enum CT_AMIXER_CTL index)
+{
+	switch (index) {
+	case AMIXER_MASTER_F:	return AMIXER_MASTER_F_C;
+	case AMIXER_PCM_F:	return AMIXER_PCM_F_C;
+	case AMIXER_SPDIFI:	return AMIXER_SPDIFI_C;
+	case AMIXER_LINEIN:	return AMIXER_LINEIN_C;
+	case AMIXER_MIC:	return AMIXER_MIC_C;
+	default:		return NUM_CT_AMIXERS;
+	}
+}
+
+static unsigned char
+get_switch_state(struct ct_mixer *mixer, enum CTALSA_MIXER_CTL type)
+{
+	return (mixer->switch_state & (0x1 << (type - SWH_MIXER_START)))
+		? 1 : 0;
+}
+
+static void
+set_switch_state(struct ct_mixer *mixer,
+		 enum CTALSA_MIXER_CTL type, unsigned char state)
+{
+	if (state)
+		mixer->switch_state |= (0x1 << (type - SWH_MIXER_START));
+	else
+		mixer->switch_state &= ~(0x1 << (type - SWH_MIXER_START));
+}
+
+#if 0 /* not used */
+/* Map integer value ranging from 0 to 65535 to 14-bit float value ranging
+ * from 2^-6 to (1+1023/1024) */
+static unsigned int uint16_to_float14(unsigned int x)
+{
+	unsigned int i;
+
+	if (x < 17)
+		return 0;
+
+	x *= 2031;
+	x /= 65535;
+	x += 16;
+
+	/* i <= 6 */
+	for (i = 0; !(x & 0x400); i++)
+		x <<= 1;
+
+	x = (((7 - i) & 0x7) << 10) | (x & 0x3ff);
+
+	return x;
+}
+
+static unsigned int float14_to_uint16(unsigned int x)
+{
+	unsigned int e;
+
+	if (!x)
+		return x;
+
+	e = (x >> 10) & 0x7;
+	x &= 0x3ff;
+	x += 1024;
+	x >>= (7 - e);
+	x -= 16;
+	x *= 65535;
+	x /= 2031;
+
+	return x;
+}
+#endif /* not used */
+
+#define VOL_SCALE	0x1c
+#define VOL_MAX		0x100
+
+static const DECLARE_TLV_DB_SCALE(ct_vol_db_scale, -6400, 25, 1);
+
+static int ct_alsa_mix_volume_info(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = VOL_MAX;
+
+	return 0;
+}
+
+static int ct_alsa_mix_volume_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+	enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value);
+	struct amixer *amixer;
+	int i, val;
+
+	for (i = 0; i < 2; i++) {
+		amixer = ((struct ct_mixer *)atc->mixer)->
+						amixers[type*CHN_NUM+i];
+		val = amixer->ops->get_scale(amixer) / VOL_SCALE;
+		if (val < 0)
+			val = 0;
+		else if (val > VOL_MAX)
+			val = VOL_MAX;
+		ucontrol->value.integer.value[i] = val;
+	}
+
+	return 0;
+}
+
+static int ct_alsa_mix_volume_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+	struct ct_mixer *mixer = atc->mixer;
+	enum CT_AMIXER_CTL type = get_amixer_index(kcontrol->private_value);
+	struct amixer *amixer;
+	int i, j, val, oval, change = 0;
+
+	for (i = 0; i < 2; i++) {
+		val = ucontrol->value.integer.value[i];
+		if (val < 0)
+			val = 0;
+		else if (val > VOL_MAX)
+			val = VOL_MAX;
+		val *= VOL_SCALE;
+		amixer = mixer->amixers[type*CHN_NUM+i];
+		oval = amixer->ops->get_scale(amixer);
+		if (val != oval) {
+			amixer->ops->set_scale(amixer, val);
+			amixer->ops->commit_write(amixer);
+			change = 1;
+			/* Synchronize Master/PCM playback AMIXERs. */
+			if (AMIXER_MASTER_F == type || AMIXER_PCM_F == type) {
+				for (j = 1; j < 4; j++) {
+					amixer = mixer->
+						amixers[(type+j)*CHN_NUM+i];
+					amixer->ops->set_scale(amixer, val);
+					amixer->ops->commit_write(amixer);
+				}
+			}
+		}
+	}
+
+	return change;
+}
+
+static struct snd_kcontrol_new vol_ctl = {
+	.access		= SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			  SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info		= ct_alsa_mix_volume_info,
+	.get		= ct_alsa_mix_volume_get,
+	.put		= ct_alsa_mix_volume_put,
+	.tlv		= { .p =  ct_vol_db_scale },
+};
+
+static void
+do_line_mic_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type)
+{
+
+	if (MIXER_LINEIN_C_S == type) {
+		atc->select_line_in(atc);
+		set_switch_state(atc->mixer, MIXER_MIC_C_S, 0);
+		snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE,
+							&kctls[1]->id);
+	} else if (MIXER_MIC_C_S == type) {
+		atc->select_mic_in(atc);
+		set_switch_state(atc->mixer, MIXER_LINEIN_C_S, 0);
+		snd_ctl_notify(atc->card, SNDRV_CTL_EVENT_MASK_VALUE,
+							&kctls[0]->id);
+	}
+}
+
+static void
+do_digit_io_switch(struct ct_atc *atc, int state)
+{
+	struct ct_mixer *mixer = atc->mixer;
+
+	if (state) {
+		atc->select_digit_io(atc);
+		atc->spdif_out_unmute(atc,
+				get_switch_state(mixer, MIXER_SPDIFO_P_S));
+		atc->spdif_in_unmute(atc, 1);
+		atc->line_in_unmute(atc, 0);
+		return;
+	}
+
+	if (get_switch_state(mixer, MIXER_LINEIN_C_S))
+		atc->select_line_in(atc);
+	else if (get_switch_state(mixer, MIXER_MIC_C_S))
+		atc->select_mic_in(atc);
+
+	atc->spdif_out_unmute(atc, 0);
+	atc->spdif_in_unmute(atc, 0);
+	atc->line_in_unmute(atc, 1);
+	return;
+}
+
+static void do_switch(struct ct_atc *atc, enum CTALSA_MIXER_CTL type, int state)
+{
+	struct ct_mixer *mixer = atc->mixer;
+
+	/* Do changes in mixer. */
+	if ((SWH_CAPTURE_START <= type) && (SWH_CAPTURE_END >= type)) {
+		if (state) {
+			ct_mixer_recording_select(mixer,
+						  get_amixer_index(type));
+		} else {
+			ct_mixer_recording_unselect(mixer,
+						    get_amixer_index(type));
+		}
+	}
+	/* Do changes out of mixer. */
+	if (state && (MIXER_LINEIN_C_S == type || MIXER_MIC_C_S == type))
+		do_line_mic_switch(atc, type);
+	else if (MIXER_WAVEF_P_S == type)
+		atc->line_front_unmute(atc, state);
+	else if (MIXER_WAVES_P_S == type)
+		atc->line_surround_unmute(atc, state);
+	else if (MIXER_WAVEC_P_S == type)
+		atc->line_clfe_unmute(atc, state);
+	else if (MIXER_WAVER_P_S == type)
+		atc->line_rear_unmute(atc, state);
+	else if (MIXER_LINEIN_P_S == type)
+		atc->line_in_unmute(atc, state);
+	else if (MIXER_SPDIFO_P_S == type)
+		atc->spdif_out_unmute(atc, state);
+	else if (MIXER_SPDIFI_P_S == type)
+		atc->spdif_in_unmute(atc, state);
+	else if (MIXER_DIGITAL_IO_S == type)
+		do_digit_io_switch(atc, state);
+
+	return;
+}
+
+static int ct_alsa_mix_switch_info(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	uinfo->value.integer.step = 1;
+
+	return 0;
+}
+
+static int ct_alsa_mix_switch_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct ct_mixer *mixer =
+		((struct ct_atc *)snd_kcontrol_chip(kcontrol))->mixer;
+	enum CTALSA_MIXER_CTL type = kcontrol->private_value;
+
+	ucontrol->value.integer.value[0] = get_switch_state(mixer, type);
+	return 0;
+}
+
+static int ct_alsa_mix_switch_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+	struct ct_mixer *mixer = atc->mixer;
+	enum CTALSA_MIXER_CTL type = kcontrol->private_value;
+	int state;
+
+	state = ucontrol->value.integer.value[0];
+	if (get_switch_state(mixer, type) == state)
+		return 0;
+
+	set_switch_state(mixer, type, state);
+	do_switch(atc, type, state);
+
+	return 1;
+}
+
+static struct snd_kcontrol_new swh_ctl = {
+	.access		= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.iface		= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info		= ct_alsa_mix_switch_info,
+	.get		= ct_alsa_mix_switch_get,
+	.put		= ct_alsa_mix_switch_put
+};
+
+static int ct_spdif_info(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static int ct_spdif_get_mask(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.iec958.status[0] = 0xff;
+	ucontrol->value.iec958.status[1] = 0xff;
+	ucontrol->value.iec958.status[2] = 0xff;
+	ucontrol->value.iec958.status[3] = 0xff;
+	return 0;
+}
+
+static int ct_spdif_default_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	unsigned int status = SNDRV_PCM_DEFAULT_CON_SPDIF;
+
+	ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (status >> 24) & 0xff;
+
+	return 0;
+}
+
+static int ct_spdif_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+	unsigned int status;
+
+	atc->spdif_out_get_status(atc, &status);
+	ucontrol->value.iec958.status[0] = (status >> 0) & 0xff;
+	ucontrol->value.iec958.status[1] = (status >> 8) & 0xff;
+	ucontrol->value.iec958.status[2] = (status >> 16) & 0xff;
+	ucontrol->value.iec958.status[3] = (status >> 24) & 0xff;
+
+	return 0;
+}
+
+static int ct_spdif_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct ct_atc *atc = snd_kcontrol_chip(kcontrol);
+	int change;
+	unsigned int status, old_status;
+
+	status = (ucontrol->value.iec958.status[0] << 0) |
+		 (ucontrol->value.iec958.status[1] << 8) |
+		 (ucontrol->value.iec958.status[2] << 16) |
+		 (ucontrol->value.iec958.status[3] << 24);
+
+	atc->spdif_out_get_status(atc, &old_status);
+	change = (old_status != status);
+	if (change)
+		atc->spdif_out_set_status(atc, status);
+
+	return change;
+}
+
+static struct snd_kcontrol_new iec958_mask_ctl = {
+	.access		= SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface		= SNDRV_CTL_ELEM_IFACE_PCM,
+	.name		= SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
+	.count		= 1,
+	.info		= ct_spdif_info,
+	.get		= ct_spdif_get_mask,
+	.private_value	= MIXER_IEC958_MASK
+};
+
+static struct snd_kcontrol_new iec958_default_ctl = {
+	.iface		= SNDRV_CTL_ELEM_IFACE_PCM,
+	.name		= SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+	.count		= 1,
+	.info		= ct_spdif_info,
+	.get		= ct_spdif_default_get,
+	.put		= ct_spdif_put,
+	.private_value	= MIXER_IEC958_DEFAULT
+};
+
+static struct snd_kcontrol_new iec958_ctl = {
+	.access		= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.iface		= SNDRV_CTL_ELEM_IFACE_PCM,
+	.name		= SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
+	.count		= 1,
+	.info		= ct_spdif_info,
+	.get		= ct_spdif_get,
+	.put		= ct_spdif_put,
+	.private_value	= MIXER_IEC958_STREAM
+};
+
+#define NUM_IEC958_CTL 3
+
+static int
+ct_mixer_kcontrol_new(struct ct_mixer *mixer, struct snd_kcontrol_new *new)
+{
+	struct snd_kcontrol *kctl;
+	int err;
+
+	kctl = snd_ctl_new1(new, mixer->atc);
+	if (NULL == kctl)
+		return -ENOMEM;
+
+	if (SNDRV_CTL_ELEM_IFACE_PCM == kctl->id.iface)
+		kctl->id.device = IEC958;
+
+	err = snd_ctl_add(mixer->atc->card, kctl);
+	if (err)
+		return err;
+
+	switch (new->private_value) {
+	case MIXER_LINEIN_C_S:
+		kctls[0] = kctl; break;
+	case MIXER_MIC_C_S:
+		kctls[1] = kctl; break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int ct_mixer_kcontrols_create(struct ct_mixer *mixer)
+{
+	enum CTALSA_MIXER_CTL type;
+	struct ct_atc *atc = mixer->atc;
+	int err;
+
+	/* Create snd kcontrol instances on demand */
+	for (type = VOL_MIXER_START; type <= VOL_MIXER_END; type++) {
+		if (ct_kcontrol_init_table[type].ctl) {
+			vol_ctl.name = ct_kcontrol_init_table[type].name;
+			vol_ctl.private_value = (unsigned long)type;
+			err = ct_mixer_kcontrol_new(mixer, &vol_ctl);
+			if (err)
+				return err;
+		}
+	}
+
+	ct_kcontrol_init_table[MIXER_DIGITAL_IO_S].ctl =
+					atc->have_digit_io_switch(atc);
+	for (type = SWH_MIXER_START; type <= SWH_MIXER_END; type++) {
+		if (ct_kcontrol_init_table[type].ctl) {
+			swh_ctl.name = ct_kcontrol_init_table[type].name;
+			swh_ctl.private_value = (unsigned long)type;
+			err = ct_mixer_kcontrol_new(mixer, &swh_ctl);
+			if (err)
+				return err;
+		}
+	}
+
+	err = ct_mixer_kcontrol_new(mixer, &iec958_mask_ctl);
+	if (err)
+		return err;
+
+	err = ct_mixer_kcontrol_new(mixer, &iec958_default_ctl);
+	if (err)
+		return err;
+
+	err = ct_mixer_kcontrol_new(mixer, &iec958_ctl);
+	if (err)
+		return err;
+
+	atc->line_front_unmute(atc, 1);
+	set_switch_state(mixer, MIXER_WAVEF_P_S, 1);
+	atc->line_surround_unmute(atc, 0);
+	set_switch_state(mixer, MIXER_WAVES_P_S, 0);
+	atc->line_clfe_unmute(atc, 0);
+	set_switch_state(mixer, MIXER_WAVEC_P_S, 0);
+	atc->line_rear_unmute(atc, 0);
+	set_switch_state(mixer, MIXER_WAVER_P_S, 0);
+	atc->spdif_out_unmute(atc, 0);
+	set_switch_state(mixer, MIXER_SPDIFO_P_S, 0);
+	atc->line_in_unmute(atc, 0);
+	set_switch_state(mixer, MIXER_LINEIN_P_S, 0);
+	atc->spdif_in_unmute(atc, 0);
+	set_switch_state(mixer, MIXER_SPDIFI_P_S, 0);
+
+	set_switch_state(mixer, MIXER_PCM_C_S, 1);
+	set_switch_state(mixer, MIXER_LINEIN_C_S, 1);
+	set_switch_state(mixer, MIXER_SPDIFI_C_S, 1);
+
+	return 0;
+}
+
+static void
+ct_mixer_recording_select(struct ct_mixer *mixer, enum CT_AMIXER_CTL type)
+{
+	struct amixer *amix_d;
+	struct sum *sum_c;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		amix_d = mixer->amixers[type*CHN_NUM+i];
+		sum_c = mixer->sums[SUM_IN_F_C*CHN_NUM+i];
+		amix_d->ops->set_sum(amix_d, sum_c);
+		amix_d->ops->commit_write(amix_d);
+	}
+}
+
+static void
+ct_mixer_recording_unselect(struct ct_mixer *mixer, enum CT_AMIXER_CTL type)
+{
+	struct amixer *amix_d;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		amix_d = mixer->amixers[type*CHN_NUM+i];
+		amix_d->ops->set_sum(amix_d, NULL);
+		amix_d->ops->commit_write(amix_d);
+	}
+}
+
+static int ct_mixer_get_resources(struct ct_mixer *mixer)
+{
+	struct sum_mgr *sum_mgr;
+	struct sum *sum;
+	struct sum_desc sum_desc = {0};
+	struct amixer_mgr *amixer_mgr;
+	struct amixer *amixer;
+	struct amixer_desc am_desc = {0};
+	int err;
+	int i;
+
+	/* Allocate sum resources for mixer obj */
+	sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM];
+	sum_desc.msr = mixer->atc->msr;
+	for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
+		err = sum_mgr->get_sum(sum_mgr, &sum_desc, &sum);
+		if (err) {
+			printk(KERN_ERR "ctxfi:Failed to get sum resources for "
+					  "front output!\n");
+			break;
+		}
+		mixer->sums[i] = sum;
+	}
+	if (err)
+		goto error1;
+
+	/* Allocate amixer resources for mixer obj */
+	amixer_mgr = (struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER];
+	am_desc.msr = mixer->atc->msr;
+	for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
+		err = amixer_mgr->get_amixer(amixer_mgr, &am_desc, &amixer);
+		if (err) {
+			printk(KERN_ERR "ctxfi:Failed to get amixer resources "
+			       "for mixer obj!\n");
+			break;
+		}
+		mixer->amixers[i] = amixer;
+	}
+	if (err)
+		goto error2;
+
+	return 0;
+
+error2:
+	for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
+		if (NULL != mixer->amixers[i]) {
+			amixer = mixer->amixers[i];
+			amixer_mgr->put_amixer(amixer_mgr, amixer);
+			mixer->amixers[i] = NULL;
+		}
+	}
+error1:
+	for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
+		if (NULL != mixer->sums[i]) {
+			sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]);
+			mixer->sums[i] = NULL;
+		}
+	}
+
+	return err;
+}
+
+static int ct_mixer_get_mem(struct ct_mixer **rmixer)
+{
+	struct ct_mixer *mixer;
+	int err;
+
+	*rmixer = NULL;
+	/* Allocate mem for mixer obj */
+	mixer = kzalloc(sizeof(*mixer), GFP_KERNEL);
+	if (NULL == mixer)
+		return -ENOMEM;
+
+	mixer->amixers = kzalloc(sizeof(void *)*(NUM_CT_AMIXERS*CHN_NUM),
+				 GFP_KERNEL);
+	if (NULL == mixer->amixers) {
+		err = -ENOMEM;
+		goto error1;
+	}
+	mixer->sums = kzalloc(sizeof(void *)*(NUM_CT_SUMS*CHN_NUM), GFP_KERNEL);
+	if (NULL == mixer->sums) {
+		err = -ENOMEM;
+		goto error2;
+	}
+
+	*rmixer = mixer;
+	return 0;
+
+error2:
+	kfree(mixer->amixers);
+error1:
+	kfree(mixer);
+	return err;
+}
+
+static int ct_mixer_topology_build(struct ct_mixer *mixer)
+{
+	struct sum *sum;
+	struct amixer *amix_d, *amix_s;
+	enum CT_AMIXER_CTL i, j;
+
+	/* Build topology from destination to source */
+
+	/* Set up Master mixer */
+	for (i = AMIXER_MASTER_F, j = SUM_IN_F;
+					i <= AMIXER_MASTER_S; i++, j++) {
+		amix_d = mixer->amixers[i*CHN_NUM];
+		sum = mixer->sums[j*CHN_NUM];
+		amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+		amix_d = mixer->amixers[i*CHN_NUM+1];
+		sum = mixer->sums[j*CHN_NUM+1];
+		amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+	}
+
+	/* Set up Wave-out mixer */
+	for (i = AMIXER_WAVE_F, j = AMIXER_MASTER_F;
+					i <= AMIXER_WAVE_S; i++, j++) {
+		amix_d = mixer->amixers[i*CHN_NUM];
+		amix_s = mixer->amixers[j*CHN_NUM];
+		amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+		amix_d = mixer->amixers[i*CHN_NUM+1];
+		amix_s = mixer->amixers[j*CHN_NUM+1];
+		amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+	}
+
+	/* Set up S/PDIF-out mixer */
+	amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM];
+	amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM];
+	amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+	amix_d = mixer->amixers[AMIXER_SPDIFO*CHN_NUM+1];
+	amix_s = mixer->amixers[AMIXER_MASTER_F*CHN_NUM+1];
+	amix_d->ops->setup(amix_d, &amix_s->rsc, INIT_VOL, NULL);
+
+	/* Set up PCM-in mixer */
+	for (i = AMIXER_PCM_F, j = SUM_IN_F; i <= AMIXER_PCM_S; i++, j++) {
+		amix_d = mixer->amixers[i*CHN_NUM];
+		sum = mixer->sums[j*CHN_NUM];
+		amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+		amix_d = mixer->amixers[i*CHN_NUM+1];
+		sum = mixer->sums[j*CHN_NUM+1];
+		amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+	}
+
+	/* Set up Line-in mixer */
+	amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM];
+	sum = mixer->sums[SUM_IN_F*CHN_NUM];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+	amix_d = mixer->amixers[AMIXER_LINEIN*CHN_NUM+1];
+	sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+	/* Set up Mic-in mixer */
+	amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM];
+	sum = mixer->sums[SUM_IN_F*CHN_NUM];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+	amix_d = mixer->amixers[AMIXER_MIC*CHN_NUM+1];
+	sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+	/* Set up S/PDIF-in mixer */
+	amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM];
+	sum = mixer->sums[SUM_IN_F*CHN_NUM];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+	amix_d = mixer->amixers[AMIXER_SPDIFI*CHN_NUM+1];
+	sum = mixer->sums[SUM_IN_F*CHN_NUM+1];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+	/* Set up Master recording mixer */
+	amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM];
+	sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+	amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+	amix_d = mixer->amixers[AMIXER_MASTER_F_C*CHN_NUM+1];
+	sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+	amix_d->ops->setup(amix_d, &sum->rsc, INIT_VOL, NULL);
+
+	/* Set up PCM-in recording mixer */
+	amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM];
+	sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+	amix_d = mixer->amixers[AMIXER_PCM_F_C*CHN_NUM+1];
+	sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+	/* Set up Line-in recording mixer */
+	amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM];
+	sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+	amix_d = mixer->amixers[AMIXER_LINEIN_C*CHN_NUM+1];
+	sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+	/* Set up Mic-in recording mixer */
+	amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM];
+	sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+	amix_d = mixer->amixers[AMIXER_MIC_C*CHN_NUM+1];
+	sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+	/* Set up S/PDIF-in recording mixer */
+	amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM];
+	sum = mixer->sums[SUM_IN_F_C*CHN_NUM];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+	amix_d = mixer->amixers[AMIXER_SPDIFI_C*CHN_NUM+1];
+	sum = mixer->sums[SUM_IN_F_C*CHN_NUM+1];
+	amix_d->ops->setup(amix_d, NULL, INIT_VOL, sum);
+
+	return 0;
+}
+
+static int mixer_set_input_port(struct amixer *amixer, struct rsc *rsc)
+{
+	amixer->ops->set_input(amixer, rsc);
+	amixer->ops->commit_write(amixer);
+
+	return 0;
+}
+
+static enum CT_AMIXER_CTL port_to_amixer(enum MIXER_PORT_T type)
+{
+	switch (type) {
+	case MIX_WAVE_FRONT:	return AMIXER_WAVE_F;
+	case MIX_WAVE_SURROUND:	return AMIXER_WAVE_S;
+	case MIX_WAVE_CENTLFE:	return AMIXER_WAVE_C;
+	case MIX_WAVE_REAR:	return AMIXER_WAVE_R;
+	case MIX_PCMO_FRONT:	return AMIXER_MASTER_F_C;
+	case MIX_SPDIF_OUT:	return AMIXER_SPDIFO;
+	case MIX_LINE_IN:	return AMIXER_LINEIN;
+	case MIX_MIC_IN:	return AMIXER_MIC;
+	case MIX_SPDIF_IN:	return AMIXER_SPDIFI;
+	case MIX_PCMI_FRONT:	return AMIXER_PCM_F;
+	case MIX_PCMI_SURROUND:	return AMIXER_PCM_S;
+	case MIX_PCMI_CENTLFE:	return AMIXER_PCM_C;
+	case MIX_PCMI_REAR:	return AMIXER_PCM_R;
+	default: 		return 0;
+	}
+}
+
+static int mixer_get_output_ports(struct ct_mixer *mixer,
+				  enum MIXER_PORT_T type,
+				  struct rsc **rleft, struct rsc **rright)
+{
+	enum CT_AMIXER_CTL amix = port_to_amixer(type);
+
+	if (NULL != rleft)
+		*rleft = &((struct amixer *)mixer->amixers[amix*CHN_NUM])->rsc;
+
+	if (NULL != rright)
+		*rright =
+			&((struct amixer *)mixer->amixers[amix*CHN_NUM+1])->rsc;
+
+	return 0;
+}
+
+static int mixer_set_input_left(struct ct_mixer *mixer,
+				enum MIXER_PORT_T type, struct rsc *rsc)
+{
+	enum CT_AMIXER_CTL amix = port_to_amixer(type);
+
+	mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc);
+	amix = get_recording_amixer(amix);
+	if (amix < NUM_CT_AMIXERS)
+		mixer_set_input_port(mixer->amixers[amix*CHN_NUM], rsc);
+
+	return 0;
+}
+
+static int
+mixer_set_input_right(struct ct_mixer *mixer,
+		      enum MIXER_PORT_T type, struct rsc *rsc)
+{
+	enum CT_AMIXER_CTL amix = port_to_amixer(type);
+
+	mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc);
+	amix = get_recording_amixer(amix);
+	if (amix < NUM_CT_AMIXERS)
+		mixer_set_input_port(mixer->amixers[amix*CHN_NUM+1], rsc);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int mixer_resume(struct ct_mixer *mixer)
+{
+	int i, state;
+	struct amixer *amixer;
+
+	/* resume topology and volume gain. */
+	for (i = 0; i < NUM_CT_AMIXERS*CHN_NUM; i++) {
+		amixer = mixer->amixers[i];
+		amixer->ops->commit_write(amixer);
+	}
+
+	/* resume switch state. */
+	for (i = SWH_MIXER_START; i <= SWH_MIXER_END; i++) {
+		state = get_switch_state(mixer, i);
+		do_switch(mixer->atc, i, state);
+	}
+
+	return 0;
+}
+#endif
+
+int ct_mixer_destroy(struct ct_mixer *mixer)
+{
+	struct sum_mgr *sum_mgr = (struct sum_mgr *)mixer->atc->rsc_mgrs[SUM];
+	struct amixer_mgr *amixer_mgr =
+			(struct amixer_mgr *)mixer->atc->rsc_mgrs[AMIXER];
+	struct amixer *amixer;
+	int i = 0;
+
+	/* Release amixer resources */
+	for (i = 0; i < (NUM_CT_AMIXERS * CHN_NUM); i++) {
+		if (NULL != mixer->amixers[i]) {
+			amixer = mixer->amixers[i];
+			amixer_mgr->put_amixer(amixer_mgr, amixer);
+		}
+	}
+
+	/* Release sum resources */
+	for (i = 0; i < (NUM_CT_SUMS * CHN_NUM); i++) {
+		if (NULL != mixer->sums[i])
+			sum_mgr->put_sum(sum_mgr, (struct sum *)mixer->sums[i]);
+	}
+
+	/* Release mem assigned to mixer object */
+	kfree(mixer->sums);
+	kfree(mixer->amixers);
+	kfree(mixer);
+
+	return 0;
+}
+
+int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer)
+{
+	struct ct_mixer *mixer;
+	int err;
+
+	*rmixer = NULL;
+
+	/* Allocate mem for mixer obj */
+	err = ct_mixer_get_mem(&mixer);
+	if (err)
+		return err;
+
+	mixer->switch_state = 0;
+	mixer->atc = atc;
+	/* Set operations */
+	mixer->get_output_ports = mixer_get_output_ports;
+	mixer->set_input_left = mixer_set_input_left;
+	mixer->set_input_right = mixer_set_input_right;
+#ifdef CONFIG_PM
+	mixer->resume = mixer_resume;
+#endif
+
+	/* Allocate chip resources for mixer obj */
+	err = ct_mixer_get_resources(mixer);
+	if (err)
+		goto error;
+
+	/* Build internal mixer topology */
+	ct_mixer_topology_build(mixer);
+
+	*rmixer = mixer;
+
+	return 0;
+
+error:
+	ct_mixer_destroy(mixer);
+	return err;
+}
+
+int ct_alsa_mix_create(struct ct_atc *atc,
+		       enum CTALSADEVS device,
+		       const char *device_name)
+{
+	int err;
+
+	/* Create snd kcontrol instances on demand */
+	/* vol_ctl.device = swh_ctl.device = device; */ /* better w/ device 0 */
+	err = ct_mixer_kcontrols_create((struct ct_mixer *)atc->mixer);
+	if (err)
+		return err;
+
+	strcpy(atc->card->mixername, device_name);
+
+	return 0;
+}
diff --git a/sound/pci/ctxfi/ctmixer.h b/sound/pci/ctxfi/ctmixer.h
new file mode 100644
index 0000000..b009e98
--- /dev/null
+++ b/sound/pci/ctxfi/ctmixer.h
@@ -0,0 +1,70 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctmixer.h
+ *
+ * @Brief
+ * This file contains the definition of the mixer device functions.
+ *
+ * @Author	Liu Chun
+ * @Date 	Mar 28 2008
+ *
+ */
+
+#ifndef CTMIXER_H
+#define CTMIXER_H
+
+#include "ctatc.h"
+#include "ctresource.h"
+
+#define INIT_VOL	0x1c00
+
+enum MIXER_PORT_T {
+	MIX_WAVE_FRONT,
+	MIX_WAVE_REAR,
+	MIX_WAVE_CENTLFE,
+	MIX_WAVE_SURROUND,
+	MIX_SPDIF_OUT,
+	MIX_PCMO_FRONT,
+	MIX_MIC_IN,
+	MIX_LINE_IN,
+	MIX_SPDIF_IN,
+	MIX_PCMI_FRONT,
+	MIX_PCMI_REAR,
+	MIX_PCMI_CENTLFE,
+	MIX_PCMI_SURROUND,
+
+	NUM_MIX_PORTS
+};
+
+/* alsa mixer descriptor */
+struct ct_mixer {
+	struct ct_atc *atc;
+
+	void **amixers;		/* amixer resources for volume control */
+	void **sums;		/* sum resources for signal collection */
+	unsigned int switch_state; /* A bit-map to indicate state of switches */
+
+	int (*get_output_ports)(struct ct_mixer *mixer, enum MIXER_PORT_T type,
+				  struct rsc **rleft, struct rsc **rright);
+
+	int (*set_input_left)(struct ct_mixer *mixer,
+			      enum MIXER_PORT_T type, struct rsc *rsc);
+	int (*set_input_right)(struct ct_mixer *mixer,
+			       enum MIXER_PORT_T type, struct rsc *rsc);
+#ifdef CONFIG_PM
+	int (*resume)(struct ct_mixer *mixer);
+#endif
+};
+
+int ct_alsa_mix_create(struct ct_atc *atc,
+		       enum CTALSADEVS device,
+		       const char *device_name);
+int ct_mixer_create(struct ct_atc *atc, struct ct_mixer **rmixer);
+int ct_mixer_destroy(struct ct_mixer *mixer);
+
+#endif /* CTMIXER_H */
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c
new file mode 100644
index 0000000..60ea231
--- /dev/null
+++ b/sound/pci/ctxfi/ctpcm.c
@@ -0,0 +1,430 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctpcm.c
+ *
+ * @Brief
+ * This file contains the definition of the pcm device functions.
+ *
+ * @Author	Liu Chun
+ * @Date 	Apr 2 2008
+ *
+ */
+
+#include "ctpcm.h"
+#include "cttimer.h"
+#include <sound/pcm.h>
+
+/* Hardware descriptions for playback */
+static struct snd_pcm_hardware ct_pcm_playback_hw = {
+	.info			= (SNDRV_PCM_INFO_MMAP |
+				   SNDRV_PCM_INFO_INTERLEAVED |
+				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				   SNDRV_PCM_INFO_MMAP_VALID |
+				   SNDRV_PCM_INFO_PAUSE),
+	.formats		= (SNDRV_PCM_FMTBIT_U8 |
+				   SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_3LE |
+				   SNDRV_PCM_FMTBIT_S32_LE |
+				   SNDRV_PCM_FMTBIT_FLOAT_LE),
+	.rates			= (SNDRV_PCM_RATE_CONTINUOUS |
+				   SNDRV_PCM_RATE_8000_192000),
+	.rate_min		= 8000,
+	.rate_max		= 192000,
+	.channels_min		= 1,
+	.channels_max		= 2,
+	.buffer_bytes_max	= (128*1024),
+	.period_bytes_min	= (64),
+	.period_bytes_max	= (128*1024),
+	.periods_min		= 2,
+	.periods_max		= 1024,
+	.fifo_size		= 0,
+};
+
+static struct snd_pcm_hardware ct_spdif_passthru_playback_hw = {
+	.info			= (SNDRV_PCM_INFO_MMAP |
+				   SNDRV_PCM_INFO_INTERLEAVED |
+				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				   SNDRV_PCM_INFO_MMAP_VALID |
+				   SNDRV_PCM_INFO_PAUSE),
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.rates			= (SNDRV_PCM_RATE_48000 |
+				   SNDRV_PCM_RATE_44100 |
+				   SNDRV_PCM_RATE_32000),
+	.rate_min		= 32000,
+	.rate_max		= 48000,
+	.channels_min		= 2,
+	.channels_max		= 2,
+	.buffer_bytes_max	= (128*1024),
+	.period_bytes_min	= (64),
+	.period_bytes_max	= (128*1024),
+	.periods_min		= 2,
+	.periods_max		= 1024,
+	.fifo_size		= 0,
+};
+
+/* Hardware descriptions for capture */
+static struct snd_pcm_hardware ct_pcm_capture_hw = {
+	.info			= (SNDRV_PCM_INFO_MMAP |
+				   SNDRV_PCM_INFO_INTERLEAVED |
+				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				   SNDRV_PCM_INFO_PAUSE |
+				   SNDRV_PCM_INFO_MMAP_VALID),
+	.formats		= (SNDRV_PCM_FMTBIT_U8 |
+				   SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_3LE |
+				   SNDRV_PCM_FMTBIT_S32_LE |
+				   SNDRV_PCM_FMTBIT_FLOAT_LE),
+	.rates			= (SNDRV_PCM_RATE_CONTINUOUS |
+				   SNDRV_PCM_RATE_8000_96000),
+	.rate_min		= 8000,
+	.rate_max		= 96000,
+	.channels_min		= 1,
+	.channels_max		= 2,
+	.buffer_bytes_max	= (128*1024),
+	.period_bytes_min	= (384),
+	.period_bytes_max	= (64*1024),
+	.periods_min		= 2,
+	.periods_max		= 1024,
+	.fifo_size		= 0,
+};
+
+static void ct_atc_pcm_interrupt(struct ct_atc_pcm *atc_pcm)
+{
+	struct ct_atc_pcm *apcm = atc_pcm;
+
+	if (NULL == apcm->substream)
+		return;
+
+	snd_pcm_period_elapsed(apcm->substream);
+}
+
+static void ct_atc_pcm_free_substream(struct snd_pcm_runtime *runtime)
+{
+	struct ct_atc_pcm *apcm = runtime->private_data;
+	struct ct_atc *atc = snd_pcm_substream_chip(apcm->substream);
+
+	atc->pcm_release_resources(atc, apcm);
+	ct_timer_instance_free(apcm->timer);
+	kfree(apcm);
+	runtime->private_data = NULL;
+}
+
+/* pcm playback operations */
+static int ct_pcm_playback_open(struct snd_pcm_substream *substream)
+{
+	struct ct_atc *atc = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ct_atc_pcm *apcm;
+	int err;
+
+	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
+	if (NULL == apcm)
+		return -ENOMEM;
+
+	apcm->substream = substream;
+	apcm->interrupt = ct_atc_pcm_interrupt;
+	runtime->private_data = apcm;
+	runtime->private_free = ct_atc_pcm_free_substream;
+	if (IEC958 == substream->pcm->device) {
+		runtime->hw = ct_spdif_passthru_playback_hw;
+		atc->spdif_out_passthru(atc, 1);
+	} else {
+		runtime->hw = ct_pcm_playback_hw;
+		if (FRONT == substream->pcm->device)
+			runtime->hw.channels_max = 8;
+	}
+
+	err = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (err < 0) {
+		kfree(apcm);
+		return err;
+	}
+	err = snd_pcm_hw_constraint_minmax(runtime,
+					   SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+					   1024, UINT_MAX);
+	if (err < 0) {
+		kfree(apcm);
+		return err;
+	}
+
+	apcm->timer = ct_timer_instance_new(atc->timer, apcm);
+	if (!apcm->timer)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int ct_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+	struct ct_atc *atc = snd_pcm_substream_chip(substream);
+
+	/* TODO: Notify mixer inactive. */
+	if (IEC958 == substream->pcm->device)
+		atc->spdif_out_passthru(atc, 0);
+
+	/* The ct_atc_pcm object will be freed by runtime->private_free */
+
+	return 0;
+}
+
+static int ct_pcm_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *hw_params)
+{
+	struct ct_atc *atc = snd_pcm_substream_chip(substream);
+	struct ct_atc_pcm *apcm = substream->runtime->private_data;
+	int err;
+
+	err = snd_pcm_lib_malloc_pages(substream,
+					params_buffer_bytes(hw_params));
+	if (err < 0)
+		return err;
+	/* clear previous resources */
+	atc->pcm_release_resources(atc, apcm);
+	return err;
+}
+
+static int ct_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct ct_atc *atc = snd_pcm_substream_chip(substream);
+	struct ct_atc_pcm *apcm = substream->runtime->private_data;
+
+	/* clear previous resources */
+	atc->pcm_release_resources(atc, apcm);
+	/* Free snd-allocated pages */
+	return snd_pcm_lib_free_pages(substream);
+}
+
+
+static int ct_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+	int err;
+	struct ct_atc *atc = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ct_atc_pcm *apcm = runtime->private_data;
+
+	if (IEC958 == substream->pcm->device)
+		err = atc->spdif_passthru_playback_prepare(atc, apcm);
+	else
+		err = atc->pcm_playback_prepare(atc, apcm);
+
+	if (err < 0) {
+		printk(KERN_ERR "ctxfi: Preparing pcm playback failed!!!\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static int
+ct_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct ct_atc *atc = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ct_atc_pcm *apcm = runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		atc->pcm_playback_start(atc, apcm);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		atc->pcm_playback_stop(atc, apcm);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t
+ct_pcm_playback_pointer(struct snd_pcm_substream *substream)
+{
+	unsigned long position;
+	struct ct_atc *atc = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ct_atc_pcm *apcm = runtime->private_data;
+
+	/* Read out playback position */
+	position = atc->pcm_playback_position(atc, apcm);
+	position = bytes_to_frames(runtime, position);
+	if (position >= runtime->buffer_size)
+		position = 0;
+	return position;
+}
+
+/* pcm capture operations */
+static int ct_pcm_capture_open(struct snd_pcm_substream *substream)
+{
+	struct ct_atc *atc = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ct_atc_pcm *apcm;
+	int err;
+
+	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
+	if (NULL == apcm)
+		return -ENOMEM;
+
+	apcm->started = 0;
+	apcm->substream = substream;
+	apcm->interrupt = ct_atc_pcm_interrupt;
+	runtime->private_data = apcm;
+	runtime->private_free = ct_atc_pcm_free_substream;
+	runtime->hw = ct_pcm_capture_hw;
+	runtime->hw.rate_max = atc->rsr * atc->msr;
+
+	err = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (err < 0) {
+		kfree(apcm);
+		return err;
+	}
+	err = snd_pcm_hw_constraint_minmax(runtime,
+					   SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+					   1024, UINT_MAX);
+	if (err < 0) {
+		kfree(apcm);
+		return err;
+	}
+
+	apcm->timer = ct_timer_instance_new(atc->timer, apcm);
+	if (!apcm->timer)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static int ct_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+	/* The ct_atc_pcm object will be freed by runtime->private_free */
+	/* TODO: Notify mixer inactive. */
+	return 0;
+}
+
+static int ct_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+	int err;
+	struct ct_atc *atc = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ct_atc_pcm *apcm = runtime->private_data;
+
+	err = atc->pcm_capture_prepare(atc, apcm);
+	if (err < 0) {
+		printk(KERN_ERR "ctxfi: Preparing pcm capture failed!!!\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static int
+ct_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct ct_atc *atc = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ct_atc_pcm *apcm = runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		atc->pcm_capture_start(atc, apcm);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		atc->pcm_capture_stop(atc, apcm);
+		break;
+	default:
+		atc->pcm_capture_stop(atc, apcm);
+		break;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t
+ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
+{
+	unsigned long position;
+	struct ct_atc *atc = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ct_atc_pcm *apcm = runtime->private_data;
+
+	/* Read out playback position */
+	position = atc->pcm_capture_position(atc, apcm);
+	position = bytes_to_frames(runtime, position);
+	if (position >= runtime->buffer_size)
+		position = 0;
+	return position;
+}
+
+/* PCM operators for playback */
+static struct snd_pcm_ops ct_pcm_playback_ops = {
+	.open	 	= ct_pcm_playback_open,
+	.close		= ct_pcm_playback_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= ct_pcm_hw_params,
+	.hw_free	= ct_pcm_hw_free,
+	.prepare	= ct_pcm_playback_prepare,
+	.trigger	= ct_pcm_playback_trigger,
+	.pointer	= ct_pcm_playback_pointer,
+	.page		= snd_pcm_sgbuf_ops_page,
+};
+
+/* PCM operators for capture */
+static struct snd_pcm_ops ct_pcm_capture_ops = {
+	.open	 	= ct_pcm_capture_open,
+	.close		= ct_pcm_capture_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= ct_pcm_hw_params,
+	.hw_free	= ct_pcm_hw_free,
+	.prepare	= ct_pcm_capture_prepare,
+	.trigger	= ct_pcm_capture_trigger,
+	.pointer	= ct_pcm_capture_pointer,
+	.page		= snd_pcm_sgbuf_ops_page,
+};
+
+/* Create ALSA pcm device */
+int ct_alsa_pcm_create(struct ct_atc *atc,
+		       enum CTALSADEVS device,
+		       const char *device_name)
+{
+	struct snd_pcm *pcm;
+	int err;
+	int playback_count, capture_count;
+
+	playback_count = (IEC958 == device) ? 1 : 8;
+	capture_count = (FRONT == device) ? 1 : 0;
+	err = snd_pcm_new(atc->card, "ctxfi", device,
+			  playback_count, capture_count, &pcm);
+	if (err < 0) {
+		printk(KERN_ERR "ctxfi: snd_pcm_new failed!! Err=%d\n", err);
+		return err;
+	}
+
+	pcm->private_data = atc;
+	pcm->info_flags = 0;
+	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
+	strlcpy(pcm->name, device_name, sizeof(pcm->name));
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ct_pcm_playback_ops);
+
+	if (FRONT == device)
+		snd_pcm_set_ops(pcm,
+				SNDRV_PCM_STREAM_CAPTURE, &ct_pcm_capture_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+			snd_dma_pci_data(atc->pci), 128*1024, 128*1024);
+
+#ifdef CONFIG_PM
+	atc->pcms[device] = pcm;
+#endif
+
+	return 0;
+}
diff --git a/sound/pci/ctxfi/ctpcm.h b/sound/pci/ctxfi/ctpcm.h
new file mode 100644
index 0000000..178da0d
--- /dev/null
+++ b/sound/pci/ctxfi/ctpcm.h
@@ -0,0 +1,27 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctpcm.h
+ *
+ * @Brief
+ * This file contains the definition of the pcm device functions.
+ *
+ * @Author	Liu Chun
+ * @Date 	Mar 28 2008
+ *
+ */
+
+#ifndef CTPCM_H
+#define CTPCM_H
+
+#include "ctatc.h"
+
+int ct_alsa_pcm_create(struct ct_atc *atc,
+		       enum CTALSADEVS device,
+		       const char *device_name);
+
+#endif /* CTPCM_H */
diff --git a/sound/pci/ctxfi/ctresource.c b/sound/pci/ctxfi/ctresource.c
new file mode 100644
index 0000000..889c495
--- /dev/null
+++ b/sound/pci/ctxfi/ctresource.c
@@ -0,0 +1,301 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctresource.c
+ *
+ * @Brief
+ * This file contains the implementation of some generic helper functions.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 15 2008
+ *
+ */
+
+#include "ctresource.h"
+#include "cthardware.h"
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#define AUDIO_SLOT_BLOCK_NUM 	256
+
+/* Resource allocation based on bit-map management mechanism */
+static int
+get_resource(u8 *rscs, unsigned int amount,
+	     unsigned int multi, unsigned int *ridx)
+{
+	int i, j, k, n;
+
+	/* Check whether there are sufficient resources to meet request. */
+	for (i = 0, n = multi; i < amount; i++) {
+		j = i / 8;
+		k = i % 8;
+		if (rscs[j] & ((u8)1 << k)) {
+			n = multi;
+			continue;
+		}
+		if (!(--n))
+			break; /* found sufficient contiguous resources */
+	}
+
+	if (i >= amount) {
+		/* Can not find sufficient contiguous resources */
+		return -ENOENT;
+	}
+
+	/* Mark the contiguous bits in resource bit-map as used */
+	for (n = multi; n > 0; n--) {
+		j = i / 8;
+		k = i % 8;
+		rscs[j] |= ((u8)1 << k);
+		i--;
+	}
+
+	*ridx = i + 1;
+
+	return 0;
+}
+
+static int put_resource(u8 *rscs, unsigned int multi, unsigned int idx)
+{
+	unsigned int i, j, k, n;
+
+	/* Mark the contiguous bits in resource bit-map as used */
+	for (n = multi, i = idx; n > 0; n--) {
+		j = i / 8;
+		k = i % 8;
+		rscs[j] &= ~((u8)1 << k);
+		i++;
+	}
+
+	return 0;
+}
+
+int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx)
+{
+	int err;
+
+	if (n > mgr->avail)
+		return -ENOENT;
+
+	err = get_resource(mgr->rscs, mgr->amount, n, ridx);
+	if (!err)
+		mgr->avail -= n;
+
+	return err;
+}
+
+int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx)
+{
+	put_resource(mgr->rscs, n, idx);
+	mgr->avail += n;
+
+	return 0;
+}
+
+static unsigned char offset_in_audio_slot_block[NUM_RSCTYP] = {
+	/* SRC channel is at Audio Ring slot 1 every 16 slots. */
+	[SRC]		= 0x1,
+	[AMIXER]	= 0x4,
+	[SUM]		= 0xc,
+};
+
+static int rsc_index(const struct rsc *rsc)
+{
+    return rsc->conj;
+}
+
+static int audio_ring_slot(const struct rsc *rsc)
+{
+    return (rsc->conj << 4) + offset_in_audio_slot_block[rsc->type];
+}
+
+static int rsc_next_conj(struct rsc *rsc)
+{
+	unsigned int i;
+	for (i = 0; (i < 8) && (!(rsc->msr & (0x1 << i))); )
+		i++;
+	rsc->conj += (AUDIO_SLOT_BLOCK_NUM >> i);
+	return rsc->conj;
+}
+
+static int rsc_master(struct rsc *rsc)
+{
+	return rsc->conj = rsc->idx;
+}
+
+static struct rsc_ops rsc_generic_ops = {
+	.index		= rsc_index,
+	.output_slot	= audio_ring_slot,
+	.master		= rsc_master,
+	.next_conj	= rsc_next_conj,
+};
+
+int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw)
+{
+	int err = 0;
+
+	rsc->idx = idx;
+	rsc->conj = idx;
+	rsc->type = type;
+	rsc->msr = msr;
+	rsc->hw = hw;
+	rsc->ops = &rsc_generic_ops;
+	if (NULL == hw) {
+		rsc->ctrl_blk = NULL;
+		return 0;
+	}
+
+	switch (type) {
+	case SRC:
+		err = ((struct hw *)hw)->src_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+		break;
+	case AMIXER:
+		err = ((struct hw *)hw)->
+				amixer_rsc_get_ctrl_blk(&rsc->ctrl_blk);
+		break;
+	case SRCIMP:
+	case SUM:
+	case DAIO:
+		break;
+	default:
+		printk(KERN_ERR
+		       "ctxfi: Invalid resource type value %d!\n", type);
+		return -EINVAL;
+	}
+
+	if (err) {
+		printk(KERN_ERR
+		       "ctxfi: Failed to get resource control block!\n");
+		return err;
+	}
+
+	return 0;
+}
+
+int rsc_uninit(struct rsc *rsc)
+{
+	if ((NULL != rsc->hw) && (NULL != rsc->ctrl_blk)) {
+		switch (rsc->type) {
+		case SRC:
+			((struct hw *)rsc->hw)->
+				src_rsc_put_ctrl_blk(rsc->ctrl_blk);
+			break;
+		case AMIXER:
+			((struct hw *)rsc->hw)->
+				amixer_rsc_put_ctrl_blk(rsc->ctrl_blk);
+			break;
+		case SUM:
+		case DAIO:
+			break;
+		default:
+			printk(KERN_ERR "ctxfi: "
+			       "Invalid resource type value %d!\n", rsc->type);
+			break;
+		}
+
+		rsc->hw = rsc->ctrl_blk = NULL;
+	}
+
+	rsc->idx = rsc->conj = 0;
+	rsc->type = NUM_RSCTYP;
+	rsc->msr = 0;
+
+	return 0;
+}
+
+int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
+		 unsigned int amount, void *hw_obj)
+{
+	int err = 0;
+	struct hw *hw = hw_obj;
+
+	mgr->type = NUM_RSCTYP;
+
+	mgr->rscs = kzalloc(((amount + 8 - 1) / 8), GFP_KERNEL);
+	if (NULL == mgr->rscs)
+		return -ENOMEM;
+
+	switch (type) {
+	case SRC:
+		err = hw->src_mgr_get_ctrl_blk(&mgr->ctrl_blk);
+		break;
+	case SRCIMP:
+		err = hw->srcimp_mgr_get_ctrl_blk(&mgr->ctrl_blk);
+		break;
+	case AMIXER:
+		err = hw->amixer_mgr_get_ctrl_blk(&mgr->ctrl_blk);
+		break;
+	case DAIO:
+		err = hw->daio_mgr_get_ctrl_blk(hw, &mgr->ctrl_blk);
+		break;
+	case SUM:
+		break;
+	default:
+		printk(KERN_ERR
+		       "ctxfi: Invalid resource type value %d!\n", type);
+		err = -EINVAL;
+		goto error;
+	}
+
+	if (err) {
+		printk(KERN_ERR
+		       "ctxfi: Failed to get manager control block!\n");
+		goto error;
+	}
+
+	mgr->type = type;
+	mgr->avail = mgr->amount = amount;
+	mgr->hw = hw;
+
+	return 0;
+
+error:
+	kfree(mgr->rscs);
+	return err;
+}
+
+int rsc_mgr_uninit(struct rsc_mgr *mgr)
+{
+	if (NULL != mgr->rscs) {
+		kfree(mgr->rscs);
+		mgr->rscs = NULL;
+	}
+
+	if ((NULL != mgr->hw) && (NULL != mgr->ctrl_blk)) {
+		switch (mgr->type) {
+		case SRC:
+			((struct hw *)mgr->hw)->
+				src_mgr_put_ctrl_blk(mgr->ctrl_blk);
+			break;
+		case SRCIMP:
+			((struct hw *)mgr->hw)->
+				srcimp_mgr_put_ctrl_blk(mgr->ctrl_blk);
+			break;
+		case AMIXER:
+			((struct hw *)mgr->hw)->
+				amixer_mgr_put_ctrl_blk(mgr->ctrl_blk);
+			break;
+		case DAIO:
+			((struct hw *)mgr->hw)->
+				daio_mgr_put_ctrl_blk(mgr->ctrl_blk);
+			break;
+		case SUM:
+			break;
+		default:
+			printk(KERN_ERR "ctxfi: "
+			       "Invalid resource type value %d!\n", mgr->type);
+			break;
+		}
+
+		mgr->hw = mgr->ctrl_blk = NULL;
+	}
+
+	mgr->type = NUM_RSCTYP;
+	mgr->avail = mgr->amount = 0;
+
+	return 0;
+}
diff --git a/sound/pci/ctxfi/ctresource.h b/sound/pci/ctxfi/ctresource.h
new file mode 100644
index 0000000..0838c2e
--- /dev/null
+++ b/sound/pci/ctxfi/ctresource.h
@@ -0,0 +1,72 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctresource.h
+ *
+ * @Brief
+ * This file contains the definition of generic hardware resources for
+ * resource management.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 13 2008
+ *
+ */
+
+#ifndef CTRESOURCE_H
+#define CTRESOURCE_H
+
+#include <linux/types.h>
+
+enum RSCTYP {
+	SRC,
+	SRCIMP,
+	AMIXER,
+	SUM,
+	DAIO,
+	NUM_RSCTYP	/* This must be the last one and less than 16 */
+};
+
+struct rsc_ops;
+
+struct rsc {
+	u32 idx:12;	/* The index of a resource */
+	u32 type:4;	/* The type (RSCTYP) of a resource */
+	u32 conj:12;	/* Current conjugate index */
+	u32 msr:4;	/* The Master Sample Rate a resource working on */
+	void *ctrl_blk;	/* Chip specific control info block for a resource */
+	void *hw;	/* Chip specific object for hardware access means */
+	struct rsc_ops *ops;	/* Generic resource operations */
+};
+
+struct rsc_ops {
+	int (*master)(struct rsc *rsc);	/* Move to master resource */
+	int (*next_conj)(struct rsc *rsc); /* Move to next conjugate resource */
+	int (*index)(const struct rsc *rsc); /* Return the index of resource */
+	/* Return the output slot number */
+	int (*output_slot)(const struct rsc *rsc);
+};
+
+int rsc_init(struct rsc *rsc, u32 idx, enum RSCTYP type, u32 msr, void *hw);
+int rsc_uninit(struct rsc *rsc);
+
+struct rsc_mgr {
+	enum RSCTYP type; /* The type (RSCTYP) of resource to manage */
+	unsigned int amount; /* The total amount of a kind of resource */
+	unsigned int avail; /* The amount of currently available resources */
+	unsigned char *rscs; /* The bit-map for resource allocation */
+	void *ctrl_blk; /* Chip specific control info block */
+	void *hw; /* Chip specific object for hardware access */
+};
+
+/* Resource management is based on bit-map mechanism */
+int rsc_mgr_init(struct rsc_mgr *mgr, enum RSCTYP type,
+		 unsigned int amount, void *hw);
+int rsc_mgr_uninit(struct rsc_mgr *mgr);
+int mgr_get_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int *ridx);
+int mgr_put_resource(struct rsc_mgr *mgr, unsigned int n, unsigned int idx);
+
+#endif /* CTRESOURCE_H */
diff --git a/sound/pci/ctxfi/ctsrc.c b/sound/pci/ctxfi/ctsrc.c
new file mode 100644
index 0000000..e1c145d
--- /dev/null
+++ b/sound/pci/ctxfi/ctsrc.c
@@ -0,0 +1,886 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctsrc.c
+ *
+ * @Brief
+ * This file contains the implementation of the Sample Rate Convertor
+ * resource management object.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 13 2008
+ *
+ */
+
+#include "ctsrc.h"
+#include "cthardware.h"
+#include <linux/slab.h>
+
+#define SRC_RESOURCE_NUM	64
+#define SRCIMP_RESOURCE_NUM	256
+
+static unsigned int conj_mask;
+
+static int src_default_config_memrd(struct src *src);
+static int src_default_config_memwr(struct src *src);
+static int src_default_config_arcrw(struct src *src);
+
+static int (*src_default_config[3])(struct src *) = {
+	[MEMRD] = src_default_config_memrd,
+	[MEMWR] = src_default_config_memwr,
+	[ARCRW] = src_default_config_arcrw
+};
+
+static int src_set_state(struct src *src, unsigned int state)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_state(src->rsc.ctrl_blk, state);
+
+	return 0;
+}
+
+static int src_set_bm(struct src *src, unsigned int bm)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_bm(src->rsc.ctrl_blk, bm);
+
+	return 0;
+}
+
+static int src_set_sf(struct src *src, unsigned int sf)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_sf(src->rsc.ctrl_blk, sf);
+
+	return 0;
+}
+
+static int src_set_pm(struct src *src, unsigned int pm)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_pm(src->rsc.ctrl_blk, pm);
+
+	return 0;
+}
+
+static int src_set_rom(struct src *src, unsigned int rom)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_rom(src->rsc.ctrl_blk, rom);
+
+	return 0;
+}
+
+static int src_set_vo(struct src *src, unsigned int vo)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_vo(src->rsc.ctrl_blk, vo);
+
+	return 0;
+}
+
+static int src_set_st(struct src *src, unsigned int st)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_st(src->rsc.ctrl_blk, st);
+
+	return 0;
+}
+
+static int src_set_bp(struct src *src, unsigned int bp)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_bp(src->rsc.ctrl_blk, bp);
+
+	return 0;
+}
+
+static int src_set_cisz(struct src *src, unsigned int cisz)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_cisz(src->rsc.ctrl_blk, cisz);
+
+	return 0;
+}
+
+static int src_set_ca(struct src *src, unsigned int ca)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_ca(src->rsc.ctrl_blk, ca);
+
+	return 0;
+}
+
+static int src_set_sa(struct src *src, unsigned int sa)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_sa(src->rsc.ctrl_blk, sa);
+
+	return 0;
+}
+
+static int src_set_la(struct src *src, unsigned int la)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_la(src->rsc.ctrl_blk, la);
+
+	return 0;
+}
+
+static int src_set_pitch(struct src *src, unsigned int pitch)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_pitch(src->rsc.ctrl_blk, pitch);
+
+	return 0;
+}
+
+static int src_set_clear_zbufs(struct src *src)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+	return 0;
+}
+
+static int src_commit_write(struct src *src)
+{
+	struct hw *hw;
+	int i;
+	unsigned int dirty = 0;
+
+	hw = src->rsc.hw;
+	src->rsc.ops->master(&src->rsc);
+	if (src->rsc.msr > 1) {
+		/* Save dirty flags for conjugate resource programming */
+		dirty = hw->src_get_dirty(src->rsc.ctrl_blk) & conj_mask;
+	}
+	hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+						src->rsc.ctrl_blk);
+
+	/* Program conjugate parameter mixer resources */
+	if (MEMWR == src->mode)
+		return 0;
+
+	for (i = 1; i < src->rsc.msr; i++) {
+		src->rsc.ops->next_conj(&src->rsc);
+		hw->src_set_dirty(src->rsc.ctrl_blk, dirty);
+		hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+							src->rsc.ctrl_blk);
+	}
+	src->rsc.ops->master(&src->rsc);
+
+	return 0;
+}
+
+static int src_get_ca(struct src *src)
+{
+	struct hw *hw;
+
+	hw = src->rsc.hw;
+	return hw->src_get_ca(hw, src->rsc.ops->index(&src->rsc),
+						src->rsc.ctrl_blk);
+}
+
+static int src_init(struct src *src)
+{
+	src_default_config[src->mode](src);
+
+	return 0;
+}
+
+static struct src *src_next_interleave(struct src *src)
+{
+	return src->intlv;
+}
+
+static int src_default_config_memrd(struct src *src)
+{
+	struct hw *hw = src->rsc.hw;
+	unsigned int rsr, msr;
+
+	hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
+	hw->src_set_bm(src->rsc.ctrl_blk, 1);
+	for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1)
+		rsr++;
+
+	hw->src_set_rsr(src->rsc.ctrl_blk, rsr);
+	hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16);
+	hw->src_set_wr(src->rsc.ctrl_blk, 0);
+	hw->src_set_pm(src->rsc.ctrl_blk, 0);
+	hw->src_set_rom(src->rsc.ctrl_blk, 0);
+	hw->src_set_vo(src->rsc.ctrl_blk, 0);
+	hw->src_set_st(src->rsc.ctrl_blk, 0);
+	hw->src_set_ilsz(src->rsc.ctrl_blk, src->multi - 1);
+	hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
+	hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
+	hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
+	hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
+	hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+	hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+	src->rsc.ops->master(&src->rsc);
+	hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+						src->rsc.ctrl_blk);
+
+	for (msr = 1; msr < src->rsc.msr; msr++) {
+		src->rsc.ops->next_conj(&src->rsc);
+		hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+		hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+							src->rsc.ctrl_blk);
+	}
+	src->rsc.ops->master(&src->rsc);
+
+	return 0;
+}
+
+static int src_default_config_memwr(struct src *src)
+{
+	struct hw *hw = src->rsc.hw;
+
+	hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
+	hw->src_set_bm(src->rsc.ctrl_blk, 1);
+	hw->src_set_rsr(src->rsc.ctrl_blk, 0);
+	hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_S16);
+	hw->src_set_wr(src->rsc.ctrl_blk, 1);
+	hw->src_set_pm(src->rsc.ctrl_blk, 0);
+	hw->src_set_rom(src->rsc.ctrl_blk, 0);
+	hw->src_set_vo(src->rsc.ctrl_blk, 0);
+	hw->src_set_st(src->rsc.ctrl_blk, 0);
+	hw->src_set_ilsz(src->rsc.ctrl_blk, 0);
+	hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
+	hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
+	hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
+	hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
+	hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+	hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+	src->rsc.ops->master(&src->rsc);
+	hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+						src->rsc.ctrl_blk);
+
+	return 0;
+}
+
+static int src_default_config_arcrw(struct src *src)
+{
+	struct hw *hw = src->rsc.hw;
+	unsigned int rsr, msr;
+	unsigned int dirty;
+
+	hw->src_set_state(src->rsc.ctrl_blk, SRC_STATE_OFF);
+	hw->src_set_bm(src->rsc.ctrl_blk, 0);
+	for (rsr = 0, msr = src->rsc.msr; msr > 1; msr >>= 1)
+		rsr++;
+
+	hw->src_set_rsr(src->rsc.ctrl_blk, rsr);
+	hw->src_set_sf(src->rsc.ctrl_blk, SRC_SF_F32);
+	hw->src_set_wr(src->rsc.ctrl_blk, 0);
+	hw->src_set_pm(src->rsc.ctrl_blk, 0);
+	hw->src_set_rom(src->rsc.ctrl_blk, 0);
+	hw->src_set_vo(src->rsc.ctrl_blk, 0);
+	hw->src_set_st(src->rsc.ctrl_blk, 0);
+	hw->src_set_ilsz(src->rsc.ctrl_blk, 0);
+	hw->src_set_cisz(src->rsc.ctrl_blk, 0x80);
+	hw->src_set_sa(src->rsc.ctrl_blk, 0x0);
+	/*hw->src_set_sa(src->rsc.ctrl_blk, 0x100);*/
+	hw->src_set_la(src->rsc.ctrl_blk, 0x1000);
+	/*hw->src_set_la(src->rsc.ctrl_blk, 0x03ffffe0);*/
+	hw->src_set_ca(src->rsc.ctrl_blk, 0x80);
+	hw->src_set_pitch(src->rsc.ctrl_blk, 0x1000000);
+	hw->src_set_clear_zbufs(src->rsc.ctrl_blk, 1);
+
+	dirty = hw->src_get_dirty(src->rsc.ctrl_blk);
+	src->rsc.ops->master(&src->rsc);
+	for (msr = 0; msr < src->rsc.msr; msr++) {
+		hw->src_set_dirty(src->rsc.ctrl_blk, dirty);
+		hw->src_commit_write(hw, src->rsc.ops->index(&src->rsc),
+							src->rsc.ctrl_blk);
+		src->rsc.ops->next_conj(&src->rsc);
+	}
+	src->rsc.ops->master(&src->rsc);
+
+	return 0;
+}
+
+static struct src_rsc_ops src_rsc_ops = {
+	.set_state		= src_set_state,
+	.set_bm			= src_set_bm,
+	.set_sf			= src_set_sf,
+	.set_pm			= src_set_pm,
+	.set_rom		= src_set_rom,
+	.set_vo			= src_set_vo,
+	.set_st			= src_set_st,
+	.set_bp			= src_set_bp,
+	.set_cisz		= src_set_cisz,
+	.set_ca			= src_set_ca,
+	.set_sa			= src_set_sa,
+	.set_la			= src_set_la,
+	.set_pitch		= src_set_pitch,
+	.set_clr_zbufs		= src_set_clear_zbufs,
+	.commit_write		= src_commit_write,
+	.get_ca			= src_get_ca,
+	.init			= src_init,
+	.next_interleave	= src_next_interleave,
+};
+
+static int
+src_rsc_init(struct src *src, u32 idx,
+	     const struct src_desc *desc, struct src_mgr *mgr)
+{
+	int err;
+	int i, n;
+	struct src *p;
+
+	n = (MEMRD == desc->mode) ? desc->multi : 1;
+	for (i = 0, p = src; i < n; i++, p++) {
+		err = rsc_init(&p->rsc, idx + i, SRC, desc->msr, mgr->mgr.hw);
+		if (err)
+			goto error1;
+
+		/* Initialize src specific rsc operations */
+		p->ops = &src_rsc_ops;
+		p->multi = (0 == i) ? desc->multi : 1;
+		p->mode = desc->mode;
+		src_default_config[desc->mode](p);
+		mgr->src_enable(mgr, p);
+		p->intlv = p + 1;
+	}
+	(--p)->intlv = NULL;	/* Set @intlv of the last SRC to NULL */
+
+	mgr->commit_write(mgr);
+
+	return 0;
+
+error1:
+	for (i--, p--; i >= 0; i--, p--) {
+		mgr->src_disable(mgr, p);
+		rsc_uninit(&p->rsc);
+	}
+	mgr->commit_write(mgr);
+	return err;
+}
+
+static int src_rsc_uninit(struct src *src, struct src_mgr *mgr)
+{
+	int i, n;
+	struct src *p;
+
+	n = (MEMRD == src->mode) ? src->multi : 1;
+	for (i = 0, p = src; i < n; i++, p++) {
+		mgr->src_disable(mgr, p);
+		rsc_uninit(&p->rsc);
+		p->multi = 0;
+		p->ops = NULL;
+		p->mode = NUM_SRCMODES;
+		p->intlv = NULL;
+	}
+	mgr->commit_write(mgr);
+
+	return 0;
+}
+
+static int
+get_src_rsc(struct src_mgr *mgr, const struct src_desc *desc, struct src **rsrc)
+{
+	unsigned int idx = SRC_RESOURCE_NUM;
+	int err;
+	struct src *src;
+	unsigned long flags;
+
+	*rsrc = NULL;
+
+	/* Check whether there are sufficient src resources to meet request. */
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	if (MEMRD == desc->mode)
+		err = mgr_get_resource(&mgr->mgr, desc->multi, &idx);
+	else
+		err = mgr_get_resource(&mgr->mgr, 1, &idx);
+
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	if (err) {
+		printk(KERN_ERR "ctxfi: Can't meet SRC resource request!\n");
+		return err;
+	}
+
+	/* Allocate mem for master src resource */
+	if (MEMRD == desc->mode)
+		src = kzalloc(sizeof(*src)*desc->multi, GFP_KERNEL);
+	else
+		src = kzalloc(sizeof(*src), GFP_KERNEL);
+
+	if (NULL == src) {
+		err = -ENOMEM;
+		goto error1;
+	}
+
+	err = src_rsc_init(src, idx, desc, mgr);
+	if (err)
+		goto error2;
+
+	*rsrc = src;
+
+	return 0;
+
+error2:
+	kfree(src);
+error1:
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	if (MEMRD == desc->mode)
+		mgr_put_resource(&mgr->mgr, desc->multi, idx);
+	else
+		mgr_put_resource(&mgr->mgr, 1, idx);
+
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	return err;
+}
+
+static int put_src_rsc(struct src_mgr *mgr, struct src *src)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	src->rsc.ops->master(&src->rsc);
+	if (MEMRD == src->mode)
+		mgr_put_resource(&mgr->mgr, src->multi,
+				 src->rsc.ops->index(&src->rsc));
+	else
+		mgr_put_resource(&mgr->mgr, 1, src->rsc.ops->index(&src->rsc));
+
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	src_rsc_uninit(src, mgr);
+	kfree(src);
+
+	return 0;
+}
+
+static int src_enable_s(struct src_mgr *mgr, struct src *src)
+{
+	struct hw *hw = mgr->mgr.hw;
+	int i;
+
+	src->rsc.ops->master(&src->rsc);
+	for (i = 0; i < src->rsc.msr; i++) {
+		hw->src_mgr_enbs_src(mgr->mgr.ctrl_blk,
+				     src->rsc.ops->index(&src->rsc));
+		src->rsc.ops->next_conj(&src->rsc);
+	}
+	src->rsc.ops->master(&src->rsc);
+
+	return 0;
+}
+
+static int src_enable(struct src_mgr *mgr, struct src *src)
+{
+	struct hw *hw = mgr->mgr.hw;
+	int i;
+
+	src->rsc.ops->master(&src->rsc);
+	for (i = 0; i < src->rsc.msr; i++) {
+		hw->src_mgr_enb_src(mgr->mgr.ctrl_blk,
+				    src->rsc.ops->index(&src->rsc));
+		src->rsc.ops->next_conj(&src->rsc);
+	}
+	src->rsc.ops->master(&src->rsc);
+
+	return 0;
+}
+
+static int src_disable(struct src_mgr *mgr, struct src *src)
+{
+	struct hw *hw = mgr->mgr.hw;
+	int i;
+
+	src->rsc.ops->master(&src->rsc);
+	for (i = 0; i < src->rsc.msr; i++) {
+		hw->src_mgr_dsb_src(mgr->mgr.ctrl_blk,
+				    src->rsc.ops->index(&src->rsc));
+		src->rsc.ops->next_conj(&src->rsc);
+	}
+	src->rsc.ops->master(&src->rsc);
+
+	return 0;
+}
+
+static int src_mgr_commit_write(struct src_mgr *mgr)
+{
+	struct hw *hw = mgr->mgr.hw;
+
+	hw->src_mgr_commit_write(hw, mgr->mgr.ctrl_blk);
+
+	return 0;
+}
+
+int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr)
+{
+	int err, i;
+	struct src_mgr *src_mgr;
+
+	*rsrc_mgr = NULL;
+	src_mgr = kzalloc(sizeof(*src_mgr), GFP_KERNEL);
+	if (NULL == src_mgr)
+		return -ENOMEM;
+
+	err = rsc_mgr_init(&src_mgr->mgr, SRC, SRC_RESOURCE_NUM, hw);
+	if (err)
+		goto error1;
+
+	spin_lock_init(&src_mgr->mgr_lock);
+	conj_mask = ((struct hw *)hw)->src_dirty_conj_mask();
+
+	src_mgr->get_src = get_src_rsc;
+	src_mgr->put_src = put_src_rsc;
+	src_mgr->src_enable_s = src_enable_s;
+	src_mgr->src_enable = src_enable;
+	src_mgr->src_disable = src_disable;
+	src_mgr->commit_write = src_mgr_commit_write;
+
+	/* Disable all SRC resources. */
+	for (i = 0; i < 256; i++)
+		((struct hw *)hw)->src_mgr_dsb_src(src_mgr->mgr.ctrl_blk, i);
+
+	((struct hw *)hw)->src_mgr_commit_write(hw, src_mgr->mgr.ctrl_blk);
+
+	*rsrc_mgr = src_mgr;
+
+	return 0;
+
+error1:
+	kfree(src_mgr);
+	return err;
+}
+
+int src_mgr_destroy(struct src_mgr *src_mgr)
+{
+	rsc_mgr_uninit(&src_mgr->mgr);
+	kfree(src_mgr);
+
+	return 0;
+}
+
+/* SRCIMP resource manager operations */
+
+static int srcimp_master(struct rsc *rsc)
+{
+	rsc->conj = 0;
+	return rsc->idx = container_of(rsc, struct srcimp, rsc)->idx[0];
+}
+
+static int srcimp_next_conj(struct rsc *rsc)
+{
+	rsc->conj++;
+	return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
+}
+
+static int srcimp_index(const struct rsc *rsc)
+{
+	return container_of(rsc, struct srcimp, rsc)->idx[rsc->conj];
+}
+
+static struct rsc_ops srcimp_basic_rsc_ops = {
+	.master		= srcimp_master,
+	.next_conj	= srcimp_next_conj,
+	.index		= srcimp_index,
+	.output_slot	= NULL,
+};
+
+static int srcimp_map(struct srcimp *srcimp, struct src *src, struct rsc *input)
+{
+	struct imapper *entry;
+	int i;
+
+	srcimp->rsc.ops->master(&srcimp->rsc);
+	src->rsc.ops->master(&src->rsc);
+	input->ops->master(input);
+
+	/* Program master and conjugate resources */
+	for (i = 0; i < srcimp->rsc.msr; i++) {
+		entry = &srcimp->imappers[i];
+		entry->slot = input->ops->output_slot(input);
+		entry->user = src->rsc.ops->index(&src->rsc);
+		entry->addr = srcimp->rsc.ops->index(&srcimp->rsc);
+		srcimp->mgr->imap_add(srcimp->mgr, entry);
+		srcimp->mapped |= (0x1 << i);
+
+		srcimp->rsc.ops->next_conj(&srcimp->rsc);
+		input->ops->next_conj(input);
+	}
+
+	srcimp->rsc.ops->master(&srcimp->rsc);
+	input->ops->master(input);
+
+	return 0;
+}
+
+static int srcimp_unmap(struct srcimp *srcimp)
+{
+	int i;
+
+	/* Program master and conjugate resources */
+	for (i = 0; i < srcimp->rsc.msr; i++) {
+		if (srcimp->mapped & (0x1 << i)) {
+			srcimp->mgr->imap_delete(srcimp->mgr,
+						 &srcimp->imappers[i]);
+			srcimp->mapped &= ~(0x1 << i);
+		}
+	}
+
+	return 0;
+}
+
+static struct srcimp_rsc_ops srcimp_ops = {
+	.map = srcimp_map,
+	.unmap = srcimp_unmap
+};
+
+static int srcimp_rsc_init(struct srcimp *srcimp,
+			   const struct srcimp_desc *desc,
+			   struct srcimp_mgr *mgr)
+{
+	int err;
+
+	err = rsc_init(&srcimp->rsc, srcimp->idx[0],
+		       SRCIMP, desc->msr, mgr->mgr.hw);
+	if (err)
+		return err;
+
+	/* Reserve memory for imapper nodes */
+	srcimp->imappers = kzalloc(sizeof(struct imapper)*desc->msr,
+				   GFP_KERNEL);
+	if (NULL == srcimp->imappers) {
+		err = -ENOMEM;
+		goto error1;
+	}
+
+	/* Set srcimp specific operations */
+	srcimp->rsc.ops = &srcimp_basic_rsc_ops;
+	srcimp->ops = &srcimp_ops;
+	srcimp->mgr = mgr;
+
+	srcimp->rsc.ops->master(&srcimp->rsc);
+
+	return 0;
+
+error1:
+	rsc_uninit(&srcimp->rsc);
+	return err;
+}
+
+static int srcimp_rsc_uninit(struct srcimp *srcimp)
+{
+	if (NULL != srcimp->imappers) {
+		kfree(srcimp->imappers);
+		srcimp->imappers = NULL;
+	}
+	srcimp->ops = NULL;
+	srcimp->mgr = NULL;
+	rsc_uninit(&srcimp->rsc);
+
+	return 0;
+}
+
+static int get_srcimp_rsc(struct srcimp_mgr *mgr,
+			  const struct srcimp_desc *desc,
+			  struct srcimp **rsrcimp)
+{
+	int err, i;
+	unsigned int idx;
+	struct srcimp *srcimp;
+	unsigned long flags;
+
+	*rsrcimp = NULL;
+
+	/* Allocate mem for SRCIMP resource */
+	srcimp = kzalloc(sizeof(*srcimp), GFP_KERNEL);
+	if (NULL == srcimp) {
+		err = -ENOMEM;
+		return err;
+	}
+
+	/* Check whether there are sufficient SRCIMP resources. */
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	for (i = 0; i < desc->msr; i++) {
+		err = mgr_get_resource(&mgr->mgr, 1, &idx);
+		if (err)
+			break;
+
+		srcimp->idx[i] = idx;
+	}
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	if (err) {
+		printk(KERN_ERR "ctxfi: Can't meet SRCIMP resource request!\n");
+		goto error1;
+	}
+
+	err = srcimp_rsc_init(srcimp, desc, mgr);
+	if (err)
+		goto error1;
+
+	*rsrcimp = srcimp;
+
+	return 0;
+
+error1:
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	for (i--; i >= 0; i--)
+		mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
+
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	kfree(srcimp);
+	return err;
+}
+
+static int put_srcimp_rsc(struct srcimp_mgr *mgr, struct srcimp *srcimp)
+{
+	unsigned long flags;
+	int i;
+
+	spin_lock_irqsave(&mgr->mgr_lock, flags);
+	for (i = 0; i < srcimp->rsc.msr; i++)
+		mgr_put_resource(&mgr->mgr, 1, srcimp->idx[i]);
+
+	spin_unlock_irqrestore(&mgr->mgr_lock, flags);
+	srcimp_rsc_uninit(srcimp);
+	kfree(srcimp);
+
+	return 0;
+}
+
+static int srcimp_map_op(void *data, struct imapper *entry)
+{
+	struct rsc_mgr *mgr = &((struct srcimp_mgr *)data)->mgr;
+	struct hw *hw = mgr->hw;
+
+	hw->srcimp_mgr_set_imaparc(mgr->ctrl_blk, entry->slot);
+	hw->srcimp_mgr_set_imapuser(mgr->ctrl_blk, entry->user);
+	hw->srcimp_mgr_set_imapnxt(mgr->ctrl_blk, entry->next);
+	hw->srcimp_mgr_set_imapaddr(mgr->ctrl_blk, entry->addr);
+	hw->srcimp_mgr_commit_write(mgr->hw, mgr->ctrl_blk);
+
+	return 0;
+}
+
+static int srcimp_imap_add(struct srcimp_mgr *mgr, struct imapper *entry)
+{
+	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&mgr->imap_lock, flags);
+	if ((0 == entry->addr) && (mgr->init_imap_added)) {
+		input_mapper_delete(&mgr->imappers,
+				    mgr->init_imap, srcimp_map_op, mgr);
+		mgr->init_imap_added = 0;
+	}
+	err = input_mapper_add(&mgr->imappers, entry, srcimp_map_op, mgr);
+	spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+	return err;
+}
+
+static int srcimp_imap_delete(struct srcimp_mgr *mgr, struct imapper *entry)
+{
+	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&mgr->imap_lock, flags);
+	err = input_mapper_delete(&mgr->imappers, entry, srcimp_map_op, mgr);
+	if (list_empty(&mgr->imappers)) {
+		input_mapper_add(&mgr->imappers, mgr->init_imap,
+				 srcimp_map_op, mgr);
+		mgr->init_imap_added = 1;
+	}
+	spin_unlock_irqrestore(&mgr->imap_lock, flags);
+
+	return err;
+}
+
+int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrcimp_mgr)
+{
+	int err;
+	struct srcimp_mgr *srcimp_mgr;
+	struct imapper *entry;
+
+	*rsrcimp_mgr = NULL;
+	srcimp_mgr = kzalloc(sizeof(*srcimp_mgr), GFP_KERNEL);
+	if (NULL == srcimp_mgr)
+		return -ENOMEM;
+
+	err = rsc_mgr_init(&srcimp_mgr->mgr, SRCIMP, SRCIMP_RESOURCE_NUM, hw);
+	if (err)
+		goto error1;
+
+	spin_lock_init(&srcimp_mgr->mgr_lock);
+	spin_lock_init(&srcimp_mgr->imap_lock);
+	INIT_LIST_HEAD(&srcimp_mgr->imappers);
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (NULL == entry) {
+		err = -ENOMEM;
+		goto error2;
+	}
+	entry->slot = entry->addr = entry->next = entry->user = 0;
+	list_add(&entry->list, &srcimp_mgr->imappers);
+	srcimp_mgr->init_imap = entry;
+	srcimp_mgr->init_imap_added = 1;
+
+	srcimp_mgr->get_srcimp = get_srcimp_rsc;
+	srcimp_mgr->put_srcimp = put_srcimp_rsc;
+	srcimp_mgr->imap_add = srcimp_imap_add;
+	srcimp_mgr->imap_delete = srcimp_imap_delete;
+
+	*rsrcimp_mgr = srcimp_mgr;
+
+	return 0;
+
+error2:
+	rsc_mgr_uninit(&srcimp_mgr->mgr);
+error1:
+	kfree(srcimp_mgr);
+	return err;
+}
+
+int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr)
+{
+	unsigned long flags;
+
+	/* free src input mapper list */
+	spin_lock_irqsave(&srcimp_mgr->imap_lock, flags);
+	free_input_mapper_list(&srcimp_mgr->imappers);
+	spin_unlock_irqrestore(&srcimp_mgr->imap_lock, flags);
+
+	rsc_mgr_uninit(&srcimp_mgr->mgr);
+	kfree(srcimp_mgr);
+
+	return 0;
+}
diff --git a/sound/pci/ctxfi/ctsrc.h b/sound/pci/ctxfi/ctsrc.h
new file mode 100644
index 0000000..259366a
--- /dev/null
+++ b/sound/pci/ctxfi/ctsrc.h
@@ -0,0 +1,149 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File	ctsrc.h
+ *
+ * @Brief
+ * This file contains the definition of the Sample Rate Convertor
+ * resource management object.
+ *
+ * @Author	Liu Chun
+ * @Date 	May 13 2008
+ *
+ */
+
+#ifndef CTSRC_H
+#define CTSRC_H
+
+#include "ctresource.h"
+#include "ctimap.h"
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+#define SRC_STATE_OFF	0x0
+#define SRC_STATE_INIT	0x4
+#define SRC_STATE_RUN	0x5
+
+#define SRC_SF_U8	0x0
+#define SRC_SF_S16	0x1
+#define SRC_SF_S24	0x2
+#define SRC_SF_S32	0x3
+#define SRC_SF_F32	0x4
+
+/* Define the descriptor of a src resource */
+enum SRCMODE {
+	MEMRD,		/* Read data from host memory */
+	MEMWR,		/* Write data to host memory */
+	ARCRW,		/* Read from and write to audio ring channel */
+	NUM_SRCMODES
+};
+
+struct src_rsc_ops;
+
+struct src {
+	struct rsc rsc; /* Basic resource info */
+	struct src *intlv; /* Pointer to next interleaved SRC in a series */
+	struct src_rsc_ops *ops; /* SRC specific operations */
+	/* Number of contiguous srcs for interleaved usage */
+	unsigned char multi;
+	unsigned char mode; /* Working mode of this SRC resource */
+};
+
+struct src_rsc_ops {
+	int (*set_state)(struct src *src, unsigned int state);
+	int (*set_bm)(struct src *src, unsigned int bm);
+	int (*set_sf)(struct src *src, unsigned int sf);
+	int (*set_pm)(struct src *src, unsigned int pm);
+	int (*set_rom)(struct src *src, unsigned int rom);
+	int (*set_vo)(struct src *src, unsigned int vo);
+	int (*set_st)(struct src *src, unsigned int st);
+	int (*set_bp)(struct src *src, unsigned int bp);
+	int (*set_cisz)(struct src *src, unsigned int cisz);
+	int (*set_ca)(struct src *src, unsigned int ca);
+	int (*set_sa)(struct src *src, unsigned int sa);
+	int (*set_la)(struct src *src, unsigned int la);
+	int (*set_pitch)(struct src *src, unsigned int pitch);
+	int (*set_clr_zbufs)(struct src *src);
+	int (*commit_write)(struct src *src);
+	int (*get_ca)(struct src *src);
+	int (*init)(struct src *src);
+	struct src* (*next_interleave)(struct src *src);
+};
+
+/* Define src resource request description info */
+struct src_desc {
+	/* Number of contiguous master srcs for interleaved usage */
+	unsigned char multi;
+	unsigned char msr;
+	unsigned char mode; /* Working mode of the requested srcs */
+};
+
+/* Define src manager object */
+struct src_mgr {
+	struct rsc_mgr mgr;	/* Basic resource manager info */
+	spinlock_t mgr_lock;
+
+	 /* request src resource */
+	int (*get_src)(struct src_mgr *mgr,
+		       const struct src_desc *desc, struct src **rsrc);
+	/* return src resource */
+	int (*put_src)(struct src_mgr *mgr, struct src *src);
+	int (*src_enable_s)(struct src_mgr *mgr, struct src *src);
+	int (*src_enable)(struct src_mgr *mgr, struct src *src);
+	int (*src_disable)(struct src_mgr *mgr, struct src *src);
+	int (*commit_write)(struct src_mgr *mgr);
+};
+
+/* Define the descriptor of a SRC Input Mapper resource */
+struct srcimp_mgr;
+struct srcimp_rsc_ops;
+
+struct srcimp {
+	struct rsc rsc;
+	unsigned char idx[8];
+	struct imapper *imappers;
+	unsigned int mapped; /* A bit-map indicating which conj rsc is mapped */
+	struct srcimp_mgr *mgr;
+	struct srcimp_rsc_ops *ops;
+};
+
+struct srcimp_rsc_ops {
+	int (*map)(struct srcimp *srcimp, struct src *user, struct rsc *input);
+	int (*unmap)(struct srcimp *srcimp);
+};
+
+/* Define SRCIMP resource request description info */
+struct srcimp_desc {
+	unsigned int msr;
+};
+
+struct srcimp_mgr {
+	struct rsc_mgr mgr;	/* Basic resource manager info */
+	spinlock_t mgr_lock;
+	spinlock_t imap_lock;
+	struct list_head imappers;
+	struct imapper *init_imap;
+	unsigned int init_imap_added;
+
+	 /* request srcimp resource */
+	int (*get_srcimp)(struct srcimp_mgr *mgr,
+			  const struct srcimp_desc *desc,
+			  struct srcimp **rsrcimp);
+	/* return srcimp resource */
+	int (*put_srcimp)(struct srcimp_mgr *mgr, struct srcimp *srcimp);
+	int (*imap_add)(struct srcimp_mgr *mgr, struct imapper *entry);
+	int (*imap_delete)(struct srcimp_mgr *mgr, struct imapper *entry);
+};
+
+/* Constructor and destructor of SRC resource manager */
+int src_mgr_create(void *hw, struct src_mgr **rsrc_mgr);
+int src_mgr_destroy(struct src_mgr *src_mgr);
+/* Constructor and destructor of SRCIMP resource manager */
+int srcimp_mgr_create(void *hw, struct srcimp_mgr **rsrc_mgr);
+int srcimp_mgr_destroy(struct srcimp_mgr *srcimp_mgr);
+
+#endif /* CTSRC_H */
diff --git a/sound/pci/ctxfi/cttimer.c b/sound/pci/ctxfi/cttimer.c
new file mode 100644
index 0000000..93b0aed
--- /dev/null
+++ b/sound/pci/ctxfi/cttimer.c
@@ -0,0 +1,443 @@
+/*
+ * PCM timer handling on ctxfi
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#include <linux/slab.h>
+#include <linux/math64.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include "ctatc.h"
+#include "cthardware.h"
+#include "cttimer.h"
+
+static int use_system_timer;
+MODULE_PARM_DESC(use_system_timer, "Foce to use system-timer");
+module_param(use_system_timer, bool, S_IRUGO);
+
+struct ct_timer_ops {
+	void (*init)(struct ct_timer_instance *);
+	void (*prepare)(struct ct_timer_instance *);
+	void (*start)(struct ct_timer_instance *);
+	void (*stop)(struct ct_timer_instance *);
+	void (*free_instance)(struct ct_timer_instance *);
+	void (*interrupt)(struct ct_timer *);
+	void (*free_global)(struct ct_timer *);
+};
+
+/* timer instance -- assigned to each PCM stream */
+struct ct_timer_instance {
+	spinlock_t lock;
+	struct ct_timer *timer_base;
+	struct ct_atc_pcm *apcm;
+	struct snd_pcm_substream *substream;
+	struct timer_list timer;
+	struct list_head instance_list;
+	struct list_head running_list;
+	unsigned int position;
+	unsigned int frag_count;
+	unsigned int running:1;
+	unsigned int need_update:1;
+};
+
+/* timer instance manager */
+struct ct_timer {
+	spinlock_t lock;		/* global timer lock (for xfitimer) */
+	spinlock_t list_lock;		/* lock for instance list */
+	struct ct_atc *atc;
+	struct ct_timer_ops *ops;
+	struct list_head instance_head;
+	struct list_head running_head;
+	unsigned int wc;		/* current wallclock */
+	unsigned int irq_handling:1;	/* in IRQ handling */
+	unsigned int reprogram:1;	/* need to reprogram the internval */
+	unsigned int running:1;		/* global timer running */
+};
+
+
+/*
+ * system-timer-based updates
+ */
+
+static void ct_systimer_callback(unsigned long data)
+{
+	struct ct_timer_instance *ti = (struct ct_timer_instance *)data;
+	struct snd_pcm_substream *substream = ti->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct ct_atc_pcm *apcm = ti->apcm;
+	unsigned int period_size = runtime->period_size;
+	unsigned int buffer_size = runtime->buffer_size;
+	unsigned long flags;
+	unsigned int position, dist, interval;
+
+	position = substream->ops->pointer(substream);
+	dist = (position + buffer_size - ti->position) % buffer_size;
+	if (dist >= period_size ||
+	    position / period_size != ti->position / period_size) {
+		apcm->interrupt(apcm);
+		ti->position = position;
+	}
+	/* Add extra HZ*5/1000 to avoid overrun issue when recording
+	 * at 8kHz in 8-bit format or at 88kHz in 24-bit format. */
+	interval = ((period_size - (position % period_size))
+		   * HZ + (runtime->rate - 1)) / runtime->rate + HZ * 5 / 1000;
+	spin_lock_irqsave(&ti->lock, flags);
+	if (ti->running)
+		mod_timer(&ti->timer, jiffies + interval);
+	spin_unlock_irqrestore(&ti->lock, flags);
+}
+
+static void ct_systimer_init(struct ct_timer_instance *ti)
+{
+	setup_timer(&ti->timer, ct_systimer_callback,
+		    (unsigned long)ti);
+}
+
+static void ct_systimer_start(struct ct_timer_instance *ti)
+{
+	struct snd_pcm_runtime *runtime = ti->substream->runtime;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ti->lock, flags);
+	ti->running = 1;
+	mod_timer(&ti->timer,
+		  jiffies + (runtime->period_size * HZ +
+			     (runtime->rate - 1)) / runtime->rate);
+	spin_unlock_irqrestore(&ti->lock, flags);
+}
+
+static void ct_systimer_stop(struct ct_timer_instance *ti)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ti->lock, flags);
+	ti->running = 0;
+	del_timer(&ti->timer);
+	spin_unlock_irqrestore(&ti->lock, flags);
+}
+
+static void ct_systimer_prepare(struct ct_timer_instance *ti)
+{
+	ct_systimer_stop(ti);
+	try_to_del_timer_sync(&ti->timer);
+}
+
+#define ct_systimer_free	ct_systimer_prepare
+
+static struct ct_timer_ops ct_systimer_ops = {
+	.init = ct_systimer_init,
+	.free_instance = ct_systimer_free,
+	.prepare = ct_systimer_prepare,
+	.start = ct_systimer_start,
+	.stop = ct_systimer_stop,
+};
+
+
+/*
+ * Handling multiple streams using a global emu20k1 timer irq
+ */
+
+#define CT_TIMER_FREQ	48000
+#define MIN_TICKS	1
+#define MAX_TICKS	((1 << 13) - 1)
+
+static void ct_xfitimer_irq_rearm(struct ct_timer *atimer, int ticks)
+{
+	struct hw *hw = atimer->atc->hw;
+	if (ticks > MAX_TICKS)
+		ticks = MAX_TICKS;
+	hw->set_timer_tick(hw, ticks);
+	if (!atimer->running)
+		hw->set_timer_irq(hw, 1);
+	atimer->running = 1;
+}
+
+static void ct_xfitimer_irq_stop(struct ct_timer *atimer)
+{
+	if (atimer->running) {
+		struct hw *hw = atimer->atc->hw;
+		hw->set_timer_irq(hw, 0);
+		hw->set_timer_tick(hw, 0);
+		atimer->running = 0;
+	}
+}
+
+static inline unsigned int ct_xfitimer_get_wc(struct ct_timer *atimer)
+{
+	struct hw *hw = atimer->atc->hw;
+	return hw->get_wc(hw);
+}
+
+/*
+ * reprogram the timer interval;
+ * checks the running instance list and determines the next timer interval.
+ * also updates the each stream position, returns the number of streams
+ * to call snd_pcm_period_elapsed() appropriately
+ *
+ * call this inside the lock and irq disabled
+ */
+static int ct_xfitimer_reprogram(struct ct_timer *atimer, int can_update)
+{
+	struct ct_timer_instance *ti;
+	unsigned int min_intr = (unsigned int)-1;
+	int updates = 0;
+	unsigned int wc, diff;
+
+	if (list_empty(&atimer->running_head)) {
+		ct_xfitimer_irq_stop(atimer);
+		atimer->reprogram = 0; /* clear flag */
+		return 0;
+	}
+
+	wc = ct_xfitimer_get_wc(atimer);
+	diff = wc - atimer->wc;
+	atimer->wc = wc;
+	list_for_each_entry(ti, &atimer->running_head, running_list) {
+		if (ti->frag_count > diff)
+			ti->frag_count -= diff;
+		else {
+			unsigned int pos;
+			unsigned int period_size, rate;
+
+			period_size = ti->substream->runtime->period_size;
+			rate = ti->substream->runtime->rate;
+			pos = ti->substream->ops->pointer(ti->substream);
+			if (pos / period_size != ti->position / period_size) {
+				ti->need_update = 1;
+				ti->position = pos;
+				updates++;
+			}
+			pos %= period_size;
+			pos = period_size - pos;
+			ti->frag_count = div_u64((u64)pos * CT_TIMER_FREQ +
+						 rate - 1, rate);
+		}
+		if (ti->need_update && !can_update)
+			min_intr = 0; /* pending to the next irq */
+		if (ti->frag_count < min_intr)
+			min_intr = ti->frag_count;
+	}
+
+	if (min_intr < MIN_TICKS)
+		min_intr = MIN_TICKS;
+	ct_xfitimer_irq_rearm(atimer, min_intr);
+	atimer->reprogram = 0; /* clear flag */
+	return updates;
+}
+
+/* look through the instance list and call period_elapsed if needed */
+static void ct_xfitimer_check_period(struct ct_timer *atimer)
+{
+	struct ct_timer_instance *ti;
+	unsigned long flags;
+
+	spin_lock_irqsave(&atimer->list_lock, flags);
+	list_for_each_entry(ti, &atimer->instance_head, instance_list) {
+		if (ti->running && ti->need_update) {
+			ti->need_update = 0;
+			ti->apcm->interrupt(ti->apcm);
+		}
+	}
+	spin_unlock_irqrestore(&atimer->list_lock, flags);
+}
+
+/* Handle timer-interrupt */
+static void ct_xfitimer_callback(struct ct_timer *atimer)
+{
+	int update;
+	unsigned long flags;
+
+	spin_lock_irqsave(&atimer->lock, flags);
+	atimer->irq_handling = 1;
+	do {
+		update = ct_xfitimer_reprogram(atimer, 1);
+		spin_unlock(&atimer->lock);
+		if (update)
+			ct_xfitimer_check_period(atimer);
+		spin_lock(&atimer->lock);
+	} while (atimer->reprogram);
+	atimer->irq_handling = 0;
+	spin_unlock_irqrestore(&atimer->lock, flags);
+}
+
+static void ct_xfitimer_prepare(struct ct_timer_instance *ti)
+{
+	ti->frag_count = ti->substream->runtime->period_size;
+	ti->running = 0;
+	ti->need_update = 0;
+}
+
+
+/* start/stop the timer */
+static void ct_xfitimer_update(struct ct_timer *atimer)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&atimer->lock, flags);
+	if (atimer->irq_handling) {
+		/* reached from IRQ handler; let it handle later */
+		atimer->reprogram = 1;
+		spin_unlock_irqrestore(&atimer->lock, flags);
+		return;
+	}
+
+	ct_xfitimer_irq_stop(atimer);
+	ct_xfitimer_reprogram(atimer, 0);
+	spin_unlock_irqrestore(&atimer->lock, flags);
+}
+
+static void ct_xfitimer_start(struct ct_timer_instance *ti)
+{
+	struct ct_timer *atimer = ti->timer_base;
+	unsigned long flags;
+
+	spin_lock_irqsave(&atimer->lock, flags);
+	if (list_empty(&ti->running_list))
+		atimer->wc = ct_xfitimer_get_wc(atimer);
+	ti->running = 1;
+	ti->need_update = 0;
+	list_add(&ti->running_list, &atimer->running_head);
+	spin_unlock_irqrestore(&atimer->lock, flags);
+	ct_xfitimer_update(atimer);
+}
+
+static void ct_xfitimer_stop(struct ct_timer_instance *ti)
+{
+	struct ct_timer *atimer = ti->timer_base;
+	unsigned long flags;
+
+	spin_lock_irqsave(&atimer->lock, flags);
+	list_del_init(&ti->running_list);
+	ti->running = 0;
+	spin_unlock_irqrestore(&atimer->lock, flags);
+	ct_xfitimer_update(atimer);
+}
+
+static void ct_xfitimer_free_global(struct ct_timer *atimer)
+{
+	ct_xfitimer_irq_stop(atimer);
+}
+
+static struct ct_timer_ops ct_xfitimer_ops = {
+	.prepare = ct_xfitimer_prepare,
+	.start = ct_xfitimer_start,
+	.stop = ct_xfitimer_stop,
+	.interrupt = ct_xfitimer_callback,
+	.free_global = ct_xfitimer_free_global,
+};
+
+/*
+ * timer instance
+ */
+
+struct ct_timer_instance *
+ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm)
+{
+	struct ct_timer_instance *ti;
+
+	ti = kzalloc(sizeof(*ti), GFP_KERNEL);
+	if (!ti)
+		return NULL;
+	spin_lock_init(&ti->lock);
+	INIT_LIST_HEAD(&ti->instance_list);
+	INIT_LIST_HEAD(&ti->running_list);
+	ti->timer_base = atimer;
+	ti->apcm = apcm;
+	ti->substream = apcm->substream;
+	if (atimer->ops->init)
+		atimer->ops->init(ti);
+
+	spin_lock_irq(&atimer->list_lock);
+	list_add(&ti->instance_list, &atimer->instance_head);
+	spin_unlock_irq(&atimer->list_lock);
+
+	return ti;
+}
+
+void ct_timer_prepare(struct ct_timer_instance *ti)
+{
+	if (ti->timer_base->ops->prepare)
+		ti->timer_base->ops->prepare(ti);
+	ti->position = 0;
+	ti->running = 0;
+}
+
+void ct_timer_start(struct ct_timer_instance *ti)
+{
+	struct ct_timer *atimer = ti->timer_base;
+	atimer->ops->start(ti);
+}
+
+void ct_timer_stop(struct ct_timer_instance *ti)
+{
+	struct ct_timer *atimer = ti->timer_base;
+	atimer->ops->stop(ti);
+}
+
+void ct_timer_instance_free(struct ct_timer_instance *ti)
+{
+	struct ct_timer *atimer = ti->timer_base;
+
+	atimer->ops->stop(ti); /* to be sure */
+	if (atimer->ops->free_instance)
+		atimer->ops->free_instance(ti);
+
+	spin_lock_irq(&atimer->list_lock);
+	list_del(&ti->instance_list);
+	spin_unlock_irq(&atimer->list_lock);
+
+	kfree(ti);
+}
+
+/*
+ * timer manager
+ */
+
+static void ct_timer_interrupt(void *data, unsigned int status)
+{
+	struct ct_timer *timer = data;
+
+	/* Interval timer interrupt */
+	if ((status & IT_INT) && timer->ops->interrupt)
+		timer->ops->interrupt(timer);
+}
+
+struct ct_timer *ct_timer_new(struct ct_atc *atc)
+{
+	struct ct_timer *atimer;
+	struct hw *hw;
+
+	atimer = kzalloc(sizeof(*atimer), GFP_KERNEL);
+	if (!atimer)
+		return NULL;
+	spin_lock_init(&atimer->lock);
+	spin_lock_init(&atimer->list_lock);
+	INIT_LIST_HEAD(&atimer->instance_head);
+	INIT_LIST_HEAD(&atimer->running_head);
+	atimer->atc = atc;
+	hw = atc->hw;
+	if (!use_system_timer && hw->set_timer_irq) {
+		snd_printd(KERN_INFO "ctxfi: Use xfi-native timer\n");
+		atimer->ops = &ct_xfitimer_ops;
+		hw->irq_callback_data = atimer;
+		hw->irq_callback = ct_timer_interrupt;
+	} else {
+		snd_printd(KERN_INFO "ctxfi: Use system timer\n");
+		atimer->ops = &ct_systimer_ops;
+	}
+	return atimer;
+}
+
+void ct_timer_free(struct ct_timer *atimer)
+{
+	struct hw *hw = atimer->atc->hw;
+	hw->irq_callback = NULL;
+	if (atimer->ops->free_global)
+		atimer->ops->free_global(atimer);
+	kfree(atimer);
+}
+
diff --git a/sound/pci/ctxfi/cttimer.h b/sound/pci/ctxfi/cttimer.h
new file mode 100644
index 0000000..9793482
--- /dev/null
+++ b/sound/pci/ctxfi/cttimer.h
@@ -0,0 +1,29 @@
+/*
+ * Timer handling
+ */
+
+#ifndef __CTTIMER_H
+#define __CTTIMER_H
+
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+
+struct snd_pcm_substream;
+struct ct_atc;
+struct ct_atc_pcm;
+
+struct ct_timer;
+struct ct_timer_instance;
+
+struct ct_timer *ct_timer_new(struct ct_atc *atc);
+void ct_timer_free(struct ct_timer *atimer);
+
+struct ct_timer_instance *
+ct_timer_instance_new(struct ct_timer *atimer, struct ct_atc_pcm *apcm);
+void ct_timer_instance_free(struct ct_timer_instance *ti);
+void ct_timer_start(struct ct_timer_instance *ti);
+void ct_timer_stop(struct ct_timer_instance *ti);
+void ct_timer_prepare(struct ct_timer_instance *ti);
+
+#endif /* __CTTIMER_H */
diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c
new file mode 100644
index 0000000..67665a7
--- /dev/null
+++ b/sound/pci/ctxfi/ctvmem.c
@@ -0,0 +1,250 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File    ctvmem.c
+ *
+ * @Brief
+ * This file contains the implementation of virtual memory management object
+ * for card device.
+ *
+ * @Author Liu Chun
+ * @Date Apr 1 2008
+ */
+
+#include "ctvmem.h"
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <sound/pcm.h>
+
+#define CT_PTES_PER_PAGE (CT_PAGE_SIZE / sizeof(void *))
+#define CT_ADDRS_PER_PAGE (CT_PTES_PER_PAGE * CT_PAGE_SIZE)
+
+/* *
+ * Find or create vm block based on requested @size.
+ * @size must be page aligned.
+ * */
+static struct ct_vm_block *
+get_vm_block(struct ct_vm *vm, unsigned int size)
+{
+	struct ct_vm_block *block = NULL, *entry;
+	struct list_head *pos;
+
+	size = CT_PAGE_ALIGN(size);
+	if (size > vm->size) {
+		printk(KERN_ERR "ctxfi: Fail! No sufficient device virtural "
+				  "memory space available!\n");
+		return NULL;
+	}
+
+	mutex_lock(&vm->lock);
+	list_for_each(pos, &vm->unused) {
+		entry = list_entry(pos, struct ct_vm_block, list);
+		if (entry->size >= size)
+			break; /* found a block that is big enough */
+	}
+	if (pos == &vm->unused)
+		goto out;
+
+	if (entry->size == size) {
+		/* Move the vm node from unused list to used list directly */
+		list_del(&entry->list);
+		list_add(&entry->list, &vm->used);
+		vm->size -= size;
+		block = entry;
+		goto out;
+	}
+
+	block = kzalloc(sizeof(*block), GFP_KERNEL);
+	if (NULL == block)
+		goto out;
+
+	block->addr = entry->addr;
+	block->size = size;
+	list_add(&block->list, &vm->used);
+	entry->addr += size;
+	entry->size -= size;
+	vm->size -= size;
+
+ out:
+	mutex_unlock(&vm->lock);
+	return block;
+}
+
+static void put_vm_block(struct ct_vm *vm, struct ct_vm_block *block)
+{
+	struct ct_vm_block *entry, *pre_ent;
+	struct list_head *pos, *pre;
+
+	block->size = CT_PAGE_ALIGN(block->size);
+
+	mutex_lock(&vm->lock);
+	list_del(&block->list);
+	vm->size += block->size;
+
+	list_for_each(pos, &vm->unused) {
+		entry = list_entry(pos, struct ct_vm_block, list);
+		if (entry->addr >= (block->addr + block->size))
+			break; /* found a position */
+	}
+	if (pos == &vm->unused) {
+		list_add_tail(&block->list, &vm->unused);
+		entry = block;
+	} else {
+		if ((block->addr + block->size) == entry->addr) {
+			entry->addr = block->addr;
+			entry->size += block->size;
+			kfree(block);
+		} else {
+			__list_add(&block->list, pos->prev, pos);
+			entry = block;
+		}
+	}
+
+	pos = &entry->list;
+	pre = pos->prev;
+	while (pre != &vm->unused) {
+		entry = list_entry(pos, struct ct_vm_block, list);
+		pre_ent = list_entry(pre, struct ct_vm_block, list);
+		if ((pre_ent->addr + pre_ent->size) > entry->addr)
+			break;
+
+		pre_ent->size += entry->size;
+		list_del(pos);
+		kfree(entry);
+		pos = pre;
+		pre = pos->prev;
+	}
+	mutex_unlock(&vm->lock);
+}
+
+/* Map host addr (kmalloced/vmalloced) to device logical addr. */
+static struct ct_vm_block *
+ct_vm_map(struct ct_vm *vm, struct snd_pcm_substream *substream, int size)
+{
+	struct ct_vm_block *block;
+	unsigned int pte_start;
+	unsigned i, pages;
+	unsigned long *ptp;
+
+	block = get_vm_block(vm, size);
+	if (block == NULL) {
+		printk(KERN_ERR "ctxfi: No virtual memory block that is big "
+				  "enough to allocate!\n");
+		return NULL;
+	}
+
+	ptp = vm->ptp[0];
+	pte_start = (block->addr >> CT_PAGE_SHIFT);
+	pages = block->size >> CT_PAGE_SHIFT;
+	for (i = 0; i < pages; i++) {
+		unsigned long addr;
+		addr = snd_pcm_sgbuf_get_addr(substream, i << CT_PAGE_SHIFT);
+		ptp[pte_start + i] = addr;
+	}
+
+	block->size = size;
+	return block;
+}
+
+static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block)
+{
+	/* do unmapping */
+	put_vm_block(vm, block);
+}
+
+/* *
+ * return the host (kmalloced) addr of the @index-th device
+ * page talbe page on success, or NULL on failure.
+ * The first returned NULL indicates the termination.
+ * */
+static void *
+ct_get_ptp_virt(struct ct_vm *vm, int index)
+{
+	void *addr;
+
+	addr = (index >= CT_PTP_NUM) ? NULL : vm->ptp[index];
+
+	return addr;
+}
+
+int ct_vm_create(struct ct_vm **rvm)
+{
+	struct ct_vm *vm;
+	struct ct_vm_block *block;
+	int i;
+
+	*rvm = NULL;
+
+	vm = kzalloc(sizeof(*vm), GFP_KERNEL);
+	if (NULL == vm)
+		return -ENOMEM;
+
+	mutex_init(&vm->lock);
+
+	/* Allocate page table pages */
+	for (i = 0; i < CT_PTP_NUM; i++) {
+		vm->ptp[i] = kmalloc(PAGE_SIZE, GFP_KERNEL);
+		if (NULL == vm->ptp[i])
+			break;
+	}
+	if (!i) {
+		/* no page table pages are allocated */
+		kfree(vm);
+		return -ENOMEM;
+	}
+	vm->size = CT_ADDRS_PER_PAGE * i;
+	/* Initialise remaining ptps */
+	for (; i < CT_PTP_NUM; i++)
+		vm->ptp[i] = NULL;
+
+	vm->map = ct_vm_map;
+	vm->unmap = ct_vm_unmap;
+	vm->get_ptp_virt = ct_get_ptp_virt;
+	INIT_LIST_HEAD(&vm->unused);
+	INIT_LIST_HEAD(&vm->used);
+	block = kzalloc(sizeof(*block), GFP_KERNEL);
+	if (NULL != block) {
+		block->addr = 0;
+		block->size = vm->size;
+		list_add(&block->list, &vm->unused);
+	}
+
+	*rvm = vm;
+	return 0;
+}
+
+/* The caller must ensure no mapping pages are being used
+ * by hardware before calling this function */
+void ct_vm_destroy(struct ct_vm *vm)
+{
+	int i;
+	struct list_head *pos;
+	struct ct_vm_block *entry;
+
+	/* free used and unused list nodes */
+	while (!list_empty(&vm->used)) {
+		pos = vm->used.next;
+		list_del(pos);
+		entry = list_entry(pos, struct ct_vm_block, list);
+		kfree(entry);
+	}
+	while (!list_empty(&vm->unused)) {
+		pos = vm->unused.next;
+		list_del(pos);
+		entry = list_entry(pos, struct ct_vm_block, list);
+		kfree(entry);
+	}
+
+	/* free allocated page table pages */
+	for (i = 0; i < CT_PTP_NUM; i++)
+		kfree(vm->ptp[i]);
+
+	vm->size = 0;
+
+	kfree(vm);
+}
diff --git a/sound/pci/ctxfi/ctvmem.h b/sound/pci/ctxfi/ctvmem.h
new file mode 100644
index 0000000..01e4fd0
--- /dev/null
+++ b/sound/pci/ctxfi/ctvmem.h
@@ -0,0 +1,61 @@
+/**
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ *
+ * @File    ctvmem.h
+ *
+ * @Brief
+ * This file contains the definition of virtual memory management object
+ * for card device.
+ *
+ * @Author Liu Chun
+ * @Date Mar 28 2008
+ */
+
+#ifndef CTVMEM_H
+#define CTVMEM_H
+
+#define CT_PTP_NUM	1	/* num of device page table pages */
+
+#include <linux/mutex.h>
+#include <linux/list.h>
+
+/* The chip can handle the page table of 4k pages
+ * (emu20k1 can handle even 8k pages, but we don't use it right now)
+ */
+#define CT_PAGE_SIZE	4096
+#define CT_PAGE_SHIFT	12
+#define CT_PAGE_MASK	(~(PAGE_SIZE - 1))
+#define CT_PAGE_ALIGN(addr)	ALIGN(addr, CT_PAGE_SIZE)
+
+struct ct_vm_block {
+	unsigned int addr;	/* starting logical addr of this block */
+	unsigned int size;	/* size of this device virtual mem block */
+	struct list_head list;
+};
+
+struct snd_pcm_substream;
+
+/* Virtual memory management object for card device */
+struct ct_vm {
+	void *ptp[CT_PTP_NUM];		/* Device page table pages */
+	unsigned int size;		/* Available addr space in bytes */
+	struct list_head unused;	/* List of unused blocks */
+	struct list_head used;		/* List of used blocks */
+	struct mutex lock;
+
+	/* Map host addr (kmalloced/vmalloced) to device logical addr. */
+	struct ct_vm_block *(*map)(struct ct_vm *, struct snd_pcm_substream *,
+				   int size);
+	/* Unmap device logical addr area. */
+	void (*unmap)(struct ct_vm *, struct ct_vm_block *block);
+	void *(*get_ptp_virt)(struct ct_vm *vm, int index);
+};
+
+int ct_vm_create(struct ct_vm **rvm);
+void ct_vm_destroy(struct ct_vm *vm);
+
+#endif /* CTVMEM_H */
diff --git a/sound/pci/ctxfi/xfi.c b/sound/pci/ctxfi/xfi.c
new file mode 100644
index 0000000..7654174
--- /dev/null
+++ b/sound/pci/ctxfi/xfi.c
@@ -0,0 +1,164 @@
+/*
+ * xfi linux driver.
+ *
+ * Copyright (C) 2008, Creative Technology Ltd. All Rights Reserved.
+ *
+ * This source file is released under GPL v2 license (no other versions).
+ * See the COPYING file included in the main directory of this source
+ * distribution for the license terms and conditions.
+ */
+
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/moduleparam.h>
+#include <linux/pci_ids.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include "ctatc.h"
+#include "cthardware.h"
+
+MODULE_AUTHOR("Creative Technology Ltd");
+MODULE_DESCRIPTION("X-Fi driver version 1.03");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE("{{Creative Labs, Sound Blaster X-Fi}");
+
+static unsigned int reference_rate = 48000;
+static unsigned int multiple = 2;
+MODULE_PARM_DESC(reference_rate, "Reference rate (default=48000)");
+module_param(reference_rate, uint, S_IRUGO);
+MODULE_PARM_DESC(multiple, "Rate multiplier (default=2)");
+module_param(multiple, uint, S_IRUGO);
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Creative X-Fi driver");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Creative X-Fi driver");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver");
+
+static struct pci_device_id ct_pci_dev_ids[] = {
+	/* only X-Fi is supported, so... */
+	{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1),
+	  .driver_data = ATC20K1,
+	},
+	{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K2),
+	  .driver_data = ATC20K2,
+	},
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, ct_pci_dev_ids);
+
+static int __devinit
+ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct ct_atc *atc;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+	if (err)
+		return err;
+	if ((reference_rate != 48000) && (reference_rate != 44100)) {
+		printk(KERN_ERR "ctxfi: Invalid reference_rate value %u!!!\n",
+		       reference_rate);
+		printk(KERN_ERR "ctxfi: The valid values for reference_rate "
+		       "are 48000 and 44100, Value 48000 is assumed.\n");
+		reference_rate = 48000;
+	}
+	if ((multiple != 1) && (multiple != 2)) {
+		printk(KERN_ERR "ctxfi: Invalid multiple value %u!!!\n",
+		       multiple);
+		printk(KERN_ERR "ctxfi: The valid values for multiple are "
+		       "1 and 2, Value 2 is assumed.\n");
+		multiple = 2;
+	}
+	err = ct_atc_create(card, pci, reference_rate, multiple,
+			    pci_id->driver_data, &atc);
+	if (err < 0)
+		goto error;
+
+	card->private_data = atc;
+
+	/* Create alsa devices supported by this card */
+	err = ct_atc_create_alsa_devs(atc);
+	if (err < 0)
+		goto error;
+
+	strcpy(card->driver, "SB-XFi");
+	strcpy(card->shortname, "Creative X-Fi");
+	snprintf(card->longname, sizeof(card->longname), "%s %s %s",
+		 card->shortname, atc->chip_name, atc->model_name);
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto error;
+
+	pci_set_drvdata(pci, card);
+	dev++;
+
+	return 0;
+
+error:
+	snd_card_free(card);
+	return err;
+}
+
+static void __devexit ct_card_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+#ifdef CONFIG_PM
+static int ct_card_suspend(struct pci_dev *pci, pm_message_t state)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct ct_atc *atc = card->private_data;
+
+	return atc->suspend(atc, state);
+}
+
+static int ct_card_resume(struct pci_dev *pci)
+{
+	struct snd_card *card = pci_get_drvdata(pci);
+	struct ct_atc *atc = card->private_data;
+
+	return atc->resume(atc);
+}
+#endif
+
+static struct pci_driver ct_driver = {
+	.name = "SB-XFi",
+	.id_table = ct_pci_dev_ids,
+	.probe = ct_card_probe,
+	.remove = __devexit_p(ct_card_remove),
+#ifdef CONFIG_PM
+	.suspend = ct_card_suspend,
+	.resume = ct_card_resume,
+#endif
+};
+
+static int __init ct_card_init(void)
+{
+	return pci_register_driver(&ct_driver);
+}
+
+static void __exit ct_card_exit(void)
+{
+	pci_unregister_driver(&ct_driver);
+}
+
+module_init(ct_card_init)
+module_exit(ct_card_exit)
diff --git a/sound/pci/emu10k1/Makefile b/sound/pci/emu10k1/Makefile
index cf2d563..fc5591e 100644
--- a/sound/pci/emu10k1/Makefile
+++ b/sound/pci/emu10k1/Makefile
@@ -9,15 +9,7 @@
 snd-emu10k1-synth-objs := emu10k1_synth.o emu10k1_callback.o emu10k1_patch.o
 snd-emu10k1x-objs := emu10k1x.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_EMU10K1) += snd-emu10k1.o
-obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-emu10k1-synth.o
+obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-emu10k1-synth.o
 obj-$(CONFIG_SND_EMU10K1X) += snd-emu10k1x.o
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 1970f0e..4d3ad79 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -858,7 +858,6 @@
 	}
 
 	pcm->info_flags = 0;
-	pcm->dev_subclass = SNDRV_PCM_SUBCLASS_GENERIC_MIX;
 	switch(device) {
 	case 0:
 		strcpy(pcm->name, "EMU10K1X Front");
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 78f62fd..55b83ef 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -1736,7 +1736,7 @@
 	.buffer_bytes_max =	(128*1024),
 	.period_bytes_min =	1024,
 	.period_bytes_max =	(128*1024),
-	.periods_min =		1,
+	.periods_min =		2,
 	.periods_max =		1024,
 	.fifo_size =		0,
 };
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index eb2a19b..04438f1 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -2,7 +2,6 @@
 	tristate "Intel HD Audio"
 	select SND_PCM
 	select SND_VMASTER
-	select SND_JACK if INPUT=y || INPUT=SND
 	help
 	  Say Y here to include support for Intel "High Definition
 	  Audio" (Azalia) and its compatible devices.
@@ -39,6 +38,14 @@
 	  Say Y here to build a digital beep interface for HD-audio
 	  driver. This interface is used to generate digital beeps.
 
+config SND_HDA_INPUT_JACK
+	bool "Support jack plugging notification via input layer"
+	depends on INPUT=y || INPUT=SND_HDA_INTEL
+	select SND_JACK
+	help
+	  Say Y here to enable the jack plugging notification via
+	  input layer.
+
 config SND_HDA_CODEC_REALTEK
 	bool "Build Realtek HD-audio codec support"
 	default y
@@ -139,6 +146,19 @@
 	  snd-hda-codec-conexant.
 	  This module is automatically loaded at probing.
 
+config SND_HDA_CODEC_CA0110
+	bool "Build Creative CA0110-IBG codec support"
+	depends on SND_HDA_INTEL
+	default y
+	help
+	  Say Y here to include Creative CA0110-IBG codec support in
+	  snd-hda-intel driver, found on some Creative X-Fi cards.
+
+	  When the HD-audio driver is built as a module, the codec
+	  support code is also built as another module,
+	  snd-hda-codec-ca0110.
+	  This module is automatically loaded at probing.
+
 config SND_HDA_CODEC_CMEDIA
 	bool "Build C-Media HD-audio codec support"
 	default y
diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile
index 50f9d09..e3081d4 100644
--- a/sound/pci/hda/Makefile
+++ b/sound/pci/hda/Makefile
@@ -13,6 +13,7 @@
 snd-hda-codec-idt-objs :=	patch_sigmatel.o
 snd-hda-codec-si3054-objs :=	patch_si3054.o
 snd-hda-codec-atihdmi-objs :=	patch_atihdmi.o
+snd-hda-codec-ca0110-objs :=	patch_ca0110.o
 snd-hda-codec-conexant-objs :=	patch_conexant.o
 snd-hda-codec-via-objs :=	patch_via.o
 snd-hda-codec-nvhdmi-objs :=	patch_nvhdmi.o
@@ -40,6 +41,9 @@
 ifdef CONFIG_SND_HDA_CODEC_ATIHDMI
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-atihdmi.o
 endif
+ifdef CONFIG_SND_HDA_CODEC_CA0110
+obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-ca0110.o
+endif
 ifdef CONFIG_SND_HDA_CODEC_CONEXANT
 obj-$(CONFIG_SND_HDA_INTEL) += snd-hda-codec-conexant.o
 endif
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 4de5bac..29272f2 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -45,6 +45,46 @@
 			AC_VERB_SET_BEEP_CONTROL, beep->tone);
 }
 
+/* (non-standard) Linear beep tone calculation for IDT/STAC codecs 
+ *
+ * The tone frequency of beep generator on IDT/STAC codecs is
+ * defined from the 8bit tone parameter, in Hz,
+ *    freq = 48000 * (257 - tone) / 1024
+ * that is from 12kHz to 93.75kHz in step of 46.875 hz
+ */
+static int beep_linear_tone(struct hda_beep *beep, int hz)
+{
+	hz *= 1000; /* fixed point */
+	hz = hz - DIGBEEP_HZ_MIN;
+	if (hz < 0)
+		hz = 0; /* turn off PC beep*/
+	else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
+		hz = 0xff;
+	else {
+		hz /= DIGBEEP_HZ_STEP;
+		hz++;
+	}
+	return hz;
+}
+
+/* HD-audio standard beep tone parameter calculation
+ *
+ * The tone frequency in Hz is calculated as
+ *   freq = 48000 / (tone * 4)
+ * from 47Hz to 12kHz
+ */
+static int beep_standard_tone(struct hda_beep *beep, int hz)
+{
+	if (hz <= 0)
+		return 0; /* disabled */
+	hz = 12000 / hz;
+	if (hz > 0xff)
+		return 0xff;
+	if (hz <= 0)
+		return 1;
+	return hz;
+}
+
 static int snd_hda_beep_event(struct input_dev *dev, unsigned int type,
 				unsigned int code, int hz)
 {
@@ -55,21 +95,14 @@
 		if (hz)
 			hz = 1000;
 	case SND_TONE:
-		hz *= 1000; /* fixed point */
-		hz = hz - DIGBEEP_HZ_MIN;
-		if (hz < 0)
-			hz = 0; /* turn off PC beep*/
-		else if (hz >= (DIGBEEP_HZ_MAX - DIGBEEP_HZ_MIN))
-			hz = 0xff;
-		else {
-			hz /= DIGBEEP_HZ_STEP;
-			hz++;
-		}
+		if (beep->linear_tone)
+			beep->tone = beep_linear_tone(beep, hz);
+		else
+			beep->tone = beep_standard_tone(beep, hz);
 		break;
 	default:
 		return -1;
 	}
-	beep->tone = hz;
 
 	/* schedule beep event */
 	schedule_work(&beep->beep_work);
diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h
index 51bf6a5..0c3de78 100644
--- a/sound/pci/hda/hda_beep.h
+++ b/sound/pci/hda/hda_beep.h
@@ -30,8 +30,9 @@
 	struct hda_codec *codec;
 	char phys[32];
 	int tone;
-	int nid;
-	int enabled;
+	hda_nid_t nid;
+	unsigned int enabled:1;
+	unsigned int linear_tone:1;	/* linear tone for IDT/STAC codec */
 	struct work_struct beep_work; /* scheduled task for beep event */
 };
 
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 8820faf..462e2ce 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -48,6 +48,7 @@
 	{ 0x1095, "Silicon Image" },
 	{ 0x10de, "Nvidia" },
 	{ 0x10ec, "Realtek" },
+	{ 0x1102, "Creative" },
 	{ 0x1106, "VIA" },
 	{ 0x111d, "IDT" },
 	{ 0x11c1, "LSI" },
@@ -157,6 +158,39 @@
 	return val;
 }
 
+/*
+ * Send and receive a verb
+ */
+static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
+			   unsigned int *res)
+{
+	struct hda_bus *bus = codec->bus;
+	int err;
+
+	if (res)
+		*res = -1;
+ again:
+	snd_hda_power_up(codec);
+	mutex_lock(&bus->cmd_mutex);
+	err = bus->ops.command(bus, cmd);
+	if (!err && res)
+		*res = bus->ops.get_response(bus);
+	mutex_unlock(&bus->cmd_mutex);
+	snd_hda_power_down(codec);
+	if (res && *res == -1 && bus->rirb_error) {
+		if (bus->response_reset) {
+			snd_printd("hda_codec: resetting BUS due to "
+				   "fatal communication error\n");
+			bus->ops.bus_reset(bus);
+		}
+		goto again;
+	}
+	/* clear reset-flag when the communication gets recovered */
+	if (!err)
+		bus->response_reset = 0;
+	return err;
+}
+
 /**
  * snd_hda_codec_read - send a command and get the response
  * @codec: the HDA codec
@@ -173,18 +207,9 @@
 				int direct,
 				unsigned int verb, unsigned int parm)
 {
-	struct hda_bus *bus = codec->bus;
+	unsigned cmd = make_codec_cmd(codec, nid, direct, verb, parm);
 	unsigned int res;
-
-	res = make_codec_cmd(codec, nid, direct, verb, parm);
-	snd_hda_power_up(codec);
-	mutex_lock(&bus->cmd_mutex);
-	if (!bus->ops.command(bus, res))
-		res = bus->ops.get_response(bus);
-	else
-		res = (unsigned int)-1;
-	mutex_unlock(&bus->cmd_mutex);
-	snd_hda_power_down(codec);
+	codec_exec_verb(codec, cmd, &res);
 	return res;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_read);
@@ -204,17 +229,10 @@
 int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
 			 unsigned int verb, unsigned int parm)
 {
-	struct hda_bus *bus = codec->bus;
+	unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm);
 	unsigned int res;
-	int err;
-
-	res = make_codec_cmd(codec, nid, direct, verb, parm);
-	snd_hda_power_up(codec);
-	mutex_lock(&bus->cmd_mutex);
-	err = bus->ops.command(bus, res);
-	mutex_unlock(&bus->cmd_mutex);
-	snd_hda_power_down(codec);
-	return err;
+	return codec_exec_verb(codec, cmd,
+			       codec->bus->sync_write ? &res : NULL);
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_write);
 
@@ -613,7 +631,10 @@
 	const struct hda_vendor_id *c;
 	const char *vendor = NULL;
 	u16 vendor_id = codec->vendor_id >> 16;
-	char tmp[16], name[32];
+	char tmp[16];
+
+	if (codec->vendor_name)
+		goto get_chip_name;
 
 	for (c = hda_vendor_ids; c->id; c++) {
 		if (c->id == vendor_id) {
@@ -625,14 +646,21 @@
 		sprintf(tmp, "Generic %04x", vendor_id);
 		vendor = tmp;
 	}
+	codec->vendor_name = kstrdup(vendor, GFP_KERNEL);
+	if (!codec->vendor_name)
+		return -ENOMEM;
+
+ get_chip_name:
+	if (codec->chip_name)
+		return 0;
+
 	if (codec->preset && codec->preset->name)
-		snprintf(name, sizeof(name), "%s %s", vendor,
-			 codec->preset->name);
-	else
-		snprintf(name, sizeof(name), "%s ID %x", vendor,
-			 codec->vendor_id & 0xffff);
-	codec->name = kstrdup(name, GFP_KERNEL);
-	if (!codec->name)
+		codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL);
+	else {
+		sprintf(tmp, "ID %x", codec->vendor_id & 0xffff);
+		codec->chip_name = kstrdup(tmp, GFP_KERNEL);
+	}
+	if (!codec->chip_name)
 		return -ENOMEM;
 	return 0;
 }
@@ -838,7 +866,8 @@
 	module_put(codec->owner);
 	free_hda_cache(&codec->amp_cache);
 	free_hda_cache(&codec->cmd_cache);
-	kfree(codec->name);
+	kfree(codec->vendor_name);
+	kfree(codec->chip_name);
 	kfree(codec->modelname);
 	kfree(codec->wcaps);
 	kfree(codec);
@@ -943,8 +972,6 @@
 			snd_hda_codec_read(codec, nid, 0,
 					   AC_VERB_GET_SUBSYSTEM_ID, 0);
 	}
-	if (bus->modelname)
-		codec->modelname = kstrdup(bus->modelname, GFP_KERNEL);
 
 	/* power-up all before initialization */
 	hda_set_power_state(codec,
@@ -979,15 +1006,16 @@
 	int err;
 
 	codec->preset = find_codec_preset(codec);
-	if (!codec->name) {
+	if (!codec->vendor_name || !codec->chip_name) {
 		err = get_codec_name(codec);
 		if (err < 0)
 			return err;
 	}
 	/* audio codec should override the mixer name */
 	if (codec->afg || !*codec->bus->card->mixername)
-		strlcpy(codec->bus->card->mixername, codec->name,
-			sizeof(codec->bus->card->mixername));
+		snprintf(codec->bus->card->mixername,
+			 sizeof(codec->bus->card->mixername),
+			 "%s %s", codec->vendor_name, codec->chip_name);
 
 	if (is_generic_config(codec)) {
 		err = snd_hda_parse_generic_codec(codec);
@@ -1055,6 +1083,8 @@
 /* FIXME: more better hash key? */
 #define HDA_HASH_KEY(nid,dir,idx) (u32)((nid) + ((idx) << 16) + ((dir) << 24))
 #define HDA_HASH_PINCAP_KEY(nid) (u32)((nid) + (0x02 << 24))
+#define HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24))
+#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24))
 #define INFO_AMP_CAPS	(1<<0)
 #define INFO_AMP_VOL(ch)	(1 << (1 + (ch)))
 
@@ -1145,19 +1175,32 @@
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
 
-u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int
+query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
+		unsigned int (*func)(struct hda_codec *, hda_nid_t))
 {
 	struct hda_amp_info *info;
 
-	info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
+	info = get_alloc_amp_hash(codec, key);
 	if (!info)
 		return 0;
 	if (!info->head.val) {
-		info->amp_caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
 		info->head.val |= INFO_AMP_CAPS;
+		info->amp_caps = func(codec, nid);
 	}
 	return info->amp_caps;
 }
+
+static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
+{
+	return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
+}
+
+u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
+{
+	return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
+			       read_pin_cap);
+}
 EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
 
 /*
@@ -1432,6 +1475,8 @@
 	memset(&id, 0, sizeof(id));
 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 	id.index = idx;
+	if (snd_BUG_ON(strlen(name) >= sizeof(id.name)))
+		return NULL;
 	strcpy(id.name, name);
 	return snd_ctl_find_id(codec->bus->card, &id);
 }
@@ -2242,28 +2287,22 @@
 int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid,
 			      int direct, unsigned int verb, unsigned int parm)
 {
-	struct hda_bus *bus = codec->bus;
-	unsigned int res;
-	int err;
+	int err = snd_hda_codec_write(codec, nid, direct, verb, parm);
+	struct hda_cache_head *c;
+	u32 key;
 
-	res = make_codec_cmd(codec, nid, direct, verb, parm);
-	snd_hda_power_up(codec);
-	mutex_lock(&bus->cmd_mutex);
-	err = bus->ops.command(bus, res);
-	if (!err) {
-		struct hda_cache_head *c;
-		u32 key;
-		/* parm may contain the verb stuff for get/set amp */
-		verb = verb | (parm >> 8);
-		parm &= 0xff;
-		key = build_cmd_cache_key(nid, verb);
-		c = get_alloc_hash(&codec->cmd_cache, key);
-		if (c)
-			c->val = parm;
-	}
-	mutex_unlock(&bus->cmd_mutex);
-	snd_hda_power_down(codec);
-	return err;
+	if (err < 0)
+		return err;
+	/* parm may contain the verb stuff for get/set amp */
+	verb = verb | (parm >> 8);
+	parm &= 0xff;
+	key = build_cmd_cache_key(nid, verb);
+	mutex_lock(&codec->bus->cmd_mutex);
+	c = get_alloc_hash(&codec->cmd_cache, key);
+	if (c)
+		c->val = parm;
+	mutex_unlock(&codec->bus->cmd_mutex);
+	return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_write_cache);
 
@@ -2321,7 +2360,8 @@
 		if (wcaps & AC_WCAP_POWER) {
 			unsigned int wid_type = (wcaps & AC_WCAP_TYPE) >>
 				AC_WCAP_TYPE_SHIFT;
-			if (wid_type == AC_WID_PIN) {
+			if (power_state == AC_PWRST_D3 &&
+			    wid_type == AC_WID_PIN) {
 				unsigned int pincap;
 				/*
 				 * don't power down the widget if it controls
@@ -2333,7 +2373,7 @@
 						nid, 0,
 						AC_VERB_GET_EAPD_BTLENABLE, 0);
 					eapd &= 0x02;
-					if (power_state == AC_PWRST_D3 && eapd)
+					if (eapd)
 						continue;
 				}
 			}
@@ -2544,6 +2584,41 @@
 }
 EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
 
+static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+{
+	unsigned int val = 0;
+	if (nid != codec->afg &&
+	    (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD))
+		val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
+	if (!val || val == -1)
+		val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+	if (!val || val == -1)
+		return 0;
+	return val;
+}
+
+static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+{
+	return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
+			       get_pcm_param);
+}
+
+static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
+{
+	unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
+	if (!streams || streams == -1)
+		streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
+	if (!streams || streams == -1)
+		return 0;
+	return streams;
+}
+
+static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
+{
+	return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
+			       get_stream_param);
+}
+
 /**
  * snd_hda_query_supported_pcm - query the supported PCM rates and formats
  * @codec: the HDA codec
@@ -2562,15 +2637,8 @@
 {
 	unsigned int i, val, wcaps;
 
-	val = 0;
 	wcaps = get_wcaps(codec, nid);
-	if (nid != codec->afg && (wcaps & AC_WCAP_FORMAT_OVRD)) {
-		val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
-		if (val == -1)
-			return -EIO;
-	}
-	if (!val)
-		val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
+	val = query_pcm_param(codec, nid);
 
 	if (ratesp) {
 		u32 rates = 0;
@@ -2592,15 +2660,9 @@
 		u64 formats = 0;
 		unsigned int streams, bps;
 
-		streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
-		if (streams == -1)
+		streams = query_stream_param(codec, nid);
+		if (!streams)
 			return -EIO;
-		if (!streams) {
-			streams = snd_hda_param_read(codec, codec->afg,
-						     AC_PAR_STREAM);
-			if (streams == -1)
-				return -EIO;
-		}
 
 		bps = 0;
 		if (streams & AC_SUPFMT_PCM) {
@@ -2674,17 +2736,9 @@
 	int i;
 	unsigned int val = 0, rate, stream;
 
-	if (nid != codec->afg &&
-	    (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) {
-		val = snd_hda_param_read(codec, nid, AC_PAR_PCM);
-		if (val == -1)
-			return 0;
-	}
-	if (!val) {
-		val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM);
-		if (val == -1)
-			return 0;
-	}
+	val = query_pcm_param(codec, nid);
+	if (!val)
+		return 0;
 
 	rate = format & 0xff00;
 	for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++)
@@ -2696,12 +2750,8 @@
 	if (i >= AC_PAR_PCM_RATE_BITS)
 		return 0;
 
-	stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
-	if (stream == -1)
-		return 0;
-	if (!stream && nid != codec->afg)
-		stream = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM);
-	if (!stream || stream == -1)
+	stream = query_stream_param(codec, nid);
+	if (!stream)
 		return 0;
 
 	if (stream & AC_SUPFMT_PCM) {
@@ -3835,11 +3885,10 @@
 /**
  * snd_hda_suspend - suspend the codecs
  * @bus: the HDA bus
- * @state: suspsend state
  *
  * Returns 0 if successful.
  */
-int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
+int snd_hda_suspend(struct hda_bus *bus)
 {
 	struct hda_codec *codec;
 
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 2fdecf4..cad79ef 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -574,6 +574,8 @@
 	/* attach a PCM stream */
 	int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
 			  struct hda_pcm *pcm);
+	/* reset bus for retry verb */
+	void (*bus_reset)(struct hda_bus *bus);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	/* notify power-up/down from codec to controller */
 	void (*pm_notify)(struct hda_bus *bus);
@@ -622,7 +624,13 @@
 
 	/* misc op flags */
 	unsigned int needs_damn_long_delay :1;
+	unsigned int allow_bus_reset:1;	/* allow bus reset at fatal error */
+	unsigned int sync_write:1;	/* sync after verb write */
+	/* status for codec/controller */
 	unsigned int shutdown :1;	/* being unloaded */
+	unsigned int rirb_error:1;	/* error in codec communication */
+	unsigned int response_reset:1;	/* controller was reset */
+	unsigned int in_reset:1;	/* during reset operation */
 };
 
 /*
@@ -747,7 +755,8 @@
 	/* detected preset */
 	const struct hda_codec_preset *preset;
 	struct module *owner;
-	const char *name;	/* codec name */
+	const char *vendor_name;	/* codec vendor name */
+	const char *chip_name;		/* codec chip name */
 	const char *modelname;	/* model name for preset */
 
 	/* set by patch */
@@ -905,7 +914,7 @@
  * power management
  */
 #ifdef CONFIG_PM
-int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
+int snd_hda_suspend(struct hda_bus *bus);
 int snd_hda_resume(struct hda_bus *bus);
 #endif
 
diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c
index 1c57505..6812fbe 100644
--- a/sound/pci/hda/hda_hwdep.c
+++ b/sound/pci/hda/hda_hwdep.c
@@ -242,7 +242,8 @@
 CODEC_INFO_SHOW(revision_id);
 CODEC_INFO_SHOW(afg);
 CODEC_INFO_SHOW(mfg);
-CODEC_INFO_STR_SHOW(name);
+CODEC_INFO_STR_SHOW(vendor_name);
+CODEC_INFO_STR_SHOW(chip_name);
 CODEC_INFO_STR_SHOW(modelname);
 
 #define CODEC_INFO_STORE(type)					\
@@ -275,7 +276,8 @@
 CODEC_INFO_STORE(vendor_id);
 CODEC_INFO_STORE(subsystem_id);
 CODEC_INFO_STORE(revision_id);
-CODEC_INFO_STR_STORE(name);
+CODEC_INFO_STR_STORE(vendor_name);
+CODEC_INFO_STR_STORE(chip_name);
 CODEC_INFO_STR_STORE(modelname);
 
 #define CODEC_ACTION_STORE(type)				\
@@ -499,7 +501,8 @@
 	CODEC_ATTR_RW(revision_id),
 	CODEC_ATTR_RO(afg),
 	CODEC_ATTR_RO(mfg),
-	CODEC_ATTR_RW(name),
+	CODEC_ATTR_RW(vendor_name),
+	CODEC_ATTR_RW(chip_name),
 	CODEC_ATTR_RW(modelname),
 	CODEC_ATTR_RW(init_verbs),
 	CODEC_ATTR_RW(hints),
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 3128e1a..4e9ea70 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -128,21 +128,33 @@
 			 "{ULI, M5461}}");
 MODULE_DESCRIPTION("Intel HDA driver");
 
+#ifdef CONFIG_SND_VERBOSE_PRINTK
+#define SFX	/* nop */
+#else
 #define SFX	"hda-intel: "
-
+#endif
 
 /*
  * registers
  */
 #define ICH6_REG_GCAP			0x00
+#define   ICH6_GCAP_64OK	(1 << 0)   /* 64bit address support */
+#define   ICH6_GCAP_NSDO	(3 << 1)   /* # of serial data out signals */
+#define   ICH6_GCAP_BSS		(31 << 3)  /* # of bidirectional streams */
+#define   ICH6_GCAP_ISS		(15 << 8)  /* # of input streams */
+#define   ICH6_GCAP_OSS		(15 << 12) /* # of output streams */
 #define ICH6_REG_VMIN			0x02
 #define ICH6_REG_VMAJ			0x03
 #define ICH6_REG_OUTPAY			0x04
 #define ICH6_REG_INPAY			0x06
 #define ICH6_REG_GCTL			0x08
+#define   ICH6_GCTL_RESET	(1 << 0)   /* controller reset */
+#define   ICH6_GCTL_FCNTRL	(1 << 1)   /* flush control */
+#define   ICH6_GCTL_UNSOL	(1 << 8)   /* accept unsol. response enable */
 #define ICH6_REG_WAKEEN			0x0c
 #define ICH6_REG_STATESTS		0x0e
 #define ICH6_REG_GSTS			0x10
+#define   ICH6_GSTS_FSTS	(1 << 1)   /* flush status */
 #define ICH6_REG_INTCTL			0x20
 #define ICH6_REG_INTSTS			0x24
 #define ICH6_REG_WALCLK			0x30
@@ -150,17 +162,27 @@
 #define ICH6_REG_CORBLBASE		0x40
 #define ICH6_REG_CORBUBASE		0x44
 #define ICH6_REG_CORBWP			0x48
-#define ICH6_REG_CORBRP			0x4A
+#define ICH6_REG_CORBRP			0x4a
+#define   ICH6_CORBRP_RST	(1 << 15)  /* read pointer reset */
 #define ICH6_REG_CORBCTL		0x4c
+#define   ICH6_CORBCTL_RUN	(1 << 1)   /* enable DMA */
+#define   ICH6_CORBCTL_CMEIE	(1 << 0)   /* enable memory error irq */
 #define ICH6_REG_CORBSTS		0x4d
+#define   ICH6_CORBSTS_CMEI	(1 << 0)   /* memory error indication */
 #define ICH6_REG_CORBSIZE		0x4e
 
 #define ICH6_REG_RIRBLBASE		0x50
 #define ICH6_REG_RIRBUBASE		0x54
 #define ICH6_REG_RIRBWP			0x58
+#define   ICH6_RIRBWP_RST	(1 << 15)  /* write pointer reset */
 #define ICH6_REG_RINTCNT		0x5a
 #define ICH6_REG_RIRBCTL		0x5c
+#define   ICH6_RBCTL_IRQ_EN	(1 << 0)   /* enable IRQ */
+#define   ICH6_RBCTL_DMA_EN	(1 << 1)   /* enable DMA */
+#define   ICH6_RBCTL_OVERRUN_EN	(1 << 2)   /* enable overrun irq */
 #define ICH6_REG_RIRBSTS		0x5d
+#define   ICH6_RBSTS_IRQ	(1 << 0)   /* response irq */
+#define   ICH6_RBSTS_OVERRUN	(1 << 2)   /* overrun irq */
 #define ICH6_REG_RIRBSIZE		0x5e
 
 #define ICH6_REG_IC			0x60
@@ -257,16 +279,6 @@
 #define ICH6_INT_CTRL_EN	0x40000000 /* controller interrupt enable bit */
 #define ICH6_INT_GLOBAL_EN	0x80000000 /* global interrupt enable bit */
 
-/* GCTL unsolicited response enable bit */
-#define ICH6_GCTL_UREN		(1<<8)
-
-/* GCTL reset bit */
-#define ICH6_GCTL_RESET		(1<<0)
-
-/* CORB/RIRB control, read/write pointer */
-#define ICH6_RBCTL_DMA_EN	0x02	/* enable DMA */
-#define ICH6_RBCTL_IRQ_EN	0x01	/* enable IRQ */
-#define ICH6_RBRWP_CLR		0x8000	/* read/write pointer clear */
 /* below are so far hardcoded - should read registers in future */
 #define ICH6_MAX_CORB_ENTRIES	256
 #define ICH6_MAX_RIRB_ENTRIES	256
@@ -512,25 +524,25 @@
 	/* set the corb write pointer to 0 */
 	azx_writew(chip, CORBWP, 0);
 	/* reset the corb hw read pointer */
-	azx_writew(chip, CORBRP, ICH6_RBRWP_CLR);
+	azx_writew(chip, CORBRP, ICH6_CORBRP_RST);
 	/* enable corb dma */
-	azx_writeb(chip, CORBCTL, ICH6_RBCTL_DMA_EN);
+	azx_writeb(chip, CORBCTL, ICH6_CORBCTL_RUN);
 
 	/* RIRB set up */
 	chip->rirb.addr = chip->rb.addr + 2048;
 	chip->rirb.buf = (u32 *)(chip->rb.area + 2048);
+	chip->rirb.wp = chip->rirb.rp = chip->rirb.cmds = 0;
 	azx_writel(chip, RIRBLBASE, (u32)chip->rirb.addr);
 	azx_writel(chip, RIRBUBASE, upper_32_bits(chip->rirb.addr));
 
 	/* set the rirb size to 256 entries (ULI requires explicitly) */
 	azx_writeb(chip, RIRBSIZE, 0x02);
 	/* reset the rirb hw write pointer */
-	azx_writew(chip, RIRBWP, ICH6_RBRWP_CLR);
+	azx_writew(chip, RIRBWP, ICH6_RIRBWP_RST);
 	/* set N=1, get RIRB response interrupt for new entry */
 	azx_writew(chip, RINTCNT, 1);
 	/* enable rirb dma and response irq */
 	azx_writeb(chip, RIRBCTL, ICH6_RBCTL_DMA_EN | ICH6_RBCTL_IRQ_EN);
-	chip->rirb.rp = chip->rirb.cmds = 0;
 }
 
 static void azx_free_cmd_io(struct azx *chip)
@@ -606,6 +618,7 @@
 		}
 		if (!chip->rirb.cmds) {
 			smp_rmb();
+			bus->rirb_error = 0;
 			return chip->rirb.res; /* the last value */
 		}
 		if (time_after(jiffies, timeout))
@@ -619,19 +632,21 @@
 	}
 
 	if (chip->msi) {
-		snd_printk(KERN_WARNING "hda_intel: No response from codec, "
+		snd_printk(KERN_WARNING SFX "No response from codec, "
 			   "disabling MSI: last cmd=0x%08x\n", chip->last_cmd);
 		free_irq(chip->irq, chip);
 		chip->irq = -1;
 		pci_disable_msi(chip->pci);
 		chip->msi = 0;
-		if (azx_acquire_irq(chip, 1) < 0)
+		if (azx_acquire_irq(chip, 1) < 0) {
+			bus->rirb_error = 1;
 			return -1;
+		}
 		goto again;
 	}
 
 	if (!chip->polling_mode) {
-		snd_printk(KERN_WARNING "hda_intel: azx_get_response timeout, "
+		snd_printk(KERN_WARNING SFX "azx_get_response timeout, "
 			   "switching to polling mode: last cmd=0x%08x\n",
 			   chip->last_cmd);
 		chip->polling_mode = 1;
@@ -646,14 +661,23 @@
 		return -1;
 	}
 
+	/* a fatal communication error; need either to reset or to fallback
+	 * to the single_cmd mode
+	 */
+	bus->rirb_error = 1;
+	if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) {
+		bus->response_reset = 1;
+		return -1; /* give a chance to retry */
+	}
+
 	snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
 		   "switching to single_cmd mode: last cmd=0x%08x\n",
 		   chip->last_cmd);
-	chip->rirb.rp = azx_readb(chip, RIRBWP);
-	chip->rirb.cmds = 0;
-	/* switch to single_cmd mode */
 	chip->single_cmd = 1;
+	bus->response_reset = 0;
+	/* re-initialize CORB/RIRB */
 	azx_free_cmd_io(chip);
+	azx_init_cmd_io(chip);
 	return -1;
 }
 
@@ -667,12 +691,34 @@
  *       I left the codes, however, for debugging/testing purposes.
  */
 
+/* receive a response */
+static int azx_single_wait_for_response(struct azx *chip)
+{
+	int timeout = 50;
+
+	while (timeout--) {
+		/* check IRV busy bit */
+		if (azx_readw(chip, IRS) & ICH6_IRS_VALID) {
+			/* reuse rirb.res as the response return value */
+			chip->rirb.res = azx_readl(chip, IR);
+			return 0;
+		}
+		udelay(1);
+	}
+	if (printk_ratelimit())
+		snd_printd(SFX "get_response timeout: IRS=0x%x\n",
+			   azx_readw(chip, IRS));
+	chip->rirb.res = -1;
+	return -EIO;
+}
+
 /* send a command */
 static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
 {
 	struct azx *chip = bus->private_data;
 	int timeout = 50;
 
+	bus->rirb_error = 0;
 	while (timeout--) {
 		/* check ICB busy bit */
 		if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
@@ -682,7 +728,7 @@
 			azx_writel(chip, IC, val);
 			azx_writew(chip, IRS, azx_readw(chip, IRS) |
 				   ICH6_IRS_BUSY);
-			return 0;
+			return azx_single_wait_for_response(chip);
 		}
 		udelay(1);
 	}
@@ -696,18 +742,7 @@
 static unsigned int azx_single_get_response(struct hda_bus *bus)
 {
 	struct azx *chip = bus->private_data;
-	int timeout = 50;
-
-	while (timeout--) {
-		/* check IRV busy bit */
-		if (azx_readw(chip, IRS) & ICH6_IRS_VALID)
-			return azx_readl(chip, IR);
-		udelay(1);
-	}
-	if (printk_ratelimit())
-		snd_printd(SFX "get_response timeout: IRS=0x%x\n",
-			   azx_readw(chip, IRS));
-	return (unsigned int)-1;
+	return chip->rirb.res;
 }
 
 /*
@@ -775,17 +810,17 @@
 
 	/* check to see if controller is ready */
 	if (!azx_readb(chip, GCTL)) {
-		snd_printd("azx_reset: controller not ready!\n");
+		snd_printd(SFX "azx_reset: controller not ready!\n");
 		return -EBUSY;
 	}
 
 	/* Accept unsolicited responses */
-	azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UREN);
+	azx_writel(chip, GCTL, azx_readl(chip, GCTL) | ICH6_GCTL_UNSOL);
 
 	/* detect codecs */
 	if (!chip->codec_mask) {
 		chip->codec_mask = azx_readw(chip, STATESTS);
-		snd_printdd("codec_mask = 0x%x\n", chip->codec_mask);
+		snd_printdd(SFX "codec_mask = 0x%x\n", chip->codec_mask);
 	}
 
 	return 0;
@@ -895,8 +930,7 @@
 	azx_int_enable(chip);
 
 	/* initialize the codec command I/O */
-	if (!chip->single_cmd)
-		azx_init_cmd_io(chip);
+	azx_init_cmd_io(chip);
 
 	/* program the position buffer */
 	azx_writel(chip, DPLBASE, (u32)chip->posbuf.addr);
@@ -953,12 +987,12 @@
 	case AZX_DRIVER_SCH:
 		pci_read_config_word(chip->pci, INTEL_SCH_HDA_DEVC, &snoop);
 		if (snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) {
-			pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC, \
+			pci_write_config_word(chip->pci, INTEL_SCH_HDA_DEVC,
 				snoop & (~INTEL_SCH_HDA_DEVC_NOSNOOP));
 			pci_read_config_word(chip->pci,
 				INTEL_SCH_HDA_DEVC, &snoop);
-			snd_printdd("HDA snoop disabled, enabling ... %s\n",\
-				(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP) \
+			snd_printdd(SFX "HDA snoop disabled, enabling ... %s\n",
+				(snoop & INTEL_SCH_HDA_DEVC_NOSNOOP)
 				? "Failed" : "OK");
 		}
 		break;
@@ -1012,7 +1046,7 @@
 	/* clear rirb int */
 	status = azx_readb(chip, RIRBSTS);
 	if (status & RIRB_INT_MASK) {
-		if (!chip->single_cmd && (status & RIRB_INT_RESPONSE))
+		if (status & RIRB_INT_RESPONSE)
 			azx_update_rirb(chip);
 		azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
 	}
@@ -1098,7 +1132,7 @@
 				pos_align;
 		pos_adj = frames_to_bytes(runtime, pos_adj);
 		if (pos_adj >= period_bytes) {
-			snd_printk(KERN_WARNING "Too big adjustment %d\n",
+			snd_printk(KERN_WARNING SFX "Too big adjustment %d\n",
 				   bdl_pos_adj[chip->dev_index]);
 			pos_adj = 0;
 		} else {
@@ -1122,7 +1156,7 @@
 	return 0;
 
  error:
-	snd_printk(KERN_ERR "Too many BDL entries: buffer=%d, period=%d\n",
+	snd_printk(KERN_ERR SFX "Too many BDL entries: buffer=%d, period=%d\n",
 		   azx_dev->bufsize, period_bytes);
 	return -EINVAL;
 }
@@ -1215,7 +1249,7 @@
 	chip->probing = 0;
 	if (res == -1)
 		return -EIO;
-	snd_printdd("hda_intel: codec #%d probed OK\n", addr);
+	snd_printdd(SFX "codec #%d probed OK\n", addr);
 	return 0;
 }
 
@@ -1223,6 +1257,26 @@
 				 struct hda_pcm *cpcm);
 static void azx_stop_chip(struct azx *chip);
 
+static void azx_bus_reset(struct hda_bus *bus)
+{
+	struct azx *chip = bus->private_data;
+
+	bus->in_reset = 1;
+	azx_stop_chip(chip);
+	azx_init_chip(chip);
+#ifdef CONFIG_PM
+	if (chip->initialized) {
+		int i;
+
+		for (i = 0; i < AZX_MAX_PCMS; i++)
+			snd_pcm_suspend_all(chip->pcm[i]);
+		snd_hda_suspend(chip->bus);
+		snd_hda_resume(chip->bus);
+	}
+#endif
+	bus->in_reset = 0;
+}
+
 /*
  * Codec initialization
  */
@@ -1246,6 +1300,7 @@
 	bus_temp.ops.command = azx_send_cmd;
 	bus_temp.ops.get_response = azx_get_response;
 	bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
+	bus_temp.ops.bus_reset = azx_bus_reset;
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 	bus_temp.power_save = &power_save;
 	bus_temp.ops.pm_notify = azx_power_notify;
@@ -1270,8 +1325,8 @@
 				/* Some BIOSen give you wrong codec addresses
 				 * that don't exist
 				 */
-				snd_printk(KERN_WARNING
-					   "hda_intel: Codec #%d probe error; "
+				snd_printk(KERN_WARNING SFX
+					   "Codec #%d probe error; "
 					   "disabling it...\n", c);
 				chip->codec_mask &= ~(1 << c);
 				/* More badly, accessing to a non-existing
@@ -1487,7 +1542,7 @@
 	bufsize = snd_pcm_lib_buffer_bytes(substream);
 	period_bytes = snd_pcm_lib_period_bytes(substream);
 
-	snd_printdd("azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
+	snd_printdd(SFX "azx_pcm_prepare: bufsize=0x%x, format=0x%x\n",
 		    bufsize, format_val);
 
 	if (bufsize != azx_dev->bufsize ||
@@ -1830,7 +1885,7 @@
 			  &pcm);
 	if (err < 0)
 		return err;
-	strcpy(pcm->name, cpcm->name);
+	strlcpy(pcm->name, cpcm->name, sizeof(pcm->name));
 	apcm = kzalloc(sizeof(*apcm), GFP_KERNEL);
 	if (apcm == NULL)
 		return -ENOMEM;
@@ -1973,7 +2028,7 @@
 	for (i = 0; i < AZX_MAX_PCMS; i++)
 		snd_pcm_suspend_all(chip->pcm[i]);
 	if (chip->initialized)
-		snd_hda_suspend(chip->bus, state);
+		snd_hda_suspend(chip->bus);
 	azx_stop_chip(chip);
 	if (chip->irq >= 0) {
 		free_irq(chip->irq, chip);
@@ -2265,14 +2320,14 @@
 	synchronize_irq(chip->irq);
 
 	gcap = azx_readw(chip, GCAP);
-	snd_printdd("chipset global capabilities = 0x%x\n", gcap);
+	snd_printdd(SFX "chipset global capabilities = 0x%x\n", gcap);
 
 	/* ATI chips seems buggy about 64bit DMA addresses */
 	if (chip->driver_type == AZX_DRIVER_ATI)
-		gcap &= ~0x01;
+		gcap &= ~ICH6_GCAP_64OK;
 
 	/* allow 64bit DMA address if supported by H/W */
-	if ((gcap & 0x01) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
+	if ((gcap & ICH6_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
 		pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
 	else {
 		pci_set_dma_mask(pci, DMA_BIT_MASK(32));
@@ -2309,7 +2364,7 @@
 	chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev),
 				GFP_KERNEL);
 	if (!chip->azx_dev) {
-		snd_printk(KERN_ERR "cannot malloc azx_dev\n");
+		snd_printk(KERN_ERR SFX "cannot malloc azx_dev\n");
 		goto errout;
 	}
 
@@ -2332,11 +2387,9 @@
 		goto errout;
 	}
 	/* allocate CORB/RIRB */
-	if (!chip->single_cmd) {
-		err = azx_alloc_cmd_io(chip);
-		if (err < 0)
-			goto errout;
-	}
+	err = azx_alloc_cmd_io(chip);
+	if (err < 0)
+		goto errout;
 
 	/* initialize streams */
 	azx_init_stream(chip);
@@ -2359,9 +2412,11 @@
 	}
 
 	strcpy(card->driver, "HDA-Intel");
-	strcpy(card->shortname, driver_short_names[chip->driver_type]);
-	sprintf(card->longname, "%s at 0x%lx irq %i",
-		card->shortname, chip->addr, chip->irq);
+	strlcpy(card->shortname, driver_short_names[chip->driver_type],
+		sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s at 0x%lx irq %i",
+		 card->shortname, chip->addr, chip->irq);
 
 	*rchip = chip;
 	return 0;
@@ -2514,6 +2569,20 @@
 	{ PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA },
 	/* Teradici */
 	{ PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
+	/* Creative X-Fi (CA0110-IBG) */
+#if !defined(CONFIG_SND_CTXFI) && !defined(CONFIG_SND_CTXFI_MODULE)
+	/* the following entry conflicts with snd-ctxfi driver,
+	 * as ctxfi driver mutates from HD-audio to native mode with
+	 * a special command sequence.
+	 */
+	{ PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
+	  .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+	  .class_mask = 0xffffff,
+	  .driver_data = AZX_DRIVER_GENERIC },
+#else
+	/* this entry seems still valid -- i.e. without emu20kx chip */
+	{ PCI_DEVICE(0x1102, 0x0009), .driver_data = AZX_DRIVER_GENERIC },
+#endif
 	/* AMD Generic, PCI class code and Vendor ID for HD Audio */
 	{ PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
 	  .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 93d7499..418c5d1 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -466,8 +466,12 @@
 	hda_nid_t nid;
 	int i, nodes;
 
-	snd_iprintf(buffer, "Codec: %s\n",
-		    codec->name ? codec->name : "Not Set");
+	snd_iprintf(buffer, "Codec: ");
+	if (codec->vendor_name && codec->chip_name)
+		snd_iprintf(buffer, "%s %s\n",
+			    codec->vendor_name, codec->chip_name);
+	else
+		snd_iprintf(buffer, "Not Set\n");
 	snd_iprintf(buffer, "Address: %d\n", codec->addr);
 	snd_iprintf(buffer, "Function Id: 0x%x\n", codec->function_id);
 	snd_iprintf(buffer, "Vendor Id: 0x%08x\n", codec->vendor_id);
diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c
new file mode 100644
index 0000000..392d108
--- /dev/null
+++ b/sound/pci/hda/patch_ca0110.c
@@ -0,0 +1,573 @@
+/*
+ * HD audio interface patch for Creative X-Fi CA0110-IBG chip
+ *
+ * Copyright (c) 2008 Takashi Iwai <tiwai@suse.de>
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This driver is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+
+/*
+ */
+
+struct ca0110_spec {
+	struct auto_pin_cfg autocfg;
+	struct hda_multi_out multiout;
+	hda_nid_t out_pins[AUTO_CFG_MAX_OUTS];
+	hda_nid_t dacs[AUTO_CFG_MAX_OUTS];
+	hda_nid_t hp_dac;
+	hda_nid_t input_pins[AUTO_PIN_LAST];
+	hda_nid_t adcs[AUTO_PIN_LAST];
+	hda_nid_t dig_out;
+	hda_nid_t dig_in;
+	unsigned int num_inputs;
+	const char *input_labels[AUTO_PIN_LAST];
+	struct hda_pcm pcm_rec[2];	/* PCM information */
+};
+
+/*
+ * PCM callbacks
+ */
+static int ca0110_playback_pcm_open(struct hda_pcm_stream *hinfo,
+				    struct hda_codec *codec,
+				    struct snd_pcm_substream *substream)
+{
+	struct ca0110_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+					     hinfo);
+}
+
+static int ca0110_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       unsigned int stream_tag,
+				       unsigned int format,
+				       struct snd_pcm_substream *substream)
+{
+	struct ca0110_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+						stream_tag, format, substream);
+}
+
+static int ca0110_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				       struct hda_codec *codec,
+				       struct snd_pcm_substream *substream)
+{
+	struct ca0110_spec *spec = codec->spec;
+	return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
+}
+
+/*
+ * Digital out
+ */
+static int ca0110_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
+{
+	struct ca0110_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
+static int ca0110_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+					 struct hda_codec *codec,
+					 struct snd_pcm_substream *substream)
+{
+	struct ca0110_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int ca0110_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
+					   struct hda_codec *codec,
+					   unsigned int stream_tag,
+					   unsigned int format,
+					   struct snd_pcm_substream *substream)
+{
+	struct ca0110_spec *spec = codec->spec;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
+					     format, substream);
+}
+
+/*
+ * Analog capture
+ */
+static int ca0110_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      unsigned int stream_tag,
+				      unsigned int format,
+				      struct snd_pcm_substream *substream)
+{
+	struct ca0110_spec *spec = codec->spec;
+
+	snd_hda_codec_setup_stream(codec, spec->adcs[substream->number],
+				   stream_tag, 0, format);
+	return 0;
+}
+
+static int ca0110_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
+				      struct hda_codec *codec,
+				      struct snd_pcm_substream *substream)
+{
+	struct ca0110_spec *spec = codec->spec;
+
+	snd_hda_codec_cleanup_stream(codec, spec->adcs[substream->number]);
+	return 0;
+}
+
+/*
+ */
+
+static char *dirstr[2] = { "Playback", "Capture" };
+
+static int _add_switch(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+		       int chan, int dir)
+{
+	char namestr[44];
+	int type = dir ? HDA_INPUT : HDA_OUTPUT;
+	struct snd_kcontrol_new knew =
+		HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
+	sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
+	return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+}
+
+static int _add_volume(struct hda_codec *codec, hda_nid_t nid, const char *pfx,
+		       int chan, int dir)
+{
+	char namestr[44];
+	int type = dir ? HDA_INPUT : HDA_OUTPUT;
+	struct snd_kcontrol_new knew =
+		HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
+	sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
+	return snd_hda_ctl_add(codec, snd_ctl_new1(&knew, codec));
+}
+
+#define add_out_switch(codec, nid, pfx)	_add_switch(codec, nid, pfx, 3, 0)
+#define add_out_volume(codec, nid, pfx)	_add_volume(codec, nid, pfx, 3, 0)
+#define add_in_switch(codec, nid, pfx)	_add_switch(codec, nid, pfx, 3, 1)
+#define add_in_volume(codec, nid, pfx)	_add_volume(codec, nid, pfx, 3, 1)
+#define add_mono_switch(codec, nid, pfx, chan) \
+	_add_switch(codec, nid, pfx, chan, 0)
+#define add_mono_volume(codec, nid, pfx, chan) \
+	_add_volume(codec, nid, pfx, chan, 0)
+
+static int ca0110_build_controls(struct hda_codec *codec)
+{
+	struct ca0110_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	static char *prefix[AUTO_CFG_MAX_OUTS] = {
+		"Front", "Surround", NULL, "Side", "Multi"
+	};
+	hda_nid_t mutenid;
+	int i, err;
+
+	for (i = 0; i < spec->multiout.num_dacs; i++) {
+		if (get_wcaps(codec, spec->out_pins[i]) & AC_WCAP_OUT_AMP)
+			mutenid = spec->out_pins[i];
+		else
+			mutenid = spec->multiout.dac_nids[i];
+		if (!prefix[i]) {
+			err = add_mono_switch(codec, mutenid,
+					      "Center", 1);
+			if (err < 0)
+				return err;
+			err = add_mono_switch(codec, mutenid,
+					      "LFE", 1);
+			if (err < 0)
+				return err;
+			err = add_mono_volume(codec, spec->multiout.dac_nids[i],
+					      "Center", 1);
+			if (err < 0)
+				return err;
+			err = add_mono_volume(codec, spec->multiout.dac_nids[i],
+					      "LFE", 1);
+			if (err < 0)
+				return err;
+		} else {
+			err = add_out_switch(codec, mutenid,
+					     prefix[i]);
+			if (err < 0)
+				return err;
+			err = add_out_volume(codec, spec->multiout.dac_nids[i],
+					 prefix[i]);
+			if (err < 0)
+				return err;
+		}
+	}
+	if (cfg->hp_outs) {
+		if (get_wcaps(codec, cfg->hp_pins[0]) & AC_WCAP_OUT_AMP)
+			mutenid = cfg->hp_pins[0];
+		else
+			mutenid = spec->multiout.dac_nids[i];
+
+		err = add_out_switch(codec, mutenid, "Headphone");
+		if (err < 0)
+			return err;
+		if (spec->hp_dac) {
+			err = add_out_volume(codec, spec->hp_dac, "Headphone");
+			if (err < 0)
+				return err;
+		}
+	}
+	for (i = 0; i < spec->num_inputs; i++) {
+		const char *label = spec->input_labels[i];
+		if (get_wcaps(codec, spec->input_pins[i]) & AC_WCAP_IN_AMP)
+			mutenid = spec->input_pins[i];
+		else
+			mutenid = spec->adcs[i];
+		err = add_in_switch(codec, mutenid, label);
+		if (err < 0)
+			return err;
+		err = add_in_volume(codec, spec->adcs[i], label);
+		if (err < 0)
+			return err;
+	}
+
+	if (spec->dig_out) {
+		err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out);
+		if (err < 0)
+			return err;
+		err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
+		if (err < 0)
+			return err;
+		spec->multiout.share_spdif = 1;
+	}
+	if (spec->dig_in) {
+		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
+		if (err < 0)
+			return err;
+		err = add_in_volume(codec, spec->dig_in, "IEC958");
+	}
+	return 0;
+}
+
+/*
+ */
+static struct hda_pcm_stream ca0110_pcm_analog_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 8,
+	.ops = {
+		.open = ca0110_playback_pcm_open,
+		.prepare = ca0110_playback_pcm_prepare,
+		.cleanup = ca0110_playback_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream ca0110_pcm_analog_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.ops = {
+		.prepare = ca0110_capture_pcm_prepare,
+		.cleanup = ca0110_capture_pcm_cleanup
+	},
+};
+
+static struct hda_pcm_stream ca0110_pcm_digital_playback = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.ops = {
+		.open = ca0110_dig_playback_pcm_open,
+		.close = ca0110_dig_playback_pcm_close,
+		.prepare = ca0110_dig_playback_pcm_prepare
+	},
+};
+
+static struct hda_pcm_stream ca0110_pcm_digital_capture = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+};
+
+static int ca0110_build_pcms(struct hda_codec *codec)
+{
+	struct ca0110_spec *spec = codec->spec;
+	struct hda_pcm *info = spec->pcm_rec;
+
+	codec->pcm_info = info;
+	codec->num_pcms = 0;
+
+	info->name = "CA0110 Analog";
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0110_pcm_analog_playback;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0];
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
+		spec->multiout.max_channels;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0110_pcm_analog_capture;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_inputs;
+	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0];
+	codec->num_pcms++;
+
+	if (!spec->dig_out && !spec->dig_in)
+		return 0;
+
+	info++;
+	info->name = "CA0110 Digital";
+	info->pcm_type = HDA_PCM_TYPE_SPDIF;
+	if (spec->dig_out) {
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
+			ca0110_pcm_digital_playback;
+		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out;
+	}
+	if (spec->dig_in) {
+		info->stream[SNDRV_PCM_STREAM_CAPTURE] =
+			ca0110_pcm_digital_capture;
+		info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in;
+	}
+	codec->num_pcms++;
+
+	return 0;
+}
+
+static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
+{
+	if (pin) {
+		snd_hda_codec_write(codec, pin, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+		if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
+			snd_hda_codec_write(codec, pin, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_OUT_UNMUTE);
+	}
+	if (dac)
+		snd_hda_codec_write(codec, dac, 0,
+				    AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
+}
+
+static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
+{
+	if (pin) {
+		snd_hda_codec_write(codec, pin, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
+		if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
+			snd_hda_codec_write(codec, pin, 0,
+					    AC_VERB_SET_AMP_GAIN_MUTE,
+					    AMP_IN_UNMUTE(0));
+	}
+	if (adc)
+		snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
+				    AMP_IN_UNMUTE(0));
+}
+
+static int ca0110_init(struct hda_codec *codec)
+{
+	struct ca0110_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+
+	for (i = 0; i < spec->multiout.num_dacs; i++)
+		init_output(codec, spec->out_pins[i],
+			    spec->multiout.dac_nids[i]);
+	init_output(codec, cfg->hp_pins[0], spec->hp_dac);
+	init_output(codec, cfg->dig_out_pins[0], spec->dig_out);
+
+	for (i = 0; i < spec->num_inputs; i++)
+		init_input(codec, spec->input_pins[i], spec->adcs[i]);
+	init_input(codec, cfg->dig_in_pin, spec->dig_in);
+	return 0;
+}
+
+static void ca0110_free(struct hda_codec *codec)
+{
+	kfree(codec->spec);
+}
+
+static struct hda_codec_ops ca0110_patch_ops = {
+	.build_controls = ca0110_build_controls,
+	.build_pcms = ca0110_build_pcms,
+	.init = ca0110_init,
+	.free = ca0110_free,
+};
+
+
+static void parse_line_outs(struct hda_codec *codec)
+{
+	struct ca0110_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i, n;
+	unsigned int def_conf;
+	hda_nid_t nid;
+
+	n = 0;
+	for (i = 0; i < cfg->line_outs; i++) {
+		nid = cfg->line_out_pins[i];
+		def_conf = snd_hda_codec_get_pincfg(codec, nid);
+		if (!def_conf)
+			continue; /* invalid pin */
+		if (snd_hda_get_connections(codec, nid, &spec->dacs[i], 1) != 1)
+			continue;
+		spec->out_pins[n++] = nid;
+	}
+	spec->multiout.dac_nids = spec->dacs;
+	spec->multiout.num_dacs = n;
+	spec->multiout.max_channels = n * 2;
+}
+
+static void parse_hp_out(struct hda_codec *codec)
+{
+	struct ca0110_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	int i;
+	unsigned int def_conf;
+	hda_nid_t nid, dac;
+
+	if (!cfg->hp_outs)
+		return;
+	nid = cfg->hp_pins[0];
+	def_conf = snd_hda_codec_get_pincfg(codec, nid);
+	if (!def_conf) {
+		cfg->hp_outs = 0;
+		return;
+	}
+	if (snd_hda_get_connections(codec, nid, &dac, 1) != 1)
+		return;
+
+	for (i = 0; i < cfg->line_outs; i++)
+		if (dac == spec->dacs[i])
+			break;
+	if (i >= cfg->line_outs) {
+		spec->hp_dac = dac;
+		spec->multiout.hp_nid = dac;
+	}
+}
+
+static void parse_input(struct hda_codec *codec)
+{
+	struct ca0110_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+	hda_nid_t nid, pin;
+	int n, i, j;
+
+	n = 0;
+	nid = codec->start_nid;
+	for (i = 0; i < codec->num_nodes; i++, nid++) {
+		unsigned int wcaps = get_wcaps(codec, nid);
+		unsigned int type = (wcaps & AC_WCAP_TYPE) >>
+			AC_WCAP_TYPE_SHIFT;
+		if (type != AC_WID_AUD_IN)
+			continue;
+		if (snd_hda_get_connections(codec, nid, &pin, 1) != 1)
+			continue;
+		if (pin == cfg->dig_in_pin) {
+			spec->dig_in = nid;
+			continue;
+		}
+		for (j = 0; j < AUTO_PIN_LAST; j++)
+			if (cfg->input_pins[j] == pin)
+				break;
+		if (j >= AUTO_PIN_LAST)
+			continue;
+		spec->input_pins[n] = pin;
+		spec->input_labels[n] = auto_pin_cfg_labels[j];
+		spec->adcs[n] = nid;
+		n++;
+	}
+	spec->num_inputs = n;
+}
+
+static void parse_digital(struct hda_codec *codec)
+{
+	struct ca0110_spec *spec = codec->spec;
+	struct auto_pin_cfg *cfg = &spec->autocfg;
+
+	if (cfg->dig_outs &&
+	    snd_hda_get_connections(codec, cfg->dig_out_pins[0],
+				    &spec->dig_out, 1) == 1)
+		spec->multiout.dig_out_nid = cfg->dig_out_pins[0];
+}
+
+static int ca0110_parse_auto_config(struct hda_codec *codec)
+{
+	struct ca0110_spec *spec = codec->spec;
+	int err;
+
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
+	if (err < 0)
+		return err;
+
+	parse_line_outs(codec);
+	parse_hp_out(codec);
+	parse_digital(codec);
+	parse_input(codec);
+	return 0;
+}
+
+
+int patch_ca0110(struct hda_codec *codec)
+{
+	struct ca0110_spec *spec;
+	int err;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (!spec)
+		return -ENOMEM;
+	codec->spec = spec;
+
+	codec->bus->needs_damn_long_delay = 1;
+
+	err = ca0110_parse_auto_config(codec);
+	if (err < 0)
+		goto error;
+
+	codec->patch_ops = ca0110_patch_ops;
+
+	return 0;
+
+ error:
+	kfree(codec->spec);
+	codec->spec = NULL;
+	return err;
+}
+
+
+/*
+ * patch entries
+ */
+static struct hda_codec_preset snd_hda_preset_ca0110[] = {
+	{ .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
+	{ .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
+	{ .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
+	{} /* terminator */
+};
+
+MODULE_ALIAS("snd-hda-codec-id:1102000a");
+MODULE_ALIAS("snd-hda-codec-id:1102000b");
+MODULE_ALIAS("snd-hda-codec-id:1102000d");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
+
+static struct hda_codec_preset_list ca0110_list = {
+	.preset = snd_hda_preset_ca0110,
+	.owner = THIS_MODULE,
+};
+
+static int __init patch_ca0110_init(void)
+{
+	return snd_hda_add_codec_preset(&ca0110_list);
+}
+
+static void __exit patch_ca0110_exit(void)
+{
+	snd_hda_delete_codec_preset(&ca0110_list);
+}
+
+module_init(patch_ca0110_init)
+module_exit(patch_ca0110_exit)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 4fcbe21..ac868c5 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -349,7 +349,7 @@
 				     &spec->cur_mux[adc_idx]);
 }
 
-#ifdef CONFIG_SND_JACK
+#ifdef CONFIG_SND_HDA_INPUT_JACK
 static void conexant_free_jack_priv(struct snd_jack *jack)
 {
 	struct conexant_jack *jacks = jack->private_data;
@@ -463,7 +463,7 @@
 
 static void conexant_free(struct hda_codec *codec)
 {
-#ifdef CONFIG_SND_JACK
+#ifdef CONFIG_SND_HDA_INPUT_JACK
 	struct conexant_spec *spec = codec->spec;
 	if (spec->jacks.list) {
 		struct conexant_jack *jacks = spec->jacks.list;
diff --git a/sound/pci/hda/patch_nvhdmi.c b/sound/pci/hda/patch_nvhdmi.c
index d57d813..f5792e2 100644
--- a/sound/pci/hda/patch_nvhdmi.c
+++ b/sound/pci/hda/patch_nvhdmi.c
@@ -35,9 +35,28 @@
 	struct hda_pcm pcm_rec;
 };
 
+#define Nv_VERB_SET_Channel_Allocation          0xF79
+#define Nv_VERB_SET_Info_Frame_Checksum         0xF7A
+#define Nv_VERB_SET_Audio_Protection_On         0xF98
+#define Nv_VERB_SET_Audio_Protection_Off        0xF99
+
+#define Nv_Master_Convert_nid   0x04
+#define Nv_Master_Pin_nid       0x05
+
+static hda_nid_t nvhdmi_convert_nids[4] = {
+	/*front, rear, clfe, rear_surr */
+	0x6, 0x8, 0xa, 0xc,
+};
+
 static struct hda_verb nvhdmi_basic_init[] = {
+	/* set audio protect on */
+	{ 0x1, Nv_VERB_SET_Audio_Protection_On, 0x1},
 	/* enable digital output on pin widget */
-	{ 0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x5, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0x7, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0x9, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0xb, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
+	{ 0xd, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x5 },
 	{} /* terminator */
 };
 
@@ -66,48 +85,205 @@
  * Digital out
  */
 static int nvhdmi_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
-				     struct hda_codec *codec,
-				     struct snd_pcm_substream *substream)
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
 {
 	struct nvhdmi_spec *spec = codec->spec;
 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
 }
 
-static int nvhdmi_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
-				      struct hda_codec *codec,
-				      struct snd_pcm_substream *substream)
+static int nvhdmi_dig_playback_pcm_close_8ch(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
+{
+	struct nvhdmi_spec *spec = codec->spec;
+	int i;
+
+	snd_hda_codec_write(codec, Nv_Master_Convert_nid,
+			0, AC_VERB_SET_CHANNEL_STREAMID, 0);
+	for (i = 0; i < 4; i++) {
+		/* set the stream id */
+		snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
+				AC_VERB_SET_CHANNEL_STREAMID, 0);
+		/* set the stream format */
+		snd_hda_codec_write(codec, nvhdmi_convert_nids[i], 0,
+				AC_VERB_SET_STREAM_FORMAT, 0);
+	}
+
+	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
+}
+
+static int nvhdmi_dig_playback_pcm_close_2ch(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
 {
 	struct nvhdmi_spec *spec = codec->spec;
 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
-static int nvhdmi_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-					    struct hda_codec *codec,
-					    unsigned int stream_tag,
-					    unsigned int format,
-					    struct snd_pcm_substream *substream)
+static int nvhdmi_dig_playback_pcm_prepare_8ch(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					unsigned int stream_tag,
+					unsigned int format,
+					struct snd_pcm_substream *substream)
+{
+	int chs;
+	unsigned int dataDCC1, dataDCC2, chan, chanmask, channel_id;
+	int i;
+
+	mutex_lock(&codec->spdif_mutex);
+
+	chs = substream->runtime->channels;
+	chan = chs ? (chs - 1) : 1;
+
+	switch (chs) {
+	default:
+	case 0:
+	case 2:
+		chanmask = 0x00;
+		break;
+	case 4:
+		chanmask = 0x08;
+		break;
+	case 6:
+		chanmask = 0x0b;
+		break;
+	case 8:
+		chanmask = 0x13;
+		break;
+	}
+	dataDCC1 = AC_DIG1_ENABLE | AC_DIG1_COPYRIGHT;
+	dataDCC2 = 0x2;
+
+	/* set the Audio InforFrame Channel Allocation */
+	snd_hda_codec_write(codec, 0x1, 0,
+			Nv_VERB_SET_Channel_Allocation, chanmask);
+
+	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
+	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE))
+		snd_hda_codec_write(codec,
+				Nv_Master_Convert_nid,
+				0,
+				AC_VERB_SET_DIGI_CONVERT_1,
+				codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+
+	/* set the stream id */
+	snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
+			AC_VERB_SET_CHANNEL_STREAMID, (stream_tag << 4) | 0x0);
+
+	/* set the stream format */
+	snd_hda_codec_write(codec, Nv_Master_Convert_nid, 0,
+			AC_VERB_SET_STREAM_FORMAT, format);
+
+	/* turn on again (if needed) */
+	/* enable and set the channel status audio/data flag */
+	if (codec->spdif_status_reset && (codec->spdif_ctls & AC_DIG1_ENABLE)) {
+		snd_hda_codec_write(codec,
+				Nv_Master_Convert_nid,
+				0,
+				AC_VERB_SET_DIGI_CONVERT_1,
+				codec->spdif_ctls & 0xff);
+		snd_hda_codec_write(codec,
+				Nv_Master_Convert_nid,
+				0,
+				AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+	}
+
+	for (i = 0; i < 4; i++) {
+		if (chs == 2)
+			channel_id = 0;
+		else
+			channel_id = i * 2;
+
+		/* turn off SPDIF once;
+		 *otherwise the IEC958 bits won't be updated
+		 */
+		if (codec->spdif_status_reset &&
+		(codec->spdif_ctls & AC_DIG1_ENABLE))
+			snd_hda_codec_write(codec,
+				nvhdmi_convert_nids[i],
+				0,
+				AC_VERB_SET_DIGI_CONVERT_1,
+				codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
+		/* set the stream id */
+		snd_hda_codec_write(codec,
+				nvhdmi_convert_nids[i],
+				0,
+				AC_VERB_SET_CHANNEL_STREAMID,
+				(stream_tag << 4) | channel_id);
+		/* set the stream format */
+		snd_hda_codec_write(codec,
+				nvhdmi_convert_nids[i],
+				0,
+				AC_VERB_SET_STREAM_FORMAT,
+				format);
+		/* turn on again (if needed) */
+		/* enable and set the channel status audio/data flag */
+		if (codec->spdif_status_reset &&
+		(codec->spdif_ctls & AC_DIG1_ENABLE)) {
+			snd_hda_codec_write(codec,
+					nvhdmi_convert_nids[i],
+					0,
+					AC_VERB_SET_DIGI_CONVERT_1,
+					codec->spdif_ctls & 0xff);
+			snd_hda_codec_write(codec,
+					nvhdmi_convert_nids[i],
+					0,
+					AC_VERB_SET_DIGI_CONVERT_2, dataDCC2);
+		}
+	}
+
+	/* set the Audio Info Frame Checksum */
+	snd_hda_codec_write(codec, 0x1, 0,
+			Nv_VERB_SET_Info_Frame_Checksum,
+			(0x71 - chan - chanmask));
+
+	mutex_unlock(&codec->spdif_mutex);
+	return 0;
+}
+
+static int nvhdmi_dig_playback_pcm_prepare_2ch(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					unsigned int stream_tag,
+					unsigned int format,
+					struct snd_pcm_substream *substream)
 {
 	struct nvhdmi_spec *spec = codec->spec;
 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
-					     format, substream);
+					format, substream);
 }
 
-static struct hda_pcm_stream nvhdmi_pcm_digital_playback = {
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback_8ch = {
 	.substreams = 1,
 	.channels_min = 2,
-	.channels_max = 2,
-	.nid = 0x4, /* NID to query formats and rates and setup streams */
+	.channels_max = 8,
+	.nid = Nv_Master_Convert_nid,
 	.rates = SNDRV_PCM_RATE_48000,
 	.maxbps = 16,
 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	.ops = {
 		.open = nvhdmi_dig_playback_pcm_open,
-		.close = nvhdmi_dig_playback_pcm_close,
-		.prepare = nvhdmi_dig_playback_pcm_prepare
+		.close = nvhdmi_dig_playback_pcm_close_8ch,
+		.prepare = nvhdmi_dig_playback_pcm_prepare_8ch
 	},
 };
 
-static int nvhdmi_build_pcms(struct hda_codec *codec)
+static struct hda_pcm_stream nvhdmi_pcm_digital_playback_2ch = {
+	.substreams = 1,
+	.channels_min = 2,
+	.channels_max = 2,
+	.nid = Nv_Master_Convert_nid,
+	.rates = SNDRV_PCM_RATE_48000,
+	.maxbps = 16,
+	.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	.ops = {
+		.open = nvhdmi_dig_playback_pcm_open,
+		.close = nvhdmi_dig_playback_pcm_close_2ch,
+		.prepare = nvhdmi_dig_playback_pcm_prepare_2ch
+	},
+};
+
+static int nvhdmi_build_pcms_8ch(struct hda_codec *codec)
 {
 	struct nvhdmi_spec *spec = codec->spec;
 	struct hda_pcm *info = &spec->pcm_rec;
@@ -117,7 +293,24 @@
 
 	info->name = "NVIDIA HDMI";
 	info->pcm_type = HDA_PCM_TYPE_HDMI;
-	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = nvhdmi_pcm_digital_playback;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK]
+					= nvhdmi_pcm_digital_playback_8ch;
+
+	return 0;
+}
+
+static int nvhdmi_build_pcms_2ch(struct hda_codec *codec)
+{
+	struct nvhdmi_spec *spec = codec->spec;
+	struct hda_pcm *info = &spec->pcm_rec;
+
+	codec->num_pcms = 1;
+	codec->pcm_info = info;
+
+	info->name = "NVIDIA HDMI";
+	info->pcm_type = HDA_PCM_TYPE_HDMI;
+	info->stream[SNDRV_PCM_STREAM_PLAYBACK]
+					= nvhdmi_pcm_digital_playback_2ch;
 
 	return 0;
 }
@@ -127,14 +320,21 @@
 	kfree(codec->spec);
 }
 
-static struct hda_codec_ops nvhdmi_patch_ops = {
+static struct hda_codec_ops nvhdmi_patch_ops_8ch = {
 	.build_controls = nvhdmi_build_controls,
-	.build_pcms = nvhdmi_build_pcms,
+	.build_pcms = nvhdmi_build_pcms_8ch,
 	.init = nvhdmi_init,
 	.free = nvhdmi_free,
 };
 
-static int patch_nvhdmi(struct hda_codec *codec)
+static struct hda_codec_ops nvhdmi_patch_ops_2ch = {
+	.build_controls = nvhdmi_build_controls,
+	.build_pcms = nvhdmi_build_pcms_2ch,
+	.init = nvhdmi_init,
+	.free = nvhdmi_free,
+};
+
+static int patch_nvhdmi_8ch(struct hda_codec *codec)
 {
 	struct nvhdmi_spec *spec;
 
@@ -144,13 +344,30 @@
 
 	codec->spec = spec;
 
-	spec->multiout.num_dacs = 0;	  /* no analog */
-	spec->multiout.max_channels = 2;
-	spec->multiout.dig_out_nid = 0x4; /* NID for copying analog to digital,
-					   * seems to be unused in pure-digital
-					   * case. */
+	spec->multiout.num_dacs = 0;  /* no analog */
+	spec->multiout.max_channels = 8;
+	spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
 
-	codec->patch_ops = nvhdmi_patch_ops;
+	codec->patch_ops = nvhdmi_patch_ops_8ch;
+
+	return 0;
+}
+
+static int patch_nvhdmi_2ch(struct hda_codec *codec)
+{
+	struct nvhdmi_spec *spec;
+
+	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+	if (spec == NULL)
+		return -ENOMEM;
+
+	codec->spec = spec;
+
+	spec->multiout.num_dacs = 0;  /* no analog */
+	spec->multiout.max_channels = 2;
+	spec->multiout.dig_out_nid = Nv_Master_Convert_nid;
+
+	codec->patch_ops = nvhdmi_patch_ops_2ch;
 
 	return 0;
 }
@@ -159,11 +376,11 @@
  * patch entries
  */
 static struct hda_codec_preset snd_hda_preset_nvhdmi[] = {
-	{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
-	{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi },
-	{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi },
-	{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi },
-	{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi },
+	{ .id = 0x10de0002, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
+	{ .id = 0x10de0006, .name = "MCP78 HDMI", .patch = patch_nvhdmi_8ch },
+	{ .id = 0x10de0007, .name = "MCP7A HDMI", .patch = patch_nvhdmi_8ch },
+	{ .id = 0x10de0067, .name = "MCP67 HDMI", .patch = patch_nvhdmi_2ch },
+	{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
 	{} /* terminator */
 };
 
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 0fd258e..3345331 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -190,6 +190,7 @@
 	ALC663_ASUS_MODE6,
 	ALC272_DELL,
 	ALC272_DELL_ZM1,
+	ALC272_SAMSUNG_NC10,
 	ALC662_AUTO,
 	ALC662_MODEL_LAST,
 };
@@ -205,6 +206,7 @@
 	ALC882_ASUS_A7M,
 	ALC885_MACPRO,
 	ALC885_MBP3,
+	ALC885_MB5,
 	ALC885_IMAC24,
 	ALC882_AUTO,
 	ALC882_MODEL_LAST,
@@ -218,9 +220,12 @@
 	ALC883_6ST_DIG,
 	ALC883_TARGA_DIG,
 	ALC883_TARGA_2ch_DIG,
+	ALC883_TARGA_8ch_DIG,
 	ALC883_ACER,
 	ALC883_ACER_ASPIRE,
 	ALC888_ACER_ASPIRE_4930G,
+	ALC888_ACER_ASPIRE_6530G,
+	ALC888_ACER_ASPIRE_8930G,
 	ALC883_MEDION,
 	ALC883_MEDION_MD2,
 	ALC883_LAPTOP_EAPD,
@@ -238,21 +243,25 @@
 	ALC883_3ST_6ch_INTEL,
 	ALC888_ASUS_M90V,
 	ALC888_ASUS_EEE1601,
+	ALC889A_MB31,
 	ALC1200_ASUS_P5Q,
+	ALC883_SONY_VAIO_TT,
 	ALC883_AUTO,
 	ALC883_MODEL_LAST,
 };
 
-/* styles of capture selection */
-enum {
-	CAPT_MUX = 0,	/* only mux based */
-	CAPT_MIX,	/* only mixer based */
-	CAPT_1MUX_MIX,	/* first mux and other mixers */
-};
-
 /* for GPIO Poll */
 #define GPIO_MASK	0x03
 
+/* extra amp-initialization sequence types */
+enum {
+	ALC_INIT_NONE,
+	ALC_INIT_DEFAULT,
+	ALC_INIT_GPIO1,
+	ALC_INIT_GPIO2,
+	ALC_INIT_GPIO3,
+};
+
 struct alc_spec {
 	/* codec parameterization */
 	struct snd_kcontrol_new *mixers[5];	/* mixer arrays */
@@ -266,13 +275,13 @@
 						 */
 	unsigned int num_init_verbs;
 
-	char *stream_name_analog;	/* analog PCM stream */
+	char stream_name_analog[16];	/* analog PCM stream */
 	struct hda_pcm_stream *stream_analog_playback;
 	struct hda_pcm_stream *stream_analog_capture;
 	struct hda_pcm_stream *stream_analog_alt_playback;
 	struct hda_pcm_stream *stream_analog_alt_capture;
 
-	char *stream_name_digital;	/* digital PCM stream */
+	char stream_name_digital[16];	/* digital PCM stream */
 	struct hda_pcm_stream *stream_digital_playback;
 	struct hda_pcm_stream *stream_digital_capture;
 
@@ -290,7 +299,6 @@
 	hda_nid_t *adc_nids;
 	hda_nid_t *capsrc_nids;
 	hda_nid_t dig_in_nid;		/* digital-in NID; optional */
-	int capture_style;		/* capture style (CAPT_*) */
 
 	/* capture source */
 	unsigned int num_mux_defs;
@@ -301,6 +309,8 @@
 	const struct hda_channel_mode *channel_mode;
 	int num_channel_mode;
 	int need_dac_fix;
+	int const_channel_count;
+	int ext_channel_count;
 
 	/* PCM information */
 	struct hda_pcm pcm_rec[3];	/* used in alc_build_pcms() */
@@ -322,6 +332,7 @@
 
 	/* other flags */
 	unsigned int no_analog :1; /* digital I/O only */
+	int init_amp;
 
 	/* for virtual master */
 	hda_nid_t vmaster_nid;
@@ -355,6 +366,7 @@
 	unsigned int num_channel_mode;
 	const struct hda_channel_mode *channel_mode;
 	int need_dac_fix;
+	int const_channel_count;
 	unsigned int num_mux_defs;
 	const struct hda_input_mux *input_mux;
 	void (*unsol_event)(struct hda_codec *, unsigned int);
@@ -400,12 +412,13 @@
 	unsigned int mux_idx;
 	hda_nid_t nid = spec->capsrc_nids ?
 		spec->capsrc_nids[adc_idx] : spec->adc_nids[adc_idx];
+	unsigned int type;
 
 	mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
 	imux = &spec->input_mux[mux_idx];
 
-	if (spec->capture_style &&
-	    !(spec->capture_style == CAPT_1MUX_MIX && !adc_idx)) {
+	type = (get_wcaps(codec, nid) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+	if (type == AC_WID_AUD_MIX) {
 		/* Matrix-mixer style (e.g. ALC882) */
 		unsigned int *cur_val = &spec->cur_mux[adc_idx];
 		unsigned int i, idx;
@@ -449,7 +462,7 @@
 	struct alc_spec *spec = codec->spec;
 	return snd_hda_ch_mode_get(codec, ucontrol, spec->channel_mode,
 				   spec->num_channel_mode,
-				   spec->multiout.max_channels);
+				   spec->ext_channel_count);
 }
 
 static int alc_ch_mode_put(struct snd_kcontrol *kcontrol,
@@ -459,9 +472,12 @@
 	struct alc_spec *spec = codec->spec;
 	int err = snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
 				      spec->num_channel_mode,
-				      &spec->multiout.max_channels);
-	if (err >= 0 && spec->need_dac_fix)
-		spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+				      &spec->ext_channel_count);
+	if (err >= 0 && !spec->const_channel_count) {
+		spec->multiout.max_channels = spec->ext_channel_count;
+		if (spec->need_dac_fix)
+			spec->multiout.num_dacs = spec->multiout.max_channels / 2;
+	}
 	return err;
 }
 
@@ -841,8 +857,13 @@
 	spec->channel_mode = preset->channel_mode;
 	spec->num_channel_mode = preset->num_channel_mode;
 	spec->need_dac_fix = preset->need_dac_fix;
+	spec->const_channel_count = preset->const_channel_count;
 
-	spec->multiout.max_channels = spec->channel_mode[0].channels;
+	if (preset->const_channel_count)
+		spec->multiout.max_channels = preset->const_channel_count;
+	else
+		spec->multiout.max_channels = spec->channel_mode[0].channels;
+	spec->ext_channel_count = spec->channel_mode[0].channels;
 
 	spec->multiout.num_dacs = preset->num_dacs;
 	spec->multiout.dac_nids = preset->dac_nids;
@@ -921,23 +942,29 @@
 	alc_fix_pll(codec);
 }
 
-static void alc_sku_automute(struct hda_codec *codec)
+static void alc_automute_pin(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 	unsigned int present;
-	unsigned int hp_nid = spec->autocfg.hp_pins[0];
-	unsigned int sp_nid = spec->autocfg.speaker_pins[0];
+	unsigned int nid = spec->autocfg.hp_pins[0];
+	int i;
 
 	/* need to execute and sync at first */
-	snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0);
-	present = snd_hda_codec_read(codec, hp_nid, 0,
+	snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+	present = snd_hda_codec_read(codec, nid, 0,
 				     AC_VERB_GET_PIN_SENSE, 0);
-	spec->jack_present = (present & 0x80000000) != 0;
-	snd_hda_codec_write(codec, sp_nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    spec->jack_present ? 0 : PIN_OUT);
+	spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
+	for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
+		nid = spec->autocfg.speaker_pins[i];
+		if (!nid)
+			break;
+		snd_hda_codec_write(codec, nid, 0,
+				    AC_VERB_SET_PIN_WIDGET_CONTROL,
+				    spec->jack_present ? 0 : PIN_OUT);
+	}
 }
 
-#if 0 /* it's broken in some acses -- temporarily disabled */
+#if 0 /* it's broken in some cases -- temporarily disabled */
 static void alc_mic_automute(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
@@ -969,16 +996,19 @@
 		res >>= 28;
 	else
 		res >>= 26;
-	if (res == ALC880_HP_EVENT)
-		alc_sku_automute(codec);
-
-	if (res == ALC880_MIC_EVENT)
+	switch (res) {
+	case ALC880_HP_EVENT:
+		alc_automute_pin(codec);
+		break;
+	case ALC880_MIC_EVENT:
 		alc_mic_automute(codec);
+		break;
+	}
 }
 
 static void alc_inithook(struct hda_codec *codec)
 {
-	alc_sku_automute(codec);
+	alc_automute_pin(codec);
 	alc_mic_automute(codec);
 }
 
@@ -1000,69 +1030,21 @@
 				    AC_VERB_SET_PROC_COEF, 0x3030);
 }
 
-/* 32-bit subsystem ID for BIOS loading in HD Audio codec.
- *	31 ~ 16 :	Manufacture ID
- *	15 ~ 8	:	SKU ID
- *	7  ~ 0	:	Assembly ID
- *	port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36
- */
-static void alc_subsystem_id(struct hda_codec *codec,
-			     unsigned int porta, unsigned int porte,
-			     unsigned int portd)
+static void alc_auto_init_amp(struct hda_codec *codec, int type)
 {
-	unsigned int ass, tmp, i;
-	unsigned nid;
-	struct alc_spec *spec = codec->spec;
+	unsigned int tmp;
 
-	ass = codec->subsystem_id & 0xffff;
-	if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
-		goto do_sku;
-
-	/*
-	 * 31~30	: port conetcivity
-	 * 29~21	: reserve
-	 * 20		: PCBEEP input
-	 * 19~16	: Check sum (15:1)
-	 * 15~1		: Custom
-	 * 0		: override
-	*/
-	nid = 0x1d;
-	if (codec->vendor_id == 0x10ec0260)
-		nid = 0x17;
-	ass = snd_hda_codec_get_pincfg(codec, nid);
-	if (!(ass & 1) && !(ass & 0x100000))
-		return;
-	if ((ass >> 30) != 1)	/* no physical connection */
-		return;
-
-	/* check sum */
-	tmp = 0;
-	for (i = 1; i < 16; i++) {
-		if ((ass >> i) & 1)
-			tmp++;
-	}
-	if (((ass >> 16) & 0xf) != tmp)
-		return;
-do_sku:
-	/*
-	 * 0 : override
-	 * 1 :	Swap Jack
-	 * 2 : 0 --> Desktop, 1 --> Laptop
-	 * 3~5 : External Amplifier control
-	 * 7~6 : Reserved
-	*/
-	tmp = (ass & 0x38) >> 3;	/* external Amp control */
-	switch (tmp) {
-	case 1:
+	switch (type) {
+	case ALC_INIT_GPIO1:
 		snd_hda_sequence_write(codec, alc_gpio1_init_verbs);
 		break;
-	case 3:
+	case ALC_INIT_GPIO2:
 		snd_hda_sequence_write(codec, alc_gpio2_init_verbs);
 		break;
-	case 7:
+	case ALC_INIT_GPIO3:
 		snd_hda_sequence_write(codec, alc_gpio3_init_verbs);
 		break;
-	case 5:	/* set EAPD output high */
+	case ALC_INIT_DEFAULT:
 		switch (codec->vendor_id) {
 		case 0x10ec0260:
 			snd_hda_codec_write(codec, 0x0f, 0,
@@ -1116,7 +1098,7 @@
 					    tmp | 0x2010);
 			break;
 		case 0x10ec0888:
-			/*alc888_coef_init(codec);*/ /* called in alc_init() */
+			alc888_coef_init(codec);
 			break;
 		case 0x10ec0267:
 		case 0x10ec0268:
@@ -1131,7 +1113,107 @@
 					    tmp | 0x3000);
 			break;
 		}
-	default:
+		break;
+	}
+}
+
+static void alc_init_auto_hp(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+
+	if (!spec->autocfg.hp_pins[0])
+		return;
+
+	if (!spec->autocfg.speaker_pins[0]) {
+		if (spec->autocfg.line_out_pins[0] &&
+		    spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+			spec->autocfg.speaker_pins[0] =
+				spec->autocfg.line_out_pins[0];
+		else
+			return;
+	}
+
+	snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n",
+		    spec->autocfg.hp_pins[0]);
+	snd_hda_codec_write_cache(codec, spec->autocfg.hp_pins[0], 0,
+				  AC_VERB_SET_UNSOLICITED_ENABLE,
+				  AC_USRSP_EN | ALC880_HP_EVENT);
+	spec->unsol_event = alc_sku_unsol_event;
+}
+
+/* check subsystem ID and set up device-specific initialization;
+ * return 1 if initialized, 0 if invalid SSID
+ */
+/* 32-bit subsystem ID for BIOS loading in HD Audio codec.
+ *	31 ~ 16 :	Manufacture ID
+ *	15 ~ 8	:	SKU ID
+ *	7  ~ 0	:	Assembly ID
+ *	port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36
+ */
+static int alc_subsystem_id(struct hda_codec *codec,
+			    hda_nid_t porta, hda_nid_t porte,
+			    hda_nid_t portd)
+{
+	unsigned int ass, tmp, i;
+	unsigned nid;
+	struct alc_spec *spec = codec->spec;
+
+	ass = codec->subsystem_id & 0xffff;
+	if ((ass != codec->bus->pci->subsystem_device) && (ass & 1))
+		goto do_sku;
+
+	/* invalid SSID, check the special NID pin defcfg instead */
+	/*
+	 * 31~30	: port connectivity
+	 * 29~21	: reserve
+	 * 20		: PCBEEP input
+	 * 19~16	: Check sum (15:1)
+	 * 15~1		: Custom
+	 * 0		: override
+	*/
+	nid = 0x1d;
+	if (codec->vendor_id == 0x10ec0260)
+		nid = 0x17;
+	ass = snd_hda_codec_get_pincfg(codec, nid);
+	snd_printd("realtek: No valid SSID, "
+		   "checking pincfg 0x%08x for NID 0x%x\n",
+		   ass, nid);
+	if (!(ass & 1) && !(ass & 0x100000))
+		return 0;
+	if ((ass >> 30) != 1)	/* no physical connection */
+		return 0;
+
+	/* check sum */
+	tmp = 0;
+	for (i = 1; i < 16; i++) {
+		if ((ass >> i) & 1)
+			tmp++;
+	}
+	if (((ass >> 16) & 0xf) != tmp)
+		return 0;
+do_sku:
+	snd_printd("realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n",
+		   ass & 0xffff, codec->vendor_id);
+	/*
+	 * 0 : override
+	 * 1 :	Swap Jack
+	 * 2 : 0 --> Desktop, 1 --> Laptop
+	 * 3~5 : External Amplifier control
+	 * 7~6 : Reserved
+	*/
+	tmp = (ass & 0x38) >> 3;	/* external Amp control */
+	switch (tmp) {
+	case 1:
+		spec->init_amp = ALC_INIT_GPIO1;
+		break;
+	case 3:
+		spec->init_amp = ALC_INIT_GPIO2;
+		break;
+	case 7:
+		spec->init_amp = ALC_INIT_GPIO3;
+		break;
+	case 5:
+		spec->init_amp = ALC_INIT_DEFAULT;
 		break;
 	}
 
@@ -1139,7 +1221,7 @@
 	 * when the external headphone out jack is plugged"
 	 */
 	if (!(ass & 0x8000))
-		return;
+		return 1;
 	/*
 	 * 10~8 : Jack location
 	 * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered
@@ -1147,14 +1229,6 @@
 	 * 15   : 1 --> enable the function "Mute internal speaker
 	 *	        when the external headphone out jack is plugged"
 	 */
-	if (!spec->autocfg.speaker_pins[0]) {
-		if (spec->autocfg.line_out_pins[0])
-			spec->autocfg.speaker_pins[0] =
-				spec->autocfg.line_out_pins[0];
-		else
-			return;
-	}
-
 	if (!spec->autocfg.hp_pins[0]) {
 		tmp = (ass >> 11) & 0x3;	/* HP to chassis */
 		if (tmp == 0)
@@ -1164,23 +1238,23 @@
 		else if (tmp == 2)
 			spec->autocfg.hp_pins[0] = portd;
 		else
-			return;
+			return 1;
 	}
-	if (spec->autocfg.hp_pins[0])
-		snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0,
-			AC_VERB_SET_UNSOLICITED_ENABLE,
-			AC_USRSP_EN | ALC880_HP_EVENT);
 
-#if 0 /* it's broken in some acses -- temporarily disabled */
-	if (spec->autocfg.input_pins[AUTO_PIN_MIC] &&
-		spec->autocfg.input_pins[AUTO_PIN_FRONT_MIC])
-		snd_hda_codec_write(codec,
-			spec->autocfg.input_pins[AUTO_PIN_MIC], 0,
-			AC_VERB_SET_UNSOLICITED_ENABLE,
-			AC_USRSP_EN | ALC880_MIC_EVENT);
-#endif /* disabled */
+	alc_init_auto_hp(codec);
+	return 1;
+}
 
-	spec->unsol_event = alc_sku_unsol_event;
+static void alc_ssid_check(struct hda_codec *codec,
+			   hda_nid_t porta, hda_nid_t porte, hda_nid_t portd)
+{
+	if (!alc_subsystem_id(codec, porta, porte, portd)) {
+		struct alc_spec *spec = codec->spec;
+		snd_printd("realtek: "
+			   "Enable default setup for auto mode as fallback\n");
+		spec->init_amp = ALC_INIT_DEFAULT;
+		alc_init_auto_hp(codec);
+	}
 }
 
 /*
@@ -1315,32 +1389,58 @@
 	{}
 };
 
-static void alc888_fujitsu_xa3530_automute(struct hda_codec *codec)
+static void alc_automute_amp(struct hda_codec *codec)
 {
-	unsigned int present;
-	unsigned int bits;
-	/* Line out presence */
-	present = snd_hda_codec_read(codec, 0x17, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	/* HP out presence */
-	present = present || snd_hda_codec_read(codec, 0x1b, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? HDA_AMP_MUTE : 0;
+	struct alc_spec *spec = codec->spec;
+	unsigned int val, mute;
+	hda_nid_t nid;
+	int i;
+
+	spec->jack_present = 0;
+	for (i = 0; i < ARRAY_SIZE(spec->autocfg.hp_pins); i++) {
+		nid = spec->autocfg.hp_pins[i];
+		if (!nid)
+			break;
+		val = snd_hda_codec_read(codec, nid, 0,
+					 AC_VERB_GET_PIN_SENSE, 0);
+		if (val & AC_PINSENSE_PRESENCE) {
+			spec->jack_present = 1;
+			break;
+		}
+	}
+
+	mute = spec->jack_present ? HDA_AMP_MUTE : 0;
 	/* Toggle internal speakers muting */
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-	/* Toggle internal bass muting */
-	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
+	for (i = 0; i < ARRAY_SIZE(spec->autocfg.speaker_pins); i++) {
+		nid = spec->autocfg.speaker_pins[i];
+		if (!nid)
+			break;
+		snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, mute);
+	}
 }
 
-static void alc888_fujitsu_xa3530_unsol_event(struct hda_codec *codec,
-		unsigned int res)
+static void alc_automute_amp_unsol_event(struct hda_codec *codec,
+					 unsigned int res)
 {
-	if (res >> 26 == ALC880_HP_EVENT)
-		alc888_fujitsu_xa3530_automute(codec);
+	if (codec->vendor_id == 0x10ec0880)
+		res >>= 28;
+	else
+		res >>= 26;
+	if (res == ALC880_HP_EVENT)
+		alc_automute_amp(codec);
 }
 
+static void alc888_fujitsu_xa3530_init_hook(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x17; /* line-out */
+	spec->autocfg.hp_pins[1] = 0x1b; /* hp */
+	spec->autocfg.speaker_pins[0] = 0x14; /* speaker */
+	spec->autocfg.speaker_pins[1] = 0x15; /* bass */
+	alc_automute_amp(codec);
+}
 
 /*
  * ALC888 Acer Aspire 4930G model
@@ -1364,6 +1464,78 @@
 	{ }
 };
 
+/*
+ * ALC888 Acer Aspire 6530G model
+ */
+
+static struct hda_verb alc888_acer_aspire_6530g_verbs[] = {
+/* Bias voltage on for external mic port */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN | PIN_VREF80},
+/* Enable unsolicited event for HP jack */
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+/* Enable speaker output */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+/* Enable headphone output */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{ }
+};
+
+/*
+ * ALC889 Acer Aspire 8930G model
+ */
+
+static struct hda_verb alc889_acer_aspire_8930g_verbs[] = {
+/* Front Mic: set to PIN_IN (empty by default) */
+	{0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+/* Unselect Front Mic by default in input mixer 3 */
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0xb)},
+/* Enable unsolicited event for HP jack */
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+/* Connect Internal Front to Front */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Connect Internal Rear to Rear */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x01},
+/* Connect Internal CLFE to CLFE */
+	{0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x16, AC_VERB_SET_CONNECT_SEL, 0x02},
+/* Connect HP out to Front */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | PIN_HP},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+/* Enable all DACs */
+/*  DAC DISABLE/MUTE 1? */
+/*  setting bits 1-5 disables DAC nids 0x02-0x06 apparently. Init=0x38 */
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x03},
+	{0x20, AC_VERB_SET_PROC_COEF, 0x0000},
+/*  DAC DISABLE/MUTE 2? */
+/*  some bit here disables the other DACs. Init=0x4900 */
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x08},
+	{0x20, AC_VERB_SET_PROC_COEF, 0x0000},
+/* Enable amplifiers */
+	{0x14, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+	{0x15, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
+/* DMIC fix
+ * This laptop has a stereo digital microphone. The mics are only 1cm apart
+ * which makes the stereo useless. However, either the mic or the ALC889
+ * makes the signal become a difference/sum signal instead of standard
+ * stereo, which is annoying. So instead we flip this bit which makes the
+ * codec replicate the sum signal to both channels, turning it into a
+ * normal mono mic.
+ */
+/*  DMIC_CONTROL? Init value = 0x0001 */
+	{0x20, AC_VERB_SET_COEF_INDEX, 0x0b},
+	{0x20, AC_VERB_SET_PROC_COEF, 0x0003},
+	{ }
+};
+
 static struct hda_input_mux alc888_2_capture_sources[2] = {
 	/* Front mic only available on one ADC */
 	{
@@ -1385,6 +1557,57 @@
 	}
 };
 
+static struct hda_input_mux alc888_acer_aspire_6530_sources[2] = {
+	/* Interal mic only available on one ADC */
+	{
+		.num_items = 3,
+		.items = {
+			{ "Ext Mic", 0x0 },
+			{ "CD", 0x4 },
+			{ "Int Mic", 0xb },
+		},
+	},
+	{
+		.num_items = 2,
+		.items = {
+			{ "Ext Mic", 0x0 },
+			{ "CD", 0x4 },
+		},
+	}
+};
+
+static struct hda_input_mux alc889_capture_sources[3] = {
+	/* Digital mic only available on first "ADC" */
+	{
+		.num_items = 5,
+		.items = {
+			{ "Mic", 0x0 },
+			{ "Line", 0x2 },
+			{ "CD", 0x4 },
+			{ "Front Mic", 0xb },
+			{ "Input Mix", 0xa },
+		},
+	},
+	{
+		.num_items = 4,
+		.items = {
+			{ "Mic", 0x0 },
+			{ "Line", 0x2 },
+			{ "CD", 0x4 },
+			{ "Input Mix", 0xa },
+		},
+	},
+	{
+		.num_items = 4,
+		.items = {
+			{ "Mic", 0x0 },
+			{ "Line", 0x2 },
+			{ "CD", 0x4 },
+			{ "Input Mix", 0xa },
+		},
+	}
+};
+
 static struct snd_kcontrol_new alc888_base_mixer[] = {
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -1407,22 +1630,24 @@
 	{ } /* end */
 };
 
-static void alc888_acer_aspire_4930g_automute(struct hda_codec *codec)
+static void alc888_acer_aspire_4930g_init_hook(struct hda_codec *codec)
 {
-	unsigned int present;
-	unsigned int bits;
-	present = snd_hda_codec_read(codec, 0x15, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc_automute_amp(codec);
 }
 
-static void alc888_acer_aspire_4930g_unsol_event(struct hda_codec *codec,
-		unsigned int res)
+static void alc889_acer_aspire_8930g_init_hook(struct hda_codec *codec)
 {
-	if (res >> 26 == ALC880_HP_EVENT)
-		alc888_acer_aspire_4930g_automute(codec);
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[1] = 0x16;
+	spec->autocfg.speaker_pins[2] = 0x1b;
+	alc_automute_amp(codec);
 }
 
 /*
@@ -2390,21 +2615,6 @@
 	{ }
 };
 
-/* toggle speaker-output according to the hp-jack state */
-static void alc880_uniwill_hp_automute(struct hda_codec *codec)
-{
- 	unsigned int present;
-	unsigned char bits;
-
- 	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-	snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-}
-
 /* auto-toggle front mic */
 static void alc880_uniwill_mic_automute(struct hda_codec *codec)
 {
@@ -2417,9 +2627,14 @@
 	snd_hda_codec_amp_stereo(codec, 0x0b, HDA_INPUT, 1, HDA_AMP_MUTE, bits);
 }
 
-static void alc880_uniwill_automute(struct hda_codec *codec)
+static void alc880_uniwill_init_hook(struct hda_codec *codec)
 {
-	alc880_uniwill_hp_automute(codec);
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x16;
+	alc_automute_amp(codec);
 	alc880_uniwill_mic_automute(codec);
 }
 
@@ -2430,24 +2645,22 @@
 	 * definition.  4bit tag is placed at 28 bit!
 	 */
 	switch (res >> 28) {
-	case ALC880_HP_EVENT:
-		alc880_uniwill_hp_automute(codec);
-		break;
 	case ALC880_MIC_EVENT:
 		alc880_uniwill_mic_automute(codec);
 		break;
+	default:
+		alc_automute_amp_unsol_event(codec, res);
+		break;
 	}
 }
 
-static void alc880_uniwill_p53_hp_automute(struct hda_codec *codec)
+static void alc880_uniwill_p53_init_hook(struct hda_codec *codec)
 {
- 	unsigned int present;
-	unsigned char bits;
+	struct alc_spec *spec = codec->spec;
 
- 	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0, HDA_AMP_MUTE, bits);
+	spec->autocfg.hp_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[0] = 0x15;
+	alc_automute_amp(codec);
 }
 
 static void alc880_uniwill_p53_dcvol_automute(struct hda_codec *codec)
@@ -2469,10 +2682,10 @@
 	/* Looks like the unsol event is incompatible with the standard
 	 * definition.  4bit tag is placed at 28 bit!
 	 */
-	if ((res >> 28) == ALC880_HP_EVENT)
-		alc880_uniwill_p53_hp_automute(codec);
 	if ((res >> 28) == ALC880_DCVOL_EVENT)
 		alc880_uniwill_p53_dcvol_automute(codec);
+	else
+		alc_automute_amp_unsol_event(codec, res);
 }
 
 /*
@@ -2542,6 +2755,7 @@
 /* Enable GPIO mask and set output */
 #define alc880_gpio1_init_verbs	alc_gpio1_init_verbs
 #define alc880_gpio2_init_verbs	alc_gpio2_init_verbs
+#define alc880_gpio3_init_verbs	alc_gpio3_init_verbs
 
 /* Clevo m520g init */
 static struct hda_verb alc880_pin_clevo_init_verbs[] = {
@@ -2704,30 +2918,18 @@
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	/* jack sense */
-	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1},
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
 	{ }
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_automute(struct hda_codec *codec)
+static void alc880_lg_init_hook(struct hda_codec *codec)
 {
-	unsigned int present;
-	unsigned char bits;
+	struct alc_spec *spec = codec->spec;
 
-	present = snd_hda_codec_read(codec, 0x1b, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-}
-
-static void alc880_lg_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-	/* Looks like the unsol event is incompatible with the standard
-	 * definition.  4bit tag is placed at 28 bit!
-	 */
-	if ((res >> 28) == 0x01)
-		alc880_lg_automute(codec);
+	spec->autocfg.hp_pins[0] = 0x1b;
+	spec->autocfg.speaker_pins[0] = 0x17;
+	alc_automute_amp(codec);
 }
 
 /*
@@ -2801,30 +3003,18 @@
 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
 	/* jack sense */
-	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1},
+	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT},
 	{ }
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc880_lg_lw_automute(struct hda_codec *codec)
+static void alc880_lg_lw_init_hook(struct hda_codec *codec)
 {
-	unsigned int present;
-	unsigned char bits;
+	struct alc_spec *spec = codec->spec;
 
-	present = snd_hda_codec_read(codec, 0x1b, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-}
-
-static void alc880_lg_lw_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-	/* Looks like the unsol event is incompatible with the standard
-	 * definition.  4bit tag is placed at 28 bit!
-	 */
-	if ((res >> 28) == 0x01)
-		alc880_lg_lw_automute(codec);
+	spec->autocfg.hp_pins[0] = 0x1b;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc_automute_amp(codec);
 }
 
 static struct snd_kcontrol_new alc880_medion_rim_mixer[] = {
@@ -2871,16 +3061,10 @@
 /* toggle speaker-output according to the hp-jack state */
 static void alc880_medion_rim_automute(struct hda_codec *codec)
 {
-	unsigned int present;
-	unsigned char bits;
-
-	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0)
-		& AC_PINSENSE_PRESENCE;
-	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-	if (present)
+	struct alc_spec *spec = codec->spec;
+	alc_automute_amp(codec);
+	/* toggle EAPD */
+	if (spec->jack_present)
 		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0);
 	else
 		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 2);
@@ -2896,6 +3080,15 @@
 		alc880_medion_rim_automute(codec);
 }
 
+static void alc880_medion_rim_init_hook(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[0] = 0x1b;
+	alc880_medion_rim_automute(codec);
+}
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 static struct hda_amp_list alc880_loopbacks[] = {
 	{ 0x0b, HDA_INPUT, 0 },
@@ -2924,8 +3117,7 @@
 	unsigned int i;
 
 	alc_fix_pll(codec);
-	if (codec->vendor_id == 0x10ec0888)
-		alc888_coef_init(codec);
+	alc_auto_init_amp(codec, spec->init_amp);
 
 	for (i = 0; i < spec->num_init_verbs; i++)
 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
@@ -3127,7 +3319,10 @@
 	if (spec->no_analog)
 		goto skip_analog;
 
+	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
+		 "%s Analog", codec->chip_name);
 	info->name = spec->stream_name_analog;
+	
 	if (spec->stream_analog_playback) {
 		if (snd_BUG_ON(!spec->multiout.dac_nids))
 			return -EINVAL;
@@ -3153,6 +3348,9 @@
  skip_analog:
 	/* SPDIF for stream index #1 */
 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
+		snprintf(spec->stream_name_digital,
+			 sizeof(spec->stream_name_digital),
+			 "%s Digital", codec->chip_name);
 		codec->num_pcms = 2;
 	        codec->slave_dig_outs = spec->multiout.slave_dig_outs;
 		info = spec->pcm_rec + 1;
@@ -3755,7 +3953,7 @@
 		.channel_mode = alc880_2_jack_modes,
 		.input_mux = &alc880_f1734_capture_source,
 		.unsol_event = alc880_uniwill_p53_unsol_event,
-		.init_hook = alc880_uniwill_p53_hp_automute,
+		.init_hook = alc880_uniwill_p53_init_hook,
 	},
 	[ALC880_ASUS] = {
 		.mixers = { alc880_asus_mixer },
@@ -3832,7 +4030,7 @@
 		.need_dac_fix = 1,
 		.input_mux = &alc880_capture_source,
 		.unsol_event = alc880_uniwill_unsol_event,
-		.init_hook = alc880_uniwill_automute,
+		.init_hook = alc880_uniwill_init_hook,
 	},
 	[ALC880_UNIWILL_P53] = {
 		.mixers = { alc880_uniwill_p53_mixer },
@@ -3844,7 +4042,7 @@
 		.channel_mode = alc880_threestack_modes,
 		.input_mux = &alc880_capture_source,
 		.unsol_event = alc880_uniwill_p53_unsol_event,
-		.init_hook = alc880_uniwill_p53_hp_automute,
+		.init_hook = alc880_uniwill_p53_init_hook,
 	},
 	[ALC880_FUJITSU] = {
 		.mixers = { alc880_fujitsu_mixer },
@@ -3858,7 +4056,7 @@
 		.channel_mode = alc880_2_jack_modes,
 		.input_mux = &alc880_capture_source,
 		.unsol_event = alc880_uniwill_p53_unsol_event,
-		.init_hook = alc880_uniwill_p53_hp_automute,
+		.init_hook = alc880_uniwill_p53_init_hook,
 	},
 	[ALC880_CLEVO] = {
 		.mixers = { alc880_three_stack_mixer },
@@ -3883,8 +4081,8 @@
 		.channel_mode = alc880_lg_ch_modes,
 		.need_dac_fix = 1,
 		.input_mux = &alc880_lg_capture_source,
-		.unsol_event = alc880_lg_unsol_event,
-		.init_hook = alc880_lg_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc880_lg_init_hook,
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 		.loopbacks = alc880_lg_loopbacks,
 #endif
@@ -3899,8 +4097,8 @@
 		.num_channel_mode = ARRAY_SIZE(alc880_lg_lw_modes),
 		.channel_mode = alc880_lg_lw_modes,
 		.input_mux = &alc880_lg_lw_capture_source,
-		.unsol_event = alc880_lg_lw_unsol_event,
-		.init_hook = alc880_lg_lw_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc880_lg_lw_init_hook,
 	},
 	[ALC880_MEDION_RIM] = {
 		.mixers = { alc880_medion_rim_mixer },
@@ -3914,7 +4112,7 @@
 		.channel_mode = alc880_2_jack_modes,
 		.input_mux = &alc880_medion_rim_capture_source,
 		.unsol_event = alc880_medion_rim_unsol_event,
-		.init_hook = alc880_medion_rim_automute,
+		.init_hook = alc880_medion_rim_init_hook,
 	},
 #ifdef CONFIG_SND_DEBUG
 	[ALC880_TEST] = {
@@ -4199,7 +4397,6 @@
 	struct alc_spec *spec = codec->spec;
 	int i;
 
-	alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
 	for (i = 0; i < spec->autocfg.line_outs; i++) {
 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
 		int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -4304,6 +4501,8 @@
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux[0];
 
+	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+
 	return 1;
 }
 
@@ -4361,8 +4560,8 @@
 						  alc880_models,
 						  alc880_cfg_tbl);
 	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC880, "
-		       "trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: Unknown model for %s, "
+		       "trying auto-probe from BIOS...\n", codec->chip_name);
 		board_config = ALC880_AUTO;
 	}
 
@@ -4389,12 +4588,10 @@
 	if (board_config != ALC880_AUTO)
 		setup_preset(spec, &alc880_presets[board_config]);
 
-	spec->stream_name_analog = "ALC880 Analog";
 	spec->stream_analog_playback = &alc880_pcm_analog_playback;
 	spec->stream_analog_capture = &alc880_pcm_analog_capture;
 	spec->stream_analog_alt_capture = &alc880_pcm_analog_alt_capture;
 
-	spec->stream_name_digital = "ALC880 Digital";
 	spec->stream_digital_playback = &alc880_pcm_digital_playback;
 	spec->stream_digital_capture = &alc880_pcm_digital_capture;
 
@@ -5679,7 +5876,6 @@
 	struct alc_spec *spec = codec->spec;
 	hda_nid_t nid;
 
-	alc_subsystem_id(codec, 0x10, 0x15, 0x0f);
 	nid = spec->autocfg.line_out_pins[0];
 	if (nid) {
 		int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -5789,6 +5985,8 @@
 	spec->num_mux_defs = 1;
 	spec->input_mux = &spec->private_imux[0];
 
+	alc_ssid_check(codec, 0x10, 0x15, 0x0f);
+
 	return 1;
 }
 
@@ -6006,8 +6204,9 @@
 						  alc260_models,
 						  alc260_cfg_tbl);
 	if (board_config < 0) {
-		snd_printd(KERN_INFO "hda_codec: Unknown model for ALC260, "
-			   "trying auto-probe from BIOS...\n");
+		snd_printd(KERN_INFO "hda_codec: Unknown model for %s, "
+			   "trying auto-probe from BIOS...\n",
+			   codec->chip_name);
 		board_config = ALC260_AUTO;
 	}
 
@@ -6034,11 +6233,9 @@
 	if (board_config != ALC260_AUTO)
 		setup_preset(spec, &alc260_presets[board_config]);
 
-	spec->stream_name_analog = "ALC260 Analog";
 	spec->stream_analog_playback = &alc260_pcm_analog_playback;
 	spec->stream_analog_capture = &alc260_pcm_analog_capture;
 
-	spec->stream_name_digital = "ALC260 Digital";
 	spec->stream_digital_playback = &alc260_pcm_digital_playback;
 	spec->stream_digital_capture = &alc260_pcm_digital_capture;
 
@@ -6115,6 +6312,16 @@
 		{ "CD", 0x4 },
 	},
 };
+
+static struct hda_input_mux mb5_capture_source = {
+	.num_items = 3,
+	.items = {
+		{ "Mic", 0x1 },
+		{ "Line", 0x2 },
+		{ "CD", 0x4 },
+	},
+};
+
 /*
  * 2ch mode
  */
@@ -6172,7 +6379,7 @@
 };
 
 /*
- * macbook pro ALC885 can switch LineIn to LineOut without loosing Mic
+ * macbook pro ALC885 can switch LineIn to LineOut without losing Mic
  */
 
 /*
@@ -6202,6 +6409,34 @@
 	{ 6, alc885_mbp_ch6_init },
 };
 
+/*
+ * 2ch
+ * Speakers/Woofer/HP = Front
+ * LineIn = Input
+ */
+static struct hda_verb alc885_mb5_ch2_init[] = {
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	{ } /* end */
+};
+
+/*
+ * 6ch mode
+ * Speakers/HP = Front
+ * Woofer = LFE
+ * LineIn = Surround
+ */
+static struct hda_verb alc885_mb5_ch6_init[] = {
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc885_mb5_6ch_modes[2] = {
+	{ 2, alc885_mb5_ch2_init },
+	{ 6, alc885_mb5_ch6_init },
+};
 
 /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17
  *                 Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b
@@ -6244,6 +6479,25 @@
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
 	{ } /* end */
 };
+
+static struct snd_kcontrol_new alc885_mb5_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+	HDA_BIND_MUTE   ("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
+	HDA_BIND_MUTE   ("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("LFE Playback Volume", 0x0e, 0x00, HDA_OUTPUT),
+	HDA_BIND_MUTE   ("LFE Playback Switch", 0x0e, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("HP Playback Volume", 0x0f, 0x00, HDA_OUTPUT),
+	HDA_BIND_MUTE   ("HP Playback Switch", 0x0f, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE  ("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE  ("Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Boost", 0x15, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x19, 0x00, HDA_INPUT),
+	{ } /* end */
+};
+
 static struct snd_kcontrol_new alc882_w2jc_mixer[] = {
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -6471,6 +6725,55 @@
 	{ }
 };
 
+/* Macbook 5,1 */
+static struct hda_verb alc885_mb5_init_verbs[] = {
+	/* DACs */
+	{0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	/* Front mixer */
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Surround mixer */
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* LFE mixer */
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* HP mixer */
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
+	{0x0f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
+	/* Front Pin (0x0c) */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x18, AC_VERB_SET_CONNECT_SEL, 0x00},
+	/* LFE Pin (0x0e) */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT | 0x01},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x02},
+	/* HP Pin (0x0f) */
+	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x14, AC_VERB_SET_CONNECT_SEL, 0x03},
+	/* Front Mic pin: input vref at 80% */
+	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+	/* Line In pin */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
+
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
+	{ }
+};
+
 /* Macbook Pro rev3 */
 static struct hda_verb alc885_mbp3_init_verbs[] = {
 	/* Front mixer: unmute input/output amp left and right (volume = 0) */
@@ -6560,45 +6863,23 @@
 };
 
 /* Toggle speaker-output according to the hp-jack state */
-static void alc885_imac24_automute(struct hda_codec *codec)
+static void alc885_imac24_automute_init_hook(struct hda_codec *codec)
 {
- 	unsigned int present;
+	struct alc_spec *spec = codec->spec;
 
- 	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-	snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+	spec->autocfg.hp_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[0] = 0x18;
+	spec->autocfg.speaker_pins[1] = 0x1a;
+	alc_automute_amp(codec);
 }
 
-/* Processes unsolicited events. */
-static void alc885_imac24_unsol_event(struct hda_codec *codec,
-				      unsigned int res)
+static void alc885_mbp3_init_hook(struct hda_codec *codec)
 {
-	/* Headphone insertion or removal. */
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc885_imac24_automute(codec);
-}
+	struct alc_spec *spec = codec->spec;
 
-static void alc885_mbp3_automute(struct hda_codec *codec)
-{
- 	unsigned int present;
-
- 	present = snd_hda_codec_read(codec, 0x15, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_amp_stereo(codec, 0x14,  HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? 0 : HDA_AMP_MUTE);
-
-}
-static void alc885_mbp3_unsol_event(struct hda_codec *codec,
-				    unsigned int res)
-{
-	/* Headphone insertion or removal. */
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc885_mbp3_automute(codec);
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc_automute_amp(codec);
 }
 
 
@@ -6623,24 +6904,25 @@
 /* toggle speaker-output according to the hp-jack state */
 static void alc882_targa_automute(struct hda_codec *codec)
 {
- 	unsigned int present;
-
- 	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+	struct alc_spec *spec = codec->spec;
+	alc_automute_amp(codec);
 	snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA,
-				  present ? 1 : 3);
+				  spec->jack_present ? 1 : 3);
+}
+
+static void alc882_targa_init_hook(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[0] = 0x1b;
+	alc882_targa_automute(codec);
 }
 
 static void alc882_targa_unsol_event(struct hda_codec *codec, unsigned int res)
 {
-	/* Looks like the unsol event is incompatible with the standard
-	 * definition.  4bit tag is placed at 26 bit!
-	 */
-	if (((res >> 26) == ALC880_HP_EVENT)) {
+	if ((res >> 26) == ALC880_HP_EVENT)
 		alc882_targa_automute(codec);
-	}
 }
 
 static struct hda_verb alc882_asus_a7j_verbs[] = {
@@ -6722,7 +7004,7 @@
 static void alc885_imac24_init_hook(struct hda_codec *codec)
 {
 	alc885_macpro_init_hook(codec);
-	alc885_imac24_automute(codec);
+	alc885_imac24_automute_init_hook(codec);
 }
 
 /*
@@ -6797,7 +7079,7 @@
 #define alc882_loopbacks	alc880_loopbacks
 #endif
 
-/* pcm configuration: identiacal with ALC880 */
+/* pcm configuration: identical with ALC880 */
 #define alc882_pcm_analog_playback	alc880_pcm_analog_playback
 #define alc882_pcm_analog_capture	alc880_pcm_analog_capture
 #define alc882_pcm_digital_playback	alc880_pcm_digital_playback
@@ -6815,6 +7097,7 @@
 	[ALC882_ASUS_A7J]	= "asus-a7j",
 	[ALC882_ASUS_A7M]	= "asus-a7m",
 	[ALC885_MACPRO]		= "macpro",
+	[ALC885_MB5]		= "mb5",
 	[ALC885_MBP3]		= "mbp3",
 	[ALC885_IMAC24]		= "imac24",
 	[ALC882_AUTO]		= "auto",
@@ -6892,8 +7175,20 @@
 		.input_mux = &alc882_capture_source,
 		.dig_out_nid = ALC882_DIGOUT_NID,
 		.dig_in_nid = ALC882_DIGIN_NID,
-		.unsol_event = alc885_mbp3_unsol_event,
-		.init_hook = alc885_mbp3_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc885_mbp3_init_hook,
+	},
+	[ALC885_MB5] = {
+		.mixers = { alc885_mb5_mixer, alc882_chmode_mixer },
+		.init_verbs = { alc885_mb5_init_verbs,
+				alc880_gpio1_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc882_dac_nids),
+		.dac_nids = alc882_dac_nids,
+		.channel_mode = alc885_mb5_6ch_modes,
+		.num_channel_mode = ARRAY_SIZE(alc885_mb5_6ch_modes),
+		.input_mux = &mb5_capture_source,
+		.dig_out_nid = ALC882_DIGOUT_NID,
+		.dig_in_nid = ALC882_DIGIN_NID,
 	},
 	[ALC885_MACPRO] = {
 		.mixers = { alc882_macpro_mixer },
@@ -6917,7 +7212,7 @@
 		.num_channel_mode = ARRAY_SIZE(alc882_ch_modes),
 		.channel_mode = alc882_ch_modes,
 		.input_mux = &alc882_capture_source,
-		.unsol_event = alc885_imac24_unsol_event,
+		.unsol_event = alc_automute_amp_unsol_event,
 		.init_hook = alc885_imac24_init_hook,
 	},
 	[ALC882_TARGA] = {
@@ -6934,7 +7229,7 @@
 		.need_dac_fix = 1,
 		.input_mux = &alc882_capture_source,
 		.unsol_event = alc882_targa_unsol_event,
-		.init_hook = alc882_targa_automute,
+		.init_hook = alc882_targa_init_hook,
 	},
 	[ALC882_ASUS_A7J] = {
 		.mixers = { alc882_asus_a7j_mixer, alc882_chmode_mixer },
@@ -7014,7 +7309,6 @@
 	struct alc_spec *spec = codec->spec;
 	int i;
 
-	alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
 	for (i = 0; i <= HDA_SIDE; i++) {
 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
 		int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -7197,10 +7491,17 @@
 		case 0x106b00a1: /* Macbook (might be wrong - PCI SSID?) */
 		case 0x106b00a4: /* MacbookPro4,1 */
 		case 0x106b2c00: /* Macbook Pro rev3 */
-		case 0x106b3600: /* Macbook 3.1 */
+		/* Macbook 3.1 (0x106b3600) is handled by patch_alc883() */
 		case 0x106b3800: /* MacbookPro4,1 - latter revision */
 			board_config = ALC885_MBP3;
 			break;
+		case 0x106b3f00: /* Macbook 5,1 */
+		case 0x106b4000: /* Macbook Pro 5,1 - FIXME: HP jack sense
+				  *   seems not working, so apparently
+				  *   no perfect solution yet
+				  */
+			board_config = ALC885_MB5;
+			break;
 		default:
 			/* ALC889A is handled better as ALC888-compatible */
 			if (codec->revision_id == 0x100101 ||
@@ -7208,8 +7509,9 @@
 				alc_free(codec);
 				return patch_alc883(codec);
 			}
-			printk(KERN_INFO "hda_codec: Unknown model for ALC882, "
-		       			 "trying auto-probe from BIOS...\n");
+			printk(KERN_INFO "hda_codec: Unknown model for %s, "
+			       "trying auto-probe from BIOS...\n",
+			       codec->chip_name);
 			board_config = ALC882_AUTO;
 		}
 	}
@@ -7239,14 +7541,6 @@
 	if (board_config != ALC882_AUTO)
 		setup_preset(spec, &alc882_presets[board_config]);
 
-	if (codec->vendor_id == 0x10ec0885) {
-		spec->stream_name_analog = "ALC885 Analog";
-		spec->stream_name_digital = "ALC885 Digital";
-	} else {
-		spec->stream_name_analog = "ALC882 Analog";
-		spec->stream_name_digital = "ALC882 Digital";
-	}
-
 	spec->stream_analog_playback = &alc882_pcm_analog_playback;
 	spec->stream_analog_capture = &alc882_pcm_analog_capture;
 	/* FIXME: setup DAC5 */
@@ -7256,7 +7550,6 @@
 	spec->stream_digital_playback = &alc882_pcm_digital_playback;
 	spec->stream_digital_capture = &alc882_pcm_digital_capture;
 
-	spec->capture_style = CAPT_MIX; /* matrix-style capture */
 	if (!spec->adc_nids && spec->input_mux) {
 		/* check whether NID 0x07 is valid */
 		unsigned int wcap = get_wcaps(codec, 0x07);
@@ -7399,6 +7692,17 @@
 	},
 };
 
+static struct hda_input_mux alc889A_mb31_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Mic", 0x0 },
+		/* Front Mic (0x01) unused */
+		{ "Line", 0x2 },
+		/* Line 2 (0x03) unused */
+		/* CD (0x04) unsused? */
+	},
+};
+
 /*
  * 2ch mode
  */
@@ -7448,6 +7752,73 @@
 	{ 6, alc883_3ST_ch6_init },
 };
 
+
+/*
+ * 2ch mode
+ */
+static struct hda_verb alc883_4ST_ch2_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ } /* end */
+};
+
+/*
+ * 4ch mode
+ */
+static struct hda_verb alc883_4ST_ch4_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+/*
+ * 6ch mode
+ */
+static struct hda_verb alc883_4ST_ch6_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+/*
+ * 8ch mode
+ */
+static struct hda_verb alc883_4ST_ch8_init[] = {
+	{ 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x17, AC_VERB_SET_CONNECT_SEL, 0x03 },
+	{ 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 },
+	{ 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
+	{ 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
+	{ 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 },
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc883_4ST_8ch_modes[4] = {
+	{ 2, alc883_4ST_ch2_init },
+	{ 4, alc883_4ST_ch4_init },
+	{ 6, alc883_4ST_ch6_init },
+	{ 8, alc883_4ST_ch8_init },
+};
+
+
 /*
  * 2ch mode
  */
@@ -7517,6 +7888,49 @@
 	{ 8, alc883_sixstack_ch8_init },
 };
 
+/* 2ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:front) */
+static struct hda_verb alc889A_mb31_ch2_init[] = {
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},             /* HP as front */
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},    /* Line as input */
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},   /* Line off */
+	{ } /* end */
+};
+
+/* 4ch mode (Speaker:front, Subwoofer:CLFE, Line:CLFE, Headphones:front) */
+static struct hda_verb alc889A_mb31_ch4_init[] = {
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},             /* HP as front */
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},   /* Line as output */
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */
+	{ } /* end */
+};
+
+/* 5ch mode (Speaker:front, Subwoofer:CLFE, Line:input, Headphones:rear) */
+static struct hda_verb alc889A_mb31_ch5_init[] = {
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},             /* HP as rear */
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Subwoofer on */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},    /* Line as input */
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},   /* Line off */
+	{ } /* end */
+};
+
+/* 6ch mode (Speaker:front, Subwoofer:off, Line:CLFE, Headphones:Rear) */
+static struct hda_verb alc889A_mb31_ch6_init[] = {
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x01},             /* HP as front */
+	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},   /* Subwoofer off */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},   /* Line as output */
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* Line on */
+	{ } /* end */
+};
+
+static struct hda_channel_mode alc889A_mb31_6ch_modes[4] = {
+	{ 2, alc889A_mb31_ch2_init },
+	{ 4, alc889A_mb31_ch4_init },
+	{ 5, alc889A_mb31_ch5_init },
+	{ 6, alc889A_mb31_ch6_init },
+};
+
 static struct hda_verb alc883_medion_eapd_verbs[] = {
         /* eanable EAPD on medion laptop */
 	{0x20, AC_VERB_SET_COEF_INDEX, 0x07},
@@ -7685,7 +8099,7 @@
 	{ } /* end */
 };
 
-static struct snd_kcontrol_new alc883_tagra_mixer[] = {
+static struct snd_kcontrol_new alc883_targa_mixer[] = {
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
@@ -7705,7 +8119,7 @@
 	{ } /* end */
 };
 
-static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = {
+static struct snd_kcontrol_new alc883_targa_2ch_mixer[] = {
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x14, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
@@ -7770,6 +8184,19 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc888_acer_aspire_6530_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("LFE Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("LFE Playback Switch", 0x0f, 2, HDA_INPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	{ } /* end */
+};
+
 static struct snd_kcontrol_new alc888_lenovo_sky_mixer[] = {
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
@@ -7782,8 +8209,6 @@
 	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0d, 2, 2, HDA_INPUT),
 	HDA_CODEC_VOLUME("Side Playback Volume", 0x0f, 0x0, HDA_OUTPUT),
 	HDA_BIND_MUTE("Side Playback Switch", 0x0f, 2, HDA_INPUT),
-	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("iSpeaker Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
 	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
@@ -7797,6 +8222,42 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc889A_mb31_mixer[] = {
+	/* Output mixers */
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x00, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Surround Playback Volume", 0x0d, 0x00, HDA_OUTPUT),
+	HDA_BIND_MUTE("Surround Playback Switch", 0x0d, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0e, 1, 0x00,
+		HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x0e, 1, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0e, 2, 0x00, HDA_OUTPUT),
+	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x0e, 2, 0x02, HDA_INPUT),
+	/* Output switches */
+	HDA_CODEC_MUTE("Enable Speaker", 0x14, 0x00, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Enable Headphones", 0x15, 0x00, HDA_OUTPUT),
+	HDA_CODEC_MUTE_MONO("Enable LFE", 0x16, 2, 0x00, HDA_OUTPUT),
+	/* Boost mixers */
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Boost", 0x1a, 0x00, HDA_INPUT),
+	/* Input mixers */
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x00, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x00, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc883_vaiott_mixer[] = {
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	{ } /* end */
+};
+
 static struct hda_bind_ctls alc883_bind_cap_vol = {
 	.ops = &snd_hda_bind_vol,
 	.values = {
@@ -7932,16 +8393,14 @@
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_mitac_hp_automute(struct hda_codec *codec)
+static void alc883_mitac_init_hook(struct hda_codec *codec)
 {
-	unsigned int present;
+	struct alc_spec *spec = codec->spec;
 
-	present = snd_hda_codec_read(codec, 0x15, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-	snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[1] = 0x17;
+	alc_automute_amp(codec);
 }
 
 /* auto-toggle front mic */
@@ -7958,25 +8417,6 @@
 }
 */
 
-static void alc883_mitac_automute(struct hda_codec *codec)
-{
-	alc883_mitac_hp_automute(codec);
-	/* alc883_mitac_mic_automute(codec); */
-}
-
-static void alc883_mitac_unsol_event(struct hda_codec *codec,
-					   unsigned int res)
-{
-	switch (res >> 26) {
-	case ALC880_HP_EVENT:
-		alc883_mitac_hp_automute(codec);
-		break;
-	case ALC880_MIC_EVENT:
-		/* alc883_mitac_mic_automute(codec); */
-		break;
-	}
-}
-
 static struct hda_verb alc883_mitac_verbs[] = {
 	/* HP */
 	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
@@ -8021,21 +8461,31 @@
 	{ } /* end */
 };
 
-static struct hda_verb alc883_tagra_verbs[] = {
+static struct hda_verb alc883_targa_verbs[] = {
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
 	{0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
 
 	{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
 
-	{0x18, AC_VERB_SET_CONNECT_SEL, 0x02}, /* mic/clfe */
-	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x01}, /* line/surround */
-	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, /* HP */
+/* Connect Line-Out side jack (SPDIF) to Side */
+	{0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x17, AC_VERB_SET_CONNECT_SEL, 0x03},
+/* Connect Mic jack to CLFE */
+	{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x18, AC_VERB_SET_CONNECT_SEL, 0x02},
+/* Connect Line-in jack to Surround */
+	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x01},
+/* Connect HP out jack to Front */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+	{0x1b, AC_VERB_SET_CONNECT_SEL, 0x00},
 
 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
-	{0x01, AC_VERB_SET_GPIO_MASK, 0x03},
-	{0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03},
-	{0x01, AC_VERB_SET_GPIO_DATA, 0x03},
 
 	{ } /* end */
 };
@@ -8094,29 +8544,26 @@
 	{ }
 };
 
-static void alc888_3st_hp_front_automute(struct hda_codec *codec)
-{
-	unsigned int present, bits;
+static struct hda_verb alc883_vaiott_verbs[] = {
+	/* HP */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
 
-	present = snd_hda_codec_read(codec, 0x1b, 0,
-			AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-	snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-	snd_hda_codec_amp_stereo(codec, 0x18, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-}
+	/* enable unsolicited event */
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
 
-static void alc888_3st_hp_unsol_event(struct hda_codec *codec,
-				      unsigned int res)
+	{ } /* end */
+};
+
+static void alc888_3st_hp_init_hook(struct hda_codec *codec)
 {
-	switch (res >> 26) {
-	case ALC880_HP_EVENT:
-		alc888_3st_hp_front_automute(codec);
-		break;
-	}
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x1b;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[1] = 0x16;
+	spec->autocfg.speaker_pins[2] = 0x18;
+	alc_automute_amp(codec);
 }
 
 static struct hda_verb alc888_3st_hp_verbs[] = {
@@ -8213,56 +8660,18 @@
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_medion_md2_automute(struct hda_codec *codec)
+static void alc883_medion_md2_init_hook(struct hda_codec *codec)
 {
- 	unsigned int present;
+	struct alc_spec *spec = codec->spec;
 
- 	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-static void alc883_medion_md2_unsol_event(struct hda_codec *codec,
-					  unsigned int res)
-{
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc883_medion_md2_automute(codec);
+	spec->autocfg.hp_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[0] = 0x15;
+	alc_automute_amp(codec);
 }
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_tagra_automute(struct hda_codec *codec)
-{
- 	unsigned int present;
-	unsigned char bits;
-
- 	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-	snd_hda_codec_write_cache(codec, 1, 0, AC_VERB_SET_GPIO_DATA,
-				  present ? 1 : 3);
-}
-
-static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc883_tagra_automute(codec);
-}
-
-/* toggle speaker-output according to the hp-jack state */
-static void alc883_clevo_m720_hp_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-	unsigned char bits;
-
-	present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0)
-		& AC_PINSENSE_PRESENCE;
-	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-}
+#define alc883_targa_init_hook		alc882_targa_init_hook
+#define alc883_targa_unsol_event	alc882_targa_unsol_event
 
 static void alc883_clevo_m720_mic_automute(struct hda_codec *codec)
 {
@@ -8274,9 +8683,13 @@
 				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 }
 
-static void alc883_clevo_m720_automute(struct hda_codec *codec)
+static void alc883_clevo_m720_init_hook(struct hda_codec *codec)
 {
-	alc883_clevo_m720_hp_automute(codec);
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc_automute_amp(codec);
 	alc883_clevo_m720_mic_automute(codec);
 }
 
@@ -8284,52 +8697,32 @@
 					   unsigned int res)
 {
 	switch (res >> 26) {
-	case ALC880_HP_EVENT:
-		alc883_clevo_m720_hp_automute(codec);
-		break;
 	case ALC880_MIC_EVENT:
 		alc883_clevo_m720_mic_automute(codec);
 		break;
+	default:
+		alc_automute_amp_unsol_event(codec, res);
+		break;
 	}
 }
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_2ch_fujitsu_pi2515_automute(struct hda_codec *codec)
+static void alc883_2ch_fujitsu_pi2515_init_hook(struct hda_codec *codec)
 {
- 	unsigned int present;
-	unsigned char bits;
+	struct alc_spec *spec = codec->spec;
 
- 	present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0)
-		& AC_PINSENSE_PRESENCE;
-	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
+	spec->autocfg.hp_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[0] = 0x15;
+	alc_automute_amp(codec);
 }
 
-static void alc883_2ch_fujitsu_pi2515_unsol_event(struct hda_codec *codec,
-						  unsigned int res)
+static void alc883_haier_w66_init_hook(struct hda_codec *codec)
 {
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc883_2ch_fujitsu_pi2515_automute(codec);
-}
+	struct alc_spec *spec = codec->spec;
 
-static void alc883_haier_w66_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-	unsigned char bits;
-
-	present = snd_hda_codec_read(codec, 0x1b, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? 0x80 : 0;
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				 0x80, bits);
-}
-
-static void alc883_haier_w66_unsol_event(struct hda_codec *codec,
-					 unsigned int res)
-{
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc883_haier_w66_automute(codec);
+	spec->autocfg.hp_pins[0] = 0x1b;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc_automute_amp(codec);
 }
 
 static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec)
@@ -8337,8 +8730,8 @@
  	unsigned int present;
 	unsigned char bits;
 
- 	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+	present = snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0)
+		& AC_PINSENSE_PRESENCE;
 	bits = present ? HDA_AMP_MUTE : 0;
 	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
 				 HDA_AMP_MUTE, bits);
@@ -8368,23 +8761,14 @@
 }
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc883_acer_aspire_automute(struct hda_codec *codec)
+static void alc883_acer_aspire_init_hook(struct hda_codec *codec)
 {
- 	unsigned int present;
+	struct alc_spec *spec = codec->spec;
 
- 	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-	snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-static void alc883_acer_aspire_unsol_event(struct hda_codec *codec,
-					   unsigned int res)
-{
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc883_acer_aspire_automute(codec);
+	spec->autocfg.hp_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[1] = 0x16;
+	alc_automute_amp(codec);
 }
 
 static struct hda_verb alc883_acer_eapd_verbs[] = {
@@ -8405,75 +8789,39 @@
 	{ }
 };
 
-static void alc888_6st_dell_front_automute(struct hda_codec *codec)
+static void alc888_6st_dell_init_hook(struct hda_codec *codec)
 {
- 	unsigned int present;
+	struct alc_spec *spec = codec->spec;
 
- 	present = snd_hda_codec_read(codec, 0x1b, 0,
-				AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-	snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-				HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-	snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-				HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-	snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
-				HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+	spec->autocfg.hp_pins[0] = 0x1b;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[1] = 0x15;
+	spec->autocfg.speaker_pins[2] = 0x16;
+	spec->autocfg.speaker_pins[3] = 0x17;
+	alc_automute_amp(codec);
 }
 
-static void alc888_6st_dell_unsol_event(struct hda_codec *codec,
-					     unsigned int res)
+static void alc888_lenovo_sky_init_hook(struct hda_codec *codec)
 {
-	switch (res >> 26) {
-	case ALC880_HP_EVENT:
-		/* printk(KERN_DEBUG "hp_event\n"); */
-		alc888_6st_dell_front_automute(codec);
-		break;
-	}
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x1b;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[1] = 0x15;
+	spec->autocfg.speaker_pins[2] = 0x16;
+	spec->autocfg.speaker_pins[3] = 0x17;
+	spec->autocfg.speaker_pins[4] = 0x1a;
+	alc_automute_amp(codec);
 }
 
-static void alc888_lenovo_sky_front_automute(struct hda_codec *codec)
+static void alc883_vaiott_init_hook(struct hda_codec *codec)
 {
-	unsigned int mute;
-	unsigned int present;
+	struct alc_spec *spec = codec->spec;
 
-	snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
-	present = snd_hda_codec_read(codec, 0x1b, 0,
-				     AC_VERB_GET_PIN_SENSE, 0);
-	present = (present & 0x80000000) != 0;
-	if (present) {
-		/* mute internal speaker */
-		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, HDA_AMP_MUTE);
-		snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, HDA_AMP_MUTE);
-		snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, HDA_AMP_MUTE);
-		snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, HDA_AMP_MUTE);
-		snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, HDA_AMP_MUTE);
-	} else {
-		/* unmute internal speaker if necessary */
-		mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
-		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, mute);
-		snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, mute);
-		snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, mute);
-		snd_hda_codec_amp_stereo(codec, 0x17, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, mute);
-		snd_hda_codec_amp_stereo(codec, 0x1a, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, mute);
-	}
-}
-
-static void alc883_lenovo_sky_unsol_event(struct hda_codec *codec,
-					     unsigned int res)
-{
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc888_lenovo_sky_front_automute(codec);
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[1] = 0x17;
+	alc_automute_amp(codec);
 }
 
 /*
@@ -8561,39 +8909,33 @@
 			    0x7000 | (0x01 << 8) | (present ? 0x80 : 0));
 }
 
-static void alc883_M90V_speaker_automute(struct hda_codec *codec)
+static void alc883_M90V_init_hook(struct hda_codec *codec)
 {
-	unsigned int present;
-	unsigned char bits;
+	struct alc_spec *spec = codec->spec;
 
-	present = snd_hda_codec_read(codec, 0x1b, 0,
-				     AC_VERB_GET_PIN_SENSE, 0)
-		& AC_PINSENSE_PRESENCE;
-	bits = present ? 0 : PIN_OUT;
-	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    bits);
-	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    bits);
-	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    bits);
+	spec->autocfg.hp_pins[0] = 0x1b;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[1] = 0x15;
+	spec->autocfg.speaker_pins[2] = 0x16;
+	alc_automute_pin(codec);
 }
 
 static void alc883_mode2_unsol_event(struct hda_codec *codec,
 					   unsigned int res)
 {
 	switch (res >> 26) {
-	case ALC880_HP_EVENT:
-		alc883_M90V_speaker_automute(codec);
-		break;
 	case ALC880_MIC_EVENT:
 		alc883_nb_mic_automute(codec);
 		break;
+	default:
+		alc_sku_unsol_event(codec, res);
+		break;
 	}
 }
 
 static void alc883_mode2_inithook(struct hda_codec *codec)
 {
-	alc883_M90V_speaker_automute(codec);
+	alc883_M90V_init_hook(codec);
 	alc883_nb_mic_automute(codec);
 }
 
@@ -8610,39 +8952,56 @@
 	{ } /* end */
 };
 
-static void alc883_eee1601_speaker_automute(struct hda_codec *codec)
+static void alc883_eee1601_inithook(struct hda_codec *codec)
 {
-	unsigned int present;
-	unsigned char bits;
+	struct alc_spec *spec = codec->spec;
 
-	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0)
-		& AC_PINSENSE_PRESENCE;
-	bits = present ? 0 : PIN_OUT;
-	snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    bits);
+	spec->autocfg.hp_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[0] = 0x1b;
+	alc_automute_pin(codec);
 }
 
-static void alc883_eee1601_unsol_event(struct hda_codec *codec,
-					   unsigned int res)
+static struct hda_verb alc889A_mb31_verbs[] = {
+	/* Init rear pin (used as headphone output) */
+	{0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc4},    /* Apple Headphones */
+	{0x15, AC_VERB_SET_CONNECT_SEL, 0x00},           /* Connect to front */
+	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
+	/* Init line pin (used as output in 4ch and 6ch mode) */
+	{0x1a, AC_VERB_SET_CONNECT_SEL, 0x02},           /* Connect to CLFE */
+	/* Init line 2 pin (used as headphone out by default) */
+	{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},  /* Use as input */
+	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, /* Mute output */
+	{ } /* end */
+};
+
+/* Mute speakers according to the headphone jack state */
+static void alc889A_mb31_automute(struct hda_codec *codec)
 {
-	switch (res >> 26) {
-	case ALC880_HP_EVENT:
-		alc883_eee1601_speaker_automute(codec);
-		break;
+	unsigned int present;
+
+	/* Mute only in 2ch or 4ch mode */
+	if (snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_CONNECT_SEL, 0)
+	    == 0x00) {
+		present = snd_hda_codec_read(codec, 0x15, 0,
+			AC_VERB_GET_PIN_SENSE, 0) & AC_PINSENSE_PRESENCE;
+		snd_hda_codec_amp_stereo(codec, 0x14,  HDA_OUTPUT, 0,
+			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
+		snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
+			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
 	}
 }
 
-static void alc883_eee1601_inithook(struct hda_codec *codec)
+static void alc889A_mb31_unsol_event(struct hda_codec *codec, unsigned int res)
 {
-	alc883_eee1601_speaker_automute(codec);
+	if ((res >> 26) == ALC880_HP_EVENT)
+		alc889A_mb31_automute(codec);
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc883_loopbacks	alc880_loopbacks
 #endif
 
-/* pcm configuration: identiacal with ALC880 */
+/* pcm configuration: identical with ALC880 */
 #define alc883_pcm_analog_playback	alc880_pcm_analog_playback
 #define alc883_pcm_analog_capture	alc880_pcm_analog_capture
 #define alc883_pcm_analog_alt_capture	alc880_pcm_analog_alt_capture
@@ -8659,9 +9018,12 @@
 	[ALC883_6ST_DIG]	= "6stack-dig",
 	[ALC883_TARGA_DIG]	= "targa-dig",
 	[ALC883_TARGA_2ch_DIG]	= "targa-2ch-dig",
+	[ALC883_TARGA_8ch_DIG]	= "targa-8ch-dig",
 	[ALC883_ACER]		= "acer",
 	[ALC883_ACER_ASPIRE]	= "acer-aspire",
 	[ALC888_ACER_ASPIRE_4930G]	= "acer-aspire-4930g",
+	[ALC888_ACER_ASPIRE_6530G]	= "acer-aspire-6530g",
+	[ALC888_ACER_ASPIRE_8930G]	= "acer-aspire-8930g",
 	[ALC883_MEDION]		= "medion",
 	[ALC883_MEDION_MD2]	= "medion-md2",
 	[ALC883_LAPTOP_EAPD]	= "laptop-eapd",
@@ -8678,6 +9040,8 @@
 	[ALC888_FUJITSU_XA3530] = "fujitsu-xa3530",
 	[ALC883_3ST_6ch_INTEL]	= "3stack-6ch-intel",
 	[ALC1200_ASUS_P5Q]	= "asus-p5q",
+	[ALC889A_MB31]		= "mb31",
+	[ALC883_SONY_VAIO_TT]	= "sony-vaio-tt",
 	[ALC883_AUTO]		= "auto",
 };
 
@@ -8693,14 +9057,20 @@
 		ALC888_ACER_ASPIRE_4930G),
 	SND_PCI_QUIRK(0x1025, 0x013f, "Acer Aspire 5930G",
 		ALC888_ACER_ASPIRE_4930G),
+	SND_PCI_QUIRK(0x1025, 0x0145, "Acer Aspire 8930G",
+		ALC888_ACER_ASPIRE_8930G),
+	SND_PCI_QUIRK(0x1025, 0x0146, "Acer Aspire 6935G",
+		ALC888_ACER_ASPIRE_8930G),
 	SND_PCI_QUIRK(0x1025, 0x0157, "Acer X3200", ALC883_AUTO),
 	SND_PCI_QUIRK(0x1025, 0x0158, "Acer AX1700-U3700A", ALC883_AUTO),
 	SND_PCI_QUIRK(0x1025, 0x015e, "Acer Aspire 6930G",
 		ALC888_ACER_ASPIRE_4930G),
 	SND_PCI_QUIRK(0x1025, 0x0166, "Acer Aspire 6530G",
-		ALC888_ACER_ASPIRE_4930G),
-	/* default Acer */
-	SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER),
+		ALC888_ACER_ASPIRE_6530G),
+	/* default Acer -- disabled as it causes more problems.
+	 *    model=auto should work fine now
+	 */
+	/* SND_PCI_QUIRK_VENDOR(0x1025, "Acer laptop", ALC883_ACER), */
 	SND_PCI_QUIRK(0x1028, 0x020d, "Dell Inspiron 530", ALC888_6ST_DELL),
 	SND_PCI_QUIRK(0x103c, 0x2a3d, "HP Pavillion", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x103c, 0x2a4f, "HP Samba", ALC888_3ST_HP),
@@ -8736,6 +9106,7 @@
 	SND_PCI_QUIRK(0x1462, 0x4314, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x4319, "MSI", ALC883_TARGA_DIG),
 	SND_PCI_QUIRK(0x1462, 0x4324, "MSI", ALC883_TARGA_DIG),
+	SND_PCI_QUIRK(0x1462, 0x6510, "MSI GX620", ALC883_TARGA_8ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x6668, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7187, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7250, "MSI", ALC883_6ST_DIG),
@@ -8743,6 +9114,7 @@
 	SND_PCI_QUIRK(0x1462, 0x7267, "MSI", ALC883_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7280, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0x7327, "MSI", ALC883_6ST_DIG),
+	SND_PCI_QUIRK(0x1462, 0x7350, "MSI", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1462, 0xa422, "MSI", ALC883_TARGA_2ch_DIG),
 	SND_PCI_QUIRK(0x147b, 0x1083, "Abit IP35-PRO", ALC883_6ST_DIG),
 	SND_PCI_QUIRK(0x1558, 0x0721, "Clevo laptop M720R", ALC883_CLEVO_M720),
@@ -8768,6 +9140,7 @@
 	SND_PCI_QUIRK(0x8086, 0x2503, "82801H", ALC883_MITAC),
 	SND_PCI_QUIRK(0x8086, 0x0022, "DX58SO", ALC883_3ST_6ch_INTEL),
 	SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch),
+	SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC883_SONY_VAIO_TT),
 	{}
 };
 
@@ -8838,8 +9211,8 @@
 		.input_mux = &alc883_capture_source,
 	},
 	[ALC883_TARGA_DIG] = {
-		.mixers = { alc883_tagra_mixer, alc883_chmode_mixer },
-		.init_verbs = { alc883_init_verbs, alc883_tagra_verbs},
+		.mixers = { alc883_targa_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc883_targa_verbs},
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.dac_nids = alc883_dac_nids,
 		.dig_out_nid = ALC883_DIGOUT_NID,
@@ -8847,12 +9220,12 @@
 		.channel_mode = alc883_3ST_6ch_modes,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_capture_source,
-		.unsol_event = alc883_tagra_unsol_event,
-		.init_hook = alc883_tagra_automute,
+		.unsol_event = alc883_targa_unsol_event,
+		.init_hook = alc883_targa_init_hook,
 	},
 	[ALC883_TARGA_2ch_DIG] = {
-		.mixers = { alc883_tagra_2ch_mixer},
-		.init_verbs = { alc883_init_verbs, alc883_tagra_verbs},
+		.mixers = { alc883_targa_2ch_mixer},
+		.init_verbs = { alc883_init_verbs, alc883_targa_verbs},
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.dac_nids = alc883_dac_nids,
 		.adc_nids = alc883_adc_nids_alt,
@@ -8861,8 +9234,26 @@
 		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
-		.unsol_event = alc883_tagra_unsol_event,
-		.init_hook = alc883_tagra_automute,
+		.unsol_event = alc883_targa_unsol_event,
+		.init_hook = alc883_targa_init_hook,
+	},
+	[ALC883_TARGA_8ch_DIG] = {
+		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc880_gpio3_init_verbs,
+				alc883_targa_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
+		.adc_nids = alc883_adc_nids_rev,
+		.capsrc_nids = alc883_capsrc_nids_rev,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.dig_in_nid = ALC883_DIGIN_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_4ST_8ch_modes),
+		.channel_mode = alc883_4ST_8ch_modes,
+		.need_dac_fix = 1,
+		.input_mux = &alc883_capture_source,
+		.unsol_event = alc883_targa_unsol_event,
+		.init_hook = alc883_targa_init_hook,
 	},
 	[ALC883_ACER] = {
 		.mixers = { alc883_base_mixer },
@@ -8887,8 +9278,8 @@
 		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
-		.unsol_event = alc883_acer_aspire_unsol_event,
-		.init_hook = alc883_acer_aspire_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc883_acer_aspire_init_hook,
 	},
 	[ALC888_ACER_ASPIRE_4930G] = {
 		.mixers = { alc888_base_mixer,
@@ -8907,8 +9298,47 @@
 		.num_mux_defs =
 			ARRAY_SIZE(alc888_2_capture_sources),
 		.input_mux = alc888_2_capture_sources,
-		.unsol_event = alc888_acer_aspire_4930g_unsol_event,
-		.init_hook = alc888_acer_aspire_4930g_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc888_acer_aspire_4930g_init_hook,
+	},
+	[ALC888_ACER_ASPIRE_6530G] = {
+		.mixers = { alc888_acer_aspire_6530_mixer },
+		.init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
+				alc888_acer_aspire_6530g_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids_rev),
+		.adc_nids = alc883_adc_nids_rev,
+		.capsrc_nids = alc883_capsrc_nids_rev,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+		.channel_mode = alc883_3ST_2ch_modes,
+		.num_mux_defs =
+			ARRAY_SIZE(alc888_2_capture_sources),
+		.input_mux = alc888_acer_aspire_6530_sources,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc888_acer_aspire_4930g_init_hook,
+	},
+	[ALC888_ACER_ASPIRE_8930G] = {
+		.mixers = { alc888_base_mixer,
+				alc883_chmode_mixer },
+		.init_verbs = { alc883_init_verbs, alc880_gpio1_init_verbs,
+				alc889_acer_aspire_8930g_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_adc_nids = ARRAY_SIZE(alc889_adc_nids),
+		.adc_nids = alc889_adc_nids,
+		.capsrc_nids = alc889_capsrc_nids,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_6ch_modes),
+		.channel_mode = alc883_3ST_6ch_modes,
+		.need_dac_fix = 1,
+		.const_channel_count = 6,
+		.num_mux_defs =
+			ARRAY_SIZE(alc889_capture_sources),
+		.input_mux = alc889_capture_sources,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc889_acer_aspire_8930g_init_hook,
 	},
 	[ALC883_MEDION] = {
 		.mixers = { alc883_fivestack_mixer,
@@ -8932,8 +9362,8 @@
 		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
-		.unsol_event = alc883_medion_md2_unsol_event,
-		.init_hook = alc883_medion_md2_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc883_medion_md2_init_hook,
 	},
 	[ALC883_LAPTOP_EAPD] = {
 		.mixers = { alc883_base_mixer },
@@ -8954,7 +9384,7 @@
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
 		.unsol_event = alc883_clevo_m720_unsol_event,
-		.init_hook = alc883_clevo_m720_automute,
+		.init_hook = alc883_clevo_m720_init_hook,
 	},
 	[ALC883_LENOVO_101E_2ch] = {
 		.mixers = { alc883_lenovo_101e_2ch_mixer},
@@ -8978,8 +9408,8 @@
 		.channel_mode = alc883_3ST_2ch_modes,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_lenovo_nb0763_capture_source,
-		.unsol_event = alc883_medion_md2_unsol_event,
-		.init_hook = alc883_medion_md2_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc883_medion_md2_init_hook,
 	},
 	[ALC888_LENOVO_MS7195_DIG] = {
 		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -8995,7 +9425,7 @@
 		.init_hook = alc888_lenovo_ms7195_front_automute,
 	},
 	[ALC883_HAIER_W66] = {
-		.mixers = { alc883_tagra_2ch_mixer},
+		.mixers = { alc883_targa_2ch_mixer},
 		.init_verbs = { alc883_init_verbs, alc883_haier_w66_verbs},
 		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
 		.dac_nids = alc883_dac_nids,
@@ -9003,8 +9433,8 @@
 		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
-		.unsol_event = alc883_haier_w66_unsol_event,
-		.init_hook = alc883_haier_w66_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc883_haier_w66_init_hook,
 	},
 	[ALC888_3ST_HP] = {
 		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9015,8 +9445,8 @@
 		.channel_mode = alc888_3st_hp_modes,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_capture_source,
-		.unsol_event = alc888_3st_hp_unsol_event,
-		.init_hook = alc888_3st_hp_front_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc888_3st_hp_init_hook,
 	},
 	[ALC888_6ST_DELL] = {
 		.mixers = { alc883_base_mixer, alc883_chmode_mixer },
@@ -9028,8 +9458,8 @@
 		.num_channel_mode = ARRAY_SIZE(alc883_sixstack_modes),
 		.channel_mode = alc883_sixstack_modes,
 		.input_mux = &alc883_capture_source,
-		.unsol_event = alc888_6st_dell_unsol_event,
-		.init_hook = alc888_6st_dell_front_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc888_6st_dell_init_hook,
 	},
 	[ALC883_MITAC] = {
 		.mixers = { alc883_mitac_mixer },
@@ -9039,8 +9469,8 @@
 		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_capture_source,
-		.unsol_event = alc883_mitac_unsol_event,
-		.init_hook = alc883_mitac_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc883_mitac_init_hook,
 	},
 	[ALC883_FUJITSU_PI2515] = {
 		.mixers = { alc883_2ch_fujitsu_pi2515_mixer },
@@ -9052,8 +9482,8 @@
 		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
 		.channel_mode = alc883_3ST_2ch_modes,
 		.input_mux = &alc883_fujitsu_pi2515_capture_source,
-		.unsol_event = alc883_2ch_fujitsu_pi2515_unsol_event,
-		.init_hook = alc883_2ch_fujitsu_pi2515_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc883_2ch_fujitsu_pi2515_init_hook,
 	},
 	[ALC888_FUJITSU_XA3530] = {
 		.mixers = { alc888_base_mixer, alc883_chmode_mixer },
@@ -9070,8 +9500,8 @@
 		.num_mux_defs =
 			ARRAY_SIZE(alc888_2_capture_sources),
 		.input_mux = alc888_2_capture_sources,
-		.unsol_event = alc888_fujitsu_xa3530_unsol_event,
-		.init_hook = alc888_fujitsu_xa3530_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc888_fujitsu_xa3530_init_hook,
 	},
 	[ALC888_LENOVO_SKY] = {
 		.mixers = { alc888_lenovo_sky_mixer, alc883_chmode_mixer },
@@ -9083,8 +9513,8 @@
 		.channel_mode = alc883_sixstack_modes,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_lenovo_sky_capture_source,
-		.unsol_event = alc883_lenovo_sky_unsol_event,
-		.init_hook = alc888_lenovo_sky_front_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc888_lenovo_sky_init_hook,
 	},
 	[ALC888_ASUS_M90V] = {
 		.mixers = { alc883_3ST_6ch_mixer, alc883_chmode_mixer },
@@ -9112,7 +9542,7 @@
 		.channel_mode = alc883_3ST_2ch_modes,
 		.need_dac_fix = 1,
 		.input_mux = &alc883_asus_eee1601_capture_source,
-		.unsol_event = alc883_eee1601_unsol_event,
+		.unsol_event = alc_sku_unsol_event,
 		.init_hook = alc883_eee1601_inithook,
 	},
 	[ALC1200_ASUS_P5Q] = {
@@ -9127,6 +9557,32 @@
 		.channel_mode = alc883_sixstack_modes,
 		.input_mux = &alc883_capture_source,
 	},
+	[ALC889A_MB31] = {
+		.mixers = { alc889A_mb31_mixer, alc883_chmode_mixer},
+		.init_verbs = { alc883_init_verbs, alc889A_mb31_verbs,
+			alc880_gpio1_init_verbs },
+		.adc_nids = alc883_adc_nids,
+		.num_adc_nids = ARRAY_SIZE(alc883_adc_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.channel_mode = alc889A_mb31_6ch_modes,
+		.num_channel_mode = ARRAY_SIZE(alc889A_mb31_6ch_modes),
+		.input_mux = &alc889A_mb31_capture_source,
+		.dig_out_nid = ALC883_DIGOUT_NID,
+		.unsol_event = alc889A_mb31_unsol_event,
+		.init_hook = alc889A_mb31_automute,
+	},
+	[ALC883_SONY_VAIO_TT] = {
+		.mixers = { alc883_vaiott_mixer },
+		.init_verbs = { alc883_init_verbs, alc883_vaiott_verbs },
+		.num_dacs = ARRAY_SIZE(alc883_dac_nids),
+		.dac_nids = alc883_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes),
+		.channel_mode = alc883_3ST_2ch_modes,
+		.input_mux = &alc883_capture_source,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc883_vaiott_init_hook,
+	},
 };
 
 
@@ -9155,7 +9611,6 @@
 	struct alc_spec *spec = codec->spec;
 	int i;
 
-	alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
 	for (i = 0; i <= HDA_SIDE; i++) {
 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
 		int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -9273,10 +9728,18 @@
 	board_config = snd_hda_check_board_config(codec, ALC883_MODEL_LAST,
 						  alc883_models,
 						  alc883_cfg_tbl);
-	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC883, "
-		       "trying auto-probe from BIOS...\n");
-		board_config = ALC883_AUTO;
+	if (board_config < 0 || board_config >= ALC883_MODEL_LAST) {
+		/* Pick up systems that don't supply PCI SSID */
+		switch (codec->subsystem_id) {
+		case 0x106b3600: /* Macbook 3.1 */
+			board_config = ALC889A_MB31;
+			break;
+		default:
+			printk(KERN_INFO
+				"hda_codec: Unknown model for %s, trying "
+				"auto-probe from BIOS...\n", codec->chip_name);
+			board_config = ALC883_AUTO;
+		}
 	}
 
 	if (board_config == ALC883_AUTO) {
@@ -9304,43 +9767,29 @@
 
 	switch (codec->vendor_id) {
 	case 0x10ec0888:
-		if (codec->revision_id == 0x100101) {
-			spec->stream_name_analog = "ALC1200 Analog";
-			spec->stream_name_digital = "ALC1200 Digital";
-		} else {
-			spec->stream_name_analog = "ALC888 Analog";
-			spec->stream_name_digital = "ALC888 Digital";
-		}
 		if (!spec->num_adc_nids) {
 			spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
 			spec->adc_nids = alc883_adc_nids;
 		}
 		if (!spec->capsrc_nids)
 			spec->capsrc_nids = alc883_capsrc_nids;
-		spec->capture_style = CAPT_MIX; /* matrix-style capture */
+		spec->init_amp = ALC_INIT_DEFAULT; /* always initialize */
 		break;
 	case 0x10ec0889:
-		spec->stream_name_analog = "ALC889 Analog";
-		spec->stream_name_digital = "ALC889 Digital";
 		if (!spec->num_adc_nids) {
 			spec->num_adc_nids = ARRAY_SIZE(alc889_adc_nids);
 			spec->adc_nids = alc889_adc_nids;
 		}
 		if (!spec->capsrc_nids)
 			spec->capsrc_nids = alc889_capsrc_nids;
-		spec->capture_style = CAPT_1MUX_MIX; /* 1mux/Nmix-style
-							capture */
 		break;
 	default:
-		spec->stream_name_analog = "ALC883 Analog";
-		spec->stream_name_digital = "ALC883 Digital";
 		if (!spec->num_adc_nids) {
 			spec->num_adc_nids = ARRAY_SIZE(alc883_adc_nids);
 			spec->adc_nids = alc883_adc_nids;
 		}
 		if (!spec->capsrc_nids)
 			spec->capsrc_nids = alc883_capsrc_nids;
-		spec->capture_style = CAPT_MIX; /* matrix-style capture */
 		break;
 	}
 
@@ -9413,24 +9862,6 @@
 	{ } /* end */
 };
 
-static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
-	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
-	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
-	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
-	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
-	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
-	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
-	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
-	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
-	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
-	/*HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0D, 0x0, HDA_OUTPUT),*/
-	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-	{ } /* end */
-};
-
 /* update HP, line and mono-out pins according to the master switch */
 static void alc262_hp_master_update(struct hda_codec *codec)
 {
@@ -9486,14 +9917,7 @@
 	alc262_hp_wildwest_automute(codec);
 }
 
-static int alc262_hp_master_sw_get(struct snd_kcontrol *kcontrol,
-				   struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct alc_spec *spec = codec->spec;
-	*ucontrol->value.integer.value = spec->master_sw;
-	return 0;
-}
+#define alc262_hp_master_sw_get		alc260_hp_master_sw_get
 
 static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
 				   struct snd_ctl_elem_value *ucontrol)
@@ -9509,14 +9933,17 @@
 	return 1;
 }
 
+#define ALC262_HP_MASTER_SWITCH					\
+	{							\
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
+		.name = "Master Playback Switch",		\
+		.info = snd_ctl_boolean_mono_info,		\
+		.get = alc262_hp_master_sw_get,			\
+		.put = alc262_hp_master_sw_put,			\
+	}
+
 static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Master Playback Switch",
-		.info = snd_ctl_boolean_mono_info,
-		.get = alc262_hp_master_sw_get,
-		.put = alc262_hp_master_sw_put,
-	},
+	ALC262_HP_MASTER_SWITCH,
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Playback Switch", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
@@ -9540,13 +9967,7 @@
 };
 
 static struct snd_kcontrol_new alc262_HP_BPC_WildWest_mixer[] = {
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Master Playback Switch",
-		.info = snd_ctl_boolean_mono_info,
-		.get = alc262_hp_master_sw_get,
-		.put = alc262_hp_master_sw_put,
-	},
+	ALC262_HP_MASTER_SWITCH,
 	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
@@ -9573,32 +9994,13 @@
 };
 
 /* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hp_t5735_automute(struct hda_codec *codec, int force)
+static void alc262_hp_t5735_init_hook(struct hda_codec *codec)
 {
 	struct alc_spec *spec = codec->spec;
 
-	if (force || !spec->sense_updated) {
-		unsigned int present;
-		present = snd_hda_codec_read(codec, 0x15, 0,
-					     AC_VERB_GET_PIN_SENSE, 0);
-		spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0;
-		spec->sense_updated = 1;
-	}
-	snd_hda_codec_amp_stereo(codec, 0x0c, HDA_OUTPUT, 0, HDA_AMP_MUTE,
-				 spec->jack_present ? HDA_AMP_MUTE : 0);
-}
-
-static void alc262_hp_t5735_unsol_event(struct hda_codec *codec,
-					unsigned int res)
-{
-	if ((res >> 26) != ALC880_HP_EVENT)
-		return;
-	alc262_hp_t5735_automute(codec, 1);
-}
-
-static void alc262_hp_t5735_init_hook(struct hda_codec *codec)
-{
-	alc262_hp_t5735_automute(codec, 1);
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x0c; /* HACK: not actually a pin */
+	alc_automute_amp(codec);
 }
 
 static struct snd_kcontrol_new alc262_hp_t5735_mixer[] = {
@@ -9651,46 +10053,132 @@
 	},
 };
 
-/* bind hp and internal speaker mute (with plug check) */
-static int alc262_sony_master_sw_put(struct snd_kcontrol *kcontrol,
-				     struct snd_ctl_elem_value *ucontrol)
+/* bind hp and internal speaker mute (with plug check) as master switch */
+static void alc262_hippo_master_update(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
+	hda_nid_t line_nid = spec->autocfg.line_out_pins[0];
+	hda_nid_t speaker_nid = spec->autocfg.speaker_pins[0];
+	unsigned int mute;
+
+	/* HP */
+	mute = spec->master_sw ? 0 : HDA_AMP_MUTE;
+	snd_hda_codec_amp_stereo(codec, hp_nid, HDA_OUTPUT, 0,
+				 HDA_AMP_MUTE, mute);
+	/* mute internal speaker per jack sense */
+	if (spec->jack_present)
+		mute = HDA_AMP_MUTE;
+	if (line_nid)
+		snd_hda_codec_amp_stereo(codec, line_nid, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, mute);
+	if (speaker_nid && speaker_nid != line_nid)
+		snd_hda_codec_amp_stereo(codec, speaker_nid, HDA_OUTPUT, 0,
+					 HDA_AMP_MUTE, mute);
+}
+
+#define alc262_hippo_master_sw_get	alc262_hp_master_sw_get
+
+static int alc262_hippo_master_sw_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	long *valp = ucontrol->value.integer.value;
-	int change;
+	struct alc_spec *spec = codec->spec;
+	int val = !!*ucontrol->value.integer.value;
 
-	/* change hp mute */
-	change = snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0,
-					  HDA_AMP_MUTE,
-					  valp[0] ? 0 : HDA_AMP_MUTE);
-	change |= snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0,
-					   HDA_AMP_MUTE,
-					   valp[1] ? 0 : HDA_AMP_MUTE);
-	if (change) {
-		/* change speaker according to HP jack state */
-		struct alc_spec *spec = codec->spec;
-		unsigned int mute;
-		if (spec->jack_present)
-			mute = HDA_AMP_MUTE;
-		else
-			mute = snd_hda_codec_amp_read(codec, 0x15, 0,
-						      HDA_OUTPUT, 0);
-		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, mute);
-	}
-	return change;
+	if (val == spec->master_sw)
+		return 0;
+	spec->master_sw = val;
+	alc262_hippo_master_update(codec);
+	return 1;
 }
 
+#define ALC262_HIPPO_MASTER_SWITCH				\
+	{							\
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
+		.name = "Master Playback Switch",		\
+		.info = snd_ctl_boolean_mono_info,		\
+		.get = alc262_hippo_master_sw_get,		\
+		.put = alc262_hippo_master_sw_put,		\
+	}
+
+static struct snd_kcontrol_new alc262_hippo_mixer[] = {
+	ALC262_HIPPO_MASTER_SWITCH,
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x0d, 0x0, HDA_OUTPUT),
+	{ } /* end */
+};
+
+static struct snd_kcontrol_new alc262_hippo1_mixer[] = {
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	ALC262_HIPPO_MASTER_SWITCH,
+	HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+	{ } /* end */
+};
+
+/* mute/unmute internal speaker according to the hp jack and mute state */
+static void alc262_hippo_automute(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+	hda_nid_t hp_nid = spec->autocfg.hp_pins[0];
+	unsigned int present;
+
+	/* need to execute and sync at first */
+	snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0);
+	present = snd_hda_codec_read(codec, hp_nid, 0,
+				     AC_VERB_GET_PIN_SENSE, 0);
+	spec->jack_present = (present & 0x80000000) != 0;
+	alc262_hippo_master_update(codec);
+}
+
+static void alc262_hippo_unsol_event(struct hda_codec *codec, unsigned int res)
+{
+	if ((res >> 26) != ALC880_HP_EVENT)
+		return;
+	alc262_hippo_automute(codec);
+}
+
+static void alc262_hippo_init_hook(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc262_hippo_automute(codec);
+}
+
+static void alc262_hippo1_init_hook(struct hda_codec *codec)
+{
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x1b;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc262_hippo_automute(codec);
+}
+
+
 static struct snd_kcontrol_new alc262_sony_mixer[] = {
 	HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Master Playback Switch",
-		.info = snd_hda_mixer_amp_switch_info,
-		.get = snd_hda_mixer_amp_switch_get,
-		.put = alc262_sony_master_sw_put,
-		.private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
-	},
+	ALC262_HIPPO_MASTER_SWITCH,
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
@@ -9699,8 +10187,8 @@
 };
 
 static struct snd_kcontrol_new alc262_benq_t31_mixer[] = {
-	HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Front Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
+	ALC262_HIPPO_MASTER_SWITCH,
 	HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
@@ -9741,34 +10229,15 @@
 };
 
 /* unsolicited event for HP jack sensing */
-static void alc262_tyan_automute(struct hda_codec *codec)
+static void alc262_tyan_init_hook(struct hda_codec *codec)
 {
-	unsigned int mute;
-	unsigned int present;
+	struct alc_spec *spec = codec->spec;
 
-	snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
-	present = snd_hda_codec_read(codec, 0x1b, 0,
-				     AC_VERB_GET_PIN_SENSE, 0);
-	present = (present & 0x80000000) != 0;
-	if (present) {
-		/* mute line output on ATX panel */
-		snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, HDA_AMP_MUTE);
-	} else {
-		/* unmute line output if necessary */
-		mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
-		snd_hda_codec_amp_stereo(codec, 0x15, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, mute);
-	}
+	spec->autocfg.hp_pins[0] = 0x1b;
+	spec->autocfg.speaker_pins[0] = 0x15;
+	alc_automute_amp(codec);
 }
 
-static void alc262_tyan_unsol_event(struct hda_codec *codec,
-				       unsigned int res)
-{
-	if ((res >> 26) != ALC880_HP_EVENT)
-		return;
-	alc262_tyan_automute(codec);
-}
 
 #define alc262_capture_mixer		alc882_capture_mixer
 #define alc262_capture_alt_mixer	alc882_capture_alt_mixer
@@ -9923,99 +10392,25 @@
 				AC_VERB_SET_CONNECT_SEL, present ? 0x0 : 0x09);
 }
 
-/* toggle speaker-output according to the hp-jack state */
-static void alc262_toshiba_s06_speaker_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-	unsigned char bits;
-
-	present = snd_hda_codec_read(codec, 0x15, 0,
-					AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? 0 : PIN_OUT;
-	snd_hda_codec_write(codec, 0x14, 0,
-					AC_VERB_SET_PIN_WIDGET_CONTROL, bits);
-}
-
-
 
 /* unsolicited event for HP jack sensing */
 static void alc262_toshiba_s06_unsol_event(struct hda_codec *codec,
 				       unsigned int res)
 {
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc262_toshiba_s06_speaker_automute(codec);
 	if ((res >> 26) == ALC880_MIC_EVENT)
 		alc262_dmic_automute(codec);
-
+	else
+		alc_sku_unsol_event(codec, res);
 }
 
 static void alc262_toshiba_s06_init_hook(struct hda_codec *codec)
 {
-	alc262_toshiba_s06_speaker_automute(codec);
-	alc262_dmic_automute(codec);
-}
-
-/* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc262_hippo_automute(struct hda_codec *codec)
-{
 	struct alc_spec *spec = codec->spec;
-	unsigned int mute;
-	unsigned int present;
 
-	/* need to execute and sync at first */
-	snd_hda_codec_read(codec, 0x15, 0, AC_VERB_SET_PIN_SENSE, 0);
-	present = snd_hda_codec_read(codec, 0x15, 0,
-				     AC_VERB_GET_PIN_SENSE, 0);
-	spec->jack_present = (present & 0x80000000) != 0;
-	if (spec->jack_present) {
-		/* mute internal speaker */
-		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, HDA_AMP_MUTE);
-	} else {
-		/* unmute internal speaker if necessary */
-		mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
-		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, mute);
-	}
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc262_hippo_unsol_event(struct hda_codec *codec,
-				       unsigned int res)
-{
-	if ((res >> 26) != ALC880_HP_EVENT)
-		return;
-	alc262_hippo_automute(codec);
-}
-
-static void alc262_hippo1_automute(struct hda_codec *codec)
-{
-	unsigned int mute;
-	unsigned int present;
-
-	snd_hda_codec_read(codec, 0x1b, 0, AC_VERB_SET_PIN_SENSE, 0);
-	present = snd_hda_codec_read(codec, 0x1b, 0,
-				     AC_VERB_GET_PIN_SENSE, 0);
-	present = (present & 0x80000000) != 0;
-	if (present) {
-		/* mute internal speaker */
-		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, HDA_AMP_MUTE);
-	} else {
-		/* unmute internal speaker if necessary */
-		mute = snd_hda_codec_amp_read(codec, 0x1b, 0, HDA_OUTPUT, 0);
-		snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-					 HDA_AMP_MUTE, mute);
-	}
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc262_hippo1_unsol_event(struct hda_codec *codec,
-				       unsigned int res)
-{
-	if ((res >> 26) != ALC880_HP_EVENT)
-		return;
-	alc262_hippo1_automute(codec);
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc_automute_pin(codec);
+	alc262_dmic_automute(codec);
 }
 
 /*
@@ -10285,14 +10680,7 @@
 
 static struct snd_kcontrol_new alc262_toshiba_rx1_mixer[] = {
 	HDA_BIND_VOL("Master Playback Volume", &alc262_fujitsu_bind_master_vol),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Master Playback Switch",
-		.info = snd_hda_mixer_amp_switch_info,
-		.get = snd_hda_mixer_amp_switch_get,
-		.put = alc262_sony_master_sw_put,
-		.private_value = HDA_COMPOSE_AMP_VAL(0x15, 3, 0, HDA_OUTPUT),
-	},
+	ALC262_HIPPO_MASTER_SWITCH,
 	HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
 	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
@@ -10513,9 +10901,27 @@
 	return 0;
 }
 
-/* identical with ALC880 */
-#define alc262_auto_create_analog_input_ctls \
-	alc880_auto_create_analog_input_ctls
+static int alc262_auto_create_analog_input_ctls(struct alc_spec *spec,
+						const struct auto_pin_cfg *cfg)
+{
+	int err;
+
+	err = alc880_auto_create_analog_input_ctls(spec, cfg);
+	if (err < 0)
+		return err;
+	/* digital-mic input pin is excluded in alc880_auto_create..()
+	 * because it's under 0x18
+	 */
+	if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 ||
+	    cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) {
+		struct hda_input_mux *imux = &spec->private_imux[0];
+		imux->items[imux->num_items].label = "Int Mic";
+		imux->items[imux->num_items].index = 0x09;
+		imux->num_items++;
+	}
+	return 0;
+}
+
 
 /*
  * generic initialization of ADC, input mixers and output mixers
@@ -10639,31 +11045,46 @@
 	{0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
 	{0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
 
-	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
+	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
         {0x19, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
-	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7023 },
+	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0x7000 },
 
 
 	/* FIXME: use matrix-type input source selection */
-	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */
-	/* Input mixer1: unmute Mic, F-Mic, Line, CD inputs */
+	/* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 0b, 12 */
+	/* Input mixer1: only unmute Mic */
 	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
+	{0x24, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
 	/* Input mixer2 */
 	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
+	{0x23, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
 	/* Input mixer3 */
 	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x00 << 8))},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x03 << 8))},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x02 << 8))},
-	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7000 | (0x04 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x01 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x02 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x03 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x04 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x05 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x06 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x07 << 8))},
+	{0x22, AC_VERB_SET_AMP_GAIN_MUTE, (0x7080 | (0x08 << 8))},
 
 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
 
@@ -10788,7 +11209,7 @@
 #define alc262_loopbacks	alc880_loopbacks
 #endif
 
-/* pcm configuration: identiacal with ALC880 */
+/* pcm configuration: identical with ALC880 */
 #define alc262_pcm_analog_playback	alc880_pcm_analog_playback
 #define alc262_pcm_analog_capture	alc880_pcm_analog_capture
 #define alc262_pcm_digital_playback	alc880_pcm_digital_playback
@@ -10843,6 +11264,8 @@
 	if (err < 0)
 		return err;
 
+	alc_ssid_check(codec, 0x15, 0x14, 0x1b);
+
 	return 1;
 }
 
@@ -10915,6 +11338,7 @@
 	SND_PCI_QUIRK(0x104d, 0x8203, "Sony UX-90", ALC262_HIPPO),
 	SND_PCI_QUIRK(0x104d, 0x820f, "Sony ASSAMD", ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x104d, 0x9016, "Sony VAIO", ALC262_AUTO), /* dig-only */
+	SND_PCI_QUIRK(0x104d, 0x9025, "Sony VAIO Z21MN", ALC262_TOSHIBA_S06),
 	SND_PCI_QUIRK_MASK(0x104d, 0xff00, 0x9000, "Sony VAIO",
 			   ALC262_SONY_ASSAMD),
 	SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba dynabook SS RX1",
@@ -10945,7 +11369,7 @@
 		.input_mux = &alc262_capture_source,
 	},
 	[ALC262_HIPPO] = {
-		.mixers = { alc262_base_mixer },
+		.mixers = { alc262_hippo_mixer },
 		.init_verbs = { alc262_init_verbs, alc262_hippo_unsol_verbs},
 		.num_dacs = ARRAY_SIZE(alc262_dac_nids),
 		.dac_nids = alc262_dac_nids,
@@ -10955,7 +11379,7 @@
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
-		.init_hook = alc262_hippo_automute,
+		.init_hook = alc262_hippo_init_hook,
 	},
 	[ALC262_HIPPO_1] = {
 		.mixers = { alc262_hippo1_mixer },
@@ -10967,8 +11391,8 @@
 		.num_channel_mode = ARRAY_SIZE(alc262_modes),
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
-		.unsol_event = alc262_hippo1_unsol_event,
-		.init_hook = alc262_hippo1_automute,
+		.unsol_event = alc262_hippo_unsol_event,
+		.init_hook = alc262_hippo1_init_hook,
 	},
 	[ALC262_FUJITSU] = {
 		.mixers = { alc262_fujitsu_mixer },
@@ -11030,7 +11454,7 @@
 		.num_channel_mode = ARRAY_SIZE(alc262_modes),
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
-		.unsol_event = alc262_hp_t5735_unsol_event,
+		.unsol_event = alc_automute_amp_unsol_event,
 		.init_hook = alc262_hp_t5735_init_hook,
 	},
 	[ALC262_HP_RP5700] = {
@@ -11062,7 +11486,7 @@
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
-		.init_hook = alc262_hippo_automute,
+		.init_hook = alc262_hippo_init_hook,
 	},
 	[ALC262_BENQ_T31] = {
 		.mixers = { alc262_benq_t31_mixer },
@@ -11074,7 +11498,7 @@
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
-		.init_hook = alc262_hippo_automute,
+		.init_hook = alc262_hippo_init_hook,
 	},
 	[ALC262_ULTRA] = {
 		.mixers = { alc262_ultra_mixer },
@@ -11122,6 +11546,7 @@
 		.capsrc_nids = alc262_dmic_capsrc_nids,
 		.dac_nids = alc262_dac_nids,
 		.adc_nids = alc262_dmic_adc_nids, /* ADC0 */
+		.num_adc_nids = 1, /* single ADC */
 		.dig_out_nid = ALC262_DIGOUT_NID,
 		.num_channel_mode = ARRAY_SIZE(alc262_modes),
 		.channel_mode = alc262_modes,
@@ -11139,7 +11564,7 @@
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
 		.unsol_event = alc262_hippo_unsol_event,
-		.init_hook = alc262_hippo_automute,
+		.init_hook = alc262_hippo_init_hook,
 	},
 	[ALC262_TYAN] = {
 		.mixers = { alc262_tyan_mixer },
@@ -11151,8 +11576,8 @@
 		.num_channel_mode = ARRAY_SIZE(alc262_modes),
 		.channel_mode = alc262_modes,
 		.input_mux = &alc262_capture_source,
-		.unsol_event = alc262_tyan_unsol_event,
-		.init_hook = alc262_tyan_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc262_tyan_init_hook,
 	},
 };
 
@@ -11187,8 +11612,8 @@
 						  alc262_cfg_tbl);
 
 	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC262, "
-		       "trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: Unknown model for %s, "
+		       "trying auto-probe from BIOS...\n", codec->chip_name);
 		board_config = ALC262_AUTO;
 	}
 
@@ -11217,29 +11642,42 @@
 	if (board_config != ALC262_AUTO)
 		setup_preset(spec, &alc262_presets[board_config]);
 
-	spec->stream_name_analog = "ALC262 Analog";
 	spec->stream_analog_playback = &alc262_pcm_analog_playback;
 	spec->stream_analog_capture = &alc262_pcm_analog_capture;
 
-	spec->stream_name_digital = "ALC262 Digital";
 	spec->stream_digital_playback = &alc262_pcm_digital_playback;
 	spec->stream_digital_capture = &alc262_pcm_digital_capture;
 
-	spec->capture_style = CAPT_MIX;
 	if (!spec->adc_nids && spec->input_mux) {
-		/* check whether NID 0x07 is valid */
-		unsigned int wcap = get_wcaps(codec, 0x07);
-
-		/* get type */
-		wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
-		if (wcap != AC_WID_AUD_IN) {
-			spec->adc_nids = alc262_adc_nids_alt;
-			spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids_alt);
-			spec->capsrc_nids = alc262_capsrc_nids_alt;
+		int i;
+		/* check whether the digital-mic has to be supported */
+		for (i = 0; i < spec->input_mux->num_items; i++) {
+			if (spec->input_mux->items[i].index >= 9)
+				break;
+		}
+		if (i < spec->input_mux->num_items) {
+			/* use only ADC0 */
+			spec->adc_nids = alc262_dmic_adc_nids;
+			spec->num_adc_nids = 1;
+			spec->capsrc_nids = alc262_dmic_capsrc_nids;
 		} else {
-			spec->adc_nids = alc262_adc_nids;
-			spec->num_adc_nids = ARRAY_SIZE(alc262_adc_nids);
-			spec->capsrc_nids = alc262_capsrc_nids;
+			/* all analog inputs */
+			/* check whether NID 0x07 is valid */
+			unsigned int wcap = get_wcaps(codec, 0x07);
+
+			/* get type */
+			wcap = (wcap & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+			if (wcap != AC_WID_AUD_IN) {
+				spec->adc_nids = alc262_adc_nids_alt;
+				spec->num_adc_nids =
+					ARRAY_SIZE(alc262_adc_nids_alt);
+				spec->capsrc_nids = alc262_capsrc_nids_alt;
+			} else {
+				spec->adc_nids = alc262_adc_nids;
+				spec->num_adc_nids =
+					ARRAY_SIZE(alc262_adc_nids);
+				spec->capsrc_nids = alc262_capsrc_nids;
+			}
 		}
 	}
 	if (!spec->cap_mixer && !spec->no_analog)
@@ -11296,6 +11734,17 @@
 	{ }
 };
 
+static struct snd_kcontrol_new alc268_toshiba_mixer[] = {
+	/* output mixer control */
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x3, 0x0, HDA_OUTPUT),
+	ALC262_HIPPO_MASTER_SWITCH,
+	HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Front Mic Boost", 0x19, 0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Line In Boost", 0x1a, 0, HDA_INPUT),
+	{ }
+};
+
 /* bind Beep switches of both NID 0x0f and 0x10 */
 static struct hda_bind_ctls alc268_bind_beep_sw = {
 	.ops = &snd_hda_bind_sw,
@@ -11319,8 +11768,6 @@
 };
 
 /* Toshiba specific */
-#define alc268_toshiba_automute	alc262_hippo_automute
-
 static struct hda_verb alc268_toshiba_verbs[] = {
 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT | AC_USRSP_EN},
 	{ } /* end */
@@ -11456,13 +11903,8 @@
 };
 
 /* unsolicited event for HP jack sensing */
-static void alc268_toshiba_unsol_event(struct hda_codec *codec,
-				       unsigned int res)
-{
-	if ((res >> 26) != ALC880_HP_EVENT)
-		return;
-	alc268_toshiba_automute(codec);
-}
+#define alc268_toshiba_unsol_event	alc262_hippo_unsol_event
+#define alc268_toshiba_init_hook	alc262_hippo_init_hook
 
 static void alc268_acer_unsol_event(struct hda_codec *codec,
 				       unsigned int res)
@@ -11537,30 +11979,15 @@
 };
 
 /* mute/unmute internal speaker according to the hp jack and mute state */
-static void alc268_dell_automute(struct hda_codec *codec)
+static void alc268_dell_init_hook(struct hda_codec *codec)
 {
-	unsigned int present;
-	unsigned int mute;
+	struct alc_spec *spec = codec->spec;
 
-	present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0);
-	if (present & 0x80000000)
-		mute = HDA_AMP_MUTE;
-	else
-		mute = snd_hda_codec_amp_read(codec, 0x15, 0, HDA_OUTPUT, 0);
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, mute);
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc_automute_pin(codec);
 }
 
-static void alc268_dell_unsol_event(struct hda_codec *codec,
-				    unsigned int res)
-{
-	if ((res >> 26) != ALC880_HP_EVENT)
-		return;
-	alc268_dell_automute(codec);
-}
-
-#define alc268_dell_init_hook	alc268_dell_automute
-
 static struct snd_kcontrol_new alc267_quanta_il1_mixer[] = {
 	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x2, 0x0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
@@ -11579,16 +12006,6 @@
 	{ }
 };
 
-static void alc267_quanta_il1_hp_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-
-	present = snd_hda_codec_read(codec, 0x15, 0, AC_VERB_GET_PIN_SENSE, 0)
-		& AC_PINSENSE_PRESENCE;
-	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-			    present ? 0 : PIN_OUT);
-}
-
 static void alc267_quanta_il1_mic_automute(struct hda_codec *codec)
 {
 	unsigned int present;
@@ -11600,9 +12017,13 @@
 			    present ? 0x00 : 0x01);
 }
 
-static void alc267_quanta_il1_automute(struct hda_codec *codec)
+static void alc267_quanta_il1_init_hook(struct hda_codec *codec)
 {
-	alc267_quanta_il1_hp_automute(codec);
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc_automute_pin(codec);
 	alc267_quanta_il1_mic_automute(codec);
 }
 
@@ -11610,12 +12031,12 @@
 					   unsigned int res)
 {
 	switch (res >> 26) {
-	case ALC880_HP_EVENT:
-		alc267_quanta_il1_hp_automute(codec);
-		break;
 	case ALC880_MIC_EVENT:
 		alc267_quanta_il1_mic_automute(codec);
 		break;
+	default:
+		alc_sku_unsol_event(codec, res);
+		break;
 	}
 }
 
@@ -11960,7 +12381,7 @@
 			    AC_VERB_SET_AMP_GAIN_MUTE, dac_vol2);
 }
 
-/* pcm configuration: identiacal with ALC880 */
+/* pcm configuration: identical with ALC880 */
 #define alc268_pcm_analog_playback	alc880_pcm_analog_playback
 #define alc268_pcm_analog_capture	alc880_pcm_analog_capture
 #define alc268_pcm_analog_alt_capture	alc880_pcm_analog_alt_capture
@@ -12063,16 +12484,16 @@
 						ALC268_ACER_ASPIRE_ONE),
 	SND_PCI_QUIRK(0x1028, 0x0253, "Dell OEM", ALC268_DELL),
 	SND_PCI_QUIRK(0x1028, 0x02b0, "Dell Inspiron Mini9", ALC268_DELL),
-	SND_PCI_QUIRK(0x103c, 0x30cc, "TOSHIBA", ALC268_TOSHIBA),
-	SND_PCI_QUIRK(0x103c, 0x30f1, "HP TX25xx series", ALC268_TOSHIBA),
+	SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP TX25xx series",
+			   ALC268_TOSHIBA),
 	SND_PCI_QUIRK(0x1043, 0x1205, "ASUS W7J", ALC268_3ST),
-	SND_PCI_QUIRK(0x1179, 0xff10, "TOSHIBA A205", ALC268_TOSHIBA),
-	SND_PCI_QUIRK(0x1179, 0xff50, "TOSHIBA A305", ALC268_TOSHIBA),
-	SND_PCI_QUIRK(0x1179, 0xff64, "TOSHIBA L305", ALC268_TOSHIBA),
+	SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO),
+	SND_PCI_QUIRK_MASK(0x1179, 0xff00, 0xff00, "TOSHIBA A/Lx05",
+			   ALC268_TOSHIBA),
 	SND_PCI_QUIRK(0x14c0, 0x0025, "COMPAL IFL90/JFL-92", ALC268_TOSHIBA),
 	SND_PCI_QUIRK(0x152d, 0x0763, "Diverse (CPR2000)", ALC268_ACER),
 	SND_PCI_QUIRK(0x152d, 0x0771, "Quanta IL1", ALC267_QUANTA_IL1),
-	SND_PCI_QUIRK(0x1170, 0x0040, "ZEPTO", ALC268_ZEPTO),
+	SND_PCI_QUIRK(0x1854, 0x1775, "LG R510", ALC268_DELL),
 	{}
 };
 
@@ -12090,7 +12511,7 @@
 		.channel_mode = alc268_modes,
 		.input_mux = &alc268_capture_source,
 		.unsol_event = alc267_quanta_il1_unsol_event,
-		.init_hook = alc267_quanta_il1_automute,
+		.init_hook = alc267_quanta_il1_init_hook,
 	},
 	[ALC268_3ST] = {
 		.mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
@@ -12108,7 +12529,7 @@
 		.input_mux = &alc268_capture_source,
 	},
 	[ALC268_TOSHIBA] = {
-		.mixers = { alc268_base_mixer, alc268_capture_alt_mixer,
+		.mixers = { alc268_toshiba_mixer, alc268_capture_alt_mixer,
 			    alc268_beep_mixer },
 		.init_verbs = { alc268_base_init_verbs, alc268_eapd_verbs,
 				alc268_toshiba_verbs },
@@ -12122,7 +12543,7 @@
 		.channel_mode = alc268_modes,
 		.input_mux = &alc268_capture_source,
 		.unsol_event = alc268_toshiba_unsol_event,
-		.init_hook = alc268_toshiba_automute,
+		.init_hook = alc268_toshiba_init_hook,
 	},
 	[ALC268_ACER] = {
 		.mixers = { alc268_acer_mixer, alc268_capture_alt_mixer,
@@ -12185,7 +12606,7 @@
 		.hp_nid = 0x02,
 		.num_channel_mode = ARRAY_SIZE(alc268_modes),
 		.channel_mode = alc268_modes,
-		.unsol_event = alc268_dell_unsol_event,
+		.unsol_event = alc_sku_unsol_event,
 		.init_hook = alc268_dell_init_hook,
 		.input_mux = &alc268_capture_source,
 	},
@@ -12205,7 +12626,7 @@
 		.channel_mode = alc268_modes,
 		.input_mux = &alc268_capture_source,
 		.unsol_event = alc268_toshiba_unsol_event,
-		.init_hook = alc268_toshiba_automute
+		.init_hook = alc268_toshiba_init_hook
 	},
 #ifdef CONFIG_SND_DEBUG
 	[ALC268_TEST] = {
@@ -12243,8 +12664,8 @@
 						  alc268_cfg_tbl);
 
 	if (board_config < 0 || board_config >= ALC268_MODEL_LAST) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC268, "
-		       "trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: Unknown model for %s, "
+		       "trying auto-probe from BIOS...\n", codec->chip_name);
 		board_config = ALC268_AUTO;
 	}
 
@@ -12265,14 +12686,6 @@
 	if (board_config != ALC268_AUTO)
 		setup_preset(spec, &alc268_presets[board_config]);
 
-	if (codec->vendor_id == 0x10ec0267) {
-		spec->stream_name_analog = "ALC267 Analog";
-		spec->stream_name_digital = "ALC267 Digital";
-	} else {
-		spec->stream_name_analog = "ALC268 Analog";
-		spec->stream_name_digital = "ALC268 Digital";
-	}
-
 	spec->stream_analog_playback = &alc268_pcm_analog_playback;
 	spec->stream_analog_capture = &alc268_pcm_analog_capture;
 	spec->stream_analog_alt_capture = &alc268_pcm_analog_alt_capture;
@@ -12854,32 +13267,14 @@
 	return 0;
 }
 
-static int alc269_auto_create_analog_input_ctls(struct alc_spec *spec,
-						const struct auto_pin_cfg *cfg)
-{
-	int err;
-
-	err = alc880_auto_create_analog_input_ctls(spec, cfg);
-	if (err < 0)
-		return err;
-	/* digital-mic input pin is excluded in alc880_auto_create..()
-	 * because it's under 0x18
-	 */
-	if (cfg->input_pins[AUTO_PIN_MIC] == 0x12 ||
-	    cfg->input_pins[AUTO_PIN_FRONT_MIC] == 0x12) {
-		struct hda_input_mux *imux = &spec->private_imux[0];
-		imux->items[imux->num_items].label = "Int Mic";
-		imux->items[imux->num_items].index = 0x05;
-		imux->num_items++;
-	}
-	return 0;
-}
+#define alc269_auto_create_analog_input_ctls \
+	alc262_auto_create_analog_input_ctls
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc269_loopbacks	alc880_loopbacks
 #endif
 
-/* pcm configuration: identiacal with ALC880 */
+/* pcm configuration: identical with ALC880 */
 #define alc269_pcm_analog_playback	alc880_pcm_analog_playback
 #define alc269_pcm_analog_capture	alc880_pcm_analog_capture
 #define alc269_pcm_digital_playback	alc880_pcm_digital_playback
@@ -13099,8 +13494,8 @@
 						  alc269_cfg_tbl);
 
 	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC269, "
-		       "trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: Unknown model for %s, "
+		       "trying auto-probe from BIOS...\n", codec->chip_name);
 		board_config = ALC269_AUTO;
 	}
 
@@ -13127,7 +13522,6 @@
 	if (board_config != ALC269_AUTO)
 		setup_preset(spec, &alc269_presets[board_config]);
 
-	spec->stream_name_analog = "ALC269 Analog";
 	if (codec->subsystem_id == 0x17aa3bf8) {
 		/* Due to a hardware problem on Lenovo Ideadpad, we need to
 		 * fix the sample rate of analog I/O to 44.1kHz
@@ -13138,7 +13532,6 @@
 		spec->stream_analog_playback = &alc269_pcm_analog_playback;
 		spec->stream_analog_capture = &alc269_pcm_analog_capture;
 	}
-	spec->stream_name_digital = "ALC269 Digital";
 	spec->stream_digital_playback = &alc269_pcm_digital_playback;
 	spec->stream_digital_capture = &alc269_pcm_digital_capture;
 
@@ -13743,7 +14136,7 @@
 		alc861_toshiba_automute(codec);
 }
 
-/* pcm configuration: identiacal with ALC880 */
+/* pcm configuration: identical with ALC880 */
 #define alc861_pcm_analog_playback	alc880_pcm_analog_playback
 #define alc861_pcm_analog_capture	alc880_pcm_analog_capture
 #define alc861_pcm_digital_playback	alc880_pcm_digital_playback
@@ -13927,7 +14320,6 @@
 	struct alc_spec *spec = codec->spec;
 	int i;
 
-	alc_subsystem_id(codec, 0x0e, 0x0f, 0x0b);
 	for (i = 0; i < spec->autocfg.line_outs; i++) {
 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
 		int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -14010,6 +14402,8 @@
 	spec->num_adc_nids = ARRAY_SIZE(alc861_adc_nids);
 	set_capture_mixer(spec);
 
+	alc_ssid_check(codec, 0x0e, 0x0f, 0x0b);
+
 	return 1;
 }
 
@@ -14199,8 +14593,8 @@
 						  alc861_cfg_tbl);
 
 	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC861, "
-		       "trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: Unknown model for %s, "
+		       "trying auto-probe from BIOS...\n", codec->chip_name);
 		board_config = ALC861_AUTO;
 	}
 
@@ -14227,11 +14621,9 @@
 	if (board_config != ALC861_AUTO)
 		setup_preset(spec, &alc861_presets[board_config]);
 
-	spec->stream_name_analog = "ALC861 Analog";
 	spec->stream_analog_playback = &alc861_pcm_analog_playback;
 	spec->stream_analog_capture = &alc861_pcm_analog_capture;
 
-	spec->stream_name_digital = "ALC861 Digital";
 	spec->stream_digital_playback = &alc861_pcm_digital_playback;
 	spec->stream_digital_capture = &alc861_pcm_digital_capture;
 
@@ -14267,7 +14659,7 @@
 
 /* dac_nids for ALC660vd are in a different order - according to
  * Realtek's driver.
- * This should probably tesult in a different mixer for 6stack models
+ * This should probably result in a different mixer for 6stack models
  * of ALC660vd codecs, but for now there is only 3stack mixer
  * - and it is the same as in 861vd.
  * adc_nids in ALC660vd are (is) the same as in 861vd
@@ -14618,19 +15010,6 @@
 	{}
 };
 
-/* toggle speaker-output according to the hp-jack state */
-static void alc861vd_lenovo_hp_automute(struct hda_codec *codec)
-{
-	unsigned int present;
-	unsigned char bits;
-
-	present = snd_hda_codec_read(codec, 0x1b, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	bits = present ? HDA_AMP_MUTE : 0;
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, bits);
-}
-
 static void alc861vd_lenovo_mic_automute(struct hda_codec *codec)
 {
 	unsigned int present;
@@ -14643,9 +15022,13 @@
 				 HDA_AMP_MUTE, bits);
 }
 
-static void alc861vd_lenovo_automute(struct hda_codec *codec)
+static void alc861vd_lenovo_init_hook(struct hda_codec *codec)
 {
-	alc861vd_lenovo_hp_automute(codec);
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x1b;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc_automute_amp(codec);
 	alc861vd_lenovo_mic_automute(codec);
 }
 
@@ -14653,12 +15036,12 @@
 					unsigned int res)
 {
 	switch (res >> 26) {
-	case ALC880_HP_EVENT:
-		alc861vd_lenovo_hp_automute(codec);
-		break;
 	case ALC880_MIC_EVENT:
 		alc861vd_lenovo_mic_automute(codec);
 		break;
+	default:
+		alc_automute_amp_unsol_event(codec, res);
+		break;
 	}
 }
 
@@ -14708,27 +15091,20 @@
 };
 
 /* toggle speaker-output according to the hp-jack state */
-static void alc861vd_dallas_automute(struct hda_codec *codec)
+static void alc861vd_dallas_init_hook(struct hda_codec *codec)
 {
-	unsigned int present;
+	struct alc_spec *spec = codec->spec;
 
-	present = snd_hda_codec_read(codec, 0x15, 0,
-				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
-	snd_hda_codec_amp_stereo(codec, 0x14, HDA_OUTPUT, 0,
-				 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-static void alc861vd_dallas_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc861vd_dallas_automute(codec);
+	spec->autocfg.hp_pins[0] = 0x15;
+	spec->autocfg.speaker_pins[0] = 0x14;
+	alc_automute_amp(codec);
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc861vd_loopbacks	alc880_loopbacks
 #endif
 
-/* pcm configuration: identiacal with ALC880 */
+/* pcm configuration: identical with ALC880 */
 #define alc861vd_pcm_analog_playback	alc880_pcm_analog_playback
 #define alc861vd_pcm_analog_capture	alc880_pcm_analog_capture
 #define alc861vd_pcm_digital_playback	alc880_pcm_digital_playback
@@ -14835,7 +15211,7 @@
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.input_mux = &alc861vd_capture_source,
 		.unsol_event = alc861vd_lenovo_unsol_event,
-		.init_hook = alc861vd_lenovo_automute,
+		.init_hook = alc861vd_lenovo_init_hook,
 	},
 	[ALC861VD_DALLAS] = {
 		.mixers = { alc861vd_dallas_mixer },
@@ -14845,8 +15221,8 @@
 		.num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.input_mux = &alc861vd_dallas_capture_source,
-		.unsol_event = alc861vd_dallas_unsol_event,
-		.init_hook = alc861vd_dallas_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc861vd_dallas_init_hook,
 	},
 	[ALC861VD_HP] = {
 		.mixers = { alc861vd_hp_mixer },
@@ -14857,8 +15233,8 @@
 		.num_channel_mode = ARRAY_SIZE(alc861vd_3stack_2ch_modes),
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.input_mux = &alc861vd_hp_capture_source,
-		.unsol_event = alc861vd_dallas_unsol_event,
-		.init_hook = alc861vd_dallas_automute,
+		.unsol_event = alc_automute_amp_unsol_event,
+		.init_hook = alc861vd_dallas_init_hook,
 	},
 	[ALC660VD_ASUS_V1S] = {
 		.mixers = { alc861vd_lenovo_mixer },
@@ -14873,7 +15249,7 @@
 		.channel_mode = alc861vd_3stack_2ch_modes,
 		.input_mux = &alc861vd_capture_source,
 		.unsol_event = alc861vd_lenovo_unsol_event,
-		.init_hook = alc861vd_lenovo_automute,
+		.init_hook = alc861vd_lenovo_init_hook,
 	},
 };
 
@@ -14891,7 +15267,6 @@
 	struct alc_spec *spec = codec->spec;
 	int i;
 
-	alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
 	for (i = 0; i <= HDA_SIDE; i++) {
 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
 		int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -14908,7 +15283,7 @@
 	hda_nid_t pin;
 
 	pin = spec->autocfg.hp_pins[0];
-	if (pin) /* connect to front and  use dac 0 */
+	if (pin) /* connect to front and use dac 0 */
 		alc861vd_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
 	pin = spec->autocfg.speaker_pins[0];
 	if (pin)
@@ -15109,6 +15484,8 @@
 	if (err < 0)
 		return err;
 
+	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+
 	return 1;
 }
 
@@ -15140,8 +15517,8 @@
 						  alc861vd_cfg_tbl);
 
 	if (board_config < 0 || board_config >= ALC861VD_MODEL_LAST) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC660VD/"
-			"ALC861VD, trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: Unknown model for %s, "
+		       "trying auto-probe from BIOS...\n", codec->chip_name);
 		board_config = ALC861VD_AUTO;
 	}
 
@@ -15169,13 +15546,8 @@
 		setup_preset(spec, &alc861vd_presets[board_config]);
 
 	if (codec->vendor_id == 0x10ec0660) {
-		spec->stream_name_analog = "ALC660-VD Analog";
-		spec->stream_name_digital = "ALC660-VD Digital";
 		/* always turn on EAPD */
 		add_verb(spec, alc660vd_eapd_verbs);
-	} else {
-		spec->stream_name_analog = "ALC861VD Analog";
-		spec->stream_name_digital = "ALC861VD Digital";
 	}
 
 	spec->stream_analog_playback = &alc861vd_pcm_analog_playback;
@@ -15187,7 +15559,6 @@
 	spec->adc_nids = alc861vd_adc_nids;
 	spec->num_adc_nids = ARRAY_SIZE(alc861vd_adc_nids);
 	spec->capsrc_nids = alc861vd_capsrc_nids;
-	spec->capture_style = CAPT_MIX;
 
 	set_capture_mixer(spec);
 	set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT);
@@ -15289,6 +15660,38 @@
 	},
 };
 
+#if 1 /* set to 0 for testing other input sources below */
+static struct hda_input_mux alc272_nc10_capture_source = {
+	.num_items = 2,
+	.items = {
+		{ "Autoselect Mic", 0x0 },
+		{ "Internal Mic", 0x1 },
+	},
+};
+#else
+static struct hda_input_mux alc272_nc10_capture_source = {
+	.num_items = 16,
+	.items = {
+		{ "Autoselect Mic", 0x0 },
+		{ "Internal Mic", 0x1 },
+		{ "In-0x02", 0x2 },
+		{ "In-0x03", 0x3 },
+		{ "In-0x04", 0x4 },
+		{ "In-0x05", 0x5 },
+		{ "In-0x06", 0x6 },
+		{ "In-0x07", 0x7 },
+		{ "In-0x08", 0x8 },
+		{ "In-0x09", 0x9 },
+		{ "In-0x0a", 0x0a },
+		{ "In-0x0b", 0x0b },
+		{ "In-0x0c", 0x0c },
+		{ "In-0x0d", 0x0d },
+		{ "In-0x0e", 0x0e },
+		{ "In-0x0f", 0x0f },
+	},
+};
+#endif
+
 /*
  * 2ch mode
  */
@@ -15428,10 +15831,8 @@
 };
 
 static struct snd_kcontrol_new alc662_eeepc_p701_mixer[] = {
-	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
-
-	HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x02, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Line-Out Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+	ALC262_HIPPO_MASTER_SWITCH,
 
 	HDA_CODEC_VOLUME("e-Mic Boost", 0x18, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("e-Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
@@ -15444,15 +15845,11 @@
 };
 
 static struct snd_kcontrol_new alc662_eeepc_ep20_mixer[] = {
-	HDA_CODEC_VOLUME("Line-Out Playback Volume", 0x02, 0x0, HDA_OUTPUT),
-	HDA_CODEC_MUTE("Line-Out Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	ALC262_HIPPO_MASTER_SWITCH,
+	HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
-	HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT),
 	HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT),
 	HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT),
-	HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT),
-	HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT),
-	HDA_CODEC_MUTE("Speaker Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
 	HDA_BIND_MUTE("MuteCtrl Playback Switch", 0x0c, 2, HDA_INPUT),
 	HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT),
 	HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT),
@@ -15960,51 +16357,25 @@
 static void alc662_eeepc_unsol_event(struct hda_codec *codec,
 				     unsigned int res)
 {
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc262_hippo1_automute( codec );
-
 	if ((res >> 26) == ALC880_MIC_EVENT)
 		alc662_eeepc_mic_automute(codec);
+	else
+		alc262_hippo_unsol_event(codec, res);
 }
 
 static void alc662_eeepc_inithook(struct hda_codec *codec)
 {
-	alc262_hippo1_automute( codec );
+	alc262_hippo1_init_hook(codec);
 	alc662_eeepc_mic_automute(codec);
 }
 
-static void alc662_eeepc_ep20_automute(struct hda_codec *codec)
-{
-	unsigned int mute;
-	unsigned int present;
-
-	snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0);
-	present = snd_hda_codec_read(codec, 0x14, 0,
-				     AC_VERB_GET_PIN_SENSE, 0);
-	present = (present & 0x80000000) != 0;
-	if (present) {
-		/* mute internal speaker */
-		snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-					HDA_AMP_MUTE, HDA_AMP_MUTE);
-	} else {
-		/* unmute internal speaker if necessary */
-		mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0);
-		snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-					HDA_AMP_MUTE, mute);
-	}
-}
-
-/* unsolicited event for HP jack sensing */
-static void alc662_eeepc_ep20_unsol_event(struct hda_codec *codec,
-					  unsigned int res)
-{
-	if ((res >> 26) == ALC880_HP_EVENT)
-		alc662_eeepc_ep20_automute(codec);
-}
-
 static void alc662_eeepc_ep20_inithook(struct hda_codec *codec)
 {
-	alc662_eeepc_ep20_automute(codec);
+	struct alc_spec *spec = codec->spec;
+
+	spec->autocfg.hp_pins[0] = 0x14;
+	spec->autocfg.speaker_pins[0] = 0x1b;
+	alc262_hippo_master_update(codec);
 }
 
 static void alc663_m51va_speaker_automute(struct hda_codec *codec)
@@ -16338,35 +16709,9 @@
 	alc662_eeepc_mic_automute(codec);
 }
 
-/* bind hp and internal speaker mute (with plug check) */
-static int alc662_ecs_master_sw_put(struct snd_kcontrol *kcontrol,
-				     struct snd_ctl_elem_value *ucontrol)
-{
-	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-	long *valp = ucontrol->value.integer.value;
-	int change;
-
-	change = snd_hda_codec_amp_update(codec, 0x1b, 0, HDA_OUTPUT, 0,
-					  HDA_AMP_MUTE,
-					  valp[0] ? 0 : HDA_AMP_MUTE);
-	change |= snd_hda_codec_amp_update(codec, 0x1b, 1, HDA_OUTPUT, 0,
-					   HDA_AMP_MUTE,
-					   valp[1] ? 0 : HDA_AMP_MUTE);
-	if (change)
-		alc262_hippo1_automute(codec);
-	return change;
-}
-
 static struct snd_kcontrol_new alc662_ecs_mixer[] = {
 	HDA_CODEC_VOLUME("Master Playback Volume", 0x02, 0x0, HDA_OUTPUT),
-	{
-		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-		.name = "Master Playback Switch",
-		.info = snd_hda_mixer_amp_switch_info,
-		.get = snd_hda_mixer_amp_switch_get,
-		.put = alc662_ecs_master_sw_put,
-		.private_value = HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
-	},
+	ALC262_HIPPO_MASTER_SWITCH,
 
 	HDA_CODEC_VOLUME("e-Mic/LineIn Boost", 0x18, 0, HDA_INPUT),
 	HDA_CODEC_VOLUME("e-Mic/LineIn Playback Volume", 0x0b, 0x0, HDA_INPUT),
@@ -16378,12 +16723,29 @@
 	{ } /* end */
 };
 
+static struct snd_kcontrol_new alc272_nc10_mixer[] = {
+	/* Master Playback automatically created from Speaker and Headphone */
+	HDA_CODEC_VOLUME("Speaker Playback Volume", 0x02, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Speaker Playback Switch", 0x14, 0x0, HDA_OUTPUT),
+	HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
+	HDA_CODEC_MUTE("Headphone Playback Switch", 0x21, 0x0, HDA_OUTPUT),
+
+	HDA_CODEC_VOLUME("Ext Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_MUTE("Ext Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
+	HDA_CODEC_VOLUME("Ext Mic Boost", 0x18, 0, HDA_INPUT),
+
+	HDA_CODEC_VOLUME("Int Mic Playback Volume", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_MUTE("Int Mic Playback Switch", 0x0b, 0x1, HDA_INPUT),
+	HDA_CODEC_VOLUME("Int Mic Boost", 0x19, 0, HDA_INPUT),
+	{ } /* end */
+};
+
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 #define alc662_loopbacks	alc880_loopbacks
 #endif
 
 
-/* pcm configuration: identiacal with ALC880 */
+/* pcm configuration: identical with ALC880 */
 #define alc662_pcm_analog_playback	alc880_pcm_analog_playback
 #define alc662_pcm_analog_capture	alc880_pcm_analog_capture
 #define alc662_pcm_digital_playback	alc880_pcm_digital_playback
@@ -16411,6 +16773,9 @@
 	[ALC663_ASUS_MODE4] = "asus-mode4",
 	[ALC663_ASUS_MODE5] = "asus-mode5",
 	[ALC663_ASUS_MODE6] = "asus-mode6",
+	[ALC272_DELL]		= "dell",
+	[ALC272_DELL_ZM1]	= "dell-zm1",
+	[ALC272_SAMSUNG_NC10]	= "samsung-nc10",
 	[ALC662_AUTO]		= "auto",
 };
 
@@ -16468,6 +16833,7 @@
 	SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_ECS),
 	SND_PCI_QUIRK(0x105b, 0x0d47, "Foxconn 45CMX/45GMX/45CMX-K",
 		      ALC662_3ST_6ch_DIG),
+	SND_PCI_QUIRK(0x144d, 0xca00, "Samsung NC10", ALC272_SAMSUNG_NC10),
 	SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte 945GCM-S2L",
 		      ALC662_3ST_6ch_DIG),
 	SND_PCI_QUIRK(0x1565, 0x820f, "Biostar TA780G M2+", ALC662_3ST_6ch_DIG),
@@ -16558,7 +16924,7 @@
 		.num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes),
 		.channel_mode = alc662_3ST_6ch_modes,
 		.input_mux = &alc662_lenovo_101e_capture_source,
-		.unsol_event = alc662_eeepc_ep20_unsol_event,
+		.unsol_event = alc662_eeepc_unsol_event,
 		.init_hook = alc662_eeepc_ep20_inithook,
 	},
 	[ALC662_ECS] = {
@@ -16739,6 +17105,18 @@
 		.unsol_event = alc663_m51va_unsol_event,
 		.init_hook = alc663_m51va_inithook,
 	},
+	[ALC272_SAMSUNG_NC10] = {
+		.mixers = { alc272_nc10_mixer },
+		.init_verbs = { alc662_init_verbs,
+				alc663_21jd_amic_init_verbs },
+		.num_dacs = ARRAY_SIZE(alc272_dac_nids),
+		.dac_nids = alc272_dac_nids,
+		.num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes),
+		.channel_mode = alc662_3ST_2ch_modes,
+		.input_mux = &alc272_nc10_capture_source,
+		.unsol_event = alc663_mode4_unsol_event,
+		.init_hook = alc663_mode4_inithook,
+	},
 };
 
 
@@ -16933,7 +17311,6 @@
 	struct alc_spec *spec = codec->spec;
 	int i;
 
-	alc_subsystem_id(codec, 0x15, 0x1b, 0x14);
 	for (i = 0; i <= HDA_SIDE; i++) {
 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
 		int pin_type = get_pin_type(spec->autocfg.line_out_type);
@@ -17030,6 +17407,8 @@
 	if (err < 0)
 		return err;
 
+	alc_ssid_check(codec, 0x15, 0x1b, 0x14);
+
 	return 1;
 }
 
@@ -17062,8 +17441,8 @@
 						  alc662_models,
 			  	                  alc662_cfg_tbl);
 	if (board_config < 0) {
-		printk(KERN_INFO "hda_codec: Unknown model for ALC662, "
-		       "trying auto-probe from BIOS...\n");
+		printk(KERN_INFO "hda_codec: Unknown model for %s, "
+		       "trying auto-probe from BIOS...\n", codec->chip_name);
 		board_config = ALC662_AUTO;
 	}
 
@@ -17090,17 +17469,6 @@
 	if (board_config != ALC662_AUTO)
 		setup_preset(spec, &alc662_presets[board_config]);
 
-	if (codec->vendor_id == 0x10ec0663) {
-		spec->stream_name_analog = "ALC663 Analog";
-		spec->stream_name_digital = "ALC663 Digital";
-	} else if (codec->vendor_id == 0x10ec0272) {
-		spec->stream_name_analog = "ALC272 Analog";
-		spec->stream_name_digital = "ALC272 Digital";
-	} else {
-		spec->stream_name_analog = "ALC662 Analog";
-		spec->stream_name_digital = "ALC662 Digital";
-	}
-
 	spec->stream_analog_playback = &alc662_pcm_analog_playback;
 	spec->stream_analog_capture = &alc662_pcm_analog_capture;
 
@@ -17110,7 +17478,6 @@
 	spec->adc_nids = alc662_adc_nids;
 	spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids);
 	spec->capsrc_nids = alc662_capsrc_nids;
-	spec->capture_style = CAPT_MIX;
 
 	if (!spec->cap_mixer)
 		set_capture_mixer(spec);
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index d2fd8ef..14f3c3e 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -100,6 +100,7 @@
 	STAC_HP_M4,
 	STAC_HP_DV5,
 	STAC_HP_HDX,
+	STAC_HP_DV4_1222NR,
 	STAC_92HD71BXX_MODELS
 };
 
@@ -193,6 +194,7 @@
 	unsigned int gpio_dir;
 	unsigned int gpio_data;
 	unsigned int gpio_mute;
+	unsigned int gpio_led;
 
 	/* stream */
 	unsigned int stream_delay;
@@ -634,6 +636,40 @@
 	return 0;
 }
 
+static unsigned int stac92xx_vref_set(struct hda_codec *codec,
+					hda_nid_t nid, unsigned int new_vref)
+{
+	int error;
+	unsigned int pincfg;
+	pincfg = snd_hda_codec_read(codec, nid, 0,
+				AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+
+	pincfg &= 0xff;
+	pincfg &= ~(AC_PINCTL_VREFEN | AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
+	pincfg |= new_vref;
+
+	if (new_vref == AC_PINCTL_VREF_HIZ)
+		pincfg |= AC_PINCTL_OUT_EN;
+	else
+		pincfg |= AC_PINCTL_IN_EN;
+
+	error = snd_hda_codec_write_cache(codec, nid, 0,
+					AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
+	if (error < 0)
+		return error;
+	else
+		return 1;
+}
+
+static unsigned int stac92xx_vref_get(struct hda_codec *codec, hda_nid_t nid)
+{
+	unsigned int vref;
+	vref = snd_hda_codec_read(codec, nid, 0,
+				AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+	vref &= AC_PINCTL_VREFEN;
+	return vref;
+}
+
 static int stac92xx_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
@@ -995,6 +1031,17 @@
 		.private_value = verb_read | (verb_write << 16), \
 	}
 
+#define DC_BIAS(xname, idx, nid) \
+	{ \
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+		.name = xname, \
+		.index = idx, \
+		.info = stac92xx_dc_bias_info, \
+		.get = stac92xx_dc_bias_get, \
+		.put = stac92xx_dc_bias_put, \
+		.private_value = nid, \
+	}
+
 static struct snd_kcontrol_new stac9200_mixer[] = {
 	HDA_CODEC_VOLUME("Master Playback Volume", 0xb, 0, HDA_OUTPUT),
 	HDA_CODEC_MUTE("Master Playback Switch", 0xb, 0, HDA_OUTPUT),
@@ -1543,6 +1590,8 @@
 	/* SigmaTel reference board */
 	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0x2668,
 		      "DFI LanParty", STAC_REF),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_INTEL, 0xfb30,
+		      "SigmaTel",STAC_9205_REF),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
 		      "DFI LanParty", STAC_REF),
 	/* Dell laptops have BIOS problem */
@@ -1837,6 +1886,7 @@
 	[STAC_HP_M4]		= NULL,
 	[STAC_HP_DV5]		= NULL,
 	[STAC_HP_HDX]           = NULL,
+	[STAC_HP_DV4_1222NR]	= NULL,
 };
 
 static const char *stac92hd71bxx_models[STAC_92HD71BXX_MODELS] = {
@@ -1848,6 +1898,7 @@
 	[STAC_HP_M4] = "hp-m4",
 	[STAC_HP_DV5] = "hp-dv5",
 	[STAC_HP_HDX] = "hp-hdx",
+	[STAC_HP_DV4_1222NR] = "hp-dv4-1222nr",
 };
 
 static struct snd_pci_quirk stac92hd71bxx_cfg_tbl[] = {
@@ -1856,6 +1907,8 @@
 		      "DFI LanParty", STAC_92HD71BXX_REF),
 	SND_PCI_QUIRK(PCI_VENDOR_ID_DFI, 0x3101,
 		      "DFI LanParty", STAC_92HD71BXX_REF),
+	SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x30fb,
+		      "HP dv4-1222nr", STAC_HP_DV4_1222NR),
 	SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x3080,
 		      "HP", STAC_HP_DV5),
 	SND_PCI_QUIRK_MASK(PCI_VENDOR_ID_HP, 0xfff0, 0x30f0,
@@ -2545,7 +2598,8 @@
 	return 0;
 }
 
-static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
+					hda_nid_t nid)
 {
 	unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
 	pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
@@ -2599,15 +2653,108 @@
 	return 1;
 }
 
-#define stac92xx_io_switch_info		snd_ctl_boolean_mono_info
+static int stac92xx_dc_bias_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	int i;
+	static char *texts[] = {
+		"Mic In", "Line In", "Line Out"
+	};
+
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t nid = kcontrol->private_value;
+
+	if (nid == spec->mic_switch || nid == spec->line_switch)
+		i = 3;
+	else
+		i = 2;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->value.enumerated.items = i;
+	uinfo->count = 1;
+	if (uinfo->value.enumerated.item >= i)
+		uinfo->value.enumerated.item = i-1;
+	strcpy(uinfo->value.enumerated.name,
+		texts[uinfo->value.enumerated.item]);
+
+	return 0;
+}
+
+static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	hda_nid_t nid = kcontrol->private_value;
+	unsigned int vref = stac92xx_vref_get(codec, nid);
+
+	if (vref == stac92xx_get_default_vref(codec, nid))
+		ucontrol->value.enumerated.item[0] = 0;
+	else if (vref == AC_PINCTL_VREF_GRD)
+		ucontrol->value.enumerated.item[0] = 1;
+	else if (vref == AC_PINCTL_VREF_HIZ)
+		ucontrol->value.enumerated.item[0] = 2;
+
+	return 0;
+}
+
+static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int new_vref = 0;
+	int error;
+	hda_nid_t nid = kcontrol->private_value;
+
+	if (ucontrol->value.enumerated.item[0] == 0)
+		new_vref = stac92xx_get_default_vref(codec, nid);
+	else if (ucontrol->value.enumerated.item[0] == 1)
+		new_vref = AC_PINCTL_VREF_GRD;
+	else if (ucontrol->value.enumerated.item[0] == 2)
+		new_vref = AC_PINCTL_VREF_HIZ;
+	else
+		return 0;
+
+	if (new_vref != stac92xx_vref_get(codec, nid)) {
+		error = stac92xx_vref_set(codec, nid, new_vref);
+		return error;
+	}
+
+	return 0;
+}
+
+static int stac92xx_io_switch_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[2];
+	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct sigmatel_spec *spec = codec->spec;
+
+	if (kcontrol->private_value == spec->line_switch)
+		texts[0] = "Line In";
+	else
+		texts[0] = "Mic In";
+	texts[1] = "Line Out";
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->value.enumerated.items = 2;
+	uinfo->count = 1;
+
+	if (uinfo->value.enumerated.item >= 2)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name,
+		texts[uinfo->value.enumerated.item]);
+
+	return 0;
+}
 
 static int stac92xx_io_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
 {
 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct sigmatel_spec *spec = codec->spec;
-	int io_idx = kcontrol-> private_value & 0xff;
+	hda_nid_t nid = kcontrol->private_value;
+	int io_idx = (nid == spec->mic_switch) ? 1 : 0;
 
-	ucontrol->value.integer.value[0] = spec->io_switch[io_idx];
+	ucontrol->value.enumerated.item[0] = spec->io_switch[io_idx];
 	return 0;
 }
 
@@ -2615,9 +2762,9 @@
 {
         struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct sigmatel_spec *spec = codec->spec;
-        hda_nid_t nid = kcontrol->private_value >> 8;
-	int io_idx = kcontrol-> private_value & 0xff;
-	unsigned short val = !!ucontrol->value.integer.value[0];
+	hda_nid_t nid = kcontrol->private_value;
+	int io_idx = (nid == spec->mic_switch) ? 1 : 0;
+	unsigned short val = !!ucontrol->value.enumerated.item[0];
 
 	spec->io_switch[io_idx] = val;
 
@@ -2626,7 +2773,7 @@
 	else {
 		unsigned int pinctl = AC_PINCTL_IN_EN;
 		if (io_idx) /* set VREF for mic */
-			pinctl |= stac92xx_get_vref(codec, nid);
+			pinctl |= stac92xx_get_default_vref(codec, nid);
 		stac92xx_auto_set_pinctl(codec, nid, pinctl);
 	}
 
@@ -2707,7 +2854,8 @@
 	STAC_CTL_WIDGET_AMP_VOL,
 	STAC_CTL_WIDGET_HP_SWITCH,
 	STAC_CTL_WIDGET_IO_SWITCH,
-	STAC_CTL_WIDGET_CLFE_SWITCH
+	STAC_CTL_WIDGET_CLFE_SWITCH,
+	STAC_CTL_WIDGET_DC_BIAS
 };
 
 static struct snd_kcontrol_new stac92xx_control_templates[] = {
@@ -2719,6 +2867,7 @@
 	STAC_CODEC_HP_SWITCH(NULL),
 	STAC_CODEC_IO_SWITCH(NULL, 0),
 	STAC_CODEC_CLFE_SWITCH(NULL, 0),
+	DC_BIAS(NULL, 0, 0),
 };
 
 /* add dynamic controls */
@@ -2782,6 +2931,34 @@
 	.put = stac92xx_mux_enum_put,
 };
 
+static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
+						hda_nid_t nid, int idx)
+{
+	int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+	int control = 0;
+	struct sigmatel_spec *spec = codec->spec;
+	char name[22];
+
+	if (!((get_defcfg_connect(def_conf)) & AC_JACK_PORT_FIXED)) {
+		if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
+			&& nid == spec->line_switch)
+			control = STAC_CTL_WIDGET_IO_SWITCH;
+		else if (snd_hda_query_pin_caps(codec, nid)
+			& (AC_PINCAP_VREF_GRD << AC_PINCAP_VREF_SHIFT))
+			control = STAC_CTL_WIDGET_DC_BIAS;
+		else if (nid == spec->mic_switch)
+			control = STAC_CTL_WIDGET_IO_SWITCH;
+	}
+
+	if (control) {
+		strcpy(name, auto_pin_cfg_labels[idx]);
+		return stac92xx_add_control(codec->spec, control,
+					strcat(name, " Jack Mode"), nid);
+	}
+
+	return 0;
+}
+
 static int stac92xx_add_input_source(struct sigmatel_spec *spec)
 {
 	struct snd_kcontrol_new *knew;
@@ -3144,7 +3321,9 @@
 					       const struct auto_pin_cfg *cfg)
 {
 	struct sigmatel_spec *spec = codec->spec;
+	hda_nid_t nid;
 	int err;
+	int idx;
 
 	err = create_multi_out_ctls(codec, cfg->line_outs, cfg->line_out_pins,
 				    spec->multiout.dac_nids,
@@ -3161,20 +3340,13 @@
 			return err;
 	}
 
-	if (spec->line_switch) {
-		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
-					   "Line In as Output Switch",
-					   spec->line_switch << 8);
-		if (err < 0)
-			return err;
-	}
-
-	if (spec->mic_switch) {
-		err = stac92xx_add_control(spec, STAC_CTL_WIDGET_IO_SWITCH,
-					   "Mic as Output Switch",
-					   (spec->mic_switch << 8) | 1);
-		if (err < 0)
-			return err;
+	for (idx = AUTO_PIN_MIC; idx <= AUTO_PIN_FRONT_LINE; idx++) {
+		nid = cfg->input_pins[idx];
+		if (nid) {
+			err = stac92xx_add_jack_mode_control(codec, nid, idx);
+			if (err < 0)
+				return err;
+		}
 	}
 
 	return 0;
@@ -3639,6 +3811,8 @@
 		err = snd_hda_attach_beep_device(codec, nid);
 		if (err < 0)
 			return err;
+		/* IDT/STAC codecs have linear beep tone parameter */
+		codec->beep->linear_tone = 1;
 		/* if no beep switch is available, make its own one */
 		caps = query_amp_caps(codec, nid, HDA_OUTPUT);
 		if (codec->beep &&
@@ -3861,7 +4035,7 @@
 			   AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */
 }
 
-#ifdef CONFIG_SND_JACK
+#ifdef CONFIG_SND_HDA_INPUT_JACK
 static void stac92xx_free_jack_priv(struct snd_jack *jack)
 {
 	struct sigmatel_jack *jacks = jack->private_data;
@@ -3873,7 +4047,7 @@
 static int stac92xx_add_jack(struct hda_codec *codec,
 		hda_nid_t nid, int type)
 {
-#ifdef CONFIG_SND_JACK
+#ifdef CONFIG_SND_HDA_INPUT_JACK
 	struct sigmatel_spec *spec = codec->spec;
 	struct sigmatel_jack *jack;
 	int def_conf = snd_hda_codec_get_pincfg(codec, nid);
@@ -4082,7 +4256,7 @@
 			unsigned int pinctl, conf;
 			if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) {
 				/* for mic pins, force to initialize */
-				pinctl = stac92xx_get_vref(codec, nid);
+				pinctl = stac92xx_get_default_vref(codec, nid);
 				pinctl |= AC_PINCTL_IN_EN;
 				stac92xx_auto_set_pinctl(codec, nid, pinctl);
 			} else {
@@ -4162,7 +4336,7 @@
 
 static void stac92xx_free_jacks(struct hda_codec *codec)
 {
-#ifdef CONFIG_SND_JACK
+#ifdef CONFIG_SND_HDA_INPUT_JACK
 	/* free jack instances manually when clearing/reconfiguring */
 	struct sigmatel_spec *spec = codec->spec;
 	if (!codec->bus->shutdown && spec->jacks.list) {
@@ -4535,17 +4709,19 @@
 	return 0;
 }
 
-
 /*
- * using power check for controlling mute led of HP HDX notebooks
+ * using power check for controlling mute led of HP notebooks
  * check for mute state only on Speakers (nid = 0x10)
  *
  * For this feature CONFIG_SND_HDA_POWER_SAVE is needed, otherwise
  * the LED is NOT working properly !
+ *
+ * Changed name to reflect that it now works for any designated
+ * model, not just HP HDX.
  */
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-static int stac92xx_hp_hdx_check_power_status(struct hda_codec *codec,
+static int stac92xx_hp_check_power_status(struct hda_codec *codec,
 					      hda_nid_t nid)
 {
 	struct sigmatel_spec *spec = codec->spec;
@@ -4553,9 +4729,9 @@
 	if (nid == 0x10) {
 		if (snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) &
 		    HDA_AMP_MUTE)
-			spec->gpio_data &= ~0x08;  /* orange */
+			spec->gpio_data &= ~spec->gpio_led; /* orange */
 		else
-			spec->gpio_data |= 0x08;   /* white */
+			spec->gpio_data |= spec->gpio_led; /* white */
 
 		stac_gpio_set(codec, spec->gpio_mask,
 			      spec->gpio_dir,
@@ -5201,6 +5377,15 @@
 	if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
 		snd_hda_sequence_write_cache(codec, unmute_init);
 
+	/* Some HP machines seem to have unstable codec communications
+	 * especially with ATI fglrx driver.  For recovering from the
+	 * CORB/RIRB stall, allow the BUS reset and keep always sync
+	 */
+	if (spec->board_config == STAC_HP_DV5) {
+		codec->bus->sync_write = 1;
+		codec->bus->allow_bus_reset = 1;
+	}
+
 	spec->aloopback_ctl = stac92hd71bxx_loopback;
 	spec->aloopback_mask = 0x50;
 	spec->aloopback_shift = 0;
@@ -5234,6 +5419,15 @@
 		spec->num_smuxes = 0;
 		spec->num_dmuxes = 1;
 		break;
+	case STAC_HP_DV4_1222NR:
+		spec->num_dmics = 1;
+		/* I don't know if it needs 1 or 2 smuxes - will wait for
+		 * bug reports to fix if needed
+		 */
+		spec->num_smuxes = 1;
+		spec->num_dmuxes = 1;
+		spec->gpio_led = 0x01;
+		/* fallthrough */
 	case STAC_HP_DV5:
 		snd_hda_codec_set_pincfg(codec, 0x0d, 0x90170010);
 		stac92xx_auto_set_pinctl(codec, 0x0d, AC_PINCTL_OUT_EN);
@@ -5242,22 +5436,21 @@
 		spec->num_dmics = 1;
 		spec->num_dmuxes = 1;
 		spec->num_smuxes = 1;
-		/*
-		 * For controlling MUTE LED on HP HDX16/HDX18 notebooks,
-		 * the CONFIG_SND_HDA_POWER_SAVE is needed to be set.
-		 */
-#ifdef CONFIG_SND_HDA_POWER_SAVE
 		/* orange/white mute led on GPIO3, orange=0, white=1 */
-		spec->gpio_mask |= 0x08;
-		spec->gpio_dir  |= 0x08;
-		spec->gpio_data |= 0x08;  /* set to white */
+		spec->gpio_led = 0x08;
+		break;
+	}
 
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+	if (spec->gpio_led) {
+		spec->gpio_mask |= spec->gpio_led;
+		spec->gpio_dir |= spec->gpio_led;
+		spec->gpio_data |= spec->gpio_led;
 		/* register check_power_status callback. */
 		codec->patch_ops.check_power_status =
-		    stac92xx_hp_hdx_check_power_status;
+			stac92xx_hp_check_power_status;
+	}
 #endif	
-		break;
-	};
 
 	spec->multiout.dac_nids = spec->dac_nids;
 	if (spec->dinput_mux)
@@ -5282,7 +5475,7 @@
 	codec->proc_widget_hook = stac92hd7x_proc_hook;
 
 	return 0;
-};
+}
 
 static int patch_stac922x(struct hda_codec *codec)
 {
@@ -5437,7 +5630,7 @@
 			/* correct the device field to SPDIF out */
 			snd_hda_codec_set_pincfg(codec, 0x21, 0x01442070);
 			break;
-		};
+		}
 		/* configure the analog microphone on some laptops */
 		snd_hda_codec_set_pincfg(codec, 0x0c, 0x90a79130);
 		/* correct the front output jack as a hp out */
@@ -5747,6 +5940,7 @@
  	{ .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
  	{ .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
  	{ .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
+	{ .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
  	{ .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
  	{ .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
  	{ .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index b25a5cc..8e004fb 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -205,7 +205,7 @@
 
 	/* playback */
 	struct hda_multi_out multiout;
-	hda_nid_t extra_dig_out_nid;
+	hda_nid_t slave_dig_outs[2];
 
 	/* capture */
 	unsigned int num_adc_nids;
@@ -731,21 +731,6 @@
 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
 }
 
-/* setup SPDIF output stream */
-static void setup_dig_playback_stream(struct hda_codec *codec, hda_nid_t nid,
-				 unsigned int stream_tag, unsigned int format)
-{
-	/* turn off SPDIF once; otherwise the IEC958 bits won't be updated */
-	if (codec->spdif_ctls & AC_DIG1_ENABLE)
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-				    codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff);
-	snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format);
-	/* turn on again (if needed) */
-	if (codec->spdif_ctls & AC_DIG1_ENABLE)
-		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1,
-				    codec->spdif_ctls & 0xff);
-}
-
 static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 					struct hda_codec *codec,
 					unsigned int stream_tag,
@@ -753,19 +738,16 @@
 					struct snd_pcm_substream *substream)
 {
 	struct via_spec *spec = codec->spec;
-	hda_nid_t nid;
+	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+					     stream_tag, format, substream);
+}
 
-	/* 1st or 2nd S/PDIF */
-	if (substream->number == 0)
-		nid = spec->multiout.dig_out_nid;
-	else if (substream->number == 1)
-		nid = spec->extra_dig_out_nid;
-	else
-		return -1;
-
-	mutex_lock(&codec->spdif_mutex);
-	setup_dig_playback_stream(codec, nid, stream_tag, format);
-	mutex_unlock(&codec->spdif_mutex);
+static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+					struct hda_codec *codec,
+					struct snd_pcm_substream *substream)
+{
+	struct via_spec *spec = codec->spec;
+	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
 	return 0;
 }
 
@@ -842,7 +824,8 @@
 	.ops = {
 		.open = via_dig_playback_pcm_open,
 		.close = via_dig_playback_pcm_close,
-		.prepare = via_dig_playback_pcm_prepare
+		.prepare = via_dig_playback_pcm_prepare,
+		.cleanup = via_dig_playback_pcm_cleanup
 	},
 };
 
@@ -874,13 +857,6 @@
 		if (err < 0)
 			return err;
 		spec->multiout.share_spdif = 1;
-
-		if (spec->extra_dig_out_nid) {
-			err = snd_hda_create_spdif_out_ctls(codec,
-						    spec->extra_dig_out_nid);
-			if (err < 0)
-				return err;
-		}
 	}
 	if (spec->dig_in_nid) {
 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
@@ -1013,10 +989,6 @@
 		via_gpio_control(codec);
 }
 
-static hda_nid_t slave_dig_outs[] = {
-	0,
-};
-
 static int via_init(struct hda_codec *codec)
 {
 	struct via_spec *spec = codec->spec;
@@ -1051,8 +1023,9 @@
 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
 
-	/* no slave outs */
-	codec->slave_dig_outs = slave_dig_outs;
+	/* assign slave outs */
+	if (spec->slave_dig_outs[0])
+		codec->slave_dig_outs = spec->slave_dig_outs;
 
  	return 0;
 }
@@ -2134,7 +2107,8 @@
 	.ops = {
 		.open = via_dig_playback_pcm_open,
 		.close = via_dig_playback_pcm_close,
-		.prepare = via_dig_playback_pcm_prepare
+		.prepare = via_dig_playback_pcm_prepare,
+		.cleanup = via_dig_playback_pcm_cleanup
 	},
 };
 
@@ -2589,14 +2563,15 @@
 };
 
 static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
-	.substreams = 2,
+	.substreams = 1,
 	.channels_min = 2,
 	.channels_max = 2,
 	/* NID is set in via_build_pcms */
 	.ops = {
 		.open = via_dig_playback_pcm_open,
 		.close = via_dig_playback_pcm_close,
-		.prepare = via_dig_playback_pcm_prepare
+		.prepare = via_dig_playback_pcm_prepare,
+		.cleanup = via_dig_playback_pcm_cleanup
 	},
 };
 
@@ -2805,14 +2780,37 @@
 	return 0;
 }
 
+/* fill out digital output widgets; one for master and one for slave outputs */
+static void fill_dig_outs(struct hda_codec *codec)
+{
+	struct via_spec *spec = codec->spec;
+	int i;
+
+	for (i = 0; i < spec->autocfg.dig_outs; i++) {
+		hda_nid_t nid;
+		int conn;
+
+		nid = spec->autocfg.dig_out_pins[i];
+		if (!nid)
+			continue;
+		conn = snd_hda_get_connections(codec, nid, &nid, 1);
+		if (conn < 1)
+			continue;
+		if (!spec->multiout.dig_out_nid)
+			spec->multiout.dig_out_nid = nid;
+		else {
+			spec->slave_dig_outs[0] = nid;
+			break; /* at most two dig outs */
+		}
+	}
+}
+
 static int vt1708S_parse_auto_config(struct hda_codec *codec)
 {
 	struct via_spec *spec = codec->spec;
 	int err;
-	static hda_nid_t vt1708s_ignore[] = {0x21, 0};
 
-	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
-					   vt1708s_ignore);
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
 	if (err < 0)
 		return err;
 	err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
@@ -2833,10 +2831,7 @@
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-	if (spec->autocfg.dig_outs)
-		spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID;
-
-	spec->extra_dig_out_nid = 0x15;
+	fill_dig_outs(codec);
 
 	if (spec->kctls.list)
 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
@@ -3000,7 +2995,8 @@
 	.ops = {
 		.open = via_dig_playback_pcm_open,
 		.close = via_dig_playback_pcm_close,
-		.prepare = via_dig_playback_pcm_prepare
+		.prepare = via_dig_playback_pcm_prepare,
+		.cleanup = via_dig_playback_pcm_cleanup
 	},
 };
 
@@ -3128,10 +3124,8 @@
 {
 	struct via_spec *spec = codec->spec;
 	int err;
-	static hda_nid_t vt1702_ignore[] = {0x1C, 0};
 
-	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg,
-					   vt1702_ignore);
+	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
 	if (err < 0)
 		return err;
 	err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
@@ -3152,10 +3146,7 @@
 
 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
 
-	if (spec->autocfg.dig_outs)
-		spec->multiout.dig_out_nid = VT1702_DIGOUT_NID;
-
-	spec->extra_dig_out_nid = 0x1B;
+	fill_dig_outs(codec);
 
 	if (spec->kctls.list)
 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
diff --git a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile
index f99fe08..536eae2 100644
--- a/sound/pci/ice1712/Makefile
+++ b/sound/pci/ice1712/Makefile
@@ -5,7 +5,7 @@
 
 snd-ice17xx-ak4xxx-objs := ak4xxx.o
 snd-ice1712-objs := ice1712.o delta.o hoontech.o ews.o
-snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o
+snd-ice1724-objs := ice1724.o amp.o revo.o aureon.o vt1720_mobo.o pontis.o prodigy192.o prodigy_hifi.o juli.o phase.o wtm.o se.o maya44.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o snd-ice17xx-ak4xxx.o
diff --git a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
index fdae6de..adc909e 100644
--- a/sound/pci/ice1712/ice1712.h
+++ b/sound/pci/ice1712/ice1712.h
@@ -335,6 +335,7 @@
 	unsigned int force_rdma1:1;	/* VT1720/4 - RDMA1 as non-spdif */
 	unsigned int midi_output:1;	/* VT1720/4: MIDI output triggered */
 	unsigned int midi_input:1;	/* VT1720/4: MIDI input triggered */
+	unsigned int own_routing:1;	/* VT1720/4: use own routing ctls */
 	unsigned int num_total_dacs;	/* total DACs */
 	unsigned int num_total_adcs;	/* total ADCs */
 	unsigned int cur_rate;		/* current rate */
@@ -458,10 +459,17 @@
 	return  snd_ice1712_gpio_read(ice) & mask;
 }
 
+/* route access functions */
+int snd_ice1724_get_route_val(struct snd_ice1712 *ice, int shift);
+int snd_ice1724_put_route_val(struct snd_ice1712 *ice, unsigned int val,
+								int shift);
+
 int snd_ice1712_spdif_build_controls(struct snd_ice1712 *ice);
 
-int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak, const struct snd_akm4xxx *template,
-			     const struct snd_ak4xxx_private *priv, struct snd_ice1712 *ice);
+int snd_ice1712_akm4xxx_init(struct snd_akm4xxx *ak,
+			     const struct snd_akm4xxx *template,
+			     const struct snd_ak4xxx_private *priv,
+			     struct snd_ice1712 *ice);
 void snd_ice1712_akm4xxx_free(struct snd_ice1712 *ice);
 int snd_ice1712_akm4xxx_build_controls(struct snd_ice1712 *ice);
 
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 128510e7..36ade77 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -49,6 +49,7 @@
 #include "prodigy192.h"
 #include "prodigy_hifi.h"
 #include "juli.h"
+#include "maya44.h"
 #include "phase.h"
 #include "wtm.h"
 #include "se.h"
@@ -65,6 +66,7 @@
 	       PRODIGY192_DEVICE_DESC
 	       PRODIGY_HIFI_DEVICE_DESC
 	       JULI_DEVICE_DESC
+	       MAYA44_DEVICE_DESC
 	       PHASE_DEVICE_DESC
 	       WTM_DEVICE_DESC
 	       SE_DEVICE_DESC
@@ -626,7 +628,7 @@
 	return 0;
 }
 
-static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
+static int snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
 				    int force)
 {
 	unsigned long flags;
@@ -634,17 +636,18 @@
 	unsigned int i, old_rate;
 
 	if (rate > ice->hw_rates->list[ice->hw_rates->count - 1])
-		return;
+		return -EINVAL;
+
 	spin_lock_irqsave(&ice->reg_lock, flags);
 	if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
 	    (inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {
 		/* running? we cannot change the rate now... */
 		spin_unlock_irqrestore(&ice->reg_lock, flags);
-		return;
+		return -EBUSY;
 	}
 	if (!force && is_pro_rate_locked(ice)) {
 		spin_unlock_irqrestore(&ice->reg_lock, flags);
-		return;
+		return (rate == ice->cur_rate) ? 0 : -EBUSY;
 	}
 
 	old_rate = ice->get_rate(ice);
@@ -652,7 +655,7 @@
 		ice->set_rate(ice, rate);
 	else if (rate == ice->cur_rate) {
 		spin_unlock_irqrestore(&ice->reg_lock, flags);
-		return;
+		return 0;
 	}
 
 	ice->cur_rate = rate;
@@ -674,13 +677,15 @@
 	}
 	if (ice->spdif.ops.setup_rate)
 		ice->spdif.ops.setup_rate(ice, rate);
+
+	return 0;
 }
 
 static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
 				    struct snd_pcm_hw_params *hw_params)
 {
 	struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
-	int i, chs;
+	int i, chs, err;
 
 	chs = params_channels(hw_params);
 	mutex_lock(&ice->open_mutex);
@@ -715,7 +720,11 @@
 		}
 	}
 	mutex_unlock(&ice->open_mutex);
-	snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
+
+	err = snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
+	if (err < 0)
+		return err;
+
 	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
 }
 
@@ -848,20 +857,39 @@
 #endif
 }
 
-static const struct vt1724_pcm_reg vt1724_playback_pro_reg = {
+static const struct vt1724_pcm_reg vt1724_pdma0_reg = {
 	.addr = VT1724_MT_PLAYBACK_ADDR,
 	.size = VT1724_MT_PLAYBACK_SIZE,
 	.count = VT1724_MT_PLAYBACK_COUNT,
 	.start = VT1724_PDMA0_START,
 };
 
-static const struct vt1724_pcm_reg vt1724_capture_pro_reg = {
+static const struct vt1724_pcm_reg vt1724_pdma4_reg = {
+	.addr = VT1724_MT_PDMA4_ADDR,
+	.size = VT1724_MT_PDMA4_SIZE,
+	.count = VT1724_MT_PDMA4_COUNT,
+	.start = VT1724_PDMA4_START,
+};
+
+static const struct vt1724_pcm_reg vt1724_rdma0_reg = {
 	.addr = VT1724_MT_CAPTURE_ADDR,
 	.size = VT1724_MT_CAPTURE_SIZE,
 	.count = VT1724_MT_CAPTURE_COUNT,
 	.start = VT1724_RDMA0_START,
 };
 
+static const struct vt1724_pcm_reg vt1724_rdma1_reg = {
+	.addr = VT1724_MT_RDMA1_ADDR,
+	.size = VT1724_MT_RDMA1_SIZE,
+	.count = VT1724_MT_RDMA1_COUNT,
+	.start = VT1724_RDMA1_START,
+};
+
+#define vt1724_playback_pro_reg vt1724_pdma0_reg
+#define vt1724_playback_spdif_reg vt1724_pdma4_reg
+#define vt1724_capture_pro_reg vt1724_rdma0_reg
+#define vt1724_capture_spdif_reg vt1724_rdma1_reg
+
 static const struct snd_pcm_hardware snd_vt1724_playback_pro = {
 	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -1077,20 +1105,6 @@
  * SPDIF PCM
  */
 
-static const struct vt1724_pcm_reg vt1724_playback_spdif_reg = {
-	.addr = VT1724_MT_PDMA4_ADDR,
-	.size = VT1724_MT_PDMA4_SIZE,
-	.count = VT1724_MT_PDMA4_COUNT,
-	.start = VT1724_PDMA4_START,
-};
-
-static const struct vt1724_pcm_reg vt1724_capture_spdif_reg = {
-	.addr = VT1724_MT_RDMA1_ADDR,
-	.size = VT1724_MT_RDMA1_SIZE,
-	.count = VT1724_MT_RDMA1_COUNT,
-	.start = VT1724_RDMA1_START,
-};
-
 /* update spdif control bits; call with reg_lock */
 static void update_spdif_bits(struct snd_ice1712 *ice, unsigned int val)
 {
@@ -1963,7 +1977,7 @@
 	return idx * 3;
 }
 
-static int get_route_val(struct snd_ice1712 *ice, int shift)
+int snd_ice1724_get_route_val(struct snd_ice1712 *ice, int shift)
 {
 	unsigned long val;
 	unsigned char eitem;
@@ -1982,7 +1996,8 @@
 	return eitem;
 }
 
-static int put_route_val(struct snd_ice1712 *ice, unsigned int val, int shift)
+int snd_ice1724_put_route_val(struct snd_ice1712 *ice, unsigned int val,
+								int shift)
 {
 	unsigned int old_val, nval;
 	int change;
@@ -2010,7 +2025,7 @@
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	ucontrol->value.enumerated.item[0] =
-		get_route_val(ice, analog_route_shift(idx));
+		snd_ice1724_get_route_val(ice, analog_route_shift(idx));
 	return 0;
 }
 
@@ -2019,8 +2034,9 @@
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-	return put_route_val(ice, ucontrol->value.enumerated.item[0],
-			     analog_route_shift(idx));
+	return snd_ice1724_put_route_val(ice,
+					 ucontrol->value.enumerated.item[0],
+					 analog_route_shift(idx));
 }
 
 static int snd_vt1724_pro_route_spdif_get(struct snd_kcontrol *kcontrol,
@@ -2029,7 +2045,7 @@
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	ucontrol->value.enumerated.item[0] =
-		get_route_val(ice, digital_route_shift(idx));
+		snd_ice1724_get_route_val(ice, digital_route_shift(idx));
 	return 0;
 }
 
@@ -2038,11 +2054,13 @@
 {
 	struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
 	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-	return put_route_val(ice, ucontrol->value.enumerated.item[0],
-			     digital_route_shift(idx));
+	return snd_ice1724_put_route_val(ice,
+					 ucontrol->value.enumerated.item[0],
+					 digital_route_shift(idx));
 }
 
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = {
+static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata =
+{
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "H/W Playback Route",
 	.info = snd_vt1724_pro_route_info,
@@ -2109,6 +2127,7 @@
 	snd_vt1724_prodigy_hifi_cards,
 	snd_vt1724_prodigy192_cards,
 	snd_vt1724_juli_cards,
+	snd_vt1724_maya44_cards,
 	snd_vt1724_phase_cards,
 	snd_vt1724_wtm_cards,
 	snd_vt1724_se_cards,
@@ -2246,8 +2265,10 @@
 static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
 {
 	outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
+	inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
 	msleep(10);
 	outb(0, ICEREG1724(ice, CONTROL));
+	inb(ICEREG1724(ice, CONTROL)); /* pci posting flush */
 	msleep(10);
 }
 
@@ -2277,9 +2298,12 @@
 	if (snd_BUG_ON(!ice->pcm))
 		return -EIO;
 
-	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
-	if (err < 0)
-		return err;
+	if (!ice->own_routing) {
+		err = snd_ctl_add(ice->card,
+			snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
+		if (err < 0)
+			return err;
+	}
 
 	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_spdif_switch, ice));
 	if (err < 0)
@@ -2326,7 +2350,7 @@
 	if (err < 0)
 		return err;
 
-	if (ice->num_total_dacs > 0) {
+	if (!ice->own_routing && ice->num_total_dacs > 0) {
 		struct snd_kcontrol_new tmp = snd_vt1724_mixer_pro_analog_route;
 		tmp.count = ice->num_total_dacs;
 		if (ice->vt1720 && tmp.count > 2)
diff --git a/sound/pci/ice1712/maya44.c b/sound/pci/ice1712/maya44.c
new file mode 100644
index 0000000..3e1c20a
--- /dev/null
+++ b/sound/pci/ice1712/maya44.c
@@ -0,0 +1,779 @@
+/*
+ *   ALSA driver for ICEnsemble VT1724 (Envy24HT)
+ *
+ *   Lowlevel functions for ESI Maya44 cards
+ *
+ *	Copyright (c) 2009 Takashi Iwai <tiwai@suse.de>
+ *	Based on the patches by Rainer Zimmermann <mail@lightshed.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/tlv.h>
+
+#include "ice1712.h"
+#include "envy24ht.h"
+#include "maya44.h"
+
+/* WM8776 register indexes */
+#define WM8776_REG_HEADPHONE_L		0x00
+#define WM8776_REG_HEADPHONE_R		0x01
+#define WM8776_REG_HEADPHONE_MASTER	0x02
+#define WM8776_REG_DAC_ATTEN_L		0x03
+#define WM8776_REG_DAC_ATTEN_R		0x04
+#define WM8776_REG_DAC_ATTEN_MASTER	0x05
+#define WM8776_REG_DAC_PHASE		0x06
+#define WM8776_REG_DAC_CONTROL		0x07
+#define WM8776_REG_DAC_MUTE		0x08
+#define WM8776_REG_DAC_DEEMPH		0x09
+#define WM8776_REG_DAC_IF_CONTROL	0x0a
+#define WM8776_REG_ADC_IF_CONTROL	0x0b
+#define WM8776_REG_MASTER_MODE_CONTROL	0x0c
+#define WM8776_REG_POWERDOWN		0x0d
+#define WM8776_REG_ADC_ATTEN_L		0x0e
+#define WM8776_REG_ADC_ATTEN_R		0x0f
+#define WM8776_REG_ADC_ALC1		0x10
+#define WM8776_REG_ADC_ALC2		0x11
+#define WM8776_REG_ADC_ALC3		0x12
+#define WM8776_REG_ADC_NOISE_GATE	0x13
+#define WM8776_REG_ADC_LIMITER		0x14
+#define WM8776_REG_ADC_MUX		0x15
+#define WM8776_REG_OUTPUT_MUX		0x16
+#define WM8776_REG_RESET		0x17
+
+#define WM8776_NUM_REGS			0x18
+
+/* clock ratio identifiers for snd_wm8776_set_rate() */
+#define WM8776_CLOCK_RATIO_128FS	0
+#define WM8776_CLOCK_RATIO_192FS	1
+#define WM8776_CLOCK_RATIO_256FS	2
+#define WM8776_CLOCK_RATIO_384FS	3
+#define WM8776_CLOCK_RATIO_512FS	4
+#define WM8776_CLOCK_RATIO_768FS	5
+
+enum { WM_VOL_HP, WM_VOL_DAC, WM_VOL_ADC, WM_NUM_VOLS };
+enum { WM_SW_DAC, WM_SW_BYPASS, WM_NUM_SWITCHES };
+
+struct snd_wm8776 {
+	unsigned char addr;
+	unsigned short regs[WM8776_NUM_REGS];
+	unsigned char volumes[WM_NUM_VOLS][2];
+	unsigned int switch_bits;
+};
+
+struct snd_maya44 {
+	struct snd_ice1712 *ice;
+	struct snd_wm8776 wm[2];
+	struct mutex mutex;
+};
+
+
+/* write the given register and save the data to the cache */
+static void wm8776_write(struct snd_ice1712 *ice, struct snd_wm8776 *wm,
+			 unsigned char reg, unsigned short val)
+{
+	/*
+	 * WM8776 registers are up to 9 bits wide, bit 8 is placed in the LSB
+	 * of the address field
+	 */
+	snd_vt1724_write_i2c(ice, wm->addr,
+			     (reg << 1) | ((val >> 8) & 1),
+			     val & 0xff);
+	wm->regs[reg] = val;
+}
+
+/*
+ * update the given register with and/or mask and save the data to the cache
+ */
+static int wm8776_write_bits(struct snd_ice1712 *ice, struct snd_wm8776 *wm,
+			     unsigned char reg,
+			     unsigned short mask, unsigned short val)
+{
+	val |= wm->regs[reg] & ~mask;
+	if (val != wm->regs[reg]) {
+		wm8776_write(ice, wm, reg, val);
+		return 1;
+	}
+	return 0;
+}
+
+
+/*
+ * WM8776 volume controls
+ */
+
+struct maya_vol_info {
+	unsigned int maxval;		/* volume range: 0..maxval */
+	unsigned char regs[2];		/* left and right registers */
+	unsigned short mask;		/* value mask */
+	unsigned short offset;		/* zero-value offset */
+	unsigned short mute;		/* mute bit */
+	unsigned short update;		/* update bits */
+	unsigned char mux_bits[2];	/* extra bits for ADC mute */
+};
+
+static struct maya_vol_info vol_info[WM_NUM_VOLS] = {
+	[WM_VOL_HP] = {
+		.maxval = 80,
+		.regs = { WM8776_REG_HEADPHONE_L, WM8776_REG_HEADPHONE_R },
+		.mask = 0x7f,
+		.offset = 0x30,
+		.mute = 0x00,
+		.update = 0x180,	/* update and zero-cross enable */
+	},
+	[WM_VOL_DAC] = {
+		.maxval = 255,
+		.regs = { WM8776_REG_DAC_ATTEN_L, WM8776_REG_DAC_ATTEN_R },
+		.mask = 0xff,
+		.offset = 0x01,
+		.mute = 0x00,
+		.update = 0x100,	/* zero-cross enable */
+	},
+	[WM_VOL_ADC] = {
+		.maxval = 91,
+		.regs = { WM8776_REG_ADC_ATTEN_L, WM8776_REG_ADC_ATTEN_R },
+		.mask = 0xff,
+		.offset = 0xa5,
+		.mute = 0xa5,
+		.update = 0x100,	/* update */
+		.mux_bits = { 0x80, 0x40 }, /* ADCMUX bits */
+	},
+};
+
+/*
+ * dB tables
+ */
+/* headphone output: mute, -73..+6db (1db step) */
+static const DECLARE_TLV_DB_SCALE(db_scale_hp, -7400, 100, 1);
+/* DAC output: mute, -127..0db (0.5db step) */
+static const DECLARE_TLV_DB_SCALE(db_scale_dac, -12750, 50, 1);
+/* ADC gain: mute, -21..+24db (0.5db step) */
+static const DECLARE_TLV_DB_SCALE(db_scale_adc, -2100, 50, 1);
+
+static int maya_vol_info(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_info *uinfo)
+{
+	unsigned int idx = kcontrol->private_value;
+	struct maya_vol_info *vol = &vol_info[idx];
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = vol->maxval;
+	return 0;
+}
+
+static int maya_vol_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wm8776 *wm =
+		&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
+	unsigned int idx = kcontrol->private_value;
+
+	mutex_lock(&chip->mutex);
+	ucontrol->value.integer.value[0] = wm->volumes[idx][0];
+	ucontrol->value.integer.value[1] = wm->volumes[idx][1];
+	mutex_unlock(&chip->mutex);
+	return 0;
+}
+
+static int maya_vol_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wm8776 *wm =
+		&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
+	unsigned int idx = kcontrol->private_value;
+	struct maya_vol_info *vol = &vol_info[idx];
+	unsigned int val, data;
+	int ch, changed = 0;
+
+	mutex_lock(&chip->mutex);
+	for (ch = 0; ch < 2; ch++) {
+		val = ucontrol->value.integer.value[ch];
+		if (val > vol->maxval)
+			val = vol->maxval;
+		if (val == wm->volumes[idx][ch])
+			continue;
+		if (!val)
+			data = vol->mute;
+		else
+			data = (val - 1) + vol->offset;
+		data |= vol->update;
+		changed |= wm8776_write_bits(chip->ice, wm, vol->regs[ch],
+					     vol->mask | vol->update, data);
+		if (vol->mux_bits[ch])
+			wm8776_write_bits(chip->ice, wm, WM8776_REG_ADC_MUX,
+					  vol->mux_bits[ch],
+					  val ? 0 : vol->mux_bits[ch]);
+		wm->volumes[idx][ch] = val;
+	}
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+/*
+ * WM8776 switch controls
+ */
+
+#define COMPOSE_SW_VAL(idx, reg, mask)	((idx) | ((reg) << 8) | ((mask) << 16))
+#define GET_SW_VAL_IDX(val)	((val) & 0xff)
+#define GET_SW_VAL_REG(val)	(((val) >> 8) & 0xff)
+#define GET_SW_VAL_MASK(val)	(((val) >> 16) & 0xff)
+
+#define maya_sw_info	snd_ctl_boolean_mono_info
+
+static int maya_sw_get(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wm8776 *wm =
+		&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
+	unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value);
+
+	ucontrol->value.integer.value[0] = (wm->switch_bits >> idx) & 1;
+	return 0;
+}
+
+static int maya_sw_put(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+	struct snd_wm8776 *wm =
+		&chip->wm[snd_ctl_get_ioff(kcontrol, &ucontrol->id)];
+	unsigned int idx = GET_SW_VAL_IDX(kcontrol->private_value);
+	unsigned int mask, val;
+	int changed;
+
+	mutex_lock(&chip->mutex);
+	mask = 1 << idx;
+	wm->switch_bits &= ~mask;
+	val = ucontrol->value.integer.value[0];
+	if (val)
+		wm->switch_bits |= mask;
+	mask = GET_SW_VAL_MASK(kcontrol->private_value);
+	changed = wm8776_write_bits(chip->ice, wm,
+				    GET_SW_VAL_REG(kcontrol->private_value),
+				    mask, val ? mask : 0);
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+/*
+ * GPIO pins (known ones for maya44)
+ */
+#define GPIO_PHANTOM_OFF	2
+#define GPIO_MIC_RELAY		4
+#define GPIO_SPDIF_IN_INV	5
+#define GPIO_MUST_BE_0		7
+
+/*
+ * GPIO switch controls
+ */
+
+#define COMPOSE_GPIO_VAL(shift, inv)	((shift) | ((inv) << 8))
+#define GET_GPIO_VAL_SHIFT(val)		((val) & 0xff)
+#define GET_GPIO_VAL_INV(val)		(((val) >> 8) & 1)
+
+static int maya_set_gpio_bits(struct snd_ice1712 *ice, unsigned int mask,
+			      unsigned int bits)
+{
+	unsigned int data;
+	data = snd_ice1712_gpio_read(ice);
+	if ((data & mask) == bits)
+		return 0;
+	snd_ice1712_gpio_write(ice, (data & ~mask) | bits);
+	return 1;
+}
+
+#define maya_gpio_sw_info	snd_ctl_boolean_mono_info
+
+static int maya_gpio_sw_get(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+	unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value);
+	unsigned int val;
+
+	val = (snd_ice1712_gpio_read(chip->ice) >> shift) & 1;
+	if (GET_GPIO_VAL_INV(kcontrol->private_value))
+		val = !val;
+	ucontrol->value.integer.value[0] = val;
+	return 0;
+}
+
+static int maya_gpio_sw_put(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+	unsigned int shift = GET_GPIO_VAL_SHIFT(kcontrol->private_value);
+	unsigned int val, mask;
+	int changed;
+
+	mutex_lock(&chip->mutex);
+	mask = 1 << shift;
+	val = ucontrol->value.integer.value[0];
+	if (GET_GPIO_VAL_INV(kcontrol->private_value))
+		val = !val;
+	val = val ? mask : 0;
+	changed = maya_set_gpio_bits(chip->ice, mask, val);
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+/*
+ * capture source selection
+ */
+
+/* known working input slots (0-4) */
+#define MAYA_LINE_IN	1	/* in-2 */
+#define MAYA_MIC_IN	4	/* in-5 */
+
+static void wm8776_select_input(struct snd_maya44 *chip, int idx, int line)
+{
+	wm8776_write_bits(chip->ice, &chip->wm[idx], WM8776_REG_ADC_MUX,
+			  0x1f, 1 << line);
+}
+
+static int maya_rec_src_info(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = { "Line", "Mic" };
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = ARRAY_SIZE(texts);
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item =
+			uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int maya_rec_src_get(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+	int sel;
+
+	if (snd_ice1712_gpio_read(chip->ice) & (1 << GPIO_MIC_RELAY))
+		sel = 1;
+	else
+		sel = 0;
+	ucontrol->value.enumerated.item[0] = sel;
+	return 0;
+}
+
+static int maya_rec_src_put(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+	int sel = ucontrol->value.enumerated.item[0];
+	int changed;
+
+	mutex_lock(&chip->mutex);
+	changed = maya_set_gpio_bits(chip->ice, GPIO_MIC_RELAY,
+				     sel ? GPIO_MIC_RELAY : 0);
+	wm8776_select_input(chip, 0, sel ? MAYA_MIC_IN : MAYA_LINE_IN);
+	mutex_unlock(&chip->mutex);
+	return changed;
+}
+
+/*
+ * Maya44 routing switch settings have different meanings than the standard
+ * ice1724 switches as defined in snd_vt1724_pro_route_info (ice1724.c).
+ */
+static int maya_pb_route_info(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_info *uinfo)
+{
+	static char *texts[] = {
+		"PCM Out", /* 0 */
+		"Input 1", "Input 2", "Input 3", "Input 4"
+	};
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = ARRAY_SIZE(texts);
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item =
+			uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name,
+	       texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int maya_pb_route_shift(int idx)
+{
+	static const unsigned char shift[10] =
+		{ 8, 20, 0, 3, 11, 23, 14, 26, 17, 29 };
+	return shift[idx % 10];
+}
+
+static int maya_pb_route_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	ucontrol->value.enumerated.item[0] =
+		snd_ice1724_get_route_val(chip->ice, maya_pb_route_shift(idx));
+	return 0;
+}
+
+static int maya_pb_route_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_maya44 *chip = snd_kcontrol_chip(kcontrol);
+	int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	return snd_ice1724_put_route_val(chip->ice,
+					 ucontrol->value.enumerated.item[0],
+					 maya_pb_route_shift(idx));
+}
+
+
+/*
+ * controls to be added
+ */
+
+static struct snd_kcontrol_new maya_controls[] __devinitdata = {
+	{
+		.name = "Crossmix Playback Volume",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+		.info = maya_vol_info,
+		.get = maya_vol_get,
+		.put = maya_vol_put,
+		.tlv = { .p = db_scale_hp },
+		.private_value = WM_VOL_HP,
+		.count = 2,
+	},
+	{
+		.name = "PCM Playback Volume",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+		.info = maya_vol_info,
+		.get = maya_vol_get,
+		.put = maya_vol_put,
+		.tlv = { .p = db_scale_dac },
+		.private_value = WM_VOL_DAC,
+		.count = 2,
+	},
+	{
+		.name = "Line Capture Volume",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+		.info = maya_vol_info,
+		.get = maya_vol_get,
+		.put = maya_vol_put,
+		.tlv = { .p = db_scale_adc },
+		.private_value = WM_VOL_ADC,
+		.count = 2,
+	},
+	{
+		.name = "PCM Playback Switch",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.info = maya_sw_info,
+		.get = maya_sw_get,
+		.put = maya_sw_put,
+		.private_value = COMPOSE_SW_VAL(WM_SW_DAC,
+						WM8776_REG_OUTPUT_MUX, 0x01),
+		.count = 2,
+	},
+	{
+		.name = "Bypass Playback Switch",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.info = maya_sw_info,
+		.get = maya_sw_get,
+		.put = maya_sw_put,
+		.private_value = COMPOSE_SW_VAL(WM_SW_BYPASS,
+						WM8776_REG_OUTPUT_MUX, 0x04),
+		.count = 2,
+	},
+	{
+		.name = "Capture Source",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.info = maya_rec_src_info,
+		.get = maya_rec_src_get,
+		.put = maya_rec_src_put,
+	},
+	{
+		.name = "Mic Phantom Power Switch",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.info = maya_gpio_sw_info,
+		.get = maya_gpio_sw_get,
+		.put = maya_gpio_sw_put,
+		.private_value = COMPOSE_GPIO_VAL(GPIO_PHANTOM_OFF, 1),
+	},
+	{
+		.name = "SPDIF Capture Switch",
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.info = maya_gpio_sw_info,
+		.get = maya_gpio_sw_get,
+		.put = maya_gpio_sw_put,
+		.private_value = COMPOSE_GPIO_VAL(GPIO_SPDIF_IN_INV, 1),
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "H/W Playback Route",
+		.info = maya_pb_route_info,
+		.get = maya_pb_route_get,
+		.put = maya_pb_route_put,
+		.count = 4,  /* FIXME: do controls 5-9 have any meaning? */
+	},
+};
+
+static int __devinit maya44_add_controls(struct snd_ice1712 *ice)
+{
+	int err, i;
+
+	for (i = 0; i < ARRAY_SIZE(maya_controls); i++) {
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&maya_controls[i],
+							  ice->spec));
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
+
+
+/*
+ * initialize a wm8776 chip
+ */
+static void __devinit wm8776_init(struct snd_ice1712 *ice,
+				  struct snd_wm8776 *wm, unsigned int addr)
+{
+	static const unsigned short inits_wm8776[] = {
+		0x02, 0x100, /* R2: headphone L+R muted + update */
+		0x05, 0x100, /* R5: DAC output L+R muted + update */
+		0x06, 0x000, /* R6: DAC output phase normal */
+		0x07, 0x091, /* R7: DAC enable zero cross detection,
+				normal output */
+		0x08, 0x000, /* R8: DAC soft mute off */
+		0x09, 0x000, /* R9: no deemph, DAC zero detect disabled */
+		0x0a, 0x022, /* R10: DAC I2C mode, std polarities, 24bit */
+		0x0b, 0x022, /* R11: ADC I2C mode, std polarities, 24bit,
+				highpass filter enabled */
+		0x0c, 0x042, /* R12: ADC+DAC slave, ADC+DAC 44,1kHz */
+		0x0d, 0x000, /* R13: all power up */
+		0x0e, 0x100, /* R14: ADC left muted,
+				enable zero cross detection */
+		0x0f, 0x100, /* R15: ADC right muted,
+				enable zero cross detection */
+			     /* R16: ALC...*/
+		0x11, 0x000, /* R17: disable ALC */
+			     /* R18: ALC...*/
+			     /* R19: noise gate...*/
+		0x15, 0x000, /* R21: ADC input mux init, mute all inputs */
+		0x16, 0x001, /* R22: output mux, select DAC */
+		0xff, 0xff
+	};
+
+	const unsigned short *ptr;
+	unsigned char reg;
+	unsigned short data;
+
+	wm->addr = addr;
+	/* enable DAC output; mute bypass, aux & all inputs */
+	wm->switch_bits = (1 << WM_SW_DAC);
+
+	ptr = inits_wm8776;
+	while (*ptr != 0xff) {
+		reg = *ptr++;
+		data = *ptr++;
+		wm8776_write(ice, wm, reg, data);
+	}
+}
+
+
+/*
+ * change the rate on the WM8776 codecs.
+ * this assumes that the VT17xx's rate is changed by the calling function.
+ * NOTE: even though the WM8776's are running in slave mode and rate
+ * selection is automatic, we need to call snd_wm8776_set_rate() here
+ * to make sure some flags are set correctly.
+ */
+static void set_rate(struct snd_ice1712 *ice, unsigned int rate)
+{
+	struct snd_maya44 *chip = ice->spec;
+	unsigned int ratio, adc_ratio, val;
+	int i;
+
+	switch (rate) {
+	case 192000:
+		ratio = WM8776_CLOCK_RATIO_128FS;
+		break;
+	case 176400:
+		ratio = WM8776_CLOCK_RATIO_128FS;
+		break;
+	case 96000:
+		ratio = WM8776_CLOCK_RATIO_256FS;
+		break;
+	case 88200:
+		ratio = WM8776_CLOCK_RATIO_384FS;
+		break;
+	case 48000:
+		ratio = WM8776_CLOCK_RATIO_512FS;
+		break;
+	case 44100:
+		ratio = WM8776_CLOCK_RATIO_512FS;
+		break;
+	case 32000:
+		ratio = WM8776_CLOCK_RATIO_768FS;
+		break;
+	case 0:
+		/* no hint - S/PDIF input is master, simply return */
+		return;
+	default:
+		snd_BUG();
+		return;
+	}
+
+	/*
+	 * this currently sets the same rate for ADC and DAC, but limits
+	 * ADC rate to 256X (96kHz). For 256X mode (96kHz), this sets ADC
+	 * oversampling to 64x, as recommended by WM8776 datasheet.
+	 * Setting the rate is not really necessary in slave mode.
+	 */
+	adc_ratio = ratio;
+	if (adc_ratio < WM8776_CLOCK_RATIO_256FS)
+		adc_ratio = WM8776_CLOCK_RATIO_256FS;
+
+	val = adc_ratio;
+	if (adc_ratio == WM8776_CLOCK_RATIO_256FS)
+		val |= 8;
+	val |= ratio << 4;
+
+	mutex_lock(&chip->mutex);
+	for (i = 0; i < 2; i++)
+		wm8776_write_bits(ice, &chip->wm[i],
+				  WM8776_REG_MASTER_MODE_CONTROL,
+				  0x180, val);
+	mutex_unlock(&chip->mutex);
+}
+
+/*
+ * supported sample rates (to override the default one)
+ */
+
+static unsigned int rates[] = {
+	32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000
+};
+
+/* playback rates: 32..192 kHz */
+static struct snd_pcm_hw_constraint_list dac_rates = {
+	.count = ARRAY_SIZE(rates),
+	.list = rates,
+	.mask = 0
+};
+
+
+/*
+ * chip addresses on I2C bus
+ */
+static unsigned char wm8776_addr[2] __devinitdata = {
+	0x34, 0x36, /* codec 0 & 1 */
+};
+
+/*
+ * initialize the chip
+ */
+static int __devinit maya44_init(struct snd_ice1712 *ice)
+{
+	int i;
+	struct snd_maya44 *chip;
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+	mutex_init(&chip->mutex);
+	chip->ice = ice;
+	ice->spec = chip;
+
+	/* initialise codecs */
+	ice->num_total_dacs = 4;
+	ice->num_total_adcs = 4;
+	ice->akm_codecs = 0;
+
+	for (i = 0; i < 2; i++) {
+		wm8776_init(ice, &chip->wm[i], wm8776_addr[i]);
+		wm8776_select_input(chip, i, MAYA_LINE_IN);
+	}
+
+	/* set card specific rates */
+	ice->hw_rates = &dac_rates;
+
+	/* register change rate notifier */
+	ice->gpio.set_pro_rate = set_rate;
+
+	/* RDMA1 (2nd input channel) is used for ADC by default */
+	ice->force_rdma1 = 1;
+
+	/* have an own routing control */
+	ice->own_routing = 1;
+
+	return 0;
+}
+
+
+/*
+ * Maya44 boards don't provide the EEPROM data except for the vendor IDs.
+ * hence the driver needs to sets up it properly.
+ */
+
+static unsigned char maya44_eeprom[] __devinitdata = {
+	[ICE_EEP2_SYSCONF]     = 0x45,
+		/* clock xin1=49.152MHz, mpu401, 2 stereo ADCs+DACs */
+	[ICE_EEP2_ACLINK]      = 0x80,
+		/* I2S */
+	[ICE_EEP2_I2S]         = 0xf8,
+		/* vol, 96k, 24bit, 192k */
+	[ICE_EEP2_SPDIF]       = 0xc3,
+		/* enable spdif out, spdif out supp, spdif-in, ext spdif out */
+	[ICE_EEP2_GPIO_DIR]    = 0xff,
+	[ICE_EEP2_GPIO_DIR1]   = 0xff,
+	[ICE_EEP2_GPIO_DIR2]   = 0xff,
+	[ICE_EEP2_GPIO_MASK]   = 0/*0x9f*/,
+	[ICE_EEP2_GPIO_MASK1]  = 0/*0xff*/,
+	[ICE_EEP2_GPIO_MASK2]  = 0/*0x7f*/,
+	[ICE_EEP2_GPIO_STATE]  = (1 << GPIO_PHANTOM_OFF) |
+			(1 << GPIO_SPDIF_IN_INV),
+	[ICE_EEP2_GPIO_STATE1] = 0x00,
+	[ICE_EEP2_GPIO_STATE2] = 0x00,
+};
+
+/* entry point */
+struct snd_ice1712_card_info snd_vt1724_maya44_cards[] __devinitdata = {
+	{
+		.subvendor = VT1724_SUBDEVICE_MAYA44,
+		.name = "ESI Maya44",
+		.model = "maya44",
+		.chip_init = maya44_init,
+		.build_controls = maya44_add_controls,
+		.eeprom_size = sizeof(maya44_eeprom),
+		.eeprom_data = maya44_eeprom,
+	},
+	{ } /* terminator */
+};
diff --git a/sound/pci/ice1712/maya44.h b/sound/pci/ice1712/maya44.h
new file mode 100644
index 0000000..eafd03a
--- /dev/null
+++ b/sound/pci/ice1712/maya44.h
@@ -0,0 +1,10 @@
+#ifndef __SOUND_MAYA44_H
+#define __SOUND_MAYA44_H
+
+#define MAYA44_DEVICE_DESC		"{ESI,Maya44},"
+
+#define VT1724_SUBDEVICE_MAYA44		0x34315441	/* Maya44 */
+
+extern struct snd_ice1712_card_info  snd_vt1724_maya44_cards[];
+
+#endif	/* __SOUND_MAYA44_H */
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 173bebf..8aa5687 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -356,8 +356,6 @@
         unsigned int position;
 	unsigned int pos_shift;
 	unsigned int last_pos;
-	unsigned long last_pos_jiffies;
-	unsigned int jiffy_to_bytes;
         int frags;
         int lvi;
         int lvi_frag;
@@ -844,7 +842,6 @@
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		val = ICH_IOCE | ICH_STARTBM;
 		ichdev->last_pos = ichdev->position;
-		ichdev->last_pos_jiffies = jiffies;
 		break;
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 		ichdev->suspended = 1;
@@ -1048,7 +1045,6 @@
 			ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
 	}
 	snd_intel8x0_setup_periods(chip, ichdev);
-	ichdev->jiffy_to_bytes = (runtime->rate * 4 * ichdev->pos_shift) / HZ;
 	return 0;
 }
 
@@ -1073,19 +1069,23 @@
 		    ptr1 == igetword(chip, ichdev->reg_offset + ichdev->roff_picb))
 			break;
 	} while (timeout--);
+	ptr = ichdev->last_pos;
 	if (ptr1 != 0) {
 		ptr1 <<= ichdev->pos_shift;
 		ptr = ichdev->fragsize1 - ptr1;
 		ptr += position;
-		ichdev->last_pos = ptr;
-		ichdev->last_pos_jiffies = jiffies;
-	} else {
-		ptr1 = jiffies - ichdev->last_pos_jiffies;
-		if (ptr1)
-			ptr1 -= 1;
-		ptr = ichdev->last_pos + ptr1 * ichdev->jiffy_to_bytes;
-		ptr %= ichdev->size;
+		if (ptr < ichdev->last_pos) {
+			unsigned int pos_base, last_base;
+			pos_base = position / ichdev->fragsize1;
+			last_base = ichdev->last_pos / ichdev->fragsize1;
+			/* another sanity check; ptr1 can go back to full
+			 * before the base position is updated
+			 */
+			if (pos_base == last_base)
+				ptr = ichdev->last_pos;
+		}
 	}
+	ichdev->last_pos = ptr;
 	spin_unlock(&chip->reg_lock);
 	if (ptr >= ichdev->size)
 		return 0;
diff --git a/sound/pci/lx6464es/Makefile b/sound/pci/lx6464es/Makefile
new file mode 100644
index 0000000..eb04a6c
--- /dev/null
+++ b/sound/pci/lx6464es/Makefile
@@ -0,0 +1,2 @@
+snd-lx6464es-objs := lx6464es.o lx_core.o
+obj-$(CONFIG_SND_LX6464ES) += snd-lx6464es.o
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
new file mode 100644
index 0000000..18da2ef
--- /dev/null
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -0,0 +1,1159 @@
+/* -*- linux-c -*- *
+ *
+ * ALSA driver for the digigram lx6464es interface
+ *
+ * Copyright (c) 2008, 2009 Tim Blechmann <tim@klingt.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/info.h>
+
+#include "lx6464es.h"
+
+MODULE_AUTHOR("Tim Blechmann");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("digigram lx6464es");
+MODULE_SUPPORTED_DEVICE("{digigram lx6464es{}}");
+
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Digigram LX6464ES interface.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for  Digigram LX6464ES interface.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable/disable specific Digigram LX6464ES soundcards.");
+
+static const char card_name[] = "LX6464ES";
+
+
+#define PCI_DEVICE_ID_PLX_LX6464ES		PCI_DEVICE_ID_PLX_9056
+
+static struct pci_device_id snd_lx6464es_ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES),
+	  .subvendor = PCI_VENDOR_ID_DIGIGRAM,
+	  .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM
+	},			/* LX6464ES */
+	{ PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES),
+	  .subvendor = PCI_VENDOR_ID_DIGIGRAM,
+	  .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_CAE_SERIAL_SUBSYSTEM
+	},			/* LX6464ES-CAE */
+	{ 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, snd_lx6464es_ids);
+
+
+
+/* PGO pour USERo dans le registre pci_0x06/loc_0xEC */
+#define CHIPSC_RESET_XILINX (1L<<16)
+
+
+/* alsa callbacks */
+static struct snd_pcm_hardware lx_caps = {
+	.info             = (SNDRV_PCM_INFO_MMAP |
+			     SNDRV_PCM_INFO_INTERLEAVED |
+			     SNDRV_PCM_INFO_MMAP_VALID |
+			     SNDRV_PCM_INFO_SYNC_START),
+	.formats	  = (SNDRV_PCM_FMTBIT_S16_LE |
+			     SNDRV_PCM_FMTBIT_S16_BE |
+			     SNDRV_PCM_FMTBIT_S24_3LE |
+			     SNDRV_PCM_FMTBIT_S24_3BE),
+	.rates            = (SNDRV_PCM_RATE_CONTINUOUS |
+			     SNDRV_PCM_RATE_8000_192000),
+	.rate_min         = 8000,
+	.rate_max         = 192000,
+	.channels_min     = 2,
+	.channels_max     = 64,
+	.buffer_bytes_max = 64*2*3*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER,
+	.period_bytes_min = (2*2*MICROBLAZE_IBL_MIN*2),
+	.period_bytes_max = (4*64*MICROBLAZE_IBL_MAX*MAX_STREAM_BUFFER),
+	.periods_min      = 2,
+	.periods_max      = MAX_STREAM_BUFFER,
+};
+
+static int lx_set_granularity(struct lx6464es *chip, u32 gran);
+
+
+static int lx_hardware_open(struct lx6464es *chip,
+			    struct snd_pcm_substream *substream)
+{
+	int err = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int channels = runtime->channels;
+	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+	snd_pcm_uframes_t period_size = runtime->period_size;
+
+	snd_printd(LXP "allocating pipe for %d channels\n", channels);
+	err = lx_pipe_allocate(chip, 0, is_capture, channels);
+	if (err < 0) {
+		snd_printk(KERN_ERR LXP "allocating pipe failed\n");
+		return err;
+	}
+
+	err = lx_set_granularity(chip, period_size);
+	if (err < 0) {
+		snd_printk(KERN_ERR LXP "setting granularity to %ld failed\n",
+			   period_size);
+		return err;
+	}
+
+	return 0;
+}
+
+static int lx_hardware_start(struct lx6464es *chip,
+			     struct snd_pcm_substream *substream)
+{
+	int err = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+	snd_printd(LXP "setting stream format\n");
+	err = lx_stream_set_format(chip, runtime, 0, is_capture);
+	if (err < 0) {
+		snd_printk(KERN_ERR LXP "setting stream format failed\n");
+		return err;
+	}
+
+	snd_printd(LXP "starting pipe\n");
+	err = lx_pipe_start(chip, 0, is_capture);
+	if (err < 0) {
+		snd_printk(KERN_ERR LXP "starting pipe failed\n");
+		return err;
+	}
+
+	snd_printd(LXP "waiting for pipe to start\n");
+	err = lx_pipe_wait_for_start(chip, 0, is_capture);
+	if (err < 0) {
+		snd_printk(KERN_ERR LXP "waiting for pipe failed\n");
+		return err;
+	}
+
+	return err;
+}
+
+
+static int lx_hardware_stop(struct lx6464es *chip,
+			    struct snd_pcm_substream *substream)
+{
+	int err = 0;
+	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+	snd_printd(LXP "pausing pipe\n");
+	err = lx_pipe_pause(chip, 0, is_capture);
+	if (err < 0) {
+		snd_printk(KERN_ERR LXP "pausing pipe failed\n");
+		return err;
+	}
+
+	snd_printd(LXP "waiting for pipe to become idle\n");
+	err = lx_pipe_wait_for_idle(chip, 0, is_capture);
+	if (err < 0) {
+		snd_printk(KERN_ERR LXP "waiting for pipe failed\n");
+		return err;
+	}
+
+	snd_printd(LXP "stopping pipe\n");
+	err = lx_pipe_stop(chip, 0, is_capture);
+	if (err < 0) {
+		snd_printk(LXP "stopping pipe failed\n");
+		return err;
+	}
+
+	return err;
+}
+
+
+static int lx_hardware_close(struct lx6464es *chip,
+			     struct snd_pcm_substream *substream)
+{
+	int err = 0;
+	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+	snd_printd(LXP "releasing pipe\n");
+	err = lx_pipe_release(chip, 0, is_capture);
+	if (err < 0) {
+		snd_printk(LXP "releasing pipe failed\n");
+		return err;
+	}
+
+	return err;
+}
+
+
+static int lx_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct lx6464es *chip = snd_pcm_substream_chip(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err = 0;
+	int board_rate;
+
+	snd_printdd("->lx_pcm_open\n");
+	mutex_lock(&chip->setup_mutex);
+
+	/* copy the struct snd_pcm_hardware struct */
+	runtime->hw = lx_caps;
+
+#if 0
+	/* buffer-size should better be multiple of period-size */
+	err = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (err < 0) {
+		snd_printk(KERN_WARNING LXP "could not constrain periods\n");
+		goto exit;
+	}
+#endif
+
+	/* the clock rate cannot be changed */
+	board_rate = chip->board_sample_rate;
+	err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
+					   board_rate, board_rate);
+
+	if (err < 0) {
+		snd_printk(KERN_WARNING LXP "could not constrain periods\n");
+		goto exit;
+	}
+
+	/* constrain period size */
+	err = snd_pcm_hw_constraint_minmax(runtime,
+					   SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+					   MICROBLAZE_IBL_MIN,
+					   MICROBLAZE_IBL_MAX);
+	if (err < 0) {
+		snd_printk(KERN_WARNING LXP
+			   "could not constrain period size\n");
+		goto exit;
+	}
+
+	snd_pcm_hw_constraint_step(runtime, 0,
+				   SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+
+	snd_pcm_set_sync(substream);
+	err = 0;
+
+exit:
+	runtime->private_data = chip;
+
+	mutex_unlock(&chip->setup_mutex);
+	snd_printdd("<-lx_pcm_open, %d\n", err);
+	return err;
+}
+
+static int lx_pcm_close(struct snd_pcm_substream *substream)
+{
+	int err = 0;
+	snd_printdd("->lx_pcm_close\n");
+	return err;
+}
+
+static snd_pcm_uframes_t lx_pcm_stream_pointer(struct snd_pcm_substream
+					       *substream)
+{
+	struct lx6464es *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_uframes_t pos;
+	unsigned long flags;
+	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+	struct lx_stream *lx_stream = is_capture ? &chip->capture_stream :
+		&chip->playback_stream;
+
+	snd_printdd("->lx_pcm_stream_pointer\n");
+
+	spin_lock_irqsave(&chip->lock, flags);
+	pos = lx_stream->frame_pos * substream->runtime->period_size;
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	snd_printdd(LXP "stream_pointer at %ld\n", pos);
+	return pos;
+}
+
+static int lx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct lx6464es *chip = snd_pcm_substream_chip(substream);
+	int err = 0;
+	const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+	snd_printdd("->lx_pcm_prepare\n");
+
+	mutex_lock(&chip->setup_mutex);
+
+	if (chip->hardware_running[is_capture]) {
+		err = lx_hardware_stop(chip, substream);
+		if (err < 0) {
+			snd_printk(KERN_ERR LXP "failed to stop hardware. "
+				   "Error code %d\n", err);
+			goto exit;
+		}
+
+		err = lx_hardware_close(chip, substream);
+		if (err < 0) {
+			snd_printk(KERN_ERR LXP "failed to close hardware. "
+				   "Error code %d\n", err);
+			goto exit;
+		}
+	}
+
+	snd_printd(LXP "opening hardware\n");
+	err = lx_hardware_open(chip, substream);
+	if (err < 0) {
+		snd_printk(KERN_ERR LXP "failed to open hardware. "
+			   "Error code %d\n", err);
+		goto exit;
+	}
+
+	err = lx_hardware_start(chip, substream);
+	if (err < 0) {
+		snd_printk(KERN_ERR LXP "failed to start hardware. "
+			   "Error code %d\n", err);
+		goto exit;
+	}
+
+	chip->hardware_running[is_capture] = 1;
+
+	if (chip->board_sample_rate != substream->runtime->rate) {
+		if (!err)
+			chip->board_sample_rate = substream->runtime->rate;
+	}
+
+exit:
+	mutex_unlock(&chip->setup_mutex);
+	return err;
+}
+
+static int lx_pcm_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *hw_params, int is_capture)
+{
+	struct lx6464es *chip = snd_pcm_substream_chip(substream);
+	int err = 0;
+
+	snd_printdd("->lx_pcm_hw_params\n");
+
+	mutex_lock(&chip->setup_mutex);
+
+	/* set dma buffer */
+	err = snd_pcm_lib_malloc_pages(substream,
+				       params_buffer_bytes(hw_params));
+
+	if (is_capture)
+		chip->capture_stream.stream = substream;
+	else
+		chip->playback_stream.stream = substream;
+
+	mutex_unlock(&chip->setup_mutex);
+	return err;
+}
+
+static int lx_pcm_hw_params_playback(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	return lx_pcm_hw_params(substream, hw_params, 0);
+}
+
+static int lx_pcm_hw_params_capture(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	return lx_pcm_hw_params(substream, hw_params, 1);
+}
+
+static int lx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct lx6464es *chip = snd_pcm_substream_chip(substream);
+	int err = 0;
+	int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+
+	snd_printdd("->lx_pcm_hw_free\n");
+	mutex_lock(&chip->setup_mutex);
+
+	if (chip->hardware_running[is_capture]) {
+		err = lx_hardware_stop(chip, substream);
+		if (err < 0) {
+			snd_printk(KERN_ERR LXP "failed to stop hardware. "
+				   "Error code %d\n", err);
+			goto exit;
+		}
+
+		err = lx_hardware_close(chip, substream);
+		if (err < 0) {
+			snd_printk(KERN_ERR LXP "failed to close hardware. "
+				   "Error code %d\n", err);
+			goto exit;
+		}
+
+		chip->hardware_running[is_capture] = 0;
+	}
+
+	err = snd_pcm_lib_free_pages(substream);
+
+	if (is_capture)
+		chip->capture_stream.stream = 0;
+	else
+		chip->playback_stream.stream = 0;
+
+exit:
+	mutex_unlock(&chip->setup_mutex);
+	return err;
+}
+
+static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream)
+{
+	struct snd_pcm_substream *substream = lx_stream->stream;
+	const int is_capture = lx_stream->is_capture;
+
+	int err;
+
+	const u32 channels = substream->runtime->channels;
+	const u32 bytes_per_frame = channels * 3;
+	const u32 period_size = substream->runtime->period_size;
+	const u32 periods = substream->runtime->periods;
+	const u32 period_bytes = period_size * bytes_per_frame;
+
+	dma_addr_t buf = substream->dma_buffer.addr;
+	int i;
+
+	u32 needed, freed;
+	u32 size_array[5];
+
+	for (i = 0; i != periods; ++i) {
+		u32 buffer_index = 0;
+
+		err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed,
+				    size_array);
+		snd_printdd(LXP "starting: needed %d, freed %d\n",
+			    needed, freed);
+
+		err = lx_buffer_give(chip, 0, is_capture, period_bytes,
+				     lower_32_bits(buf), upper_32_bits(buf),
+				     &buffer_index);
+
+		snd_printdd(LXP "starting: buffer index %x on %p (%d bytes)\n",
+			    buffer_index, (void *)buf, period_bytes);
+		buf += period_bytes;
+	}
+
+	err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
+	snd_printdd(LXP "starting: needed %d, freed %d\n", needed, freed);
+
+	snd_printd(LXP "starting: starting stream\n");
+	err = lx_stream_start(chip, 0, is_capture);
+	if (err < 0)
+		snd_printk(KERN_ERR LXP "couldn't start stream\n");
+	else
+		lx_stream->status = LX_STREAM_STATUS_RUNNING;
+
+	lx_stream->frame_pos = 0;
+}
+
+static void lx_trigger_stop(struct lx6464es *chip, struct lx_stream *lx_stream)
+{
+	const int is_capture = lx_stream->is_capture;
+	int err;
+
+	snd_printd(LXP "stopping: stopping stream\n");
+	err = lx_stream_stop(chip, 0, is_capture);
+	if (err < 0)
+		snd_printk(KERN_ERR LXP "couldn't stop stream\n");
+	else
+		lx_stream->status = LX_STREAM_STATUS_FREE;
+
+}
+
+static void lx_trigger_tasklet_dispatch_stream(struct lx6464es *chip,
+					       struct lx_stream *lx_stream)
+{
+	switch (lx_stream->status) {
+	case LX_STREAM_STATUS_SCHEDULE_RUN:
+		lx_trigger_start(chip, lx_stream);
+		break;
+
+	case LX_STREAM_STATUS_SCHEDULE_STOP:
+		lx_trigger_stop(chip, lx_stream);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void lx_trigger_tasklet(unsigned long data)
+{
+	struct lx6464es *chip = (struct lx6464es *)data;
+	unsigned long flags;
+
+	snd_printdd("->lx_trigger_tasklet\n");
+
+	spin_lock_irqsave(&chip->lock, flags);
+	lx_trigger_tasklet_dispatch_stream(chip, &chip->capture_stream);
+	lx_trigger_tasklet_dispatch_stream(chip, &chip->playback_stream);
+	spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+static int lx_pcm_trigger_dispatch(struct lx6464es *chip,
+				   struct lx_stream *lx_stream, int cmd)
+{
+	int err = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		lx_stream->status = LX_STREAM_STATUS_SCHEDULE_RUN;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		lx_stream->status = LX_STREAM_STATUS_SCHEDULE_STOP;
+		break;
+
+	default:
+		err = -EINVAL;
+		goto exit;
+	}
+	tasklet_schedule(&chip->trigger_tasklet);
+
+exit:
+	return err;
+}
+
+
+static int lx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct lx6464es *chip = snd_pcm_substream_chip(substream);
+	const int is_capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+	struct lx_stream *stream = is_capture ? &chip->capture_stream :
+		&chip->playback_stream;
+
+	snd_printdd("->lx_pcm_trigger\n");
+
+	return lx_pcm_trigger_dispatch(chip, stream, cmd);
+}
+
+static int snd_lx6464es_free(struct lx6464es *chip)
+{
+	snd_printdd("->snd_lx6464es_free\n");
+
+	lx_irq_disable(chip);
+
+	if (chip->irq >= 0)
+		free_irq(chip->irq, chip);
+
+	iounmap(chip->port_dsp_bar);
+	ioport_unmap(chip->port_plx_remapped);
+
+	pci_release_regions(chip->pci);
+	pci_disable_device(chip->pci);
+
+	kfree(chip);
+
+	return 0;
+}
+
+static int snd_lx6464es_dev_free(struct snd_device *device)
+{
+	return snd_lx6464es_free(device->device_data);
+}
+
+/* reset the dsp during initialization */
+static int __devinit lx_init_xilinx_reset(struct lx6464es *chip)
+{
+	int i;
+	u32 plx_reg = lx_plx_reg_read(chip, ePLX_CHIPSC);
+
+	snd_printdd("->lx_init_xilinx_reset\n");
+
+	/* activate reset of xilinx */
+	plx_reg &= ~CHIPSC_RESET_XILINX;
+
+	lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg);
+	msleep(1);
+
+	lx_plx_reg_write(chip, ePLX_MBOX3, 0);
+	msleep(1);
+
+	plx_reg |= CHIPSC_RESET_XILINX;
+	lx_plx_reg_write(chip, ePLX_CHIPSC, plx_reg);
+
+	/* deactivate reset of xilinx */
+	for (i = 0; i != 100; ++i) {
+		u32 reg_mbox3;
+		msleep(10);
+		reg_mbox3 = lx_plx_reg_read(chip, ePLX_MBOX3);
+		if (reg_mbox3) {
+			snd_printd(LXP "xilinx reset done\n");
+			snd_printdd(LXP "xilinx took %d loops\n", i);
+			break;
+		}
+	}
+
+	/* todo: add some error handling? */
+
+	/* clear mr */
+	lx_dsp_reg_write(chip, eReg_CSM, 0);
+
+	/* le xilinx ES peut ne pas etre encore pret, on attend. */
+	msleep(600);
+
+	return 0;
+}
+
+static int __devinit lx_init_xilinx_test(struct lx6464es *chip)
+{
+	u32 reg;
+
+	snd_printdd("->lx_init_xilinx_test\n");
+
+	/* TEST if we have access to Xilinx/MicroBlaze */
+	lx_dsp_reg_write(chip, eReg_CSM, 0);
+
+	reg = lx_dsp_reg_read(chip, eReg_CSM);
+
+	if (reg) {
+		snd_printk(KERN_ERR LXP "Problem: Reg_CSM %x.\n", reg);
+
+		/* PCI9056_SPACE0_REMAP */
+		lx_plx_reg_write(chip, ePLX_PCICR, 1);
+
+		reg = lx_dsp_reg_read(chip, eReg_CSM);
+		if (reg) {
+			snd_printk(KERN_ERR LXP "Error: Reg_CSM %x.\n", reg);
+			return -EAGAIN; /* seems to be appropriate */
+		}
+	}
+
+	snd_printd(LXP "Xilinx/MicroBlaze access test successful\n");
+
+	return 0;
+}
+
+/* initialize ethersound */
+static int __devinit lx_init_ethersound_config(struct lx6464es *chip)
+{
+	int i;
+	u32 orig_conf_es = lx_dsp_reg_read(chip, eReg_CONFES);
+
+	u32 default_conf_es = (64 << IOCR_OUTPUTS_OFFSET) |
+		(64 << IOCR_INPUTS_OFFSET) |
+		(FREQ_RATIO_SINGLE_MODE << FREQ_RATIO_OFFSET);
+
+	u32 conf_es = (orig_conf_es & CONFES_READ_PART_MASK)
+		| (default_conf_es & CONFES_WRITE_PART_MASK);
+
+	snd_printdd("->lx_init_ethersound\n");
+
+	chip->freq_ratio = FREQ_RATIO_SINGLE_MODE;
+
+	/*
+	 * write it to the card !
+	 * this actually kicks the ES xilinx, the first time since poweron.
+	 * the MAC address in the Reg_ADMACESMSB Reg_ADMACESLSB registers
+	 * is not ready before this is done, and the bit 2 in Reg_CSES is set.
+	 * */
+	lx_dsp_reg_write(chip, eReg_CONFES, conf_es);
+
+	for (i = 0; i != 1000; ++i) {
+		if (lx_dsp_reg_read(chip, eReg_CSES) & 4) {
+			snd_printd(LXP "ethersound initialized after %dms\n",
+				   i);
+			goto ethersound_initialized;
+		}
+		msleep(1);
+	}
+	snd_printk(KERN_WARNING LXP
+		   "ethersound could not be initialized after %dms\n", i);
+	return -ETIMEDOUT;
+
+ ethersound_initialized:
+	snd_printd(LXP "ethersound initialized\n");
+	return 0;
+}
+
+static int __devinit lx_init_get_version_features(struct lx6464es *chip)
+{
+	u32 dsp_version;
+
+	int err;
+
+	snd_printdd("->lx_init_get_version_features\n");
+
+	err = lx_dsp_get_version(chip, &dsp_version);
+
+	if (err == 0) {
+		u32 freq;
+
+		snd_printk(LXP "DSP version: V%02d.%02d #%d\n",
+			   (dsp_version>>16) & 0xff, (dsp_version>>8) & 0xff,
+			   dsp_version & 0xff);
+
+		/* later: what firmware version do we expect? */
+
+		/* retrieve Play/Rec features */
+		/* done here because we may have to handle alternate
+		 * DSP files. */
+		/* later */
+
+		/* init the EtherSound sample rate */
+		err = lx_dsp_get_clock_frequency(chip, &freq);
+		if (err == 0)
+			chip->board_sample_rate = freq;
+		snd_printd(LXP "actual clock frequency %d\n", freq);
+	} else {
+		snd_printk(KERN_ERR LXP "DSP corrupted \n");
+		err = -EAGAIN;
+	}
+
+	return err;
+}
+
+static int lx_set_granularity(struct lx6464es *chip, u32 gran)
+{
+	int err = 0;
+	u32 snapped_gran = MICROBLAZE_IBL_MIN;
+
+	snd_printdd("->lx_set_granularity\n");
+
+	/* blocksize is a power of 2 */
+	while ((snapped_gran < gran) &&
+	       (snapped_gran < MICROBLAZE_IBL_MAX)) {
+		snapped_gran *= 2;
+	}
+
+	if (snapped_gran == chip->pcm_granularity)
+		return 0;
+
+	err = lx_dsp_set_granularity(chip, snapped_gran);
+	if (err < 0) {
+		snd_printk(KERN_WARNING LXP "could not set granularity\n");
+		err = -EAGAIN;
+	}
+
+	if (snapped_gran != gran)
+		snd_printk(LXP "snapped blocksize to %d\n", snapped_gran);
+
+	snd_printd(LXP "set blocksize on board %d\n", snapped_gran);
+	chip->pcm_granularity = snapped_gran;
+
+	return err;
+}
+
+/* initialize and test the xilinx dsp chip */
+static int __devinit lx_init_dsp(struct lx6464es *chip)
+{
+	int err;
+	u8 mac_address[6];
+	int i;
+
+	snd_printdd("->lx_init_dsp\n");
+
+	snd_printd(LXP "initialize board\n");
+	err = lx_init_xilinx_reset(chip);
+	if (err)
+		return err;
+
+	snd_printd(LXP "testing board\n");
+	err = lx_init_xilinx_test(chip);
+	if (err)
+		return err;
+
+	snd_printd(LXP "initialize ethersound configuration\n");
+	err = lx_init_ethersound_config(chip);
+	if (err)
+		return err;
+
+	lx_irq_enable(chip);
+
+	/** \todo the mac address should be ready by not, but it isn't,
+	 *  so we wait for it */
+	for (i = 0; i != 1000; ++i) {
+		err = lx_dsp_get_mac(chip, mac_address);
+		if (err)
+			return err;
+		if (mac_address[0] || mac_address[1] || mac_address[2] ||
+		    mac_address[3] || mac_address[4] || mac_address[5])
+			goto mac_ready;
+		msleep(1);
+	}
+	return -ETIMEDOUT;
+
+mac_ready:
+	snd_printd(LXP "mac address ready read after: %dms\n", i);
+	snd_printk(LXP "mac address: %02X.%02X.%02X.%02X.%02X.%02X\n",
+		   mac_address[0], mac_address[1], mac_address[2],
+		   mac_address[3], mac_address[4], mac_address[5]);
+
+	err = lx_init_get_version_features(chip);
+	if (err)
+		return err;
+
+	lx_set_granularity(chip, MICROBLAZE_IBL_DEFAULT);
+
+	chip->playback_mute = 0;
+
+	return err;
+}
+
+static struct snd_pcm_ops lx_ops_playback = {
+	.open      = lx_pcm_open,
+	.close     = lx_pcm_close,
+	.ioctl     = snd_pcm_lib_ioctl,
+	.prepare   = lx_pcm_prepare,
+	.hw_params = lx_pcm_hw_params_playback,
+	.hw_free   = lx_pcm_hw_free,
+	.trigger   = lx_pcm_trigger,
+	.pointer   = lx_pcm_stream_pointer,
+};
+
+static struct snd_pcm_ops lx_ops_capture = {
+	.open      = lx_pcm_open,
+	.close     = lx_pcm_close,
+	.ioctl     = snd_pcm_lib_ioctl,
+	.prepare   = lx_pcm_prepare,
+	.hw_params = lx_pcm_hw_params_capture,
+	.hw_free   = lx_pcm_hw_free,
+	.trigger   = lx_pcm_trigger,
+	.pointer   = lx_pcm_stream_pointer,
+};
+
+static int __devinit lx_pcm_create(struct lx6464es *chip)
+{
+	int err;
+	struct snd_pcm *pcm;
+
+	u32 size = 64 *		     /* channels */
+		3 *		     /* 24 bit samples */
+		MAX_STREAM_BUFFER *  /* periods */
+		MICROBLAZE_IBL_MAX * /* frames per period */
+		2;		     /* duplex */
+
+	size = PAGE_ALIGN(size);
+
+	/* hardcoded device name & channel count */
+	err = snd_pcm_new(chip->card, (char *)card_name, 0,
+			  1, 1, &pcm);
+
+	pcm->private_data = chip;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &lx_ops_playback);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &lx_ops_capture);
+
+	pcm->info_flags = 0;
+	strcpy(pcm->name, card_name);
+
+	err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+						    snd_dma_pci_data(chip->pci),
+						    size, size);
+	if (err < 0)
+		return err;
+
+	chip->pcm = pcm;
+	chip->capture_stream.is_capture = 1;
+
+	return 0;
+}
+
+static int lx_control_playback_info(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int lx_control_playback_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct lx6464es *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = chip->playback_mute;
+	return 0;
+}
+
+static int lx_control_playback_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct lx6464es *chip = snd_kcontrol_chip(kcontrol);
+	int changed = 0;
+	int current_value = chip->playback_mute;
+
+	if (current_value != ucontrol->value.integer.value[0]) {
+		lx_level_unmute(chip, 0, !current_value);
+		chip->playback_mute = !current_value;
+		changed = 1;
+	}
+	return changed;
+}
+
+static struct snd_kcontrol_new lx_control_playback_switch __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "PCM Playback Switch",
+	.index = 0,
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.private_value = 0,
+	.info = lx_control_playback_info,
+	.get = lx_control_playback_get,
+	.put = lx_control_playback_put
+};
+
+
+
+static void lx_proc_levels_read(struct snd_info_entry *entry,
+				struct snd_info_buffer *buffer)
+{
+	u32 levels[64];
+	int err;
+	int i, j;
+	struct lx6464es *chip = entry->private_data;
+
+	snd_iprintf(buffer, "capture levels:\n");
+	err = lx_level_peaks(chip, 1, 64, levels);
+	if (err < 0)
+		return;
+
+	for (i = 0; i != 8; ++i) {
+		for (j = 0; j != 8; ++j)
+			snd_iprintf(buffer, "%08x ", levels[i*8+j]);
+		snd_iprintf(buffer, "\n");
+	}
+
+	snd_iprintf(buffer, "\nplayback levels:\n");
+
+	err = lx_level_peaks(chip, 0, 64, levels);
+	if (err < 0)
+		return;
+
+	for (i = 0; i != 8; ++i) {
+		for (j = 0; j != 8; ++j)
+			snd_iprintf(buffer, "%08x ", levels[i*8+j]);
+		snd_iprintf(buffer, "\n");
+	}
+
+	snd_iprintf(buffer, "\n");
+}
+
+static int __devinit lx_proc_create(struct snd_card *card, struct lx6464es *chip)
+{
+	struct snd_info_entry *entry;
+	int err = snd_card_proc_new(card, "levels", &entry);
+	if (err < 0)
+		return err;
+
+	snd_info_set_text_ops(entry, chip, lx_proc_levels_read);
+	return 0;
+}
+
+
+static int __devinit snd_lx6464es_create(struct snd_card *card,
+					 struct pci_dev *pci,
+					 struct lx6464es **rchip)
+{
+	struct lx6464es *chip;
+	int err;
+
+	static struct snd_device_ops ops = {
+		.dev_free = snd_lx6464es_dev_free,
+	};
+
+	snd_printdd("->snd_lx6464es_create\n");
+
+	*rchip = NULL;
+
+	/* enable PCI device */
+	err = pci_enable_device(pci);
+	if (err < 0)
+		return err;
+
+	pci_set_master(pci);
+
+	/* check if we can restrict PCI DMA transfers to 32 bits */
+	err = pci_set_dma_mask(pci, DMA_BIT_MASK(32));
+	if (err < 0) {
+		snd_printk(KERN_ERR "architecture does not support "
+			   "32bit PCI busmaster DMA\n");
+		pci_disable_device(pci);
+		return -ENXIO;
+	}
+
+	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+	if (chip == NULL) {
+		err = -ENOMEM;
+		goto alloc_failed;
+	}
+
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+
+	/* initialize synchronization structs */
+	spin_lock_init(&chip->lock);
+	spin_lock_init(&chip->msg_lock);
+	mutex_init(&chip->setup_mutex);
+	tasklet_init(&chip->trigger_tasklet, lx_trigger_tasklet,
+		     (unsigned long)chip);
+	tasklet_init(&chip->tasklet_capture, lx_tasklet_capture,
+		     (unsigned long)chip);
+	tasklet_init(&chip->tasklet_playback, lx_tasklet_playback,
+		     (unsigned long)chip);
+
+	/* request resources */
+	err = pci_request_regions(pci, card_name);
+	if (err < 0)
+		goto request_regions_failed;
+
+	/* plx port */
+	chip->port_plx = pci_resource_start(pci, 1);
+	chip->port_plx_remapped = ioport_map(chip->port_plx,
+					     pci_resource_len(pci, 1));
+
+	/* dsp port */
+	chip->port_dsp_bar = pci_ioremap_bar(pci, 2);
+
+	err = request_irq(pci->irq, lx_interrupt, IRQF_SHARED,
+			  card_name, chip);
+	if (err) {
+		snd_printk(KERN_ERR LXP "unable to grab IRQ %d\n", pci->irq);
+		goto request_irq_failed;
+	}
+	chip->irq = pci->irq;
+
+	err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+	if (err < 0)
+		goto device_new_failed;
+
+	err = lx_init_dsp(chip);
+	if (err < 0) {
+		snd_printk(KERN_ERR LXP "error during DSP initialization\n");
+		return err;
+	}
+
+	err = lx_pcm_create(chip);
+	if (err < 0)
+		return err;
+
+	err = lx_proc_create(card, chip);
+	if (err < 0)
+		return err;
+
+	err = snd_ctl_add(card, snd_ctl_new1(&lx_control_playback_switch,
+					     chip));
+	if (err < 0)
+		return err;
+
+	snd_card_set_dev(card, &pci->dev);
+
+	*rchip = chip;
+	return 0;
+
+device_new_failed:
+	free_irq(pci->irq, chip);
+
+request_irq_failed:
+	pci_release_regions(pci);
+
+request_regions_failed:
+	kfree(chip);
+
+alloc_failed:
+	pci_disable_device(pci);
+
+	return err;
+}
+
+static int __devinit snd_lx6464es_probe(struct pci_dev *pci,
+					const struct pci_device_id *pci_id)
+{
+	static int dev;
+	struct snd_card *card;
+	struct lx6464es *chip;
+	int err;
+
+	snd_printdd("->snd_lx6464es_probe\n");
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+	if (err < 0)
+		return err;
+
+	err = snd_lx6464es_create(card, pci, &chip);
+	if (err < 0) {
+		snd_printk(KERN_ERR LXP "error during snd_lx6464es_create\n");
+		goto out_free;
+	}
+
+	strcpy(card->driver, "lx6464es");
+	strcpy(card->shortname, "Digigram LX6464ES");
+	sprintf(card->longname, "%s at 0x%lx, 0x%p, irq %i",
+		card->shortname, chip->port_plx,
+		chip->port_dsp_bar, chip->irq);
+
+	err = snd_card_register(card);
+	if (err < 0)
+		goto out_free;
+
+	snd_printdd(LXP "initialization successful\n");
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+
+out_free:
+	snd_card_free(card);
+	return err;
+
+}
+
+static void __devexit snd_lx6464es_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+
+static struct pci_driver driver = {
+	.name =     "Digigram LX6464ES",
+	.id_table = snd_lx6464es_ids,
+	.probe =    snd_lx6464es_probe,
+	.remove = __devexit_p(snd_lx6464es_remove),
+};
+
+
+/* module initialization */
+static int __init mod_init(void)
+{
+	return pci_register_driver(&driver);
+}
+
+static void __exit mod_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
diff --git a/sound/pci/lx6464es/lx6464es.h b/sound/pci/lx6464es/lx6464es.h
new file mode 100644
index 0000000..012c010
--- /dev/null
+++ b/sound/pci/lx6464es/lx6464es.h
@@ -0,0 +1,114 @@
+/* -*- linux-c -*- *
+ *
+ * ALSA driver for the digigram lx6464es interface
+ *
+ * Copyright (c) 2009 Tim Blechmann <tim@klingt.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef LX6464ES_H
+#define LX6464ES_H
+
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+
+#include "lx_core.h"
+
+#define LXP "LX6464ES: "
+
+enum {
+    ES_cmd_free         = 0,    /* no command executing */
+    ES_cmd_processing   = 1,	/* execution of a read/write command */
+    ES_read_pending     = 2,    /* a asynchron read command is pending */
+    ES_read_finishing   = 3,    /* a read command has finished waiting (set by
+				 * Interrupt or CancelIrp) */
+};
+
+enum lx_stream_status {
+	LX_STREAM_STATUS_FREE,
+/* 	LX_STREAM_STATUS_OPEN, */
+	LX_STREAM_STATUS_SCHEDULE_RUN,
+/* 	LX_STREAM_STATUS_STARTED, */
+	LX_STREAM_STATUS_RUNNING,
+	LX_STREAM_STATUS_SCHEDULE_STOP,
+/* 	LX_STREAM_STATUS_STOPPED, */
+/* 	LX_STREAM_STATUS_PAUSED */
+};
+
+
+struct lx_stream {
+	struct snd_pcm_substream  *stream;
+	snd_pcm_uframes_t          frame_pos;
+	enum lx_stream_status      status; /* free, open, running, draining
+					    * pause */
+	int                        is_capture:1;
+};
+
+
+struct lx6464es {
+	struct snd_card        *card;
+	struct pci_dev         *pci;
+	int			irq;
+
+	spinlock_t		lock;        /* interrupt spinlock */
+	struct mutex            setup_mutex; /* mutex used in hw_params, open
+					      * and close */
+
+	struct tasklet_struct   trigger_tasklet; /* trigger tasklet */
+	struct tasklet_struct   tasklet_capture;
+	struct tasklet_struct   tasklet_playback;
+
+	/* ports */
+	unsigned long		port_plx;	   /* io port (size=256) */
+	void __iomem           *port_plx_remapped; /* remapped plx port */
+	void __iomem           *port_dsp_bar;      /* memory port (32-bit,
+						    * non-prefetchable,
+						    * size=8K) */
+
+	/* messaging */
+	spinlock_t		msg_lock;          /* message spinlock */
+	atomic_t	        send_message_locked;
+	struct lx_rmh           rmh;
+
+	/* configuration */
+	uint			freq_ratio : 2;
+	uint                    playback_mute : 1;
+	uint                    hardware_running[2];
+	u32                     board_sample_rate; /* sample rate read from
+						    * board */
+	u32                     sample_rate;	   /* our sample rate */
+	u16                     pcm_granularity;   /* board blocksize */
+
+	/* dma */
+	struct snd_dma_buffer   capture_dma_buf;
+	struct snd_dma_buffer   playback_dma_buf;
+
+	/* pcm */
+	struct snd_pcm         *pcm;
+
+	/* streams */
+	struct lx_stream        capture_stream;
+	struct lx_stream        playback_stream;
+};
+
+
+#endif /* LX6464ES_H */
diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c
new file mode 100644
index 0000000..5812780
--- /dev/null
+++ b/sound/pci/lx6464es/lx_core.c
@@ -0,0 +1,1444 @@
+/* -*- linux-c -*- *
+ *
+ * ALSA driver for the digigram lx6464es interface
+ * low-level interface
+ *
+ * Copyright (c) 2009 Tim Blechmann <tim@klingt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+/* #define RMH_DEBUG 1 */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include "lx6464es.h"
+#include "lx_core.h"
+
+/* low-level register access */
+
+static const unsigned long dsp_port_offsets[] = {
+	0,
+	0x400,
+	0x401,
+	0x402,
+	0x403,
+	0x404,
+	0x405,
+	0x406,
+	0x407,
+	0x408,
+	0x409,
+	0x40a,
+	0x40b,
+	0x40c,
+
+	0x410,
+	0x411,
+	0x412,
+	0x413,
+	0x414,
+	0x415,
+	0x416,
+
+	0x420,
+	0x430,
+	0x431,
+	0x432,
+	0x433,
+	0x434,
+	0x440
+};
+
+static void __iomem *lx_dsp_register(struct lx6464es *chip, int port)
+{
+	void __iomem *base_address = chip->port_dsp_bar;
+	return base_address + dsp_port_offsets[port]*4;
+}
+
+unsigned long lx_dsp_reg_read(struct lx6464es *chip, int port)
+{
+	void __iomem *address = lx_dsp_register(chip, port);
+	return ioread32(address);
+}
+
+void lx_dsp_reg_readbuf(struct lx6464es *chip, int port, u32 *data, u32 len)
+{
+	void __iomem *address = lx_dsp_register(chip, port);
+	memcpy_fromio(data, address, len*sizeof(u32));
+}
+
+
+void lx_dsp_reg_write(struct lx6464es *chip, int port, unsigned data)
+{
+	void __iomem *address = lx_dsp_register(chip, port);
+	iowrite32(data, address);
+}
+
+void lx_dsp_reg_writebuf(struct lx6464es *chip, int port, const u32 *data,
+			 u32 len)
+{
+	void __iomem *address = lx_dsp_register(chip, port);
+	memcpy_toio(address, data, len*sizeof(u32));
+}
+
+
+static const unsigned long plx_port_offsets[] = {
+	0x04,
+	0x40,
+	0x44,
+	0x48,
+	0x4c,
+	0x50,
+	0x54,
+	0x58,
+	0x5c,
+	0x64,
+	0x68,
+	0x6C
+};
+
+static void __iomem *lx_plx_register(struct lx6464es *chip, int port)
+{
+	void __iomem *base_address = chip->port_plx_remapped;
+	return base_address + plx_port_offsets[port];
+}
+
+unsigned long lx_plx_reg_read(struct lx6464es *chip, int port)
+{
+	void __iomem *address = lx_plx_register(chip, port);
+	return ioread32(address);
+}
+
+void lx_plx_reg_write(struct lx6464es *chip, int port, u32 data)
+{
+	void __iomem *address = lx_plx_register(chip, port);
+	iowrite32(data, address);
+}
+
+u32 lx_plx_mbox_read(struct lx6464es *chip, int mbox_nr)
+{
+	int index;
+
+	switch (mbox_nr) {
+	case 1:
+		index = ePLX_MBOX1;    break;
+	case 2:
+		index = ePLX_MBOX2;    break;
+	case 3:
+		index = ePLX_MBOX3;    break;
+	case 4:
+		index = ePLX_MBOX4;    break;
+	case 5:
+		index = ePLX_MBOX5;    break;
+	case 6:
+		index = ePLX_MBOX6;    break;
+	case 7:
+		index = ePLX_MBOX7;    break;
+	case 0:			/* reserved for HF flags */
+		snd_BUG();
+	default:
+		return 0xdeadbeef;
+	}
+
+	return lx_plx_reg_read(chip, index);
+}
+
+int lx_plx_mbox_write(struct lx6464es *chip, int mbox_nr, u32 value)
+{
+	int index = -1;
+
+	switch (mbox_nr) {
+	case 1:
+		index = ePLX_MBOX1;    break;
+	case 3:
+		index = ePLX_MBOX3;    break;
+	case 4:
+		index = ePLX_MBOX4;    break;
+	case 5:
+		index = ePLX_MBOX5;    break;
+	case 6:
+		index = ePLX_MBOX6;    break;
+	case 7:
+		index = ePLX_MBOX7;    break;
+	case 0:			/* reserved for HF flags */
+	case 2:			/* reserved for Pipe States
+				 * the DSP keeps an image of it */
+		snd_BUG();
+		return -EBADRQC;
+	}
+
+	lx_plx_reg_write(chip, index, value);
+	return 0;
+}
+
+
+/* rmh */
+
+#ifdef CONFIG_SND_DEBUG
+#define CMD_NAME(a) a
+#else
+#define CMD_NAME(a) NULL
+#endif
+
+#define Reg_CSM_MR			0x00000002
+#define Reg_CSM_MC			0x00000001
+
+struct dsp_cmd_info {
+	u32    dcCodeOp;	/* Op Code of the command (usually 1st 24-bits
+				 * word).*/
+	u16    dcCmdLength;	/* Command length in words of 24 bits.*/
+	u16    dcStatusType;	/* Status type: 0 for fixed length, 1 for
+				 * random. */
+	u16    dcStatusLength;	/* Status length (if fixed).*/
+	char  *dcOpName;
+};
+
+/*
+  Initialization and control data for the Microblaze interface
+  - OpCode:
+    the opcode field of the command set at the proper offset
+  - CmdLength
+    the number of command words
+  - StatusType
+    offset in the status registers: 0 means that the return value may be
+    different from 0, and must be read
+  - StatusLength
+    the number of status words (in addition to the return value)
+*/
+
+static struct dsp_cmd_info dsp_commands[] =
+{
+	{ (CMD_00_INFO_DEBUG << OPCODE_OFFSET)			, 1 /*custom*/
+	  , 1	, 0 /**/		    , CMD_NAME("INFO_DEBUG") },
+	{ (CMD_01_GET_SYS_CFG << OPCODE_OFFSET) 		, 1 /**/
+	  , 1      , 2 /**/		    , CMD_NAME("GET_SYS_CFG") },
+	{ (CMD_02_SET_GRANULARITY << OPCODE_OFFSET)	        , 1 /**/
+	  , 1      , 0 /**/		    , CMD_NAME("SET_GRANULARITY") },
+	{ (CMD_03_SET_TIMER_IRQ << OPCODE_OFFSET)		, 1 /**/
+	  , 1      , 0 /**/		    , CMD_NAME("SET_TIMER_IRQ") },
+	{ (CMD_04_GET_EVENT << OPCODE_OFFSET)			, 1 /**/
+	  , 1      , 0 /*up to 10*/     , CMD_NAME("GET_EVENT") },
+	{ (CMD_05_GET_PIPES << OPCODE_OFFSET)			, 1 /**/
+	  , 1      , 2 /*up to 4*/      , CMD_NAME("GET_PIPES") },
+	{ (CMD_06_ALLOCATE_PIPE << OPCODE_OFFSET)		, 1 /**/
+	  , 0      , 0 /**/		    , CMD_NAME("ALLOCATE_PIPE") },
+	{ (CMD_07_RELEASE_PIPE << OPCODE_OFFSET)		, 1 /**/
+	  , 0      , 0 /**/		    , CMD_NAME("RELEASE_PIPE") },
+	{ (CMD_08_ASK_BUFFERS << OPCODE_OFFSET) 		, 1 /**/
+	  , 1      , MAX_STREAM_BUFFER  , CMD_NAME("ASK_BUFFERS") },
+	{ (CMD_09_STOP_PIPE << OPCODE_OFFSET)			, 1 /**/
+	  , 0      , 0 /*up to 2*/      , CMD_NAME("STOP_PIPE") },
+	{ (CMD_0A_GET_PIPE_SPL_COUNT << OPCODE_OFFSET)	        , 1 /**/
+	  , 1      , 1 /*up to 2*/      , CMD_NAME("GET_PIPE_SPL_COUNT") },
+	{ (CMD_0B_TOGGLE_PIPE_STATE << OPCODE_OFFSET)           , 1 /*up to 5*/
+	  , 1      , 0 /**/		    , CMD_NAME("TOGGLE_PIPE_STATE") },
+	{ (CMD_0C_DEF_STREAM << OPCODE_OFFSET)			, 1 /*up to 4*/
+	  , 1      , 0 /**/		    , CMD_NAME("DEF_STREAM") },
+	{ (CMD_0D_SET_MUTE  << OPCODE_OFFSET)			, 3 /**/
+	  , 1      , 0 /**/		    , CMD_NAME("SET_MUTE") },
+	{ (CMD_0E_GET_STREAM_SPL_COUNT << OPCODE_OFFSET)        , 1/**/
+	  , 1      , 2 /**/		    , CMD_NAME("GET_STREAM_SPL_COUNT") },
+	{ (CMD_0F_UPDATE_BUFFER << OPCODE_OFFSET)		, 3 /*up to 4*/
+	  , 0      , 1 /**/		    , CMD_NAME("UPDATE_BUFFER") },
+	{ (CMD_10_GET_BUFFER << OPCODE_OFFSET)			, 1 /**/
+	  , 1      , 4 /**/		    , CMD_NAME("GET_BUFFER") },
+	{ (CMD_11_CANCEL_BUFFER << OPCODE_OFFSET)		, 1 /**/
+	  , 1      , 1 /*up to 4*/      , CMD_NAME("CANCEL_BUFFER") },
+	{ (CMD_12_GET_PEAK << OPCODE_OFFSET)			, 1 /**/
+	  , 1      , 1 /**/		    , CMD_NAME("GET_PEAK") },
+	{ (CMD_13_SET_STREAM_STATE << OPCODE_OFFSET)	        , 1 /**/
+	  , 1      , 0 /**/		    , CMD_NAME("SET_STREAM_STATE") },
+};
+
+static void lx_message_init(struct lx_rmh *rmh, enum cmd_mb_opcodes cmd)
+{
+	snd_BUG_ON(cmd >= CMD_14_INVALID);
+
+	rmh->cmd[0] = dsp_commands[cmd].dcCodeOp;
+	rmh->cmd_len = dsp_commands[cmd].dcCmdLength;
+	rmh->stat_len = dsp_commands[cmd].dcStatusLength;
+	rmh->dsp_stat = dsp_commands[cmd].dcStatusType;
+	rmh->cmd_idx = cmd;
+	memset(&rmh->cmd[1], 0, (REG_CRM_NUMBER - 1) * sizeof(u32));
+
+#ifdef CONFIG_SND_DEBUG
+	memset(rmh->stat, 0, REG_CRM_NUMBER * sizeof(u32));
+#endif
+#ifdef RMH_DEBUG
+	rmh->cmd_idx = cmd;
+#endif
+}
+
+#ifdef RMH_DEBUG
+#define LXRMH "lx6464es rmh: "
+static void lx_message_dump(struct lx_rmh *rmh)
+{
+	u8 idx = rmh->cmd_idx;
+	int i;
+
+	snd_printk(LXRMH "command %s\n", dsp_commands[idx].dcOpName);
+
+	for (i = 0; i != rmh->cmd_len; ++i)
+		snd_printk(LXRMH "\tcmd[%d] %08x\n", i, rmh->cmd[i]);
+
+	for (i = 0; i != rmh->stat_len; ++i)
+		snd_printk(LXRMH "\tstat[%d]: %08x\n", i, rmh->stat[i]);
+	snd_printk("\n");
+}
+#else
+static inline void lx_message_dump(struct lx_rmh *rmh)
+{}
+#endif
+
+
+
+/* sleep 500 - 100 = 400 times 100us -> the timeout is >= 40 ms */
+#define XILINX_TIMEOUT_MS       40
+#define XILINX_POLL_NO_SLEEP    100
+#define XILINX_POLL_ITERATIONS  150
+
+#if 0 /* not used now */
+static int lx_message_send(struct lx6464es *chip, struct lx_rmh *rmh)
+{
+	u32 reg = ED_DSP_TIMED_OUT;
+	int dwloop;
+	int answer_received;
+
+	if (lx_dsp_reg_read(chip, eReg_CSM) & (Reg_CSM_MC | Reg_CSM_MR)) {
+		snd_printk(KERN_ERR LXP "PIOSendMessage eReg_CSM %x\n", reg);
+		return -EBUSY;
+	}
+
+	/* write command */
+	lx_dsp_reg_writebuf(chip, eReg_CRM1, rmh->cmd, rmh->cmd_len);
+
+	snd_BUG_ON(atomic_read(&chip->send_message_locked) != 0);
+	atomic_set(&chip->send_message_locked, 1);
+
+	/* MicoBlaze gogogo */
+	lx_dsp_reg_write(chip, eReg_CSM, Reg_CSM_MC);
+
+	/* wait for interrupt to answer */
+	for (dwloop = 0; dwloop != XILINX_TIMEOUT_MS; ++dwloop) {
+		answer_received = atomic_read(&chip->send_message_locked);
+		if (answer_received == 0)
+			break;
+		msleep(1);
+	}
+
+	if (answer_received == 0) {
+		/* in Debug mode verify Reg_CSM_MR */
+		snd_BUG_ON(!(lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR));
+
+		/* command finished, read status */
+		if (rmh->dsp_stat == 0)
+			reg = lx_dsp_reg_read(chip, eReg_CRM1);
+		else
+			reg = 0;
+	} else {
+		int i;
+		snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send! "
+			   "Interrupts disabled?\n");
+
+		/* attente bit Reg_CSM_MR */
+		for (i = 0; i != XILINX_POLL_ITERATIONS; i++) {
+			if ((lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR)) {
+				if (rmh->dsp_stat == 0)
+					reg = lx_dsp_reg_read(chip, eReg_CRM1);
+				else
+					reg = 0;
+				goto polling_successful;
+			}
+
+			if (i > XILINX_POLL_NO_SLEEP)
+				msleep(1);
+		}
+		snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send! "
+			   "polling failed\n");
+
+polling_successful:
+		atomic_set(&chip->send_message_locked, 0);
+	}
+
+	if ((reg & ERROR_VALUE) == 0) {
+		/* read response */
+		if (rmh->stat_len) {
+			snd_BUG_ON(rmh->stat_len >= (REG_CRM_NUMBER-1));
+
+			lx_dsp_reg_readbuf(chip, eReg_CRM2, rmh->stat,
+					   rmh->stat_len);
+		}
+	} else
+		snd_printk(KERN_WARNING LXP "lx_message_send: error_value %x\n",
+			   reg);
+
+	/* clear Reg_CSM_MR */
+	lx_dsp_reg_write(chip, eReg_CSM, 0);
+
+	switch (reg) {
+	case ED_DSP_TIMED_OUT:
+		snd_printk(KERN_WARNING LXP "lx_message_send: dsp timeout\n");
+		return -ETIMEDOUT;
+
+	case ED_DSP_CRASHED:
+		snd_printk(KERN_WARNING LXP "lx_message_send: dsp crashed\n");
+		return -EAGAIN;
+	}
+
+	lx_message_dump(rmh);
+	return 0;
+}
+#endif /* not used now */
+
+static int lx_message_send_atomic(struct lx6464es *chip, struct lx_rmh *rmh)
+{
+	u32 reg = ED_DSP_TIMED_OUT;
+	int dwloop;
+
+	if (lx_dsp_reg_read(chip, eReg_CSM) & (Reg_CSM_MC | Reg_CSM_MR)) {
+		snd_printk(KERN_ERR LXP "PIOSendMessage eReg_CSM %x\n", reg);
+		return -EBUSY;
+	}
+
+	/* write command */
+	lx_dsp_reg_writebuf(chip, eReg_CRM1, rmh->cmd, rmh->cmd_len);
+
+	/* MicoBlaze gogogo */
+	lx_dsp_reg_write(chip, eReg_CSM, Reg_CSM_MC);
+
+	/* wait for interrupt to answer */
+	for (dwloop = 0; dwloop != XILINX_TIMEOUT_MS * 1000; ++dwloop) {
+		if (lx_dsp_reg_read(chip, eReg_CSM) & Reg_CSM_MR) {
+			if (rmh->dsp_stat == 0)
+				reg = lx_dsp_reg_read(chip, eReg_CRM1);
+			else
+				reg = 0;
+			goto polling_successful;
+		} else
+			udelay(1);
+	}
+	snd_printk(KERN_WARNING LXP "TIMEOUT lx_message_send_atomic! "
+		   "polling failed\n");
+
+polling_successful:
+	if ((reg & ERROR_VALUE) == 0) {
+		/* read response */
+		if (rmh->stat_len) {
+			snd_BUG_ON(rmh->stat_len >= (REG_CRM_NUMBER-1));
+			lx_dsp_reg_readbuf(chip, eReg_CRM2, rmh->stat,
+					   rmh->stat_len);
+		}
+	} else
+		snd_printk(LXP "rmh error: %08x\n", reg);
+
+	/* clear Reg_CSM_MR */
+	lx_dsp_reg_write(chip, eReg_CSM, 0);
+
+	switch (reg) {
+	case ED_DSP_TIMED_OUT:
+		snd_printk(KERN_WARNING LXP "lx_message_send: dsp timeout\n");
+		return -ETIMEDOUT;
+
+	case ED_DSP_CRASHED:
+		snd_printk(KERN_WARNING LXP "lx_message_send: dsp crashed\n");
+		return -EAGAIN;
+	}
+
+	lx_message_dump(rmh);
+
+	return reg;
+}
+
+
+/* low-level dsp access */
+int __devinit lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version)
+{
+	u16 ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+
+	lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG);
+	ret = lx_message_send_atomic(chip, &chip->rmh);
+
+	*rdsp_version = chip->rmh.stat[1];
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return ret;
+}
+
+int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq)
+{
+	u16 ret = 0;
+	unsigned long flags;
+	u32 freq_raw = 0;
+	u32 freq = 0;
+	u32 frequency = 0;
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+
+	lx_message_init(&chip->rmh, CMD_01_GET_SYS_CFG);
+	ret = lx_message_send_atomic(chip, &chip->rmh);
+
+	if (ret == 0) {
+		freq_raw = chip->rmh.stat[0] >> FREQ_FIELD_OFFSET;
+		freq = freq_raw & XES_FREQ_COUNT8_MASK;
+
+		if ((freq < XES_FREQ_COUNT8_48_MAX) ||
+		    (freq > XES_FREQ_COUNT8_44_MIN))
+			frequency = 0; /* unknown */
+		else if (freq >= XES_FREQ_COUNT8_44_MAX)
+			frequency = 44100;
+		else
+			frequency = 48000;
+	}
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+
+	*rfreq = frequency * chip->freq_ratio;
+
+	return ret;
+}
+
+int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address)
+{
+	u32 macmsb, maclsb;
+
+	macmsb = lx_dsp_reg_read(chip, eReg_ADMACESMSB) & 0x00FFFFFF;
+	maclsb = lx_dsp_reg_read(chip, eReg_ADMACESLSB) & 0x00FFFFFF;
+
+	/* todo: endianess handling */
+	mac_address[5] = ((u8 *)(&maclsb))[0];
+	mac_address[4] = ((u8 *)(&maclsb))[1];
+	mac_address[3] = ((u8 *)(&maclsb))[2];
+	mac_address[2] = ((u8 *)(&macmsb))[0];
+	mac_address[1] = ((u8 *)(&macmsb))[1];
+	mac_address[0] = ((u8 *)(&macmsb))[2];
+
+	return 0;
+}
+
+
+int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+
+	lx_message_init(&chip->rmh, CMD_02_SET_GRANULARITY);
+	chip->rmh.cmd[0] |= gran;
+
+	ret = lx_message_send_atomic(chip, &chip->rmh);
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return ret;
+}
+
+int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+
+	lx_message_init(&chip->rmh, CMD_04_GET_EVENT);
+	chip->rmh.stat_len = 9;	/* we don't necessarily need the full length */
+
+	ret = lx_message_send_atomic(chip, &chip->rmh);
+
+	if (!ret)
+		memcpy(data, chip->rmh.stat, chip->rmh.stat_len * sizeof(u32));
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return ret;
+}
+
+#define CSES_TIMEOUT        100     /* microseconds */
+#define CSES_CE             0x0001
+#define CSES_BROADCAST      0x0002
+#define CSES_UPDATE_LDSV    0x0004
+
+int lx_dsp_es_check_pipeline(struct lx6464es *chip)
+{
+	int i;
+
+	for (i = 0; i != CSES_TIMEOUT; ++i) {
+		/*
+		 * le bit CSES_UPDATE_LDSV est à 1 dés que le macprog
+		 * est pret. il re-passe à 0 lorsque le premier read a
+		 * été fait. pour l'instant on retire le test car ce bit
+		 * passe a 1 environ 200 à 400 ms aprés que le registre
+		 * confES à été écrit (kick du xilinx ES).
+		 *
+		 * On ne teste que le bit CE.
+		 * */
+
+		u32 cses = lx_dsp_reg_read(chip, eReg_CSES);
+
+		if ((cses & CSES_CE) == 0)
+			return 0;
+
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+
+#define PIPE_INFO_TO_CMD(capture, pipe)					\
+	((u32)((u32)(pipe) | ((capture) ? ID_IS_CAPTURE : 0L)) << ID_OFFSET)
+
+
+
+/* low-level pipe handling */
+int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture,
+		     int channels)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_06_ALLOCATE_PIPE);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+	chip->rmh.cmd[0] |= channels;
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+
+	if (err != 0)
+		snd_printk(KERN_ERR "lx6464es: could not allocate pipe\n");
+
+	return err;
+}
+
+int lx_pipe_release(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_07_RELEASE_PIPE);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+
+	return err;
+}
+
+int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
+		  u32 *r_needed, u32 *r_freed, u32 *size_array)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+#ifdef CONFIG_SND_DEBUG
+	if (size_array)
+		memset(size_array, 0, sizeof(u32)*MAX_STREAM_BUFFER);
+#endif
+
+	*r_needed = 0;
+	*r_freed = 0;
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_08_ASK_BUFFERS);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+
+	if (!err) {
+		int i;
+		for (i = 0; i < MAX_STREAM_BUFFER; ++i) {
+			u32 stat = chip->rmh.stat[i];
+			if (stat & (BF_EOB << BUFF_FLAGS_OFFSET)) {
+				/* finished */
+				*r_freed += 1;
+				if (size_array)
+					size_array[i] = stat & MASK_DATA_SIZE;
+			} else if ((stat & (BF_VALID << BUFF_FLAGS_OFFSET))
+				   == 0)
+				/* free */
+				*r_needed += 1;
+		}
+
+#if 0
+		snd_printdd(LXP "CMD_08_ASK_BUFFERS: needed %d, freed %d\n",
+			    *r_needed, *r_freed);
+		for (i = 0; i < MAX_STREAM_BUFFER; ++i) {
+			for (i = 0; i != chip->rmh.stat_len; ++i)
+				snd_printdd("  stat[%d]: %x, %x\n", i,
+					    chip->rmh.stat[i],
+					    chip->rmh.stat[i] & MASK_DATA_SIZE);
+		}
+#endif
+	}
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+
+int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_09_STOP_PIPE);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+static int lx_pipe_toggle_state(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_0B_TOGGLE_PIPE_STATE);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+
+int lx_pipe_start(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+	int err;
+
+	err = lx_pipe_wait_for_idle(chip, pipe, is_capture);
+	if (err < 0)
+		return err;
+
+	err = lx_pipe_toggle_state(chip, pipe, is_capture);
+
+	return err;
+}
+
+int lx_pipe_pause(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+	int err = 0;
+
+	err = lx_pipe_wait_for_start(chip, pipe, is_capture);
+	if (err < 0)
+		return err;
+
+	err = lx_pipe_toggle_state(chip, pipe, is_capture);
+
+	return err;
+}
+
+
+int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
+			 u64 *rsample_count)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+	chip->rmh.stat_len = 2;	/* need all words here! */
+
+	err = lx_message_send_atomic(chip, &chip->rmh); /* don't sleep! */
+
+	if (err != 0)
+		snd_printk(KERN_ERR
+			   "lx6464es: could not query pipe's sample count\n");
+	else {
+		*rsample_count = ((u64)(chip->rmh.stat[0] & MASK_SPL_COUNT_HI)
+				  << 24)     /* hi part */
+			+ chip->rmh.stat[1]; /* lo part */
+	}
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_0A_GET_PIPE_SPL_COUNT);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+
+	if (err != 0)
+		snd_printk(KERN_ERR "lx6464es: could not query pipe's state\n");
+	else
+		*rstate = (chip->rmh.stat[0] >> PSTATE_OFFSET) & 0x0F;
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+static int lx_pipe_wait_for_state(struct lx6464es *chip, u32 pipe,
+				  int is_capture, u16 state)
+{
+	int i;
+
+	/* max 2*PCMOnlyGranularity = 2*1024 at 44100 = < 50 ms:
+	 * timeout 50 ms */
+	for (i = 0; i != 50; ++i) {
+		u16 current_state;
+		int err = lx_pipe_state(chip, pipe, is_capture, &current_state);
+
+		if (err < 0)
+			return err;
+
+		if (current_state == state)
+			return 0;
+
+		mdelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+int lx_pipe_wait_for_start(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+	return lx_pipe_wait_for_state(chip, pipe, is_capture, PSTATE_RUN);
+}
+
+int lx_pipe_wait_for_idle(struct lx6464es *chip, u32 pipe, int is_capture)
+{
+	return lx_pipe_wait_for_state(chip, pipe, is_capture, PSTATE_IDLE);
+}
+
+/* low-level stream handling */
+int lx_stream_set_state(struct lx6464es *chip, u32 pipe,
+			       int is_capture, enum stream_state_t state)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_13_SET_STREAM_STATE);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+	chip->rmh.cmd[0] |= state;
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+
+	return err;
+}
+
+int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime,
+			 u32 pipe, int is_capture)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	u32 channels = runtime->channels;
+
+	if (runtime->channels != channels)
+		snd_printk(KERN_ERR LXP "channel count mismatch: %d vs %d",
+			   runtime->channels, channels);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_0C_DEF_STREAM);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+
+	if (runtime->sample_bits == 16)
+		/* 16 bit format */
+		chip->rmh.cmd[0] |= (STREAM_FMT_16b << STREAM_FMT_OFFSET);
+
+	if (snd_pcm_format_little_endian(runtime->format))
+		/* little endian/intel format */
+		chip->rmh.cmd[0] |= (STREAM_FMT_intel << STREAM_FMT_OFFSET);
+
+	chip->rmh.cmd[0] |= channels-1;
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+
+	return err;
+}
+
+int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
+		    int *rstate)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+
+	*rstate = (chip->rmh.stat[0] & SF_START) ? START_STATE : PAUSE_STATE;
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
+			      u64 *r_bytepos)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_0E_GET_STREAM_SPL_COUNT);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+
+	*r_bytepos = ((u64) (chip->rmh.stat[0] & MASK_SPL_COUNT_HI)
+		      << 32)	     /* hi part */
+		+ chip->rmh.stat[1]; /* lo part */
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+/* low-level buffer handling */
+int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
+		   u32 buffer_size, u32 buf_address_lo, u32 buf_address_hi,
+		   u32 *r_buffer_index)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_0F_UPDATE_BUFFER);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+	chip->rmh.cmd[0] |= BF_NOTIFY_EOB; /* request interrupt notification */
+
+	/* todo: pause request, circular buffer */
+
+	chip->rmh.cmd[1] = buffer_size & MASK_DATA_SIZE;
+	chip->rmh.cmd[2] = buf_address_lo;
+
+	if (buf_address_hi) {
+		chip->rmh.cmd_len = 4;
+		chip->rmh.cmd[3] = buf_address_hi;
+		chip->rmh.cmd[0] |= BF_64BITS_ADR;
+	}
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+
+	if (err == 0) {
+		*r_buffer_index = chip->rmh.stat[0];
+		goto done;
+	}
+
+	if (err == EB_RBUFFERS_TABLE_OVERFLOW)
+		snd_printk(LXP "lx_buffer_give EB_RBUFFERS_TABLE_OVERFLOW\n");
+
+	if (err == EB_INVALID_STREAM)
+		snd_printk(LXP "lx_buffer_give EB_INVALID_STREAM\n");
+
+	if (err == EB_CMD_REFUSED)
+		snd_printk(LXP "lx_buffer_give EB_CMD_REFUSED\n");
+
+ done:
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture,
+		   u32 *r_buffer_size)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+	chip->rmh.cmd[0] |= MASK_BUFFER_ID; /* ask for the current buffer: the
+					     * microblaze will seek for it */
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+
+	if (err == 0)
+		*r_buffer_size = chip->rmh.stat[0]  & MASK_DATA_SIZE;
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
+		     u32 buffer_index)
+{
+	int err;
+	unsigned long flags;
+
+	u32 pipe_cmd = PIPE_INFO_TO_CMD(is_capture, pipe);
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_11_CANCEL_BUFFER);
+
+	chip->rmh.cmd[0] |= pipe_cmd;
+	chip->rmh.cmd[0] |= buffer_index;
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+
+/* low-level gain/peak handling
+ *
+ * \todo: can we unmute capture/playback channels independently?
+ *
+ * */
+int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute)
+{
+	int err;
+	unsigned long flags;
+
+	/* bit set to 1: channel muted */
+	u64 mute_mask = unmute ? 0 : 0xFFFFFFFFFFFFFFFFLLU;
+
+	spin_lock_irqsave(&chip->msg_lock, flags);
+	lx_message_init(&chip->rmh, CMD_0D_SET_MUTE);
+
+	chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, 0);
+
+	chip->rmh.cmd[1] = (u32)(mute_mask >> (u64)32);	       /* hi part */
+	chip->rmh.cmd[2] = (u32)(mute_mask & (u64)0xFFFFFFFF); /* lo part */
+
+	snd_printk("mute %x %x %x\n", chip->rmh.cmd[0], chip->rmh.cmd[1],
+		   chip->rmh.cmd[2]);
+
+	err = lx_message_send_atomic(chip, &chip->rmh);
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+static u32 peak_map[] = {
+	0x00000109, /* -90.308dB */
+	0x0000083B, /* -72.247dB */
+	0x000020C4, /* -60.205dB */
+	0x00008273, /* -48.030dB */
+	0x00020756, /* -36.005dB */
+	0x00040C37, /* -30.001dB */
+	0x00081385, /* -24.002dB */
+	0x00101D3F, /* -18.000dB */
+	0x0016C310, /* -15.000dB */
+	0x002026F2, /* -12.001dB */
+	0x002D6A86, /* -9.000dB */
+	0x004026E6, /* -6.004dB */
+	0x005A9DF6, /* -3.000dB */
+	0x0065AC8B, /* -2.000dB */
+	0x00721481, /* -1.000dB */
+	0x007FFFFF, /* FS */
+};
+
+int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
+		   u32 *r_levels)
+{
+	int err = 0;
+	unsigned long flags;
+	int i;
+	spin_lock_irqsave(&chip->msg_lock, flags);
+
+	for (i = 0; i < channels; i += 4) {
+		u32 s0, s1, s2, s3;
+
+		lx_message_init(&chip->rmh, CMD_12_GET_PEAK);
+		chip->rmh.cmd[0] |= PIPE_INFO_TO_CMD(is_capture, i);
+
+		err = lx_message_send_atomic(chip, &chip->rmh);
+
+		if (err == 0) {
+			s0 = peak_map[chip->rmh.stat[0] & 0x0F];
+			s1 = peak_map[(chip->rmh.stat[0] >>  4) & 0xf];
+			s2 = peak_map[(chip->rmh.stat[0] >>  8) & 0xf];
+			s3 = peak_map[(chip->rmh.stat[0] >>  12) & 0xf];
+		} else
+			s0 = s1 = s2 = s3 = 0;
+
+		r_levels[0] = s0;
+		r_levels[1] = s1;
+		r_levels[2] = s2;
+		r_levels[3] = s3;
+
+		r_levels += 4;
+	}
+
+	spin_unlock_irqrestore(&chip->msg_lock, flags);
+	return err;
+}
+
+/* interrupt handling */
+#define PCX_IRQ_NONE 0
+#define IRQCS_ACTIVE_PCIDB  0x00002000L         /* Bit nø 13 */
+#define IRQCS_ENABLE_PCIIRQ 0x00000100L         /* Bit nø 08 */
+#define IRQCS_ENABLE_PCIDB  0x00000200L         /* Bit nø 09 */
+
+static u32 lx_interrupt_test_ack(struct lx6464es *chip)
+{
+	u32 irqcs = lx_plx_reg_read(chip, ePLX_IRQCS);
+
+	/* Test if PCI Doorbell interrupt is active */
+	if (irqcs & IRQCS_ACTIVE_PCIDB)	{
+		u32 temp;
+		irqcs = PCX_IRQ_NONE;
+
+		while ((temp = lx_plx_reg_read(chip, ePLX_L2PCIDB))) {
+			/* RAZ interrupt */
+			irqcs |= temp;
+			lx_plx_reg_write(chip, ePLX_L2PCIDB, temp);
+		}
+
+		return irqcs;
+	}
+	return PCX_IRQ_NONE;
+}
+
+static int lx_interrupt_ack(struct lx6464es *chip, u32 *r_irqsrc,
+			    int *r_async_pending, int *r_async_escmd)
+{
+	u32 irq_async;
+	u32 irqsrc = lx_interrupt_test_ack(chip);
+
+	if (irqsrc == PCX_IRQ_NONE)
+		return 0;
+
+	*r_irqsrc = irqsrc;
+
+	irq_async = irqsrc & MASK_SYS_ASYNC_EVENTS; /* + EtherSound response
+						     * (set by xilinx) + EOB */
+
+	if (irq_async & MASK_SYS_STATUS_ESA) {
+		irq_async &= ~MASK_SYS_STATUS_ESA;
+		*r_async_escmd = 1;
+	}
+
+	if (irqsrc & MASK_SYS_STATUS_CMD_DONE)
+		/* xilinx command notification */
+		atomic_set(&chip->send_message_locked, 0);
+
+	if (irq_async) {
+		/* snd_printd("interrupt: async event pending\n"); */
+		*r_async_pending = 1;
+	}
+
+	return 1;
+}
+
+static int lx_interrupt_handle_async_events(struct lx6464es *chip, u32 irqsrc,
+					    int *r_freq_changed,
+					    u64 *r_notified_in_pipe_mask,
+					    u64 *r_notified_out_pipe_mask)
+{
+	int err;
+	u32 stat[9];		/* answer from CMD_04_GET_EVENT */
+
+	/* On peut optimiser pour ne pas lire les evenements vides
+	 * les mots de rÃĩponse sont dans l'ordre suivant :
+	 * Stat[0]	mot de status gÃĩnÃĩral
+	 * Stat[1]	fin de buffer OUT pF
+	 * Stat[2]	fin de buffer OUT pf
+	 * Stat[3]	fin de buffer IN pF
+	 * Stat[4]	fin de buffer IN pf
+	 * Stat[5]	underrun poid fort
+	 * Stat[6]	underrun poid faible
+	 * Stat[7]	overrun poid fort
+	 * Stat[8]	overrun poid faible
+	 * */
+
+	u64 orun_mask;
+	u64 urun_mask;
+#if 0
+	int has_underrun   = (irqsrc & MASK_SYS_STATUS_URUN) ? 1 : 0;
+	int has_overrun    = (irqsrc & MASK_SYS_STATUS_ORUN) ? 1 : 0;
+#endif
+	int eb_pending_out = (irqsrc & MASK_SYS_STATUS_EOBO) ? 1 : 0;
+	int eb_pending_in  = (irqsrc & MASK_SYS_STATUS_EOBI) ? 1 : 0;
+
+	*r_freq_changed = (irqsrc & MASK_SYS_STATUS_FREQ) ? 1 : 0;
+
+	err = lx_dsp_read_async_events(chip, stat);
+	if (err < 0)
+		return err;
+
+	if (eb_pending_in) {
+		*r_notified_in_pipe_mask = ((u64)stat[3] << 32)
+			+ stat[4];
+		snd_printdd(LXP "interrupt: EOBI pending %llx\n",
+			    *r_notified_in_pipe_mask);
+	}
+	if (eb_pending_out) {
+		*r_notified_out_pipe_mask = ((u64)stat[1] << 32)
+			+ stat[2];
+		snd_printdd(LXP "interrupt: EOBO pending %llx\n",
+			    *r_notified_out_pipe_mask);
+	}
+
+	orun_mask = ((u64)stat[7] << 32) + stat[8];
+	urun_mask = ((u64)stat[5] << 32) + stat[6];
+
+	/* todo: handle xrun notification */
+
+	return err;
+}
+
+static int lx_interrupt_request_new_buffer(struct lx6464es *chip,
+					   struct lx_stream *lx_stream)
+{
+	struct snd_pcm_substream *substream = lx_stream->stream;
+	int is_capture = lx_stream->is_capture;
+	int err;
+	unsigned long flags;
+
+	const u32 channels = substream->runtime->channels;
+	const u32 bytes_per_frame = channels * 3;
+	const u32 period_size = substream->runtime->period_size;
+	const u32 period_bytes = period_size * bytes_per_frame;
+	const u32 pos = lx_stream->frame_pos;
+	const u32 next_pos = ((pos+1) == substream->runtime->periods) ?
+		0 : pos + 1;
+
+	dma_addr_t buf = substream->dma_buffer.addr + pos * period_bytes;
+	u32 buf_hi = 0;
+	u32 buf_lo = 0;
+	u32 buffer_index = 0;
+
+	u32 needed, freed;
+	u32 size_array[MAX_STREAM_BUFFER];
+
+	snd_printdd("->lx_interrupt_request_new_buffer\n");
+
+	spin_lock_irqsave(&chip->lock, flags);
+
+	err = lx_buffer_ask(chip, 0, is_capture, &needed, &freed, size_array);
+	snd_printdd(LXP "interrupt: needed %d, freed %d\n", needed, freed);
+
+	unpack_pointer(buf, &buf_lo, &buf_hi);
+	err = lx_buffer_give(chip, 0, is_capture, period_bytes, buf_lo, buf_hi,
+			     &buffer_index);
+	snd_printdd(LXP "interrupt: gave buffer index %x on %p (%d bytes)\n",
+		    buffer_index, (void *)buf, period_bytes);
+
+	lx_stream->frame_pos = next_pos;
+	spin_unlock_irqrestore(&chip->lock, flags);
+
+	return err;
+}
+
+void lx_tasklet_playback(unsigned long data)
+{
+	struct lx6464es *chip = (struct lx6464es *)data;
+	struct lx_stream *lx_stream = &chip->playback_stream;
+	int err;
+
+	snd_printdd("->lx_tasklet_playback\n");
+
+	err = lx_interrupt_request_new_buffer(chip, lx_stream);
+	if (err < 0)
+		snd_printk(KERN_ERR LXP
+			   "cannot request new buffer for playback\n");
+
+	snd_pcm_period_elapsed(lx_stream->stream);
+}
+
+void lx_tasklet_capture(unsigned long data)
+{
+	struct lx6464es *chip = (struct lx6464es *)data;
+	struct lx_stream *lx_stream = &chip->capture_stream;
+	int err;
+
+	snd_printdd("->lx_tasklet_capture\n");
+	err = lx_interrupt_request_new_buffer(chip, lx_stream);
+	if (err < 0)
+		snd_printk(KERN_ERR LXP
+			   "cannot request new buffer for capture\n");
+
+	snd_pcm_period_elapsed(lx_stream->stream);
+}
+
+
+
+static int lx_interrupt_handle_audio_transfer(struct lx6464es *chip,
+					      u64 notified_in_pipe_mask,
+					      u64 notified_out_pipe_mask)
+{
+	int err = 0;
+
+	if (notified_in_pipe_mask) {
+		snd_printdd(LXP "requesting audio transfer for capture\n");
+		tasklet_hi_schedule(&chip->tasklet_capture);
+	}
+
+	if (notified_out_pipe_mask) {
+		snd_printdd(LXP "requesting audio transfer for playback\n");
+		tasklet_hi_schedule(&chip->tasklet_playback);
+	}
+
+	return err;
+}
+
+
+irqreturn_t lx_interrupt(int irq, void *dev_id)
+{
+	struct lx6464es *chip = dev_id;
+	int async_pending, async_escmd;
+	u32 irqsrc;
+
+	spin_lock(&chip->lock);
+
+	snd_printdd("**************************************************\n");
+
+	if (!lx_interrupt_ack(chip, &irqsrc, &async_pending, &async_escmd)) {
+		spin_unlock(&chip->lock);
+		snd_printdd("IRQ_NONE\n");
+		return IRQ_NONE; /* this device did not cause the interrupt */
+	}
+
+	if (irqsrc & MASK_SYS_STATUS_CMD_DONE)
+		goto exit;
+
+#if 0
+	if (irqsrc & MASK_SYS_STATUS_EOBI)
+		snd_printdd(LXP "interrupt: EOBI\n");
+
+	if (irqsrc & MASK_SYS_STATUS_EOBO)
+		snd_printdd(LXP "interrupt: EOBO\n");
+
+	if (irqsrc & MASK_SYS_STATUS_URUN)
+		snd_printdd(LXP "interrupt: URUN\n");
+
+	if (irqsrc & MASK_SYS_STATUS_ORUN)
+		snd_printdd(LXP "interrupt: ORUN\n");
+#endif
+
+	if (async_pending) {
+		u64 notified_in_pipe_mask = 0;
+		u64 notified_out_pipe_mask = 0;
+		int freq_changed;
+		int err;
+
+		/* handle async events */
+		err = lx_interrupt_handle_async_events(chip, irqsrc,
+						       &freq_changed,
+						       &notified_in_pipe_mask,
+						       &notified_out_pipe_mask);
+		if (err)
+			snd_printk(KERN_ERR LXP
+				   "error handling async events\n");
+
+		err = lx_interrupt_handle_audio_transfer(chip,
+							 notified_in_pipe_mask,
+							 notified_out_pipe_mask
+			);
+		if (err)
+			snd_printk(KERN_ERR LXP
+				   "error during audio transfer\n");
+	}
+
+	if (async_escmd) {
+#if 0
+		/* backdoor for ethersound commands
+		 *
+		 * for now, we do not need this
+		 *
+		 * */
+
+		snd_printdd("lx6464es: interrupt requests escmd handling\n");
+#endif
+	}
+
+exit:
+	spin_unlock(&chip->lock);
+	return IRQ_HANDLED;	/* this device caused the interrupt */
+}
+
+
+static void lx_irq_set(struct lx6464es *chip, int enable)
+{
+	u32 reg = lx_plx_reg_read(chip, ePLX_IRQCS);
+
+	/* enable/disable interrupts
+	 *
+	 * Set the Doorbell and PCI interrupt enable bits
+	 *
+	 * */
+	if (enable)
+		reg |=  (IRQCS_ENABLE_PCIIRQ | IRQCS_ENABLE_PCIDB);
+	else
+		reg &= ~(IRQCS_ENABLE_PCIIRQ | IRQCS_ENABLE_PCIDB);
+	lx_plx_reg_write(chip, ePLX_IRQCS, reg);
+}
+
+void lx_irq_enable(struct lx6464es *chip)
+{
+	snd_printdd("->lx_irq_enable\n");
+	lx_irq_set(chip, 1);
+}
+
+void lx_irq_disable(struct lx6464es *chip)
+{
+	snd_printdd("->lx_irq_disable\n");
+	lx_irq_set(chip, 0);
+}
diff --git a/sound/pci/lx6464es/lx_core.h b/sound/pci/lx6464es/lx_core.h
new file mode 100644
index 0000000..6bd9cbb
--- /dev/null
+++ b/sound/pci/lx6464es/lx_core.h
@@ -0,0 +1,242 @@
+/* -*- linux-c -*- *
+ *
+ * ALSA driver for the digigram lx6464es interface
+ * low-level interface
+ *
+ * Copyright (c) 2009 Tim Blechmann <tim@klingt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef LX_CORE_H
+#define LX_CORE_H
+
+#include <linux/interrupt.h>
+
+#include "lx_defs.h"
+
+#define REG_CRM_NUMBER		12
+
+struct lx6464es;
+
+/* low-level register access */
+
+/* dsp register access */
+enum {
+	eReg_BASE,
+	eReg_CSM,
+	eReg_CRM1,
+	eReg_CRM2,
+	eReg_CRM3,
+	eReg_CRM4,
+	eReg_CRM5,
+	eReg_CRM6,
+	eReg_CRM7,
+	eReg_CRM8,
+	eReg_CRM9,
+	eReg_CRM10,
+	eReg_CRM11,
+	eReg_CRM12,
+
+	eReg_ICR,
+	eReg_CVR,
+	eReg_ISR,
+	eReg_RXHTXH,
+	eReg_RXMTXM,
+	eReg_RHLTXL,
+	eReg_RESETDSP,
+
+	eReg_CSUF,
+	eReg_CSES,
+	eReg_CRESMSB,
+	eReg_CRESLSB,
+	eReg_ADMACESMSB,
+	eReg_ADMACESLSB,
+	eReg_CONFES,
+
+	eMaxPortLx
+};
+
+unsigned long lx_dsp_reg_read(struct lx6464es *chip, int port);
+void lx_dsp_reg_readbuf(struct lx6464es *chip, int port, u32 *data, u32 len);
+void lx_dsp_reg_write(struct lx6464es *chip, int port, unsigned data);
+void lx_dsp_reg_writebuf(struct lx6464es *chip, int port, const u32 *data,
+			 u32 len);
+
+/* plx register access */
+enum {
+    ePLX_PCICR,
+
+    ePLX_MBOX0,
+    ePLX_MBOX1,
+    ePLX_MBOX2,
+    ePLX_MBOX3,
+    ePLX_MBOX4,
+    ePLX_MBOX5,
+    ePLX_MBOX6,
+    ePLX_MBOX7,
+
+    ePLX_L2PCIDB,
+    ePLX_IRQCS,
+    ePLX_CHIPSC,
+
+    eMaxPort
+};
+
+unsigned long lx_plx_reg_read(struct lx6464es *chip, int port);
+void lx_plx_reg_write(struct lx6464es *chip, int port, u32 data);
+
+/* rhm */
+struct lx_rmh {
+	u16	cmd_len;	/* length of the command to send (WORDs) */
+	u16	stat_len;	/* length of the status received (WORDs) */
+	u16	dsp_stat;	/* status type, RMP_SSIZE_XXX */
+	u16	cmd_idx;	/* index of the command */
+	u32	cmd[REG_CRM_NUMBER];
+	u32	stat[REG_CRM_NUMBER];
+};
+
+
+/* low-level dsp access */
+int __devinit lx_dsp_get_version(struct lx6464es *chip, u32 *rdsp_version);
+int lx_dsp_get_clock_frequency(struct lx6464es *chip, u32 *rfreq);
+int lx_dsp_set_granularity(struct lx6464es *chip, u32 gran);
+int lx_dsp_read_async_events(struct lx6464es *chip, u32 *data);
+int lx_dsp_get_mac(struct lx6464es *chip, u8 *mac_address);
+
+
+/* low-level pipe handling */
+int lx_pipe_allocate(struct lx6464es *chip, u32 pipe, int is_capture,
+		     int channels);
+int lx_pipe_release(struct lx6464es *chip, u32 pipe, int is_capture);
+int lx_pipe_sample_count(struct lx6464es *chip, u32 pipe, int is_capture,
+			 u64 *rsample_count);
+int lx_pipe_state(struct lx6464es *chip, u32 pipe, int is_capture, u16 *rstate);
+int lx_pipe_stop(struct lx6464es *chip, u32 pipe, int is_capture);
+int lx_pipe_start(struct lx6464es *chip, u32 pipe, int is_capture);
+int lx_pipe_pause(struct lx6464es *chip, u32 pipe, int is_capture);
+
+int lx_pipe_wait_for_start(struct lx6464es *chip, u32 pipe, int is_capture);
+int lx_pipe_wait_for_idle(struct lx6464es *chip, u32 pipe, int is_capture);
+
+/* low-level stream handling */
+int lx_stream_set_format(struct lx6464es *chip, struct snd_pcm_runtime *runtime,
+			 u32 pipe, int is_capture);
+int lx_stream_state(struct lx6464es *chip, u32 pipe, int is_capture,
+		    int *rstate);
+int lx_stream_sample_position(struct lx6464es *chip, u32 pipe, int is_capture,
+			      u64 *r_bytepos);
+
+int lx_stream_set_state(struct lx6464es *chip, u32 pipe,
+			int is_capture, enum stream_state_t state);
+
+static inline int lx_stream_start(struct lx6464es *chip, u32 pipe,
+				  int is_capture)
+{
+	snd_printdd("->lx_stream_start\n");
+	return lx_stream_set_state(chip, pipe, is_capture, SSTATE_RUN);
+}
+
+static inline int lx_stream_pause(struct lx6464es *chip, u32 pipe,
+				  int is_capture)
+{
+	snd_printdd("->lx_stream_pause\n");
+	return lx_stream_set_state(chip, pipe, is_capture, SSTATE_PAUSE);
+}
+
+static inline int lx_stream_stop(struct lx6464es *chip, u32 pipe,
+				 int is_capture)
+{
+	snd_printdd("->lx_stream_stop\n");
+	return lx_stream_set_state(chip, pipe, is_capture, SSTATE_STOP);
+}
+
+/* low-level buffer handling */
+int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
+		  u32 *r_needed, u32 *r_freed, u32 *size_array);
+int lx_buffer_give(struct lx6464es *chip, u32 pipe, int is_capture,
+		   u32 buffer_size, u32 buf_address_lo, u32 buf_address_hi,
+		   u32 *r_buffer_index);
+int lx_buffer_free(struct lx6464es *chip, u32 pipe, int is_capture,
+		   u32 *r_buffer_size);
+int lx_buffer_cancel(struct lx6464es *chip, u32 pipe, int is_capture,
+		     u32 buffer_index);
+
+/* low-level gain/peak handling */
+int lx_level_unmute(struct lx6464es *chip, int is_capture, int unmute);
+int lx_level_peaks(struct lx6464es *chip, int is_capture, int channels,
+		   u32 *r_levels);
+
+
+/* interrupt handling */
+irqreturn_t lx_interrupt(int irq, void *dev_id);
+void lx_irq_enable(struct lx6464es *chip);
+void lx_irq_disable(struct lx6464es *chip);
+
+void lx_tasklet_capture(unsigned long data);
+void lx_tasklet_playback(unsigned long data);
+
+
+/* Stream Format Header Defines (for LIN and IEEE754) */
+#define HEADER_FMT_BASE		HEADER_FMT_BASE_LIN
+#define HEADER_FMT_BASE_LIN	0xFED00000
+#define HEADER_FMT_BASE_FLOAT	0xFAD00000
+#define HEADER_FMT_MONO		0x00000080 /* bit 23 in header_lo. WARNING: old
+					    * bit 22 is ignored in float
+					    * format */
+#define HEADER_FMT_INTEL	0x00008000
+#define HEADER_FMT_16BITS	0x00002000
+#define HEADER_FMT_24BITS	0x00004000
+#define HEADER_FMT_UPTO11	0x00000200 /* frequency is less or equ. to 11k.
+					    * */
+#define HEADER_FMT_UPTO32	0x00000100 /* frequency is over 11k and less
+					    * then 32k.*/
+
+
+#define BIT_FMP_HEADER          23
+#define BIT_FMP_SD              22
+#define BIT_FMP_MULTICHANNEL    19
+
+#define START_STATE             1
+#define PAUSE_STATE             0
+
+
+
+
+
+/* from PcxAll_e.h */
+/* Start/Pause condition for pipes (PCXStartPipe, PCXPausePipe) */
+#define START_PAUSE_IMMEDIATE           0
+#define START_PAUSE_ON_SYNCHRO          1
+#define START_PAUSE_ON_TIME_CODE        2
+
+
+/* Pipe / Stream state */
+#define START_STATE             1
+#define PAUSE_STATE             0
+
+static inline void unpack_pointer(dma_addr_t ptr, u32 *r_low, u32 *r_high)
+{
+	*r_low = (u32)(ptr & 0xffffffff);
+#if BITS_PER_LONG == 32
+	*r_high = 0;
+#else
+	*r_high = (u32)((u64)ptr>>32);
+#endif
+}
+
+#endif /* LX_CORE_H */
diff --git a/sound/pci/lx6464es/lx_defs.h b/sound/pci/lx6464es/lx_defs.h
new file mode 100644
index 0000000..49d36bd
--- /dev/null
+++ b/sound/pci/lx6464es/lx_defs.h
@@ -0,0 +1,376 @@
+/* -*- linux-c -*- *
+ *
+ * ALSA driver for the digigram lx6464es interface
+ * adapted upstream headers
+ *
+ * Copyright (c) 2009 Tim Blechmann <tim@klingt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef LX_DEFS_H
+#define LX_DEFS_H
+
+/* code adapted from ethersound.h */
+#define	XES_FREQ_COUNT8_MASK    0x00001FFF /* compteur 25MHz entre 8 ech. */
+#define	XES_FREQ_COUNT8_44_MIN  0x00001288 /* 25M /
+					    * [ 44k - ( 44.1k + 48k ) / 2 ]
+					    * * 8 */
+#define	XES_FREQ_COUNT8_44_MAX	0x000010F0 /* 25M / [ ( 44.1k + 48k ) / 2 ]
+					    * * 8 */
+#define	XES_FREQ_COUNT8_48_MAX	0x00000F08 /* 25M /
+					    * [ 48k + ( 44.1k + 48k ) / 2 ]
+					    * * 8 */
+
+/* code adapted from LXES_registers.h */
+
+#define IOCR_OUTPUTS_OFFSET 0	/* (rw) offset for the number of OUTs in the
+				 * ConfES register. */
+#define IOCR_INPUTS_OFFSET  8	/* (rw) offset for the number of INs in the
+				 * ConfES register. */
+#define FREQ_RATIO_OFFSET  19	/* (rw) offset for frequency ratio in the
+				 * ConfES register. */
+#define	FREQ_RATIO_SINGLE_MODE 0x01 /* value for single mode frequency ratio:
+				     * sample rate = frequency rate. */
+
+#define CONFES_READ_PART_MASK	0x00070000
+#define CONFES_WRITE_PART_MASK	0x00F80000
+
+/* code adapted from if_drv_mb.h */
+
+#define MASK_SYS_STATUS_ERROR	(1L << 31) /* events that lead to a PCI irq if
+					    * not yet pending */
+#define MASK_SYS_STATUS_URUN	(1L << 30)
+#define MASK_SYS_STATUS_ORUN	(1L << 29)
+#define MASK_SYS_STATUS_EOBO	(1L << 28)
+#define MASK_SYS_STATUS_EOBI	(1L << 27)
+#define MASK_SYS_STATUS_FREQ	(1L << 26)
+#define MASK_SYS_STATUS_ESA	(1L << 25) /* reserved, this is set by the
+					    * XES */
+#define MASK_SYS_STATUS_TIMER	(1L << 24)
+
+#define MASK_SYS_ASYNC_EVENTS	(MASK_SYS_STATUS_ERROR |		\
+				 MASK_SYS_STATUS_URUN  |		\
+				 MASK_SYS_STATUS_ORUN  |		\
+				 MASK_SYS_STATUS_EOBO  |		\
+				 MASK_SYS_STATUS_EOBI  |		\
+				 MASK_SYS_STATUS_FREQ  |		\
+				 MASK_SYS_STATUS_ESA)
+
+#define MASK_SYS_PCI_EVENTS		(MASK_SYS_ASYNC_EVENTS |	\
+					 MASK_SYS_STATUS_TIMER)
+
+#define MASK_SYS_TIMER_COUNT	0x0000FFFF
+
+#define MASK_SYS_STATUS_EOT_PLX		(1L << 22) /* event that remains
+						    * internal: reserved fo end
+						    * of plx dma */
+#define MASK_SYS_STATUS_XES		(1L << 21) /* event that remains
+						    * internal: pending XES
+						    * IRQ */
+#define MASK_SYS_STATUS_CMD_DONE	(1L << 20) /* alternate command
+						    * management: notify driver
+						    * instead of polling */
+
+
+#define MAX_STREAM_BUFFER 5	/* max amount of stream buffers. */
+
+#define MICROBLAZE_IBL_MIN		 32
+#define MICROBLAZE_IBL_DEFAULT	        128
+#define MICROBLAZE_IBL_MAX		512
+/* #define MASK_GRANULARITY		(2*MICROBLAZE_IBL_MAX-1) */
+
+
+
+/* command opcodes, see reference for details */
+
+/*
+ the capture bit position in the object_id field in driver commands
+ depends upon the number of managed channels. For now, 64 IN + 64 OUT are
+ supported. HOwever, the communication protocol forsees 1024 channels, hence
+ bit 10 indicates a capture (input) object).
+*/
+#define ID_IS_CAPTURE (1L << 10)
+#define ID_OFFSET	13	/* object ID is at the 13th bit in the
+				 * 1st command word.*/
+#define ID_CH_MASK    0x3F
+#define OPCODE_OFFSET	24	/* offset of the command opcode in the first
+				 * command word.*/
+
+enum cmd_mb_opcodes {
+	CMD_00_INFO_DEBUG	        = 0x00,
+	CMD_01_GET_SYS_CFG		= 0x01,
+	CMD_02_SET_GRANULARITY		= 0x02,
+	CMD_03_SET_TIMER_IRQ		= 0x03,
+	CMD_04_GET_EVENT		= 0x04,
+	CMD_05_GET_PIPES		= 0x05,
+
+	CMD_06_ALLOCATE_PIPE            = 0x06,
+	CMD_07_RELEASE_PIPE		= 0x07,
+	CMD_08_ASK_BUFFERS		= 0x08,
+	CMD_09_STOP_PIPE		= 0x09,
+	CMD_0A_GET_PIPE_SPL_COUNT	= 0x0a,
+	CMD_0B_TOGGLE_PIPE_STATE	= 0x0b,
+
+	CMD_0C_DEF_STREAM		= 0x0c,
+	CMD_0D_SET_MUTE			= 0x0d,
+	CMD_0E_GET_STREAM_SPL_COUNT     = 0x0e,
+	CMD_0F_UPDATE_BUFFER		= 0x0f,
+	CMD_10_GET_BUFFER		= 0x10,
+	CMD_11_CANCEL_BUFFER		= 0x11,
+	CMD_12_GET_PEAK			= 0x12,
+	CMD_13_SET_STREAM_STATE		= 0x13,
+	CMD_14_INVALID			= 0x14,
+};
+
+/* pipe states */
+enum pipe_state_t {
+	PSTATE_IDLE	= 0,	/* the pipe is not processed in the XES_IRQ
+				 * (free or stopped, or paused). */
+	PSTATE_RUN	= 1,	/* sustained play/record state. */
+	PSTATE_PURGE	= 2,	/* the ES channels are now off, render pipes do
+				 * not DMA, record pipe do a last DMA. */
+	PSTATE_ACQUIRE	= 3,	/* the ES channels are now on, render pipes do
+				 * not yet increase their sample count, record
+				 * pipes do not DMA. */
+	PSTATE_CLOSING	= 4,	/* the pipe is releasing, and may not yet
+				 * receive an "alloc" command. */
+};
+
+/* stream states */
+enum stream_state_t {
+	SSTATE_STOP	=  0x00,       /* setting to stop resets the stream spl
+					* count.*/
+	SSTATE_RUN	= (0x01 << 0), /* start DMA and spl count handling. */
+	SSTATE_PAUSE	= (0x01 << 1), /* pause DMA and spl count handling. */
+};
+
+/* buffer flags */
+enum buffer_flags {
+	BF_VALID	= 0x80,	/* set if the buffer is valid, clear if free.*/
+	BF_CURRENT	= 0x40,	/* set if this is the current buffer (there is
+				 * always a current buffer).*/
+	BF_NOTIFY_EOB	= 0x20,	/* set if this buffer must cause a PCI event
+				 * when finished.*/
+	BF_CIRCULAR	= 0x10,	/* set if buffer[1] must be copied to buffer[0]
+				 * by the end of this buffer.*/
+	BF_64BITS_ADR	= 0x08,	/* set if the hi part of the address is valid.*/
+	BF_xx		= 0x04,	/* future extension.*/
+	BF_EOB		= 0x02,	/* set if finished, but not yet free.*/
+	BF_PAUSE	= 0x01,	/* pause stream at buffer end.*/
+	BF_ZERO		= 0x00,	/* no flags (init).*/
+};
+
+/**
+*	Stream Flags definitions
+*/
+enum stream_flags {
+	SF_ZERO		= 0x00000000, /* no flags (stream invalid). */
+	SF_VALID	= 0x10000000, /* the stream has a valid DMA_conf
+				       * info (setstreamformat). */
+	SF_XRUN		= 0x20000000, /* the stream is un x-run state. */
+	SF_START	= 0x40000000, /* the DMA is running.*/
+	SF_ASIO		= 0x80000000, /* ASIO.*/
+};
+
+
+#define MASK_SPL_COUNT_HI 0x00FFFFFF /* 4 MSBits are status bits */
+#define PSTATE_OFFSET             28 /* 4 MSBits are status bits */
+
+
+#define MASK_STREAM_HAS_MAPPING	(1L << 12)
+#define MASK_STREAM_IS_ASIO	(1L <<  9)
+#define STREAM_FMT_OFFSET	10   /* the stream fmt bits start at the 10th
+				      * bit in the command word. */
+
+#define STREAM_FMT_16b          0x02
+#define STREAM_FMT_intel        0x01
+
+#define FREQ_FIELD_OFFSET	15  /* offset of the freq field in the response
+				     * word */
+
+#define BUFF_FLAGS_OFFSET	  24 /*  offset of the buffer flags in the
+				      *  response word. */
+#define MASK_DATA_SIZE	  0x00FFFFFF /* this must match the field size of
+				      * datasize in the buffer_t structure. */
+
+#define MASK_BUFFER_ID	        0xFF /* the cancel command awaits a buffer ID,
+				      * may be 0xFF for "current". */
+
+
+/* code adapted from PcxErr_e.h */
+
+/* Bits masks */
+
+#define ERROR_MASK              0x8000
+
+#define SOURCE_MASK             0x7800
+
+#define E_SOURCE_BOARD          0x4000 /* 8 >> 1 */
+#define E_SOURCE_DRV            0x2000 /* 4 >> 1 */
+#define E_SOURCE_API            0x1000 /* 2 >> 1 */
+/* Error tools */
+#define E_SOURCE_TOOLS          0x0800 /* 1 >> 1 */
+/* Error pcxaudio */
+#define E_SOURCE_AUDIO          0x1800 /* 3 >> 1 */
+/* Error virtual pcx */
+#define E_SOURCE_VPCX           0x2800 /* 5 >> 1 */
+/* Error dispatcher */
+#define E_SOURCE_DISPATCHER     0x3000 /* 6 >> 1 */
+/* Error from CobraNet firmware */
+#define E_SOURCE_COBRANET       0x3800 /* 7 >> 1 */
+
+#define E_SOURCE_USER           0x7800
+
+#define CLASS_MASK              0x0700
+
+#define CODE_MASK               0x00FF
+
+/* Bits values */
+
+/* Values for the error/warning bit */
+#define ERROR_VALUE             0x8000
+#define WARNING_VALUE           0x0000
+
+/* Class values */
+#define E_CLASS_GENERAL                  0x0000
+#define E_CLASS_INVALID_CMD              0x0100
+#define E_CLASS_INVALID_STD_OBJECT       0x0200
+#define E_CLASS_RSRC_IMPOSSIBLE          0x0300
+#define E_CLASS_WRONG_CONTEXT            0x0400
+#define E_CLASS_BAD_SPECIFIC_PARAMETER   0x0500
+#define E_CLASS_REAL_TIME_ERROR          0x0600
+#define E_CLASS_DIRECTSHOW               0x0700
+#define E_CLASS_FREE                     0x0700
+
+
+/* Complete DRV error code for the general class */
+#define ED_GN           (ERROR_VALUE | E_SOURCE_DRV | E_CLASS_GENERAL)
+#define ED_CONCURRENCY                  (ED_GN | 0x01)
+#define ED_DSP_CRASHED                  (ED_GN | 0x02)
+#define ED_UNKNOWN_BOARD                (ED_GN | 0x03)
+#define ED_NOT_INSTALLED                (ED_GN | 0x04)
+#define ED_CANNOT_OPEN_SVC_MANAGER      (ED_GN | 0x05)
+#define ED_CANNOT_READ_REGISTRY         (ED_GN | 0x06)
+#define ED_DSP_VERSION_MISMATCH         (ED_GN | 0x07)
+#define ED_UNAVAILABLE_FEATURE          (ED_GN | 0x08)
+#define ED_CANCELLED                    (ED_GN | 0x09)
+#define ED_NO_RESPONSE_AT_IRQA          (ED_GN | 0x10)
+#define ED_INVALID_ADDRESS              (ED_GN | 0x11)
+#define ED_DSP_CORRUPTED                (ED_GN | 0x12)
+#define ED_PENDING_OPERATION            (ED_GN | 0x13)
+#define ED_NET_ALLOCATE_MEMORY_IMPOSSIBLE   (ED_GN | 0x14)
+#define ED_NET_REGISTER_ERROR               (ED_GN | 0x15)
+#define ED_NET_THREAD_ERROR                 (ED_GN | 0x16)
+#define ED_NET_OPEN_ERROR                   (ED_GN | 0x17)
+#define ED_NET_CLOSE_ERROR                  (ED_GN | 0x18)
+#define ED_NET_NO_MORE_PACKET               (ED_GN | 0x19)
+#define ED_NET_NO_MORE_BUFFER               (ED_GN | 0x1A)
+#define ED_NET_SEND_ERROR                   (ED_GN | 0x1B)
+#define ED_NET_RECEIVE_ERROR                (ED_GN | 0x1C)
+#define ED_NET_WRONG_MSG_SIZE               (ED_GN | 0x1D)
+#define ED_NET_WAIT_ERROR                   (ED_GN | 0x1E)
+#define ED_NET_EEPROM_ERROR                 (ED_GN | 0x1F)
+#define ED_INVALID_RS232_COM_NUMBER         (ED_GN | 0x20)
+#define ED_INVALID_RS232_INIT               (ED_GN | 0x21)
+#define ED_FILE_ERROR                       (ED_GN | 0x22)
+#define ED_INVALID_GPIO_CMD                 (ED_GN | 0x23)
+#define ED_RS232_ALREADY_OPENED             (ED_GN | 0x24)
+#define ED_RS232_NOT_OPENED                 (ED_GN | 0x25)
+#define ED_GPIO_ALREADY_OPENED              (ED_GN | 0x26)
+#define ED_GPIO_NOT_OPENED                  (ED_GN | 0x27)
+#define ED_REGISTRY_ERROR                   (ED_GN | 0x28) /* <- NCX */
+#define ED_INVALID_SERVICE                  (ED_GN | 0x29) /* <- NCX */
+
+#define ED_READ_FILE_ALREADY_OPENED	    (ED_GN | 0x2a) /* <- Decalage
+							    * pour RCX
+							    * (old 0x28)
+							    * */
+#define ED_READ_FILE_INVALID_COMMAND	    (ED_GN | 0x2b) /* ~ */
+#define ED_READ_FILE_INVALID_PARAMETER	    (ED_GN | 0x2c) /* ~ */
+#define ED_READ_FILE_ALREADY_CLOSED	    (ED_GN | 0x2d) /* ~ */
+#define ED_READ_FILE_NO_INFORMATION	    (ED_GN | 0x2e) /* ~ */
+#define ED_READ_FILE_INVALID_HANDLE	    (ED_GN | 0x2f) /* ~ */
+#define ED_READ_FILE_END_OF_FILE	    (ED_GN | 0x30) /* ~ */
+#define ED_READ_FILE_ERROR	            (ED_GN | 0x31) /* ~ */
+
+#define ED_DSP_CRASHED_EXC_DSPSTACK_OVERFLOW (ED_GN | 0x32) /* <- Decalage pour
+							     * PCX (old 0x14) */
+#define ED_DSP_CRASHED_EXC_SYSSTACK_OVERFLOW (ED_GN | 0x33) /* ~ */
+#define ED_DSP_CRASHED_EXC_ILLEGAL           (ED_GN | 0x34) /* ~ */
+#define ED_DSP_CRASHED_EXC_TIMER_REENTRY     (ED_GN | 0x35) /* ~ */
+#define ED_DSP_CRASHED_EXC_FATAL_ERROR       (ED_GN | 0x36) /* ~ */
+
+#define ED_FLASH_PCCARD_NOT_PRESENT          (ED_GN | 0x37)
+
+#define ED_NO_CURRENT_CLOCK                  (ED_GN | 0x38)
+
+/* Complete DRV error code for real time class */
+#define ED_RT           (ERROR_VALUE | E_SOURCE_DRV | E_CLASS_REAL_TIME_ERROR)
+#define ED_DSP_TIMED_OUT                (ED_RT | 0x01)
+#define ED_DSP_CHK_TIMED_OUT            (ED_RT | 0x02)
+#define ED_STREAM_OVERRUN               (ED_RT | 0x03)
+#define ED_DSP_BUSY                     (ED_RT | 0x04)
+#define ED_DSP_SEMAPHORE_TIME_OUT       (ED_RT | 0x05)
+#define ED_BOARD_TIME_OUT               (ED_RT | 0x06)
+#define ED_XILINX_ERROR                 (ED_RT | 0x07)
+#define ED_COBRANET_ITF_NOT_RESPONDING  (ED_RT | 0x08)
+
+/* Complete BOARD error code for the invaid standard object class */
+#define EB_ISO          (ERROR_VALUE | E_SOURCE_BOARD | \
+			 E_CLASS_INVALID_STD_OBJECT)
+#define EB_INVALID_EFFECT               (EB_ISO | 0x00)
+#define EB_INVALID_PIPE                 (EB_ISO | 0x40)
+#define EB_INVALID_STREAM               (EB_ISO | 0x80)
+#define EB_INVALID_AUDIO                (EB_ISO | 0xC0)
+
+/* Complete BOARD error code for impossible resource allocation class */
+#define EB_RI           (ERROR_VALUE | E_SOURCE_BOARD | E_CLASS_RSRC_IMPOSSIBLE)
+#define EB_ALLOCATE_ALL_STREAM_TRANSFERT_BUFFERS_IMPOSSIBLE (EB_RI | 0x01)
+#define EB_ALLOCATE_PIPE_SAMPLE_BUFFER_IMPOSSIBLE           (EB_RI | 0x02)
+
+#define EB_ALLOCATE_MEM_STREAM_IMPOSSIBLE		\
+	EB_ALLOCATE_ALL_STREAM_TRANSFERT_BUFFERS_IMPOSSIBLE
+#define EB_ALLOCATE_MEM_PIPE_IMPOSSIBLE			\
+	EB_ALLOCATE_PIPE_SAMPLE_BUFFER_IMPOSSIBLE
+
+#define EB_ALLOCATE_DIFFERED_CMD_IMPOSSIBLE     (EB_RI | 0x03)
+#define EB_TOO_MANY_DIFFERED_CMD                (EB_RI | 0x04)
+#define EB_RBUFFERS_TABLE_OVERFLOW              (EB_RI | 0x05)
+#define EB_ALLOCATE_EFFECTS_IMPOSSIBLE          (EB_RI | 0x08)
+#define EB_ALLOCATE_EFFECT_POS_IMPOSSIBLE       (EB_RI | 0x09)
+#define EB_RBUFFER_NOT_AVAILABLE                (EB_RI | 0x0A)
+#define EB_ALLOCATE_CONTEXT_LIII_IMPOSSIBLE     (EB_RI | 0x0B)
+#define EB_STATUS_DIALOG_IMPOSSIBLE             (EB_RI | 0x1D)
+#define EB_CONTROL_CMD_IMPOSSIBLE               (EB_RI | 0x1E)
+#define EB_STATUS_SEND_IMPOSSIBLE               (EB_RI | 0x1F)
+#define EB_ALLOCATE_PIPE_IMPOSSIBLE             (EB_RI | 0x40)
+#define EB_ALLOCATE_STREAM_IMPOSSIBLE           (EB_RI | 0x80)
+#define EB_ALLOCATE_AUDIO_IMPOSSIBLE            (EB_RI | 0xC0)
+
+/* Complete BOARD error code for wrong call context class */
+#define EB_WCC          (ERROR_VALUE | E_SOURCE_BOARD | E_CLASS_WRONG_CONTEXT)
+#define EB_CMD_REFUSED                  (EB_WCC | 0x00)
+#define EB_START_STREAM_REFUSED         (EB_WCC | 0xFC)
+#define EB_SPC_REFUSED                  (EB_WCC | 0xFD)
+#define EB_CSN_REFUSED                  (EB_WCC | 0xFE)
+#define EB_CSE_REFUSED                  (EB_WCC | 0xFF)
+
+
+
+
+#endif /* LX_DEFS_H */
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index c262049..3b5ca70 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -487,10 +487,14 @@
 {
 	struct oxygen *chip = snd_pcm_substream_chip(substream);
 	unsigned int channel = oxygen_substream_channel(substream);
+	unsigned int channel_mask = 1 << channel;
 
 	spin_lock_irq(&chip->reg_lock);
-	chip->interrupt_mask &= ~(1 << channel);
+	chip->interrupt_mask &= ~channel_mask;
 	oxygen_write16(chip, OXYGEN_INTERRUPT_MASK, chip->interrupt_mask);
+
+	oxygen_set_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
+	oxygen_clear_bits8(chip, OXYGEN_DMA_FLUSH, channel_mask);
 	spin_unlock_irq(&chip->reg_lock);
 
 	return snd_pcm_lib_free_pages(substream);
diff --git a/sound/pci/oxygen/virtuoso.c b/sound/pci/oxygen/virtuoso.c
index bc5ce11..bf971f7 100644
--- a/sound/pci/oxygen/virtuoso.c
+++ b/sound/pci/oxygen/virtuoso.c
@@ -113,8 +113,8 @@
  */
 
 /*
- * Xonar Essence STX
- * -----------------
+ * Xonar Essence ST (Deluxe)/STX
+ * -----------------------------
  *
  * CMI8788:
  *
@@ -180,6 +180,8 @@
 	MODEL_DX,
 	MODEL_HDAV,	/* without daughterboard */
 	MODEL_HDAV_H6,	/* with H6 daughterboard */
+	MODEL_ST,
+	MODEL_ST_H6,
 	MODEL_STX,
 };
 
@@ -188,8 +190,10 @@
 	{ OXYGEN_PCI_SUBID(0x1043, 0x8275), .driver_data = MODEL_DX },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x82b7), .driver_data = MODEL_D2X },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x8314), .driver_data = MODEL_HDAV },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x8327), .driver_data = MODEL_DX },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x834f), .driver_data = MODEL_D1 },
 	{ OXYGEN_PCI_SUBID(0x1043, 0x835c), .driver_data = MODEL_STX },
+	{ OXYGEN_PCI_SUBID(0x1043, 0x835d), .driver_data = MODEL_ST },
 	{ OXYGEN_PCI_SUBID_BROKEN_EEPROM },
 	{ }
 };
@@ -210,9 +214,9 @@
 #define GPIO_DX_FRONT_PANEL	0x0002
 #define GPIO_DX_INPUT_ROUTE	0x0100
 
-#define GPIO_HDAV_DB_MASK	0x0030
-#define GPIO_HDAV_DB_H6		0x0000
-#define GPIO_HDAV_DB_XX		0x0020
+#define GPIO_DB_MASK		0x0030
+#define GPIO_DB_H6		0x0000
+#define GPIO_DB_XX		0x0020
 
 #define GPIO_ST_HP_REAR		0x0002
 #define GPIO_ST_HP		0x0080
@@ -530,7 +534,7 @@
 	snd_component_add(chip->card, "CS5381");
 }
 
-static void xonar_stx_init(struct oxygen *chip)
+static void xonar_st_init(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
 
@@ -539,12 +543,11 @@
 		       OXYGEN_2WIRE_INTERRUPT_MASK |
 		       OXYGEN_2WIRE_SPEED_FAST);
 
+	if (chip->model.private_data == MODEL_ST_H6)
+		chip->model.dac_channels = 8;
 	data->anti_pop_delay = 100;
-	data->dacs = 1;
+	data->dacs = chip->model.private_data == MODEL_ST_H6 ? 4 : 1;
 	data->output_enable_bit = GPIO_DX_OUTPUT_ENABLE;
-	data->ext_power_reg = OXYGEN_GPI_DATA;
-	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
-	data->ext_power_bit = GPI_DX_EXT_POWER;
 	data->pcm1796_oversampling = PCM1796_OS_64;
 
 	pcm1796_init(chip);
@@ -560,6 +563,17 @@
 	snd_component_add(chip->card, "CS5381");
 }
 
+static void xonar_stx_init(struct oxygen *chip)
+{
+	struct xonar_data *data = chip->model_data;
+
+	data->ext_power_reg = OXYGEN_GPI_DATA;
+	data->ext_power_int_reg = OXYGEN_GPI_INTERRUPT_MASK;
+	data->ext_power_bit = GPI_DX_EXT_POWER;
+
+	xonar_st_init(chip);
+}
+
 static void xonar_disable_output(struct oxygen *chip)
 {
 	struct xonar_data *data = chip->model_data;
@@ -1021,7 +1035,8 @@
 	.model_data_size = sizeof(struct xonar_data),
 	.device_config = PLAYBACK_0_TO_I2S |
 			 PLAYBACK_1_TO_SPDIF |
-			 CAPTURE_0_FROM_I2S_2,
+			 CAPTURE_0_FROM_I2S_2 |
+			 CAPTURE_1_FROM_SPDIF,
 	.dac_channels = 8,
 	.dac_volume_min = 255 - 2*60,
 	.dac_volume_max = 255,
@@ -1034,7 +1049,7 @@
 static const struct oxygen_model model_xonar_st = {
 	.longname = "Asus Virtuoso 100",
 	.chip = "AV200",
-	.init = xonar_stx_init,
+	.init = xonar_st_init,
 	.control_filter = xonar_st_control_filter,
 	.mixer_init = xonar_st_mixer_init,
 	.cleanup = xonar_st_cleanup,
@@ -1067,6 +1082,7 @@
 		[MODEL_D2]	= &model_xonar_d2,
 		[MODEL_D2X]	= &model_xonar_d2,
 		[MODEL_HDAV]	= &model_xonar_hdav,
+		[MODEL_ST]	= &model_xonar_st,
 		[MODEL_STX]	= &model_xonar_st,
 	};
 	static const char *const names[] = {
@@ -1076,6 +1092,8 @@
 		[MODEL_D2X]	= "Xonar D2X",
 		[MODEL_HDAV]	= "Xonar HDAV1.3",
 		[MODEL_HDAV_H6]	= "Xonar HDAV1.3+H6",
+		[MODEL_ST]	= "Xonar Essence ST",
+		[MODEL_ST_H6]	= "Xonar Essence ST+H6",
 		[MODEL_STX]	= "Xonar Essence STX",
 	};
 	unsigned int model = id->driver_data;
@@ -1092,21 +1110,27 @@
 		chip->model.init = xonar_dx_init;
 		break;
 	case MODEL_HDAV:
-		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
-				    GPIO_HDAV_DB_MASK);
-		switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) &
-			GPIO_HDAV_DB_MASK) {
-		case GPIO_HDAV_DB_H6:
+		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
+		switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
+		case GPIO_DB_H6:
 			model = MODEL_HDAV_H6;
 			break;
-		case GPIO_HDAV_DB_XX:
+		case GPIO_DB_XX:
 			snd_printk(KERN_ERR "unknown daughterboard\n");
 			return -ENODEV;
 		}
 		break;
+	case MODEL_ST:
+		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
+		switch (oxygen_read16(chip, OXYGEN_GPIO_DATA) & GPIO_DB_MASK) {
+		case GPIO_DB_H6:
+			model = MODEL_ST_H6;
+			break;
+		}
+		break;
 	case MODEL_STX:
-		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL,
-				    GPIO_HDAV_DB_MASK);
+		chip->model.init = xonar_stx_init;
+		oxygen_clear_bits16(chip, OXYGEN_GPIO_CONTROL, GPIO_DB_MASK);
 		break;
 	}
 
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index e51a5ef..235a71e 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -507,41 +507,19 @@
  */
 
 static struct pci_device_id snd_riptide_ids[] = {
-	{
-	 .vendor = 0x127a,.device = 0x4310,
-	 .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-	 },
-	{
-	 .vendor = 0x127a,.device = 0x4320,
-	 .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-	 },
-	{
-	 .vendor = 0x127a,.device = 0x4330,
-	 .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-	 },
-	{
-	 .vendor = 0x127a,.device = 0x4340,
-	 .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-	 },
+	{ PCI_DEVICE(0x127a, 0x4310) },
+	{ PCI_DEVICE(0x127a, 0x4320) },
+	{ PCI_DEVICE(0x127a, 0x4330) },
+	{ PCI_DEVICE(0x127a, 0x4340) },
 	{0,},
 };
 
 #ifdef SUPPORT_JOYSTICK
 static struct pci_device_id snd_riptide_joystick_ids[] __devinitdata = {
-	{
-	 .vendor = 0x127a,.device = 0x4312,
-	 .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-	 },
-	{
-	 .vendor = 0x127a,.device = 0x4322,
-	 .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-	 },
-	{.vendor = 0x127a,.device = 0x4332,
-	 .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-	 },
-	{.vendor = 0x127a,.device = 0x4342,
-	 .subvendor = PCI_ANY_ID,.subdevice = PCI_ANY_ID,
-	 },
+	{ PCI_DEVICE(0x127a, 0x4312) },
+	{ PCI_DEVICE(0x127a, 0x4322) },
+	{ PCI_DEVICE(0x127a, 0x4332) },
+	{ PCI_DEVICE(0x127a, 0x4342) },
 	{0,},
 };
 #endif
@@ -1209,12 +1187,79 @@
 }
 #endif
 
+static int try_to_load_firmware(struct cmdif *cif, struct snd_riptide *chip)
+{
+	union firmware_version firmware = { .ret = CMDRET_ZERO };
+	int i, timeout, err;
+
+	for (i = 0; i < 2; i++) {
+		WRITE_PORT_ULONG(cif->hwport->port[i].data1, 0);
+		WRITE_PORT_ULONG(cif->hwport->port[i].data2, 0);
+	}
+	SET_GRESET(cif->hwport);
+	udelay(100);
+	UNSET_GRESET(cif->hwport);
+	udelay(100);
+
+	for (timeout = 100000; --timeout; udelay(10)) {
+		if (IS_READY(cif->hwport) && !IS_GERR(cif->hwport))
+			break;
+	}
+	if (!timeout) {
+		snd_printk(KERN_ERR
+			   "Riptide: device not ready, audio status: 0x%x "
+			   "ready: %d gerr: %d\n",
+			   READ_AUDIO_STATUS(cif->hwport),
+			   IS_READY(cif->hwport), IS_GERR(cif->hwport));
+		return -EIO;
+	} else {
+		snd_printdd
+			("Riptide: audio status: 0x%x ready: %d gerr: %d\n",
+			 READ_AUDIO_STATUS(cif->hwport),
+			 IS_READY(cif->hwport), IS_GERR(cif->hwport));
+	}
+
+	SEND_GETV(cif, &firmware.ret);
+	snd_printdd("Firmware version: ASIC: %d CODEC %d AUXDSP %d PROG %d\n",
+		    firmware.firmware.ASIC, firmware.firmware.CODEC,
+		    firmware.firmware.AUXDSP, firmware.firmware.PROG);
+
+	for (i = 0; i < FIRMWARE_VERSIONS; i++) {
+		if (!memcmp(&firmware_versions[i], &firmware, sizeof(firmware)))
+			break;
+	}
+	if (i >= FIRMWARE_VERSIONS)
+		return 0; /* no match */
+
+	if (!chip)
+		return 1; /* OK */
+
+	snd_printdd("Writing Firmware\n");
+	if (!chip->fw_entry) {
+		err = request_firmware(&chip->fw_entry, "riptide.hex",
+				       &chip->pci->dev);
+		if (err) {
+			snd_printk(KERN_ERR
+				   "Riptide: Firmware not available %d\n", err);
+			return -EIO;
+		}
+	}
+	err = loadfirmware(cif, chip->fw_entry->data, chip->fw_entry->size);
+	if (err) {
+		snd_printk(KERN_ERR
+			   "Riptide: Could not load firmware %d\n", err);
+		return err;
+	}
+
+	chip->firmware = firmware;
+
+	return 1; /* OK */
+}
+
 static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip)
 {
-	int timeout, tries;
 	union cmdret rptr = CMDRET_ZERO;
-	union firmware_version firmware;
-	int i, j, err, has_firmware;
+	int err, tries;
 
 	if (!cif)
 		return -EINVAL;
@@ -1227,75 +1272,11 @@
 	cif->is_reset = 0;
 
 	tries = RESET_TRIES;
-	has_firmware = 0;
-	while (has_firmware == 0 && tries-- > 0) {
-		for (i = 0; i < 2; i++) {
-			WRITE_PORT_ULONG(cif->hwport->port[i].data1, 0);
-			WRITE_PORT_ULONG(cif->hwport->port[i].data2, 0);
-		}
-		SET_GRESET(cif->hwport);
-		udelay(100);
-		UNSET_GRESET(cif->hwport);
-		udelay(100);
-
-		for (timeout = 100000; --timeout; udelay(10)) {
-			if (IS_READY(cif->hwport) && !IS_GERR(cif->hwport))
-				break;
-		}
-		if (timeout == 0) {
-			snd_printk(KERN_ERR
-				   "Riptide: device not ready, audio status: 0x%x ready: %d gerr: %d\n",
-				   READ_AUDIO_STATUS(cif->hwport),
-				   IS_READY(cif->hwport), IS_GERR(cif->hwport));
-			return -EIO;
-		} else {
-			snd_printdd
-			    ("Riptide: audio status: 0x%x ready: %d gerr: %d\n",
-			     READ_AUDIO_STATUS(cif->hwport),
-			     IS_READY(cif->hwport), IS_GERR(cif->hwport));
-		}
-
-		SEND_GETV(cif, &rptr);
-		for (i = 0; i < 4; i++)
-			firmware.ret.retwords[i] = rptr.retwords[i];
-
-		snd_printdd
-		    ("Firmware version: ASIC: %d CODEC %d AUXDSP %d PROG %d\n",
-		     firmware.firmware.ASIC, firmware.firmware.CODEC,
-		     firmware.firmware.AUXDSP, firmware.firmware.PROG);
-
-		for (j = 0; j < FIRMWARE_VERSIONS; j++) {
-			has_firmware = 1;
-			for (i = 0; i < 4; i++) {
-				if (firmware_versions[j].ret.retwords[i] !=
-				    firmware.ret.retwords[i])
-					has_firmware = 0;
-			}
-			if (has_firmware)
-				break;
-		}
-
-		if (chip != NULL && has_firmware == 0) {
-			snd_printdd("Writing Firmware\n");
-			if (!chip->fw_entry) {
-				if ((err =
-				     request_firmware(&chip->fw_entry,
-						      "riptide.hex",
-						      &chip->pci->dev)) != 0) {
-					snd_printk(KERN_ERR
-						   "Riptide: Firmware not available %d\n",
-						   err);
-					return -EIO;
-				}
-			}
-			err = loadfirmware(cif, chip->fw_entry->data,
-					   chip->fw_entry->size);
-			if (err)
-				snd_printk(KERN_ERR
-					   "Riptide: Could not load firmware %d\n",
-					   err);
-		}
-	}
+	do {
+		err = try_to_load_firmware(cif, chip);
+		if (err < 0)
+			return err;
+	} while (!err && --tries);
 
 	SEND_SACR(cif, 0, AC97_RESET);
 	SEND_RACR(cif, AC97_RESET, &rptr);
@@ -1337,11 +1318,6 @@
 	SET_AIE(cif->hwport);
 	SET_AIACK(cif->hwport);
 	cif->is_reset = 1;
-	if (chip) {
-		for (i = 0; i < 4; i++)
-			chip->firmware.ret.retwords[i] =
-			    firmware.ret.retwords[i];
-	}
 
 	return 0;
 }
@@ -2038,14 +2014,12 @@
 }
 
 #ifdef SUPPORT_JOYSTICK
-static int have_joystick;
-static struct pci_dev *riptide_gameport_pci;
-static struct gameport *riptide_gameport;
 
 static int __devinit
 snd_riptide_joystick_probe(struct pci_dev *pci, const struct pci_device_id *id)
 {
 	static int dev;
+	struct gameport *gameport;
 
 	if (dev >= SNDRV_CARDS)
 		return -ENODEV;
@@ -2054,36 +2028,33 @@
 		return -ENOENT;
 	}
 
-	if (joystick_port[dev]) {
-		riptide_gameport = gameport_allocate_port();
-		if (riptide_gameport) {
-			if (!request_region
-			    (joystick_port[dev], 8, "Riptide gameport")) {
-				snd_printk(KERN_WARNING
-					   "Riptide: cannot grab gameport 0x%x\n",
-					   joystick_port[dev]);
-				gameport_free_port(riptide_gameport);
-				riptide_gameport = NULL;
-			} else {
-				riptide_gameport_pci = pci;
-				riptide_gameport->io = joystick_port[dev];
-				gameport_register_port(riptide_gameport);
-			}
-		}
+	if (!joystick_port[dev++])
+		return 0;
+
+	gameport = gameport_allocate_port();
+	if (!gameport)
+		return -ENOMEM;
+	if (!request_region(joystick_port[dev], 8, "Riptide gameport")) {
+		snd_printk(KERN_WARNING
+			   "Riptide: cannot grab gameport 0x%x\n",
+			   joystick_port[dev]);
+		gameport_free_port(gameport);
+		return -EBUSY;
 	}
-	dev++;
+
+	gameport->io = joystick_port[dev];
+	gameport_register_port(gameport);
+	pci_set_drvdata(pci, gameport);
 	return 0;
 }
 
 static void __devexit snd_riptide_joystick_remove(struct pci_dev *pci)
 {
-	if (riptide_gameport) {
-		if (riptide_gameport_pci == pci) {
-			release_region(riptide_gameport->io, 8);
-			riptide_gameport_pci = NULL;
-			gameport_unregister_port(riptide_gameport);
-			riptide_gameport = NULL;
-		}
+	struct gameport *gameport = pci_get_drvdata(pci);
+	if (gameport) {
+		release_region(gameport->io, 8);
+		gameport_unregister_port(gameport);
+		pci_set_drvdata(pci, NULL);
 	}
 }
 #endif
@@ -2094,8 +2065,8 @@
 	static int dev;
 	struct snd_card *card;
 	struct snd_riptide *chip;
-	unsigned short addr;
-	int err = 0;
+	unsigned short val;
+	int err;
 
 	if (dev >= SNDRV_CARDS)
 		return -ENODEV;
@@ -2107,60 +2078,63 @@
 	err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
 	if (err < 0)
 		return err;
-	if ((err = snd_riptide_create(card, pci, &chip)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
+	err = snd_riptide_create(card, pci, &chip);
+	if (err < 0)
+		goto error;
 	card->private_data = chip;
-	if ((err = snd_riptide_pcm(chip, 0, NULL)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	if ((err = snd_riptide_mixer(chip)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	pci_write_config_word(chip->pci, PCI_EXT_Legacy_Mask, LEGACY_ENABLE_ALL
-			      | (opl3_port[dev] ? LEGACY_ENABLE_FM : 0)
+	err = snd_riptide_pcm(chip, 0, NULL);
+	if (err < 0)
+		goto error;
+	err = snd_riptide_mixer(chip);
+	if (err < 0)
+		goto error;
+
+	val = LEGACY_ENABLE_ALL;
+	if (opl3_port[dev])
+		val |= LEGACY_ENABLE_FM;
 #ifdef SUPPORT_JOYSTICK
-			      | (joystick_port[dev] ? LEGACY_ENABLE_GAMEPORT :
-				 0)
+	if (joystick_port[dev])
+		val |= LEGACY_ENABLE_GAMEPORT;
 #endif
-			      | (mpu_port[dev]
-				 ? (LEGACY_ENABLE_MPU_INT | LEGACY_ENABLE_MPU) :
-				 0)
-			      | ((chip->irq << 4) & 0xF0));
-	if ((addr = mpu_port[dev]) != 0) {
-		pci_write_config_word(chip->pci, PCI_EXT_MPU_Base, addr);
-		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_RIPTIDE,
-					       addr, 0, chip->irq, 0,
-					       &chip->rmidi)) < 0)
+	if (mpu_port[dev])
+		val |= LEGACY_ENABLE_MPU_INT | LEGACY_ENABLE_MPU;
+	val |= (chip->irq << 4) & 0xf0;
+	pci_write_config_word(chip->pci, PCI_EXT_Legacy_Mask, val);
+	if (mpu_port[dev]) {
+		val = mpu_port[dev];
+		pci_write_config_word(chip->pci, PCI_EXT_MPU_Base, val);
+		err = snd_mpu401_uart_new(card, 0, MPU401_HW_RIPTIDE,
+					  val, 0, chip->irq, 0,
+					  &chip->rmidi);
+		if (err < 0)
 			snd_printk(KERN_WARNING
 				   "Riptide: Can't Allocate MPU at 0x%x\n",
-				   addr);
+				   val);
 		else
-			chip->mpuaddr = addr;
+			chip->mpuaddr = val;
 	}
-	if ((addr = opl3_port[dev]) != 0) {
-		pci_write_config_word(chip->pci, PCI_EXT_FM_Base, addr);
-		if ((err = snd_opl3_create(card, addr, addr + 2,
-					   OPL3_HW_RIPTIDE, 0,
-					   &chip->opl3)) < 0)
+	if (opl3_port[dev]) {
+		val = opl3_port[dev];
+		pci_write_config_word(chip->pci, PCI_EXT_FM_Base, val);
+		err = snd_opl3_create(card, val, val + 2,
+				      OPL3_HW_RIPTIDE, 0, &chip->opl3);
+		if (err < 0)
 			snd_printk(KERN_WARNING
 				   "Riptide: Can't Allocate OPL3 at 0x%x\n",
-				   addr);
+				   val);
 		else {
-			chip->opladdr = addr;
-			if ((err =
-			     snd_opl3_hwdep_new(chip->opl3, 0, 1, NULL)) < 0)
+			chip->opladdr = val;
+			err = snd_opl3_hwdep_new(chip->opl3, 0, 1, NULL);
+			if (err < 0)
 				snd_printk(KERN_WARNING
 					   "Riptide: Can't Allocate OPL3-HWDEP\n");
 		}
 	}
 #ifdef SUPPORT_JOYSTICK
-	if ((addr = joystick_port[dev]) != 0) {
-		pci_write_config_word(chip->pci, PCI_EXT_Game_Base, addr);
-		chip->gameaddr = addr;
+	if (joystick_port[dev]) {
+		val = joystick_port[dev];
+		pci_write_config_word(chip->pci, PCI_EXT_Game_Base, val);
+		chip->gameaddr = val;
 	}
 #endif
 
@@ -2178,13 +2152,16 @@
 		 chip->opladdr);
 #endif
 	snd_riptide_proc_init(chip);
-	if ((err = snd_card_register(card)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
+	err = snd_card_register(card);
+	if (err < 0)
+		goto error;
 	pci_set_drvdata(pci, card);
 	dev++;
 	return 0;
+
+ error:
+	snd_card_free(card);
+	return err;
 }
 
 static void __devexit snd_card_riptide_remove(struct pci_dev *pci)
@@ -2216,14 +2193,11 @@
 static int __init alsa_card_riptide_init(void)
 {
 	int err;
-	if ((err = pci_register_driver(&driver)) < 0)
+	err = pci_register_driver(&driver);
+	if (err < 0)
 		return err;
 #if defined(SUPPORT_JOYSTICK)
-	if (pci_register_driver(&joystick_driver) < 0) {
-		have_joystick = 0;
-		snd_printk(KERN_INFO "no joystick found\n");
-	} else
-		have_joystick = 1;
+	pci_register_driver(&joystick_driver);
 #endif
 	return 0;
 }
@@ -2232,8 +2206,7 @@
 {
 	pci_unregister_driver(&driver);
 #if defined(SUPPORT_JOYSTICK)
-	if (have_joystick)
-		pci_unregister_driver(&joystick_driver);
+	pci_unregister_driver(&joystick_driver);
 #endif
 }
 
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 314e735..3da5c02 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -28,6 +28,7 @@
 #include <linux/pci.h>
 #include <linux/firmware.h>
 #include <linux/moduleparam.h>
+#include <linux/math64.h>
 
 #include <sound/core.h>
 #include <sound/control.h>
@@ -402,9 +403,9 @@
 #define HDSP_DMA_AREA_BYTES ((HDSP_MAX_CHANNELS+1) * HDSP_CHANNEL_BUFFER_BYTES)
 #define HDSP_DMA_AREA_KILOBYTES (HDSP_DMA_AREA_BYTES/1024)
 
-/* use hotplug firmeare loader? */
+/* use hotplug firmware loader? */
 #if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
-#if !defined(HDSP_USE_HWDEP_LOADER) && !defined(CONFIG_SND_HDSP)
+#if !defined(HDSP_USE_HWDEP_LOADER)
 #define HDSP_FW_LOADER
 #endif
 #endif
@@ -1047,7 +1048,6 @@
 static void hdsp_set_dds_value(struct hdsp *hdsp, int rate)
 {
 	u64 n;
-	u32 r;
 
 	if (rate >= 112000)
 		rate /= 4;
@@ -1055,7 +1055,7 @@
 		rate /= 2;
 
 	n = DDS_NUMERATOR;
-	div64_32(&n, rate, &r);
+	n = div_u64(n, rate);
 	/* n should be less than 2^32 for being written to FREQ register */
 	snd_BUG_ON(n >> 32);
 	/* HDSP_freqReg and HDSP_resetPointer are the same, so keep the DDS
@@ -3097,7 +3097,6 @@
 static int hdsp_dds_offset(struct hdsp *hdsp)
 {
 	u64 n;
-	u32 r;
 	unsigned int dds_value = hdsp->dds_value;
 	int system_sample_rate = hdsp->system_sample_rate;
 
@@ -3109,7 +3108,7 @@
 	 * dds_value = n / rate
 	 * rate = n / dds_value
 	 */
-	div64_32(&n, dds_value, &r);
+	n = div_u64(n, dds_value);
 	if (system_sample_rate >= 112000)
 		n *= 4;
 	else if (system_sample_rate >= 56000)
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index bac2dc0..0dce331 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -29,6 +29,7 @@
 #include <linux/moduleparam.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
+#include <linux/math64.h>
 #include <asm/io.h>
 
 #include <sound/core.h>
@@ -831,7 +832,6 @@
 static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
 {
 	u64 n;
-	u32 r;
 	
 	if (rate >= 112000)
 		rate /= 4;
@@ -844,7 +844,7 @@
         */	   
 	/* n = 104857600000000ULL; */ /*  =  2^20 * 10^8 */
 	n = 110100480000000ULL;    /* Value checked for AES32 and MADI */
-	div64_32(&n, rate, &r);
+	n = div_u64(n, rate);
 	/* n should be less than 2^32 for being written to FREQ register */
 	snd_BUG_ON(n >> 32);
 	hdspm_write(hdspm, HDSPM_freqReg, (u32)n);
diff --git a/sound/pci/sis7019.h b/sound/pci/sis7019.h
index 013b673..bc8c768 100644
--- a/sound/pci/sis7019.h
+++ b/sound/pci/sis7019.h
@@ -203,7 +203,7 @@
 #define SIS_WEISR_B	0xac
 
 
-/* Playback DMA parameters (paramter RAM) */
+/* Playback DMA parameters (parameter RAM) */
 #define SIS_PLAY_DMA_OFFSET	0x0000
 #define SIS_PLAY_DMA_SIZE	0x10
 #define SIS_PLAY_DMA_ADDR(addr, num) \
@@ -228,7 +228,7 @@
 #define		SIS_PLAY_DMA_SSO_MASK		0xffff0000
 #define		SIS_PLAY_DMA_ESO_MASK		0x0000ffff
 
-/* Capture DMA parameters (paramter RAM) */
+/* Capture DMA parameters (parameter RAM) */
 #define SIS_CAPTURE_DMA_OFFSET	0x0800
 #define SIS_CAPTURE_DMA_SIZE	0x10
 #define SIS_CAPTURE_DMA_ADDR(addr, num) \
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 1ef58c5..949fcaf 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -85,6 +85,7 @@
 static int ac97_clock = 48000;
 static char *ac97_quirk;
 static int dxs_support;
+static int nodelay;
 
 module_param(index, int, 0444);
 MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge.");
@@ -102,6 +103,8 @@
 MODULE_PARM_DESC(ac97_quirk, "AC'97 workaround for strange hardware.");
 module_param(dxs_support, int, 0444);
 MODULE_PARM_DESC(dxs_support, "Support for DXS channels (0 = auto, 1 = enable, 2 = disable, 3 = 48k only, 4 = no VRA, 5 = enable any sample rate)");
+module_param(nodelay, int, 0444);
+MODULE_PARM_DESC(nodelay, "Disable 500ms init delay");
 
 /* just for backward compatibility */
 static int enable;
@@ -549,7 +552,8 @@
 	int err;
 	err = snd_via82xx_codec_ready(chip, ac97->num);
 	/* here we need to wait fairly for long time.. */
-	msleep(500);
+	if (!nodelay)
+		msleep(500);
 }
 
 static void snd_via82xx_codec_write(struct snd_ac97 *ac97,
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index c0efe44..6416d3f 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -367,7 +367,7 @@
 	unsigned int port;
 	const unsigned char *image;
 
-	/* XILINX reset (wait at least 1 milisecond between reset on and off). */
+	/* XILINX reset (wait at least 1 millisecond between reset on and off). */
 	vx_outl(chip, CNTRL, VX_CNTRL_REGISTER_VALUE | VX_XILINX_RESET_MASK);
 	vx_inl(chip, CNTRL);
 	msleep(10);
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 80df9b1..2cc0eda 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -477,7 +477,7 @@
 #define AMP_CH_SPK	0
 #define AMP_CH_HD	1
 
-static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_amp_vol[] __devinitdata = {
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	  .name = "PC Speaker Playback Volume",
 	  .info = snd_pmac_awacs_info_volume_amp,
@@ -514,7 +514,7 @@
 	},
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_amp_hp_sw __devinitdata = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "Headphone Playback Switch",
 	.info = snd_pmac_boolean_stereo_info,
@@ -523,7 +523,7 @@
 	.private_value = AMP_CH_HD,
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_amp_spk_sw __devinitdata = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "PC Speaker Playback Switch",
 	.info = snd_pmac_boolean_stereo_info,
@@ -595,46 +595,46 @@
 /*
  * lists of mixer elements
  */
-static struct snd_kcontrol_new snd_pmac_awacs_mixers[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers[] __devinitdata = {
 	AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0),
 	AWACS_VOLUME("Master Capture Volume", 0, 4, 0),
 /*	AWACS_SWITCH("Unknown Playback Switch", 6, SHIFT_PAROUT0, 0), */
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_beige[] __devinitdata = {
 	AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
 	AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_LINE, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_lo[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_lo[] __devinitdata = {
 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_imac[] __devinitdata = {
 	AWACS_VOLUME("Play-through Playback Volume", 5, 6, 1),
 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mixers_g4agp[] __devinitdata = {
 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
 	AWACS_VOLUME("Master Playback Volume", 5, 6, 1),
 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac7500[] __devinitdata = {
 	AWACS_VOLUME("Line out Playback Volume", 2, 6, 1),
 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac5500[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac5500[] __devinitdata = {
 	AWACS_VOLUME("Headphone Playback Volume", 2, 6, 1),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers_pmac[] __devinitdata = {
 	AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
 };
@@ -642,34 +642,34 @@
 /* FIXME: is this correct order?
  * screamer (powerbook G3 pismo) seems to have different bits...
  */
-static struct snd_kcontrol_new snd_pmac_awacs_mixers2[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers2[] __devinitdata = {
 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0),
 	AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mixers2[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mixers2[] __devinitdata = {
 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_MIC, 0),
 	AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_LINE, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_mixers2_pmac5500[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mixers2_pmac5500[] __devinitdata = {
 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_master_sw __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_master_sw __devinitdata =
 AWACS_SWITCH("Master Playback Switch", 1, SHIFT_HDMUTE, 1);
 
-static struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_master_sw_imac __devinitdata =
 AWACS_SWITCH("Line out Playback Switch", 1, SHIFT_HDMUTE, 1);
 
-static struct snd_kcontrol_new snd_pmac_awacs_master_sw_pmac5500 __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_master_sw_pmac5500 __devinitdata =
 AWACS_SWITCH("Headphone Playback Switch", 1, SHIFT_HDMUTE, 1);
 
-static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_mic_boost[] __devinitdata = {
 	AWACS_SWITCH("Mic Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_screamer_mic_boost[] __devinitdata = {
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	  .name = "Mic Boost Capture Volume",
 	  .info = snd_pmac_screamer_mic_boost_info,
@@ -678,34 +678,34 @@
 	},
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_mic_boost_pmac7500[] __devinitdata =
 {
 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] __initdata =
+static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_beige[] __devinitdata =
 {
 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
 	AWACS_SWITCH("CD Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] __initdata =
+static struct snd_kcontrol_new snd_pmac_screamer_mic_boost_imac[] __devinitdata =
 {
 	AWACS_SWITCH("Line Boost Capture Switch", 0, SHIFT_GAINLINE, 0),
 	AWACS_SWITCH("Mic Boost Capture Switch", 6, SHIFT_MIC_BOOST, 0),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_awacs_speaker_vol[] __devinitdata = {
 	AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1),
 };
 
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw __devinitdata =
 AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
 
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac1 __devinitdata =
 AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 1);
 
-static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 __initdata =
+static struct snd_kcontrol_new snd_pmac_awacs_speaker_sw_imac2 __devinitdata =
 AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_PAROUT1, 0);
 
 
@@ -872,7 +872,7 @@
 /*
  * initialize chip
  */
-int __init
+int __devinit
 snd_pmac_awacs_init(struct snd_pmac *chip)
 {
 	int pm7500 = IS_PM7500;
diff --git a/sound/ppc/beep.c b/sound/ppc/beep.c
index 89f5c32..a9d3507 100644
--- a/sound/ppc/beep.c
+++ b/sound/ppc/beep.c
@@ -215,7 +215,7 @@
 };
 
 /* Initialize beep stuff */
-int __init snd_pmac_attach_beep(struct snd_pmac *chip)
+int __devinit snd_pmac_attach_beep(struct snd_pmac *chip)
 {
 	struct pmac_beep *beep;
 	struct input_dev *input_dev;
diff --git a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c
index 45a7629..16ed240 100644
--- a/sound/ppc/burgundy.c
+++ b/sound/ppc/burgundy.c
@@ -46,12 +46,12 @@
 	timeout = 50;
 	while (!(in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--)
 		udelay(1);
-	if (! timeout)
+	if (timeout < 0)
 		printk(KERN_DEBUG "burgundy_extend_wait: timeout #1\n");
 	timeout = 50;
 	while ((in_le32(&chip->awacs->codec_stat) & MASK_EXTEND) && timeout--)
 		udelay(1);
-	if (! timeout)
+	if (timeout < 0)
 		printk(KERN_DEBUG "burgundy_extend_wait: timeout #2\n");
 }
 
@@ -468,7 +468,7 @@
 /*
  * Burgundy mixers
  */
-static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_burgundy_mixers[] __devinitdata = {
 	BURGUNDY_VOLUME_W("Master Playback Volume", 0,
 			MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8),
 	BURGUNDY_VOLUME_W("CD Capture Volume", 0,
@@ -496,7 +496,7 @@
  */	BURGUNDY_SWITCH_B("PCM Capture Switch", 0,
 			MASK_ADDR_BURGUNDY_HOSTIFEH, 0x01, 0, 0)
 };
-static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_burgundy_mixers_imac[] __devinitdata = {
 	BURGUNDY_VOLUME_W("Line in Capture Volume", 0,
 			MASK_ADDR_BURGUNDY_VOLLINE, 16),
 	BURGUNDY_VOLUME_W("Mic Capture Volume", 0,
@@ -522,7 +522,7 @@
 	BURGUNDY_SWITCH_B("Mic Boost Capture Switch", 0,
 			MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1)
 };
-static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] __initdata = {
+static struct snd_kcontrol_new snd_pmac_burgundy_mixers_pmac[] __devinitdata = {
 	BURGUNDY_VOLUME_W("Line in Capture Volume", 0,
 			MASK_ADDR_BURGUNDY_VOLMIC, 16),
 	BURGUNDY_VOLUME_B("Line in Gain Capture Volume", 0,
@@ -538,33 +538,33 @@
 /*	BURGUNDY_SWITCH_B("Line in Boost Capture Switch", 0,
  *		MASK_ADDR_BURGUNDY_INPBOOST, 0x40, 0x80, 1) */
 };
-static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_imac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_imac __devinitdata =
 BURGUNDY_SWITCH_B("Master Playback Switch", 0,
 	MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
 	BURGUNDY_OUTPUT_LEFT | BURGUNDY_LINEOUT_LEFT | BURGUNDY_HP_LEFT,
 	BURGUNDY_OUTPUT_RIGHT | BURGUNDY_LINEOUT_RIGHT | BURGUNDY_HP_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_pmac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_master_sw_pmac __devinitdata =
 BURGUNDY_SWITCH_B("Master Playback Switch", 0,
 	MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
 	BURGUNDY_OUTPUT_INTERN
 	| BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_imac __devinitdata =
 BURGUNDY_SWITCH_B("PC Speaker Playback Switch", 0,
 	MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
 	BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_speaker_sw_pmac __devinitdata =
 BURGUNDY_SWITCH_B("PC Speaker Playback Switch", 0,
 	MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
 	BURGUNDY_OUTPUT_INTERN, 0, 0);
-static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_imac __devinitdata =
 BURGUNDY_SWITCH_B("Line out Playback Switch", 0,
 	MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
 	BURGUNDY_LINEOUT_LEFT, BURGUNDY_LINEOUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_pmac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_line_sw_pmac __devinitdata =
 BURGUNDY_SWITCH_B("Line out Playback Switch", 0,
 	MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
 	BURGUNDY_OUTPUT_LEFT, BURGUNDY_OUTPUT_RIGHT, 1);
-static struct snd_kcontrol_new snd_pmac_burgundy_hp_sw_imac __initdata =
+static struct snd_kcontrol_new snd_pmac_burgundy_hp_sw_imac __devinitdata =
 BURGUNDY_SWITCH_B("Headphone Playback Switch", 0,
 	MASK_ADDR_BURGUNDY_MORE_OUTPUTENABLES,
 	BURGUNDY_HP_LEFT, BURGUNDY_HP_RIGHT, 1);
@@ -618,7 +618,7 @@
 /*
  * initialize burgundy
  */
-int __init snd_pmac_burgundy_init(struct snd_pmac *chip)
+int __devinit snd_pmac_burgundy_init(struct snd_pmac *chip)
 {
 	int imac = machine_is_compatible("iMac");
 	int i, err;
diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c
index f8d478c..24200b7 100644
--- a/sound/ppc/daca.c
+++ b/sound/ppc/daca.c
@@ -244,7 +244,7 @@
 }
 
 /* exported */
-int __init snd_pmac_daca_init(struct snd_pmac *chip)
+int __devinit snd_pmac_daca_init(struct snd_pmac *chip)
 {
 	int i, err;
 	struct pmac_daca *mix;
diff --git a/sound/ppc/keywest.c b/sound/ppc/keywest.c
index a5afb26..835fa19 100644
--- a/sound/ppc/keywest.c
+++ b/sound/ppc/keywest.c
@@ -33,10 +33,6 @@
 static struct pmac_keywest *keywest_ctx;
 
 
-#ifndef i2c_device_name
-#define i2c_device_name(x)	((x)->name)
-#endif
-
 static int keywest_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
@@ -56,7 +52,7 @@
 	if (! keywest_ctx)
 		return -EINVAL;
 
-	if (strncmp(i2c_device_name(adapter), "mac-io", 6))
+	if (strncmp(adapter->name, "mac-io", 6))
 		return 0; /* ignored */
 
 	memset(&info, 0, sizeof(struct i2c_board_info));
@@ -109,7 +105,7 @@
 	}
 }
 
-int __init snd_pmac_tumbler_post_init(void)
+int __devinit snd_pmac_tumbler_post_init(void)
 {
 	int err;
 	
@@ -124,7 +120,7 @@
 }
 
 /* exported */
-int __init snd_pmac_keywest_init(struct pmac_keywest *i2c)
+int __devinit snd_pmac_keywest_init(struct pmac_keywest *i2c)
 {
 	int err;
 
diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c
index 9b4e9c3..7bc492e 100644
--- a/sound/ppc/pmac.c
+++ b/sound/ppc/pmac.c
@@ -702,7 +702,7 @@
 	.pointer =	snd_pmac_capture_pointer,
 };
 
-int __init snd_pmac_pcm_new(struct snd_pmac *chip)
+int __devinit snd_pmac_pcm_new(struct snd_pmac *chip)
 {
 	struct snd_pcm *pcm;
 	int err;
@@ -908,7 +908,7 @@
  * check the machine support byteswap (little-endian)
  */
 
-static void __init detect_byte_swap(struct snd_pmac *chip)
+static void __devinit detect_byte_swap(struct snd_pmac *chip)
 {
 	struct device_node *mio;
 
@@ -934,7 +934,7 @@
 /*
  * detect a sound chip
  */
-static int __init snd_pmac_detect(struct snd_pmac *chip)
+static int __devinit snd_pmac_detect(struct snd_pmac *chip)
 {
 	struct device_node *sound;
 	struct device_node *dn;
@@ -1143,7 +1143,7 @@
 	return 0;
 }
 
-static struct snd_kcontrol_new auto_mute_controls[] __initdata = {
+static struct snd_kcontrol_new auto_mute_controls[] __devinitdata = {
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	  .name = "Auto Mute Switch",
 	  .info = snd_pmac_boolean_mono_info,
@@ -1158,7 +1158,7 @@
 	},
 };
 
-int __init snd_pmac_add_automute(struct snd_pmac *chip)
+int __devinit snd_pmac_add_automute(struct snd_pmac *chip)
 {
 	int err;
 	chip->auto_mute = 1;
@@ -1175,7 +1175,7 @@
 /*
  * create and detect a pmac chip record
  */
-int __init snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
+int __devinit snd_pmac_new(struct snd_card *card, struct snd_pmac **chip_return)
 {
 	struct snd_pmac *chip;
 	struct device_node *np;
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index f361c26..53c81a5 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -18,80 +18,30 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
 #include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/io.h>
 #include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <sound/asound.h>
+#include <sound/control.h>
 #include <sound/core.h>
 #include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/asound.h>
 #include <sound/memalloc.h>
+#include <sound/pcm.h>
 #include <sound/pcm_params.h>
-#include <sound/control.h>
-#include <linux/dmapool.h>
-#include <linux/dma-mapping.h>
-#include <asm/firmware.h>
+
 #include <asm/dma.h>
+#include <asm/firmware.h>
 #include <asm/lv1call.h>
 #include <asm/ps3.h>
 #include <asm/ps3av.h>
 
-#include "snd_ps3_reg.h"
 #include "snd_ps3.h"
+#include "snd_ps3_reg.h"
 
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("PS3 sound driver");
-MODULE_AUTHOR("Sony Computer Entertainment Inc.");
-
-/* module  entries */
-static int __init snd_ps3_init(void);
-static void __exit snd_ps3_exit(void);
-
-/* ALSA snd driver ops */
-static int snd_ps3_pcm_open(struct snd_pcm_substream *substream);
-static int snd_ps3_pcm_close(struct snd_pcm_substream *substream);
-static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream);
-static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream,
-				 int cmd);
-static snd_pcm_uframes_t snd_ps3_pcm_pointer(struct snd_pcm_substream
-					     *substream);
-static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
-				 struct snd_pcm_hw_params *hw_params);
-static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream);
-
-
-/* ps3_system_bus_driver entries */
-static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev);
-static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev);
-
-/* address setup */
-static int snd_ps3_map_mmio(void);
-static void snd_ps3_unmap_mmio(void);
-static int snd_ps3_allocate_irq(void);
-static void snd_ps3_free_irq(void);
-static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start);
-
-/* interrupt handler */
-static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id);
-
-
-/* set sampling rate/format */
-static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream);
-/* take effect parameter change */
-static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card);
-/* initialize avsetting and take it effect */
-static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card);
-/* setup dma */
-static int snd_ps3_program_dma(struct snd_ps3_card_info *card,
-			       enum snd_ps3_dma_filltype filltype);
-static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card);
-
-static dma_addr_t v_to_bus(struct snd_ps3_card_info *, void  *vaddr, int ch);
-
-
-module_init(snd_ps3_init);
-module_exit(snd_ps3_exit);
 
 /*
  * global
@@ -165,25 +115,13 @@
 	.fifo_size = PS3_AUDIO_FIFO_SIZE
 };
 
-static struct snd_pcm_ops snd_ps3_pcm_spdif_ops =
-{
-	.open = snd_ps3_pcm_open,
-	.close = snd_ps3_pcm_close,
-	.prepare = snd_ps3_pcm_prepare,
-	.ioctl = snd_pcm_lib_ioctl,
-	.trigger = snd_ps3_pcm_trigger,
-	.pointer = snd_ps3_pcm_pointer,
-	.hw_params = snd_ps3_pcm_hw_params,
-	.hw_free = snd_ps3_pcm_hw_free
-};
-
 static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card,
 				   int count, int force_stop)
 {
 	int dma_ch, done, retries, stop_forced = 0;
 	uint32_t status;
 
-	for (dma_ch = 0; dma_ch < 8; dma_ch ++) {
+	for (dma_ch = 0; dma_ch < 8; dma_ch++) {
 		retries = count;
 		do {
 			status = read_reg(PS3_AUDIO_KICK(dma_ch)) &
@@ -259,9 +197,7 @@
 /*
  * convert virtual addr to ioif bus addr.
  */
-static dma_addr_t v_to_bus(struct snd_ps3_card_info *card,
-			   void * paddr,
-			   int ch)
+static dma_addr_t v_to_bus(struct snd_ps3_card_info *card, void *paddr, int ch)
 {
 	return card->dma_start_bus_addr[ch] +
 		(paddr - card->dma_start_vaddr[ch]);
@@ -321,7 +257,7 @@
 	spin_lock_irqsave(&card->dma_lock, irqsave);
 	for (ch = 0; ch < 2; ch++) {
 		start_vaddr = card->dma_next_transfer_vaddr[0];
-		for (stage = 0; stage < fill_stages; stage ++) {
+		for (stage = 0; stage < fill_stages; stage++) {
 			dma_ch = stage * 2 + ch;
 			if (silent)
 				dma_addr = card->null_buffer_start_dma_addr;
@@ -372,6 +308,71 @@
 }
 
 /*
+ * Interrupt handler
+ */
+static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id)
+{
+
+	uint32_t port_intr;
+	int underflow_occured = 0;
+	struct snd_ps3_card_info *card = dev_id;
+
+	if (!card->running) {
+		update_reg(PS3_AUDIO_AX_IS, 0);
+		update_reg(PS3_AUDIO_INTR_0, 0);
+		return IRQ_HANDLED;
+	}
+
+	port_intr = read_reg(PS3_AUDIO_AX_IS);
+	/*
+	 *serial buffer empty detected (every 4 times),
+	 *program next dma and kick it
+	 */
+	if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) {
+		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0));
+		if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
+			write_reg(PS3_AUDIO_AX_IS, port_intr);
+			underflow_occured = 1;
+		}
+		if (card->silent) {
+			/* we are still in silent time */
+			snd_ps3_program_dma(card,
+				(underflow_occured) ?
+				SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL :
+				SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
+			snd_ps3_kick_dma(card);
+			card->silent--;
+		} else {
+			snd_ps3_program_dma(card,
+				(underflow_occured) ?
+				SND_PS3_DMA_FILLTYPE_FIRSTFILL :
+				SND_PS3_DMA_FILLTYPE_RUNNING);
+			snd_ps3_kick_dma(card);
+			snd_pcm_period_elapsed(card->substream);
+		}
+	} else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
+		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0));
+		/*
+		 * serial out underflow, but buffer empty not detected.
+		 * in this case, fill fifo with 0 to recover.  After
+		 * filling dummy data, serial automatically start to
+		 * consume them and then will generate normal buffer
+		 * empty interrupts.
+		 * If both buffer underflow and buffer empty are occured,
+		 * it is better to do nomal data transfer than empty one
+		 */
+		snd_ps3_program_dma(card,
+				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+		snd_ps3_kick_dma(card);
+		snd_ps3_program_dma(card,
+				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+		snd_ps3_kick_dma(card);
+	}
+	/* clear interrupt cause */
+	return IRQ_HANDLED;
+};
+
+/*
  * audio mute on/off
  * mute_on : 0 output enabled
  *           1 mute
@@ -382,6 +383,142 @@
 }
 
 /*
+ * av setting
+ * NOTE: calling this function may generate audio interrupt.
+ */
+static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card)
+{
+	int ret, retries, i;
+	pr_debug("%s: start\n", __func__);
+
+	ret = ps3av_set_audio_mode(card->avs.avs_audio_ch,
+				  card->avs.avs_audio_rate,
+				  card->avs.avs_audio_width,
+				  card->avs.avs_audio_format,
+				  card->avs.avs_audio_source);
+	/*
+	 * Reset the following unwanted settings:
+	 */
+
+	/* disable all 3wire buffers */
+	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
+			~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) |
+			  PS3_AUDIO_AO_3WMCTRL_ASOEN(1) |
+			  PS3_AUDIO_AO_3WMCTRL_ASOEN(2) |
+			  PS3_AUDIO_AO_3WMCTRL_ASOEN(3)),
+			0);
+	wmb();	/* ensure the hardware sees the change */
+	/* wait for actually stopped */
+	retries = 1000;
+	while ((read_reg(PS3_AUDIO_AO_3WMCTRL) &
+		(PS3_AUDIO_AO_3WMCTRL_ASORUN(0) |
+		 PS3_AUDIO_AO_3WMCTRL_ASORUN(1) |
+		 PS3_AUDIO_AO_3WMCTRL_ASORUN(2) |
+		 PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) &&
+	       --retries) {
+		udelay(1);
+	}
+
+	/* reset buffer pointer */
+	for (i = 0; i < 4; i++) {
+		update_reg(PS3_AUDIO_AO_3WCTRL(i),
+			   PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET);
+		udelay(10);
+	}
+	wmb(); /* ensure the hardware actually start resetting */
+
+	/* enable 3wire#0 buffer */
+	update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0));
+
+
+	/* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */
+	update_mask_reg(PS3_AUDIO_AO_3WCTRL(0),
+			~PS3_AUDIO_AO_3WCTRL_ASODF,
+			PS3_AUDIO_AO_3WCTRL_ASODF_LSB);
+	update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0),
+			~PS3_AUDIO_AO_SPDCTRL_SPODF,
+			PS3_AUDIO_AO_SPDCTRL_SPODF_LSB);
+	/* ensure all the setting above is written back to register */
+	wmb();
+	/* avsetting driver altered AX_IE, caller must reset it if you want */
+	pr_debug("%s: end\n", __func__);
+	return ret;
+}
+
+/*
+ *  set sampling rate according to the substream
+ */
+static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream)
+{
+	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
+	struct snd_ps3_avsetting_info avs;
+	int ret;
+
+	avs = card->avs;
+
+	pr_debug("%s: called freq=%d width=%d\n", __func__,
+		 substream->runtime->rate,
+		 snd_pcm_format_width(substream->runtime->format));
+
+	pr_debug("%s: before freq=%d width=%d\n", __func__,
+		 card->avs.avs_audio_rate, card->avs.avs_audio_width);
+
+	/* sample rate */
+	switch (substream->runtime->rate) {
+	case 44100:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K;
+		break;
+	case 48000:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
+		break;
+	case 88200:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K;
+		break;
+	case 96000:
+		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K;
+		break;
+	default:
+		pr_info("%s: invalid rate %d\n", __func__,
+			substream->runtime->rate);
+		return 1;
+	}
+
+	/* width */
+	switch (snd_pcm_format_width(substream->runtime->format)) {
+	case 16:
+		avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
+		break;
+	case 24:
+		avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24;
+		break;
+	default:
+		pr_info("%s: invalid width %d\n", __func__,
+			snd_pcm_format_width(substream->runtime->format));
+		return 1;
+	}
+
+	memcpy(avs.avs_cs_info, ps3av_mode_cs_info, 8);
+
+	if (memcmp(&card->avs, &avs, sizeof(avs))) {
+		pr_debug("%s: after freq=%d width=%d\n", __func__,
+			 card->avs.avs_audio_rate, card->avs.avs_audio_width);
+
+		card->avs = avs;
+		snd_ps3_change_avsetting(card);
+		ret = 0;
+	} else
+		ret = 1;
+
+	/* check CS non-audio bit and mute accordingly */
+	if (avs.avs_cs_info[0] & 0x02)
+		ps3av_audio_mute_analog(1); /* mute if non-audio */
+	else
+		ps3av_audio_mute_analog(0);
+
+	return ret;
+}
+
+/*
  * PCM operators
  */
 static int snd_ps3_pcm_open(struct snd_pcm_substream *substream)
@@ -406,6 +543,13 @@
 	return 0;
 };
 
+static int snd_ps3_pcm_close(struct snd_pcm_substream *substream)
+{
+	/* mute on */
+	snd_ps3_mute(1);
+	return 0;
+};
+
 static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
 				 struct snd_pcm_hw_params *hw_params)
 {
@@ -417,6 +561,13 @@
 	return 0;
 };
 
+static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	int ret;
+	ret = snd_pcm_lib_free_pages(substream);
+	return ret;
+};
+
 static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream,
 				  unsigned int delay_ms)
 {
@@ -556,202 +707,6 @@
 	return ret;
 };
 
-static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-	int ret;
-	ret = snd_pcm_lib_free_pages(substream);
-	return ret;
-};
-
-static int snd_ps3_pcm_close(struct snd_pcm_substream *substream)
-{
-	/* mute on */
-	snd_ps3_mute(1);
-	return 0;
-};
-
-static void snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
-{
-	/*
-	 * avsetting driver seems to never change the followings
-	 * so, init them here once
-	 */
-
-	/* no dma interrupt needed */
-	write_reg(PS3_AUDIO_INTR_EN_0, 0);
-
-	/* use every 4 buffer empty interrupt */
-	update_mask_reg(PS3_AUDIO_AX_IC,
-			PS3_AUDIO_AX_IC_AASOIMD_MASK,
-			PS3_AUDIO_AX_IC_AASOIMD_EVERY4);
-
-	/* enable 3wire clocks */
-	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
-			~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED |
-			  PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED),
-			0);
-	update_reg(PS3_AUDIO_AO_3WMCTRL,
-		   PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT);
-}
-
-/*
- * av setting
- * NOTE: calling this function may generate audio interrupt.
- */
-static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card)
-{
-	int ret, retries, i;
-	pr_debug("%s: start\n", __func__);
-
-	ret = ps3av_set_audio_mode(card->avs.avs_audio_ch,
-				  card->avs.avs_audio_rate,
-				  card->avs.avs_audio_width,
-				  card->avs.avs_audio_format,
-				  card->avs.avs_audio_source);
-	/*
-	 * Reset the following unwanted settings:
-	 */
-
-	/* disable all 3wire buffers */
-	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
-			~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) |
-			  PS3_AUDIO_AO_3WMCTRL_ASOEN(1) |
-			  PS3_AUDIO_AO_3WMCTRL_ASOEN(2) |
-			  PS3_AUDIO_AO_3WMCTRL_ASOEN(3)),
-			0);
-	wmb(); 	/* ensure the hardware sees the change */
-	/* wait for actually stopped */
-	retries = 1000;
-	while ((read_reg(PS3_AUDIO_AO_3WMCTRL) &
-		(PS3_AUDIO_AO_3WMCTRL_ASORUN(0) |
-		 PS3_AUDIO_AO_3WMCTRL_ASORUN(1) |
-		 PS3_AUDIO_AO_3WMCTRL_ASORUN(2) |
-		 PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) &&
-	       --retries) {
-		udelay(1);
-	}
-
-	/* reset buffer pointer */
-	for (i = 0; i < 4; i++) {
-		update_reg(PS3_AUDIO_AO_3WCTRL(i),
-			   PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET);
-		udelay(10);
-	}
-	wmb(); /* ensure the hardware actually start resetting */
-
-	/* enable 3wire#0 buffer */
-	update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0));
-
-
-	/* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */
-	update_mask_reg(PS3_AUDIO_AO_3WCTRL(0),
-			~PS3_AUDIO_AO_3WCTRL_ASODF,
-			PS3_AUDIO_AO_3WCTRL_ASODF_LSB);
-	update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0),
-			~PS3_AUDIO_AO_SPDCTRL_SPODF,
-			PS3_AUDIO_AO_SPDCTRL_SPODF_LSB);
-	/* ensure all the setting above is written back to register */
-	wmb();
-	/* avsetting driver altered AX_IE, caller must reset it if you want */
-	pr_debug("%s: end\n", __func__);
-	return ret;
-}
-
-static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card)
-{
-	int ret;
-	pr_debug("%s: start\n", __func__);
-	card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2;
-	card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
-	card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
-	card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM;
-	card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL;
-	memcpy(card->avs.avs_cs_info, ps3av_mode_cs_info, 8);
-
-	ret = snd_ps3_change_avsetting(card);
-
-	snd_ps3_audio_fixup(card);
-
-	/* to start to generate SPDIF signal, fill data */
-	snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
-	snd_ps3_kick_dma(card);
-	pr_debug("%s: end\n", __func__);
-	return ret;
-}
-
-/*
- *  set sampling rate according to the substream
- */
-static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream)
-{
-	struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream);
-	struct snd_ps3_avsetting_info avs;
-	int ret;
-
-	avs = card->avs;
-
-	pr_debug("%s: called freq=%d width=%d\n", __func__,
-		 substream->runtime->rate,
-		 snd_pcm_format_width(substream->runtime->format));
-
-	pr_debug("%s: before freq=%d width=%d\n", __func__,
-		 card->avs.avs_audio_rate, card->avs.avs_audio_width);
-
-	/* sample rate */
-	switch (substream->runtime->rate) {
-	case 44100:
-		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K;
-		break;
-	case 48000:
-		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
-		break;
-	case 88200:
-		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K;
-		break;
-	case 96000:
-		avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K;
-		break;
-	default:
-		pr_info("%s: invalid rate %d\n", __func__,
-			substream->runtime->rate);
-		return 1;
-	}
-
-	/* width */
-	switch (snd_pcm_format_width(substream->runtime->format)) {
-	case 16:
-		avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
-		break;
-	case 24:
-		avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24;
-		break;
-	default:
-		pr_info("%s: invalid width %d\n", __func__,
-			snd_pcm_format_width(substream->runtime->format));
-		return 1;
-	}
-
-	memcpy(avs.avs_cs_info, ps3av_mode_cs_info, 8);
-
-	if (memcmp(&card->avs, &avs, sizeof(avs))) {
-		pr_debug("%s: after freq=%d width=%d\n", __func__,
-			 card->avs.avs_audio_rate, card->avs.avs_audio_width);
-
-		card->avs = avs;
-		snd_ps3_change_avsetting(card);
-		ret = 0;
-	} else
-		ret = 1;
-
-	/* check CS non-audio bit and mute accordingly */
-	if (avs.avs_cs_info[0] & 0x02)
-		ps3av_audio_mute_analog(1); /* mute if non-audio */
-	else
-		ps3av_audio_mute_analog(0);
-
-	return ret;
-}
-
 /*
  * SPDIF status bits controls
  */
@@ -798,28 +753,39 @@
 	{
 		.access = SNDRV_CTL_ELEM_ACCESS_READ,
 		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
-		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
 		.info = snd_ps3_spdif_mask_info,
 		.get = snd_ps3_spdif_cmask_get,
 	},
 	{
 		.access = SNDRV_CTL_ELEM_ACCESS_READ,
 		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
-		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK),
 		.info = snd_ps3_spdif_mask_info,
 		.get = snd_ps3_spdif_pmask_get,
 	},
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
-		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+		.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
 		.info = snd_ps3_spdif_mask_info,
 		.get = snd_ps3_spdif_default_get,
 		.put = snd_ps3_spdif_default_put,
 	},
 };
 
+static struct snd_pcm_ops snd_ps3_pcm_spdif_ops = {
+	.open = snd_ps3_pcm_open,
+	.close = snd_ps3_pcm_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = snd_ps3_pcm_hw_params,
+	.hw_free = snd_ps3_pcm_hw_free,
+	.prepare = snd_ps3_pcm_prepare,
+	.trigger = snd_ps3_pcm_trigger,
+	.pointer = snd_ps3_pcm_pointer,
+};
 
-static int snd_ps3_map_mmio(void)
+
+static int __devinit snd_ps3_map_mmio(void)
 {
 	the_card.mapped_mmio_vaddr =
 		ioremap(the_card.ps3_dev->m_region->bus_addr,
@@ -841,7 +807,7 @@
 	the_card.mapped_mmio_vaddr = NULL;
 }
 
-static int snd_ps3_allocate_irq(void)
+static int __devinit snd_ps3_allocate_irq(void)
 {
 	int ret;
 	u64 lpar_addr, lpar_size;
@@ -899,7 +865,7 @@
 	ps3_irq_plug_destroy(the_card.irq_no);
 }
 
-static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
+static void __devinit snd_ps3_audio_set_base_addr(uint64_t ioaddr_start)
 {
 	uint64_t val;
 	int ret;
@@ -915,7 +881,53 @@
 			ret);
 }
 
-static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
+static void __devinit snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
+{
+	/*
+	 * avsetting driver seems to never change the followings
+	 * so, init them here once
+	 */
+
+	/* no dma interrupt needed */
+	write_reg(PS3_AUDIO_INTR_EN_0, 0);
+
+	/* use every 4 buffer empty interrupt */
+	update_mask_reg(PS3_AUDIO_AX_IC,
+			PS3_AUDIO_AX_IC_AASOIMD_MASK,
+			PS3_AUDIO_AX_IC_AASOIMD_EVERY4);
+
+	/* enable 3wire clocks */
+	update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
+			~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED |
+			  PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED),
+			0);
+	update_reg(PS3_AUDIO_AO_3WMCTRL,
+		   PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT);
+}
+
+static int __devinit snd_ps3_init_avsetting(struct snd_ps3_card_info *card)
+{
+	int ret;
+	pr_debug("%s: start\n", __func__);
+	card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2;
+	card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K;
+	card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16;
+	card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM;
+	card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL;
+	memcpy(card->avs.avs_cs_info, ps3av_mode_cs_info, 8);
+
+	ret = snd_ps3_change_avsetting(card);
+
+	snd_ps3_audio_fixup(card);
+
+	/* to start to generate SPDIF signal, fill data */
+	snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
+	snd_ps3_kick_dma(card);
+	pr_debug("%s: end\n", __func__);
+	return ret;
+}
+
+static int __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
 {
 	int i, ret;
 	u64 lpar_addr, lpar_size;
@@ -1020,11 +1032,12 @@
 	 * its size should be lager than PS3_AUDIO_FIFO_STAGE_SIZE * 2
 	 * PAGE_SIZE is enogh
 	 */
-	if (!(the_card.null_buffer_start_vaddr =
-	      dma_alloc_coherent(&the_card.ps3_dev->core,
-				 PAGE_SIZE,
-				 &the_card.null_buffer_start_dma_addr,
-				 GFP_KERNEL))) {
+	the_card.null_buffer_start_vaddr =
+		dma_alloc_coherent(&the_card.ps3_dev->core,
+				   PAGE_SIZE,
+				   &the_card.null_buffer_start_dma_addr,
+				   GFP_KERNEL);
+	if (!the_card.null_buffer_start_vaddr) {
 		pr_info("%s: nullbuffer alloc failed\n", __func__);
 		goto clean_preallocate;
 	}
@@ -1115,71 +1128,6 @@
 
 
 /*
- * Interrupt handler
- */
-static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id)
-{
-
-	uint32_t port_intr;
-	int underflow_occured = 0;
-	struct snd_ps3_card_info *card = dev_id;
-
-	if (!card->running) {
-		update_reg(PS3_AUDIO_AX_IS, 0);
-		update_reg(PS3_AUDIO_INTR_0, 0);
-		return IRQ_HANDLED;
-	}
-
-	port_intr = read_reg(PS3_AUDIO_AX_IS);
-	/*
-	 *serial buffer empty detected (every 4 times),
-	 *program next dma and kick it
-	 */
-	if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) {
-		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0));
-		if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
-			write_reg(PS3_AUDIO_AX_IS, port_intr);
-			underflow_occured = 1;
-		}
-		if (card->silent) {
-			/* we are still in silent time */
-			snd_ps3_program_dma(card,
-				(underflow_occured) ?
-				SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL :
-				SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
-			snd_ps3_kick_dma(card);
-			card->silent --;
-		} else {
-			snd_ps3_program_dma(card,
-				(underflow_occured) ?
-				SND_PS3_DMA_FILLTYPE_FIRSTFILL :
-				SND_PS3_DMA_FILLTYPE_RUNNING);
-			snd_ps3_kick_dma(card);
-			snd_pcm_period_elapsed(card->substream);
-		}
-	} else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
-		write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0));
-		/*
-		 * serial out underflow, but buffer empty not detected.
-		 * in this case, fill fifo with 0 to recover.  After
-		 * filling dummy data, serial automatically start to
-		 * consume them and then will generate normal buffer
-		 * empty interrupts.
-		 * If both buffer underflow and buffer empty are occured,
-		 * it is better to do nomal data transfer than empty one
-		 */
-		snd_ps3_program_dma(card,
-				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
-		snd_ps3_kick_dma(card);
-		snd_ps3_program_dma(card,
-				    SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
-		snd_ps3_kick_dma(card);
-	}
-	/* clear interrupt cause */
-	return IRQ_HANDLED;
-};
-
-/*
  * module/subsystem initialize/terminate
  */
 static int __init snd_ps3_init(void)
@@ -1197,10 +1145,15 @@
 
 	return ret;
 }
+module_init(snd_ps3_init);
 
 static void __exit snd_ps3_exit(void)
 {
 	ps3_system_bus_driver_unregister(&snd_ps3_bus_driver_info);
 }
+module_exit(snd_ps3_exit);
 
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PS3 sound driver");
+MODULE_AUTHOR("Sony Computer Entertainment Inc.");
 MODULE_ALIAS(PS3_MODULE_ALIAS_SOUND);
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index 40222fc..08e584d 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -838,7 +838,7 @@
 
 /*
  */
-static struct snd_kcontrol_new tumbler_mixers[] __initdata = {
+static struct snd_kcontrol_new tumbler_mixers[] __devinitdata = {
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	  .name = "Master Playback Volume",
 	  .info = tumbler_info_master_volume,
@@ -862,7 +862,7 @@
 	},
 };
 
-static struct snd_kcontrol_new snapper_mixers[] __initdata = {
+static struct snd_kcontrol_new snapper_mixers[] __devinitdata = {
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	  .name = "Master Playback Volume",
 	  .info = tumbler_info_master_volume,
@@ -895,7 +895,7 @@
 	},
 };
 
-static struct snd_kcontrol_new tumbler_hp_sw __initdata = {
+static struct snd_kcontrol_new tumbler_hp_sw __devinitdata = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "Headphone Playback Switch",
 	.info = snd_pmac_boolean_mono_info,
@@ -903,7 +903,7 @@
 	.put = tumbler_put_mute_switch,
 	.private_value = TUMBLER_MUTE_HP,
 };
-static struct snd_kcontrol_new tumbler_speaker_sw __initdata = {
+static struct snd_kcontrol_new tumbler_speaker_sw __devinitdata = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "PC Speaker Playback Switch",
 	.info = snd_pmac_boolean_mono_info,
@@ -911,7 +911,7 @@
 	.put = tumbler_put_mute_switch,
 	.private_value = TUMBLER_MUTE_AMP,
 };
-static struct snd_kcontrol_new tumbler_lineout_sw __initdata = {
+static struct snd_kcontrol_new tumbler_lineout_sw __devinitdata = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "Line Out Playback Switch",
 	.info = snd_pmac_boolean_mono_info,
@@ -919,7 +919,7 @@
 	.put = tumbler_put_mute_switch,
 	.private_value = TUMBLER_MUTE_LINE,
 };
-static struct snd_kcontrol_new tumbler_drc_sw __initdata = {
+static struct snd_kcontrol_new tumbler_drc_sw __devinitdata = {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "DRC Switch",
 	.info = snd_pmac_boolean_mono_info,
@@ -1269,7 +1269,7 @@
 #endif
 
 /* initialize tumbler */
-static int __init tumbler_init(struct snd_pmac *chip)
+static int __devinit tumbler_init(struct snd_pmac *chip)
 {
 	int irq;
 	struct pmac_tumbler *mix = chip->mixer_data;
@@ -1339,7 +1339,7 @@
 }
 
 /* exported */
-int __init snd_pmac_tumbler_init(struct snd_pmac *chip)
+int __devinit snd_pmac_tumbler_init(struct snd_pmac *chip)
 {
 	int i, err;
 	struct pmac_tumbler *mix;
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 3d2bb6f..d3e786a 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -32,7 +32,9 @@
 source "sound/soc/omap/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
+source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
+source "sound/soc/txx9/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 8120b52..0c5eac0 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,4 +1,4 @@
-snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o
+snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o
 
 obj-$(CONFIG_SND_SOC)	+= snd-soc-core.o
 obj-$(CONFIG_SND_SOC)	+= codecs/
@@ -7,8 +7,10 @@
 obj-$(CONFIG_SND_SOC)	+= blackfin/
 obj-$(CONFIG_SND_SOC)	+= davinci/
 obj-$(CONFIG_SND_SOC)	+= fsl/
+obj-$(CONFIG_SND_SOC)   += imx/
 obj-$(CONFIG_SND_SOC)	+= omap/
 obj-$(CONFIG_SND_SOC)	+= pxa/
 obj-$(CONFIG_SND_SOC)	+= s3c24xx/
+obj-$(CONFIG_SND_SOC)	+= s6000/
 obj-$(CONFIG_SND_SOC)	+= sh/
-obj-$(CONFIG_SND_SOC)   += imx/
+obj-$(CONFIG_SND_SOC)	+= txx9/
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index a608d70..e720d5e 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -41,3 +41,11 @@
           and FRAME signals on the PlayPaq.  Unless you want to play
           with the AT32 as the SSC master, you probably want to say N here,
           as this will give you better sound quality.
+
+config SND_AT91_SOC_AFEB9260
+	tristate "SoC Audio support for AFEB9260 board"
+	depends on ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
+	select SND_ATMEL_SOC_SSC
+	select SND_SOC_TLV320AIC23
+	help
+	  Say Y here to support sound on AFEB9260 board.
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index f54a7cc..e7ea56b 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -13,3 +13,4 @@
 
 obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
 obj-$(CONFIG_SND_AT32_SOC_PLAYPAQ) += snd-soc-playpaq.o
+obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
diff --git a/sound/soc/atmel/playpaq_wm8510.c b/sound/soc/atmel/playpaq_wm8510.c
index 7065753..9eb610c 100644
--- a/sound/soc/atmel/playpaq_wm8510.c
+++ b/sound/soc/atmel/playpaq_wm8510.c
@@ -117,7 +117,7 @@
 	 * Find actual rate, compare to requested rate
 	 */
 	actual_rate = (cd.ssc_rate / (cd.cmr_div * 2)) / (2 * (cd.period + 1));
-	pr_debug("playpaq_wm8510: Request rate = %d, actual rate = %d\n",
+	pr_debug("playpaq_wm8510: Request rate = %u, actual rate = %u\n",
 		 rate, actual_rate);
 
 
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 173a239..130b121 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -56,133 +56,32 @@
 
 #define MCLK_RATE 12000000
 
+/*
+ * As shipped the board does not have inputs.  However, it is relatively
+ * straightforward to modify the board to hook them up so support is left
+ * in the driver.
+ */
+#undef ENABLE_MIC_INPUT
+
 static struct clk *mclk;
 
-static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
-	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
-	int ret;
-
-	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
-		MCLK_RATE, SND_SOC_CLOCK_IN);
-	if (ret < 0) {
-		clk_disable(mclk);
-		return ret;
-	}
-
-	return 0;
-}
-
-static void at91sam9g20ek_shutdown(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
-
-	dev_dbg(rtd->socdev->dev, "shutdown");
-}
-
 static int at91sam9g20ek_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;
-	struct atmel_ssc_info *ssc_p = cpu_dai->private_data;
-	struct ssc_device *ssc = ssc_p->ssc;
 	int ret;
 
-	unsigned int rate;
-	int cmr_div, period;
-
-	if (ssc == NULL) {
-		printk(KERN_INFO "at91sam9g20ek_hw_params: ssc is NULL!\n");
-		return -EINVAL;
-	}
-
 	/* set codec DAI configuration */
 	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
-		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 		return ret;
 
 	/* set cpu DAI configuration */
 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
-		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
-	if (ret < 0)
-		return ret;
-
-	/*
-	 * The SSC clock dividers depend on the sample rate.  The CMR.DIV
-	 * field divides the system master clock MCK to drive the SSC TK
-	 * signal which provides the codec BCLK.  The TCMR.PERIOD and
-	 * RCMR.PERIOD fields further divide the BCLK signal to drive
-	 * the SSC TF and RF signals which provide the codec DACLRC and
-	 * ADCLRC clocks.
-	 *
-	 * The dividers were determined through trial and error, where a
-	 * CMR.DIV value is chosen such that the resulting BCLK value is
-	 * divisible, or almost divisible, by (2 * sample rate), and then
-	 * the TCMR.PERIOD or RCMR.PERIOD is BCLK / (2 * sample rate) - 1.
-	 */
-	rate = params_rate(params);
-
-	switch (rate) {
-	case 8000:
-		cmr_div = 55;	/* BCLK = 133MHz/(2*55) = 1.209MHz */
-		period = 74;	/* LRC = BCLK/(2*(74+1)) ~= 8060,6Hz */
-		break;
-	case 11025:
-		cmr_div = 67;	/* BCLK = 133MHz/(2*60) = 1.108MHz */
-		period = 45;	/* LRC = BCLK/(2*(49+1)) = 11083,3Hz */
-		break;
-	case 16000:
-		cmr_div = 63;	/* BCLK = 133MHz/(2*63) = 1.055MHz */
-		period = 32;	/* LRC = BCLK/(2*(32+1)) = 15993,2Hz */
-		break;
-	case 22050:
-		cmr_div = 52;	/* BCLK = 133MHz/(2*52) = 1.278MHz */
-		period = 28;	/* LRC = BCLK/(2*(28+1)) = 22049Hz */
-		break;
-	case 32000:
-		cmr_div = 66;	/* BCLK = 133MHz/(2*66) = 1.007MHz */
-		period = 15;	/* LRC = BCLK/(2*(15+1)) = 31486,742Hz */
-		break;
-	case 44100:
-		cmr_div = 29;	/* BCLK = 133MHz/(2*29) = 2.293MHz */
-		period = 25;	/* LRC = BCLK/(2*(25+1)) = 44098Hz */
-		break;
-	case 48000:
-		cmr_div = 33;	/* BCLK = 133MHz/(2*33) = 2.015MHz */
-		period = 20;	/* LRC = BCLK/(2*(20+1)) = 47979,79Hz */
-		break;
-	case 88200:
-		cmr_div = 29;	/* BCLK = 133MHz/(2*29) = 2.293MHz */
-		period = 12;	/* LRC = BCLK/(2*(12+1)) = 88196Hz */
-		break;
-	case 96000:
-		cmr_div = 23;	/* BCLK = 133MHz/(2*23) = 2.891MHz */
-		period = 14;	/* LRC = BCLK/(2*(14+1)) = 96376Hz */
-		break;
-	default:
-		printk(KERN_WARNING "unsupported rate %d"
-				" on at91sam9g20ek board\n", rate);
-		return -EINVAL;
-	}
-
-	/* set the MCK divider for BCLK */
-	ret = snd_soc_dai_set_clkdiv(cpu_dai, ATMEL_SSC_CMR_DIV, cmr_div);
-	if (ret < 0)
-		return ret;
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		/* set the BCLK divider for DACLRC */
-		ret = snd_soc_dai_set_clkdiv(cpu_dai,
-						ATMEL_SSC_TCMR_PERIOD, period);
-	} else {
-		/* set the BCLK divider for ADCLRC */
-		ret = snd_soc_dai_set_clkdiv(cpu_dai,
-						ATMEL_SSC_RCMR_PERIOD, period);
-	}
+		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 		return ret;
 
@@ -190,9 +89,7 @@
 }
 
 static struct snd_soc_ops at91sam9g20ek_ops = {
-	.startup = at91sam9g20ek_startup,
 	.hw_params = at91sam9g20ek_hw_params,
-	.shutdown = at91sam9g20ek_shutdown,
 };
 
 static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
@@ -241,10 +138,20 @@
  */
 static int at91sam9g20ek_wm8731_init(struct snd_soc_codec *codec)
 {
+	struct snd_soc_dai *codec_dai = &codec->dai[0];
+	int ret;
+
 	printk(KERN_DEBUG
 			"at91sam9g20ek_wm8731 "
 			": at91sam9g20ek_wm8731_init() called\n");
 
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
+		MCLK_RATE, SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		printk(KERN_ERR "Failed to set WM8731 SYSCLK: %d\n", ret);
+		return ret;
+	}
+
 	/* Add specific widgets */
 	snd_soc_dapm_new_controls(codec, at91sam9g20ek_dapm_widgets,
 				  ARRAY_SIZE(at91sam9g20ek_dapm_widgets));
@@ -255,8 +162,13 @@
 	snd_soc_dapm_nc_pin(codec, "RLINEIN");
 	snd_soc_dapm_nc_pin(codec, "LLINEIN");
 
-	/* always connected */
+#ifdef ENABLE_MIC_INPUT
 	snd_soc_dapm_enable_pin(codec, "Int Mic");
+#else
+	snd_soc_dapm_nc_pin(codec, "Int Mic");
+#endif
+
+	/* always connected */
 	snd_soc_dapm_enable_pin(codec, "Ext Spk");
 
 	snd_soc_dapm_sync(codec);
diff --git a/sound/soc/atmel/snd-soc-afeb9260.c b/sound/soc/atmel/snd-soc-afeb9260.c
new file mode 100644
index 0000000..23349de
--- /dev/null
+++ b/sound/soc/atmel/snd-soc-afeb9260.c
@@ -0,0 +1,203 @@
+/*
+ * afeb9260.c  --  SoC audio for AFEB9260
+ *
+ * Copyright (C) 2009 Sergey Lapin <slapin@ossfans.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include <linux/atmel-ssc.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <linux/gpio.h>
+
+#include "../codecs/tlv320aic23.h"
+#include "atmel-pcm.h"
+#include "atmel_ssc_dai.h"
+
+#define CODEC_CLOCK 	12000000
+
+static int afeb9260_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 err;
+
+	/* Set codec DAI configuration */
+	err = snd_soc_dai_set_fmt(codec_dai,
+				  SND_SOC_DAIFMT_I2S|
+				  SND_SOC_DAIFMT_NB_IF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+	if (err < 0) {
+		printk(KERN_ERR "can't set codec DAI configuration\n");
+		return err;
+	}
+
+	/* Set cpu DAI configuration */
+	err = snd_soc_dai_set_fmt(cpu_dai,
+				  SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_IF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+	if (err < 0) {
+		printk(KERN_ERR "can't set cpu DAI configuration\n");
+		return err;
+	}
+
+	/* Set the codec system clock for DAC and ADC */
+	err =
+	    snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
+
+	if (err < 0) {
+		printk(KERN_ERR "can't set codec system clock\n");
+		return err;
+	}
+
+	return err;
+}
+
+static struct snd_soc_ops afeb9260_ops = {
+	.hw_params = afeb9260_hw_params,
+};
+
+static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_LINE("Line In", NULL),
+	SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	{"Headphone Jack", NULL, "LHPOUT"},
+	{"Headphone Jack", NULL, "RHPOUT"},
+
+	{"LLINEIN", NULL, "Line In"},
+	{"RLINEIN", NULL, "Line In"},
+
+	{"MICIN", NULL, "Mic Jack"},
+};
+
+static int afeb9260_tlv320aic23_init(struct snd_soc_codec *codec)
+{
+
+	/* Add afeb9260 specific widgets */
+	snd_soc_dapm_new_controls(codec, tlv320aic23_dapm_widgets,
+				  ARRAY_SIZE(tlv320aic23_dapm_widgets));
+
+	/* Set up afeb9260 specific audio path audio_map */
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+	snd_soc_dapm_enable_pin(codec, "Line In");
+	snd_soc_dapm_enable_pin(codec, "Mic Jack");
+
+	snd_soc_dapm_sync(codec);
+
+	return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link afeb9260_dai = {
+	.name = "TLV320AIC23",
+	.stream_name = "AIC23",
+	.cpu_dai = &atmel_ssc_dai[0],
+	.codec_dai = &tlv320aic23_dai,
+	.init = afeb9260_tlv320aic23_init,
+	.ops = &afeb9260_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_machine_afeb9260 = {
+	.name = "AFEB9260",
+	.platform = &atmel_soc_platform,
+	.dai_link = &afeb9260_dai,
+	.num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device afeb9260_snd_devdata = {
+	.card = &snd_soc_machine_afeb9260,
+	.codec_dev = &soc_codec_dev_tlv320aic23,
+};
+
+static struct platform_device *afeb9260_snd_device;
+
+static int __init afeb9260_soc_init(void)
+{
+	int err;
+	struct device *dev;
+	struct atmel_ssc_info *ssc_p = afeb9260_dai.cpu_dai->private_data;
+	struct ssc_device *ssc = NULL;
+
+	if (!(machine_is_afeb9260()))
+		return -ENODEV;
+
+	ssc = ssc_request(0);
+	if (IS_ERR(ssc)) {
+		printk(KERN_ERR "ASoC: Failed to request SSC 0\n");
+		err = PTR_ERR(ssc);
+		ssc = NULL;
+		goto err_ssc;
+	}
+	ssc_p->ssc = ssc;
+
+	afeb9260_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!afeb9260_snd_device) {
+		printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(afeb9260_snd_device, &afeb9260_snd_devdata);
+	afeb9260_snd_devdata.dev = &afeb9260_snd_device->dev;
+	err = platform_device_add(afeb9260_snd_device);
+	if (err)
+		goto err1;
+
+	dev = &afeb9260_snd_device->dev;
+
+	return 0;
+err1:
+	platform_device_del(afeb9260_snd_device);
+	platform_device_put(afeb9260_snd_device);
+err_ssc:
+	return err;
+
+}
+
+static void __exit afeb9260_soc_exit(void)
+{
+	platform_device_unregister(afeb9260_snd_device);
+}
+
+module_init(afeb9260_soc_init);
+module_exit(afeb9260_soc_exit);
+
+MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>");
+MODULE_DESCRIPTION("ALSA SoC for AFEB9260");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
index 811596f..8a4de4de 100644
--- a/sound/soc/blackfin/Kconfig
+++ b/sound/soc/blackfin/Kconfig
@@ -7,6 +7,15 @@
 	  mode (supports single stereo In/Out).
 	  You will also need to select the audio interfaces to support below.
 
+config SND_BF5XX_TDM
+	tristate "SoC I2S(TDM mode) Audio for the ADI BF5xx chip"
+	depends on (BLACKFIN && SND_SOC)
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the Blackfin SPORT (synchronous serial ports) interface in TDM
+	  mode.
+	  You will also need to select the audio interfaces to support below.
+
 config SND_BF5XX_SOC_SSM2602
 	tristate "SoC SSM2602 Audio support for BF52x ezkit"
 	depends on SND_BF5XX_I2S
@@ -69,6 +78,10 @@
 	tristate
 	select SND_BF5XX_SOC_SPORT
 
+config SND_BF5XX_SOC_TDM
+	tristate
+	select SND_BF5XX_SOC_SPORT
+
 config SND_BF5XX_SOC_AC97
 	tristate
 	select AC97_BUS
@@ -83,9 +96,17 @@
 	help
 	  Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
 
+config SND_BF5XX_SOC_AD1938
+        tristate "SoC AD1938 Audio support for Blackfin"
+        depends on SND_BF5XX_TDM
+        select SND_BF5XX_SOC_TDM
+        select SND_SOC_AD1938
+        help
+          Say Y if you want to add support for AD1938 codec on Blackfin.
+
 config SND_BF5XX_SPORT_NUM
 	int "Set a SPORT for Sound chip"
-	depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
+	depends on (SND_BF5XX_I2S || SND_BF5XX_AC97 || SND_BF5XX_TDM)
 	range 0 3 if BF54x
 	range 0 1 if !BF54x
 	default 0
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
index 97bb37a..f4d7607 100644
--- a/sound/soc/blackfin/Makefile
+++ b/sound/soc/blackfin/Makefile
@@ -1,21 +1,27 @@
 # Blackfin Platform Support
 snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
 snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
+snd-bf5xx-tdm-objs := bf5xx-tdm-pcm.o
 snd-soc-bf5xx-sport-objs := bf5xx-sport.o
 snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
 snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
+snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o
 
 obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
 obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
+obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.o
 obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
 obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o
 obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
+obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o
 
 # Blackfin Machine Support
 snd-ad1980-objs := bf5xx-ad1980.o
 snd-ssm2602-objs := bf5xx-ssm2602.o
 snd-ad73311-objs := bf5xx-ad73311.o
+snd-ad1938-objs := bf5xx-ad1938.o
 
 obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
 obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
 obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
+obj-$(CONFIG_SND_BF5XX_SOC_AD1938) += snd-ad1938.o
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index 8a935f2..2758b90 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -31,6 +31,15 @@
 #include "bf5xx-sport.h"
 #include "bf5xx-ac97.h"
 
+/* Anomaly notes:
+ *  05000250 -	AD1980 is running in TDM mode and RFS/TFS are generated by SPORT
+ *		contrtoller. But, RFSDIV and TFSDIV are always set to 16*16-1,
+ *		while the max AC97 data size is 13*16. The DIV is always larger
+ *		than data size. AD73311 and ad2602 are not running in TDM mode.
+ *		AD1836 and AD73322 depend on external RFS/TFS only. So, this
+ *		anomaly does not affect blackfin sound drivers.
+*/
+
 static int *cmd_count;
 static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
 
@@ -268,28 +277,24 @@
 	if (!dai->active)
 		return 0;
 
-	ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
+	ret = sport_set_multichannel(sport, 16, 0x1F, 1);
 	if (ret) {
 		pr_err("SPORT is busy!\n");
 		return -EBUSY;
 	}
 
-	ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
+	ret = sport_config_rx(sport, IRFS, 0xF, 0, (16*16-1));
 	if (ret) {
 		pr_err("SPORT is busy!\n");
 		return -EBUSY;
 	}
 
-	ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
+	ret = sport_config_tx(sport, ITFS, 0xF, 0, (16*16-1));
 	if (ret) {
 		pr_err("SPORT is busy!\n");
 		return -EBUSY;
 	}
 
-	if (dai->capture.active)
-		sport_rx_start(sport);
-	if (dai->playback.active)
-		sport_tx_start(sport);
 	return 0;
 }
 
diff --git a/sound/soc/blackfin/bf5xx-ad1938.c b/sound/soc/blackfin/bf5xx-ad1938.c
new file mode 100644
index 0000000..08269e9
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ad1938.c
@@ -0,0 +1,142 @@
+/*
+ * File:         sound/soc/blackfin/bf5xx-ad1938.c
+ * Author:       Barry Song <Barry.Song@analog.com>
+ *
+ * Created:      Thur June 4 2009
+ * Description:  Board driver for ad1938 sound chip
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm_params.h>
+
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+#include "../codecs/ad1938.h"
+#include "bf5xx-sport.h"
+
+#include "bf5xx-tdm-pcm.h"
+#include "bf5xx-tdm.h"
+
+static struct snd_soc_card bf5xx_ad1938;
+
+static int bf5xx_ad1938_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	cpu_dai->private_data = sport_handle;
+	return 0;
+}
+
+static int bf5xx_ad1938_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	int ret = 0;
+	/* set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
+		SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_A |
+		SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set codec DAI slots, 8 channels, all channels are enabled */
+	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops bf5xx_ad1938_ops = {
+	.startup = bf5xx_ad1938_startup,
+	.hw_params = bf5xx_ad1938_hw_params,
+};
+
+static struct snd_soc_dai_link bf5xx_ad1938_dai = {
+	.name = "ad1938",
+	.stream_name = "AD1938",
+	.cpu_dai = &bf5xx_tdm_dai,
+	.codec_dai = &ad1938_dai,
+	.ops = &bf5xx_ad1938_ops,
+};
+
+static struct snd_soc_card bf5xx_ad1938 = {
+	.name = "bf5xx_ad1938",
+	.platform = &bf5xx_tdm_soc_platform,
+	.dai_link = &bf5xx_ad1938_dai,
+	.num_links = 1,
+};
+
+static struct snd_soc_device bf5xx_ad1938_snd_devdata = {
+	.card = &bf5xx_ad1938,
+	.codec_dev = &soc_codec_dev_ad1938,
+};
+
+static struct platform_device *bfxx_ad1938_snd_device;
+
+static int __init bf5xx_ad1938_init(void)
+{
+	int ret;
+
+	bfxx_ad1938_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!bfxx_ad1938_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(bfxx_ad1938_snd_device, &bf5xx_ad1938_snd_devdata);
+	bf5xx_ad1938_snd_devdata.dev = &bfxx_ad1938_snd_device->dev;
+	ret = platform_device_add(bfxx_ad1938_snd_device);
+
+	if (ret)
+		platform_device_put(bfxx_ad1938_snd_device);
+
+	return ret;
+}
+
+static void __exit bf5xx_ad1938_exit(void)
+{
+	platform_device_unregister(bfxx_ad1938_snd_device);
+}
+
+module_init(bf5xx_ad1938_init);
+module_exit(bf5xx_ad1938_exit);
+
+/* Module information */
+MODULE_AUTHOR("Barry Song");
+MODULE_DESCRIPTION("ALSA SoC AD1938 board driver");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
index edfbdc0..9825b71 100644
--- a/sound/soc/blackfin/bf5xx-ad73311.c
+++ b/sound/soc/blackfin/bf5xx-ad73311.c
@@ -203,23 +203,23 @@
 	.codec_dev = &soc_codec_dev_ad73311,
 };
 
-static struct platform_device *bf52x_ad73311_snd_device;
+static struct platform_device *bf5xx_ad73311_snd_device;
 
 static int __init bf5xx_ad73311_init(void)
 {
 	int ret;
 
 	pr_debug("%s enter\n", __func__);
-	bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
-	if (!bf52x_ad73311_snd_device)
+	bf5xx_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!bf5xx_ad73311_snd_device)
 		return -ENOMEM;
 
-	platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
-	bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev;
-	ret = platform_device_add(bf52x_ad73311_snd_device);
+	platform_set_drvdata(bf5xx_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
+	bf5xx_ad73311_snd_devdata.dev = &bf5xx_ad73311_snd_device->dev;
+	ret = platform_device_add(bf5xx_ad73311_snd_device);
 
 	if (ret)
-		platform_device_put(bf52x_ad73311_snd_device);
+		platform_device_put(bf5xx_ad73311_snd_device);
 
 	return ret;
 }
@@ -227,7 +227,7 @@
 static void __exit bf5xx_ad73311_exit(void)
 {
 	pr_debug("%s enter\n", __func__);
-	platform_device_unregister(bf52x_ad73311_snd_device);
+	platform_device_unregister(bf5xx_ad73311_snd_device);
 }
 
 module_init(bf5xx_ad73311_init);
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
index 9648244..876abad 100644
--- a/sound/soc/blackfin/bf5xx-i2s.c
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -50,6 +50,7 @@
 	u16 tcr2;
 	u16 rcr2;
 	int counter;
+	int configured;
 };
 
 static struct bf5xx_i2s_port bf5xx_i2s;
@@ -168,7 +169,7 @@
 		break;
 	}
 
-	if (bf5xx_i2s.counter == 1) {
+	if (!bf5xx_i2s.configured) {
 		/*
 		 * TX and RX are not independent,they are enabled at the
 		 * same time, even if only one side is running. So, we
@@ -177,6 +178,7 @@
 		 *
 		 * CPU DAI:slave mode.
 		 */
+		bf5xx_i2s.configured = 1;
 		ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
 				      bf5xx_i2s.rcr2, 0, 0);
 		if (ret) {
@@ -200,6 +202,9 @@
 {
 	pr_debug("%s enter\n", __func__);
 	bf5xx_i2s.counter--;
+	/* No active stream, SPORT is allowed to be configured again. */
+	if (!bf5xx_i2s.counter)
+		bf5xx_i2s.configured = 0;
 }
 
 static int bf5xx_i2s_probe(struct platform_device *pdev,
@@ -244,8 +249,7 @@
 	return 0;
 }
 
-static int bf5xx_i2s_resume(struct platform_device *pdev,
-			    struct snd_soc_dai *dai)
+static int bf5xx_i2s_resume(struct snd_soc_dai *dai)
 {
 	int ret;
 	struct sport_device *sport =
@@ -255,22 +259,18 @@
 	if (!dai->active)
 		return 0;
 
-	ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
+	ret = sport_config_rx(sport, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
 	if (ret) {
 		pr_err("SPORT is busy!\n");
 		return -EBUSY;
 	}
 
-	ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
+	ret = sport_config_tx(sport, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
 	if (ret) {
 		pr_err("SPORT is busy!\n");
 		return -EBUSY;
 	}
 
-	if (dai->capture.active)
-		sport_rx_start(sport);
-	if (dai->playback.active)
-		sport_tx_start(sport);
 	return 0;
 }
 
diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c
index b7953c8..469ce7f 100644
--- a/sound/soc/blackfin/bf5xx-sport.c
+++ b/sound/soc/blackfin/bf5xx-sport.c
@@ -190,7 +190,7 @@
 	desc = get_dma_next_desc_ptr(sport->dma_rx_chan);
 	/* Copy the descriptor which will be damaged to backup */
 	temp_desc = *desc;
-	desc->x_count = 0xa;
+	desc->x_count = sport->dummy_count / 2;
 	desc->y_count = 0;
 	desc->next_desc_addr = sport->dummy_rx_desc;
 	local_irq_restore(flags);
@@ -309,7 +309,7 @@
 	desc = get_dma_next_desc_ptr(sport->dma_tx_chan);
 	/* Store the descriptor which will be damaged */
 	temp_desc = *desc;
-	desc->x_count = 0xa;
+	desc->x_count = sport->dummy_count / 2;
 	desc->y_count = 0;
 	desc->next_desc_addr = sport->dummy_tx_desc;
 	local_irq_restore(flags);
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
index bc0cdde..3a00fa4 100644
--- a/sound/soc/blackfin/bf5xx-ssm2602.c
+++ b/sound/soc/blackfin/bf5xx-ssm2602.c
@@ -148,24 +148,24 @@
 	.codec_data = &bf5xx_ssm2602_setup,
 };
 
-static struct platform_device *bf52x_ssm2602_snd_device;
+static struct platform_device *bf5xx_ssm2602_snd_device;
 
 static int __init bf5xx_ssm2602_init(void)
 {
 	int ret;
 
 	pr_debug("%s enter\n", __func__);
-	bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
-	if (!bf52x_ssm2602_snd_device)
+	bf5xx_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!bf5xx_ssm2602_snd_device)
 		return -ENOMEM;
 
-	platform_set_drvdata(bf52x_ssm2602_snd_device,
+	platform_set_drvdata(bf5xx_ssm2602_snd_device,
 				&bf5xx_ssm2602_snd_devdata);
-	bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev;
-	ret = platform_device_add(bf52x_ssm2602_snd_device);
+	bf5xx_ssm2602_snd_devdata.dev = &bf5xx_ssm2602_snd_device->dev;
+	ret = platform_device_add(bf5xx_ssm2602_snd_device);
 
 	if (ret)
-		platform_device_put(bf52x_ssm2602_snd_device);
+		platform_device_put(bf5xx_ssm2602_snd_device);
 
 	return ret;
 }
@@ -173,7 +173,7 @@
 static void __exit bf5xx_ssm2602_exit(void)
 {
 	pr_debug("%s enter\n", __func__);
-	platform_device_unregister(bf52x_ssm2602_snd_device);
+	platform_device_unregister(bf5xx_ssm2602_snd_device);
 }
 
 module_init(bf5xx_ssm2602_init);
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
new file mode 100644
index 0000000..ccb5e82
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c
@@ -0,0 +1,330 @@
+/*
+ * File:         sound/soc/blackfin/bf5xx-tdm-pcm.c
+ * Author:       Barry Song <Barry.Song@analog.com>
+ *
+ * Created:      Tue June 06 2009
+ * Description:  DMA driver for tdm codec
+ *
+ * Modified:
+ *               Copyright 2009 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+
+#include "bf5xx-tdm-pcm.h"
+#include "bf5xx-tdm.h"
+#include "bf5xx-sport.h"
+
+#define PCM_BUFFER_MAX  0x10000
+#define FRAGMENT_SIZE_MIN  (4*1024)
+#define FRAGMENTS_MIN  2
+#define FRAGMENTS_MAX  32
+
+static void bf5xx_dma_irq(void *data)
+{
+	struct snd_pcm_substream *pcm = data;
+	snd_pcm_period_elapsed(pcm);
+}
+
+static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
+	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_RESUME),
+	.formats =          SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =            SNDRV_PCM_RATE_48000,
+	.channels_min =     2,
+	.channels_max =     8,
+	.buffer_bytes_max = PCM_BUFFER_MAX,
+	.period_bytes_min = FRAGMENT_SIZE_MIN,
+	.period_bytes_max = PCM_BUFFER_MAX/2,
+	.periods_min =      FRAGMENTS_MIN,
+	.periods_max =      FRAGMENTS_MAX,
+};
+
+static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
+	snd_pcm_lib_malloc_pages(substream, size * 4);
+
+	return 0;
+}
+
+static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_lib_free_pages(substream);
+
+	return 0;
+}
+
+static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+	int fragsize_bytes = frames_to_bytes(runtime, runtime->period_size);
+
+	fragsize_bytes /= runtime->channels;
+	/* inflate the fragsize to match the dma width of SPORT */
+	fragsize_bytes *= 8;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
+		sport_config_tx_dma(sport, runtime->dma_area,
+			runtime->periods, fragsize_bytes);
+	} else {
+		sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
+		sport_config_rx_dma(sport, runtime->dma_area,
+			runtime->periods, fragsize_bytes);
+	}
+
+	return 0;
+}
+
+static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			sport_tx_start(sport);
+		else
+			sport_rx_start(sport);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			sport_tx_stop(sport);
+		else
+			sport_rx_stop(sport);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+	unsigned int diff;
+	snd_pcm_uframes_t frames;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		diff = sport_curr_offset_tx(sport);
+		frames = diff / (8*4); /* 32 bytes per frame */
+	} else {
+		diff = sport_curr_offset_rx(sport);
+		frames = diff / (8*4);
+	}
+	return frames;
+}
+
+static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int ret = 0;
+
+	snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+		SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		goto out;
+
+	if (sport_handle != NULL)
+		runtime->private_data = sport_handle;
+	else {
+		pr_err("sport_handle is NULL\n");
+		ret = -ENODEV;
+	}
+out:
+	return ret;
+}
+
+static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
+	snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
+{
+	unsigned int *src;
+	unsigned int *dst;
+	int i;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		src = buf;
+		dst = (unsigned int *)substream->runtime->dma_area;
+
+		dst += pos * 8;
+		while (count--) {
+			for (i = 0; i < substream->runtime->channels; i++)
+				*(dst + i) = *src++;
+			dst += 8;
+		}
+	} else {
+		src = (unsigned int *)substream->runtime->dma_area;
+		dst = buf;
+
+		src += pos * 8;
+		while (count--) {
+			for (i = 0; i < substream->runtime->channels; i++)
+				*dst++ = *(src+i);
+			src += 8;
+		}
+	}
+
+	return 0;
+}
+
+static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
+	int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count)
+{
+	unsigned char *buf = substream->runtime->dma_area;
+	buf += pos * 8 * 4;
+	memset(buf, '\0', count * 8 * 4);
+
+	return 0;
+}
+
+
+struct snd_pcm_ops bf5xx_pcm_tdm_ops = {
+	.open           = bf5xx_pcm_open,
+	.ioctl          = snd_pcm_lib_ioctl,
+	.hw_params      = bf5xx_pcm_hw_params,
+	.hw_free        = bf5xx_pcm_hw_free,
+	.prepare        = bf5xx_pcm_prepare,
+	.trigger        = bf5xx_pcm_trigger,
+	.pointer        = bf5xx_pcm_pointer,
+	.copy           = bf5xx_pcm_copy,
+	.silence        = bf5xx_pcm_silence,
+};
+
+static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->area = dma_alloc_coherent(pcm->card->dev, size * 4,
+		&buf->addr, GFP_KERNEL);
+	if (!buf->area) {
+		pr_err("Failed to allocate dma memory \
+			Please increase uncached DMA memory region\n");
+		return -ENOMEM;
+	}
+	buf->bytes = size;
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		sport_handle->tx_buf = buf->area;
+	else
+		sport_handle->rx_buf = buf->area;
+
+	return 0;
+}
+
+static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		dma_free_coherent(NULL, buf->bytes, buf->area, 0);
+		buf->area = NULL;
+	}
+	if (sport_handle)
+		sport_done(sport_handle);
+}
+
+static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32);
+
+static int bf5xx_pcm_tdm_new(struct snd_card *card, struct snd_soc_dai *dai,
+	struct snd_pcm *pcm)
+{
+	int ret = 0;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &bf5xx_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	if (dai->playback.channels_min) {
+		ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto out;
+	}
+
+	if (dai->capture.channels_min) {
+		ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto out;
+	}
+out:
+	return ret;
+}
+
+struct snd_soc_platform bf5xx_tdm_soc_platform = {
+	.name           = "bf5xx-audio",
+	.pcm_ops        = &bf5xx_pcm_tdm_ops,
+	.pcm_new        = bf5xx_pcm_tdm_new,
+	.pcm_free       = bf5xx_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(bf5xx_tdm_soc_platform);
+
+static int __init bfin_pcm_tdm_init(void)
+{
+	return snd_soc_register_platform(&bf5xx_tdm_soc_platform);
+}
+module_init(bfin_pcm_tdm_init);
+
+static void __exit bfin_pcm_tdm_exit(void)
+{
+	snd_soc_unregister_platform(&bf5xx_tdm_soc_platform);
+}
+module_exit(bfin_pcm_tdm_exit);
+
+MODULE_AUTHOR("Barry Song");
+MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.h b/sound/soc/blackfin/bf5xx-tdm-pcm.h
new file mode 100644
index 0000000..ddc5047
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.h
@@ -0,0 +1,21 @@
+/*
+ * sound/soc/blackfin/bf5xx-tdm-pcm.h -- ALSA PCM interface for the Blackfin
+ *
+ * Copyright 2009 Analog Device Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BF5XX_TDM_PCM_H
+#define _BF5XX_TDM_PCM_H
+
+struct bf5xx_pcm_dma_params {
+	char *name;                     /* stream identifier */
+};
+
+/* platform data */
+extern struct snd_soc_platform bf5xx_tdm_soc_platform;
+
+#endif
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c
new file mode 100644
index 0000000..3096bad
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-tdm.c
@@ -0,0 +1,343 @@
+/*
+ * File:         sound/soc/blackfin/bf5xx-tdm.c
+ * Author:       Barry Song <Barry.Song@analog.com>
+ *
+ * Created:      Thurs June 04 2009
+ * Description:  Blackfin I2S(TDM) CPU DAI driver
+ *              Even though TDM mode can be as part of I2S DAI, but there
+ *              are so much difference in configuration and data flow,
+ *              it's very ugly to integrate I2S and TDM into a module
+ *
+ * Modified:
+ *               Copyright 2009 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include <asm/irq.h>
+#include <asm/portmux.h>
+#include <linux/mutex.h>
+#include <linux/gpio.h>
+
+#include "bf5xx-sport.h"
+#include "bf5xx-tdm.h"
+
+struct bf5xx_tdm_port {
+	u16 tcr1;
+	u16 rcr1;
+	u16 tcr2;
+	u16 rcr2;
+	int configured;
+};
+
+static struct bf5xx_tdm_port bf5xx_tdm;
+static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
+
+static struct sport_param sport_params[2] = {
+	{
+		.dma_rx_chan    = CH_SPORT0_RX,
+		.dma_tx_chan    = CH_SPORT0_TX,
+		.err_irq        = IRQ_SPORT0_ERROR,
+		.regs           = (struct sport_register *)SPORT0_TCR1,
+	},
+	{
+		.dma_rx_chan    = CH_SPORT1_RX,
+		.dma_tx_chan    = CH_SPORT1_TX,
+		.err_irq        = IRQ_SPORT1_ERROR,
+		.regs           = (struct sport_register *)SPORT1_TCR1,
+	}
+};
+
+/*
+ * Setting the TFS pin selector for SPORT 0 based on whether the selected
+ * port id F or G. If the port is F then no conflict should exist for the
+ * TFS. When Port G is selected and EMAC then there is a conflict between
+ * the PHY interrupt line and TFS.  Current settings prevent the conflict
+ * by ignoring the TFS pin when Port G is selected. This allows both
+ * ssm2602 using Port G and EMAC concurrently.
+ */
+#ifdef CONFIG_BF527_SPORT0_PORTF
+#define LOCAL_SPORT0_TFS (P_SPORT0_TFS)
+#else
+#define LOCAL_SPORT0_TFS (0)
+#endif
+
+static u16 sport_req[][7] = { {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
+	P_SPORT0_DRPRI, P_SPORT0_RSCLK, LOCAL_SPORT0_TFS, 0},
+	   {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, P_SPORT1_DRPRI,
+		   P_SPORT1_RSCLK, P_SPORT1_TFS, 0} };
+
+static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+	unsigned int fmt)
+{
+	int ret = 0;
+
+	/* interface format:support TDM,slave mode */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+		break;
+	default:
+		printk(KERN_ERR "%s: Unknown DAI format type\n", __func__);
+		ret = -EINVAL;
+		break;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+	case SND_SOC_DAIFMT_CBM_CFS:
+	case SND_SOC_DAIFMT_CBS_CFM:
+		ret = -EINVAL;
+		break;
+	default:
+		printk(KERN_ERR "%s: Unknown DAI master type\n", __func__);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params,
+	struct snd_soc_dai *dai)
+{
+	int ret = 0;
+
+	bf5xx_tdm.tcr2 &= ~0x1f;
+	bf5xx_tdm.rcr2 &= ~0x1f;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bf5xx_tdm.tcr2 |= 31;
+		bf5xx_tdm.rcr2 |= 31;
+		sport_handle->wdsize = 4;
+		break;
+		/* at present, we only support 32bit transfer */
+	default:
+		pr_err("not supported PCM format yet\n");
+		return -EINVAL;
+		break;
+	}
+
+	if (!bf5xx_tdm.configured) {
+		/*
+		 * TX and RX are not independent,they are enabled at the
+		 * same time, even if only one side is running. So, we
+		 * need to configure both of them at the time when the first
+		 * stream is opened.
+		 *
+		 * CPU DAI:slave mode.
+		 */
+		ret = sport_config_rx(sport_handle, bf5xx_tdm.rcr1,
+			bf5xx_tdm.rcr2, 0, 0);
+		if (ret) {
+			pr_err("SPORT is busy!\n");
+			return -EBUSY;
+		}
+
+		ret = sport_config_tx(sport_handle, bf5xx_tdm.tcr1,
+			bf5xx_tdm.tcr2, 0, 0);
+		if (ret) {
+			pr_err("SPORT is busy!\n");
+			return -EBUSY;
+		}
+
+		bf5xx_tdm.configured = 1;
+	}
+
+	return 0;
+}
+
+static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	/* No active stream, SPORT is allowed to be configured again. */
+	if (!dai->active)
+		bf5xx_tdm.configured = 0;
+}
+
+#ifdef CONFIG_PM
+static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
+{
+	struct sport_device *sport =
+		(struct sport_device *)dai->private_data;
+
+	if (!dai->active)
+		return 0;
+	if (dai->capture.active)
+		sport_rx_stop(sport);
+	if (dai->playback.active)
+		sport_tx_stop(sport);
+	return 0;
+}
+
+static int bf5xx_tdm_resume(struct snd_soc_dai *dai)
+{
+	int ret;
+	struct sport_device *sport =
+		(struct sport_device *)dai->private_data;
+
+	if (!dai->active)
+		return 0;
+
+	ret = sport_set_multichannel(sport, 8, 0xFF, 1);
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		ret = -EBUSY;
+	}
+
+	ret = sport_config_rx(sport, IRFS, 0x1F, 0, 0);
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		ret = -EBUSY;
+	}
+
+	ret = sport_config_tx(sport, ITFS, 0x1F, 0, 0);
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		ret = -EBUSY;
+	}
+
+	return 0;
+}
+
+#else
+#define bf5xx_tdm_suspend      NULL
+#define bf5xx_tdm_resume       NULL
+#endif
+
+static struct snd_soc_dai_ops bf5xx_tdm_dai_ops = {
+	.hw_params      = bf5xx_tdm_hw_params,
+	.set_fmt        = bf5xx_tdm_set_dai_fmt,
+	.shutdown       = bf5xx_tdm_shutdown,
+};
+
+struct snd_soc_dai bf5xx_tdm_dai = {
+	.name = "bf5xx-tdm",
+	.id = 0,
+	.suspend = bf5xx_tdm_suspend,
+	.resume = bf5xx_tdm_resume,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE,},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE,},
+	.ops = &bf5xx_tdm_dai_ops,
+};
+EXPORT_SYMBOL_GPL(bf5xx_tdm_dai);
+
+static int __devinit bfin_tdm_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
+		pr_err("Requesting Peripherals failed\n");
+		return -EFAULT;
+	}
+
+	/* request DMA for SPORT */
+	sport_handle = sport_init(&sport_params[sport_num], 4, \
+		8 * sizeof(u32), NULL);
+	if (!sport_handle) {
+		peripheral_free_list(&sport_req[sport_num][0]);
+		return -ENODEV;
+	}
+
+	/* SPORT works in TDM mode */
+	ret = sport_set_multichannel(sport_handle, 8, 0xFF, 1);
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		ret = -EBUSY;
+		goto sport_config_err;
+	}
+
+	ret = sport_config_rx(sport_handle, IRFS, 0x1F, 0, 0);
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		ret = -EBUSY;
+		goto sport_config_err;
+	}
+
+	ret = sport_config_tx(sport_handle, ITFS, 0x1F, 0, 0);
+	if (ret) {
+		pr_err("SPORT is busy!\n");
+		ret = -EBUSY;
+		goto sport_config_err;
+	}
+
+	ret = snd_soc_register_dai(&bf5xx_tdm_dai);
+	if (ret) {
+		pr_err("Failed to register DAI: %d\n", ret);
+		goto sport_config_err;
+	}
+	return 0;
+
+sport_config_err:
+	peripheral_free_list(&sport_req[sport_num][0]);
+	return ret;
+}
+
+static int __devexit bfin_tdm_remove(struct platform_device *pdev)
+{
+	peripheral_free_list(&sport_req[sport_num][0]);
+	snd_soc_unregister_dai(&bf5xx_tdm_dai);
+
+	return 0;
+}
+
+static struct platform_driver bfin_tdm_driver = {
+	.probe  = bfin_tdm_probe,
+	.remove = __devexit_p(bfin_tdm_remove),
+	.driver = {
+		.name   = "bfin-tdm",
+		.owner  = THIS_MODULE,
+	},
+};
+
+static int __init bfin_tdm_init(void)
+{
+	return platform_driver_register(&bfin_tdm_driver);
+}
+module_init(bfin_tdm_init);
+
+static void __exit bfin_tdm_exit(void)
+{
+	platform_driver_unregister(&bfin_tdm_driver);
+}
+module_exit(bfin_tdm_exit);
+
+/* Module information */
+MODULE_AUTHOR("Barry Song");
+MODULE_DESCRIPTION("TDM driver for ADI Blackfin");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h
new file mode 100644
index 0000000..618ec3d
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-tdm.h
@@ -0,0 +1,14 @@
+/*
+ * sound/soc/blackfin/bf5xx-tdm.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _BF5XX_TDM_H
+#define _BF5XX_TDM_H
+
+extern struct snd_soc_dai bf5xx_tdm_dai;
+
+#endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index b6c7f7a..910b916 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -12,13 +12,18 @@
 	tristate "Build all ASoC CODEC drivers"
 	select SND_SOC_L3
 	select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
+	select SND_SOC_AD1836 if SPI_MASTER
+	select SND_SOC_AD1938 if SPI_MASTER
 	select SND_SOC_AD1980 if SND_SOC_AC97_BUS
 	select SND_SOC_AD73311 if I2C
 	select SND_SOC_AK4104 if SPI_MASTER
 	select SND_SOC_AK4535 if I2C
 	select SND_SOC_CS4270 if I2C
+	select SND_SOC_MAX9877 if I2C
 	select SND_SOC_PCM3008
+	select SND_SOC_SPDIF
 	select SND_SOC_SSM2602 if I2C
+	select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
 	select SND_SOC_TLV320AIC23 if I2C
 	select SND_SOC_TLV320AIC26 if SPI_MASTER
 	select SND_SOC_TLV320AIC3X if I2C
@@ -28,15 +33,23 @@
 	select SND_SOC_WM8350 if MFD_WM8350
 	select SND_SOC_WM8400 if MFD_WM8400
 	select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
+	select SND_SOC_WM8523 if I2C
 	select SND_SOC_WM8580 if I2C
 	select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8731 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8750 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8753 if SND_SOC_I2C_AND_SPI
+	select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8900 if I2C
 	select SND_SOC_WM8903 if I2C
+	select SND_SOC_WM8940 if I2C
+	select SND_SOC_WM8960 if I2C
+	select SND_SOC_WM8961 if I2C
 	select SND_SOC_WM8971 if I2C
+	select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8990 if I2C
+	select SND_SOC_WM8993 if I2C
+	select SND_SOC_WM9081 if I2C
 	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
@@ -51,11 +64,21 @@
 
           If unsure select "N".
 
+config SND_SOC_WM_HUBS
+	tristate
+	default y if SND_SOC_WM8993=y
+	default m if SND_SOC_WM8993=m
 
 config SND_SOC_AC97_CODEC
 	tristate
 	select SND_AC97_CODEC
 
+config SND_SOC_AD1836
+	tristate
+
+config SND_SOC_AD1938
+	tristate
+
 config SND_SOC_AD1980
 	tristate
 
@@ -80,15 +103,24 @@
 	bool
 	depends on SND_SOC_CS4270
 
+config SND_SOC_CX20442
+	tristate
+
 config SND_SOC_L3
        tristate
 
 config SND_SOC_PCM3008
        tristate
 
+config SND_SOC_SPDIF
+	tristate
+
 config SND_SOC_SSM2602
 	tristate
 
+config SND_SOC_STAC9766
+	tristate
+
 config SND_SOC_TLV320AIC23
 	tristate
 
@@ -117,6 +149,9 @@
 config SND_SOC_WM8510
 	tristate
 
+config SND_SOC_WM8523
+	tristate
+
 config SND_SOC_WM8580
 	tristate
 
@@ -132,18 +167,39 @@
 config SND_SOC_WM8753
 	tristate
 
+config SND_SOC_WM8776
+	tristate
+
 config SND_SOC_WM8900
 	tristate
 
 config SND_SOC_WM8903
 	tristate
 
+config SND_SOC_WM8940
+        tristate
+
+config SND_SOC_WM8960
+	tristate
+
+config SND_SOC_WM8961
+	tristate
+
 config SND_SOC_WM8971
 	tristate
 
+config SND_SOC_WM8988
+	tristate
+
 config SND_SOC_WM8990
 	tristate
 
+config SND_SOC_WM8993
+	tristate
+
+config SND_SOC_WM9081
+	tristate
+
 config SND_SOC_WM9705
 	tristate
 
@@ -152,3 +208,7 @@
 
 config SND_SOC_WM9713
 	tristate
+
+# Amp
+config SND_SOC_MAX9877
+	tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f265380..c7fd229 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -1,12 +1,17 @@
 snd-soc-ac97-objs := ac97.o
+snd-soc-ad1836-objs := ad1836.o
+snd-soc-ad1938-objs := ad1938.o
 snd-soc-ad1980-objs := ad1980.o
 snd-soc-ad73311-objs := ad73311.o
 snd-soc-ak4104-objs := ak4104.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-cs4270-objs := cs4270.o
+snd-soc-cx20442-objs := cx20442.o
 snd-soc-l3-objs := l3.o
 snd-soc-pcm3008-objs := pcm3008.o
+snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
+snd-soc-stac9766-objs := stac9766.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
@@ -16,28 +21,45 @@
 snd-soc-wm8350-objs := wm8350.o
 snd-soc-wm8400-objs := wm8400.o
 snd-soc-wm8510-objs := wm8510.o
+snd-soc-wm8523-objs := wm8523.o
 snd-soc-wm8580-objs := wm8580.o
 snd-soc-wm8728-objs := wm8728.o
 snd-soc-wm8731-objs := wm8731.o
 snd-soc-wm8750-objs := wm8750.o
 snd-soc-wm8753-objs := wm8753.o
+snd-soc-wm8776-objs := wm8776.o
 snd-soc-wm8900-objs := wm8900.o
 snd-soc-wm8903-objs := wm8903.o
+snd-soc-wm8940-objs := wm8940.o
+snd-soc-wm8960-objs := wm8960.o
+snd-soc-wm8961-objs := wm8961.o
 snd-soc-wm8971-objs := wm8971.o
+snd-soc-wm8988-objs := wm8988.o
 snd-soc-wm8990-objs := wm8990.o
+snd-soc-wm8993-objs := wm8993.o
+snd-soc-wm9081-objs := wm9081.o
 snd-soc-wm9705-objs := wm9705.o
 snd-soc-wm9712-objs := wm9712.o
 snd-soc-wm9713-objs := wm9713.o
+snd-soc-wm-hubs-objs := wm_hubs.o
+
+# Amp
+snd-soc-max9877-objs := max9877.o
 
 obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o
+obj-$(CONFIG_SND_SOC_AD1836)	+= snd-soc-ad1836.o
+obj-$(CONFIG_SND_SOC_AD1938)	+= snd-soc-ad1938.o
 obj-$(CONFIG_SND_SOC_AD1980)	+= snd-soc-ad1980.o
 obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
 obj-$(CONFIG_SND_SOC_AK4104)	+= snd-soc-ak4104.o
 obj-$(CONFIG_SND_SOC_AK4535)	+= snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_PCM3008)	+= snd-soc-pcm3008.o
+obj-$(CONFIG_SND_SOC_SPDIF)	+= snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)	+= snd-soc-ssm2602.o
+obj-$(CONFIG_SND_SOC_STAC9766)	+= snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)	+= snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)	+= snd-soc-tlv320aic26.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)	+= snd-soc-tlv320aic3x.o
@@ -47,15 +69,27 @@
 obj-$(CONFIG_SND_SOC_WM8350)	+= snd-soc-wm8350.o
 obj-$(CONFIG_SND_SOC_WM8400)	+= snd-soc-wm8400.o
 obj-$(CONFIG_SND_SOC_WM8510)	+= snd-soc-wm8510.o
+obj-$(CONFIG_SND_SOC_WM8523)	+= snd-soc-wm8523.o
 obj-$(CONFIG_SND_SOC_WM8580)	+= snd-soc-wm8580.o
 obj-$(CONFIG_SND_SOC_WM8728)	+= snd-soc-wm8728.o
 obj-$(CONFIG_SND_SOC_WM8731)	+= snd-soc-wm8731.o
 obj-$(CONFIG_SND_SOC_WM8750)	+= snd-soc-wm8750.o
 obj-$(CONFIG_SND_SOC_WM8753)	+= snd-soc-wm8753.o
+obj-$(CONFIG_SND_SOC_WM8776)	+= snd-soc-wm8776.o
 obj-$(CONFIG_SND_SOC_WM8900)	+= snd-soc-wm8900.o
 obj-$(CONFIG_SND_SOC_WM8903)	+= snd-soc-wm8903.o
 obj-$(CONFIG_SND_SOC_WM8971)	+= snd-soc-wm8971.o
+obj-$(CONFIG_SND_SOC_WM8940)	+= snd-soc-wm8940.o
+obj-$(CONFIG_SND_SOC_WM8960)	+= snd-soc-wm8960.o
+obj-$(CONFIG_SND_SOC_WM8961)	+= snd-soc-wm8961.o
+obj-$(CONFIG_SND_SOC_WM8988)	+= snd-soc-wm8988.o
 obj-$(CONFIG_SND_SOC_WM8990)	+= snd-soc-wm8990.o
+obj-$(CONFIG_SND_SOC_WM8993)	+= snd-soc-wm8993.o
+obj-$(CONFIG_SND_SOC_WM9081)	+= snd-soc-wm9081.o
 obj-$(CONFIG_SND_SOC_WM9705)	+= snd-soc-wm9705.o
 obj-$(CONFIG_SND_SOC_WM9712)	+= snd-soc-wm9712.o
 obj-$(CONFIG_SND_SOC_WM9713)	+= snd-soc-wm9713.o
+obj-$(CONFIG_SND_SOC_WM_HUBS)	+= snd-soc-wm-hubs.o
+
+# Amp
+obj-$(CONFIG_SND_SOC_MAX9877)	+= snd-soc-max9877.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index b0d4af1..932299b 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -53,13 +53,13 @@
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = STD_AC97_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.formats = SND_SOC_STD_AC97_FMTS,},
 	.capture = {
 		.stream_name = "AC97 Capture",
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = STD_AC97_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.formats = SND_SOC_STD_AC97_FMTS,},
 	.ops = &ac97_dai_ops,
 };
 EXPORT_SYMBOL_GPL(ac97_dai);
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
new file mode 100644
index 0000000..3612bb9
--- /dev/null
+++ b/sound/soc/codecs/ad1836.c
@@ -0,0 +1,446 @@
+/*
+ * File:         sound/soc/codecs/ad1836.c
+ * Author:       Barry Song <Barry.Song@analog.com>
+ *
+ * Created:      Aug 04 2009
+ * Description:  Driver for AD1836 sound chip
+ *
+ * Modified:
+ *               Copyright 2009 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-dapm.h>
+#include <linux/spi/spi.h>
+#include "ad1836.h"
+
+/* codec private data */
+struct ad1836_priv {
+	struct snd_soc_codec codec;
+	u16 reg_cache[AD1836_NUM_REGS];
+};
+
+static struct snd_soc_codec *ad1836_codec;
+struct snd_soc_codec_device soc_codec_dev_ad1836;
+static int ad1836_register(struct ad1836_priv *ad1836);
+static void ad1836_unregister(struct ad1836_priv *ad1836);
+
+/*
+ * AD1836 volume/mute/de-emphasis etc. controls
+ */
+static const char *ad1836_deemp[] = {"None", "44.1kHz", "32kHz", "48kHz"};
+
+static const struct soc_enum ad1836_deemp_enum =
+	SOC_ENUM_SINGLE(AD1836_DAC_CTRL1, 8, 4, ad1836_deemp);
+
+static const struct snd_kcontrol_new ad1836_snd_controls[] = {
+	/* DAC volume control */
+	SOC_DOUBLE_R("DAC1 Volume", AD1836_DAC_L1_VOL,
+			AD1836_DAC_R1_VOL, 0, 0x3FF, 0),
+	SOC_DOUBLE_R("DAC2 Volume", AD1836_DAC_L2_VOL,
+			AD1836_DAC_R2_VOL, 0, 0x3FF, 0),
+	SOC_DOUBLE_R("DAC3 Volume", AD1836_DAC_L3_VOL,
+			AD1836_DAC_R3_VOL, 0, 0x3FF, 0),
+
+	/* ADC switch control */
+	SOC_DOUBLE("ADC1 Switch", AD1836_ADC_CTRL2, AD1836_ADCL1_MUTE,
+		AD1836_ADCR1_MUTE, 1, 1),
+	SOC_DOUBLE("ADC2 Switch", AD1836_ADC_CTRL2, AD1836_ADCL2_MUTE,
+		AD1836_ADCR2_MUTE, 1, 1),
+
+	/* DAC switch control */
+	SOC_DOUBLE("DAC1 Switch", AD1836_DAC_CTRL2, AD1836_DACL1_MUTE,
+		AD1836_DACR1_MUTE, 1, 1),
+	SOC_DOUBLE("DAC2 Switch", AD1836_DAC_CTRL2, AD1836_DACL2_MUTE,
+		AD1836_DACR2_MUTE, 1, 1),
+	SOC_DOUBLE("DAC3 Switch", AD1836_DAC_CTRL2, AD1836_DACL3_MUTE,
+		AD1836_DACR3_MUTE, 1, 1),
+
+	/* ADC high-pass filter */
+	SOC_SINGLE("ADC High Pass Filter Switch", AD1836_ADC_CTRL1,
+			AD1836_ADC_HIGHPASS_FILTER, 1, 0),
+
+	/* DAC de-emphasis */
+	SOC_ENUM("Playback Deemphasis", ad1836_deemp_enum),
+};
+
+static const struct snd_soc_dapm_widget ad1836_dapm_widgets[] = {
+	SND_SOC_DAPM_DAC("DAC", "Playback", AD1836_DAC_CTRL1,
+				AD1836_DAC_POWERDOWN, 1),
+	SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1836_ADC_CTRL1,
+				AD1836_ADC_POWERDOWN, 1, NULL, 0),
+	SND_SOC_DAPM_OUTPUT("DAC1OUT"),
+	SND_SOC_DAPM_OUTPUT("DAC2OUT"),
+	SND_SOC_DAPM_OUTPUT("DAC3OUT"),
+	SND_SOC_DAPM_INPUT("ADC1IN"),
+	SND_SOC_DAPM_INPUT("ADC2IN"),
+};
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+	{ "DAC", NULL, "ADC_PWR" },
+	{ "ADC", NULL, "ADC_PWR" },
+	{ "DAC1OUT", "DAC1 Switch", "DAC" },
+	{ "DAC2OUT", "DAC2 Switch", "DAC" },
+	{ "DAC3OUT", "DAC3 Switch", "DAC" },
+	{ "ADC", "ADC1 Switch", "ADC1IN" },
+	{ "ADC", "ADC2 Switch", "ADC2IN" },
+};
+
+/*
+ * DAI ops entries
+ */
+
+static int ad1836_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	/* at present, we support adc aux mode to interface with
+	 * blackfin sport tdm mode
+	 */
+	case SND_SOC_DAIFMT_DSP_A:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_IB_IF:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	/* ALCLK,ABCLK are both output, AD1836 can only be master */
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ad1836_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	int word_len = 0;
+
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		word_len = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		word_len = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S32_LE:
+		word_len = 0;
+		break;
+	}
+
+	snd_soc_update_bits(codec, AD1836_DAC_CTRL1,
+		AD1836_DAC_WORD_LEN_MASK, word_len);
+
+	snd_soc_update_bits(codec, AD1836_ADC_CTRL2,
+		AD1836_ADC_WORD_LEN_MASK, word_len);
+
+	return 0;
+}
+
+
+/*
+ * interface to read/write ad1836 register
+ */
+#define AD1836_SPI_REG_SHFT 12
+#define AD1836_SPI_READ     (1 << 11)
+#define AD1836_SPI_VAL_MSK  0x3FF
+
+/*
+ * write to the ad1836 register space
+ */
+
+static int ad1836_write_reg(struct snd_soc_codec *codec, unsigned int reg,
+		unsigned int value)
+{
+	u16 *reg_cache = codec->reg_cache;
+	int ret = 0;
+
+	if (value != reg_cache[reg]) {
+		unsigned short buf;
+		struct spi_transfer t = {
+			.tx_buf = &buf,
+			.len = 2,
+		};
+		struct spi_message m;
+
+		buf = (reg << AD1836_SPI_REG_SHFT) |
+			(value & AD1836_SPI_VAL_MSK);
+		spi_message_init(&m);
+		spi_message_add_tail(&t, &m);
+		ret = spi_sync(codec->control_data, &m);
+		if (ret == 0)
+			reg_cache[reg] = value;
+	}
+
+	return ret;
+}
+
+/*
+ * read from the ad1836 register space cache
+ */
+static unsigned int ad1836_read_reg_cache(struct snd_soc_codec *codec,
+					  unsigned int reg)
+{
+	u16 *reg_cache = codec->reg_cache;
+
+	if (reg >= codec->reg_cache_size)
+		return -EINVAL;
+
+	return reg_cache[reg];
+}
+
+static int __devinit ad1836_spi_probe(struct spi_device *spi)
+{
+	struct snd_soc_codec *codec;
+	struct ad1836_priv *ad1836;
+
+	ad1836 = kzalloc(sizeof(struct ad1836_priv), GFP_KERNEL);
+	if (ad1836 == NULL)
+		return -ENOMEM;
+
+	codec = &ad1836->codec;
+	codec->control_data = spi;
+	codec->dev = &spi->dev;
+
+	dev_set_drvdata(&spi->dev, ad1836);
+
+	return ad1836_register(ad1836);
+}
+
+static int __devexit ad1836_spi_remove(struct spi_device *spi)
+{
+	struct ad1836_priv *ad1836 = dev_get_drvdata(&spi->dev);
+
+	ad1836_unregister(ad1836);
+	return 0;
+}
+
+static struct spi_driver ad1836_spi_driver = {
+	.driver = {
+		.name	= "ad1836-spi",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ad1836_spi_probe,
+	.remove		= __devexit_p(ad1836_spi_remove),
+};
+
+static struct snd_soc_dai_ops ad1836_dai_ops = {
+	.hw_params = ad1836_hw_params,
+	.set_fmt = ad1836_set_dai_fmt,
+};
+
+/* codec DAI instance */
+struct snd_soc_dai ad1836_dai = {
+	.name = "AD1836",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 6,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 4,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+	},
+	.ops = &ad1836_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ad1836_dai);
+
+static int ad1836_register(struct ad1836_priv *ad1836)
+{
+	int ret;
+	struct snd_soc_codec *codec = &ad1836->codec;
+
+	if (ad1836_codec) {
+		dev_err(codec->dev, "Another ad1836 is registered\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+	codec->private_data = ad1836;
+	codec->reg_cache = ad1836->reg_cache;
+	codec->reg_cache_size = AD1836_NUM_REGS;
+	codec->name = "AD1836";
+	codec->owner = THIS_MODULE;
+	codec->dai = &ad1836_dai;
+	codec->num_dai = 1;
+	codec->write = ad1836_write_reg;
+	codec->read = ad1836_read_reg_cache;
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	ad1836_dai.dev = codec->dev;
+	ad1836_codec = codec;
+
+	/* default setting for ad1836 */
+	/* de-emphasis: 48kHz, power-on dac */
+	codec->write(codec, AD1836_DAC_CTRL1, 0x300);
+	/* unmute dac channels */
+	codec->write(codec, AD1836_DAC_CTRL2, 0x0);
+	/* high-pass filter enable, power-on adc */
+	codec->write(codec, AD1836_ADC_CTRL1, 0x100);
+	/* unmute adc channles, adc aux mode */
+	codec->write(codec, AD1836_ADC_CTRL2, 0x180);
+	/* left/right diff:PGA/MUX */
+	codec->write(codec, AD1836_ADC_CTRL3, 0x3A);
+	/* volume */
+	codec->write(codec, AD1836_DAC_L1_VOL, 0x3FF);
+	codec->write(codec, AD1836_DAC_R1_VOL, 0x3FF);
+	codec->write(codec, AD1836_DAC_L2_VOL, 0x3FF);
+	codec->write(codec, AD1836_DAC_R2_VOL, 0x3FF);
+	codec->write(codec, AD1836_DAC_L3_VOL, 0x3FF);
+	codec->write(codec, AD1836_DAC_R3_VOL, 0x3FF);
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		kfree(ad1836);
+		return ret;
+	}
+
+	ret = snd_soc_register_dai(&ad1836_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		kfree(ad1836);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void ad1836_unregister(struct ad1836_priv *ad1836)
+{
+	snd_soc_unregister_dai(&ad1836_dai);
+	snd_soc_unregister_codec(&ad1836->codec);
+	kfree(ad1836);
+	ad1836_codec = NULL;
+}
+
+static int ad1836_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (ad1836_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = ad1836_codec;
+	codec = ad1836_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, ad1836_snd_controls,
+			     ARRAY_SIZE(ad1836_snd_controls));
+	snd_soc_dapm_new_controls(codec, ad1836_dapm_widgets,
+				  ARRAY_SIZE(ad1836_dapm_widgets));
+	snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+	snd_soc_dapm_new_widgets(codec);
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+/* power down chip */
+static int ad1836_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ad1836 = {
+	.probe = 	ad1836_probe,
+	.remove = 	ad1836_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ad1836);
+
+static int __init ad1836_init(void)
+{
+	int ret;
+
+	ret = spi_register_driver(&ad1836_spi_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register ad1836 SPI driver: %d\n",
+				ret);
+	}
+
+	return ret;
+}
+module_init(ad1836_init);
+
+static void __exit ad1836_exit(void)
+{
+	spi_unregister_driver(&ad1836_spi_driver);
+}
+module_exit(ad1836_exit);
+
+MODULE_DESCRIPTION("ASoC ad1836 driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad1836.h b/sound/soc/codecs/ad1836.h
new file mode 100644
index 0000000..7660ee6
--- /dev/null
+++ b/sound/soc/codecs/ad1836.h
@@ -0,0 +1,64 @@
+/*
+ * File:         sound/soc/codecs/ad1836.h
+ * Based on:
+ * Author:       Barry Song <Barry.Song@analog.com>
+ *
+ * Created:      Aug 04, 2009
+ * Description:  definitions for AD1836 registers
+ *
+ * Modified:
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __AD1836_H__
+#define __AD1836_H__
+
+#define AD1836_DAC_CTRL1               0
+#define AD1836_DAC_POWERDOWN           2
+#define AD1836_DAC_SERFMT_MASK	       0xE0
+#define AD1836_DAC_SERFMT_PCK256       (0x4 << 5)
+#define AD1836_DAC_SERFMT_PCK128       (0x5 << 5)
+#define AD1836_DAC_WORD_LEN_MASK       0x18
+
+#define AD1836_DAC_CTRL2               1
+#define AD1836_DACL1_MUTE              0
+#define AD1836_DACR1_MUTE              1
+#define AD1836_DACL2_MUTE              2
+#define AD1836_DACR2_MUTE              3
+#define AD1836_DACL3_MUTE              4
+#define AD1836_DACR3_MUTE              5
+
+#define AD1836_DAC_L1_VOL              2
+#define AD1836_DAC_R1_VOL              3
+#define AD1836_DAC_L2_VOL              4
+#define AD1836_DAC_R2_VOL              5
+#define AD1836_DAC_L3_VOL              6
+#define AD1836_DAC_R3_VOL              7
+
+#define AD1836_ADC_CTRL1               12
+#define AD1836_ADC_POWERDOWN           7
+#define AD1836_ADC_HIGHPASS_FILTER     8
+
+#define AD1836_ADC_CTRL2               13
+#define AD1836_ADCL1_MUTE 		0
+#define AD1836_ADCR1_MUTE 		1
+#define AD1836_ADCL2_MUTE 		2
+#define AD1836_ADCR2_MUTE 		3
+#define AD1836_ADC_WORD_LEN_MASK       0x30
+#define AD1836_ADC_SERFMT_MASK	       (7 << 6)
+#define AD1836_ADC_SERFMT_PCK256       (0x4 << 6)
+#define AD1836_ADC_SERFMT_PCK128       (0x5 << 6)
+
+#define AD1836_ADC_CTRL3               14
+
+#define AD1836_NUM_REGS                16
+
+extern struct snd_soc_dai ad1836_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ad1836;
+#endif
diff --git a/sound/soc/codecs/ad1938.c b/sound/soc/codecs/ad1938.c
new file mode 100644
index 0000000..e62b277
--- /dev/null
+++ b/sound/soc/codecs/ad1938.c
@@ -0,0 +1,682 @@
+/*
+ * File:         sound/soc/codecs/ad1938.c
+ * Author:       Barry Song <Barry.Song@analog.com>
+ *
+ * Created:      June 04 2009
+ * Description:  Driver for AD1938 sound chip
+ *
+ * Modified:
+ *               Copyright 2009 Analog Devices Inc.
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-dapm.h>
+#include <linux/spi/spi.h>
+#include "ad1938.h"
+
+/* codec private data */
+struct ad1938_priv {
+	struct snd_soc_codec codec;
+	u8 reg_cache[AD1938_NUM_REGS];
+};
+
+static struct snd_soc_codec *ad1938_codec;
+struct snd_soc_codec_device soc_codec_dev_ad1938;
+static int ad1938_register(struct ad1938_priv *ad1938);
+static void ad1938_unregister(struct ad1938_priv *ad1938);
+
+/*
+ * AD1938 volume/mute/de-emphasis etc. controls
+ */
+static const char *ad1938_deemp[] = {"None", "48kHz", "44.1kHz", "32kHz"};
+
+static const struct soc_enum ad1938_deemp_enum =
+	SOC_ENUM_SINGLE(AD1938_DAC_CTRL2, 1, 4, ad1938_deemp);
+
+static const struct snd_kcontrol_new ad1938_snd_controls[] = {
+	/* DAC volume control */
+	SOC_DOUBLE_R("DAC1  Volume", AD1938_DAC_L1_VOL,
+			AD1938_DAC_R1_VOL, 0, 0xFF, 1),
+	SOC_DOUBLE_R("DAC2  Volume", AD1938_DAC_L2_VOL,
+			AD1938_DAC_R2_VOL, 0, 0xFF, 1),
+	SOC_DOUBLE_R("DAC3  Volume", AD1938_DAC_L3_VOL,
+			AD1938_DAC_R3_VOL, 0, 0xFF, 1),
+	SOC_DOUBLE_R("DAC4  Volume", AD1938_DAC_L4_VOL,
+			AD1938_DAC_R4_VOL, 0, 0xFF, 1),
+
+	/* ADC switch control */
+	SOC_DOUBLE("ADC1 Switch", AD1938_ADC_CTRL0, AD1938_ADCL1_MUTE,
+		AD1938_ADCR1_MUTE, 1, 1),
+	SOC_DOUBLE("ADC2 Switch", AD1938_ADC_CTRL0, AD1938_ADCL2_MUTE,
+		AD1938_ADCR2_MUTE, 1, 1),
+
+	/* DAC switch control */
+	SOC_DOUBLE("DAC1 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL1_MUTE,
+		AD1938_DACR1_MUTE, 1, 1),
+	SOC_DOUBLE("DAC2 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL2_MUTE,
+		AD1938_DACR2_MUTE, 1, 1),
+	SOC_DOUBLE("DAC3 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL3_MUTE,
+		AD1938_DACR3_MUTE, 1, 1),
+	SOC_DOUBLE("DAC4 Switch", AD1938_DAC_CHNL_MUTE, AD1938_DACL4_MUTE,
+		AD1938_DACR4_MUTE, 1, 1),
+
+	/* ADC high-pass filter */
+	SOC_SINGLE("ADC High Pass Filter Switch", AD1938_ADC_CTRL0,
+			AD1938_ADC_HIGHPASS_FILTER, 1, 0),
+
+	/* DAC de-emphasis */
+	SOC_ENUM("Playback Deemphasis", ad1938_deemp_enum),
+};
+
+static const struct snd_soc_dapm_widget ad1938_dapm_widgets[] = {
+	SND_SOC_DAPM_DAC("DAC", "Playback", AD1938_DAC_CTRL0, 0, 1),
+	SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_SUPPLY("ADC_PWR", AD1938_ADC_CTRL0, 0, 1, NULL, 0),
+	SND_SOC_DAPM_OUTPUT("DAC1OUT"),
+	SND_SOC_DAPM_OUTPUT("DAC2OUT"),
+	SND_SOC_DAPM_OUTPUT("DAC3OUT"),
+	SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+	SND_SOC_DAPM_INPUT("ADC1IN"),
+	SND_SOC_DAPM_INPUT("ADC2IN"),
+};
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+	{ "DAC", NULL, "ADC_PWR" },
+	{ "ADC", NULL, "ADC_PWR" },
+	{ "DAC1OUT", "DAC1 Switch", "DAC" },
+	{ "DAC2OUT", "DAC2 Switch", "DAC" },
+	{ "DAC3OUT", "DAC3 Switch", "DAC" },
+	{ "DAC4OUT", "DAC4 Switch", "DAC" },
+	{ "ADC", "ADC1 Switch", "ADC1IN" },
+	{ "ADC", "ADC2 Switch", "ADC2IN" },
+};
+
+/*
+ * DAI ops entries
+ */
+
+static int ad1938_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	int reg;
+
+	reg = codec->read(codec, AD1938_DAC_CTRL2);
+	reg = (mute > 0) ? reg | AD1938_DAC_MASTER_MUTE : reg &
+		(~AD1938_DAC_MASTER_MUTE);
+	codec->write(codec, AD1938_DAC_CTRL2, reg);
+
+	return 0;
+}
+
+static inline int ad1938_pll_powerctrl(struct snd_soc_codec *codec, int cmd)
+{
+	int reg = codec->read(codec, AD1938_PLL_CLK_CTRL0);
+	reg = (cmd > 0) ? reg & (~AD1938_PLL_POWERDOWN) : reg |
+		AD1938_PLL_POWERDOWN;
+	codec->write(codec, AD1938_PLL_CLK_CTRL0, reg);
+
+	return 0;
+}
+
+static int ad1938_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+			       unsigned int mask, int slots, int width)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	int dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
+	int adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
+
+	dac_reg &= ~AD1938_DAC_CHAN_MASK;
+	adc_reg &= ~AD1938_ADC_CHAN_MASK;
+
+	switch (slots) {
+	case 2:
+		dac_reg |= AD1938_DAC_2_CHANNELS << AD1938_DAC_CHAN_SHFT;
+		adc_reg |= AD1938_ADC_2_CHANNELS << AD1938_ADC_CHAN_SHFT;
+		break;
+	case 4:
+		dac_reg |= AD1938_DAC_4_CHANNELS << AD1938_DAC_CHAN_SHFT;
+		adc_reg |= AD1938_ADC_4_CHANNELS << AD1938_ADC_CHAN_SHFT;
+		break;
+	case 8:
+		dac_reg |= AD1938_DAC_8_CHANNELS << AD1938_DAC_CHAN_SHFT;
+		adc_reg |= AD1938_ADC_8_CHANNELS << AD1938_ADC_CHAN_SHFT;
+		break;
+	case 16:
+		dac_reg |= AD1938_DAC_16_CHANNELS << AD1938_DAC_CHAN_SHFT;
+		adc_reg |= AD1938_ADC_16_CHANNELS << AD1938_ADC_CHAN_SHFT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
+	codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
+
+	return 0;
+}
+
+static int ad1938_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	int adc_reg, dac_reg;
+
+	adc_reg = codec->read(codec, AD1938_ADC_CTRL2);
+	dac_reg = codec->read(codec, AD1938_DAC_CTRL1);
+
+	/* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S
+	 * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A)
+	 */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		adc_reg &= ~AD1938_ADC_SERFMT_MASK;
+		adc_reg |= AD1938_ADC_SERFMT_TDM;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		adc_reg &= ~AD1938_ADC_SERFMT_MASK;
+		adc_reg |= AD1938_ADC_SERFMT_AUX;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
+		adc_reg &= ~AD1938_ADC_LEFT_HIGH;
+		adc_reg &= ~AD1938_ADC_BCLK_INV;
+		dac_reg &= ~AD1938_DAC_LEFT_HIGH;
+		dac_reg &= ~AD1938_DAC_BCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_NB_IF: /* normal bclk + invert frm */
+		adc_reg |= AD1938_ADC_LEFT_HIGH;
+		adc_reg &= ~AD1938_ADC_BCLK_INV;
+		dac_reg |= AD1938_DAC_LEFT_HIGH;
+		dac_reg &= ~AD1938_DAC_BCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_IB_NF: /* invert bclk + normal frm */
+		adc_reg &= ~AD1938_ADC_LEFT_HIGH;
+		adc_reg |= AD1938_ADC_BCLK_INV;
+		dac_reg &= ~AD1938_DAC_LEFT_HIGH;
+		dac_reg |= AD1938_DAC_BCLK_INV;
+		break;
+
+	case SND_SOC_DAIFMT_IB_IF: /* invert bclk + frm */
+		adc_reg |= AD1938_ADC_LEFT_HIGH;
+		adc_reg |= AD1938_ADC_BCLK_INV;
+		dac_reg |= AD1938_DAC_LEFT_HIGH;
+		dac_reg |= AD1938_DAC_BCLK_INV;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */
+		adc_reg |= AD1938_ADC_LCR_MASTER;
+		adc_reg |= AD1938_ADC_BCLK_MASTER;
+		dac_reg |= AD1938_DAC_LCR_MASTER;
+		dac_reg |= AD1938_DAC_BCLK_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & frm master */
+		adc_reg |= AD1938_ADC_LCR_MASTER;
+		adc_reg &= ~AD1938_ADC_BCLK_MASTER;
+		dac_reg |= AD1938_DAC_LCR_MASTER;
+		dac_reg &= ~AD1938_DAC_BCLK_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
+		adc_reg &= ~AD1938_ADC_LCR_MASTER;
+		adc_reg |= AD1938_ADC_BCLK_MASTER;
+		dac_reg &= ~AD1938_DAC_LCR_MASTER;
+		dac_reg |= AD1938_DAC_BCLK_MASTER;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & frm slave */
+		adc_reg &= ~AD1938_ADC_LCR_MASTER;
+		adc_reg &= ~AD1938_ADC_BCLK_MASTER;
+		dac_reg &= ~AD1938_DAC_LCR_MASTER;
+		dac_reg &= ~AD1938_DAC_BCLK_MASTER;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	codec->write(codec, AD1938_ADC_CTRL2, adc_reg);
+	codec->write(codec, AD1938_DAC_CTRL1, dac_reg);
+
+	return 0;
+}
+
+static int ad1938_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	int word_len = 0, reg = 0;
+
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		word_len = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		word_len = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S32_LE:
+		word_len = 0;
+		break;
+	}
+
+	reg = codec->read(codec, AD1938_DAC_CTRL2);
+	reg = (reg & (~AD1938_DAC_WORD_LEN_MASK)) | word_len;
+	codec->write(codec, AD1938_DAC_CTRL2, reg);
+
+	reg = codec->read(codec, AD1938_ADC_CTRL1);
+	reg = (reg & (~AD1938_ADC_WORD_LEN_MASK)) | word_len;
+	codec->write(codec, AD1938_ADC_CTRL1, reg);
+
+	return 0;
+}
+
+static int ad1938_set_bias_level(struct snd_soc_codec *codec,
+		enum snd_soc_bias_level level)
+{
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		ad1938_pll_powerctrl(codec, 1);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+	case SND_SOC_BIAS_OFF:
+		ad1938_pll_powerctrl(codec, 0);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+/*
+ * interface to read/write ad1938 register
+ */
+
+#define AD1938_SPI_ADDR    0x4
+#define AD1938_SPI_READ    0x1
+#define AD1938_SPI_BUFLEN  3
+
+/*
+ * write to the ad1938 register space
+ */
+
+static int ad1938_write_reg(struct snd_soc_codec *codec, unsigned int reg,
+		unsigned int value)
+{
+	u8 *reg_cache = codec->reg_cache;
+	int ret = 0;
+
+	if (value != reg_cache[reg]) {
+		uint8_t buf[AD1938_SPI_BUFLEN];
+		struct spi_transfer t = {
+			.tx_buf = buf,
+			.len = AD1938_SPI_BUFLEN,
+		};
+		struct spi_message m;
+
+		buf[0] = AD1938_SPI_ADDR << 1;
+		buf[1] = reg;
+		buf[2] = value;
+		spi_message_init(&m);
+		spi_message_add_tail(&t, &m);
+		ret = spi_sync(codec->control_data, &m);
+		if (ret == 0)
+			reg_cache[reg] = value;
+	}
+
+	return ret;
+}
+
+/*
+ * read from the ad1938 register space cache
+ */
+
+static unsigned int ad1938_read_reg_cache(struct snd_soc_codec *codec,
+					  unsigned int reg)
+{
+	u8 *reg_cache = codec->reg_cache;
+
+	if (reg >= codec->reg_cache_size)
+		return -EINVAL;
+
+	return reg_cache[reg];
+}
+
+/*
+ * read from the ad1938 register space
+ */
+
+static unsigned int ad1938_read_reg(struct snd_soc_codec *codec,
+						unsigned int reg)
+{
+	char w_buf[AD1938_SPI_BUFLEN];
+	char r_buf[AD1938_SPI_BUFLEN];
+	int ret;
+
+	struct spi_transfer t = {
+		.tx_buf = w_buf,
+		.rx_buf = r_buf,
+		.len = AD1938_SPI_BUFLEN,
+	};
+	struct spi_message m;
+
+	w_buf[0] = (AD1938_SPI_ADDR << 1) | AD1938_SPI_READ;
+	w_buf[1] = reg;
+	w_buf[2] = 0;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+	ret = spi_sync(codec->control_data, &m);
+	if (ret == 0)
+		return	r_buf[2];
+	else
+		return -EIO;
+}
+
+static int ad1938_fill_cache(struct snd_soc_codec *codec)
+{
+	int i;
+	u8 *reg_cache = codec->reg_cache;
+	struct spi_device *spi = codec->control_data;
+
+	for (i = 0; i < codec->reg_cache_size; i++) {
+		int ret = ad1938_read_reg(codec, i);
+		if (ret == -EIO) {
+			dev_err(&spi->dev, "AD1938 SPI read failure\n");
+			return ret;
+		}
+		reg_cache[i] = ret;
+	}
+
+	return 0;
+}
+
+static int __devinit ad1938_spi_probe(struct spi_device *spi)
+{
+	struct snd_soc_codec *codec;
+	struct ad1938_priv *ad1938;
+
+	ad1938 = kzalloc(sizeof(struct ad1938_priv), GFP_KERNEL);
+	if (ad1938 == NULL)
+		return -ENOMEM;
+
+	codec = &ad1938->codec;
+	codec->control_data = spi;
+	codec->dev = &spi->dev;
+
+	dev_set_drvdata(&spi->dev, ad1938);
+
+	return ad1938_register(ad1938);
+}
+
+static int __devexit ad1938_spi_remove(struct spi_device *spi)
+{
+	struct ad1938_priv *ad1938 = dev_get_drvdata(&spi->dev);
+
+	ad1938_unregister(ad1938);
+	return 0;
+}
+
+static struct spi_driver ad1938_spi_driver = {
+	.driver = {
+		.name	= "ad1938",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ad1938_spi_probe,
+	.remove		= __devexit_p(ad1938_spi_remove),
+};
+
+static struct snd_soc_dai_ops ad1938_dai_ops = {
+	.hw_params = ad1938_hw_params,
+	.digital_mute = ad1938_mute,
+	.set_tdm_slot = ad1938_set_tdm_slot,
+	.set_fmt = ad1938_set_dai_fmt,
+};
+
+/* codec DAI instance */
+struct snd_soc_dai ad1938_dai = {
+	.name = "AD1938",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 4,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE,
+	},
+	.ops = &ad1938_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ad1938_dai);
+
+static int ad1938_register(struct ad1938_priv *ad1938)
+{
+	int ret;
+	struct snd_soc_codec *codec = &ad1938->codec;
+
+	if (ad1938_codec) {
+		dev_err(codec->dev, "Another ad1938 is registered\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+	codec->private_data = ad1938;
+	codec->reg_cache = ad1938->reg_cache;
+	codec->reg_cache_size = AD1938_NUM_REGS;
+	codec->name = "AD1938";
+	codec->owner = THIS_MODULE;
+	codec->dai = &ad1938_dai;
+	codec->num_dai = 1;
+	codec->write = ad1938_write_reg;
+	codec->read = ad1938_read_reg_cache;
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	ad1938_dai.dev = codec->dev;
+	ad1938_codec = codec;
+
+	/* default setting for ad1938 */
+
+	/* unmute dac channels */
+	codec->write(codec, AD1938_DAC_CHNL_MUTE, 0x0);
+	/* de-emphasis: 48kHz, powedown dac */
+	codec->write(codec, AD1938_DAC_CTRL2, 0x1A);
+	/* powerdown dac, dac in tdm mode */
+	codec->write(codec, AD1938_DAC_CTRL0, 0x41);
+	/* high-pass filter enable */
+	codec->write(codec, AD1938_ADC_CTRL0, 0x3);
+	/* sata delay=1, adc aux mode */
+	codec->write(codec, AD1938_ADC_CTRL1, 0x43);
+	/* pll input: mclki/xi */
+	codec->write(codec, AD1938_PLL_CLK_CTRL0, 0x9D);
+	codec->write(codec, AD1938_PLL_CLK_CTRL1, 0x04);
+
+	ad1938_fill_cache(codec);
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		kfree(ad1938);
+		return ret;
+	}
+
+	ret = snd_soc_register_dai(&ad1938_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		kfree(ad1938);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void ad1938_unregister(struct ad1938_priv *ad1938)
+{
+	ad1938_set_bias_level(&ad1938->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dai(&ad1938_dai);
+	snd_soc_unregister_codec(&ad1938->codec);
+	kfree(ad1938);
+	ad1938_codec = NULL;
+}
+
+static int ad1938_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (ad1938_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = ad1938_codec;
+	codec = ad1938_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, ad1938_snd_controls,
+			     ARRAY_SIZE(ad1938_snd_controls));
+	snd_soc_dapm_new_controls(codec, ad1938_dapm_widgets,
+				  ARRAY_SIZE(ad1938_dapm_widgets));
+	snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+	snd_soc_dapm_new_widgets(codec);
+
+	ad1938_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+/* power down chip */
+static int ad1938_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ad1938_suspend(struct platform_device *pdev,
+		pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	ad1938_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int ad1938_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+		ad1938_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+	return 0;
+}
+#else
+#define ad1938_suspend NULL
+#define ad1938_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_ad1938 = {
+	.probe = 	ad1938_probe,
+	.remove = 	ad1938_remove,
+	.suspend =      ad1938_suspend,
+	.resume =       ad1938_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ad1938);
+
+static int __init ad1938_init(void)
+{
+	int ret;
+
+	ret = spi_register_driver(&ad1938_spi_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register ad1938 SPI driver: %d\n",
+				ret);
+	}
+
+	return ret;
+}
+module_init(ad1938_init);
+
+static void __exit ad1938_exit(void)
+{
+	spi_unregister_driver(&ad1938_spi_driver);
+}
+module_exit(ad1938_exit);
+
+MODULE_DESCRIPTION("ASoC ad1938 driver");
+MODULE_AUTHOR("Barry Song ");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ad1938.h b/sound/soc/codecs/ad1938.h
new file mode 100644
index 0000000..fe3c48c
--- /dev/null
+++ b/sound/soc/codecs/ad1938.h
@@ -0,0 +1,100 @@
+/*
+ * File:         sound/soc/codecs/ad1836.h
+ * Based on:
+ * Author:       Barry Song <Barry.Song@analog.com>
+ *
+ * Created:      May 25, 2009
+ * Description:  definitions for AD1938 registers
+ *
+ * Modified:
+ *
+ * Bugs:         Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __AD1938_H__
+#define __AD1938_H__
+
+#define AD1938_PLL_CLK_CTRL0    0
+#define AD1938_PLL_POWERDOWN           0x01
+#define AD1938_PLL_CLK_CTRL1    1
+#define AD1938_DAC_CTRL0        2
+#define AD1938_DAC_POWERDOWN           0x01
+#define AD1938_DAC_SERFMT_MASK		0xC0
+#define AD1938_DAC_SERFMT_STEREO	(0 << 6)
+#define AD1938_DAC_SERFMT_TDM		(1 << 6)
+#define AD1938_DAC_CTRL1        3
+#define AD1938_DAC_2_CHANNELS   0
+#define AD1938_DAC_4_CHANNELS   1
+#define AD1938_DAC_8_CHANNELS   2
+#define AD1938_DAC_16_CHANNELS  3
+#define AD1938_DAC_CHAN_SHFT    1
+#define AD1938_DAC_CHAN_MASK    (3 << AD1938_DAC_CHAN_SHFT)
+#define AD1938_DAC_LCR_MASTER   (1 << 4)
+#define AD1938_DAC_BCLK_MASTER  (1 << 5)
+#define AD1938_DAC_LEFT_HIGH    (1 << 3)
+#define AD1938_DAC_BCLK_INV     (1 << 7)
+#define AD1938_DAC_CTRL2        4
+#define AD1938_DAC_WORD_LEN_MASK	0xC
+#define AD1938_DAC_MASTER_MUTE  1
+#define AD1938_DAC_CHNL_MUTE    5
+#define AD1938_DACL1_MUTE       0
+#define AD1938_DACR1_MUTE       1
+#define AD1938_DACL2_MUTE       2
+#define AD1938_DACR2_MUTE       3
+#define AD1938_DACL3_MUTE       4
+#define AD1938_DACR3_MUTE       5
+#define AD1938_DACL4_MUTE       6
+#define AD1938_DACR4_MUTE       7
+#define AD1938_DAC_L1_VOL       6
+#define AD1938_DAC_R1_VOL       7
+#define AD1938_DAC_L2_VOL       8
+#define AD1938_DAC_R2_VOL       9
+#define AD1938_DAC_L3_VOL       10
+#define AD1938_DAC_R3_VOL       11
+#define AD1938_DAC_L4_VOL       12
+#define AD1938_DAC_R4_VOL       13
+#define AD1938_ADC_CTRL0        14
+#define AD1938_ADC_POWERDOWN           0x01
+#define AD1938_ADC_HIGHPASS_FILTER	1
+#define AD1938_ADCL1_MUTE 		2
+#define AD1938_ADCR1_MUTE 		3
+#define AD1938_ADCL2_MUTE 		4
+#define AD1938_ADCR2_MUTE 		5
+#define AD1938_ADC_CTRL1        15
+#define AD1938_ADC_SERFMT_MASK		0x60
+#define AD1938_ADC_SERFMT_STEREO	(0 << 5)
+#define AD1938_ADC_SERFMT_TDM		(1 << 2)
+#define AD1938_ADC_SERFMT_AUX		(2 << 5)
+#define AD1938_ADC_WORD_LEN_MASK	0x3
+#define AD1938_ADC_CTRL2        16
+#define AD1938_ADC_2_CHANNELS   0
+#define AD1938_ADC_4_CHANNELS   1
+#define AD1938_ADC_8_CHANNELS   2
+#define AD1938_ADC_16_CHANNELS  3
+#define AD1938_ADC_CHAN_SHFT    4
+#define AD1938_ADC_CHAN_MASK    (3 << AD1938_ADC_CHAN_SHFT)
+#define AD1938_ADC_LCR_MASTER   (1 << 3)
+#define AD1938_ADC_BCLK_MASTER  (1 << 6)
+#define AD1938_ADC_LEFT_HIGH    (1 << 2)
+#define AD1938_ADC_BCLK_INV     (1 << 1)
+
+#define AD1938_NUM_REGS          17
+
+extern struct snd_soc_dai ad1938_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ad1938;
+#endif
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index ddb3b08..d7440a9 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -137,13 +137,13 @@
 		.channels_min = 2,
 		.channels_max = 6,
 		.rates = SNDRV_PCM_RATE_48000,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE, },
+		.formats = SND_SOC_STD_AC97_FMTS, },
 	.capture = {
 		.stream_name = "Capture",
 		.channels_min = 2,
 		.channels_max = 2,
 		.rates = SNDRV_PCM_RATE_48000,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE, },
+		.formats = SND_SOC_STD_AC97_FMTS, },
 };
 EXPORT_SYMBOL_GPL(ad1980_dai);
 
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index dd33802..0abec0d 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -59,21 +59,6 @@
 	return cache[reg];
 }
 
-static inline unsigned int ak4535_read(struct snd_soc_codec *codec,
-	unsigned int reg)
-{
-	u8 data;
-	data = reg;
-
-	if (codec->hw_write(codec->control_data, &data, 1) != 1)
-		return -EIO;
-
-	if (codec->hw_read(codec->control_data, &data, 1) != 1)
-		return -EIO;
-
-	return data;
-};
-
 /*
  * write ak4535 register cache
  */
@@ -635,7 +620,6 @@
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
 		codec->hw_write = (hw_write_t)i2c_master_send;
-		codec->hw_read = (hw_read_t)i2c_master_recv;
 		ret = ak4535_add_i2c_device(pdev, setup);
 	}
 #endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 7fa09a3..ca1e24a 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -18,7 +18,7 @@
  * - The machine driver's 'startup' function must call
  *   cs4270_set_dai_sysclk() with the value of MCLK.
  * - Only I2S and left-justified modes are supported
- * - Power management is not supported
+ * - Power management is supported
  */
 
 #include <linux/module.h>
@@ -27,6 +27,7 @@
 #include <sound/soc.h>
 #include <sound/initval.h>
 #include <linux/i2c.h>
+#include <linux/delay.h>
 
 #include "cs4270.h"
 
@@ -56,6 +57,7 @@
 #define CS4270_FIRSTREG	0x01
 #define CS4270_LASTREG	0x08
 #define CS4270_NUMREGS	(CS4270_LASTREG - CS4270_FIRSTREG + 1)
+#define CS4270_I2C_INCR	0x80
 
 /* Bit masks for the CS4270 registers */
 #define CS4270_CHIPID_ID	0xF0
@@ -64,6 +66,8 @@
 #define CS4270_PWRCTL_PDN_ADC	0x20
 #define CS4270_PWRCTL_PDN_DAC	0x02
 #define CS4270_PWRCTL_PDN	0x01
+#define CS4270_PWRCTL_PDN_ALL	\
+	(CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC | CS4270_PWRCTL_PDN)
 #define CS4270_MODE_SPEED_MASK	0x30
 #define CS4270_MODE_1X		0x00
 #define CS4270_MODE_2X		0x10
@@ -109,6 +113,7 @@
 	unsigned int mclk; /* Input frequency of the MCLK pin */
 	unsigned int mode; /* The mode (I2S or left-justified) */
 	unsigned int slave_mode;
+	unsigned int manual_mute;
 };
 
 /**
@@ -295,7 +300,7 @@
 	s32 length;
 
 	length = i2c_smbus_read_i2c_block_data(i2c_client,
-		CS4270_FIRSTREG | 0x80, CS4270_NUMREGS, cache);
+		CS4270_FIRSTREG | CS4270_I2C_INCR, CS4270_NUMREGS, cache);
 
 	if (length != CS4270_NUMREGS) {
 		dev_err(codec->dev, "i2c read failure, addr=0x%x\n",
@@ -453,7 +458,7 @@
 }
 
 /**
- * cs4270_mute - enable/disable the CS4270 external mute
+ * cs4270_dai_mute - enable/disable the CS4270 external mute
  * @dai: the SOC DAI
  * @mute: 0 = disable mute, 1 = enable mute
  *
@@ -462,21 +467,52 @@
  * board does not have the MUTEA or MUTEB pins connected to such circuitry,
  * then this function will do nothing.
  */
-static int cs4270_mute(struct snd_soc_dai *dai, int mute)
+static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
+	struct cs4270_private *cs4270 = codec->private_data;
 	int reg6;
 
 	reg6 = snd_soc_read(codec, CS4270_MUTE);
 
 	if (mute)
 		reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B;
-	else
+	else {
 		reg6 &= ~(CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B);
+		reg6 |= cs4270->manual_mute;
+	}
 
 	return snd_soc_write(codec, CS4270_MUTE, reg6);
 }
 
+/**
+ * cs4270_soc_put_mute - put callback for the 'Master Playback switch'
+ * 			 alsa control.
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * This function basically passes the arguments on to the generic
+ * snd_soc_put_volsw() function and saves the mute information in
+ * our private data structure. This is because we want to prevent
+ * cs4270_dai_mute() neglecting the user's decision to manually
+ * mute the codec's output.
+ *
+ * Returns 0 for success.
+ */
+static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cs4270_private *cs4270 = codec->private_data;
+	int left = !ucontrol->value.integer.value[0];
+	int right = !ucontrol->value.integer.value[1];
+
+	cs4270->manual_mute = (left ? CS4270_MUTE_DAC_A : 0) |
+			      (right ? CS4270_MUTE_DAC_B : 0);
+
+	return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
 /* A list of non-DAPM controls that the CS4270 supports */
 static const struct snd_kcontrol_new cs4270_snd_controls[] = {
 	SOC_DOUBLE_R("Master Playback Volume",
@@ -486,7 +522,9 @@
 	SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0),
 	SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1),
 	SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0),
-	SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 0)
+	SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1),
+	SOC_DOUBLE_EXT("Master Playback Switch", CS4270_MUTE, 0, 1, 1, 1,
+		snd_soc_get_volsw, cs4270_soc_put_mute),
 };
 
 /*
@@ -506,7 +544,7 @@
 	.hw_params	= cs4270_hw_params,
 	.set_sysclk	= cs4270_set_dai_sysclk,
 	.set_fmt	= cs4270_set_dai_fmt,
-	.digital_mute	= cs4270_mute,
+	.digital_mute	= cs4270_dai_mute,
 };
 
 struct snd_soc_dai cs4270_dai = {
@@ -753,6 +791,74 @@
 };
 MODULE_DEVICE_TABLE(i2c, cs4270_id);
 
+#ifdef CONFIG_PM
+
+/* This suspend/resume implementation can handle both - a simple standby
+ * where the codec remains powered, and a full suspend, where the voltage
+ * domain the codec is connected to is teared down and/or any other hardware
+ * reset condition is asserted.
+ *
+ * The codec's own power saving features are enabled in the suspend callback,
+ * and all registers are written back to the hardware when resuming.
+ */
+
+static int cs4270_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+	struct cs4270_private *cs4270 = i2c_get_clientdata(client);
+	struct snd_soc_codec *codec = &cs4270->codec;
+
+	return snd_soc_suspend_device(codec->dev);
+}
+
+static int cs4270_i2c_resume(struct i2c_client *client)
+{
+	struct cs4270_private *cs4270 = i2c_get_clientdata(client);
+	struct snd_soc_codec *codec = &cs4270->codec;
+
+	return snd_soc_resume_device(codec->dev);
+}
+
+static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+	struct snd_soc_codec *codec = cs4270_codec;
+	int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+
+	return snd_soc_write(codec, CS4270_PWRCTL, reg);
+}
+
+static int cs4270_soc_resume(struct platform_device *pdev)
+{
+	struct snd_soc_codec *codec = cs4270_codec;
+	struct i2c_client *i2c_client = codec->control_data;
+	int reg;
+
+	/* In case the device was put to hard reset during sleep, we need to
+	 * wait 500ns here before any I2C communication. */
+	ndelay(500);
+
+	/* first restore the entire register cache ... */
+	for (reg = CS4270_FIRSTREG; reg <= CS4270_LASTREG; reg++) {
+		u8 val = snd_soc_read(codec, reg);
+
+		if (i2c_smbus_write_byte_data(i2c_client, reg, val)) {
+			dev_err(codec->dev, "i2c write failed\n");
+			return -EIO;
+		}
+	}
+
+	/* ... then disable the power-down bits */
+	reg = snd_soc_read(codec, CS4270_PWRCTL);
+	reg &= ~CS4270_PWRCTL_PDN_ALL;
+
+	return snd_soc_write(codec, CS4270_PWRCTL, reg);
+}
+#else
+#define cs4270_i2c_suspend	NULL
+#define cs4270_i2c_resume	NULL
+#define cs4270_soc_suspend	NULL
+#define cs4270_soc_resume	NULL
+#endif /* CONFIG_PM */
+
 /*
  * cs4270_i2c_driver - I2C device identification
  *
@@ -767,6 +873,8 @@
 	.id_table = cs4270_id,
 	.probe = cs4270_i2c_probe,
 	.remove = cs4270_i2c_remove,
+	.suspend = cs4270_i2c_suspend,
+	.resume = cs4270_i2c_resume,
 };
 
 /*
@@ -777,7 +885,9 @@
  */
 struct snd_soc_codec_device soc_codec_device_cs4270 = {
 	.probe = 	cs4270_probe,
-	.remove = 	cs4270_remove
+	.remove = 	cs4270_remove,
+	.suspend =	cs4270_soc_suspend,
+	.resume =	cs4270_soc_resume,
 };
 EXPORT_SYMBOL_GPL(soc_codec_device_cs4270);
 
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
new file mode 100644
index 0000000..38eac9c
--- /dev/null
+++ b/sound/soc/codecs/cx20442.c
@@ -0,0 +1,501 @@
+/*
+ * cx20442.c  --  CX20442 ALSA Soc Audio driver
+ *
+ * Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Initially based on sound/soc/codecs/wm8400.c
+ * Copyright 2008, 2009 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/tty.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/soc-dapm.h>
+
+#include "cx20442.h"
+
+
+struct cx20442_priv {
+	struct snd_soc_codec codec;
+	u8 reg_cache[1];
+};
+
+#define CX20442_PM		0x0
+
+#define CX20442_TELIN		0
+#define CX20442_TELOUT		1
+#define CX20442_MIC		2
+#define CX20442_SPKOUT		3
+#define CX20442_AGC		4
+
+static const struct snd_soc_dapm_widget cx20442_dapm_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("TELOUT"),
+	SND_SOC_DAPM_OUTPUT("SPKOUT"),
+	SND_SOC_DAPM_OUTPUT("AGCOUT"),
+
+	SND_SOC_DAPM_MIXER("SPKOUT Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("TELOUT Amp", CX20442_PM, CX20442_TELOUT, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("SPKOUT Amp", CX20442_PM, CX20442_SPKOUT, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("SPKOUT AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
+
+	SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MICBIAS("TELIN Bias", CX20442_PM, CX20442_TELIN, 0),
+	SND_SOC_DAPM_MICBIAS("MIC Bias", CX20442_PM, CX20442_MIC, 0),
+
+	SND_SOC_DAPM_PGA("MIC AGC", CX20442_PM, CX20442_AGC, 0, NULL, 0),
+
+	SND_SOC_DAPM_INPUT("TELIN"),
+	SND_SOC_DAPM_INPUT("MIC"),
+	SND_SOC_DAPM_INPUT("AGCIN"),
+};
+
+static const struct snd_soc_dapm_route cx20442_audio_map[] = {
+	{"TELOUT", NULL, "TELOUT Amp"},
+
+	{"SPKOUT", NULL, "SPKOUT Mixer"},
+	{"SPKOUT Mixer", NULL, "SPKOUT Amp"},
+
+	{"TELOUT Amp", NULL, "DAC"},
+	{"SPKOUT Amp", NULL, "DAC"},
+
+	{"SPKOUT Mixer", NULL, "SPKOUT AGC"},
+	{"SPKOUT AGC", NULL, "AGCIN"},
+
+	{"AGCOUT", NULL, "MIC AGC"},
+	{"MIC AGC", NULL, "MIC"},
+
+	{"MIC Bias", NULL, "MIC"},
+	{"Input Mixer", NULL, "MIC Bias"},
+
+	{"TELIN Bias", NULL, "TELIN"},
+	{"Input Mixer", NULL, "TELIN Bias"},
+
+	{"ADC", NULL, "Input Mixer"},
+};
+
+static int cx20442_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, cx20442_dapm_widgets,
+				  ARRAY_SIZE(cx20442_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, cx20442_audio_map,
+				ARRAY_SIZE(cx20442_audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+static unsigned int cx20442_read_reg_cache(struct snd_soc_codec *codec,
+							unsigned int reg)
+{
+	u8 *reg_cache = codec->reg_cache;
+
+	if (reg >= codec->reg_cache_size)
+		return -EINVAL;
+
+	return reg_cache[reg];
+}
+
+enum v253_vls {
+	V253_VLS_NONE = 0,
+	V253_VLS_T,
+	V253_VLS_L,
+	V253_VLS_LT,
+	V253_VLS_S,
+	V253_VLS_ST,
+	V253_VLS_M,
+	V253_VLS_MST,
+	V253_VLS_S1,
+	V253_VLS_S1T,
+	V253_VLS_MS1T,
+	V253_VLS_M1,
+	V253_VLS_M1ST,
+	V253_VLS_M1S1T,
+	V253_VLS_H,
+	V253_VLS_HT,
+	V253_VLS_MS,
+	V253_VLS_MS1,
+	V253_VLS_M1S,
+	V253_VLS_M1S1,
+	V253_VLS_TEST,
+};
+
+static int cx20442_pm_to_v253_vls(u8 value)
+{
+	switch (value & ~(1 << CX20442_AGC)) {
+	case 0:
+		return V253_VLS_T;
+	case (1 << CX20442_SPKOUT):
+	case (1 << CX20442_MIC):
+	case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
+		return V253_VLS_M1S1;
+	case (1 << CX20442_TELOUT):
+	case (1 << CX20442_TELIN):
+	case (1 << CX20442_TELOUT) | (1 << CX20442_TELIN):
+		return V253_VLS_L;
+	case (1 << CX20442_TELOUT) | (1 << CX20442_MIC):
+		return V253_VLS_NONE;
+	}
+	return -EINVAL;
+}
+static int cx20442_pm_to_v253_vsp(u8 value)
+{
+	switch (value & ~(1 << CX20442_AGC)) {
+	case (1 << CX20442_SPKOUT):
+	case (1 << CX20442_MIC):
+	case (1 << CX20442_SPKOUT) | (1 << CX20442_MIC):
+		return (bool)(value & (1 << CX20442_AGC));
+	}
+	return (value & (1 << CX20442_AGC)) ? -EINVAL : 0;
+}
+
+static int cx20442_write(struct snd_soc_codec *codec, unsigned int reg,
+							unsigned int value)
+{
+	u8 *reg_cache = codec->reg_cache;
+	int vls, vsp, old, len;
+	char buf[18];
+
+	if (reg >= codec->reg_cache_size)
+		return -EINVAL;
+
+	/* hw_write and control_data pointers required for talking to the modem
+	 * are expected to be set by the line discipline initialization code */
+	if (!codec->hw_write || !codec->control_data)
+		return -EIO;
+
+	old = reg_cache[reg];
+	reg_cache[reg] = value;
+
+	vls = cx20442_pm_to_v253_vls(value);
+	if (vls < 0)
+		return vls;
+
+	vsp = cx20442_pm_to_v253_vsp(value);
+	if (vsp < 0)
+		return vsp;
+
+	if ((vls == V253_VLS_T) ||
+			(vls == cx20442_pm_to_v253_vls(old))) {
+		if (vsp == cx20442_pm_to_v253_vsp(old))
+			return 0;
+		len = snprintf(buf, ARRAY_SIZE(buf), "at+vsp=%d\r", vsp);
+	} else if (vsp == cx20442_pm_to_v253_vsp(old))
+		len = snprintf(buf, ARRAY_SIZE(buf), "at+vls=%d\r", vls);
+	else
+		len = snprintf(buf, ARRAY_SIZE(buf),
+					"at+vls=%d;+vsp=%d\r", vls, vsp);
+
+	if (unlikely(len > (ARRAY_SIZE(buf) - 1)))
+		return -ENOMEM;
+
+	dev_dbg(codec->dev, "%s: %s\n", __func__, buf);
+	if (codec->hw_write(codec->control_data, buf, len) != len)
+		return -EIO;
+
+	return 0;
+}
+
+
+/* Moved up here as line discipline referres it during initialization */
+static struct snd_soc_codec *cx20442_codec;
+
+
+/*
+ * Line discpline related code
+ *
+ * Any of the callback functions below can be used in two ways:
+ * 1) registerd by a machine driver as one of line discipline operations,
+ * 2) called from a machine's provided line discipline callback function
+ *    in case when extra machine specific code must be run as well.
+ */
+
+/* Modem init: echo off, digital speaker off, quiet off, voice mode */
+static const char *v253_init = "ate0m0q0+fclass=8\r";
+
+/* Line discipline .open() */
+static int v253_open(struct tty_struct *tty)
+{
+	struct snd_soc_codec *codec = cx20442_codec;
+	int ret, len = strlen(v253_init);
+
+	/* Doesn't make sense without write callback */
+	if (!tty->ops->write)
+		return -EINVAL;
+
+	/* Pass the codec structure address for use by other ldisc callbacks */
+	tty->disc_data = codec;
+
+	if (tty->ops->write(tty, v253_init, len) != len) {
+		ret = -EIO;
+		goto err;
+	}
+	/* Actual setup will be performed after the modem responds. */
+	return 0;
+err:
+	tty->disc_data = NULL;
+	return ret;
+}
+
+/* Line discipline .close() */
+static void v253_close(struct tty_struct *tty)
+{
+	struct snd_soc_codec *codec = tty->disc_data;
+
+	tty->disc_data = NULL;
+
+	if (!codec)
+		return;
+
+	/* Prevent the codec driver from further accessing the modem */
+	codec->hw_write = NULL;
+	codec->control_data = NULL;
+	codec->pop_time = 0;
+}
+
+/* Line discipline .hangup() */
+static int v253_hangup(struct tty_struct *tty)
+{
+	v253_close(tty);
+	return 0;
+}
+
+/* Line discipline .receive_buf() */
+static void v253_receive(struct tty_struct *tty,
+				const unsigned char *cp, char *fp, int count)
+{
+	struct snd_soc_codec *codec = tty->disc_data;
+
+	if (!codec)
+		return;
+
+	if (!codec->control_data) {
+		/* First modem response, complete setup procedure */
+
+		/* Set up codec driver access to modem controls */
+		codec->control_data = tty;
+		codec->hw_write = (hw_write_t)tty->ops->write;
+		codec->pop_time = 1;
+	}
+}
+
+/* Line discipline .write_wakeup() */
+static void v253_wakeup(struct tty_struct *tty)
+{
+}
+
+struct tty_ldisc_ops v253_ops = {
+	.magic = TTY_LDISC_MAGIC,
+	.name = "cx20442",
+	.owner = THIS_MODULE,
+	.open = v253_open,
+	.close = v253_close,
+	.hangup = v253_hangup,
+	.receive_buf = v253_receive,
+	.write_wakeup = v253_wakeup,
+};
+EXPORT_SYMBOL_GPL(v253_ops);
+
+
+/*
+ * Codec DAI
+ */
+
+struct snd_soc_dai cx20442_dai = {
+	.name = "CX20442",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+	},
+};
+EXPORT_SYMBOL_GPL(cx20442_dai);
+
+static int cx20442_codec_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret;
+
+	if (!cx20442_codec) {
+		dev_err(&pdev->dev, "cx20442 not yet discovered\n");
+		return -ENODEV;
+	}
+	codec = cx20442_codec;
+
+	socdev->card->codec = codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to create pcms\n");
+		goto pcm_err;
+	}
+
+	cx20442_add_widgets(codec);
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register card\n");
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+/* power down chip */
+static int cx20442_codec_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device cx20442_codec_dev = {
+	.probe = 	cx20442_codec_probe,
+	.remove = 	cx20442_codec_remove,
+};
+EXPORT_SYMBOL_GPL(cx20442_codec_dev);
+
+static int cx20442_register(struct cx20442_priv *cx20442)
+{
+	struct snd_soc_codec *codec = &cx20442->codec;
+	int ret;
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->name = "CX20442";
+	codec->owner = THIS_MODULE;
+	codec->private_data = cx20442;
+
+	codec->dai = &cx20442_dai;
+	codec->num_dai = 1;
+
+	codec->reg_cache = &cx20442->reg_cache;
+	codec->reg_cache_size = ARRAY_SIZE(cx20442->reg_cache);
+	codec->read = cx20442_read_reg_cache;
+	codec->write = cx20442_write;
+
+	codec->bias_level = SND_SOC_BIAS_OFF;
+
+	cx20442_dai.dev = codec->dev;
+
+	cx20442_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto err;
+	}
+
+	ret = snd_soc_register_dai(&cx20442_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		goto err_codec;
+	}
+
+	return 0;
+
+err_codec:
+	snd_soc_unregister_codec(codec);
+err:
+	cx20442_codec = NULL;
+	kfree(cx20442);
+	return ret;
+}
+
+static void cx20442_unregister(struct cx20442_priv *cx20442)
+{
+	snd_soc_unregister_dai(&cx20442_dai);
+	snd_soc_unregister_codec(&cx20442->codec);
+
+	cx20442_codec = NULL;
+	kfree(cx20442);
+}
+
+static int cx20442_platform_probe(struct platform_device *pdev)
+{
+	struct cx20442_priv *cx20442;
+	struct snd_soc_codec *codec;
+
+	cx20442 = kzalloc(sizeof(struct cx20442_priv), GFP_KERNEL);
+	if (cx20442 == NULL)
+		return -ENOMEM;
+
+	codec = &cx20442->codec;
+
+	codec->control_data = NULL;
+	codec->hw_write = NULL;
+	codec->pop_time = 0;
+
+	codec->dev = &pdev->dev;
+	platform_set_drvdata(pdev, cx20442);
+
+	return cx20442_register(cx20442);
+}
+
+static int __exit cx20442_platform_remove(struct platform_device *pdev)
+{
+	struct cx20442_priv *cx20442 = platform_get_drvdata(pdev);
+
+	cx20442_unregister(cx20442);
+	return 0;
+}
+
+static struct platform_driver cx20442_platform_driver = {
+	.driver = {
+		.name = "cx20442",
+		.owner = THIS_MODULE,
+		},
+	.probe = cx20442_platform_probe,
+	.remove = __exit_p(cx20442_platform_remove),
+};
+
+static int __init cx20442_init(void)
+{
+	return platform_driver_register(&cx20442_platform_driver);
+}
+module_init(cx20442_init);
+
+static void __exit cx20442_exit(void)
+{
+	platform_driver_unregister(&cx20442_platform_driver);
+}
+module_exit(cx20442_exit);
+
+MODULE_DESCRIPTION("ASoC CX20442-11 voice modem codec driver");
+MODULE_AUTHOR("Janusz Krzysztofik");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:cx20442");
diff --git a/sound/soc/codecs/cx20442.h b/sound/soc/codecs/cx20442.h
new file mode 100644
index 0000000..688a5eb
--- /dev/null
+++ b/sound/soc/codecs/cx20442.h
@@ -0,0 +1,20 @@
+/*
+ * cx20442.h  --  audio driver for CX20442
+ *
+ * Copyright 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef _CX20442_CODEC_H
+#define _CX20442_CODEC_H
+
+extern struct snd_soc_dai cx20442_dai;
+extern struct snd_soc_codec_device cx20442_codec_dev;
+extern struct tty_ldisc_ops v253_ops;
+
+#endif
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
new file mode 100644
index 0000000..9e7e964
--- /dev/null
+++ b/sound/soc/codecs/max9877.c
@@ -0,0 +1,308 @@
+/*
+ * max9877.c  --  amp driver for max9877
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max9877.h"
+
+static struct i2c_client *i2c;
+
+static u8 max9877_regs[5] = { 0x40, 0x00, 0x00, 0x00, 0x49 };
+
+static void max9877_write_regs(void)
+{
+	unsigned int i;
+	u8 data[6];
+
+	data[0] = MAX9877_INPUT_MODE;
+	for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
+		data[i + 1] = max9877_regs[i];
+
+	if (i2c_master_send(i2c, data, 6) != 6)
+		dev_err(&i2c->dev, "i2c write failed\n");
+}
+
+static int max9877_get_reg(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int mask = mc->max;
+	unsigned int invert = mc->invert;
+
+	ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
+
+	if (invert)
+		ucontrol->value.integer.value[0] =
+			mask - ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int max9877_set_reg(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	unsigned int mask = mc->max;
+	unsigned int invert = mc->invert;
+	unsigned int val = (ucontrol->value.integer.value[0] & mask);
+
+	if (invert)
+		val = mask - val;
+
+	if (((max9877_regs[reg] >> shift) & mask) == val)
+		return 0;
+
+	max9877_regs[reg] &= ~(mask << shift);
+	max9877_regs[reg] |= val << shift;
+	max9877_write_regs();
+
+	return 1;
+}
+
+static int max9877_get_2reg(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	unsigned int mask = mc->max;
+
+	ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
+	ucontrol->value.integer.value[1] = (max9877_regs[reg2] >> shift) & mask;
+
+	return 0;
+}
+
+static int max9877_set_2reg(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int reg = mc->reg;
+	unsigned int reg2 = mc->rreg;
+	unsigned int shift = mc->shift;
+	unsigned int mask = mc->max;
+	unsigned int val = (ucontrol->value.integer.value[0] & mask);
+	unsigned int val2 = (ucontrol->value.integer.value[1] & mask);
+	unsigned int change = 1;
+
+	if (((max9877_regs[reg] >> shift) & mask) == val)
+		change = 0;
+
+	if (((max9877_regs[reg2] >> shift) & mask) == val2)
+		change = 0;
+
+	if (change) {
+		max9877_regs[reg] &= ~(mask << shift);
+		max9877_regs[reg] |= val << shift;
+		max9877_regs[reg2] &= ~(mask << shift);
+		max9877_regs[reg2] |= val2 << shift;
+		max9877_write_regs();
+	}
+
+	return change;
+}
+
+static int max9877_get_out_mode(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u8 value = max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK;
+
+	if (value)
+		value -= 1;
+
+	ucontrol->value.integer.value[0] = value;
+	return 0;
+}
+
+static int max9877_set_out_mode(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u8 value = ucontrol->value.integer.value[0];
+
+	value += 1;
+
+	if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK) == value)
+		return 0;
+
+	max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OUTMODE_MASK;
+	max9877_regs[MAX9877_OUTPUT_MODE] |= value;
+	max9877_write_regs();
+	return 1;
+}
+
+static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u8 value = (max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK);
+
+	value = value >> MAX9877_OSC_OFFSET;
+
+	ucontrol->value.integer.value[0] = value;
+	return 0;
+}
+
+static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	u8 value = ucontrol->value.integer.value[0];
+
+	value = value << MAX9877_OSC_OFFSET;
+	if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK) == value)
+		return 0;
+
+	max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OSC_MASK;
+	max9877_regs[MAX9877_OUTPUT_MODE] |= value;
+	max9877_write_regs();
+	return 1;
+}
+
+static const unsigned int max9877_pgain_tlv[] = {
+	TLV_DB_RANGE_HEAD(2),
+	0, 1, TLV_DB_SCALE_ITEM(0, 900, 0),
+	2, 2, TLV_DB_SCALE_ITEM(2000, 0, 0),
+};
+
+static const unsigned int max9877_output_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 7, TLV_DB_SCALE_ITEM(-7900, 400, 1),
+	8, 15, TLV_DB_SCALE_ITEM(-4700, 300, 0),
+	16, 23, TLV_DB_SCALE_ITEM(-2300, 200, 0),
+	24, 31, TLV_DB_SCALE_ITEM(-700, 100, 0),
+};
+
+static const char *max9877_out_mode[] = {
+	"INA -> SPK",
+	"INA -> HP",
+	"INA -> SPK and HP",
+	"INB -> SPK",
+	"INB -> HP",
+	"INB -> SPK and HP",
+	"INA + INB -> SPK",
+	"INA + INB -> HP",
+	"INA + INB -> SPK and HP",
+};
+
+static const char *max9877_osc_mode[] = {
+	"1176KHz",
+	"1100KHz",
+	"700KHz",
+};
+
+static const struct soc_enum max9877_enum[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_out_mode), max9877_out_mode),
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
+};
+
+static const struct snd_kcontrol_new max9877_controls[] = {
+	SOC_SINGLE_EXT_TLV("MAX9877 PGAINA Playback Volume",
+			MAX9877_INPUT_MODE, 0, 2, 0,
+			max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
+	SOC_SINGLE_EXT_TLV("MAX9877 PGAINB Playback Volume",
+			MAX9877_INPUT_MODE, 2, 2, 0,
+			max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
+	SOC_SINGLE_EXT_TLV("MAX9877 Amp Speaker Playback Volume",
+			MAX9877_SPK_VOLUME, 0, 31, 0,
+			max9877_get_reg, max9877_set_reg, max9877_output_tlv),
+	SOC_DOUBLE_R_EXT_TLV("MAX9877 Amp HP Playback Volume",
+			MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
+			max9877_get_2reg, max9877_set_2reg, max9877_output_tlv),
+	SOC_SINGLE_EXT("MAX9877 INB Stereo Switch",
+			MAX9877_INPUT_MODE, 4, 1, 1,
+			max9877_get_reg, max9877_set_reg),
+	SOC_SINGLE_EXT("MAX9877 INA Stereo Switch",
+			MAX9877_INPUT_MODE, 5, 1, 1,
+			max9877_get_reg, max9877_set_reg),
+	SOC_SINGLE_EXT("MAX9877 Zero-crossing detection Switch",
+			MAX9877_INPUT_MODE, 6, 1, 0,
+			max9877_get_reg, max9877_set_reg),
+	SOC_SINGLE_EXT("MAX9877 Bypass Mode Switch",
+			MAX9877_OUTPUT_MODE, 6, 1, 0,
+			max9877_get_reg, max9877_set_reg),
+	SOC_SINGLE_EXT("MAX9877 Shutdown Mode Switch",
+			MAX9877_OUTPUT_MODE, 7, 1, 1,
+			max9877_get_reg, max9877_set_reg),
+	SOC_ENUM_EXT("MAX9877 Output Mode", max9877_enum[0],
+			max9877_get_out_mode, max9877_set_out_mode),
+	SOC_ENUM_EXT("MAX9877 Oscillator Mode", max9877_enum[1],
+			max9877_get_osc_mode, max9877_set_osc_mode),
+};
+
+/* This function is called from ASoC machine driver */
+int max9877_add_controls(struct snd_soc_codec *codec)
+{
+	return snd_soc_add_controls(codec, max9877_controls,
+			ARRAY_SIZE(max9877_controls));
+}
+EXPORT_SYMBOL_GPL(max9877_add_controls);
+
+static int __devinit max9877_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	i2c = client;
+
+	max9877_write_regs();
+
+	return 0;
+}
+
+static __devexit int max9877_i2c_remove(struct i2c_client *client)
+{
+	i2c = NULL;
+
+	return 0;
+}
+
+static const struct i2c_device_id max9877_i2c_id[] = {
+	{ "max9877", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
+
+static struct i2c_driver max9877_i2c_driver = {
+	.driver = {
+		.name = "max9877",
+		.owner = THIS_MODULE,
+	},
+	.probe = max9877_i2c_probe,
+	.remove = __devexit_p(max9877_i2c_remove),
+	.id_table = max9877_i2c_id,
+};
+
+static int __init max9877_init(void)
+{
+	return i2c_add_driver(&max9877_i2c_driver);
+}
+module_init(max9877_init);
+
+static void __exit max9877_exit(void)
+{
+	i2c_del_driver(&max9877_i2c_driver);
+}
+module_exit(max9877_exit);
+
+MODULE_DESCRIPTION("ASoC MAX9877 amp driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9877.h b/sound/soc/codecs/max9877.h
new file mode 100644
index 0000000..6da7229
--- /dev/null
+++ b/sound/soc/codecs/max9877.h
@@ -0,0 +1,37 @@
+/*
+ * max9877.h  --  amp driver for max9877
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef _MAX9877_H
+#define _MAX9877_H
+
+#define MAX9877_INPUT_MODE		0x00
+#define MAX9877_SPK_VOLUME		0x01
+#define MAX9877_HPL_VOLUME		0x02
+#define MAX9877_HPR_VOLUME		0x03
+#define MAX9877_OUTPUT_MODE		0x04
+
+/* MAX9877_INPUT_MODE */
+#define MAX9877_INB			(1 << 4)
+#define MAX9877_INA			(1 << 5)
+#define MAX9877_ZCD			(1 << 6)
+
+/* MAX9877_OUTPUT_MODE */
+#define MAX9877_OUTMODE_MASK		(15 << 0)
+#define MAX9877_OSC_MASK		(3 << 4)
+#define MAX9877_OSC_OFFSET		4
+#define MAX9877_BYPASS			(1 << 6)
+#define MAX9877_SHDN			(1 << 7)
+
+extern int max9877_add_controls(struct snd_soc_codec *codec);
+
+#endif
diff --git a/sound/soc/codecs/spdif_transciever.c b/sound/soc/codecs/spdif_transciever.c
new file mode 100644
index 0000000..a631911
--- /dev/null
+++ b/sound/soc/codecs/spdif_transciever.c
@@ -0,0 +1,74 @@
+/*
+ * ALSA SoC SPDIF DIT driver
+ *
+ *  This driver is used by controllers which can operate in DIT (SPDI/F) where
+ *  no codec is needed.  This file provides stub codec that can be used
+ *  in these configurations. TI DaVinci Audio controller uses this driver.
+ *
+ * Author:      Steve Chen,  <schen@mvista.com>
+ * Copyright:   (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright:   (C) 2009  Texas Instruments, India
+ *
+ * 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 <sound/soc.h>
+#include <sound/pcm.h>
+
+#include "spdif_transciever.h"
+
+MODULE_LICENSE("GPL");
+
+#define STUB_RATES	SNDRV_PCM_RATE_8000_96000
+#define STUB_FORMATS	SNDRV_PCM_FMTBIT_S16_LE
+
+struct snd_soc_dai dit_stub_dai = {
+	.name		= "DIT",
+	.playback 	= {
+		.stream_name	= "Playback",
+		.channels_min	= 1,
+		.channels_max	= 384,
+		.rates		= STUB_RATES,
+		.formats	= STUB_FORMATS,
+	},
+};
+EXPORT_SYMBOL_GPL(dit_stub_dai);
+
+static int spdif_dit_probe(struct platform_device *pdev)
+{
+	dit_stub_dai.dev = &pdev->dev;
+	return snd_soc_register_dai(&dit_stub_dai);
+}
+
+static int spdif_dit_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_dai(&dit_stub_dai);
+	return 0;
+}
+
+static struct platform_driver spdif_dit_driver = {
+	.probe		= spdif_dit_probe,
+	.remove		= spdif_dit_remove,
+	.driver		= {
+		.name	= "spdif-dit",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init dit_modinit(void)
+{
+	return platform_driver_register(&spdif_dit_driver);
+}
+
+static void __exit dit_exit(void)
+{
+	platform_driver_unregister(&spdif_dit_driver);
+}
+
+module_init(dit_modinit);
+module_exit(dit_exit);
+
diff --git a/sound/soc/codecs/spdif_transciever.h b/sound/soc/codecs/spdif_transciever.h
new file mode 100644
index 0000000..296f2eb
--- /dev/null
+++ b/sound/soc/codecs/spdif_transciever.h
@@ -0,0 +1,17 @@
+/*
+ * ALSA SoC DIT/DIR driver header
+ *
+ * Author:      Steve Chen,  <schen@mvista.com>
+ * Copyright:   (C) 2008 MontaVista Software, Inc., <source@mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef CODEC_STUBS_H
+#define CODEC_STUBS_H
+
+extern struct snd_soc_dai dit_stub_dai;
+
+#endif /* CODEC_STUBS_H */
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 87f606c7..c550750 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -336,15 +336,17 @@
 			master_runtime->sample_bits,
 			master_runtime->rate);
 
-		snd_pcm_hw_constraint_minmax(substream->runtime,
-					     SNDRV_PCM_HW_PARAM_RATE,
-					     master_runtime->rate,
-					     master_runtime->rate);
+		if (master_runtime->rate != 0)
+			snd_pcm_hw_constraint_minmax(substream->runtime,
+						     SNDRV_PCM_HW_PARAM_RATE,
+						     master_runtime->rate,
+						     master_runtime->rate);
 
-		snd_pcm_hw_constraint_minmax(substream->runtime,
-					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-					     master_runtime->sample_bits,
-					     master_runtime->sample_bits);
+		if (master_runtime->sample_bits != 0)
+			snd_pcm_hw_constraint_minmax(substream->runtime,
+						     SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+						     master_runtime->sample_bits,
+						     master_runtime->sample_bits);
 
 		ssm2602->slave_substream = substream;
 	} else
@@ -372,6 +374,7 @@
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
 	struct ssm2602_priv *ssm2602 = codec->private_data;
+
 	/* deactivate */
 	if (!codec->active)
 		ssm2602_write(codec, SSM2602_ACTIVE, 0);
@@ -497,11 +500,9 @@
 	return 0;
 }
 
-#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
-		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
-		SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
-		SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
-		SNDRV_PCM_RATE_96000)
+#define SSM2602_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_32000 |\
+		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+		SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 
 #define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
 		SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
new file mode 100644
index 0000000..befc648
--- /dev/null
+++ b/sound/soc/codecs/stac9766.c
@@ -0,0 +1,463 @@
+/*
+ * stac9766.c  --  ALSA SoC STAC9766 codec support
+ *
+ * Copyright 2009 Jon Smirl, Digispeaker
+ * Author: Jon Smirl <jonsmirl@gmail.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Features:-
+ *
+ *   o Support for AC97 Codec, S/PDIF
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/soc-of-simple.h>
+
+#include "stac9766.h"
+
+#define STAC9766_VERSION "0.10"
+
+/*
+ * STAC9766 register cache
+ */
+static const u16 stac9766_reg[] = {
+	0x6A90, 0x8000, 0x8000, 0x8000, /* 6 */
+	0x0000, 0x0000, 0x8008, 0x8008, /* e */
+	0x8808, 0x8808, 0x8808, 0x8808, /* 16 */
+	0x8808, 0x0000, 0x8000, 0x0000, /* 1e */
+	0x0000, 0x0000, 0x0000, 0x000f, /* 26 */
+	0x0a05, 0x0400, 0xbb80, 0x0000, /* 2e */
+	0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */
+	0x0000, 0x2000, 0x0000, 0x0100, /* 3e */
+	0x0000, 0x0000, 0x0080, 0x0000, /* 46 */
+	0x0000, 0x0000, 0x0003, 0xffff, /* 4e */
+	0x0000, 0x0000, 0x0000, 0x0000, /* 56 */
+	0x4000, 0x0000, 0x0000, 0x0000, /* 5e */
+	0x1201, 0xFFFF, 0xFFFF, 0x0000, /* 66 */
+	0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
+	0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
+	0x0000, 0x0000, 0x0000, 0x0000, /* 7e */
+};
+
+static const char *stac9766_record_mux[] = {"Mic", "CD", "Video", "AUX",
+			"Line", "Stereo Mix", "Mono Mix", "Phone"};
+static const char *stac9766_mono_mux[] = {"Mix", "Mic"};
+static const char *stac9766_mic_mux[] = {"Mic1", "Mic2"};
+static const char *stac9766_SPDIF_mux[] = {"PCM", "ADC Record"};
+static const char *stac9766_popbypass_mux[] = {"Normal", "Bypass Mixer"};
+static const char *stac9766_record_all_mux[] = {"All analog",
+	"Analog plus DAC"};
+static const char *stac9766_boost1[] = {"0dB", "10dB"};
+static const char *stac9766_boost2[] = {"0dB", "20dB"};
+static const char *stac9766_stereo_mic[] = {"Off", "On"};
+
+static const struct soc_enum stac9766_record_enum =
+	SOC_ENUM_DOUBLE(AC97_REC_SEL, 8, 0, 8, stac9766_record_mux);
+static const struct soc_enum stac9766_mono_enum =
+	SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 9, 2, stac9766_mono_mux);
+static const struct soc_enum stac9766_mic_enum =
+	SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, stac9766_mic_mux);
+static const struct soc_enum stac9766_SPDIF_enum =
+	SOC_ENUM_SINGLE(AC97_STAC_DA_CONTROL, 1, 2, stac9766_SPDIF_mux);
+static const struct soc_enum stac9766_popbypass_enum =
+	SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, stac9766_popbypass_mux);
+static const struct soc_enum stac9766_record_all_enum =
+	SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 12, 2,
+			stac9766_record_all_mux);
+static const struct soc_enum stac9766_boost1_enum =
+	SOC_ENUM_SINGLE(AC97_MIC, 6, 2, stac9766_boost1); /* 0/10dB */
+static const struct soc_enum stac9766_boost2_enum =
+	SOC_ENUM_SINGLE(AC97_STAC_ANALOG_SPECIAL, 2, 2, stac9766_boost2); /* 0/20dB */
+static const struct soc_enum stac9766_stereo_mic_enum =
+	SOC_ENUM_SINGLE(AC97_STAC_STEREO_MIC, 2, 1, stac9766_stereo_mic);
+
+static const DECLARE_TLV_DB_LINEAR(master_tlv, -4600, 0);
+static const DECLARE_TLV_DB_LINEAR(record_tlv, 0, 2250);
+static const DECLARE_TLV_DB_LINEAR(beep_tlv, -4500, 0);
+static const DECLARE_TLV_DB_LINEAR(mix_tlv, -3450, 1200);
+
+static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
+	SOC_DOUBLE_TLV("Speaker Volume", AC97_MASTER, 8, 0, 31, 1, master_tlv),
+	SOC_SINGLE("Speaker Switch", AC97_MASTER, 15, 1, 1),
+	SOC_DOUBLE_TLV("Headphone Volume", AC97_HEADPHONE, 8, 0, 31, 1,
+		       master_tlv),
+	SOC_SINGLE("Headphone Switch", AC97_HEADPHONE, 15, 1, 1),
+	SOC_SINGLE_TLV("Mono Out Volume", AC97_MASTER_MONO, 0, 31, 1,
+		       master_tlv),
+	SOC_SINGLE("Mono Out Switch", AC97_MASTER_MONO, 15, 1, 1),
+
+	SOC_DOUBLE_TLV("Record Volume", AC97_REC_GAIN, 8, 0, 15, 0, record_tlv),
+	SOC_SINGLE("Record Switch", AC97_REC_GAIN, 15, 1, 1),
+
+
+	SOC_SINGLE_TLV("Beep Volume", AC97_PC_BEEP, 1, 15, 1, beep_tlv),
+	SOC_SINGLE("Beep Switch", AC97_PC_BEEP, 15, 1, 1),
+	SOC_SINGLE("Beep Frequency", AC97_PC_BEEP, 5, 127, 1),
+	SOC_SINGLE_TLV("Phone Volume", AC97_PHONE, 0, 31, 1, mix_tlv),
+	SOC_SINGLE("Phone Switch", AC97_PHONE, 15, 1, 1),
+
+	SOC_ENUM("Mic Boost1", stac9766_boost1_enum),
+	SOC_ENUM("Mic Boost2", stac9766_boost2_enum),
+	SOC_SINGLE_TLV("Mic Volume", AC97_MIC, 0, 31, 1, mix_tlv),
+	SOC_SINGLE("Mic Switch", AC97_MIC, 15, 1, 1),
+	SOC_ENUM("Stereo Mic", stac9766_stereo_mic_enum),
+
+	SOC_DOUBLE_TLV("Line Volume", AC97_LINE, 8, 0, 31, 1, mix_tlv),
+	SOC_SINGLE("Line Switch", AC97_LINE, 15, 1, 1),
+	SOC_DOUBLE_TLV("CD Volume", AC97_CD, 8, 0, 31, 1, mix_tlv),
+	SOC_SINGLE("CD Switch", AC97_CD, 15, 1, 1),
+	SOC_DOUBLE_TLV("AUX Volume", AC97_AUX, 8, 0, 31, 1, mix_tlv),
+	SOC_SINGLE("AUX Switch", AC97_AUX, 15, 1, 1),
+	SOC_DOUBLE_TLV("Video Volume", AC97_VIDEO, 8, 0, 31, 1, mix_tlv),
+	SOC_SINGLE("Video Switch", AC97_VIDEO, 15, 1, 1),
+
+	SOC_DOUBLE_TLV("DAC Volume", AC97_PCM, 8, 0, 31, 1, mix_tlv),
+	SOC_SINGLE("DAC Switch", AC97_PCM, 15, 1, 1),
+	SOC_SINGLE("Loopback Test Switch", AC97_GENERAL_PURPOSE, 7, 1, 0),
+	SOC_SINGLE("3D Volume", AC97_3D_CONTROL, 3, 2, 1),
+	SOC_SINGLE("3D Switch", AC97_GENERAL_PURPOSE, 13, 1, 0),
+
+	SOC_ENUM("SPDIF Mux", stac9766_SPDIF_enum),
+	SOC_ENUM("Mic1/2 Mux", stac9766_mic_enum),
+	SOC_ENUM("Record All Mux", stac9766_record_all_enum),
+	SOC_ENUM("Record Mux", stac9766_record_enum),
+	SOC_ENUM("Mono Mux", stac9766_mono_enum),
+	SOC_ENUM("Pop Bypass Mux", stac9766_popbypass_enum),
+};
+
+static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
+			       unsigned int val)
+{
+	u16 *cache = codec->reg_cache;
+
+	if (reg > AC97_STAC_PAGE0) {
+		stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
+		soc_ac97_ops.write(codec->ac97, reg, val);
+		stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
+		return 0;
+	}
+	if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
+		return -EIO;
+
+	soc_ac97_ops.write(codec->ac97, reg, val);
+	cache[reg / 2] = val;
+	return 0;
+}
+
+static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
+				       unsigned int reg)
+{
+	u16 val = 0, *cache = codec->reg_cache;
+
+	if (reg > AC97_STAC_PAGE0) {
+		stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
+		val = soc_ac97_ops.read(codec->ac97, reg - AC97_STAC_PAGE0);
+		stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
+		return val;
+	}
+	if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
+		return -EIO;
+
+	if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
+		reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
+		reg == AC97_VENDOR_ID2) {
+
+		val = soc_ac97_ops.read(codec->ac97, reg);
+		return val;
+	}
+	return cache[reg / 2];
+}
+
+static int ac97_analog_prepare(struct snd_pcm_substream *substream,
+			       struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned short reg, vra;
+
+	vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
+
+	vra |= 0x1; /* enable variable rate audio */
+
+	stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		reg = AC97_PCM_FRONT_DAC_RATE;
+	else
+		reg = AC97_PCM_LR_ADC_RATE;
+
+	return stac9766_ac97_write(codec, reg, runtime->rate);
+}
+
+static int ac97_digital_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned short reg, vra;
+
+	stac9766_ac97_write(codec, AC97_SPDIF, 0x2002);
+
+	vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
+	vra |= 0x5; /* Enable VRA and SPDIF out */
+
+	stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+
+	reg = AC97_PCM_FRONT_DAC_RATE;
+
+	return stac9766_ac97_write(codec, reg, runtime->rate);
+}
+
+static int ac97_digital_trigger(struct snd_pcm_substream *substream,
+				int cmd, struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	unsigned short vra;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_STOP:
+		vra = stac9766_ac97_read(codec, AC97_EXTENDED_STATUS);
+		vra &= !0x04;
+		stac9766_ac97_write(codec, AC97_EXTENDED_STATUS, vra);
+		break;
+	}
+	return 0;
+}
+
+static int stac9766_set_bias_level(struct snd_soc_codec *codec,
+				   enum snd_soc_bias_level level)
+{
+	switch (level) {
+	case SND_SOC_BIAS_ON: /* full On */
+	case SND_SOC_BIAS_PREPARE: /* partial On */
+	case SND_SOC_BIAS_STANDBY: /* Off, with power */
+		stac9766_ac97_write(codec, AC97_POWERDOWN, 0x0000);
+		break;
+	case SND_SOC_BIAS_OFF: /* Off, without power */
+		/* disable everything including AC link */
+		stac9766_ac97_write(codec, AC97_POWERDOWN, 0xffff);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
+{
+	if (try_warm && soc_ac97_ops.warm_reset) {
+		soc_ac97_ops.warm_reset(codec->ac97);
+		if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
+			return 1;
+	}
+
+	soc_ac97_ops.reset(codec->ac97);
+	if (soc_ac97_ops.warm_reset)
+		soc_ac97_ops.warm_reset(codec->ac97);
+	if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
+		return -EIO;
+	return 0;
+}
+
+static int stac9766_codec_suspend(struct platform_device *pdev,
+				  pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	stac9766_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int stac9766_codec_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	u16 id, reset;
+
+	reset = 0;
+	/* give the codec an AC97 warm reset to start the link */
+reset:
+	if (reset > 5) {
+		printk(KERN_ERR "stac9766 failed to resume");
+		return -EIO;
+	}
+	codec->ac97->bus->ops->warm_reset(codec->ac97);
+	id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2);
+	if (id != 0x4c13) {
+		stac9766_reset(codec, 0);
+		reset++;
+		goto reset;
+	}
+	stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	if (codec->suspend_bias_level == SND_SOC_BIAS_ON)
+		stac9766_set_bias_level(codec, SND_SOC_BIAS_ON);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops stac9766_dai_ops_analog = {
+	.prepare = ac97_analog_prepare,
+};
+
+static struct snd_soc_dai_ops stac9766_dai_ops_digital = {
+	.prepare = ac97_digital_prepare,
+	.trigger = ac97_digital_trigger,
+};
+
+struct snd_soc_dai stac9766_dai[] = {
+{
+	.name = "stac9766 analog",
+	.id = 0,
+	.ac97_control = 1,
+
+	/* stream cababilities */
+	.playback = {
+		.stream_name = "stac9766 analog",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SND_SOC_STD_AC97_FMTS,
+	},
+	.capture = {
+		.stream_name = "stac9766 analog",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = SND_SOC_STD_AC97_FMTS,
+	},
+	/* alsa ops */
+	.ops = &stac9766_dai_ops_analog,
+},
+{
+	.name = "stac9766 IEC958",
+	.id = 1,
+	.ac97_control = 1,
+
+	/* stream cababilities */
+	.playback = {
+		.stream_name = "stac9766 IEC958",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_32000 | \
+			SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE,
+	},
+	/* alsa ops */
+	.ops = &stac9766_dai_ops_digital,
+}
+};
+EXPORT_SYMBOL_GPL(stac9766_dai);
+
+static int stac9766_codec_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION);
+
+	socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
+	if (socdev->card->codec == NULL)
+		return -ENOMEM;
+	codec = socdev->card->codec;
+	mutex_init(&codec->mutex);
+
+	codec->reg_cache = kmemdup(stac9766_reg, sizeof(stac9766_reg),
+				   GFP_KERNEL);
+	if (codec->reg_cache == NULL) {
+		ret = -ENOMEM;
+		goto cache_err;
+	}
+	codec->reg_cache_size = sizeof(stac9766_reg);
+	codec->reg_cache_step = 2;
+
+	codec->name = "STAC9766";
+	codec->owner = THIS_MODULE;
+	codec->dai = stac9766_dai;
+	codec->num_dai = ARRAY_SIZE(stac9766_dai);
+	codec->write = stac9766_ac97_write;
+	codec->read = stac9766_ac97_read;
+	codec->set_bias_level = stac9766_set_bias_level;
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+	if (ret < 0)
+		goto codec_err;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0)
+		goto pcm_err;
+
+	/* do a cold reset for the controller and then try
+	 * a warm reset followed by an optional cold reset for codec */
+	stac9766_reset(codec, 0);
+	ret = stac9766_reset(codec, 1);
+	if (ret < 0) {
+		printk(KERN_ERR "Failed to reset STAC9766: AC97 link error\n");
+		goto reset_err;
+	}
+
+	stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	snd_soc_add_controls(codec, stac9766_snd_ac97_controls,
+			     ARRAY_SIZE(stac9766_snd_ac97_controls));
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0)
+		goto reset_err;
+	return 0;
+
+reset_err:
+	snd_soc_free_pcms(socdev);
+pcm_err:
+	snd_soc_free_ac97_codec(codec);
+codec_err:
+	kfree(codec->private_data);
+cache_err:
+	kfree(socdev->card->codec);
+	socdev->card->codec = NULL;
+	return ret;
+}
+
+static int stac9766_codec_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	if (codec == NULL)
+		return 0;
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_free_ac97_codec(codec);
+	kfree(codec->reg_cache);
+	kfree(codec);
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_stac9766 = {
+	.probe = stac9766_codec_probe,
+	.remove = stac9766_codec_remove,
+	.suspend = stac9766_codec_suspend,
+	.resume = stac9766_codec_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_stac9766);
+
+MODULE_DESCRIPTION("ASoC stac9766 driver");
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/stac9766.h b/sound/soc/codecs/stac9766.h
new file mode 100644
index 0000000..65642eb
--- /dev/null
+++ b/sound/soc/codecs/stac9766.h
@@ -0,0 +1,21 @@
+/*
+ * stac9766.h  --  STAC9766 Soc Audio driver
+ */
+
+#ifndef _STAC9766_H
+#define _STAC9766_H
+
+#define AC97_STAC_PAGE0 0x1000
+#define AC97_STAC_DA_CONTROL (AC97_STAC_PAGE0 | 0x6A)
+#define AC97_STAC_ANALOG_SPECIAL (AC97_STAC_PAGE0 | 0x6E)
+#define AC97_STAC_STEREO_MIC 0x78
+
+/* STAC9766 DAI ID's */
+#define STAC9766_DAI_AC97_ANALOG		0
+#define STAC9766_DAI_AC97_DIGITAL		1
+
+extern struct snd_soc_dai stac9766_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_stac9766;
+
+
+#endif
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index c3f4afb..0b8dcb5 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -86,7 +86,7 @@
 	 */
 
 	if ((reg < 0 || reg > 9) && (reg != 15)) {
-		printk(KERN_WARNING "%s Invalid register R%d\n", __func__, reg);
+		printk(KERN_WARNING "%s Invalid register R%u\n", __func__, reg);
 		return -1;
 	}
 
@@ -98,7 +98,7 @@
 	if (codec->hw_write(codec->control_data, data, 2) == 2)
 		return 0;
 
-	printk(KERN_ERR "%s cannot write %03x to register R%d\n", __func__,
+	printk(KERN_ERR "%s cannot write %03x to register R%u\n", __func__,
 	       value, reg);
 
 	return -EIO;
@@ -273,14 +273,14 @@
  * Every divisor is a factor of 11*12
  */
 #define SR_MULT (11*12)
-#define A(x) (x) ? (SR_MULT/x) : 0
+#define A(x) (SR_MULT/x)
 static const unsigned char sr_adc_mult_table[] = {
-	A(2), A(2), A(12), A(12),  A(0), A(0), A(3), A(1),
-	A(2), A(2), A(11), A(11),  A(0), A(0), A(0), A(1)
+	A(2), A(2), A(12), A(12),  0, 0, A(3), A(1),
+	A(2), A(2), A(11), A(11),  0, 0, 0, A(1)
 };
 static const unsigned char sr_dac_mult_table[] = {
-	A(2), A(12), A(2), A(12),  A(0), A(0), A(3), A(1),
-	A(2), A(11), A(2), A(11),  A(0), A(0), A(0), A(1)
+	A(2), A(12), A(2), A(12),  0, 0, A(3), A(1),
+	A(2), A(11), A(2), A(11),  0, 0, 0, A(1)
 };
 
 static unsigned get_score(int adc, int adc_l, int adc_h, int need_adc,
@@ -523,6 +523,8 @@
 	case SND_SOC_DAIFMT_I2S:
 		iface_reg |= TLV320AIC23_FOR_I2S;
 		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface_reg |= TLV320AIC23_LRP_ON;
 	case SND_SOC_DAIFMT_DSP_B:
 		iface_reg |= TLV320AIC23_FOR_DSP;
 		break;
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index ab099f4..126b15b 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -145,8 +145,8 @@
 		      u8 *value)
 {
 	*value = reg & 0xff;
-	if (codec->hw_read(codec->control_data, value, 1) != 1)
-		return -EIO;
+
+	value[0] = i2c_smbus_read_byte_data(codec->control_data, value[0]);
 
 	aic3x_write_reg_cache(codec, reg, *value);
 	return 0;
@@ -767,6 +767,7 @@
 	int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
 	u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
 	u16 pll_d = 1;
+	u8 reg;
 
 	/* select data word length */
 	data =
@@ -801,8 +802,16 @@
 		pll_q &= 0xf;
 		aic3x_write(codec, AIC3X_PLL_PROGA_REG, pll_q << PLLQ_SHIFT);
 		aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_CLKDIV);
-	} else
+		/* disable PLL if it is bypassed */
+		reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
+		aic3x_write(codec, AIC3X_PLL_PROGA_REG, reg & ~PLL_ENABLE);
+
+	} else {
 		aic3x_write(codec, AIC3X_GPIOB_REG, CODEC_CLKIN_PLLDIV);
+		/* enable PLL when it is used */
+		reg = aic3x_read_reg_cache(codec, AIC3X_PLL_PROGA_REG);
+		aic3x_write(codec, AIC3X_PLL_PROGA_REG, reg | PLL_ENABLE);
+	}
 
 	/* Route Left DAC to left channel input and
 	 * right DAC to right channel input */
@@ -1316,12 +1325,6 @@
 	.id_table = aic3x_i2c_id,
 };
 
-static int aic3x_i2c_read(struct i2c_client *client, u8 *value, int len)
-{
-	value[0] = i2c_smbus_read_byte_data(client, value[0]);
-	return (len == 1);
-}
-
 static int aic3x_add_i2c_device(struct platform_device *pdev,
 				 const struct aic3x_setup_data *setup)
 {
@@ -1394,7 +1397,6 @@
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
 		codec->hw_write = (hw_write_t) i2c_master_send;
-		codec->hw_read = (hw_read_t) aic3x_i2c_read;
 		ret = aic3x_add_i2c_device(pdev, setup);
 	}
 #else
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index df7c8c2..4df7c6c 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -115,6 +115,7 @@
 	0x00, /* REG_VIBRA_PWM_SET	(0x47)	*/
 	0x00, /* REG_ANAMIC_GAIN	(0x48)	*/
 	0x00, /* REG_MISC_SET_2		(0x49)	*/
+	0x00, /* REG_SW_SHADOW		(0x4A)	- Shadow, non HW register */
 };
 
 /* codec private data */
@@ -125,6 +126,17 @@
 
 	struct snd_pcm_substream *master_substream;
 	struct snd_pcm_substream *slave_substream;
+
+	unsigned int configured;
+	unsigned int rate;
+	unsigned int sample_bits;
+	unsigned int channels;
+
+	unsigned int sysclk;
+
+	/* Headset output state handling */
+	unsigned int hsl_enabled;
+	unsigned int hsr_enabled;
 };
 
 /*
@@ -161,7 +173,11 @@
 			unsigned int reg, unsigned int value)
 {
 	twl4030_write_reg_cache(codec, reg, value);
-	return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
+	if (likely(reg < TWL4030_REG_SW_SHADOW))
+		return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value,
+					    reg);
+	else
+		return 0;
 }
 
 static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
@@ -188,6 +204,7 @@
 
 static void twl4030_init_chip(struct snd_soc_codec *codec)
 {
+	u8 *cache = codec->reg_cache;
 	int i;
 
 	/* clear CODECPDZ prior to setting register defaults */
@@ -195,7 +212,7 @@
 
 	/* set all audio section registers to reasonable defaults */
 	for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
-		twl4030_write(codec, i,	twl4030_reg[i]);
+		twl4030_write(codec, i,	cache[i]);
 
 }
 
@@ -208,55 +225,11 @@
 		return;
 
 	if (mute) {
-		/* Bypass the reg_cache and mute the volumes
-		 * Headset mute is done in it's own event handler
-		 * Things to mute:  Earpiece, PreDrivL/R, CarkitL/R
-		 */
-		reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL);
-		twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
-					reg_val & (~TWL4030_EAR_GAIN),
-					TWL4030_REG_EAR_CTL);
-
-		reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL);
-		twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
-					reg_val & (~TWL4030_PREDL_GAIN),
-					TWL4030_REG_PREDL_CTL);
-		reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL);
-		twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
-					reg_val & (~TWL4030_PREDR_GAIN),
-					TWL4030_REG_PREDL_CTL);
-
-		reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL);
-		twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
-					reg_val & (~TWL4030_PRECKL_GAIN),
-					TWL4030_REG_PRECKL_CTL);
-		reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL);
-		twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
-					reg_val & (~TWL4030_PRECKL_GAIN),
-					TWL4030_REG_PRECKR_CTL);
-
 		/* Disable PLL */
 		reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
 		reg_val &= ~TWL4030_APLL_EN;
 		twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
 	} else {
-		/* Restore the volumes
-		 * Headset mute is done in it's own event handler
-		 * Things to restore:  Earpiece, PreDrivL/R, CarkitL/R
-		 */
-		twl4030_write(codec, TWL4030_REG_EAR_CTL,
-			twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL));
-
-		twl4030_write(codec, TWL4030_REG_PREDL_CTL,
-			twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL));
-		twl4030_write(codec, TWL4030_REG_PREDR_CTL,
-			twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL));
-
-		twl4030_write(codec, TWL4030_REG_PRECKL_CTL,
-			twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL));
-		twl4030_write(codec, TWL4030_REG_PRECKR_CTL,
-			twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL));
-
 		/* Enable PLL */
 		reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
 		reg_val |= TWL4030_APLL_EN;
@@ -316,104 +289,60 @@
 }
 
 /* Earpiece */
-static const char *twl4030_earpiece_texts[] =
-		{"Off", "DACL1", "DACL2", "DACR1"};
-
-static const unsigned int twl4030_earpiece_values[] =
-		{0x0, 0x1, 0x2, 0x4};
-
-static const struct soc_enum twl4030_earpiece_enum =
-	SOC_VALUE_ENUM_SINGLE(TWL4030_REG_EAR_CTL, 1, 0x7,
-			ARRAY_SIZE(twl4030_earpiece_texts),
-			twl4030_earpiece_texts,
-			twl4030_earpiece_values);
-
-static const struct snd_kcontrol_new twl4030_dapm_earpiece_control =
-SOC_DAPM_VALUE_ENUM("Route", twl4030_earpiece_enum);
+static const struct snd_kcontrol_new twl4030_dapm_earpiece_controls[] = {
+	SOC_DAPM_SINGLE("Voice", TWL4030_REG_EAR_CTL, 0, 1, 0),
+	SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_EAR_CTL, 1, 1, 0),
+	SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_EAR_CTL, 2, 1, 0),
+	SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_EAR_CTL, 3, 1, 0),
+};
 
 /* PreDrive Left */
-static const char *twl4030_predrivel_texts[] =
-		{"Off", "DACL1", "DACL2", "DACR2"};
-
-static const unsigned int twl4030_predrivel_values[] =
-		{0x0, 0x1, 0x2, 0x4};
-
-static const struct soc_enum twl4030_predrivel_enum =
-	SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDL_CTL, 1, 0x7,
-			ARRAY_SIZE(twl4030_predrivel_texts),
-			twl4030_predrivel_texts,
-			twl4030_predrivel_values);
-
-static const struct snd_kcontrol_new twl4030_dapm_predrivel_control =
-SOC_DAPM_VALUE_ENUM("Route", twl4030_predrivel_enum);
+static const struct snd_kcontrol_new twl4030_dapm_predrivel_controls[] = {
+	SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDL_CTL, 0, 1, 0),
+	SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PREDL_CTL, 1, 1, 0),
+	SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDL_CTL, 2, 1, 0),
+	SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDL_CTL, 3, 1, 0),
+};
 
 /* PreDrive Right */
-static const char *twl4030_predriver_texts[] =
-		{"Off", "DACR1", "DACR2", "DACL2"};
-
-static const unsigned int twl4030_predriver_values[] =
-		{0x0, 0x1, 0x2, 0x4};
-
-static const struct soc_enum twl4030_predriver_enum =
-	SOC_VALUE_ENUM_SINGLE(TWL4030_REG_PREDR_CTL, 1, 0x7,
-			ARRAY_SIZE(twl4030_predriver_texts),
-			twl4030_predriver_texts,
-			twl4030_predriver_values);
-
-static const struct snd_kcontrol_new twl4030_dapm_predriver_control =
-SOC_DAPM_VALUE_ENUM("Route", twl4030_predriver_enum);
+static const struct snd_kcontrol_new twl4030_dapm_predriver_controls[] = {
+	SOC_DAPM_SINGLE("Voice", TWL4030_REG_PREDR_CTL, 0, 1, 0),
+	SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PREDR_CTL, 1, 1, 0),
+	SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PREDR_CTL, 2, 1, 0),
+	SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PREDR_CTL, 3, 1, 0),
+};
 
 /* Headset Left */
-static const char *twl4030_hsol_texts[] =
-		{"Off", "DACL1", "DACL2"};
-
-static const struct soc_enum twl4030_hsol_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 1,
-			ARRAY_SIZE(twl4030_hsol_texts),
-			twl4030_hsol_texts);
-
-static const struct snd_kcontrol_new twl4030_dapm_hsol_control =
-SOC_DAPM_ENUM("Route", twl4030_hsol_enum);
+static const struct snd_kcontrol_new twl4030_dapm_hsol_controls[] = {
+	SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 0, 1, 0),
+	SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_HS_SEL, 1, 1, 0),
+	SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_HS_SEL, 2, 1, 0),
+};
 
 /* Headset Right */
-static const char *twl4030_hsor_texts[] =
-		{"Off", "DACR1", "DACR2"};
-
-static const struct soc_enum twl4030_hsor_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_HS_SEL, 4,
-			ARRAY_SIZE(twl4030_hsor_texts),
-			twl4030_hsor_texts);
-
-static const struct snd_kcontrol_new twl4030_dapm_hsor_control =
-SOC_DAPM_ENUM("Route", twl4030_hsor_enum);
+static const struct snd_kcontrol_new twl4030_dapm_hsor_controls[] = {
+	SOC_DAPM_SINGLE("Voice", TWL4030_REG_HS_SEL, 3, 1, 0),
+	SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_HS_SEL, 4, 1, 0),
+	SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_HS_SEL, 5, 1, 0),
+};
 
 /* Carkit Left */
-static const char *twl4030_carkitl_texts[] =
-		{"Off", "DACL1", "DACL2"};
-
-static const struct soc_enum twl4030_carkitl_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_PRECKL_CTL, 1,
-			ARRAY_SIZE(twl4030_carkitl_texts),
-			twl4030_carkitl_texts);
-
-static const struct snd_kcontrol_new twl4030_dapm_carkitl_control =
-SOC_DAPM_ENUM("Route", twl4030_carkitl_enum);
+static const struct snd_kcontrol_new twl4030_dapm_carkitl_controls[] = {
+	SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKL_CTL, 0, 1, 0),
+	SOC_DAPM_SINGLE("AudioL1", TWL4030_REG_PRECKL_CTL, 1, 1, 0),
+	SOC_DAPM_SINGLE("AudioL2", TWL4030_REG_PRECKL_CTL, 2, 1, 0),
+};
 
 /* Carkit Right */
-static const char *twl4030_carkitr_texts[] =
-		{"Off", "DACR1", "DACR2"};
-
-static const struct soc_enum twl4030_carkitr_enum =
-	SOC_ENUM_SINGLE(TWL4030_REG_PRECKR_CTL, 1,
-			ARRAY_SIZE(twl4030_carkitr_texts),
-			twl4030_carkitr_texts);
-
-static const struct snd_kcontrol_new twl4030_dapm_carkitr_control =
-SOC_DAPM_ENUM("Route", twl4030_carkitr_enum);
+static const struct snd_kcontrol_new twl4030_dapm_carkitr_controls[] = {
+	SOC_DAPM_SINGLE("Voice", TWL4030_REG_PRECKR_CTL, 0, 1, 0),
+	SOC_DAPM_SINGLE("AudioR1", TWL4030_REG_PRECKR_CTL, 1, 1, 0),
+	SOC_DAPM_SINGLE("AudioR2", TWL4030_REG_PRECKR_CTL, 2, 1, 0),
+};
 
 /* Handsfree Left */
 static const char *twl4030_handsfreel_texts[] =
-		{"Voice", "DACL1", "DACL2", "DACR2"};
+		{"Voice", "AudioL1", "AudioL2", "AudioR2"};
 
 static const struct soc_enum twl4030_handsfreel_enum =
 	SOC_ENUM_SINGLE(TWL4030_REG_HFL_CTL, 0,
@@ -423,9 +352,13 @@
 static const struct snd_kcontrol_new twl4030_dapm_handsfreel_control =
 SOC_DAPM_ENUM("Route", twl4030_handsfreel_enum);
 
+/* Handsfree Left virtual mute */
+static const struct snd_kcontrol_new twl4030_dapm_handsfreelmute_control =
+	SOC_DAPM_SINGLE("Switch", TWL4030_REG_SW_SHADOW, 0, 1, 0);
+
 /* Handsfree Right */
 static const char *twl4030_handsfreer_texts[] =
-		{"Voice", "DACR1", "DACR2", "DACL2"};
+		{"Voice", "AudioR1", "AudioR2", "AudioL2"};
 
 static const struct soc_enum twl4030_handsfreer_enum =
 	SOC_ENUM_SINGLE(TWL4030_REG_HFR_CTL, 0,
@@ -435,37 +368,52 @@
 static const struct snd_kcontrol_new twl4030_dapm_handsfreer_control =
 SOC_DAPM_ENUM("Route", twl4030_handsfreer_enum);
 
+/* Handsfree Right virtual mute */
+static const struct snd_kcontrol_new twl4030_dapm_handsfreermute_control =
+	SOC_DAPM_SINGLE("Switch", TWL4030_REG_SW_SHADOW, 1, 1, 0);
+
+/* Vibra */
+/* Vibra audio path selection */
+static const char *twl4030_vibra_texts[] =
+		{"AudioL1", "AudioR1", "AudioL2", "AudioR2"};
+
+static const struct soc_enum twl4030_vibra_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 2,
+			ARRAY_SIZE(twl4030_vibra_texts),
+			twl4030_vibra_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_vibra_control =
+SOC_DAPM_ENUM("Route", twl4030_vibra_enum);
+
+/* Vibra path selection: local vibrator (PWM) or audio driven */
+static const char *twl4030_vibrapath_texts[] =
+		{"Local vibrator", "Audio"};
+
+static const struct soc_enum twl4030_vibrapath_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 4,
+			ARRAY_SIZE(twl4030_vibrapath_texts),
+			twl4030_vibrapath_texts);
+
+static const struct snd_kcontrol_new twl4030_dapm_vibrapath_control =
+SOC_DAPM_ENUM("Route", twl4030_vibrapath_enum);
+
 /* Left analog microphone selection */
-static const char *twl4030_analoglmic_texts[] =
-		{"Off", "Main mic", "Headset mic", "AUXL", "Carkit mic"};
-
-static const unsigned int twl4030_analoglmic_values[] =
-		{0x0, 0x1, 0x2, 0x4, 0x8};
-
-static const struct soc_enum twl4030_analoglmic_enum =
-	SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICL, 0, 0xf,
-			ARRAY_SIZE(twl4030_analoglmic_texts),
-			twl4030_analoglmic_texts,
-			twl4030_analoglmic_values);
-
-static const struct snd_kcontrol_new twl4030_dapm_analoglmic_control =
-SOC_DAPM_VALUE_ENUM("Route", twl4030_analoglmic_enum);
+static const struct snd_kcontrol_new twl4030_dapm_analoglmic_controls[] = {
+	SOC_DAPM_SINGLE("Main Mic Capture Switch",
+			TWL4030_REG_ANAMICL, 0, 1, 0),
+	SOC_DAPM_SINGLE("Headset Mic Capture Switch",
+			TWL4030_REG_ANAMICL, 1, 1, 0),
+	SOC_DAPM_SINGLE("AUXL Capture Switch",
+			TWL4030_REG_ANAMICL, 2, 1, 0),
+	SOC_DAPM_SINGLE("Carkit Mic Capture Switch",
+			TWL4030_REG_ANAMICL, 3, 1, 0),
+};
 
 /* Right analog microphone selection */
-static const char *twl4030_analogrmic_texts[] =
-		{"Off", "Sub mic", "AUXR"};
-
-static const unsigned int twl4030_analogrmic_values[] =
-		{0x0, 0x1, 0x4};
-
-static const struct soc_enum twl4030_analogrmic_enum =
-	SOC_VALUE_ENUM_SINGLE(TWL4030_REG_ANAMICR, 0, 0x5,
-			ARRAY_SIZE(twl4030_analogrmic_texts),
-			twl4030_analogrmic_texts,
-			twl4030_analogrmic_values);
-
-static const struct snd_kcontrol_new twl4030_dapm_analogrmic_control =
-SOC_DAPM_VALUE_ENUM("Route", twl4030_analogrmic_enum);
+static const struct snd_kcontrol_new twl4030_dapm_analogrmic_controls[] = {
+	SOC_DAPM_SINGLE("Sub Mic Capture Switch", TWL4030_REG_ANAMICR, 0, 1, 0),
+	SOC_DAPM_SINGLE("AUXR Capture Switch", TWL4030_REG_ANAMICR, 2, 1, 0),
+};
 
 /* TX1 L/R Analog/Digital microphone selection */
 static const char *twl4030_micpathtx1_texts[] =
@@ -507,6 +455,10 @@
 static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control =
 	SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0);
 
+/* Analog bypass for Voice */
+static const struct snd_kcontrol_new twl4030_dapm_abypassv_control =
+	SOC_DAPM_SINGLE("Switch", TWL4030_REG_VDL_APGA_CTL, 2, 1, 0);
+
 /* Digital bypass gain, 0 mutes the bypass */
 static const unsigned int twl4030_dapm_dbypass_tlv[] = {
 	TLV_DB_RANGE_HEAD(2),
@@ -526,6 +478,18 @@
 			TWL4030_REG_ATX2ARXPGA, 0, 7, 0,
 			twl4030_dapm_dbypass_tlv);
 
+/*
+ * Voice Sidetone GAIN volume control:
+ * from -51 to -10 dB in 1 dB steps (mute instead of -51 dB)
+ */
+static DECLARE_TLV_DB_SCALE(twl4030_dapm_dbypassv_tlv, -5100, 100, 1);
+
+/* Digital bypass voice: sidetone (VUL -> VDL)*/
+static const struct snd_kcontrol_new twl4030_dapm_dbypassv_control =
+	SOC_DAPM_SINGLE_TLV("Volume",
+			TWL4030_REG_VSTPGA, 0, 0x29, 0,
+			twl4030_dapm_dbypassv_tlv);
+
 static int micpath_event(struct snd_soc_dapm_widget *w,
 	struct snd_kcontrol *kcontrol, int event)
 {
@@ -556,63 +520,205 @@
 	return 0;
 }
 
-static int handsfree_event(struct snd_soc_dapm_widget *w,
-		struct snd_kcontrol *kcontrol, int event)
+/*
+ * Output PGA builder:
+ * Handle the muting and unmuting of the given output (turning off the
+ * amplifier associated with the output pin)
+ * On mute bypass the reg_cache and mute the volume
+ * On unmute: restore the register content
+ * Outputs handled in this way:  Earpiece, PreDrivL/R, CarkitL/R
+ */
+#define TWL4030_OUTPUT_PGA(pin_name, reg, mask)				\
+static int pin_name##pga_event(struct snd_soc_dapm_widget *w,		\
+		struct snd_kcontrol *kcontrol, int event)		\
+{									\
+	u8 reg_val;							\
+									\
+	switch (event) {						\
+	case SND_SOC_DAPM_POST_PMU:					\
+		twl4030_write(w->codec, reg,				\
+			twl4030_read_reg_cache(w->codec, reg));		\
+		break;							\
+	case SND_SOC_DAPM_POST_PMD:					\
+		reg_val = twl4030_read_reg_cache(w->codec, reg);	\
+		twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,	\
+					reg_val & (~mask),		\
+					reg);				\
+		break;							\
+	}								\
+	return 0;							\
+}
+
+TWL4030_OUTPUT_PGA(earpiece, TWL4030_REG_EAR_CTL, TWL4030_EAR_GAIN);
+TWL4030_OUTPUT_PGA(predrivel, TWL4030_REG_PREDL_CTL, TWL4030_PREDL_GAIN);
+TWL4030_OUTPUT_PGA(predriver, TWL4030_REG_PREDR_CTL, TWL4030_PREDR_GAIN);
+TWL4030_OUTPUT_PGA(carkitl, TWL4030_REG_PRECKL_CTL, TWL4030_PRECKL_GAIN);
+TWL4030_OUTPUT_PGA(carkitr, TWL4030_REG_PRECKR_CTL, TWL4030_PRECKR_GAIN);
+
+static void handsfree_ramp(struct snd_soc_codec *codec, int reg, int ramp)
 {
-	struct soc_enum *e = (struct soc_enum *)w->kcontrols->private_value;
 	unsigned char hs_ctl;
 
-	hs_ctl = twl4030_read_reg_cache(w->codec, e->reg);
+	hs_ctl = twl4030_read_reg_cache(codec, reg);
 
-	if (hs_ctl & TWL4030_HF_CTL_REF_EN) {
+	if (ramp) {
+		/* HF ramp-up */
+		hs_ctl |= TWL4030_HF_CTL_REF_EN;
+		twl4030_write(codec, reg, hs_ctl);
+		udelay(10);
 		hs_ctl |= TWL4030_HF_CTL_RAMP_EN;
-		twl4030_write(w->codec, e->reg, hs_ctl);
+		twl4030_write(codec, reg, hs_ctl);
+		udelay(40);
 		hs_ctl |= TWL4030_HF_CTL_LOOP_EN;
-		twl4030_write(w->codec, e->reg, hs_ctl);
 		hs_ctl |= TWL4030_HF_CTL_HB_EN;
-		twl4030_write(w->codec, e->reg, hs_ctl);
+		twl4030_write(codec, reg, hs_ctl);
 	} else {
-		hs_ctl &= ~(TWL4030_HF_CTL_RAMP_EN | TWL4030_HF_CTL_LOOP_EN
-				| TWL4030_HF_CTL_HB_EN);
-		twl4030_write(w->codec, e->reg, hs_ctl);
+		/* HF ramp-down */
+		hs_ctl &= ~TWL4030_HF_CTL_LOOP_EN;
+		hs_ctl &= ~TWL4030_HF_CTL_HB_EN;
+		twl4030_write(codec, reg, hs_ctl);
+		hs_ctl &= ~TWL4030_HF_CTL_RAMP_EN;
+		twl4030_write(codec, reg, hs_ctl);
+		udelay(40);
+		hs_ctl &= ~TWL4030_HF_CTL_REF_EN;
+		twl4030_write(codec, reg, hs_ctl);
 	}
+}
 
+static int handsfreelpga_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		handsfree_ramp(w->codec, TWL4030_REG_HFL_CTL, 1);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		handsfree_ramp(w->codec, TWL4030_REG_HFL_CTL, 0);
+		break;
+	}
 	return 0;
 }
 
-static int headsetl_event(struct snd_soc_dapm_widget *w,
+static int handsfreerpga_event(struct snd_soc_dapm_widget *w,
 		struct snd_kcontrol *kcontrol, int event)
 {
-	unsigned char hs_gain, hs_pop;
-
-	/* Save the current volume */
-	hs_gain = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_GAIN_SET);
-	hs_pop = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_POPN_SET);
-
 	switch (event) {
 	case SND_SOC_DAPM_POST_PMU:
-		/* Do the anti-pop/bias ramp enable according to the TRM */
-		hs_pop |= TWL4030_VMID_EN;
-		twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
-		/* Is this needed? Can we just use whatever gain here? */
-		twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET,
-				(hs_gain & (~0x0f)) | 0x0a);
-		hs_pop |= TWL4030_RAMP_EN;
-		twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
-
-		/* Restore the original volume */
-		twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET, hs_gain);
+		handsfree_ramp(w->codec, TWL4030_REG_HFR_CTL, 1);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
-		/* Do the anti-pop/bias ramp disable according to the TRM */
+		handsfree_ramp(w->codec, TWL4030_REG_HFR_CTL, 0);
+		break;
+	}
+	return 0;
+}
+
+static void headset_ramp(struct snd_soc_codec *codec, int ramp)
+{
+	struct snd_soc_device *socdev = codec->socdev;
+	struct twl4030_setup_data *setup = socdev->codec_data;
+
+	unsigned char hs_gain, hs_pop;
+	struct twl4030_priv *twl4030 = codec->private_data;
+	/* Base values for ramp delay calculation: 2^19 - 2^26 */
+	unsigned int ramp_base[] = {524288, 1048576, 2097152, 4194304,
+				    8388608, 16777216, 33554432, 67108864};
+
+	hs_gain = twl4030_read_reg_cache(codec, TWL4030_REG_HS_GAIN_SET);
+	hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
+
+	/* Enable external mute control, this dramatically reduces
+	 * the pop-noise */
+	if (setup && setup->hs_extmute) {
+		if (setup->set_hs_extmute) {
+			setup->set_hs_extmute(1);
+		} else {
+			hs_pop |= TWL4030_EXTMUTE;
+			twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+		}
+	}
+
+	if (ramp) {
+		/* Headset ramp-up according to the TRM */
+		hs_pop |= TWL4030_VMID_EN;
+		twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+		twl4030_write(codec, TWL4030_REG_HS_GAIN_SET, hs_gain);
+		hs_pop |= TWL4030_RAMP_EN;
+		twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+		/* Wait ramp delay time + 1, so the VMID can settle */
+		mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
+			twl4030->sysclk) + 1);
+	} else {
+		/* Headset ramp-down _not_ according to
+		 * the TRM, but in a way that it is working */
 		hs_pop &= ~TWL4030_RAMP_EN;
-		twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+		twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+		/* Wait ramp delay time + 1, so the VMID can settle */
+		mdelay((ramp_base[(hs_pop & TWL4030_RAMP_DELAY) >> 2] /
+			twl4030->sysclk) + 1);
 		/* Bypass the reg_cache to mute the headset */
 		twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
 					hs_gain & (~0x0f),
 					TWL4030_REG_HS_GAIN_SET);
+
 		hs_pop &= ~TWL4030_VMID_EN;
-		twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+		twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+	}
+
+	/* Disable external mute */
+	if (setup && setup->hs_extmute) {
+		if (setup->set_hs_extmute) {
+			setup->set_hs_extmute(0);
+		} else {
+			hs_pop &= ~TWL4030_EXTMUTE;
+			twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+		}
+	}
+}
+
+static int headsetlpga_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct twl4030_priv *twl4030 = w->codec->private_data;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		/* Do the ramp-up only once */
+		if (!twl4030->hsr_enabled)
+			headset_ramp(w->codec, 1);
+
+		twl4030->hsl_enabled = 1;
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* Do the ramp-down only if both headsetL/R is disabled */
+		if (!twl4030->hsr_enabled)
+			headset_ramp(w->codec, 0);
+
+		twl4030->hsl_enabled = 0;
+		break;
+	}
+	return 0;
+}
+
+static int headsetrpga_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct twl4030_priv *twl4030 = w->codec->private_data;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		/* Do the ramp-up only once */
+		if (!twl4030->hsl_enabled)
+			headset_ramp(w->codec, 1);
+
+		twl4030->hsr_enabled = 1;
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* Do the ramp-down only if both headsetL/R is disabled */
+		if (!twl4030->hsl_enabled)
+			headset_ramp(w->codec, 0);
+
+		twl4030->hsr_enabled = 0;
 		break;
 	}
 	return 0;
@@ -624,11 +730,23 @@
 	struct soc_mixer_control *m =
 		(struct soc_mixer_control *)w->kcontrols->private_value;
 	struct twl4030_priv *twl4030 = w->codec->private_data;
-	unsigned char reg;
+	unsigned char reg, misc;
 
 	reg = twl4030_read_reg_cache(w->codec, m->reg);
 
-	if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
+	/*
+	 * bypass_state[0:3] - analog HiFi bypass
+	 * bypass_state[4]   - analog voice bypass
+	 * bypass_state[5]   - digital voice bypass
+	 * bypass_state[6:7] - digital HiFi bypass
+	 */
+	if (m->reg == TWL4030_REG_VSTPGA) {
+		/* Voice digital bypass */
+		if (reg)
+			twl4030->bypass_state |= (1 << 5);
+		else
+			twl4030->bypass_state &= ~(1 << 5);
+	} else if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
 		/* Analog bypass */
 		if (reg & (1 << m->shift))
 			twl4030->bypass_state |=
@@ -636,14 +754,28 @@
 		else
 			twl4030->bypass_state &=
 				~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
+	} else if (m->reg == TWL4030_REG_VDL_APGA_CTL) {
+		/* Analog voice bypass */
+		if (reg & (1 << m->shift))
+			twl4030->bypass_state |= (1 << 4);
+		else
+			twl4030->bypass_state &= ~(1 << 4);
 	} else {
 		/* Digital bypass */
 		if (reg & (0x7 << m->shift))
-			twl4030->bypass_state |= (1 << (m->shift ? 5 : 4));
+			twl4030->bypass_state |= (1 << (m->shift ? 7 : 6));
 		else
-			twl4030->bypass_state &= ~(1 << (m->shift ? 5 : 4));
+			twl4030->bypass_state &= ~(1 << (m->shift ? 7 : 6));
 	}
 
+	/* Enable master analog loopback mode if any analog switch is enabled*/
+	misc = twl4030_read_reg_cache(w->codec, TWL4030_REG_MISC_SET_1);
+	if (twl4030->bypass_state & 0x1F)
+		misc |= TWL4030_FMLOOP_EN;
+	else
+		misc &= ~TWL4030_FMLOOP_EN;
+	twl4030_write(w->codec, TWL4030_REG_MISC_SET_1, misc);
+
 	if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) {
 		if (twl4030->bypass_state)
 			twl4030_codec_mute(w->codec, 0);
@@ -810,6 +942,48 @@
 	return err;
 }
 
+/* Codec operation modes */
+static const char *twl4030_op_modes_texts[] = {
+	"Option 2 (voice/audio)", "Option 1 (audio)"
+};
+
+static const struct soc_enum twl4030_op_modes_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_CODEC_MODE, 0,
+			ARRAY_SIZE(twl4030_op_modes_texts),
+			twl4030_op_modes_texts);
+
+static int snd_soc_put_twl4030_opmode_enum_double(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct twl4030_priv *twl4030 = codec->private_data;
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned short val;
+	unsigned short mask, bitmask;
+
+	if (twl4030->configured) {
+		printk(KERN_ERR "twl4030 operation mode cannot be "
+			"changed on-the-fly\n");
+		return -EBUSY;
+	}
+
+	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
+		;
+	if (ucontrol->value.enumerated.item[0] > e->max - 1)
+		return -EINVAL;
+
+	val = ucontrol->value.enumerated.item[0] << e->shift_l;
+	mask = (bitmask - 1) << e->shift_l;
+	if (e->shift_l != e->shift_r) {
+		if (ucontrol->value.enumerated.item[1] > e->max - 1)
+			return -EINVAL;
+		val |= ucontrol->value.enumerated.item[1] << e->shift_r;
+		mask |= (bitmask - 1) << e->shift_r;
+	}
+
+	return snd_soc_update_bits(codec, e->reg, mask, val);
+}
+
 /*
  * FGAIN volume control:
  * from -62 to 0 dB in 1 dB steps (mute instead of -63 dB)
@@ -824,6 +998,12 @@
 static DECLARE_TLV_DB_SCALE(digital_coarse_tlv, 0, 600, 0);
 
 /*
+ * Voice Downlink GAIN volume control:
+ * from -37 to 12 dB in 1 dB steps (mute instead of -37 dB)
+ */
+static DECLARE_TLV_DB_SCALE(digital_voice_downlink_tlv, -3700, 100, 1);
+
+/*
  * Analog playback gain
  * -24 dB to 12 dB in 2 dB steps
  */
@@ -853,6 +1033,16 @@
  */
 static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
 
+/* AVADC clock priority */
+static const char *twl4030_avadc_clk_priority_texts[] = {
+	"Voice high priority", "HiFi high priority"
+};
+
+static const struct soc_enum twl4030_avadc_clk_priority_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_AVADC_CTL, 2,
+			ARRAY_SIZE(twl4030_avadc_clk_priority_texts),
+			twl4030_avadc_clk_priority_texts);
+
 static const char *twl4030_rampdelay_texts[] = {
 	"27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms",
 	"437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms",
@@ -864,7 +1054,32 @@
 			ARRAY_SIZE(twl4030_rampdelay_texts),
 			twl4030_rampdelay_texts);
 
+/* Vibra H-bridge direction mode */
+static const char *twl4030_vibradirmode_texts[] = {
+	"Vibra H-bridge direction", "Audio data MSB",
+};
+
+static const struct soc_enum twl4030_vibradirmode_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 5,
+			ARRAY_SIZE(twl4030_vibradirmode_texts),
+			twl4030_vibradirmode_texts);
+
+/* Vibra H-bridge direction */
+static const char *twl4030_vibradir_texts[] = {
+	"Positive polarity", "Negative polarity",
+};
+
+static const struct soc_enum twl4030_vibradir_enum =
+	SOC_ENUM_SINGLE(TWL4030_REG_VIBRA_CTL, 1,
+			ARRAY_SIZE(twl4030_vibradir_texts),
+			twl4030_vibradir_texts);
+
 static const struct snd_kcontrol_new twl4030_snd_controls[] = {
+	/* Codec operation mode control */
+	SOC_ENUM_EXT("Codec Operation Mode", twl4030_op_modes_enum,
+		snd_soc_get_enum_double,
+		snd_soc_put_twl4030_opmode_enum_double),
+
 	/* Common playback gain controls */
 	SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
 		TWL4030_REG_ARXL1PGA, TWL4030_REG_ARXR1PGA,
@@ -893,6 +1108,16 @@
 		TWL4030_REG_ARXL2_APGA_CTL, TWL4030_REG_ARXR2_APGA_CTL,
 		1, 1, 0),
 
+	/* Common voice downlink gain controls */
+	SOC_SINGLE_TLV("DAC Voice Digital Downlink Volume",
+		TWL4030_REG_VRXPGA, 0, 0x31, 0, digital_voice_downlink_tlv),
+
+	SOC_SINGLE_TLV("DAC Voice Analog Downlink Volume",
+		TWL4030_REG_VDL_APGA_CTL, 3, 0x12, 1, analog_tlv),
+
+	SOC_SINGLE("DAC Voice Analog Downlink Switch",
+		TWL4030_REG_VDL_APGA_CTL, 1, 1, 0),
+
 	/* Separate output gain controls */
 	SOC_DOUBLE_R_TLV_TWL4030("PreDriv Playback Volume",
 		TWL4030_REG_PREDL_CTL, TWL4030_REG_PREDR_CTL,
@@ -919,7 +1144,12 @@
 	SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN,
 		0, 3, 5, 0, input_gain_tlv),
 
+	SOC_ENUM("AVADC Clock Priority", twl4030_avadc_clk_priority_enum),
+
 	SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum),
+
+	SOC_ENUM("Vibra H-bridge mode", twl4030_vibradirmode_enum),
+	SOC_ENUM("Vibra H-bridge direction", twl4030_vibradir_enum),
 };
 
 static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
@@ -947,26 +1177,19 @@
 	SND_SOC_DAPM_OUTPUT("CARKITR"),
 	SND_SOC_DAPM_OUTPUT("HFL"),
 	SND_SOC_DAPM_OUTPUT("HFR"),
+	SND_SOC_DAPM_OUTPUT("VIBRA"),
 
 	/* DACs */
-	SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback",
+	SND_SOC_DAPM_DAC("DAC Right1", "Right Front HiFi Playback",
 			SND_SOC_NOPM, 0, 0),
-	SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback",
+	SND_SOC_DAPM_DAC("DAC Left1", "Left Front HiFi Playback",
 			SND_SOC_NOPM, 0, 0),
-	SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback",
+	SND_SOC_DAPM_DAC("DAC Right2", "Right Rear HiFi Playback",
 			SND_SOC_NOPM, 0, 0),
-	SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback",
+	SND_SOC_DAPM_DAC("DAC Left2", "Left Rear HiFi Playback",
 			SND_SOC_NOPM, 0, 0),
-
-	/* Analog PGAs */
-	SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL,
-			0, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("ARXL1_APGA", TWL4030_REG_ARXL1_APGA_CTL,
-			0, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("ARXR2_APGA", TWL4030_REG_ARXR2_APGA_CTL,
-			0, 0, NULL, 0),
-	SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL,
-			0, 0, NULL, 0),
+	SND_SOC_DAPM_DAC("DAC Voice", "Voice Playback",
+			SND_SOC_NOPM, 0, 0),
 
 	/* Analog bypasses */
 	SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
@@ -981,6 +1204,9 @@
 	SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
 			&twl4030_dapm_abypassl2_control,
 			bypass_event, SND_SOC_DAPM_POST_REG),
+	SND_SOC_DAPM_SWITCH_E("Voice Analog Loopback", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_abypassv_control,
+			bypass_event, SND_SOC_DAPM_POST_REG),
 
 	/* Digital bypasses */
 	SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
@@ -989,43 +1215,103 @@
 	SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
 			&twl4030_dapm_dbypassr_control, bypass_event,
 			SND_SOC_DAPM_POST_REG),
+	SND_SOC_DAPM_SWITCH_E("Voice Digital Loopback", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_dbypassv_control, bypass_event,
+			SND_SOC_DAPM_POST_REG),
 
-	SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", TWL4030_REG_AVDAC_CTL,
-			0, 0, NULL, 0),
-	SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", TWL4030_REG_AVDAC_CTL,
-			1, 0, NULL, 0),
-	SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer", TWL4030_REG_AVDAC_CTL,
-			2, 0, NULL, 0),
-	SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer", TWL4030_REG_AVDAC_CTL,
-			3, 0, NULL, 0),
+	/* Digital mixers, power control for the physical DACs */
+	SND_SOC_DAPM_MIXER("Digital R1 Playback Mixer",
+			TWL4030_REG_AVDAC_CTL, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Digital L1 Playback Mixer",
+			TWL4030_REG_AVDAC_CTL, 1, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Digital R2 Playback Mixer",
+			TWL4030_REG_AVDAC_CTL, 2, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Digital L2 Playback Mixer",
+			TWL4030_REG_AVDAC_CTL, 3, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Digital Voice Playback Mixer",
+			TWL4030_REG_AVDAC_CTL, 4, 0, NULL, 0),
+
+	/* Analog mixers, power control for the physical PGAs */
+	SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer",
+			TWL4030_REG_ARXR1_APGA_CTL, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer",
+			TWL4030_REG_ARXL1_APGA_CTL, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer",
+			TWL4030_REG_ARXR2_APGA_CTL, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer",
+			TWL4030_REG_ARXL2_APGA_CTL, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("Analog Voice Playback Mixer",
+			TWL4030_REG_VDL_APGA_CTL, 0, 0, NULL, 0),
+
+	/* Output MIXER controls */
+	/* Earpiece */
+	SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_earpiece_controls[0],
+			ARRAY_SIZE(twl4030_dapm_earpiece_controls)),
+	SND_SOC_DAPM_PGA_E("Earpiece PGA", SND_SOC_NOPM,
+			0, 0, NULL, 0, earpiecepga_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	/* PreDrivL/R */
+	SND_SOC_DAPM_MIXER("PredriveL Mixer", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_predrivel_controls[0],
+			ARRAY_SIZE(twl4030_dapm_predrivel_controls)),
+	SND_SOC_DAPM_PGA_E("PredriveL PGA", SND_SOC_NOPM,
+			0, 0, NULL, 0, predrivelpga_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("PredriveR Mixer", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_predriver_controls[0],
+			ARRAY_SIZE(twl4030_dapm_predriver_controls)),
+	SND_SOC_DAPM_PGA_E("PredriveR PGA", SND_SOC_NOPM,
+			0, 0, NULL, 0, predriverpga_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	/* HeadsetL/R */
+	SND_SOC_DAPM_MIXER("HeadsetL Mixer", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_hsol_controls[0],
+			ARRAY_SIZE(twl4030_dapm_hsol_controls)),
+	SND_SOC_DAPM_PGA_E("HeadsetL PGA", SND_SOC_NOPM,
+			0, 0, NULL, 0, headsetlpga_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("HeadsetR Mixer", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_hsor_controls[0],
+			ARRAY_SIZE(twl4030_dapm_hsor_controls)),
+	SND_SOC_DAPM_PGA_E("HeadsetR PGA", SND_SOC_NOPM,
+			0, 0, NULL, 0, headsetrpga_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	/* CarkitL/R */
+	SND_SOC_DAPM_MIXER("CarkitL Mixer", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_carkitl_controls[0],
+			ARRAY_SIZE(twl4030_dapm_carkitl_controls)),
+	SND_SOC_DAPM_PGA_E("CarkitL PGA", SND_SOC_NOPM,
+			0, 0, NULL, 0, carkitlpga_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("CarkitR Mixer", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_carkitr_controls[0],
+			ARRAY_SIZE(twl4030_dapm_carkitr_controls)),
+	SND_SOC_DAPM_PGA_E("CarkitR PGA", SND_SOC_NOPM,
+			0, 0, NULL, 0, carkitrpga_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
 
 	/* Output MUX controls */
-	/* Earpiece */
-	SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0,
-		&twl4030_dapm_earpiece_control),
-	/* PreDrivL/R */
-	SND_SOC_DAPM_VALUE_MUX("PredriveL Mux", SND_SOC_NOPM, 0, 0,
-		&twl4030_dapm_predrivel_control),
-	SND_SOC_DAPM_VALUE_MUX("PredriveR Mux", SND_SOC_NOPM, 0, 0,
-		&twl4030_dapm_predriver_control),
-	/* HeadsetL/R */
-	SND_SOC_DAPM_MUX_E("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
-		&twl4030_dapm_hsol_control, headsetl_event,
-		SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0,
-		&twl4030_dapm_hsor_control),
-	/* CarkitL/R */
-	SND_SOC_DAPM_MUX("CarkitL Mux", SND_SOC_NOPM, 0, 0,
-		&twl4030_dapm_carkitl_control),
-	SND_SOC_DAPM_MUX("CarkitR Mux", SND_SOC_NOPM, 0, 0,
-		&twl4030_dapm_carkitr_control),
 	/* HandsfreeL/R */
-	SND_SOC_DAPM_MUX_E("HandsfreeL Mux", TWL4030_REG_HFL_CTL, 5, 0,
-		&twl4030_dapm_handsfreel_control, handsfree_event,
-		SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
-	SND_SOC_DAPM_MUX_E("HandsfreeR Mux", TWL4030_REG_HFR_CTL, 5, 0,
-		&twl4030_dapm_handsfreer_control, handsfree_event,
-		SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX("HandsfreeL Mux", SND_SOC_NOPM, 0, 0,
+		&twl4030_dapm_handsfreel_control),
+	SND_SOC_DAPM_SWITCH("HandsfreeL", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_handsfreelmute_control),
+	SND_SOC_DAPM_PGA_E("HandsfreeL PGA", SND_SOC_NOPM,
+			0, 0, NULL, 0, handsfreelpga_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX("HandsfreeR Mux", SND_SOC_NOPM, 5, 0,
+		&twl4030_dapm_handsfreer_control),
+	SND_SOC_DAPM_SWITCH("HandsfreeR", SND_SOC_NOPM, 0, 0,
+			&twl4030_dapm_handsfreermute_control),
+	SND_SOC_DAPM_PGA_E("HandsfreeR PGA", SND_SOC_NOPM,
+			0, 0, NULL, 0, handsfreerpga_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
+	/* Vibra */
+	SND_SOC_DAPM_MUX("Vibra Mux", TWL4030_REG_VIBRA_CTL, 0, 0,
+		&twl4030_dapm_vibra_control),
+	SND_SOC_DAPM_MUX("Vibra Route", SND_SOC_NOPM, 0, 0,
+		&twl4030_dapm_vibrapath_control),
 
 	/* Introducing four virtual ADC, since TWL4030 have four channel for
 	   capture */
@@ -1050,11 +1336,15 @@
 		SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
 		SND_SOC_DAPM_POST_REG),
 
-	/* Analog input muxes with switch for the capture amplifiers */
-	SND_SOC_DAPM_VALUE_MUX("Analog Left Capture Route",
-		TWL4030_REG_ANAMICL, 4, 0, &twl4030_dapm_analoglmic_control),
-	SND_SOC_DAPM_VALUE_MUX("Analog Right Capture Route",
-		TWL4030_REG_ANAMICR, 4, 0, &twl4030_dapm_analogrmic_control),
+	/* Analog input mixers for the capture amplifiers */
+	SND_SOC_DAPM_MIXER("Analog Left",
+		TWL4030_REG_ANAMICL, 4, 0,
+		&twl4030_dapm_analoglmic_controls[0],
+		ARRAY_SIZE(twl4030_dapm_analoglmic_controls)),
+	SND_SOC_DAPM_MIXER("Analog Right",
+		TWL4030_REG_ANAMICR, 4, 0,
+		&twl4030_dapm_analogrmic_controls[0],
+		ARRAY_SIZE(twl4030_dapm_analogrmic_controls)),
 
 	SND_SOC_DAPM_PGA("ADC Physical Left",
 		TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0),
@@ -1073,74 +1363,103 @@
 };
 
 static const struct snd_soc_dapm_route intercon[] = {
-	{"Analog L1 Playback Mixer", NULL, "DAC Left1"},
-	{"Analog R1 Playback Mixer", NULL, "DAC Right1"},
-	{"Analog L2 Playback Mixer", NULL, "DAC Left2"},
-	{"Analog R2 Playback Mixer", NULL, "DAC Right2"},
+	{"Digital L1 Playback Mixer", NULL, "DAC Left1"},
+	{"Digital R1 Playback Mixer", NULL, "DAC Right1"},
+	{"Digital L2 Playback Mixer", NULL, "DAC Left2"},
+	{"Digital R2 Playback Mixer", NULL, "DAC Right2"},
+	{"Digital Voice Playback Mixer", NULL, "DAC Voice"},
 
-	{"ARXL1_APGA", NULL, "Analog L1 Playback Mixer"},
-	{"ARXR1_APGA", NULL, "Analog R1 Playback Mixer"},
-	{"ARXL2_APGA", NULL, "Analog L2 Playback Mixer"},
-	{"ARXR2_APGA", NULL, "Analog R2 Playback Mixer"},
+	{"Analog L1 Playback Mixer", NULL, "Digital L1 Playback Mixer"},
+	{"Analog R1 Playback Mixer", NULL, "Digital R1 Playback Mixer"},
+	{"Analog L2 Playback Mixer", NULL, "Digital L2 Playback Mixer"},
+	{"Analog R2 Playback Mixer", NULL, "Digital R2 Playback Mixer"},
+	{"Analog Voice Playback Mixer", NULL, "Digital Voice Playback Mixer"},
 
 	/* Internal playback routings */
 	/* Earpiece */
-	{"Earpiece Mux", "DACL1", "ARXL1_APGA"},
-	{"Earpiece Mux", "DACL2", "ARXL2_APGA"},
-	{"Earpiece Mux", "DACR1", "ARXR1_APGA"},
+	{"Earpiece Mixer", "Voice", "Analog Voice Playback Mixer"},
+	{"Earpiece Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+	{"Earpiece Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+	{"Earpiece Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+	{"Earpiece PGA", NULL, "Earpiece Mixer"},
 	/* PreDrivL */
-	{"PredriveL Mux", "DACL1", "ARXL1_APGA"},
-	{"PredriveL Mux", "DACL2", "ARXL2_APGA"},
-	{"PredriveL Mux", "DACR2", "ARXR2_APGA"},
+	{"PredriveL Mixer", "Voice", "Analog Voice Playback Mixer"},
+	{"PredriveL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+	{"PredriveL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+	{"PredriveL Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+	{"PredriveL PGA", NULL, "PredriveL Mixer"},
 	/* PreDrivR */
-	{"PredriveR Mux", "DACR1", "ARXR1_APGA"},
-	{"PredriveR Mux", "DACR2", "ARXR2_APGA"},
-	{"PredriveR Mux", "DACL2", "ARXL2_APGA"},
+	{"PredriveR Mixer", "Voice", "Analog Voice Playback Mixer"},
+	{"PredriveR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+	{"PredriveR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+	{"PredriveR Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+	{"PredriveR PGA", NULL, "PredriveR Mixer"},
 	/* HeadsetL */
-	{"HeadsetL Mux", "DACL1", "ARXL1_APGA"},
-	{"HeadsetL Mux", "DACL2", "ARXL2_APGA"},
+	{"HeadsetL Mixer", "Voice", "Analog Voice Playback Mixer"},
+	{"HeadsetL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+	{"HeadsetL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+	{"HeadsetL PGA", NULL, "HeadsetL Mixer"},
 	/* HeadsetR */
-	{"HeadsetR Mux", "DACR1", "ARXR1_APGA"},
-	{"HeadsetR Mux", "DACR2", "ARXR2_APGA"},
+	{"HeadsetR Mixer", "Voice", "Analog Voice Playback Mixer"},
+	{"HeadsetR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+	{"HeadsetR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+	{"HeadsetR PGA", NULL, "HeadsetR Mixer"},
 	/* CarkitL */
-	{"CarkitL Mux", "DACL1", "ARXL1_APGA"},
-	{"CarkitL Mux", "DACL2", "ARXL2_APGA"},
+	{"CarkitL Mixer", "Voice", "Analog Voice Playback Mixer"},
+	{"CarkitL Mixer", "AudioL1", "Analog L1 Playback Mixer"},
+	{"CarkitL Mixer", "AudioL2", "Analog L2 Playback Mixer"},
+	{"CarkitL PGA", NULL, "CarkitL Mixer"},
 	/* CarkitR */
-	{"CarkitR Mux", "DACR1", "ARXR1_APGA"},
-	{"CarkitR Mux", "DACR2", "ARXR2_APGA"},
+	{"CarkitR Mixer", "Voice", "Analog Voice Playback Mixer"},
+	{"CarkitR Mixer", "AudioR1", "Analog R1 Playback Mixer"},
+	{"CarkitR Mixer", "AudioR2", "Analog R2 Playback Mixer"},
+	{"CarkitR PGA", NULL, "CarkitR Mixer"},
 	/* HandsfreeL */
-	{"HandsfreeL Mux", "DACL1", "ARXL1_APGA"},
-	{"HandsfreeL Mux", "DACL2", "ARXL2_APGA"},
-	{"HandsfreeL Mux", "DACR2", "ARXR2_APGA"},
+	{"HandsfreeL Mux", "Voice", "Analog Voice Playback Mixer"},
+	{"HandsfreeL Mux", "AudioL1", "Analog L1 Playback Mixer"},
+	{"HandsfreeL Mux", "AudioL2", "Analog L2 Playback Mixer"},
+	{"HandsfreeL Mux", "AudioR2", "Analog R2 Playback Mixer"},
+	{"HandsfreeL", "Switch", "HandsfreeL Mux"},
+	{"HandsfreeL PGA", NULL, "HandsfreeL"},
 	/* HandsfreeR */
-	{"HandsfreeR Mux", "DACR1", "ARXR1_APGA"},
-	{"HandsfreeR Mux", "DACR2", "ARXR2_APGA"},
-	{"HandsfreeR Mux", "DACL2", "ARXL2_APGA"},
+	{"HandsfreeR Mux", "Voice", "Analog Voice Playback Mixer"},
+	{"HandsfreeR Mux", "AudioR1", "Analog R1 Playback Mixer"},
+	{"HandsfreeR Mux", "AudioR2", "Analog R2 Playback Mixer"},
+	{"HandsfreeR Mux", "AudioL2", "Analog L2 Playback Mixer"},
+	{"HandsfreeR", "Switch", "HandsfreeR Mux"},
+	{"HandsfreeR PGA", NULL, "HandsfreeR"},
+	/* Vibra */
+	{"Vibra Mux", "AudioL1", "DAC Left1"},
+	{"Vibra Mux", "AudioR1", "DAC Right1"},
+	{"Vibra Mux", "AudioL2", "DAC Left2"},
+	{"Vibra Mux", "AudioR2", "DAC Right2"},
 
 	/* outputs */
-	{"OUTL", NULL, "ARXL2_APGA"},
-	{"OUTR", NULL, "ARXR2_APGA"},
-	{"EARPIECE", NULL, "Earpiece Mux"},
-	{"PREDRIVEL", NULL, "PredriveL Mux"},
-	{"PREDRIVER", NULL, "PredriveR Mux"},
-	{"HSOL", NULL, "HeadsetL Mux"},
-	{"HSOR", NULL, "HeadsetR Mux"},
-	{"CARKITL", NULL, "CarkitL Mux"},
-	{"CARKITR", NULL, "CarkitR Mux"},
-	{"HFL", NULL, "HandsfreeL Mux"},
-	{"HFR", NULL, "HandsfreeR Mux"},
+	{"OUTL", NULL, "Analog L2 Playback Mixer"},
+	{"OUTR", NULL, "Analog R2 Playback Mixer"},
+	{"EARPIECE", NULL, "Earpiece PGA"},
+	{"PREDRIVEL", NULL, "PredriveL PGA"},
+	{"PREDRIVER", NULL, "PredriveR PGA"},
+	{"HSOL", NULL, "HeadsetL PGA"},
+	{"HSOR", NULL, "HeadsetR PGA"},
+	{"CARKITL", NULL, "CarkitL PGA"},
+	{"CARKITR", NULL, "CarkitR PGA"},
+	{"HFL", NULL, "HandsfreeL PGA"},
+	{"HFR", NULL, "HandsfreeR PGA"},
+	{"Vibra Route", "Audio", "Vibra Mux"},
+	{"VIBRA", NULL, "Vibra Route"},
 
 	/* Capture path */
-	{"Analog Left Capture Route", "Main mic", "MAINMIC"},
-	{"Analog Left Capture Route", "Headset mic", "HSMIC"},
-	{"Analog Left Capture Route", "AUXL", "AUXL"},
-	{"Analog Left Capture Route", "Carkit mic", "CARKITMIC"},
+	{"Analog Left", "Main Mic Capture Switch", "MAINMIC"},
+	{"Analog Left", "Headset Mic Capture Switch", "HSMIC"},
+	{"Analog Left", "AUXL Capture Switch", "AUXL"},
+	{"Analog Left", "Carkit Mic Capture Switch", "CARKITMIC"},
 
-	{"Analog Right Capture Route", "Sub mic", "SUBMIC"},
-	{"Analog Right Capture Route", "AUXR", "AUXR"},
+	{"Analog Right", "Sub Mic Capture Switch", "SUBMIC"},
+	{"Analog Right", "AUXR Capture Switch", "AUXR"},
 
-	{"ADC Physical Left", NULL, "Analog Left Capture Route"},
-	{"ADC Physical Right", NULL, "Analog Right Capture Route"},
+	{"ADC Physical Left", NULL, "Analog Left"},
+	{"ADC Physical Right", NULL, "Analog Right"},
 
 	{"Digimic0 Enable", NULL, "DIGIMIC0"},
 	{"Digimic1 Enable", NULL, "DIGIMIC1"},
@@ -1164,22 +1483,26 @@
 	{"ADC Virtual Right2", NULL, "TX2 Capture Route"},
 
 	/* Analog bypass routes */
-	{"Right1 Analog Loopback", "Switch", "Analog Right Capture Route"},
-	{"Left1 Analog Loopback", "Switch", "Analog Left Capture Route"},
-	{"Right2 Analog Loopback", "Switch", "Analog Right Capture Route"},
-	{"Left2 Analog Loopback", "Switch", "Analog Left Capture Route"},
+	{"Right1 Analog Loopback", "Switch", "Analog Right"},
+	{"Left1 Analog Loopback", "Switch", "Analog Left"},
+	{"Right2 Analog Loopback", "Switch", "Analog Right"},
+	{"Left2 Analog Loopback", "Switch", "Analog Left"},
+	{"Voice Analog Loopback", "Switch", "Analog Left"},
 
 	{"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
 	{"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
 	{"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
 	{"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"},
+	{"Analog Voice Playback Mixer", NULL, "Voice Analog Loopback"},
 
 	/* Digital bypass routes */
 	{"Right Digital Loopback", "Volume", "TX1 Capture Route"},
 	{"Left Digital Loopback", "Volume", "TX1 Capture Route"},
+	{"Voice Digital Loopback", "Volume", "TX2 Capture Route"},
 
-	{"Analog R2 Playback Mixer", NULL, "Right Digital Loopback"},
-	{"Analog L2 Playback Mixer", NULL, "Left Digital Loopback"},
+	{"Digital R2 Playback Mixer", NULL, "Right Digital Loopback"},
+	{"Digital L2 Playback Mixer", NULL, "Left Digital Loopback"},
+	{"Digital Voice Playback Mixer", NULL, "Voice Digital Loopback"},
 
 };
 
@@ -1226,6 +1549,58 @@
 	return 0;
 }
 
+static void twl4030_constraints(struct twl4030_priv *twl4030,
+				struct snd_pcm_substream *mst_substream)
+{
+	struct snd_pcm_substream *slv_substream;
+
+	/* Pick the stream, which need to be constrained */
+	if (mst_substream == twl4030->master_substream)
+		slv_substream = twl4030->slave_substream;
+	else if (mst_substream == twl4030->slave_substream)
+		slv_substream = twl4030->master_substream;
+	else /* This should not happen.. */
+		return;
+
+	/* Set the constraints according to the already configured stream */
+	snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+				SNDRV_PCM_HW_PARAM_RATE,
+				twl4030->rate,
+				twl4030->rate);
+
+	snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+				SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+				twl4030->sample_bits,
+				twl4030->sample_bits);
+
+	snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+				SNDRV_PCM_HW_PARAM_CHANNELS,
+				twl4030->channels,
+				twl4030->channels);
+}
+
+/* In case of 4 channel mode, the RX1 L/R for playback and the TX2 L/R for
+ * capture has to be enabled/disabled. */
+static void twl4030_tdm_enable(struct snd_soc_codec *codec, int direction,
+				int enable)
+{
+	u8 reg, mask;
+
+	reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION);
+
+	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+		mask = TWL4030_ARXL1_VRX_EN | TWL4030_ARXR1_EN;
+	else
+		mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN;
+
+	if (enable)
+		reg |= mask;
+	else
+		reg &= ~mask;
+
+	twl4030_write(codec, TWL4030_REG_OPTION, reg);
+}
+
 static int twl4030_startup(struct snd_pcm_substream *substream,
 			   struct snd_soc_dai *dai)
 {
@@ -1234,26 +1609,25 @@
 	struct snd_soc_codec *codec = socdev->card->codec;
 	struct twl4030_priv *twl4030 = codec->private_data;
 
-	/* If we already have a playback or capture going then constrain
-	 * this substream to match it.
-	 */
 	if (twl4030->master_substream) {
-		struct snd_pcm_runtime *master_runtime;
-		master_runtime = twl4030->master_substream->runtime;
-
-		snd_pcm_hw_constraint_minmax(substream->runtime,
-					     SNDRV_PCM_HW_PARAM_RATE,
-					     master_runtime->rate,
-					     master_runtime->rate);
-
-		snd_pcm_hw_constraint_minmax(substream->runtime,
-					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-					     master_runtime->sample_bits,
-					     master_runtime->sample_bits);
-
 		twl4030->slave_substream = substream;
-	} else
+		/* The DAI has one configuration for playback and capture, so
+		 * if the DAI has been already configured then constrain this
+		 * substream to match it. */
+		if (twl4030->configured)
+			twl4030_constraints(twl4030, twl4030->master_substream);
+	} else {
+		if (!(twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE) &
+			TWL4030_OPTION_1)) {
+			/* In option2 4 channel is not supported, set the
+			 * constraint for the first stream for channels, the
+			 * second stream will 'inherit' this cosntraint */
+			snd_pcm_hw_constraint_minmax(substream->runtime,
+						SNDRV_PCM_HW_PARAM_CHANNELS,
+						2, 2);
+		}
 		twl4030->master_substream = substream;
+	}
 
 	return 0;
 }
@@ -1270,6 +1644,17 @@
 		twl4030->master_substream = twl4030->slave_substream;
 
 	twl4030->slave_substream = NULL;
+
+	/* If all streams are closed, or the remaining stream has not yet
+	 * been configured than set the DAI as not configured. */
+	if (!twl4030->master_substream)
+		twl4030->configured = 0;
+	 else if (!twl4030->master_substream->runtime->channels)
+		twl4030->configured = 0;
+
+	 /* If the closing substream had 4 channel, do the necessary cleanup */
+	if (substream->runtime->channels == 4)
+		twl4030_tdm_enable(codec, substream->stream, 0);
 }
 
 static int twl4030_hw_params(struct snd_pcm_substream *substream,
@@ -1282,8 +1667,22 @@
 	struct twl4030_priv *twl4030 = codec->private_data;
 	u8 mode, old_mode, format, old_format;
 
-	if (substream == twl4030->slave_substream)
-		/* Ignoring hw_params for slave substream */
+	 /* If the substream has 4 channel, do the necessary setup */
+	if (params_channels(params) == 4) {
+		format = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+		mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
+
+		/* Safety check: are we in the correct operating mode and
+		 * the interface is in TDM mode? */
+		if ((mode & TWL4030_OPTION_1) &&
+		    ((format & TWL4030_AIF_FORMAT) == TWL4030_AIF_FORMAT_TDM))
+			twl4030_tdm_enable(codec, substream->stream, 1);
+		else
+			return -EINVAL;
+	}
+
+	if (twl4030->configured)
+		/* Ignoring hw_params for already configured DAI */
 		return 0;
 
 	/* bit rate */
@@ -1363,6 +1762,21 @@
 		/* set CODECPDZ afterwards */
 		twl4030_codec_enable(codec, 1);
 	}
+
+	/* Store the important parameters for the DAI configuration and set
+	 * the DAI as configured */
+	twl4030->configured = 1;
+	twl4030->rate = params_rate(params);
+	twl4030->sample_bits = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
+	twl4030->channels = params_channels(params);
+
+	/* If both playback and capture streams are open, and one of them
+	 * is setting the hw parameters right now (since we are here), set
+	 * constraints to the other stream to match the current one. */
+	if (twl4030->slave_substream)
+		twl4030_constraints(twl4030, substream);
+
 	return 0;
 }
 
@@ -1370,17 +1784,21 @@
 		int clk_id, unsigned int freq, int dir)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
+	struct twl4030_priv *twl4030 = codec->private_data;
 	u8 infreq;
 
 	switch (freq) {
 	case 19200000:
 		infreq = TWL4030_APLL_INFREQ_19200KHZ;
+		twl4030->sysclk = 19200;
 		break;
 	case 26000000:
 		infreq = TWL4030_APLL_INFREQ_26000KHZ;
+		twl4030->sysclk = 26000;
 		break;
 	case 38400000:
 		infreq = TWL4030_APLL_INFREQ_38400KHZ;
+		twl4030->sysclk = 38400;
 		break;
 	default:
 		printk(KERN_ERR "TWL4030 set sysclk: unknown rate %d\n",
@@ -1424,6 +1842,9 @@
 	case SND_SOC_DAIFMT_I2S:
 		format |= TWL4030_AIF_FORMAT_CODEC;
 		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		format |= TWL4030_AIF_FORMAT_TDM;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -1443,6 +1864,206 @@
 	return 0;
 }
 
+static int twl4030_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_AUDIO_IF);
+
+	if (tristate)
+		reg |= TWL4030_AIF_TRI_EN;
+	else
+		reg &= ~TWL4030_AIF_TRI_EN;
+
+	return twl4030_write(codec, TWL4030_REG_AUDIO_IF, reg);
+}
+
+/* In case of voice mode, the RX1 L(VRX) for downlink and the TX2 L/R
+ * (VTXL, VTXR) for uplink has to be enabled/disabled. */
+static void twl4030_voice_enable(struct snd_soc_codec *codec, int direction,
+				int enable)
+{
+	u8 reg, mask;
+
+	reg = twl4030_read_reg_cache(codec, TWL4030_REG_OPTION);
+
+	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+		mask = TWL4030_ARXL1_VRX_EN;
+	else
+		mask = TWL4030_ATXL2_VTXL_EN | TWL4030_ATXR2_VTXR_EN;
+
+	if (enable)
+		reg |= mask;
+	else
+		reg &= ~mask;
+
+	twl4030_write(codec, TWL4030_REG_OPTION, reg);
+}
+
+static int twl4030_voice_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	u8 infreq;
+	u8 mode;
+
+	/* If the system master clock is not 26MHz, the voice PCM interface is
+	 * not avilable.
+	 */
+	infreq = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL)
+		& TWL4030_APLL_INFREQ;
+
+	if (infreq != TWL4030_APLL_INFREQ_26000KHZ) {
+		printk(KERN_ERR "TWL4030 voice startup: "
+			"MCLK is not 26MHz, call set_sysclk() on init\n");
+		return -EINVAL;
+	}
+
+	/* If the codec mode is not option2, the voice PCM interface is not
+	 * avilable.
+	 */
+	mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
+		& TWL4030_OPT_MODE;
+
+	if (mode != TWL4030_OPTION_2) {
+		printk(KERN_ERR "TWL4030 voice startup: "
+			"the codec mode is not option2\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	/* Enable voice digital filters */
+	twl4030_voice_enable(codec, substream->stream, 0);
+}
+
+static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	u8 old_mode, mode;
+
+	/* Enable voice digital filters */
+	twl4030_voice_enable(codec, substream->stream, 1);
+
+	/* bit rate */
+	old_mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE)
+		& ~(TWL4030_CODECPDZ);
+	mode = old_mode;
+
+	switch (params_rate(params)) {
+	case 8000:
+		mode &= ~(TWL4030_SEL_16K);
+		break;
+	case 16000:
+		mode |= TWL4030_SEL_16K;
+		break;
+	default:
+		printk(KERN_ERR "TWL4030 voice hw params: unknown rate %d\n",
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	if (mode != old_mode) {
+		/* change rate and set CODECPDZ */
+		twl4030_codec_enable(codec, 0);
+		twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
+		twl4030_codec_enable(codec, 1);
+	}
+
+	return 0;
+}
+
+static int twl4030_voice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u8 infreq;
+
+	switch (freq) {
+	case 26000000:
+		infreq = TWL4030_APLL_INFREQ_26000KHZ;
+		break;
+	default:
+		printk(KERN_ERR "TWL4030 voice set sysclk: unknown rate %d\n",
+			freq);
+		return -EINVAL;
+	}
+
+	infreq |= TWL4030_APLL_EN;
+	twl4030_write(codec, TWL4030_REG_APLL_CTL, infreq);
+
+	return 0;
+}
+
+static int twl4030_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u8 old_format, format;
+
+	/* get format */
+	old_format = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF);
+	format = old_format;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		format &= ~(TWL4030_VIF_SLAVE_EN);
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		format |= TWL4030_VIF_SLAVE_EN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_IB_NF:
+		format &= ~(TWL4030_VIF_FORMAT);
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		format |= TWL4030_VIF_FORMAT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (format != old_format) {
+		/* change format and set CODECPDZ */
+		twl4030_codec_enable(codec, 0);
+		twl4030_write(codec, TWL4030_REG_VOICE_IF, format);
+		twl4030_codec_enable(codec, 1);
+	}
+
+	return 0;
+}
+
+static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u8 reg = twl4030_read_reg_cache(codec, TWL4030_REG_VOICE_IF);
+
+	if (tristate)
+		reg |= TWL4030_VIF_TRI_EN;
+	else
+		reg &= ~TWL4030_VIF_TRI_EN;
+
+	return twl4030_write(codec, TWL4030_REG_VOICE_IF, reg);
+}
+
 #define TWL4030_RATES	 (SNDRV_PCM_RATE_8000_48000)
 #define TWL4030_FORMATS	 (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
 
@@ -1452,23 +2073,51 @@
 	.hw_params	= twl4030_hw_params,
 	.set_sysclk	= twl4030_set_dai_sysclk,
 	.set_fmt	= twl4030_set_dai_fmt,
+	.set_tristate	= twl4030_set_tristate,
 };
 
-struct snd_soc_dai twl4030_dai = {
+static struct snd_soc_dai_ops twl4030_dai_voice_ops = {
+	.startup	= twl4030_voice_startup,
+	.shutdown	= twl4030_voice_shutdown,
+	.hw_params	= twl4030_voice_hw_params,
+	.set_sysclk	= twl4030_voice_set_dai_sysclk,
+	.set_fmt	= twl4030_voice_set_dai_fmt,
+	.set_tristate	= twl4030_voice_set_tristate,
+};
+
+struct snd_soc_dai twl4030_dai[] = {
+{
 	.name = "twl4030",
 	.playback = {
-		.stream_name = "Playback",
+		.stream_name = "HiFi Playback",
 		.channels_min = 2,
-		.channels_max = 2,
+		.channels_max = 4,
 		.rates = TWL4030_RATES | SNDRV_PCM_RATE_96000,
 		.formats = TWL4030_FORMATS,},
 	.capture = {
 		.stream_name = "Capture",
 		.channels_min = 2,
-		.channels_max = 2,
+		.channels_max = 4,
 		.rates = TWL4030_RATES,
 		.formats = TWL4030_FORMATS,},
 	.ops = &twl4030_dai_ops,
+},
+{
+	.name = "twl4030 Voice",
+	.playback = {
+		.stream_name = "Voice Playback",
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.ops = &twl4030_dai_voice_ops,
+},
 };
 EXPORT_SYMBOL_GPL(twl4030_dai);
 
@@ -1500,6 +2149,8 @@
 static int twl4030_init(struct snd_soc_device *socdev)
 {
 	struct snd_soc_codec *codec = socdev->card->codec;
+	struct twl4030_setup_data *setup = socdev->codec_data;
+	struct twl4030_priv *twl4030 = codec->private_data;
 	int ret = 0;
 
 	printk(KERN_INFO "TWL4030 Audio Codec init \n");
@@ -1509,14 +2160,31 @@
 	codec->read = twl4030_read_reg_cache;
 	codec->write = twl4030_write;
 	codec->set_bias_level = twl4030_set_bias_level;
-	codec->dai = &twl4030_dai;
-	codec->num_dai = 1;
+	codec->dai = twl4030_dai;
+	codec->num_dai = ARRAY_SIZE(twl4030_dai),
 	codec->reg_cache_size = sizeof(twl4030_reg);
 	codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
 					GFP_KERNEL);
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
 
+	/* Configuration for headset ramp delay from setup data */
+	if (setup) {
+		unsigned char hs_pop;
+
+		if (setup->sysclk)
+			twl4030->sysclk = setup->sysclk;
+		else
+			twl4030->sysclk = 26000;
+
+		hs_pop = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
+		hs_pop &= ~TWL4030_RAMP_DELAY;
+		hs_pop |= (setup->ramp_delay_value << 2);
+		twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
+	} else {
+		twl4030->sysclk = 26000;
+	}
+
 	/* register pcms */
 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 	if (ret < 0) {
@@ -1604,13 +2272,13 @@
 
 static int __init twl4030_modinit(void)
 {
-	return snd_soc_register_dai(&twl4030_dai);
+	return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
 }
 module_init(twl4030_modinit);
 
 static void __exit twl4030_exit(void)
 {
-	snd_soc_unregister_dai(&twl4030_dai);
+	snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
 }
 module_exit(twl4030_exit);
 
diff --git a/sound/soc/codecs/twl4030.h b/sound/soc/codecs/twl4030.h
index cb63765..2b4bfa2 100644
--- a/sound/soc/codecs/twl4030.h
+++ b/sound/soc/codecs/twl4030.h
@@ -92,8 +92,9 @@
 #define TWL4030_REG_VIBRA_PWM_SET	0x47
 #define TWL4030_REG_ANAMIC_GAIN		0x48
 #define TWL4030_REG_MISC_SET_2		0x49
+#define TWL4030_REG_SW_SHADOW		0x4A
 
-#define TWL4030_CACHEREGNUM	(TWL4030_REG_MISC_SET_2 + 1)
+#define TWL4030_CACHEREGNUM	(TWL4030_REG_SW_SHADOW + 1)
 
 /* Bitfield Definitions */
 
@@ -110,9 +111,22 @@
 #define TWL4030_APLL_RATE_44100		0x90
 #define TWL4030_APLL_RATE_48000		0xA0
 #define TWL4030_APLL_RATE_96000		0xE0
-#define TWL4030_SEL_16K			0x04
+#define TWL4030_SEL_16K			0x08
 #define TWL4030_CODECPDZ		0x02
 #define TWL4030_OPT_MODE		0x01
+#define TWL4030_OPTION_1		(1 << 0)
+#define TWL4030_OPTION_2		(0 << 0)
+
+/* TWL4030_OPTION (0x02) Fields */
+
+#define TWL4030_ATXL1_EN		(1 << 0)
+#define TWL4030_ATXR1_EN		(1 << 1)
+#define TWL4030_ATXL2_VTXL_EN		(1 << 2)
+#define TWL4030_ATXR2_VTXR_EN		(1 << 3)
+#define TWL4030_ARXL1_VRX_EN		(1 << 4)
+#define TWL4030_ARXR1_EN		(1 << 5)
+#define TWL4030_ARXL2_EN		(1 << 6)
+#define TWL4030_ARXR2_EN		(1 << 7)
 
 /* TWL4030_REG_MICBIAS_CTL (0x04) Fields */
 
@@ -171,6 +185,17 @@
 #define TWL4030_CLK256FS_EN		0x02
 #define TWL4030_AIF_EN			0x01
 
+/* VOICE_IF (0x0F) Fields */
+
+#define TWL4030_VIF_SLAVE_EN		0x80
+#define TWL4030_VIF_DIN_EN		0x40
+#define TWL4030_VIF_DOUT_EN		0x20
+#define TWL4030_VIF_SWAP		0x10
+#define TWL4030_VIF_FORMAT		0x08
+#define TWL4030_VIF_TRI_EN		0x04
+#define TWL4030_VIF_SUB_EN		0x02
+#define TWL4030_VIF_EN			0x01
+
 /* EAR_CTL (0x21) */
 #define TWL4030_EAR_GAIN		0x30
 
@@ -236,7 +261,21 @@
 #define TWL4030_SMOOTH_ANAVOL_EN	0x02
 #define TWL4030_DIGMIC_LR_SWAP_EN	0x01
 
-extern struct snd_soc_dai twl4030_dai;
+/* TWL4030_REG_SW_SHADOW (0x4A) Fields */
+#define TWL4030_HFL_EN			0x01
+#define TWL4030_HFR_EN			0x02
+
+#define TWL4030_DAI_HIFI		0
+#define TWL4030_DAI_VOICE		1
+
+extern struct snd_soc_dai twl4030_dai[2];
 extern struct snd_soc_codec_device soc_codec_dev_twl4030;
 
+struct twl4030_setup_data {
+	unsigned int ramp_delay_value;
+	unsigned int sysclk;
+	unsigned int hs_extmute:1;
+	void (*set_hs_extmute)(int mute);
+};
+
 #endif	/* End of __TWL4030_AUDIO_H__ */
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index ddefb8f..269b108 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -101,7 +101,7 @@
 	pr_debug("%s reg: %02X, value:%02X\n", __func__, reg, value);
 
 	if (reg >= UDA134X_REGS_NUM) {
-		printk(KERN_ERR "%s unkown register: reg: %d",
+		printk(KERN_ERR "%s unkown register: reg: %u",
 		       __func__, reg);
 		return -EINVAL;
 	}
@@ -296,7 +296,7 @@
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct uda134x_priv *uda134x = codec->private_data;
 
-	pr_debug("%s clk_id: %d, freq: %d, dir: %d\n", __func__,
+	pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__,
 		 clk_id, freq, dir);
 
 	/* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 5b21594..92ec034 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -5,9 +5,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  *
- * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
- * Improved support for DAPM and audio routing/mixing capabilities,
- * added TLV support.
+ * Copyright (c) 2007-2009 Philipp Zabel <philipp.zabel@gmail.com>
  *
  * Modified by Richard Purdie <richard@openedhand.com> to fit into SoC
  * codec model.
@@ -19,26 +17,32 @@
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/types.h>
-#include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
-#include <linux/ioctl.h>
+#include <linux/gpio.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/workqueue.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/initval.h>
-#include <sound/info.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/tlv.h>
+#include <sound/uda1380.h>
 
 #include "uda1380.h"
 
-static struct work_struct uda1380_work;
 static struct snd_soc_codec *uda1380_codec;
 
+/* codec private data */
+struct uda1380_priv {
+	struct snd_soc_codec codec;
+	u16 reg_cache[UDA1380_CACHEREGNUM];
+	unsigned int dac_clk;
+	struct work_struct work;
+};
+
 /*
  * uda1380 register cache
  */
@@ -473,6 +477,7 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
+	struct uda1380_priv *uda1380 = codec->private_data;
 	int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
 
 	switch (cmd) {
@@ -480,13 +485,13 @@
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
 		uda1380_write_reg_cache(codec, UDA1380_MIXER,
 					mixer & ~R14_SILENCE);
-		schedule_work(&uda1380_work);
+		schedule_work(&uda1380->work);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
 		uda1380_write_reg_cache(codec, UDA1380_MIXER,
 					mixer | R14_SILENCE);
-		schedule_work(&uda1380_work);
+		schedule_work(&uda1380->work);
 		break;
 	}
 	return 0;
@@ -670,44 +675,33 @@
 	return 0;
 }
 
-/*
- * initialise the UDA1380 driver
- * register mixer and dsp interfaces with the kernel
- */
-static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
+static int uda1380_probe(struct platform_device *pdev)
 {
-	struct snd_soc_codec *codec = socdev->card->codec;
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	struct uda1380_platform_data *pdata;
 	int ret = 0;
 
-	codec->name = "UDA1380";
-	codec->owner = THIS_MODULE;
-	codec->read = uda1380_read_reg_cache;
-	codec->write = uda1380_write;
-	codec->set_bias_level = uda1380_set_bias_level;
-	codec->dai = uda1380_dai;
-	codec->num_dai = ARRAY_SIZE(uda1380_dai);
-	codec->reg_cache = kmemdup(uda1380_reg, sizeof(uda1380_reg),
-				   GFP_KERNEL);
-	if (codec->reg_cache == NULL)
-		return -ENOMEM;
-	codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
-	codec->reg_cache_step = 1;
-	uda1380_reset(codec);
+	if (uda1380_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
 
-	uda1380_codec = codec;
-	INIT_WORK(&uda1380_work, uda1380_flush_work);
+	socdev->card->codec = uda1380_codec;
+	codec = uda1380_codec;
+	pdata = codec->dev->platform_data;
 
 	/* register pcms */
 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 	if (ret < 0) {
-		pr_err("uda1380: failed to create pcms\n");
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
 		goto pcm_err;
 	}
 
 	/* power on device */
 	uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	/* set clock input */
-	switch (dac_clk) {
+	switch (pdata->dac_clk) {
 	case UDA1380_DAC_CLK_SYSCLK:
 		uda1380_write(codec, UDA1380_CLK, 0);
 		break;
@@ -716,13 +710,12 @@
 		break;
 	}
 
-	/* uda1380 init */
 	snd_soc_add_controls(codec, uda1380_snd_controls,
 				ARRAY_SIZE(uda1380_snd_controls));
 	uda1380_add_widgets(codec);
 	ret = snd_soc_init_card(socdev);
 	if (ret < 0) {
-		pr_err("uda1380: failed to register card\n");
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
 		goto card_err;
 	}
 
@@ -732,36 +725,164 @@
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
 pcm_err:
-	kfree(codec->reg_cache);
 	return ret;
 }
 
-static struct snd_soc_device *uda1380_socdev;
+/* power down chip */
+static int uda1380_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	if (codec->control_data)
+		uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_uda1380 = {
+	.probe = 	uda1380_probe,
+	.remove = 	uda1380_remove,
+	.suspend = 	uda1380_suspend,
+	.resume =	uda1380_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
+
+static int uda1380_register(struct uda1380_priv *uda1380)
+{
+	int ret, i;
+	struct snd_soc_codec *codec = &uda1380->codec;
+	struct uda1380_platform_data *pdata = codec->dev->platform_data;
+
+	if (uda1380_codec) {
+		dev_err(codec->dev, "Another UDA1380 is registered\n");
+		return -EINVAL;
+	}
+
+	if (!pdata || !pdata->gpio_power || !pdata->gpio_reset)
+		return -EINVAL;
+
+	ret = gpio_request(pdata->gpio_power, "uda1380 power");
+	if (ret)
+		goto err_out;
+	ret = gpio_request(pdata->gpio_reset, "uda1380 reset");
+	if (ret)
+		goto err_gpio;
+
+	gpio_direction_output(pdata->gpio_power, 1);
+
+	/* we may need to have the clock running here - pH5 */
+	gpio_direction_output(pdata->gpio_reset, 1);
+	udelay(5);
+	gpio_set_value(pdata->gpio_reset, 0);
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = uda1380;
+	codec->name = "UDA1380";
+	codec->owner = THIS_MODULE;
+	codec->read = uda1380_read_reg_cache;
+	codec->write = uda1380_write;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = uda1380_set_bias_level;
+	codec->dai = uda1380_dai;
+	codec->num_dai = ARRAY_SIZE(uda1380_dai);
+	codec->reg_cache_size = ARRAY_SIZE(uda1380_reg);
+	codec->reg_cache = &uda1380->reg_cache;
+	codec->reg_cache_step = 1;
+
+	memcpy(codec->reg_cache, uda1380_reg, sizeof(uda1380_reg));
+
+	ret = uda1380_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		goto err_reset;
+	}
+
+	INIT_WORK(&uda1380->work, uda1380_flush_work);
+
+	for (i = 0; i < ARRAY_SIZE(uda1380_dai); i++)
+		uda1380_dai[i].dev = codec->dev;
+
+	uda1380_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto err_reset;
+	}
+
+	ret = snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+		goto err_dai;
+	}
+
+	return 0;
+
+err_dai:
+	snd_soc_unregister_codec(codec);
+err_reset:
+	gpio_set_value(pdata->gpio_power, 0);
+	gpio_free(pdata->gpio_reset);
+err_gpio:
+	gpio_free(pdata->gpio_power);
+err_out:
+	return ret;
+}
+
+static void uda1380_unregister(struct uda1380_priv *uda1380)
+{
+	struct snd_soc_codec *codec = &uda1380->codec;
+	struct uda1380_platform_data *pdata = codec->dev->platform_data;
+
+	snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+	snd_soc_unregister_codec(&uda1380->codec);
+
+	gpio_set_value(pdata->gpio_power, 0);
+	gpio_free(pdata->gpio_reset);
+	gpio_free(pdata->gpio_power);
+
+	kfree(uda1380);
+	uda1380_codec = NULL;
+}
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-
-static int uda1380_i2c_probe(struct i2c_client *i2c,
-			     const struct i2c_device_id *id)
+static __devinit int uda1380_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
 {
-	struct snd_soc_device *socdev = uda1380_socdev;
-	struct uda1380_setup_data *setup = socdev->codec_data;
-	struct snd_soc_codec *codec = socdev->card->codec;
+	struct uda1380_priv *uda1380;
+	struct snd_soc_codec *codec;
 	int ret;
 
-	i2c_set_clientdata(i2c, codec);
+	uda1380 = kzalloc(sizeof(struct uda1380_priv), GFP_KERNEL);
+	if (uda1380 == NULL)
+		return -ENOMEM;
+
+	codec = &uda1380->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(i2c, uda1380);
 	codec->control_data = i2c;
 
-	ret = uda1380_init(socdev, setup->dac_clk);
-	if (ret < 0)
-		pr_err("uda1380: failed to initialise UDA1380\n");
+	codec->dev = &i2c->dev;
+
+	ret = uda1380_register(uda1380);
+	if (ret != 0)
+		kfree(uda1380);
 
 	return ret;
 }
 
-static int uda1380_i2c_remove(struct i2c_client *client)
+static int __devexit uda1380_i2c_remove(struct i2c_client *i2c)
 {
-	struct snd_soc_codec *codec = i2c_get_clientdata(client);
-	kfree(codec->reg_cache);
+	struct uda1380_priv *uda1380 = i2c_get_clientdata(i2c);
+	uda1380_unregister(uda1380);
 	return 0;
 }
 
@@ -777,120 +898,28 @@
 		.owner = THIS_MODULE,
 	},
 	.probe =    uda1380_i2c_probe,
-	.remove =   uda1380_i2c_remove,
+	.remove =   __devexit_p(uda1380_i2c_remove),
 	.id_table = uda1380_i2c_id,
 };
-
-static int uda1380_add_i2c_device(struct platform_device *pdev,
-				  const struct uda1380_setup_data *setup)
-{
-	struct i2c_board_info info;
-	struct i2c_adapter *adapter;
-	struct i2c_client *client;
-	int ret;
-
-	ret = i2c_add_driver(&uda1380_i2c_driver);
-	if (ret != 0) {
-		dev_err(&pdev->dev, "can't add i2c driver\n");
-		return ret;
-	}
-
-	memset(&info, 0, sizeof(struct i2c_board_info));
-	info.addr = setup->i2c_address;
-	strlcpy(info.type, "uda1380", I2C_NAME_SIZE);
-
-	adapter = i2c_get_adapter(setup->i2c_bus);
-	if (!adapter) {
-		dev_err(&pdev->dev, "can't get i2c adapter %d\n",
-			setup->i2c_bus);
-		goto err_driver;
-	}
-
-	client = i2c_new_device(adapter, &info);
-	i2c_put_adapter(adapter);
-	if (!client) {
-		dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
-			(unsigned int)info.addr);
-		goto err_driver;
-	}
-
-	return 0;
-
-err_driver:
-	i2c_del_driver(&uda1380_i2c_driver);
-	return -ENODEV;
-}
 #endif
 
-static int uda1380_probe(struct platform_device *pdev)
-{
-	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct uda1380_setup_data *setup;
-	struct snd_soc_codec *codec;
-	int ret;
-
-	setup = socdev->codec_data;
-	codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
-	if (codec == NULL)
-		return -ENOMEM;
-
-	socdev->card->codec = codec;
-	mutex_init(&codec->mutex);
-	INIT_LIST_HEAD(&codec->dapm_widgets);
-	INIT_LIST_HEAD(&codec->dapm_paths);
-
-	uda1380_socdev = socdev;
-	ret = -ENODEV;
-
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-	if (setup->i2c_address) {
-		codec->hw_write = (hw_write_t)i2c_master_send;
-		ret = uda1380_add_i2c_device(pdev, setup);
-	}
-#endif
-
-	if (ret != 0)
-		kfree(codec);
-	return ret;
-}
-
-/* power down chip */
-static int uda1380_remove(struct platform_device *pdev)
-{
-	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct snd_soc_codec *codec = socdev->card->codec;
-
-	if (codec->control_data)
-		uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	snd_soc_free_pcms(socdev);
-	snd_soc_dapm_free(socdev);
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-	i2c_unregister_device(codec->control_data);
-	i2c_del_driver(&uda1380_i2c_driver);
-#endif
-	kfree(codec);
-
-	return 0;
-}
-
-struct snd_soc_codec_device soc_codec_dev_uda1380 = {
-	.probe = 	uda1380_probe,
-	.remove = 	uda1380_remove,
-	.suspend = 	uda1380_suspend,
-	.resume =	uda1380_resume,
-};
-EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
-
 static int __init uda1380_modinit(void)
 {
-	return snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+	int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&uda1380_i2c_driver);
+	if (ret != 0)
+		pr_err("Failed to register UDA1380 I2C driver: %d\n", ret);
+#endif
+	return 0;
 }
 module_init(uda1380_modinit);
 
 static void __exit uda1380_exit(void)
 {
-	snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai));
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&uda1380_i2c_driver);
+#endif
 }
 module_exit(uda1380_exit);
 
diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h
index c55c17a..9cefa8a 100644
--- a/sound/soc/codecs/uda1380.h
+++ b/sound/soc/codecs/uda1380.h
@@ -72,14 +72,6 @@
 #define R22_SKIP_DCFIL	0x0002
 #define R23_AGC_EN	0x0001
 
-struct uda1380_setup_data {
-	int            i2c_bus;
-	unsigned short i2c_address;
-	int            dac_clk;
-#define UDA1380_DAC_CLK_SYSCLK 0
-#define UDA1380_DAC_CLK_WSPLL  1
-};
-
 #define UDA1380_DAI_DUPLEX	0 /* playback and capture on single DAI */
 #define UDA1380_DAI_PLAYBACK	1 /* playback DAI */
 #define UDA1380_DAI_CAPTURE	2 /* capture DAI */
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 0275321..4ded0e3 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -406,7 +406,6 @@
 static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" };
 static const char *wm8350_dacmutem[] = { "Normal", "Soft" };
 static const char *wm8350_dacmutes[] = { "Fast", "Slow" };
-static const char *wm8350_dacfilter[] = { "Normal", "Sloping" };
 static const char *wm8350_adcfilter[] = { "None", "High Pass" };
 static const char *wm8350_adchp[] = { "44.1kHz", "8kHz", "16kHz", "32kHz" };
 static const char *wm8350_lr[] = { "Left", "Right" };
@@ -416,7 +415,6 @@
 	SOC_ENUM_SINGLE(WM8350_DAC_CONTROL, 0, 4, wm8350_pol),
 	SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 14, 2, wm8350_dacmutem),
 	SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 13, 2, wm8350_dacmutes),
-	SOC_ENUM_SINGLE(WM8350_DAC_MUTE_VOLUME, 12, 2, wm8350_dacfilter),
 	SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 15, 2, wm8350_adcfilter),
 	SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 8, 4, wm8350_adchp),
 	SOC_ENUM_SINGLE(WM8350_ADC_CONTROL, 0, 4, wm8350_pol),
@@ -444,10 +442,9 @@
 				0, 255, 0, dac_pcm_tlv),
 	SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]),
 	SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]),
-	SOC_ENUM("Playback PCM Filter", wm8350_enum[4]),
-	SOC_ENUM("Capture PCM Filter", wm8350_enum[5]),
-	SOC_ENUM("Capture PCM HP Filter", wm8350_enum[6]),
-	SOC_ENUM("Capture ADC Inversion", wm8350_enum[7]),
+	SOC_ENUM("Capture PCM Filter", wm8350_enum[4]),
+	SOC_ENUM("Capture PCM HP Filter", wm8350_enum[5]),
+	SOC_ENUM("Capture ADC Inversion", wm8350_enum[6]),
 	SOC_WM8350_DOUBLE_R_TLV("Capture PCM Volume",
 				WM8350_ADC_DIGITAL_VOLUME_L,
 				WM8350_ADC_DIGITAL_VOLUME_R,
@@ -993,6 +990,7 @@
 				struct snd_soc_dai *codec_dai)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8350 *wm8350 = codec->control_data;
 	u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) &
 	    ~WM8350_AIF_WL_MASK;
 
@@ -1012,6 +1010,19 @@
 	}
 
 	wm8350_codec_write(codec, WM8350_AI_FORMATING, iface);
+
+	/* The sloping stopband filter is recommended for use with
+	 * lower sample rates to improve performance.
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (params_rate(params) < 24000)
+			wm8350_set_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
+					WM8350_DAC_SB_FILT);
+		else
+			wm8350_clear_bits(wm8350, WM8350_DAC_MUTE_VOLUME,
+					  WM8350_DAC_SB_FILT);
+	}
+
 	return 0;
 }
 
@@ -1108,7 +1119,7 @@
 	if (ret < 0)
 		return ret;
 	dev_dbg(wm8350->dev,
-		"FLL in %d FLL out %d N 0x%x K 0x%x div %d ratio %d",
+		"FLL in %u FLL out %u N 0x%x K 0x%x div %d ratio %d",
 		freq_in, freq_out, fll_div.n, fll_div.k, fll_div.div,
 		fll_div.ratio);
 
@@ -1660,6 +1671,21 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8350_codec_suspend(struct platform_device *pdev, pm_message_t m)
+{
+	return snd_soc_suspend_device(&pdev->dev);
+}
+
+static int wm8350_codec_resume(struct platform_device *pdev)
+{
+	return snd_soc_resume_device(&pdev->dev);
+}
+#else
+#define wm8350_codec_suspend NULL
+#define wm8350_codec_resume NULL
+#endif
+
 static struct platform_driver wm8350_codec_driver = {
 	.driver = {
 		   .name = "wm8350-codec",
@@ -1667,6 +1693,8 @@
 		   },
 	.probe = wm8350_codec_probe,
 	.remove = __devexit_p(wm8350_codec_remove),
+	.suspend = wm8350_codec_suspend,
+	.resume = wm8350_codec_resume,
 };
 
 static __init int wm8350_init(void)
diff --git a/sound/soc/codecs/wm8350.h b/sound/soc/codecs/wm8350.h
index d11bd92..d088eb4 100644
--- a/sound/soc/codecs/wm8350.h
+++ b/sound/soc/codecs/wm8350.h
@@ -13,6 +13,7 @@
 #define _WM8350_H
 
 #include <sound/soc.h>
+#include <linux/mfd/wm8350/audio.h>
 
 extern struct snd_soc_dai wm8350_dai;
 extern struct snd_soc_codec_device soc_codec_dev_wm8350;
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index 510efa6..b9ef4d9 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -954,7 +954,7 @@
 		factors->outdiv *= 2;
 		if (factors->outdiv > 32) {
 			dev_err(wm8400->wm8400->dev,
-				"Unsupported FLL output frequency %dHz\n",
+				"Unsupported FLL output frequency %uHz\n",
 				Fout);
 			return -EINVAL;
 		}
@@ -1003,7 +1003,7 @@
 	factors->k = K / 10;
 
 	dev_dbg(wm8400->wm8400->dev,
-		"FLL: Fref=%d Fout=%d N=%x K=%x, FRATIO=%x OUTDIV=%x\n",
+		"FLL: Fref=%u Fout=%u N=%x K=%x, FRATIO=%x OUTDIV=%x\n",
 		Fref, Fout,
 		factors->n, factors->k, factors->fratio, factors->outdiv);
 
@@ -1022,10 +1022,15 @@
 	if (freq_in == wm8400->fll_in && freq_out == wm8400->fll_out)
 		return 0;
 
-	if (freq_out != 0) {
+	if (freq_out) {
 		ret = fll_factors(wm8400, &factors, freq_in, freq_out);
 		if (ret != 0)
 			return ret;
+	} else {
+		/* Bodge GCC 4.4.0 uninitialised variable warning - it
+		 * doesn't seem capable of working out that we exit if
+		 * freq_out is 0 before any of the uses. */
+		memset(&factors, 0, sizeof(factors));
 	}
 
 	wm8400->fll_out = freq_out;
@@ -1040,7 +1045,7 @@
 	reg &= ~WM8400_FLL_OSC_ENA;
 	wm8400_write(codec, WM8400_FLL_CONTROL_1, reg);
 
-	if (freq_out == 0)
+	if (!freq_out)
 		return 0;
 
 	reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK);
@@ -1473,8 +1478,8 @@
 
 	codec = &priv->codec;
 	codec->private_data = priv;
-	codec->control_data = dev->dev.driver_data;
-	priv->wm8400 = dev->dev.driver_data;
+	codec->control_data = dev_get_drvdata(&dev->dev);
+	priv->wm8400 = dev_get_drvdata(&dev->dev);
 
 	ret = regulator_bulk_get(priv->wm8400->dev,
 				 ARRAY_SIZE(power), &power[0]);
@@ -1553,6 +1558,21 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8400_pdev_suspend(struct platform_device *pdev, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&pdev->dev);
+}
+
+static int wm8400_pdev_resume(struct platform_device *pdev)
+{
+	return snd_soc_resume_device(&pdev->dev);
+}
+#else
+#define wm8400_pdev_suspend NULL
+#define wm8400_pdev_resume NULL
+#endif
+
 static struct platform_driver wm8400_codec_driver = {
 	.driver = {
 		.name = "wm8400-codec",
@@ -1560,6 +1580,8 @@
 	},
 	.probe = wm8400_codec_probe,
 	.remove	= __exit_p(wm8400_codec_remove),
+	.suspend = wm8400_pdev_suspend,
+	.resume = wm8400_pdev_resume,
 };
 
 static int __init wm8400_codec_init(void)
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 6a4cea0..060d5d0 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -58,55 +58,7 @@
 #define WM8510_POWER1_BIASEN  0x08
 #define WM8510_POWER1_BUFIOEN 0x10
 
-/*
- * read wm8510 register cache
- */
-static inline unsigned int wm8510_read_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg)
-{
-	u16 *cache = codec->reg_cache;
-	if (reg == WM8510_RESET)
-		return 0;
-	if (reg >= WM8510_CACHEREGNUM)
-		return -1;
-	return cache[reg];
-}
-
-/*
- * write wm8510 register cache
- */
-static inline void wm8510_write_reg_cache(struct snd_soc_codec *codec,
-	u16 reg, unsigned int value)
-{
-	u16 *cache = codec->reg_cache;
-	if (reg >= WM8510_CACHEREGNUM)
-		return;
-	cache[reg] = value;
-}
-
-/*
- * write to the WM8510 register space
- */
-static int wm8510_write(struct snd_soc_codec *codec, unsigned int reg,
-	unsigned int value)
-{
-	u8 data[2];
-
-	/* data is
-	 *   D15..D9 WM8510 register offset
-	 *   D8...D0 register data
-	 */
-	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-	data[1] = value & 0x00ff;
-
-	wm8510_write_reg_cache(codec, reg, value);
-	if (codec->hw_write(codec->control_data, data, 2) == 2)
-		return 0;
-	else
-		return -EIO;
-}
-
-#define wm8510_reset(c)	wm8510_write(c, WM8510_RESET, 0)
+#define wm8510_reset(c)	snd_soc_write(c, WM8510_RESET, 0)
 
 static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" };
 static const char *wm8510_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" };
@@ -298,7 +250,7 @@
 
 	if ((Ndiv < 6) || (Ndiv > 12))
 		printk(KERN_WARNING
-			"WM8510 N value %d outwith recommended range!d\n",
+			"WM8510 N value %u outwith recommended range!d\n",
 			Ndiv);
 
 	pll_div.n = Ndiv;
@@ -327,27 +279,27 @@
 
 	if (freq_in == 0 || freq_out == 0) {
 		/* Clock CODEC directly from MCLK */
-		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
-		wm8510_write(codec, WM8510_CLOCK, reg & 0x0ff);
+		reg = snd_soc_read(codec, WM8510_CLOCK);
+		snd_soc_write(codec, WM8510_CLOCK, reg & 0x0ff);
 
 		/* Turn off PLL */
-		reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
-		wm8510_write(codec, WM8510_POWER1, reg & 0x1df);
+		reg = snd_soc_read(codec, WM8510_POWER1);
+		snd_soc_write(codec, WM8510_POWER1, reg & 0x1df);
 		return 0;
 	}
 
 	pll_factors(freq_out*4, freq_in);
 
-	wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
-	wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
-	wm8510_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
-	wm8510_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
-	reg = wm8510_read_reg_cache(codec, WM8510_POWER1);
-	wm8510_write(codec, WM8510_POWER1, reg | 0x020);
+	snd_soc_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+	snd_soc_write(codec, WM8510_PLLK1, pll_div.k >> 18);
+	snd_soc_write(codec, WM8510_PLLK2, (pll_div.k >> 9) & 0x1ff);
+	snd_soc_write(codec, WM8510_PLLK3, pll_div.k & 0x1ff);
+	reg = snd_soc_read(codec, WM8510_POWER1);
+	snd_soc_write(codec, WM8510_POWER1, reg | 0x020);
 
 	/* Run CODEC from PLL instead of MCLK */
-	reg = wm8510_read_reg_cache(codec, WM8510_CLOCK);
-	wm8510_write(codec, WM8510_CLOCK, reg | 0x100);
+	reg = snd_soc_read(codec, WM8510_CLOCK);
+	snd_soc_write(codec, WM8510_CLOCK, reg | 0x100);
 
 	return 0;
 }
@@ -363,24 +315,24 @@
 
 	switch (div_id) {
 	case WM8510_OPCLKDIV:
-		reg = wm8510_read_reg_cache(codec, WM8510_GPIO) & 0x1cf;
-		wm8510_write(codec, WM8510_GPIO, reg | div);
+		reg = snd_soc_read(codec, WM8510_GPIO) & 0x1cf;
+		snd_soc_write(codec, WM8510_GPIO, reg | div);
 		break;
 	case WM8510_MCLKDIV:
-		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x11f;
-		wm8510_write(codec, WM8510_CLOCK, reg | div);
+		reg = snd_soc_read(codec, WM8510_CLOCK) & 0x11f;
+		snd_soc_write(codec, WM8510_CLOCK, reg | div);
 		break;
 	case WM8510_ADCCLK:
-		reg = wm8510_read_reg_cache(codec, WM8510_ADC) & 0x1f7;
-		wm8510_write(codec, WM8510_ADC, reg | div);
+		reg = snd_soc_read(codec, WM8510_ADC) & 0x1f7;
+		snd_soc_write(codec, WM8510_ADC, reg | div);
 		break;
 	case WM8510_DACCLK:
-		reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0x1f7;
-		wm8510_write(codec, WM8510_DAC, reg | div);
+		reg = snd_soc_read(codec, WM8510_DAC) & 0x1f7;
+		snd_soc_write(codec, WM8510_DAC, reg | div);
 		break;
 	case WM8510_BCLKDIV:
-		reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1e3;
-		wm8510_write(codec, WM8510_CLOCK, reg | div);
+		reg = snd_soc_read(codec, WM8510_CLOCK) & 0x1e3;
+		snd_soc_write(codec, WM8510_CLOCK, reg | div);
 		break;
 	default:
 		return -EINVAL;
@@ -394,7 +346,7 @@
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 iface = 0;
-	u16 clk = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1fe;
+	u16 clk = snd_soc_read(codec, WM8510_CLOCK) & 0x1fe;
 
 	/* set master/slave audio interface */
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -441,8 +393,8 @@
 		return -EINVAL;
 	}
 
-	wm8510_write(codec, WM8510_IFACE, iface);
-	wm8510_write(codec, WM8510_CLOCK, clk);
+	snd_soc_write(codec, WM8510_IFACE, iface);
+	snd_soc_write(codec, WM8510_CLOCK, clk);
 	return 0;
 }
 
@@ -453,8 +405,8 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
-	u16 iface = wm8510_read_reg_cache(codec, WM8510_IFACE) & 0x19f;
-	u16 adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
+	u16 iface = snd_soc_read(codec, WM8510_IFACE) & 0x19f;
+	u16 adn = snd_soc_read(codec, WM8510_ADD) & 0x1f1;
 
 	/* bit size */
 	switch (params_format(params)) {
@@ -493,20 +445,20 @@
 		break;
 	}
 
-	wm8510_write(codec, WM8510_IFACE, iface);
-	wm8510_write(codec, WM8510_ADD, adn);
+	snd_soc_write(codec, WM8510_IFACE, iface);
+	snd_soc_write(codec, WM8510_ADD, adn);
 	return 0;
 }
 
 static int wm8510_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
-	u16 mute_reg = wm8510_read_reg_cache(codec, WM8510_DAC) & 0xffbf;
+	u16 mute_reg = snd_soc_read(codec, WM8510_DAC) & 0xffbf;
 
 	if (mute)
-		wm8510_write(codec, WM8510_DAC, mute_reg | 0x40);
+		snd_soc_write(codec, WM8510_DAC, mute_reg | 0x40);
 	else
-		wm8510_write(codec, WM8510_DAC, mute_reg);
+		snd_soc_write(codec, WM8510_DAC, mute_reg);
 	return 0;
 }
 
@@ -514,13 +466,13 @@
 static int wm8510_set_bias_level(struct snd_soc_codec *codec,
 	enum snd_soc_bias_level level)
 {
-	u16 power1 = wm8510_read_reg_cache(codec, WM8510_POWER1) & ~0x3;
+	u16 power1 = snd_soc_read(codec, WM8510_POWER1) & ~0x3;
 
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 	case SND_SOC_BIAS_PREPARE:
 		power1 |= 0x1;  /* VMID 50k */
-		wm8510_write(codec, WM8510_POWER1, power1);
+		snd_soc_write(codec, WM8510_POWER1, power1);
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
@@ -528,18 +480,18 @@
 
 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
 			/* Initial cap charge at VMID 5k */
-			wm8510_write(codec, WM8510_POWER1, power1 | 0x3);
+			snd_soc_write(codec, WM8510_POWER1, power1 | 0x3);
 			mdelay(100);
 		}
 
 		power1 |= 0x2;  /* VMID 500k */
-		wm8510_write(codec, WM8510_POWER1, power1);
+		snd_soc_write(codec, WM8510_POWER1, power1);
 		break;
 
 	case SND_SOC_BIAS_OFF:
-		wm8510_write(codec, WM8510_POWER1, 0);
-		wm8510_write(codec, WM8510_POWER2, 0);
-		wm8510_write(codec, WM8510_POWER3, 0);
+		snd_soc_write(codec, WM8510_POWER1, 0);
+		snd_soc_write(codec, WM8510_POWER2, 0);
+		snd_soc_write(codec, WM8510_POWER3, 0);
 		break;
 	}
 
@@ -577,6 +529,7 @@
 		.rates = WM8510_RATES,
 		.formats = WM8510_FORMATS,},
 	.ops = &wm8510_dai_ops,
+	.symmetric_rates = 1,
 };
 EXPORT_SYMBOL_GPL(wm8510_dai);
 
@@ -612,15 +565,14 @@
  * initialise the WM8510 driver
  * register the mixer and dsp interfaces with the kernel
  */
-static int wm8510_init(struct snd_soc_device *socdev)
+static int wm8510_init(struct snd_soc_device *socdev,
+		       enum snd_soc_control_type control)
 {
 	struct snd_soc_codec *codec = socdev->card->codec;
 	int ret = 0;
 
 	codec->name = "WM8510";
 	codec->owner = THIS_MODULE;
-	codec->read = wm8510_read_reg_cache;
-	codec->write = wm8510_write;
 	codec->set_bias_level = wm8510_set_bias_level;
 	codec->dai = &wm8510_dai;
 	codec->num_dai = 1;
@@ -630,13 +582,20 @@
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
 
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8510: failed to set cache I/O: %d\n",
+		       ret);
+		goto err;
+	}
+
 	wm8510_reset(codec);
 
 	/* register pcms */
 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 	if (ret < 0) {
 		printk(KERN_ERR "wm8510: failed to create pcms\n");
-		goto pcm_err;
+		goto err;
 	}
 
 	/* power on device */
@@ -655,7 +614,7 @@
 card_err:
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
-pcm_err:
+err:
 	kfree(codec->reg_cache);
 	return ret;
 }
@@ -678,7 +637,7 @@
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	ret = wm8510_init(socdev);
+	ret = wm8510_init(socdev, SND_SOC_I2C);
 	if (ret < 0)
 		pr_err("failed to initialise WM8510\n");
 
@@ -758,7 +717,7 @@
 
 	codec->control_data = spi;
 
-	ret = wm8510_init(socdev);
+	ret = wm8510_init(socdev, SND_SOC_SPI);
 	if (ret < 0)
 		dev_err(&spi->dev, "failed to initialise WM8510\n");
 
@@ -779,30 +738,6 @@
 	.probe		= wm8510_spi_probe,
 	.remove		= __devexit_p(wm8510_spi_remove),
 };
-
-static int wm8510_spi_write(struct spi_device *spi, const char *data, int len)
-{
-	struct spi_transfer t;
-	struct spi_message m;
-	u8 msg[2];
-
-	if (len <= 0)
-		return 0;
-
-	msg[0] = data[0];
-	msg[1] = data[1];
-
-	spi_message_init(&m);
-	memset(&t, 0, (sizeof t));
-
-	t.tx_buf = &msg[0];
-	t.len = len;
-
-	spi_message_add_tail(&t, &m);
-	spi_sync(spi, &m);
-
-	return len;
-}
 #endif /* CONFIG_SPI_MASTER */
 
 static int wm8510_probe(struct platform_device *pdev)
@@ -827,13 +762,11 @@
 	wm8510_socdev = socdev;
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		codec->hw_write = (hw_write_t)i2c_master_send;
 		ret = wm8510_add_i2c_device(pdev, setup);
 	}
 #endif
 #if defined(CONFIG_SPI_MASTER)
 	if (setup->spi) {
-		codec->hw_write = (hw_write_t)wm8510_spi_write;
 		ret = spi_register_driver(&wm8510_spi_driver);
 		if (ret != 0)
 			printk(KERN_ERR "can't add spi driver");
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
new file mode 100644
index 0000000..25870a4
--- /dev/null
+++ b/sound/soc/codecs/wm8523.c
@@ -0,0 +1,699 @@
+/*
+ * wm8523.c  --  WM8523 ALSA SoC Audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8523.h"
+
+static struct snd_soc_codec *wm8523_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8523;
+
+#define WM8523_NUM_SUPPLIES 2
+static const char *wm8523_supply_names[WM8523_NUM_SUPPLIES] = {
+	"AVDD",
+	"LINEVDD",
+};
+
+#define WM8523_NUM_RATES 7
+
+/* codec private data */
+struct wm8523_priv {
+	struct snd_soc_codec codec;
+	u16 reg_cache[WM8523_REGISTER_COUNT];
+	struct regulator_bulk_data supplies[WM8523_NUM_SUPPLIES];
+	unsigned int sysclk;
+	unsigned int rate_constraint_list[WM8523_NUM_RATES];
+	struct snd_pcm_hw_constraint_list rate_constraint;
+};
+
+static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = {
+	0x8523,     /* R0 - DEVICE_ID */
+	0x0001,     /* R1 - REVISION */
+	0x0000,     /* R2 - PSCTRL1 */
+	0x1812,     /* R3 - AIF_CTRL1 */
+	0x0000,     /* R4 - AIF_CTRL2 */
+	0x0001,     /* R5 - DAC_CTRL3 */
+	0x0190,     /* R6 - DAC_GAINL */
+	0x0190,     /* R7 - DAC_GAINR */
+	0x0000,     /* R8 - ZERO_DETECT */
+};
+
+static int wm8523_volatile_register(unsigned int reg)
+{
+	switch (reg) {
+	case WM8523_DEVICE_ID:
+	case WM8523_REVISION:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static int wm8523_reset(struct snd_soc_codec *codec)
+{
+	return snd_soc_write(codec, WM8523_DEVICE_ID, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -10000, 25, 0);
+
+static const char *wm8523_zd_count_text[] = {
+	"1024",
+	"2048",
+};
+
+static const struct soc_enum wm8523_zc_count =
+	SOC_ENUM_SINGLE(WM8523_ZERO_DETECT, 0, 2, wm8523_zd_count_text);
+
+static const struct snd_kcontrol_new wm8523_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Playback Volume", WM8523_DAC_GAINL, WM8523_DAC_GAINR,
+		 0, 448, 0, dac_tlv),
+SOC_SINGLE("ZC Switch", WM8523_DAC_CTRL3, 4, 1, 0),
+SOC_SINGLE("Playback Deemphasis Switch", WM8523_AIF_CTRL1, 8, 1, 0),
+SOC_DOUBLE("Playback Switch", WM8523_DAC_CTRL3, 2, 3, 1, 1),
+SOC_SINGLE("Volume Ramp Up Switch", WM8523_DAC_CTRL3, 1, 1, 0),
+SOC_SINGLE("Volume Ramp Down Switch", WM8523_DAC_CTRL3, 0, 1, 0),
+SOC_ENUM("Zero Detect Count", wm8523_zc_count),
+};
+
+static const struct snd_soc_dapm_widget wm8523_dapm_widgets[] = {
+SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_OUTPUT("LINEVOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEVOUTR"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	{ "LINEVOUTL", NULL, "DAC" },
+	{ "LINEVOUTR", NULL, "DAC" },
+};
+
+static int wm8523_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8523_dapm_widgets,
+				  ARRAY_SIZE(wm8523_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+static struct {
+	int value;
+	int ratio;
+} lrclk_ratios[WM8523_NUM_RATES] = {
+	{ 1, 128 },
+	{ 2, 192 },
+	{ 3, 256 },
+	{ 4, 384 },
+	{ 5, 512 },
+	{ 6, 768 },
+	{ 7, 1152 },
+};
+
+static int wm8523_startup(struct snd_pcm_substream *substream,
+			  struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8523_priv *wm8523 = codec->private_data;
+
+	/* The set of sample rates that can be supported depends on the
+	 * MCLK supplied to the CODEC - enforce this.
+	 */
+	if (!wm8523->sysclk) {
+		dev_err(codec->dev,
+			"No MCLK configured, call set_sysclk() on init\n");
+		return -EINVAL;
+	}
+
+	return 0;
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_RATE,
+				   &wm8523->rate_constraint);
+
+	return 0;
+}
+
+static int wm8523_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct wm8523_priv *wm8523 = codec->private_data;
+	int i;
+	u16 aifctrl1 = snd_soc_read(codec, WM8523_AIF_CTRL1);
+	u16 aifctrl2 = snd_soc_read(codec, WM8523_AIF_CTRL2);
+
+	/* Find a supported LRCLK ratio */
+	for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+		if (wm8523->sysclk / params_rate(params) ==
+		    lrclk_ratios[i].ratio)
+			break;
+	}
+
+	/* Should never happen, should be handled by constraints */
+	if (i == ARRAY_SIZE(lrclk_ratios)) {
+		dev_err(codec->dev, "MCLK/fs ratio %d unsupported\n",
+			wm8523->sysclk / params_rate(params));
+		return -EINVAL;
+	}
+
+	aifctrl2 &= ~WM8523_SR_MASK;
+	aifctrl2 |= lrclk_ratios[i].value;
+
+	aifctrl1 &= ~WM8523_WL_MASK;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		aifctrl1 |= 0x8;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		aifctrl1 |= 0x10;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		aifctrl1 |= 0x18;
+		break;
+	}
+
+	snd_soc_write(codec, WM8523_AIF_CTRL1, aifctrl1);
+	snd_soc_write(codec, WM8523_AIF_CTRL2, aifctrl2);
+
+	return 0;
+}
+
+static int wm8523_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8523_priv *wm8523 = codec->private_data;
+	unsigned int val;
+	int i;
+
+	wm8523->sysclk = freq;
+
+	wm8523->rate_constraint.count = 0;
+	for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
+		val = freq / lrclk_ratios[i].ratio;
+		/* Check that it's a standard rate since core can't
+		 * cope with others and having the odd rates confuses
+		 * constraint matching.
+		 */
+		switch (val) {
+		case 8000:
+		case 11025:
+		case 16000:
+		case 22050:
+		case 32000:
+		case 44100:
+		case 48000:
+		case 64000:
+		case 88200:
+		case 96000:
+		case 176400:
+		case 192000:
+			dev_dbg(codec->dev, "Supported sample rate: %dHz\n",
+				val);
+			wm8523->rate_constraint_list[i] = val;
+			wm8523->rate_constraint.count++;
+			break;
+		default:
+			dev_dbg(codec->dev, "Skipping sample rate: %dHz\n",
+				val);
+		}
+	}
+
+	/* Need at least one supported rate... */
+	if (wm8523->rate_constraint.count == 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+
+static int wm8523_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 aifctrl1 = snd_soc_read(codec, WM8523_AIF_CTRL1);
+
+	aifctrl1 &= ~(WM8523_BCLK_INV_MASK | WM8523_LRCLK_INV_MASK |
+		      WM8523_FMT_MASK | WM8523_AIF_MSTR_MASK);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		aifctrl1 |= WM8523_AIF_MSTR;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		aifctrl1 |= 0x0002;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		aifctrl1 |= 0x0001;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		aifctrl1 |= 0x0003;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		aifctrl1 |= 0x0023;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		aifctrl1 |= WM8523_BCLK_INV | WM8523_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		aifctrl1 |= WM8523_BCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		aifctrl1 |= WM8523_LRCLK_INV;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, WM8523_AIF_CTRL1, aifctrl1);
+
+	return 0;
+}
+
+static int wm8523_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	struct wm8523_priv *wm8523 = codec->private_data;
+	int ret, i;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		/* Full power on */
+		snd_soc_update_bits(codec, WM8523_PSCTRL1,
+				    WM8523_SYS_ENA_MASK, 3);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
+						    wm8523->supplies);
+			if (ret != 0) {
+				dev_err(codec->dev,
+					"Failed to enable supplies: %d\n",
+					ret);
+				return ret;
+			}
+
+			/* Initial power up */
+			snd_soc_update_bits(codec, WM8523_PSCTRL1,
+					    WM8523_SYS_ENA_MASK, 1);
+
+			/* Sync back default/cached values */
+			for (i = WM8523_AIF_CTRL1;
+			     i < WM8523_MAX_REGISTER; i++)
+				snd_soc_write(codec, i, wm8523->reg_cache[i]);
+
+
+			msleep(100);
+		}
+
+		/* Power up to mute */
+		snd_soc_update_bits(codec, WM8523_PSCTRL1,
+				    WM8523_SYS_ENA_MASK, 2);
+
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		/* The chip runs through the power down sequence for us. */
+		snd_soc_update_bits(codec, WM8523_PSCTRL1,
+				    WM8523_SYS_ENA_MASK, 0);
+		msleep(100);
+
+		regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies),
+				       wm8523->supplies);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8523_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8523_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm8523_dai_ops = {
+	.startup	= wm8523_startup,
+	.hw_params	= wm8523_hw_params,
+	.set_sysclk	= wm8523_set_dai_sysclk,
+	.set_fmt	= wm8523_set_dai_fmt,
+};
+
+struct snd_soc_dai wm8523_dai = {
+	.name = "WM8523",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,  /* Mono modes not yet supported */
+		.channels_max = 2,
+		.rates = WM8523_RATES,
+		.formats = WM8523_FORMATS,
+	},
+	.ops = &wm8523_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8523_dai);
+
+#ifdef CONFIG_PM
+static int wm8523_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm8523_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8523_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+#else
+#define wm8523_suspend NULL
+#define wm8523_resume NULL
+#endif
+
+static int wm8523_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8523_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8523_codec;
+	codec = wm8523_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, wm8523_snd_controls,
+			     ARRAY_SIZE(wm8523_snd_controls));
+	wm8523_add_widgets(codec);
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+static int wm8523_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8523 = {
+	.probe = 	wm8523_probe,
+	.remove = 	wm8523_remove,
+	.suspend = 	wm8523_suspend,
+	.resume =	wm8523_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8523);
+
+static int wm8523_register(struct wm8523_priv *wm8523,
+			   enum snd_soc_control_type control)
+{
+	int ret;
+	struct snd_soc_codec *codec = &wm8523->codec;
+	int i;
+
+	if (wm8523_codec) {
+		dev_err(codec->dev, "Another WM8523 is registered\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8523;
+	codec->name = "WM8523";
+	codec->owner = THIS_MODULE;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8523_set_bias_level;
+	codec->dai = &wm8523_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = WM8523_REGISTER_COUNT;
+	codec->reg_cache = &wm8523->reg_cache;
+	codec->volatile_register = wm8523_volatile_register;
+
+	wm8523->rate_constraint.list = &wm8523->rate_constraint_list[0];
+	wm8523->rate_constraint.count =
+		ARRAY_SIZE(wm8523->rate_constraint_list);
+
+	memcpy(codec->reg_cache, wm8523_reg, sizeof(wm8523_reg));
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, control);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(wm8523->supplies); i++)
+		wm8523->supplies[i].supply = wm8523_supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8523->supplies),
+				 wm8523->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+		goto err;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8523->supplies),
+				    wm8523->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+		goto err_get;
+	}
+
+	ret = snd_soc_read(codec, WM8523_DEVICE_ID);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to read ID register\n");
+		goto err_enable;
+	}
+	if (ret != wm8523_reg[WM8523_DEVICE_ID]) {
+		dev_err(codec->dev, "Device is not a WM8523, ID is %x\n", ret);
+		ret = -EINVAL;
+		goto err_enable;
+	}
+
+	ret = snd_soc_read(codec, WM8523_REVISION);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to read revision register\n");
+		goto err_enable;
+	}
+	dev_info(codec->dev, "revision %c\n",
+		 (ret & WM8523_CHIP_REV_MASK) + 'A');
+
+	ret = wm8523_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		goto err_enable;
+	}
+
+	wm8523_dai.dev = codec->dev;
+
+	/* Change some default settings - latch VU and enable ZC */
+	wm8523->reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU;
+	wm8523->reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC;
+
+	wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* Bias level configuration will have done an extra enable */
+	regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+
+	wm8523_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_register_dai(&wm8523_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		return ret;
+	}
+
+	return 0;
+
+err_enable:
+	regulator_bulk_disable(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+err_get:
+	regulator_bulk_free(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+err:
+	kfree(wm8523);
+	return ret;
+}
+
+static void wm8523_unregister(struct wm8523_priv *wm8523)
+{
+	wm8523_set_bias_level(&wm8523->codec, SND_SOC_BIAS_OFF);
+	regulator_bulk_free(ARRAY_SIZE(wm8523->supplies), wm8523->supplies);
+	snd_soc_unregister_dai(&wm8523_dai);
+	snd_soc_unregister_codec(&wm8523->codec);
+	kfree(wm8523);
+	wm8523_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8523_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8523_priv *wm8523;
+	struct snd_soc_codec *codec;
+
+	wm8523 = kzalloc(sizeof(struct wm8523_priv), GFP_KERNEL);
+	if (wm8523 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8523->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(i2c, wm8523);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+
+	return wm8523_register(wm8523, SND_SOC_I2C);
+}
+
+static __devexit int wm8523_i2c_remove(struct i2c_client *client)
+{
+	struct wm8523_priv *wm8523 = i2c_get_clientdata(client);
+	wm8523_unregister(wm8523);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8523_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&i2c->dev);
+}
+
+static int wm8523_i2c_resume(struct i2c_client *i2c)
+{
+	return snd_soc_resume_device(&i2c->dev);
+}
+#else
+#define wm8523_i2c_suspend NULL
+#define wm8523_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm8523_i2c_id[] = {
+	{ "wm8523", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id);
+
+static struct i2c_driver wm8523_i2c_driver = {
+	.driver = {
+		.name = "WM8523",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm8523_i2c_probe,
+	.remove =   __devexit_p(wm8523_i2c_remove),
+	.suspend =  wm8523_i2c_suspend,
+	.resume =   wm8523_i2c_resume,
+	.id_table = wm8523_i2c_id,
+};
+#endif
+
+static int __init wm8523_modinit(void)
+{
+	int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&wm8523_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8523 I2C driver: %d\n",
+		       ret);
+	}
+#endif
+	return 0;
+}
+module_init(wm8523_modinit);
+
+static void __exit wm8523_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8523_i2c_driver);
+#endif
+}
+module_exit(wm8523_exit);
+
+MODULE_DESCRIPTION("ASoC WM8523 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8523.h b/sound/soc/codecs/wm8523.h
new file mode 100644
index 0000000..1aa9ce3
--- /dev/null
+++ b/sound/soc/codecs/wm8523.h
@@ -0,0 +1,160 @@
+/*
+ * wm8523.h  --  WM8423 ASoC driver
+ *
+ * Copyright 2009 Wolfson Microelectronics, plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * Based on wm8753.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8523_H
+#define _WM8523_H
+
+/*
+ * Register values.
+ */
+#define WM8523_DEVICE_ID                        0x00
+#define WM8523_REVISION                         0x01
+#define WM8523_PSCTRL1                          0x02
+#define WM8523_AIF_CTRL1                        0x03
+#define WM8523_AIF_CTRL2                        0x04
+#define WM8523_DAC_CTRL3                        0x05
+#define WM8523_DAC_GAINL                        0x06
+#define WM8523_DAC_GAINR                        0x07
+#define WM8523_ZERO_DETECT                      0x08
+
+#define WM8523_REGISTER_COUNT                   9
+#define WM8523_MAX_REGISTER                     0x08
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - DEVICE_ID
+ */
+#define WM8523_CHIP_ID_MASK                     0xFFFF  /* CHIP_ID - [15:0] */
+#define WM8523_CHIP_ID_SHIFT                         0  /* CHIP_ID - [15:0] */
+#define WM8523_CHIP_ID_WIDTH                        16  /* CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - REVISION
+ */
+#define WM8523_CHIP_REV_MASK                    0x0007  /* CHIP_REV - [2:0] */
+#define WM8523_CHIP_REV_SHIFT                        0  /* CHIP_REV - [2:0] */
+#define WM8523_CHIP_REV_WIDTH                        3  /* CHIP_REV - [2:0] */
+
+/*
+ * R2 (0x02) - PSCTRL1
+ */
+#define WM8523_SYS_ENA_MASK                     0x0003  /* SYS_ENA - [1:0] */
+#define WM8523_SYS_ENA_SHIFT                         0  /* SYS_ENA - [1:0] */
+#define WM8523_SYS_ENA_WIDTH                         2  /* SYS_ENA - [1:0] */
+
+/*
+ * R3 (0x03) - AIF_CTRL1
+ */
+#define WM8523_TDM_MODE_MASK                    0x1800  /* TDM_MODE - [12:11] */
+#define WM8523_TDM_MODE_SHIFT                       11  /* TDM_MODE - [12:11] */
+#define WM8523_TDM_MODE_WIDTH                        2  /* TDM_MODE - [12:11] */
+#define WM8523_TDM_SLOT_MASK                    0x0600  /* TDM_SLOT - [10:9] */
+#define WM8523_TDM_SLOT_SHIFT                        9  /* TDM_SLOT - [10:9] */
+#define WM8523_TDM_SLOT_WIDTH                        2  /* TDM_SLOT - [10:9] */
+#define WM8523_DEEMPH                           0x0100  /* DEEMPH  */
+#define WM8523_DEEMPH_MASK                      0x0100  /* DEEMPH  */
+#define WM8523_DEEMPH_SHIFT                          8  /* DEEMPH  */
+#define WM8523_DEEMPH_WIDTH                          1  /* DEEMPH  */
+#define WM8523_AIF_MSTR                         0x0080  /* AIF_MSTR  */
+#define WM8523_AIF_MSTR_MASK                    0x0080  /* AIF_MSTR  */
+#define WM8523_AIF_MSTR_SHIFT                        7  /* AIF_MSTR  */
+#define WM8523_AIF_MSTR_WIDTH                        1  /* AIF_MSTR  */
+#define WM8523_LRCLK_INV                        0x0040  /* LRCLK_INV  */
+#define WM8523_LRCLK_INV_MASK                   0x0040  /* LRCLK_INV  */
+#define WM8523_LRCLK_INV_SHIFT                       6  /* LRCLK_INV  */
+#define WM8523_LRCLK_INV_WIDTH                       1  /* LRCLK_INV  */
+#define WM8523_BCLK_INV                         0x0020  /* BCLK_INV  */
+#define WM8523_BCLK_INV_MASK                    0x0020  /* BCLK_INV  */
+#define WM8523_BCLK_INV_SHIFT                        5  /* BCLK_INV  */
+#define WM8523_BCLK_INV_WIDTH                        1  /* BCLK_INV  */
+#define WM8523_WL_MASK                          0x0018  /* WL - [4:3] */
+#define WM8523_WL_SHIFT                              3  /* WL - [4:3] */
+#define WM8523_WL_WIDTH                              2  /* WL - [4:3] */
+#define WM8523_FMT_MASK                         0x0007  /* FMT - [2:0] */
+#define WM8523_FMT_SHIFT                             0  /* FMT - [2:0] */
+#define WM8523_FMT_WIDTH                             3  /* FMT - [2:0] */
+
+/*
+ * R4 (0x04) - AIF_CTRL2
+ */
+#define WM8523_DAC_OP_MUX_MASK                  0x00C0  /* DAC_OP_MUX - [7:6] */
+#define WM8523_DAC_OP_MUX_SHIFT                      6  /* DAC_OP_MUX - [7:6] */
+#define WM8523_DAC_OP_MUX_WIDTH                      2  /* DAC_OP_MUX - [7:6] */
+#define WM8523_BCLKDIV_MASK                     0x0038  /* BCLKDIV - [5:3] */
+#define WM8523_BCLKDIV_SHIFT                         3  /* BCLKDIV - [5:3] */
+#define WM8523_BCLKDIV_WIDTH                         3  /* BCLKDIV - [5:3] */
+#define WM8523_SR_MASK                          0x0007  /* SR - [2:0] */
+#define WM8523_SR_SHIFT                              0  /* SR - [2:0] */
+#define WM8523_SR_WIDTH                              3  /* SR - [2:0] */
+
+/*
+ * R5 (0x05) - DAC_CTRL3
+ */
+#define WM8523_ZC                               0x0010  /* ZC  */
+#define WM8523_ZC_MASK                          0x0010  /* ZC  */
+#define WM8523_ZC_SHIFT                              4  /* ZC  */
+#define WM8523_ZC_WIDTH                              1  /* ZC  */
+#define WM8523_DACR                             0x0008  /* DACR  */
+#define WM8523_DACR_MASK                        0x0008  /* DACR  */
+#define WM8523_DACR_SHIFT                            3  /* DACR  */
+#define WM8523_DACR_WIDTH                            1  /* DACR  */
+#define WM8523_DACL                             0x0004  /* DACL  */
+#define WM8523_DACL_MASK                        0x0004  /* DACL  */
+#define WM8523_DACL_SHIFT                            2  /* DACL  */
+#define WM8523_DACL_WIDTH                            1  /* DACL  */
+#define WM8523_VOL_UP_RAMP                      0x0002  /* VOL_UP_RAMP  */
+#define WM8523_VOL_UP_RAMP_MASK                 0x0002  /* VOL_UP_RAMP  */
+#define WM8523_VOL_UP_RAMP_SHIFT                     1  /* VOL_UP_RAMP  */
+#define WM8523_VOL_UP_RAMP_WIDTH                     1  /* VOL_UP_RAMP  */
+#define WM8523_VOL_DOWN_RAMP                    0x0001  /* VOL_DOWN_RAMP  */
+#define WM8523_VOL_DOWN_RAMP_MASK               0x0001  /* VOL_DOWN_RAMP  */
+#define WM8523_VOL_DOWN_RAMP_SHIFT                   0  /* VOL_DOWN_RAMP  */
+#define WM8523_VOL_DOWN_RAMP_WIDTH                   1  /* VOL_DOWN_RAMP  */
+
+/*
+ * R6 (0x06) - DAC_GAINL
+ */
+#define WM8523_DACL_VU                          0x0200  /* DACL_VU  */
+#define WM8523_DACL_VU_MASK                     0x0200  /* DACL_VU  */
+#define WM8523_DACL_VU_SHIFT                         9  /* DACL_VU  */
+#define WM8523_DACL_VU_WIDTH                         1  /* DACL_VU  */
+#define WM8523_DACL_VOL_MASK                    0x01FF  /* DACL_VOL - [8:0] */
+#define WM8523_DACL_VOL_SHIFT                        0  /* DACL_VOL - [8:0] */
+#define WM8523_DACL_VOL_WIDTH                        9  /* DACL_VOL - [8:0] */
+
+/*
+ * R7 (0x07) - DAC_GAINR
+ */
+#define WM8523_DACR_VU                          0x0200  /* DACR_VU  */
+#define WM8523_DACR_VU_MASK                     0x0200  /* DACR_VU  */
+#define WM8523_DACR_VU_SHIFT                         9  /* DACR_VU  */
+#define WM8523_DACR_VU_WIDTH                         1  /* DACR_VU  */
+#define WM8523_DACR_VOL_MASK                    0x01FF  /* DACR_VOL - [8:0] */
+#define WM8523_DACR_VOL_SHIFT                        0  /* DACR_VOL - [8:0] */
+#define WM8523_DACR_VOL_WIDTH                        9  /* DACR_VOL - [8:0] */
+
+/*
+ * R8 (0x08) - ZERO_DETECT
+ */
+#define WM8523_ZD_COUNT_MASK                    0x0003  /* ZD_COUNT - [1:0] */
+#define WM8523_ZD_COUNT_SHIFT                        0  /* ZD_COUNT - [1:0] */
+#define WM8523_ZD_COUNT_WIDTH                        2  /* ZD_COUNT - [1:0] */
+
+extern struct snd_soc_dai wm8523_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8523;
+
+#endif
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 9f6be3d..d547347 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -24,6 +24,8 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -187,82 +189,22 @@
 	unsigned int out;
 };
 
+#define WM8580_NUM_SUPPLIES 3
+static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = {
+	"AVDD",
+	"DVDD",
+	"PVDD",
+};
+
 /* codec private data */
 struct wm8580_priv {
 	struct snd_soc_codec codec;
+	struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES];
 	u16 reg_cache[WM8580_MAX_REGISTER + 1];
 	struct pll_state a;
 	struct pll_state b;
 };
 
-
-/*
- * read wm8580 register cache
- */
-static inline unsigned int wm8580_read_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg)
-{
-	u16 *cache = codec->reg_cache;
-	BUG_ON(reg >= ARRAY_SIZE(wm8580_reg));
-	return cache[reg];
-}
-
-/*
- * write wm8580 register cache
- */
-static inline void wm8580_write_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg, unsigned int value)
-{
-	u16 *cache = codec->reg_cache;
-
-	cache[reg] = value;
-}
-
-/*
- * write to the WM8580 register space
- */
-static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg,
-	unsigned int value)
-{
-	u8 data[2];
-
-	BUG_ON(reg >= ARRAY_SIZE(wm8580_reg));
-
-	/* Registers are 9 bits wide */
-	value &= 0x1ff;
-
-	switch (reg) {
-	case WM8580_RESET:
-		/* Uncached */
-		break;
-	default:
-		if (value == wm8580_read_reg_cache(codec, reg))
-			return 0;
-	}
-
-	/* data is
-	 *   D15..D9 WM8580 register offset
-	 *   D8...D0 register data
-	 */
-	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-	data[1] = value & 0x00ff;
-
-	wm8580_write_reg_cache(codec, reg, value);
-	if (codec->hw_write(codec->control_data, data, 2) == 2)
-		return 0;
-	else
-		return -EIO;
-}
-
-static inline unsigned int wm8580_read(struct snd_soc_codec *codec,
-				       unsigned int reg)
-{
-	switch (reg) {
-	default:
-		return wm8580_read_reg_cache(codec, reg);
-	}
-}
-
 static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
 
 static int wm8580_out_vu(struct snd_kcontrol *kcontrol,
@@ -271,25 +213,22 @@
 	struct soc_mixer_control *mc =
 		(struct soc_mixer_control *)kcontrol->private_value;
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	u16 *reg_cache = codec->reg_cache;
 	unsigned int reg = mc->reg;
 	unsigned int reg2 = mc->rreg;
 	int ret;
-	u16 val;
 
 	/* Clear the register cache so we write without VU set */
-	wm8580_write_reg_cache(codec, reg, 0);
-	wm8580_write_reg_cache(codec, reg2, 0);
+	reg_cache[reg] = 0;
+	reg_cache[reg2] = 0;
 
 	ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
 	if (ret < 0)
 		return ret;
 
 	/* Now write again with the volume update bit set */
-	val = wm8580_read_reg_cache(codec, reg);
-	wm8580_write(codec, reg, val | 0x0100);
-
-	val = wm8580_read_reg_cache(codec, reg2);
-	wm8580_write(codec, reg2, val | 0x0100);
+	snd_soc_update_bits(codec, reg, 0x100, 0x100);
+	snd_soc_update_bits(codec, reg2, 0x100, 0x100);
 
 	return 0;
 }
@@ -415,7 +354,7 @@
 	unsigned int K, Ndiv, Nmod;
 	int i;
 
-	pr_debug("wm8580: PLL %dHz->%dHz\n", source, target);
+	pr_debug("wm8580: PLL %uHz->%uHz\n", source, target);
 
 	/* Scale the output frequency up; the PLL should run in the
 	 * region of 90-100MHz.
@@ -447,7 +386,7 @@
 
 	if ((Ndiv < 5) || (Ndiv > 13)) {
 		printk(KERN_ERR
-			"WM8580 N=%d outside supported range\n", Ndiv);
+			"WM8580 N=%u outside supported range\n", Ndiv);
 		return -EINVAL;
 	}
 
@@ -512,27 +451,27 @@
 	/* Always disable the PLL - it is not safe to leave it running
 	 * while reprogramming it.
 	 */
-	reg = wm8580_read(codec, WM8580_PWRDN2);
-	wm8580_write(codec, WM8580_PWRDN2, reg | pwr_mask);
+	reg = snd_soc_read(codec, WM8580_PWRDN2);
+	snd_soc_write(codec, WM8580_PWRDN2, reg | pwr_mask);
 
 	if (!freq_in || !freq_out)
 		return 0;
 
-	wm8580_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff);
-	wm8580_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0xff);
-	wm8580_write(codec, WM8580_PLLA3 + offset,
+	snd_soc_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff);
+	snd_soc_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0xff);
+	snd_soc_write(codec, WM8580_PLLA3 + offset,
 		     (pll_div.k >> 18 & 0xf) | (pll_div.n << 4));
 
-	reg = wm8580_read(codec, WM8580_PLLA4 + offset);
+	reg = snd_soc_read(codec, WM8580_PLLA4 + offset);
 	reg &= ~0x3f;
 	reg |= pll_div.prescale | pll_div.postscale << 1 |
 		pll_div.freqmode << 3;
 
-	wm8580_write(codec, WM8580_PLLA4 + offset, reg);
+	snd_soc_write(codec, WM8580_PLLA4 + offset, reg);
 
 	/* All done, turn it on */
-	reg = wm8580_read(codec, WM8580_PWRDN2);
-	wm8580_write(codec, WM8580_PWRDN2, reg & ~pwr_mask);
+	reg = snd_soc_read(codec, WM8580_PWRDN2);
+	snd_soc_write(codec, WM8580_PWRDN2, reg & ~pwr_mask);
 
 	return 0;
 }
@@ -547,7 +486,7 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
-	u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->id);
+	u16 paifb = snd_soc_read(codec, WM8580_PAIF3 + dai->id);
 
 	paifb &= ~WM8580_AIF_LENGTH_MASK;
 	/* bit size */
@@ -567,7 +506,7 @@
 		return -EINVAL;
 	}
 
-	wm8580_write(codec, WM8580_PAIF3 + dai->id, paifb);
+	snd_soc_write(codec, WM8580_PAIF3 + dai->id, paifb);
 	return 0;
 }
 
@@ -579,8 +518,8 @@
 	unsigned int aifb;
 	int can_invert_lrclk;
 
-	aifa = wm8580_read(codec, WM8580_PAIF1 + codec_dai->id);
-	aifb = wm8580_read(codec, WM8580_PAIF3 + codec_dai->id);
+	aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->id);
+	aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->id);
 
 	aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP);
 
@@ -646,8 +585,8 @@
 		return -EINVAL;
 	}
 
-	wm8580_write(codec, WM8580_PAIF1 + codec_dai->id, aifa);
-	wm8580_write(codec, WM8580_PAIF3 + codec_dai->id, aifb);
+	snd_soc_write(codec, WM8580_PAIF1 + codec_dai->id, aifa);
+	snd_soc_write(codec, WM8580_PAIF3 + codec_dai->id, aifb);
 
 	return 0;
 }
@@ -660,7 +599,7 @@
 
 	switch (div_id) {
 	case WM8580_MCLK:
-		reg = wm8580_read(codec, WM8580_PLLB4);
+		reg = snd_soc_read(codec, WM8580_PLLB4);
 		reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK;
 
 		switch (div) {
@@ -682,11 +621,11 @@
 		default:
 			return -EINVAL;
 		}
-		wm8580_write(codec, WM8580_PLLB4, reg);
+		snd_soc_write(codec, WM8580_PLLB4, reg);
 		break;
 
 	case WM8580_DAC_CLKSEL:
-		reg = wm8580_read(codec, WM8580_CLKSEL);
+		reg = snd_soc_read(codec, WM8580_CLKSEL);
 		reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK;
 
 		switch (div) {
@@ -704,11 +643,11 @@
 		default:
 			return -EINVAL;
 		}
-		wm8580_write(codec, WM8580_CLKSEL, reg);
+		snd_soc_write(codec, WM8580_CLKSEL, reg);
 		break;
 
 	case WM8580_CLKOUTSRC:
-		reg = wm8580_read(codec, WM8580_PLLB4);
+		reg = snd_soc_read(codec, WM8580_PLLB4);
 		reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK;
 
 		switch (div) {
@@ -730,7 +669,7 @@
 		default:
 			return -EINVAL;
 		}
-		wm8580_write(codec, WM8580_PLLB4, reg);
+		snd_soc_write(codec, WM8580_PLLB4, reg);
 		break;
 
 	default:
@@ -745,14 +684,14 @@
 	struct snd_soc_codec *codec = codec_dai->codec;
 	unsigned int reg;
 
-	reg = wm8580_read(codec, WM8580_DAC_CONTROL5);
+	reg = snd_soc_read(codec, WM8580_DAC_CONTROL5);
 
 	if (mute)
 		reg |= WM8580_DAC_CONTROL5_MUTEALL;
 	else
 		reg &= ~WM8580_DAC_CONTROL5_MUTEALL;
 
-	wm8580_write(codec, WM8580_DAC_CONTROL5, reg);
+	snd_soc_write(codec, WM8580_DAC_CONTROL5, reg);
 
 	return 0;
 }
@@ -769,20 +708,20 @@
 	case SND_SOC_BIAS_STANDBY:
 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
 			/* Power up and get individual control of the DACs */
-			reg = wm8580_read(codec, WM8580_PWRDN1);
+			reg = snd_soc_read(codec, WM8580_PWRDN1);
 			reg &= ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD);
-			wm8580_write(codec, WM8580_PWRDN1, reg);
+			snd_soc_write(codec, WM8580_PWRDN1, reg);
 
 			/* Make VMID high impedence */
-			reg = wm8580_read(codec,  WM8580_ADC_CONTROL1);
+			reg = snd_soc_read(codec,  WM8580_ADC_CONTROL1);
 			reg &= ~0x100;
-			wm8580_write(codec, WM8580_ADC_CONTROL1, reg);
+			snd_soc_write(codec, WM8580_ADC_CONTROL1, reg);
 		}
 		break;
 
 	case SND_SOC_BIAS_OFF:
-		reg = wm8580_read(codec, WM8580_PWRDN1);
-		wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
+		reg = snd_soc_read(codec, WM8580_PWRDN1);
+		snd_soc_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
 		break;
 	}
 	codec->bias_level = level;
@@ -893,7 +832,8 @@
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
 
-static int wm8580_register(struct wm8580_priv *wm8580)
+static int wm8580_register(struct wm8580_priv *wm8580,
+			   enum snd_soc_control_type control)
 {
 	int ret, i;
 	struct snd_soc_codec *codec = &wm8580->codec;
@@ -911,8 +851,6 @@
 	codec->private_data = wm8580;
 	codec->name = "WM8580";
 	codec->owner = THIS_MODULE;
-	codec->read = wm8580_read_reg_cache;
-	codec->write = wm8580_write;
 	codec->bias_level = SND_SOC_BIAS_OFF;
 	codec->set_bias_level = wm8580_set_bias_level;
 	codec->dai = wm8580_dai;
@@ -922,11 +860,34 @@
 
 	memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg));
 
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++)
+		wm8580->supplies[i].supply = wm8580_supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8580->supplies),
+				 wm8580->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+		goto err;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies),
+				    wm8580->supplies);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+		goto err_regulator_get;
+	}
+
 	/* Get the codec into a known state */
-	ret = wm8580_write(codec, WM8580_RESET, 0);
+	ret = snd_soc_write(codec, WM8580_RESET, 0);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
-		goto err;
+		goto err_regulator_enable;
 	}
 
 	for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++)
@@ -939,7 +900,7 @@
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-		goto err;
+		goto err_regulator_enable;
 	}
 
 	ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
@@ -952,6 +913,10 @@
 
 err_codec:
 	snd_soc_unregister_codec(codec);
+err_regulator_enable:
+	regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
+err_regulator_get:
+	regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
 err:
 	kfree(wm8580);
 	return ret;
@@ -962,6 +927,8 @@
 	wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF);
 	snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
 	snd_soc_unregister_codec(&wm8580->codec);
+	regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
+	regulator_bulk_free(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
 	kfree(wm8580);
 	wm8580_codec = NULL;
 }
@@ -978,14 +945,13 @@
 		return -ENOMEM;
 
 	codec = &wm8580->codec;
-	codec->hw_write = (hw_write_t)i2c_master_send;
 
 	i2c_set_clientdata(i2c, wm8580);
 	codec->control_data = i2c;
 
 	codec->dev = &i2c->dev;
 
-	return wm8580_register(wm8580);
+	return wm8580_register(wm8580, SND_SOC_I2C);
 }
 
 static int wm8580_i2c_remove(struct i2c_client *client)
@@ -995,6 +961,21 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8580_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8580_i2c_resume(struct i2c_client *client)
+{
+	return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8580_i2c_suspend NULL
+#define wm8580_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8580_i2c_id[] = {
 	{ "wm8580", 0 },
 	{ }
@@ -1008,6 +989,8 @@
 	},
 	.probe =    wm8580_i2c_probe,
 	.remove =   wm8580_i2c_remove,
+	.suspend =  wm8580_i2c_suspend,
+	.resume =   wm8580_i2c_resume,
 	.id_table = wm8580_i2c_id,
 };
 #endif
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index e7ff212..16e969a 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -43,45 +43,6 @@
 	0x100,
 };
 
-static inline unsigned int wm8728_read_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg)
-{
-	u16 *cache = codec->reg_cache;
-	BUG_ON(reg >= ARRAY_SIZE(wm8728_reg_defaults));
-	return cache[reg];
-}
-
-static inline void wm8728_write_reg_cache(struct snd_soc_codec *codec,
-	u16 reg, unsigned int value)
-{
-	u16 *cache = codec->reg_cache;
-	BUG_ON(reg >= ARRAY_SIZE(wm8728_reg_defaults));
-	cache[reg] = value;
-}
-
-/*
- * write to the WM8728 register space
- */
-static int wm8728_write(struct snd_soc_codec *codec, unsigned int reg,
-	unsigned int value)
-{
-	u8 data[2];
-
-	/* data is
-	 *   D15..D9 WM8728 register offset
-	 *   D8...D0 register data
-	 */
-	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-	data[1] = value & 0x00ff;
-
-	wm8728_write_reg_cache(codec, reg, value);
-
-	if (codec->hw_write(codec->control_data, data, 2) == 2)
-		return 0;
-	else
-		return -EIO;
-}
-
 static const DECLARE_TLV_DB_SCALE(wm8728_tlv, -12750, 50, 1);
 
 static const struct snd_kcontrol_new wm8728_snd_controls[] = {
@@ -121,12 +82,12 @@
 static int wm8728_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
-	u16 mute_reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+	u16 mute_reg = snd_soc_read(codec, WM8728_DACCTL);
 
 	if (mute)
-		wm8728_write(codec, WM8728_DACCTL, mute_reg | 1);
+		snd_soc_write(codec, WM8728_DACCTL, mute_reg | 1);
 	else
-		wm8728_write(codec, WM8728_DACCTL, mute_reg & ~1);
+		snd_soc_write(codec, WM8728_DACCTL, mute_reg & ~1);
 
 	return 0;
 }
@@ -138,7 +99,7 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
-	u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
+	u16 dac = snd_soc_read(codec, WM8728_DACCTL);
 
 	dac &= ~0x18;
 
@@ -155,7 +116,7 @@
 		return -EINVAL;
 	}
 
-	wm8728_write(codec, WM8728_DACCTL, dac);
+	snd_soc_write(codec, WM8728_DACCTL, dac);
 
 	return 0;
 }
@@ -164,7 +125,7 @@
 		unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
-	u16 iface = wm8728_read_reg_cache(codec, WM8728_IFCTL);
+	u16 iface = snd_soc_read(codec, WM8728_IFCTL);
 
 	/* Currently only I2S is supported by the driver, though the
 	 * hardware is more flexible.
@@ -204,7 +165,7 @@
 		return -EINVAL;
 	}
 
-	wm8728_write(codec, WM8728_IFCTL, iface);
+	snd_soc_write(codec, WM8728_IFCTL, iface);
 	return 0;
 }
 
@@ -220,19 +181,19 @@
 	case SND_SOC_BIAS_STANDBY:
 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
 			/* Power everything up... */
-			reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
-			wm8728_write(codec, WM8728_DACCTL, reg & ~0x4);
+			reg = snd_soc_read(codec, WM8728_DACCTL);
+			snd_soc_write(codec, WM8728_DACCTL, reg & ~0x4);
 
 			/* ..then sync in the register cache. */
 			for (i = 0; i < ARRAY_SIZE(wm8728_reg_defaults); i++)
-				wm8728_write(codec, i,
-					     wm8728_read_reg_cache(codec, i));
+				snd_soc_write(codec, i,
+					     snd_soc_read(codec, i));
 		}
 		break;
 
 	case SND_SOC_BIAS_OFF:
-		reg = wm8728_read_reg_cache(codec, WM8728_DACCTL);
-		wm8728_write(codec, WM8728_DACCTL, reg | 0x4);
+		reg = snd_soc_read(codec, WM8728_DACCTL);
+		snd_soc_write(codec, WM8728_DACCTL, reg | 0x4);
 		break;
 	}
 	codec->bias_level = level;
@@ -287,15 +248,14 @@
  * initialise the WM8728 driver
  * register the mixer and dsp interfaces with the kernel
  */
-static int wm8728_init(struct snd_soc_device *socdev)
+static int wm8728_init(struct snd_soc_device *socdev,
+		       enum snd_soc_control_type control)
 {
 	struct snd_soc_codec *codec = socdev->card->codec;
 	int ret = 0;
 
 	codec->name = "WM8728";
 	codec->owner = THIS_MODULE;
-	codec->read = wm8728_read_reg_cache;
-	codec->write = wm8728_write;
 	codec->set_bias_level = wm8728_set_bias_level;
 	codec->dai = &wm8728_dai;
 	codec->num_dai = 1;
@@ -307,11 +267,18 @@
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
 
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8728: failed to configure cache I/O: %d\n",
+		       ret);
+		goto err;
+	}
+
 	/* register pcms */
 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 	if (ret < 0) {
 		printk(KERN_ERR "wm8728: failed to create pcms\n");
-		goto pcm_err;
+		goto err;
 	}
 
 	/* power on device */
@@ -331,7 +298,7 @@
 card_err:
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
-pcm_err:
+err:
 	kfree(codec->reg_cache);
 	return ret;
 }
@@ -357,7 +324,7 @@
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	ret = wm8728_init(socdev);
+	ret = wm8728_init(socdev, SND_SOC_I2C);
 	if (ret < 0)
 		pr_err("failed to initialise WM8728\n");
 
@@ -437,7 +404,7 @@
 
 	codec->control_data = spi;
 
-	ret = wm8728_init(socdev);
+	ret = wm8728_init(socdev, SND_SOC_SPI);
 	if (ret < 0)
 		dev_err(&spi->dev, "failed to initialise WM8728\n");
 
@@ -458,30 +425,6 @@
 	.probe		= wm8728_spi_probe,
 	.remove		= __devexit_p(wm8728_spi_remove),
 };
-
-static int wm8728_spi_write(struct spi_device *spi, const char *data, int len)
-{
-	struct spi_transfer t;
-	struct spi_message m;
-	u8 msg[2];
-
-	if (len <= 0)
-		return 0;
-
-	msg[0] = data[0];
-	msg[1] = data[1];
-
-	spi_message_init(&m);
-	memset(&t, 0, (sizeof t));
-
-	t.tx_buf = &msg[0];
-	t.len = len;
-
-	spi_message_add_tail(&t, &m);
-	spi_sync(spi, &m);
-
-	return len;
-}
 #endif /* CONFIG_SPI_MASTER */
 
 static int wm8728_probe(struct platform_device *pdev)
@@ -506,13 +449,11 @@
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		codec->hw_write = (hw_write_t)i2c_master_send;
 		ret = wm8728_add_i2c_device(pdev, setup);
 	}
 #endif
 #if defined(CONFIG_SPI_MASTER)
 	if (setup->spi) {
-		codec->hw_write = (hw_write_t)wm8728_spi_write;
 		ret = spi_register_driver(&wm8728_spi_driver);
 		if (ret != 0)
 			printk(KERN_ERR "can't add spi driver");
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index e043e3f..d3fd4f2 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -26,6 +26,7 @@
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/initval.h>
+#include <sound/tlv.h>
 
 #include "wm8731.h"
 
@@ -39,9 +40,6 @@
 	unsigned int sysclk;
 };
 
-#ifdef CONFIG_SPI_MASTER
-static int wm8731_spi_write(struct spi_device *spi, const char *data, int len);
-#endif
 
 /*
  * wm8731 register cache
@@ -50,60 +48,12 @@
  * There is no point in caching the reset register
  */
 static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {
-    0x0097, 0x0097, 0x0079, 0x0079,
-    0x000a, 0x0008, 0x009f, 0x000a,
-    0x0000, 0x0000
+	0x0097, 0x0097, 0x0079, 0x0079,
+	0x000a, 0x0008, 0x009f, 0x000a,
+	0x0000, 0x0000
 };
 
-/*
- * read wm8731 register cache
- */
-static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg)
-{
-	u16 *cache = codec->reg_cache;
-	if (reg == WM8731_RESET)
-		return 0;
-	if (reg >= WM8731_CACHEREGNUM)
-		return -1;
-	return cache[reg];
-}
-
-/*
- * write wm8731 register cache
- */
-static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec,
-	u16 reg, unsigned int value)
-{
-	u16 *cache = codec->reg_cache;
-	if (reg >= WM8731_CACHEREGNUM)
-		return;
-	cache[reg] = value;
-}
-
-/*
- * write to the WM8731 register space
- */
-static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg,
-	unsigned int value)
-{
-	u8 data[2];
-
-	/* data is
-	 *   D15..D9 WM8731 register offset
-	 *   D8...D0 register data
-	 */
-	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-	data[1] = value & 0x00ff;
-
-	wm8731_write_reg_cache(codec, reg, value);
-	if (codec->hw_write(codec->control_data, data, 2) == 2)
-		return 0;
-	else
-		return -EIO;
-}
-
-#define wm8731_reset(c)	wm8731_write(c, WM8731_RESET, 0)
+#define wm8731_reset(c)	snd_soc_write(c, WM8731_RESET, 0)
 
 static const char *wm8731_input_select[] = {"Line In", "Mic"};
 static const char *wm8731_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
@@ -113,20 +63,26 @@
 	SOC_ENUM_SINGLE(WM8731_APDIGI, 1, 4, wm8731_deemph),
 };
 
+static const DECLARE_TLV_DB_SCALE(in_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -1500, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+
 static const struct snd_kcontrol_new wm8731_snd_controls[] = {
 
-SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
-	0, 127, 0),
+SOC_DOUBLE_R_TLV("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
+		 0, 127, 0, out_tlv),
 SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
 	7, 1, 0),
 
-SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0),
+SOC_DOUBLE_R_TLV("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0,
+		 in_tlv),
 SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
 
 SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0),
-SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1),
+SOC_SINGLE("Mic Capture Switch", WM8731_APANA, 1, 1, 1),
 
-SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1),
+SOC_SINGLE_TLV("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1,
+	       sidetone_tlv),
 
 SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
 SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
@@ -260,12 +216,12 @@
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
 	struct wm8731_priv *wm8731 = codec->private_data;
-	u16 iface = wm8731_read_reg_cache(codec, WM8731_IFACE) & 0xfff3;
+	u16 iface = snd_soc_read(codec, WM8731_IFACE) & 0xfff3;
 	int i = get_coeff(wm8731->sysclk, params_rate(params));
 	u16 srate = (coeff_div[i].sr << 2) |
 		(coeff_div[i].bosr << 1) | coeff_div[i].usb;
 
-	wm8731_write(codec, WM8731_SRATE, srate);
+	snd_soc_write(codec, WM8731_SRATE, srate);
 
 	/* bit size */
 	switch (params_format(params)) {
@@ -279,7 +235,7 @@
 		break;
 	}
 
-	wm8731_write(codec, WM8731_IFACE, iface);
+	snd_soc_write(codec, WM8731_IFACE, iface);
 	return 0;
 }
 
@@ -291,7 +247,7 @@
 	struct snd_soc_codec *codec = socdev->card->codec;
 
 	/* set active */
-	wm8731_write(codec, WM8731_ACTIVE, 0x0001);
+	snd_soc_write(codec, WM8731_ACTIVE, 0x0001);
 
 	return 0;
 }
@@ -306,19 +262,19 @@
 	/* deactivate */
 	if (!codec->active) {
 		udelay(50);
-		wm8731_write(codec, WM8731_ACTIVE, 0x0);
+		snd_soc_write(codec, WM8731_ACTIVE, 0x0);
 	}
 }
 
 static int wm8731_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
-	u16 mute_reg = wm8731_read_reg_cache(codec, WM8731_APDIGI) & 0xfff7;
+	u16 mute_reg = snd_soc_read(codec, WM8731_APDIGI) & 0xfff7;
 
 	if (mute)
-		wm8731_write(codec, WM8731_APDIGI, mute_reg | 0x8);
+		snd_soc_write(codec, WM8731_APDIGI, mute_reg | 0x8);
 	else
-		wm8731_write(codec, WM8731_APDIGI, mute_reg);
+		snd_soc_write(codec, WM8731_APDIGI, mute_reg);
 	return 0;
 }
 
@@ -396,7 +352,7 @@
 	}
 
 	/* set iface */
-	wm8731_write(codec, WM8731_IFACE, iface);
+	snd_soc_write(codec, WM8731_IFACE, iface);
 	return 0;
 }
 
@@ -412,12 +368,12 @@
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		/* Clear PWROFF, gate CLKOUT, everything else as-is */
-		reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
-		wm8731_write(codec, WM8731_PWR, reg | 0x0040);
+		reg = snd_soc_read(codec, WM8731_PWR) & 0xff7f;
+		snd_soc_write(codec, WM8731_PWR, reg | 0x0040);
 		break;
 	case SND_SOC_BIAS_OFF:
-		wm8731_write(codec, WM8731_ACTIVE, 0x0);
-		wm8731_write(codec, WM8731_PWR, 0xffff);
+		snd_soc_write(codec, WM8731_ACTIVE, 0x0);
+		snd_soc_write(codec, WM8731_PWR, 0xffff);
 		break;
 	}
 	codec->bias_level = level;
@@ -457,15 +413,17 @@
 		.rates = WM8731_RATES,
 		.formats = WM8731_FORMATS,},
 	.ops = &wm8731_dai_ops,
+	.symmetric_rates = 1,
 };
 EXPORT_SYMBOL_GPL(wm8731_dai);
 
+#ifdef CONFIG_PM
 static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
 {
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_codec *codec = socdev->card->codec;
 
-	wm8731_write(codec, WM8731_ACTIVE, 0x0);
+	snd_soc_write(codec, WM8731_ACTIVE, 0x0);
 	wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
@@ -488,6 +446,10 @@
 	wm8731_set_bias_level(codec, codec->suspend_bias_level);
 	return 0;
 }
+#else
+#define wm8731_suspend NULL
+#define wm8731_resume NULL
+#endif
 
 static int wm8731_probe(struct platform_device *pdev)
 {
@@ -547,15 +509,16 @@
 };
 EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
 
-static int wm8731_register(struct wm8731_priv *wm8731)
+static int wm8731_register(struct wm8731_priv *wm8731,
+			   enum snd_soc_control_type control)
 {
 	int ret;
 	struct snd_soc_codec *codec = &wm8731->codec;
-	u16 reg;
 
 	if (wm8731_codec) {
 		dev_err(codec->dev, "Another WM8731 is registered\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto err;
 	}
 
 	mutex_init(&codec->mutex);
@@ -565,8 +528,6 @@
 	codec->private_data = wm8731;
 	codec->name = "WM8731";
 	codec->owner = THIS_MODULE;
-	codec->read = wm8731_read_reg_cache;
-	codec->write = wm8731_write;
 	codec->bias_level = SND_SOC_BIAS_OFF;
 	codec->set_bias_level = wm8731_set_bias_level;
 	codec->dai = &wm8731_dai;
@@ -576,10 +537,16 @@
 
 	memcpy(codec->reg_cache, wm8731_reg, sizeof(wm8731_reg));
 
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
 	ret = wm8731_reset(codec);
 	if (ret < 0) {
-		dev_err(codec->dev, "Failed to issue reset\n");
-		return ret;
+		dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+		goto err;
 	}
 
 	wm8731_dai.dev = codec->dev;
@@ -587,35 +554,36 @@
 	wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* Latch the update bits */
-	reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
-	wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100);
-	reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
-	wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100);
-	reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
-	wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100);
-	reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
-	wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100);
+	snd_soc_update_bits(codec, WM8731_LOUT1V, 0x100, 0);
+	snd_soc_update_bits(codec, WM8731_ROUT1V, 0x100, 0);
+	snd_soc_update_bits(codec, WM8731_LINVOL, 0x100, 0);
+	snd_soc_update_bits(codec, WM8731_RINVOL, 0x100, 0);
 
 	/* Disable bypass path by default */
-	reg = wm8731_read_reg_cache(codec, WM8731_APANA);
-	wm8731_write(codec, WM8731_APANA, reg & ~0x4);
+	snd_soc_update_bits(codec, WM8731_APANA, 0x4, 0);
 
 	wm8731_codec = codec;
 
 	ret = snd_soc_register_codec(codec);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
-		return ret;
+		goto err;
 	}
 
 	ret = snd_soc_register_dai(&wm8731_dai);
 	if (ret != 0) {
 		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
 		snd_soc_unregister_codec(codec);
-		return ret;
+		goto err_codec;
 	}
 
 	return 0;
+
+err_codec:
+	snd_soc_unregister_codec(codec);
+err:
+	kfree(wm8731);
+	return ret;
 }
 
 static void wm8731_unregister(struct wm8731_priv *wm8731)
@@ -628,30 +596,6 @@
 }
 
 #if defined(CONFIG_SPI_MASTER)
-static int wm8731_spi_write(struct spi_device *spi, const char *data, int len)
-{
-	struct spi_transfer t;
-	struct spi_message m;
-	u8 msg[2];
-
-	if (len <= 0)
-		return 0;
-
-	msg[0] = data[0];
-	msg[1] = data[1];
-
-	spi_message_init(&m);
-	memset(&t, 0, (sizeof t));
-
-	t.tx_buf = &msg[0];
-	t.len = len;
-
-	spi_message_add_tail(&t, &m);
-	spi_sync(spi, &m);
-
-	return len;
-}
-
 static int __devinit wm8731_spi_probe(struct spi_device *spi)
 {
 	struct snd_soc_codec *codec;
@@ -663,23 +607,37 @@
 
 	codec = &wm8731->codec;
 	codec->control_data = spi;
-	codec->hw_write = (hw_write_t)wm8731_spi_write;
 	codec->dev = &spi->dev;
 
-	spi->dev.driver_data = wm8731;
+	dev_set_drvdata(&spi->dev, wm8731);
 
-	return wm8731_register(wm8731);
+	return wm8731_register(wm8731, SND_SOC_SPI);
 }
 
 static int __devexit wm8731_spi_remove(struct spi_device *spi)
 {
-	struct wm8731_priv *wm8731 = spi->dev.driver_data;
+	struct wm8731_priv *wm8731 = dev_get_drvdata(&spi->dev);
 
 	wm8731_unregister(wm8731);
 
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8731_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8731_spi_resume(struct spi_device *spi)
+{
+	return snd_soc_resume_device(&spi->dev);
+}
+#else
+#define wm8731_spi_suspend NULL
+#define wm8731_spi_resume NULL
+#endif
+
 static struct spi_driver wm8731_spi_driver = {
 	.driver = {
 		.name	= "wm8731",
@@ -687,6 +645,8 @@
 		.owner	= THIS_MODULE,
 	},
 	.probe		= wm8731_spi_probe,
+	.suspend	= wm8731_spi_suspend,
+	.resume		= wm8731_spi_resume,
 	.remove		= __devexit_p(wm8731_spi_remove),
 };
 #endif /* CONFIG_SPI_MASTER */
@@ -703,14 +663,13 @@
 		return -ENOMEM;
 
 	codec = &wm8731->codec;
-	codec->hw_write = (hw_write_t)i2c_master_send;
 
 	i2c_set_clientdata(i2c, wm8731);
 	codec->control_data = i2c;
 
 	codec->dev = &i2c->dev;
 
-	return wm8731_register(wm8731);
+	return wm8731_register(wm8731, SND_SOC_I2C);
 }
 
 static __devexit int wm8731_i2c_remove(struct i2c_client *client)
@@ -720,6 +679,21 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8731_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&i2c->dev);
+}
+
+static int wm8731_i2c_resume(struct i2c_client *i2c)
+{
+	return snd_soc_resume_device(&i2c->dev);
+}
+#else
+#define wm8731_i2c_suspend NULL
+#define wm8731_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8731_i2c_id[] = {
 	{ "wm8731", 0 },
 	{ }
@@ -733,6 +707,8 @@
 	},
 	.probe =    wm8731_i2c_probe,
 	.remove =   __devexit_p(wm8731_i2c_remove),
+	.suspend =  wm8731_i2c_suspend,
+	.resume =   wm8731_i2c_resume,
 	.id_table = wm8731_i2c_id,
 };
 #endif
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index b64509b..4ba1e7e 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -55,50 +55,7 @@
 	0x0079, 0x0079, 0x0079,          /* 40 */
 };
 
-/*
- * read wm8750 register cache
- */
-static inline unsigned int wm8750_read_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg)
-{
-	u16 *cache = codec->reg_cache;
-	if (reg > WM8750_CACHE_REGNUM)
-		return -1;
-	return cache[reg];
-}
-
-/*
- * write wm8750 register cache
- */
-static inline void wm8750_write_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg, unsigned int value)
-{
-	u16 *cache = codec->reg_cache;
-	if (reg > WM8750_CACHE_REGNUM)
-		return;
-	cache[reg] = value;
-}
-
-static int wm8750_write(struct snd_soc_codec *codec, unsigned int reg,
-	unsigned int value)
-{
-	u8 data[2];
-
-	/* data is
-	 *   D15..D9 WM8753 register offset
-	 *   D8...D0 register data
-	 */
-	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-	data[1] = value & 0x00ff;
-
-	wm8750_write_reg_cache(codec, reg, value);
-	if (codec->hw_write(codec->control_data, data, 2) == 2)
-		return 0;
-	else
-		return -EIO;
-}
-
-#define wm8750_reset(c)	wm8750_write(c, WM8750_RESET, 0)
+#define wm8750_reset(c)	snd_soc_write(c, WM8750_RESET, 0)
 
 /*
  * WM8750 Controls
@@ -594,7 +551,7 @@
 		return -EINVAL;
 	}
 
-	wm8750_write(codec, WM8750_IFACE, iface);
+	snd_soc_write(codec, WM8750_IFACE, iface);
 	return 0;
 }
 
@@ -606,8 +563,8 @@
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
 	struct wm8750_priv *wm8750 = codec->private_data;
-	u16 iface = wm8750_read_reg_cache(codec, WM8750_IFACE) & 0x1f3;
-	u16 srate = wm8750_read_reg_cache(codec, WM8750_SRATE) & 0x1c0;
+	u16 iface = snd_soc_read(codec, WM8750_IFACE) & 0x1f3;
+	u16 srate = snd_soc_read(codec, WM8750_SRATE) & 0x1c0;
 	int coeff = get_coeff(wm8750->sysclk, params_rate(params));
 
 	/* bit size */
@@ -626,9 +583,9 @@
 	}
 
 	/* set iface & srate */
-	wm8750_write(codec, WM8750_IFACE, iface);
+	snd_soc_write(codec, WM8750_IFACE, iface);
 	if (coeff >= 0)
-		wm8750_write(codec, WM8750_SRATE, srate |
+		snd_soc_write(codec, WM8750_SRATE, srate |
 			(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
 
 	return 0;
@@ -637,35 +594,35 @@
 static int wm8750_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
-	u16 mute_reg = wm8750_read_reg_cache(codec, WM8750_ADCDAC) & 0xfff7;
+	u16 mute_reg = snd_soc_read(codec, WM8750_ADCDAC) & 0xfff7;
 
 	if (mute)
-		wm8750_write(codec, WM8750_ADCDAC, mute_reg | 0x8);
+		snd_soc_write(codec, WM8750_ADCDAC, mute_reg | 0x8);
 	else
-		wm8750_write(codec, WM8750_ADCDAC, mute_reg);
+		snd_soc_write(codec, WM8750_ADCDAC, mute_reg);
 	return 0;
 }
 
 static int wm8750_set_bias_level(struct snd_soc_codec *codec,
 				 enum snd_soc_bias_level level)
 {
-	u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e;
+	u16 pwr_reg = snd_soc_read(codec, WM8750_PWR1) & 0xfe3e;
 
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 		/* set vmid to 50k and unmute dac */
-		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
+		snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x00c0);
 		break;
 	case SND_SOC_BIAS_PREPARE:
 		/* set vmid to 5k for quick power up */
-		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
+		snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x01c1);
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		/* mute dac and set vmid to 500k, enable VREF */
-		wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
+		snd_soc_write(codec, WM8750_PWR1, pwr_reg | 0x0141);
 		break;
 	case SND_SOC_BIAS_OFF:
-		wm8750_write(codec, WM8750_PWR1, 0x0001);
+		snd_soc_write(codec, WM8750_PWR1, 0x0001);
 		break;
 	}
 	codec->bias_level = level;
@@ -754,15 +711,14 @@
  * initialise the WM8750 driver
  * register the mixer and dsp interfaces with the kernel
  */
-static int wm8750_init(struct snd_soc_device *socdev)
+static int wm8750_init(struct snd_soc_device *socdev,
+		       enum snd_soc_control_type control)
 {
 	struct snd_soc_codec *codec = socdev->card->codec;
 	int reg, ret = 0;
 
 	codec->name = "WM8750";
 	codec->owner = THIS_MODULE;
-	codec->read = wm8750_read_reg_cache;
-	codec->write = wm8750_write;
 	codec->set_bias_level = wm8750_set_bias_level;
 	codec->dai = &wm8750_dai;
 	codec->num_dai = 1;
@@ -771,13 +727,23 @@
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
 
-	wm8750_reset(codec);
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8750: failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	ret = wm8750_reset(codec);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8750: failed to reset: %d\n", ret);
+		goto err;
+	}
 
 	/* register pcms */
 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 	if (ret < 0) {
 		printk(KERN_ERR "wm8750: failed to create pcms\n");
-		goto pcm_err;
+		goto err;
 	}
 
 	/* charge output caps */
@@ -786,22 +752,22 @@
 	schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000));
 
 	/* set the update bits */
-	reg = wm8750_read_reg_cache(codec, WM8750_LDAC);
-	wm8750_write(codec, WM8750_LDAC, reg | 0x0100);
-	reg = wm8750_read_reg_cache(codec, WM8750_RDAC);
-	wm8750_write(codec, WM8750_RDAC, reg | 0x0100);
-	reg = wm8750_read_reg_cache(codec, WM8750_LOUT1V);
-	wm8750_write(codec, WM8750_LOUT1V, reg | 0x0100);
-	reg = wm8750_read_reg_cache(codec, WM8750_ROUT1V);
-	wm8750_write(codec, WM8750_ROUT1V, reg | 0x0100);
-	reg = wm8750_read_reg_cache(codec, WM8750_LOUT2V);
-	wm8750_write(codec, WM8750_LOUT2V, reg | 0x0100);
-	reg = wm8750_read_reg_cache(codec, WM8750_ROUT2V);
-	wm8750_write(codec, WM8750_ROUT2V, reg | 0x0100);
-	reg = wm8750_read_reg_cache(codec, WM8750_LINVOL);
-	wm8750_write(codec, WM8750_LINVOL, reg | 0x0100);
-	reg = wm8750_read_reg_cache(codec, WM8750_RINVOL);
-	wm8750_write(codec, WM8750_RINVOL, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8750_LDAC);
+	snd_soc_write(codec, WM8750_LDAC, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8750_RDAC);
+	snd_soc_write(codec, WM8750_RDAC, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8750_LOUT1V);
+	snd_soc_write(codec, WM8750_LOUT1V, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8750_ROUT1V);
+	snd_soc_write(codec, WM8750_ROUT1V, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8750_LOUT2V);
+	snd_soc_write(codec, WM8750_LOUT2V, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8750_ROUT2V);
+	snd_soc_write(codec, WM8750_ROUT2V, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8750_LINVOL);
+	snd_soc_write(codec, WM8750_LINVOL, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8750_RINVOL);
+	snd_soc_write(codec, WM8750_RINVOL, reg | 0x0100);
 
 	snd_soc_add_controls(codec, wm8750_snd_controls,
 				ARRAY_SIZE(wm8750_snd_controls));
@@ -816,7 +782,7 @@
 card_err:
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
-pcm_err:
+err:
 	kfree(codec->reg_cache);
 	return ret;
 }
@@ -844,7 +810,7 @@
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	ret = wm8750_init(socdev);
+	ret = wm8750_init(socdev, SND_SOC_I2C);
 	if (ret < 0)
 		pr_err("failed to initialise WM8750\n");
 
@@ -924,7 +890,7 @@
 
 	codec->control_data = spi;
 
-	ret = wm8750_init(socdev);
+	ret = wm8750_init(socdev, SND_SOC_SPI);
 	if (ret < 0)
 		dev_err(&spi->dev, "failed to initialise WM8750\n");
 
@@ -945,30 +911,6 @@
 	.probe		= wm8750_spi_probe,
 	.remove		= __devexit_p(wm8750_spi_remove),
 };
-
-static int wm8750_spi_write(struct spi_device *spi, const char *data, int len)
-{
-	struct spi_transfer t;
-	struct spi_message m;
-	u8 msg[2];
-
-	if (len <= 0)
-		return 0;
-
-	msg[0] = data[0];
-	msg[1] = data[1];
-
-	spi_message_init(&m);
-	memset(&t, 0, (sizeof t));
-
-	t.tx_buf = &msg[0];
-	t.len = len;
-
-	spi_message_add_tail(&t, &m);
-	spi_sync(spi, &m);
-
-	return len;
-}
 #endif
 
 static int wm8750_probe(struct platform_device *pdev)
@@ -1002,13 +944,11 @@
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		codec->hw_write = (hw_write_t)i2c_master_send;
 		ret = wm8750_add_i2c_device(pdev, setup);
 	}
 #endif
 #if defined(CONFIG_SPI_MASTER)
 	if (setup->spi) {
-		codec->hw_write = (hw_write_t)wm8750_spi_write;
 		ret = spi_register_driver(&wm8750_spi_driver);
 		if (ret != 0)
 			printk(KERN_ERR "can't add spi driver");
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index a6e8f3f..d80d414 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -79,7 +79,7 @@
 	0x0097, 0x0097, 0x0000, 0x0004,
 	0x0000, 0x0083, 0x0024, 0x01ba,
 	0x0000, 0x0083, 0x0024, 0x01ba,
-	0x0000, 0x0000
+	0x0000, 0x0000, 0x0000
 };
 
 /* codec private data */
@@ -703,7 +703,7 @@
 
 	if ((Ndiv < 6) || (Ndiv > 12))
 		printk(KERN_WARNING
-			"wm8753: unsupported N = %d\n", Ndiv);
+			"wm8753: unsupported N = %u\n", Ndiv);
 
 	pll_div->n = Ndiv;
 	Nmod = target % source;
@@ -1660,11 +1660,11 @@
 	codec->set_bias_level = wm8753_set_bias_level;
 	codec->dai = wm8753_dai;
 	codec->num_dai = 2;
-	codec->reg_cache_size = ARRAY_SIZE(wm8753->reg_cache);
+	codec->reg_cache_size = ARRAY_SIZE(wm8753->reg_cache) + 1;
 	codec->reg_cache = &wm8753->reg_cache;
 	codec->private_data = wm8753;
 
-	memcpy(codec->reg_cache, wm8753_reg, sizeof(codec->reg_cache));
+	memcpy(codec->reg_cache, wm8753_reg, sizeof(wm8753->reg_cache));
 	INIT_DELAYED_WORK(&codec->delayed_work, wm8753_work);
 
 	ret = wm8753_reset(codec);
@@ -1766,6 +1766,21 @@
         return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8753_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8753_i2c_resume(struct i2c_client *client)
+{
+	return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8753_i2c_suspend NULL
+#define wm8753_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8753_i2c_id[] = {
 	{ "wm8753", 0 },
 	{ }
@@ -1779,6 +1794,8 @@
 	},
 	.probe =    wm8753_i2c_probe,
 	.remove =   wm8753_i2c_remove,
+	.suspend =  wm8753_i2c_suspend,
+	.resume =   wm8753_i2c_resume,
 	.id_table = wm8753_i2c_id,
 };
 #endif
@@ -1822,18 +1839,34 @@
 	codec->hw_write = (hw_write_t)wm8753_spi_write;
 	codec->dev = &spi->dev;
 
-	spi->dev.driver_data = wm8753;
+	dev_set_drvdata(&spi->dev, wm8753);
 
 	return wm8753_register(wm8753);
 }
 
 static int __devexit wm8753_spi_remove(struct spi_device *spi)
 {
-	struct wm8753_priv *wm8753 = spi->dev.driver_data;
+	struct wm8753_priv *wm8753 = dev_get_drvdata(&spi->dev);
 	wm8753_unregister(wm8753);
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8753_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8753_spi_resume(struct spi_device *spi)
+{
+	return snd_soc_resume_device(&spi->dev);
+}
+
+#else
+#define wm8753_spi_suspend NULL
+#define wm8753_spi_resume NULL
+#endif
+
 static struct spi_driver wm8753_spi_driver = {
 	.driver = {
 		.name	= "wm8753",
@@ -1842,6 +1875,8 @@
 	},
 	.probe		= wm8753_spi_probe,
 	.remove		= __devexit_p(wm8753_spi_remove),
+	.suspend	= wm8753_spi_suspend,
+	.resume		= wm8753_spi_resume,
 };
 #endif
 
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
new file mode 100644
index 0000000..a9829aa
--- /dev/null
+++ b/sound/soc/codecs/wm8776.c
@@ -0,0 +1,744 @@
+/*
+ * wm8776.c  --  WM8776 ALSA SoC Audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * TODO: Input ALC/limiter support
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8776.h"
+
+static struct snd_soc_codec *wm8776_codec;
+struct snd_soc_codec_device soc_codec_dev_wm8776;
+
+/* codec private data */
+struct wm8776_priv {
+	struct snd_soc_codec codec;
+	u16 reg_cache[WM8776_CACHEREGNUM];
+	int sysclk[2];
+};
+
+#ifdef CONFIG_SPI_MASTER
+static int wm8776_spi_write(struct spi_device *spi, const char *data, int len);
+#endif
+
+static const u16 wm8776_reg[WM8776_CACHEREGNUM] = {
+	0x79, 0x79, 0x79, 0xff, 0xff,  /* 4 */
+	0xff, 0x00, 0x90, 0x00, 0x00,  /* 9 */
+	0x22, 0x22, 0x22, 0x08, 0xcf,  /* 14 */
+	0xcf, 0x7b, 0x00, 0x32, 0x00,  /* 19 */
+	0xa6, 0x01, 0x01
+};
+
+static int wm8776_reset(struct snd_soc_codec *codec)
+{
+	return snd_soc_write(codec, WM8776_RESET, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(hp_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -10350, 50, 1);
+
+static const struct snd_kcontrol_new wm8776_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8776_HPLVOL, WM8776_HPRVOL,
+		 0, 127, 0, hp_tlv),
+SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8776_DACLVOL, WM8776_DACRVOL,
+		 0, 255, 0, dac_tlv),
+SOC_SINGLE("Digital Playback ZC Switch", WM8776_DACCTRL1, 0, 1, 0),
+
+SOC_SINGLE("Deemphasis Switch", WM8776_DACCTRL2, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Volume", WM8776_ADCLVOL, WM8776_ADCRVOL,
+		 0, 255, 0, adc_tlv),
+SOC_DOUBLE("Capture Switch", WM8776_ADCMUX, 7, 6, 1, 1),
+SOC_DOUBLE_R("Capture ZC Switch", WM8776_ADCLVOL, WM8776_ADCRVOL, 8, 1, 0),
+SOC_SINGLE("Capture HPF Switch", WM8776_ADCIFCTRL, 8, 1, 1),
+};
+
+static const struct snd_kcontrol_new inmix_controls[] = {
+SOC_DAPM_SINGLE("AIN1 Switch", WM8776_ADCMUX, 0, 1, 0),
+SOC_DAPM_SINGLE("AIN2 Switch", WM8776_ADCMUX, 1, 1, 0),
+SOC_DAPM_SINGLE("AIN3 Switch", WM8776_ADCMUX, 2, 1, 0),
+SOC_DAPM_SINGLE("AIN4 Switch", WM8776_ADCMUX, 3, 1, 0),
+SOC_DAPM_SINGLE("AIN5 Switch", WM8776_ADCMUX, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new outmix_controls[] = {
+SOC_DAPM_SINGLE("DAC Switch", WM8776_OUTMUX, 0, 1, 0),
+SOC_DAPM_SINGLE("AUX Switch", WM8776_OUTMUX, 1, 1, 0),
+SOC_DAPM_SINGLE("Bypass Switch", WM8776_OUTMUX, 2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8776_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AUX"),
+SND_SOC_DAPM_INPUT("AUX"),
+
+SND_SOC_DAPM_INPUT("AIN1"),
+SND_SOC_DAPM_INPUT("AIN2"),
+SND_SOC_DAPM_INPUT("AIN3"),
+SND_SOC_DAPM_INPUT("AIN4"),
+SND_SOC_DAPM_INPUT("AIN5"),
+
+SND_SOC_DAPM_MIXER("Input Mixer", WM8776_PWRDOWN, 6, 1,
+		   inmix_controls, ARRAY_SIZE(inmix_controls)),
+
+SND_SOC_DAPM_ADC("ADC", "Capture", WM8776_PWRDOWN, 1, 1),
+SND_SOC_DAPM_DAC("DAC", "Playback", WM8776_PWRDOWN, 2, 1),
+
+SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0,
+		   outmix_controls, ARRAY_SIZE(outmix_controls)),
+
+SND_SOC_DAPM_PGA("Headphone PGA", WM8776_PWRDOWN, 3, 1, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("VOUT"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+};
+
+static const struct snd_soc_dapm_route routes[] = {
+	{ "Input Mixer", "AIN1 Switch", "AIN1" },
+	{ "Input Mixer", "AIN2 Switch", "AIN2" },
+	{ "Input Mixer", "AIN3 Switch", "AIN3" },
+	{ "Input Mixer", "AIN4 Switch", "AIN4" },
+	{ "Input Mixer", "AIN5 Switch", "AIN5" },
+
+	{ "ADC", NULL, "Input Mixer" },
+
+	{ "Output Mixer", "DAC Switch", "DAC" },
+	{ "Output Mixer", "AUX Switch", "AUX" },
+	{ "Output Mixer", "Bypass Switch", "Input Mixer" },
+
+	{ "VOUT", NULL, "Output Mixer" },
+
+	{ "Headphone PGA", NULL, "Output Mixer" },
+
+	{ "HPOUTL", NULL, "Headphone PGA" },
+	{ "HPOUTR", NULL, "Headphone PGA" },
+};
+
+static int wm8776_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	int reg, iface, master;
+
+	switch (dai->id) {
+	case WM8776_DAI_DAC:
+		reg = WM8776_DACIFCTRL;
+		master = 0x80;
+		break;
+	case WM8776_DAI_ADC:
+		reg = WM8776_ADCIFCTRL;
+		master = 0x100;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	iface = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		master = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= 0x0002;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x0001;
+		break;
+		/* FIXME: CHECK A/B */
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x0003;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		iface |= 0x0007;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= 0x00c;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= 0x008;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x004;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Finally, write out the values */
+	snd_soc_update_bits(codec, reg, 0xf, iface);
+	snd_soc_update_bits(codec, WM8776_MSTRCTRL, 0x180, master);
+
+	return 0;
+}
+
+static int mclk_ratios[] = {
+	128,
+	192,
+	256,
+	384,
+	512,
+	768,
+};
+
+static int wm8776_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8776_priv *wm8776 = codec->private_data;
+	int iface_reg, iface;
+	int ratio_shift, master;
+	int i;
+
+	iface = 0;
+
+	switch (dai->id) {
+	case WM8776_DAI_DAC:
+		iface_reg = WM8776_DACIFCTRL;
+		master = 0x80;
+		ratio_shift = 4;
+		break;
+	case WM8776_DAI_ADC:
+		iface_reg = WM8776_ADCIFCTRL;
+		master = 0x100;
+		ratio_shift = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+
+	/* Set word length */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= 0x10;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= 0x20;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface |= 0x30;
+		break;
+	}
+
+	/* Only need to set MCLK/LRCLK ratio if we're master */
+	if (snd_soc_read(codec, WM8776_MSTRCTRL) & master) {
+		for (i = 0; i < ARRAY_SIZE(mclk_ratios); i++) {
+			if (wm8776->sysclk[dai->id] / params_rate(params)
+			    == mclk_ratios[i])
+				break;
+		}
+
+		if (i == ARRAY_SIZE(mclk_ratios)) {
+			dev_err(codec->dev,
+				"Unable to configure MCLK ratio %d/%d\n",
+				wm8776->sysclk[dai->id], params_rate(params));
+			return -EINVAL;
+		}
+
+		dev_dbg(codec->dev, "MCLK is %dfs\n", mclk_ratios[i]);
+
+		snd_soc_update_bits(codec, WM8776_MSTRCTRL,
+				    0x7 << ratio_shift, i << ratio_shift);
+	} else {
+		dev_dbg(codec->dev, "DAI in slave mode\n");
+	}
+
+	snd_soc_update_bits(codec, iface_reg, 0x30, iface);
+
+	return 0;
+}
+
+static int wm8776_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+
+	return snd_soc_write(codec, WM8776_DACMUTE, !!mute);
+}
+
+static int wm8776_set_sysclk(struct snd_soc_dai *dai,
+			     int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8776_priv *wm8776 = codec->private_data;
+
+	BUG_ON(dai->id >= ARRAY_SIZE(wm8776->sysclk));
+
+	wm8776->sysclk[dai->id] = freq;
+
+	return 0;
+}
+
+static int wm8776_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* Disable the global powerdown; DAPM does the rest */
+			snd_soc_update_bits(codec, WM8776_PWRDOWN, 1, 0);
+		}
+
+		break;
+	case SND_SOC_BIAS_OFF:
+		snd_soc_update_bits(codec, WM8776_PWRDOWN, 1, 1);
+		break;
+	}
+
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8776_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+		      SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
+		      SNDRV_PCM_RATE_96000)
+
+
+#define WM8776_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm8776_dac_ops = {
+	.digital_mute	= wm8776_mute,
+	.hw_params      = wm8776_hw_params,
+	.set_fmt        = wm8776_set_fmt,
+	.set_sysclk     = wm8776_set_sysclk,
+};
+
+static struct snd_soc_dai_ops wm8776_adc_ops = {
+	.hw_params      = wm8776_hw_params,
+	.set_fmt        = wm8776_set_fmt,
+	.set_sysclk     = wm8776_set_sysclk,
+};
+
+struct snd_soc_dai wm8776_dai[] = {
+	{
+		.name = "WM8776 Playback",
+		.id = WM8776_DAI_DAC,
+		.playback = {
+			.stream_name = "Playback",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = WM8776_RATES,
+			.formats = WM8776_FORMATS,
+		},
+		.ops = &wm8776_dac_ops,
+	},
+	{
+		.name = "WM8776 Capture",
+		.id = WM8776_DAI_ADC,
+		.capture = {
+			.stream_name = "Capture",
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = WM8776_RATES,
+			.formats = WM8776_FORMATS,
+		},
+		.ops = &wm8776_adc_ops,
+	},
+};
+EXPORT_SYMBOL_GPL(wm8776_dai);
+
+#ifdef CONFIG_PM
+static int wm8776_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm8776_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int wm8776_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	int i;
+	u8 data[2];
+	u16 *cache = codec->reg_cache;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < ARRAY_SIZE(wm8776_reg); i++) {
+		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+
+	wm8776_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+#else
+#define wm8776_suspend NULL
+#define wm8776_resume NULL
+#endif
+
+static int wm8776_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8776_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8776_codec;
+	codec = wm8776_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, wm8776_snd_controls,
+			     ARRAY_SIZE(wm8776_snd_controls));
+	snd_soc_dapm_new_controls(codec, wm8776_dapm_widgets,
+				  ARRAY_SIZE(wm8776_dapm_widgets));
+	snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes));
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+/* power down chip */
+static int wm8776_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8776 = {
+	.probe = 	wm8776_probe,
+	.remove = 	wm8776_remove,
+	.suspend = 	wm8776_suspend,
+	.resume =	wm8776_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8776);
+
+static int wm8776_register(struct wm8776_priv *wm8776,
+			   enum snd_soc_control_type control)
+{
+	int ret, i;
+	struct snd_soc_codec *codec = &wm8776->codec;
+
+	if (wm8776_codec) {
+		dev_err(codec->dev, "Another WM8776 is registered\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8776;
+	codec->name = "WM8776";
+	codec->owner = THIS_MODULE;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8776_set_bias_level;
+	codec->dai = wm8776_dai;
+	codec->num_dai = ARRAY_SIZE(wm8776_dai);
+	codec->reg_cache_size = WM8776_CACHEREGNUM;
+	codec->reg_cache = &wm8776->reg_cache;
+
+	memcpy(codec->reg_cache, wm8776_reg, sizeof(wm8776_reg));
+
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(wm8776_dai); i++)
+		wm8776_dai[i].dev = codec->dev;
+
+	ret = wm8776_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
+		goto err;
+	}
+
+	wm8776_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* Latch the update bits; right channel only since we always
+	 * update both. */
+	snd_soc_update_bits(codec, WM8776_HPRVOL, 0x100, 0x100);
+	snd_soc_update_bits(codec, WM8776_DACRVOL, 0x100, 0x100);
+
+	wm8776_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto err;
+	}
+
+	ret = snd_soc_register_dais(wm8776_dai, ARRAY_SIZE(wm8776_dai));
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+		goto err_codec;
+	}
+
+	return 0;
+
+err_codec:
+	snd_soc_unregister_codec(codec);
+err:
+	kfree(wm8776);
+	return ret;
+}
+
+static void wm8776_unregister(struct wm8776_priv *wm8776)
+{
+	wm8776_set_bias_level(&wm8776->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dais(wm8776_dai, ARRAY_SIZE(wm8776_dai));
+	snd_soc_unregister_codec(&wm8776->codec);
+	kfree(wm8776);
+	wm8776_codec = NULL;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int wm8776_spi_write(struct spi_device *spi, const char *data, int len)
+{
+	struct spi_transfer t;
+	struct spi_message m;
+	u8 msg[2];
+
+	if (len <= 0)
+		return 0;
+
+	msg[0] = data[0];
+	msg[1] = data[1];
+
+	spi_message_init(&m);
+	memset(&t, 0, (sizeof t));
+
+	t.tx_buf = &msg[0];
+	t.len = len;
+
+	spi_message_add_tail(&t, &m);
+	spi_sync(spi, &m);
+
+	return len;
+}
+
+static int __devinit wm8776_spi_probe(struct spi_device *spi)
+{
+	struct snd_soc_codec *codec;
+	struct wm8776_priv *wm8776;
+
+	wm8776 = kzalloc(sizeof(struct wm8776_priv), GFP_KERNEL);
+	if (wm8776 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8776->codec;
+	codec->control_data = spi;
+	codec->hw_write = (hw_write_t)wm8776_spi_write;
+	codec->dev = &spi->dev;
+
+	dev_set_drvdata(&spi->dev, wm8776);
+
+	return wm8776_register(wm8776, SND_SOC_SPI);
+}
+
+static int __devexit wm8776_spi_remove(struct spi_device *spi)
+{
+	struct wm8776_priv *wm8776 = dev_get_drvdata(&spi->dev);
+
+	wm8776_unregister(wm8776);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8776_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8776_spi_resume(struct spi_device *spi)
+{
+	return snd_soc_resume_device(&spi->dev);
+}
+#else
+#define wm8776_spi_suspend NULL
+#define wm8776_spi_resume NULL
+#endif
+
+static struct spi_driver wm8776_spi_driver = {
+	.driver = {
+		.name	= "wm8776",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= wm8776_spi_probe,
+	.suspend	= wm8776_spi_suspend,
+	.resume		= wm8776_spi_resume,
+	.remove		= __devexit_p(wm8776_spi_remove),
+};
+#endif /* CONFIG_SPI_MASTER */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static __devinit int wm8776_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8776_priv *wm8776;
+	struct snd_soc_codec *codec;
+
+	wm8776 = kzalloc(sizeof(struct wm8776_priv), GFP_KERNEL);
+	if (wm8776 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8776->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(i2c, wm8776);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+
+	return wm8776_register(wm8776, SND_SOC_I2C);
+}
+
+static __devexit int wm8776_i2c_remove(struct i2c_client *client)
+{
+	struct wm8776_priv *wm8776 = i2c_get_clientdata(client);
+	wm8776_unregister(wm8776);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8776_i2c_suspend(struct i2c_client *i2c, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&i2c->dev);
+}
+
+static int wm8776_i2c_resume(struct i2c_client *i2c)
+{
+	return snd_soc_resume_device(&i2c->dev);
+}
+#else
+#define wm8776_i2c_suspend NULL
+#define wm8776_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm8776_i2c_id[] = {
+	{ "wm8776", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id);
+
+static struct i2c_driver wm8776_i2c_driver = {
+	.driver = {
+		.name = "wm8776",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm8776_i2c_probe,
+	.remove =   __devexit_p(wm8776_i2c_remove),
+	.suspend =  wm8776_i2c_suspend,
+	.resume =   wm8776_i2c_resume,
+	.id_table = wm8776_i2c_id,
+};
+#endif
+
+static int __init wm8776_modinit(void)
+{
+	int ret;
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&wm8776_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8776 I2C driver: %d\n",
+		       ret);
+	}
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	ret = spi_register_driver(&wm8776_spi_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8776 SPI driver: %d\n",
+		       ret);
+	}
+#endif
+	return 0;
+}
+module_init(wm8776_modinit);
+
+static void __exit wm8776_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8776_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	spi_unregister_driver(&wm8776_spi_driver);
+#endif
+}
+module_exit(wm8776_exit);
+
+MODULE_DESCRIPTION("ASoC WM8776 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8776.h b/sound/soc/codecs/wm8776.h
new file mode 100644
index 0000000..6606d25
--- /dev/null
+++ b/sound/soc/codecs/wm8776.h
@@ -0,0 +1,51 @@
+/*
+ * wm8776.h  --  WM8776 ASoC driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8776_H
+#define _WM8776_H
+
+/* Registers */
+
+#define WM8776_HPLVOL    0x00
+#define WM8776_HPRVOL    0x01
+#define WM8776_HPMASTER  0x02
+#define WM8776_DACLVOL   0x03
+#define WM8776_DACRVOL   0x04
+#define WM8776_DACMASTER 0x05
+#define WM8776_PHASESWAP 0x06
+#define WM8776_DACCTRL1  0x07
+#define WM8776_DACMUTE   0x08
+#define WM8776_DACCTRL2  0x09
+#define WM8776_DACIFCTRL 0x0a
+#define WM8776_ADCIFCTRL 0x0b
+#define WM8776_MSTRCTRL  0x0c
+#define WM8776_PWRDOWN   0x0d
+#define WM8776_ADCLVOL   0x0e
+#define WM8776_ADCRVOL   0x0f
+#define WM8776_ALCCTRL1  0x10
+#define WM8776_ALCCTRL2  0x11
+#define WM8776_ALCCTRL3  0x12
+#define WM8776_NOISEGATE 0x13
+#define WM8776_LIMITER   0x14
+#define WM8776_ADCMUX    0x15
+#define WM8776_OUTMUX    0x16
+#define WM8776_RESET     0x17
+
+#define WM8776_CACHEREGNUM 0x17
+
+#define WM8776_DAI_DAC 0
+#define WM8776_DAI_ADC 1
+
+extern struct snd_soc_dai wm8776_dai[];
+extern struct snd_soc_codec_device soc_codec_dev_wm8776;
+
+#endif
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 46c5ea1..5e9c855 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -116,6 +116,7 @@
 #define WM8900_REG_CLOCKING2_DAC_CLKDIV 0x1c
 
 #define WM8900_REG_DACCTRL_MUTE          0x004
+#define WM8900_REG_DACCTRL_DAC_SB_FILT   0x100
 #define WM8900_REG_DACCTRL_AIF_LRCLKRATE 0x400
 
 #define WM8900_REG_AUDIO3_ADCLRC_DIR    0x0800
@@ -182,111 +183,20 @@
 	/* Remaining registers all zero */
 };
 
-/*
- * read wm8900 register cache
- */
-static inline unsigned int wm8900_read_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg)
-{
-	u16 *cache = codec->reg_cache;
-
-	BUG_ON(reg >= WM8900_MAXREG);
-
-	if (reg == WM8900_REG_ID)
-		return 0;
-
-	return cache[reg];
-}
-
-/*
- * write wm8900 register cache
- */
-static inline void wm8900_write_reg_cache(struct snd_soc_codec *codec,
-	u16 reg, unsigned int value)
-{
-	u16 *cache = codec->reg_cache;
-
-	BUG_ON(reg >= WM8900_MAXREG);
-
-	cache[reg] = value;
-}
-
-/*
- * write to the WM8900 register space
- */
-static int wm8900_write(struct snd_soc_codec *codec, unsigned int reg,
-			unsigned int value)
-{
-	u8 data[3];
-
-	if (value == wm8900_read_reg_cache(codec, reg))
-		return 0;
-
-	/* data is
-	 *   D15..D9 WM8900 register offset
-	 *   D8...D0 register data
-	 */
-	data[0] = reg;
-	data[1] = value >> 8;
-	data[2] = value & 0x00ff;
-
-	wm8900_write_reg_cache(codec, reg, value);
-	if (codec->hw_write(codec->control_data, data, 3) == 3)
-		return 0;
-	else
-		return -EIO;
-}
-
-/*
- * Read from the wm8900.
- */
-static unsigned int wm8900_chip_read(struct snd_soc_codec *codec, u8 reg)
-{
-	struct i2c_msg xfer[2];
-	u16 data;
-	int ret;
-	struct i2c_client *client = codec->control_data;
-
-	BUG_ON(reg != WM8900_REG_ID && reg != WM8900_REG_POWER1);
-
-	/* Write register */
-	xfer[0].addr = client->addr;
-	xfer[0].flags = 0;
-	xfer[0].len = 1;
-	xfer[0].buf = &reg;
-
-	/* Read data */
-	xfer[1].addr = client->addr;
-	xfer[1].flags = I2C_M_RD;
-	xfer[1].len = 2;
-	xfer[1].buf = (u8 *)&data;
-
-	ret = i2c_transfer(client->adapter, xfer, 2);
-	if (ret != 2) {
-		printk(KERN_CRIT "i2c_transfer returned %d\n", ret);
-		return 0;
-	}
-
-	return (data >> 8) | ((data & 0xff) << 8);
-}
-
-/*
- * Read from the WM8900 register space.  Most registers can't be read
- * and are therefore supplied from cache.
- */
-static unsigned int wm8900_read(struct snd_soc_codec *codec, unsigned int reg)
+static int wm8900_volatile_register(unsigned int reg)
 {
 	switch (reg) {
 	case WM8900_REG_ID:
-		return wm8900_chip_read(codec, reg);
+	case WM8900_REG_POWER1:
+		return 1;
 	default:
-		return wm8900_read_reg_cache(codec, reg);
+		return 0;
 	}
 }
 
 static void wm8900_reset(struct snd_soc_codec *codec)
 {
-	wm8900_write(codec, WM8900_REG_RESET, 0);
+	snd_soc_write(codec, WM8900_REG_RESET, 0);
 
 	memcpy(codec->reg_cache, wm8900_reg_defaults,
 	       sizeof(codec->reg_cache));
@@ -296,14 +206,14 @@
 			   struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
-	u16 hpctl1 = wm8900_read(codec, WM8900_REG_HPCTL1);
+	u16 hpctl1 = snd_soc_read(codec, WM8900_REG_HPCTL1);
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
 		/* Clamp headphone outputs */
 		hpctl1 = WM8900_REG_HPCTL1_HP_CLAMP_IP |
 			WM8900_REG_HPCTL1_HP_CLAMP_OP;
-		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
 		break;
 
 	case SND_SOC_DAPM_POST_PMU:
@@ -312,41 +222,41 @@
 		hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT |
 			WM8900_REG_HPCTL1_HP_SHORT2 |
 			WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
-		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
 
 		msleep(400);
 
 		/* Enable the output stage */
 		hpctl1 &= ~WM8900_REG_HPCTL1_HP_CLAMP_OP;
 		hpctl1 |= WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
-		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
 
 		/* Remove the shorts */
 		hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT2;
-		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
 		hpctl1 &= ~WM8900_REG_HPCTL1_HP_SHORT;
-		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
 		break;
 
 	case SND_SOC_DAPM_PRE_PMD:
 		/* Short the output */
 		hpctl1 |= WM8900_REG_HPCTL1_HP_SHORT;
-		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
 
 		/* Disable the output stage */
 		hpctl1 &= ~WM8900_REG_HPCTL1_HP_OPSTAGE_ENA;
-		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
 
 		/* Clamp the outputs and power down input */
 		hpctl1 |= WM8900_REG_HPCTL1_HP_CLAMP_IP |
 			WM8900_REG_HPCTL1_HP_CLAMP_OP;
 		hpctl1 &= ~WM8900_REG_HPCTL1_HP_IPSTAGE_ENA;
-		wm8900_write(codec, WM8900_REG_HPCTL1, hpctl1);
+		snd_soc_write(codec, WM8900_REG_HPCTL1, hpctl1);
 		break;
 
 	case SND_SOC_DAPM_POST_PMD:
 		/* Disable everything */
-		wm8900_write(codec, WM8900_REG_HPCTL1, 0);
+		snd_soc_write(codec, WM8900_REG_HPCTL1, 0);
 		break;
 
 	default:
@@ -439,7 +349,6 @@
 SOC_ENUM("DAC Mute Rate", dac_mute_rate),
 SOC_SINGLE("DAC Mono Switch", WM8900_REG_DACCTRL, 9, 1, 0),
 SOC_ENUM("DAC Deemphasis", dac_deemphasis),
-SOC_SINGLE("DAC Sloping Stopband Filter Switch", WM8900_REG_DACCTRL, 8, 1, 0),
 SOC_SINGLE("DAC Sigma-Delta Modulator Clock Switch", WM8900_REG_DACCTRL,
 	   12, 1, 0),
 
@@ -723,7 +632,7 @@
 	struct snd_soc_codec *codec = socdev->card->codec;
 	u16 reg;
 
-	reg = wm8900_read(codec, WM8900_REG_AUDIO1) & ~0x60;
+	reg = snd_soc_read(codec, WM8900_REG_AUDIO1) & ~0x60;
 
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
@@ -741,7 +650,18 @@
 		return -EINVAL;
 	}
 
-	wm8900_write(codec, WM8900_REG_AUDIO1, reg);
+	snd_soc_write(codec, WM8900_REG_AUDIO1, reg);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		reg = snd_soc_read(codec, WM8900_REG_DACCTRL);
+
+		if (params_rate(params) <= 24000)
+			reg |= WM8900_REG_DACCTRL_DAC_SB_FILT;
+		else
+			reg &= ~WM8900_REG_DACCTRL_DAC_SB_FILT;
+
+		snd_soc_write(codec, WM8900_REG_DACCTRL, reg);
+	}
 
 	return 0;
 }
@@ -778,11 +698,11 @@
 	}
 
 	if (target > 100000000)
-		printk(KERN_WARNING "wm8900: FLL rate %d out of range, Fref=%d"
-		       " Fout=%d\n", target, Fref, Fout);
+		printk(KERN_WARNING "wm8900: FLL rate %u out of range, Fref=%u"
+		       " Fout=%u\n", target, Fref, Fout);
 	if (div > 32) {
 		printk(KERN_ERR "wm8900: Invalid FLL division rate %u, "
-		       "Fref=%d, Fout=%d, target=%d\n",
+		       "Fref=%u, Fout=%u, target=%u\n",
 		       div, Fref, Fout, target);
 		return -EINVAL;
 	}
@@ -834,18 +754,18 @@
 		return 0;
 
 	/* The digital side should be disabled during any change. */
-	reg = wm8900_read(codec, WM8900_REG_POWER1);
-	wm8900_write(codec, WM8900_REG_POWER1,
+	reg = snd_soc_read(codec, WM8900_REG_POWER1);
+	snd_soc_write(codec, WM8900_REG_POWER1,
 		     reg & (~WM8900_REG_POWER1_FLL_ENA));
 
 	/* Disable the FLL? */
 	if (!freq_in || !freq_out) {
-		reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
-		wm8900_write(codec, WM8900_REG_CLOCKING1,
+		reg = snd_soc_read(codec, WM8900_REG_CLOCKING1);
+		snd_soc_write(codec, WM8900_REG_CLOCKING1,
 			     reg & (~WM8900_REG_CLOCKING1_MCLK_SRC));
 
-		reg = wm8900_read(codec, WM8900_REG_FLLCTL1);
-		wm8900_write(codec, WM8900_REG_FLLCTL1,
+		reg = snd_soc_read(codec, WM8900_REG_FLLCTL1);
+		snd_soc_write(codec, WM8900_REG_FLLCTL1,
 			     reg & (~WM8900_REG_FLLCTL1_OSC_ENA));
 
 		wm8900->fll_in = freq_in;
@@ -862,33 +782,33 @@
 
 	/* The osclilator *MUST* be enabled before we enable the
 	 * digital circuit. */
-	wm8900_write(codec, WM8900_REG_FLLCTL1,
+	snd_soc_write(codec, WM8900_REG_FLLCTL1,
 		     fll_div.fll_ratio | WM8900_REG_FLLCTL1_OSC_ENA);
 
-	wm8900_write(codec, WM8900_REG_FLLCTL4, fll_div.n >> 5);
-	wm8900_write(codec, WM8900_REG_FLLCTL5,
+	snd_soc_write(codec, WM8900_REG_FLLCTL4, fll_div.n >> 5);
+	snd_soc_write(codec, WM8900_REG_FLLCTL5,
 		     (fll_div.fllclk_div << 6) | (fll_div.n & 0x1f));
 
 	if (fll_div.k) {
-		wm8900_write(codec, WM8900_REG_FLLCTL2,
+		snd_soc_write(codec, WM8900_REG_FLLCTL2,
 			     (fll_div.k >> 8) | 0x100);
-		wm8900_write(codec, WM8900_REG_FLLCTL3, fll_div.k & 0xff);
+		snd_soc_write(codec, WM8900_REG_FLLCTL3, fll_div.k & 0xff);
 	} else
-		wm8900_write(codec, WM8900_REG_FLLCTL2, 0);
+		snd_soc_write(codec, WM8900_REG_FLLCTL2, 0);
 
 	if (fll_div.fll_slow_lock_ref)
-		wm8900_write(codec, WM8900_REG_FLLCTL6,
+		snd_soc_write(codec, WM8900_REG_FLLCTL6,
 			     WM8900_REG_FLLCTL6_FLL_SLOW_LOCK_REF);
 	else
-		wm8900_write(codec, WM8900_REG_FLLCTL6, 0);
+		snd_soc_write(codec, WM8900_REG_FLLCTL6, 0);
 
-	reg = wm8900_read(codec, WM8900_REG_POWER1);
-	wm8900_write(codec, WM8900_REG_POWER1,
+	reg = snd_soc_read(codec, WM8900_REG_POWER1);
+	snd_soc_write(codec, WM8900_REG_POWER1,
 		     reg | WM8900_REG_POWER1_FLL_ENA);
 
 reenable:
-	reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
-	wm8900_write(codec, WM8900_REG_CLOCKING1,
+	reg = snd_soc_read(codec, WM8900_REG_CLOCKING1);
+	snd_soc_write(codec, WM8900_REG_CLOCKING1,
 		     reg | WM8900_REG_CLOCKING1_MCLK_SRC);
 
 	return 0;
@@ -908,38 +828,38 @@
 
 	switch (div_id) {
 	case WM8900_BCLK_DIV:
-		reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
-		wm8900_write(codec, WM8900_REG_CLOCKING1,
+		reg = snd_soc_read(codec, WM8900_REG_CLOCKING1);
+		snd_soc_write(codec, WM8900_REG_CLOCKING1,
 			     div | (reg & WM8900_REG_CLOCKING1_BCLK_MASK));
 		break;
 	case WM8900_OPCLK_DIV:
-		reg = wm8900_read(codec, WM8900_REG_CLOCKING1);
-		wm8900_write(codec, WM8900_REG_CLOCKING1,
+		reg = snd_soc_read(codec, WM8900_REG_CLOCKING1);
+		snd_soc_write(codec, WM8900_REG_CLOCKING1,
 			     div | (reg & WM8900_REG_CLOCKING1_OPCLK_MASK));
 		break;
 	case WM8900_DAC_LRCLK:
-		reg = wm8900_read(codec, WM8900_REG_AUDIO4);
-		wm8900_write(codec, WM8900_REG_AUDIO4,
+		reg = snd_soc_read(codec, WM8900_REG_AUDIO4);
+		snd_soc_write(codec, WM8900_REG_AUDIO4,
 			     div | (reg & WM8900_LRC_MASK));
 		break;
 	case WM8900_ADC_LRCLK:
-		reg = wm8900_read(codec, WM8900_REG_AUDIO3);
-		wm8900_write(codec, WM8900_REG_AUDIO3,
+		reg = snd_soc_read(codec, WM8900_REG_AUDIO3);
+		snd_soc_write(codec, WM8900_REG_AUDIO3,
 			     div | (reg & WM8900_LRC_MASK));
 		break;
 	case WM8900_DAC_CLKDIV:
-		reg = wm8900_read(codec, WM8900_REG_CLOCKING2);
-		wm8900_write(codec, WM8900_REG_CLOCKING2,
+		reg = snd_soc_read(codec, WM8900_REG_CLOCKING2);
+		snd_soc_write(codec, WM8900_REG_CLOCKING2,
 			     div | (reg & WM8900_REG_CLOCKING2_DAC_CLKDIV));
 		break;
 	case WM8900_ADC_CLKDIV:
-		reg = wm8900_read(codec, WM8900_REG_CLOCKING2);
-		wm8900_write(codec, WM8900_REG_CLOCKING2,
+		reg = snd_soc_read(codec, WM8900_REG_CLOCKING2);
+		snd_soc_write(codec, WM8900_REG_CLOCKING2,
 			     div | (reg & WM8900_REG_CLOCKING2_ADC_CLKDIV));
 		break;
 	case WM8900_LRCLK_MODE:
-		reg = wm8900_read(codec, WM8900_REG_DACCTRL);
-		wm8900_write(codec, WM8900_REG_DACCTRL,
+		reg = snd_soc_read(codec, WM8900_REG_DACCTRL);
+		snd_soc_write(codec, WM8900_REG_DACCTRL,
 			     div | (reg & WM8900_REG_DACCTRL_AIF_LRCLKRATE));
 		break;
 	default:
@@ -956,10 +876,10 @@
 	struct snd_soc_codec *codec = codec_dai->codec;
 	unsigned int clocking1, aif1, aif3, aif4;
 
-	clocking1 = wm8900_read(codec, WM8900_REG_CLOCKING1);
-	aif1 = wm8900_read(codec, WM8900_REG_AUDIO1);
-	aif3 = wm8900_read(codec, WM8900_REG_AUDIO3);
-	aif4 = wm8900_read(codec, WM8900_REG_AUDIO4);
+	clocking1 = snd_soc_read(codec, WM8900_REG_CLOCKING1);
+	aif1 = snd_soc_read(codec, WM8900_REG_AUDIO1);
+	aif3 = snd_soc_read(codec, WM8900_REG_AUDIO3);
+	aif4 = snd_soc_read(codec, WM8900_REG_AUDIO4);
 
 	/* set master/slave audio interface */
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -1055,10 +975,10 @@
 		return -EINVAL;
 	}
 
-	wm8900_write(codec, WM8900_REG_CLOCKING1, clocking1);
-	wm8900_write(codec, WM8900_REG_AUDIO1, aif1);
-	wm8900_write(codec, WM8900_REG_AUDIO3, aif3);
-	wm8900_write(codec, WM8900_REG_AUDIO4, aif4);
+	snd_soc_write(codec, WM8900_REG_CLOCKING1, clocking1);
+	snd_soc_write(codec, WM8900_REG_AUDIO1, aif1);
+	snd_soc_write(codec, WM8900_REG_AUDIO3, aif3);
+	snd_soc_write(codec, WM8900_REG_AUDIO4, aif4);
 
 	return 0;
 }
@@ -1068,14 +988,14 @@
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 reg;
 
-	reg = wm8900_read(codec, WM8900_REG_DACCTRL);
+	reg = snd_soc_read(codec, WM8900_REG_DACCTRL);
 
 	if (mute)
 		reg |= WM8900_REG_DACCTRL_MUTE;
 	else
 		reg &= ~WM8900_REG_DACCTRL_MUTE;
 
-	wm8900_write(codec, WM8900_REG_DACCTRL, reg);
+	snd_soc_write(codec, WM8900_REG_DACCTRL, reg);
 
 	return 0;
 }
@@ -1124,11 +1044,11 @@
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 		/* Enable thermal shutdown */
-		reg = wm8900_read(codec, WM8900_REG_GPIO);
-		wm8900_write(codec, WM8900_REG_GPIO,
+		reg = snd_soc_read(codec, WM8900_REG_GPIO);
+		snd_soc_write(codec, WM8900_REG_GPIO,
 			     reg | WM8900_REG_GPIO_TEMP_ENA);
-		reg = wm8900_read(codec, WM8900_REG_ADDCTL);
-		wm8900_write(codec, WM8900_REG_ADDCTL,
+		reg = snd_soc_read(codec, WM8900_REG_ADDCTL);
+		snd_soc_write(codec, WM8900_REG_ADDCTL,
 			     reg | WM8900_REG_ADDCTL_TEMP_SD);
 		break;
 
@@ -1139,69 +1059,69 @@
 		/* Charge capacitors if initial power up */
 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
 			/* STARTUP_BIAS_ENA on */
-			wm8900_write(codec, WM8900_REG_POWER1,
+			snd_soc_write(codec, WM8900_REG_POWER1,
 				     WM8900_REG_POWER1_STARTUP_BIAS_ENA);
 
 			/* Startup bias mode */
-			wm8900_write(codec, WM8900_REG_ADDCTL,
+			snd_soc_write(codec, WM8900_REG_ADDCTL,
 				     WM8900_REG_ADDCTL_BIAS_SRC |
 				     WM8900_REG_ADDCTL_VMID_SOFTST);
 
 			/* VMID 2x50k */
-			wm8900_write(codec, WM8900_REG_POWER1,
+			snd_soc_write(codec, WM8900_REG_POWER1,
 				     WM8900_REG_POWER1_STARTUP_BIAS_ENA | 0x1);
 
 			/* Allow capacitors to charge */
 			schedule_timeout_interruptible(msecs_to_jiffies(400));
 
 			/* Enable bias */
-			wm8900_write(codec, WM8900_REG_POWER1,
+			snd_soc_write(codec, WM8900_REG_POWER1,
 				     WM8900_REG_POWER1_STARTUP_BIAS_ENA |
 				     WM8900_REG_POWER1_BIAS_ENA | 0x1);
 
-			wm8900_write(codec, WM8900_REG_ADDCTL, 0);
+			snd_soc_write(codec, WM8900_REG_ADDCTL, 0);
 
-			wm8900_write(codec, WM8900_REG_POWER1,
+			snd_soc_write(codec, WM8900_REG_POWER1,
 				     WM8900_REG_POWER1_BIAS_ENA | 0x1);
 		}
 
-		reg = wm8900_read(codec, WM8900_REG_POWER1);
-		wm8900_write(codec, WM8900_REG_POWER1,
+		reg = snd_soc_read(codec, WM8900_REG_POWER1);
+		snd_soc_write(codec, WM8900_REG_POWER1,
 			     (reg & WM8900_REG_POWER1_FLL_ENA) |
 			     WM8900_REG_POWER1_BIAS_ENA | 0x1);
-		wm8900_write(codec, WM8900_REG_POWER2,
+		snd_soc_write(codec, WM8900_REG_POWER2,
 			     WM8900_REG_POWER2_SYSCLK_ENA);
-		wm8900_write(codec, WM8900_REG_POWER3, 0);
+		snd_soc_write(codec, WM8900_REG_POWER3, 0);
 		break;
 
 	case SND_SOC_BIAS_OFF:
 		/* Startup bias enable */
-		reg = wm8900_read(codec, WM8900_REG_POWER1);
-		wm8900_write(codec, WM8900_REG_POWER1,
+		reg = snd_soc_read(codec, WM8900_REG_POWER1);
+		snd_soc_write(codec, WM8900_REG_POWER1,
 			     reg & WM8900_REG_POWER1_STARTUP_BIAS_ENA);
-		wm8900_write(codec, WM8900_REG_ADDCTL,
+		snd_soc_write(codec, WM8900_REG_ADDCTL,
 			     WM8900_REG_ADDCTL_BIAS_SRC |
 			     WM8900_REG_ADDCTL_VMID_SOFTST);
 
 		/* Discharge caps */
-		wm8900_write(codec, WM8900_REG_POWER1,
+		snd_soc_write(codec, WM8900_REG_POWER1,
 			     WM8900_REG_POWER1_STARTUP_BIAS_ENA);
 		schedule_timeout_interruptible(msecs_to_jiffies(500));
 
 		/* Remove clamp */
-		wm8900_write(codec, WM8900_REG_HPCTL1, 0);
+		snd_soc_write(codec, WM8900_REG_HPCTL1, 0);
 
 		/* Power down */
-		wm8900_write(codec, WM8900_REG_ADDCTL, 0);
-		wm8900_write(codec, WM8900_REG_POWER1, 0);
-		wm8900_write(codec, WM8900_REG_POWER2, 0);
-		wm8900_write(codec, WM8900_REG_POWER3, 0);
+		snd_soc_write(codec, WM8900_REG_ADDCTL, 0);
+		snd_soc_write(codec, WM8900_REG_POWER1, 0);
+		snd_soc_write(codec, WM8900_REG_POWER2, 0);
+		snd_soc_write(codec, WM8900_REG_POWER3, 0);
 
 		/* Need to let things settle before stopping the clock
 		 * to ensure that restart works, see "Stopping the
 		 * master clock" in the datasheet. */
 		schedule_timeout_interruptible(msecs_to_jiffies(1));
-		wm8900_write(codec, WM8900_REG_POWER2,
+		snd_soc_write(codec, WM8900_REG_POWER2,
 			     WM8900_REG_POWER2_SYSCLK_ENA);
 		break;
 	}
@@ -1264,7 +1184,7 @@
 
 	if (cache) {
 		for (i = 0; i < WM8900_MAXREG; i++)
-			wm8900_write(codec, i, cache[i]);
+			snd_soc_write(codec, i, cache[i]);
 		kfree(cache);
 	} else
 		dev_err(&pdev->dev, "Unable to allocate register cache\n");
@@ -1297,16 +1217,20 @@
 
 	codec->name = "WM8900";
 	codec->owner = THIS_MODULE;
-	codec->read = wm8900_read;
-	codec->write = wm8900_write;
 	codec->dai = &wm8900_dai;
 	codec->num_dai = 1;
-	codec->hw_write = (hw_write_t)i2c_master_send;
 	codec->control_data = i2c;
 	codec->set_bias_level = wm8900_set_bias_level;
+	codec->volatile_register = wm8900_volatile_register;
 	codec->dev = &i2c->dev;
 
-	reg = wm8900_read(codec, WM8900_REG_ID);
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	reg = snd_soc_read(codec, WM8900_REG_ID);
 	if (reg != 0x8900) {
 		dev_err(&i2c->dev, "Device is not a WM8900 - ID %x\n", reg);
 		ret = -ENODEV;
@@ -1314,7 +1238,7 @@
 	}
 
 	/* Read back from the chip */
-	reg = wm8900_chip_read(codec, WM8900_REG_POWER1);
+	reg = snd_soc_read(codec, WM8900_REG_POWER1);
 	reg = (reg >> 12) & 0xf;
 	dev_info(&i2c->dev, "WM8900 revision %d\n", reg);
 
@@ -1324,29 +1248,29 @@
 	wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* Latch the volume update bits */
-	wm8900_write(codec, WM8900_REG_LINVOL,
-		     wm8900_read(codec, WM8900_REG_LINVOL) | 0x100);
-	wm8900_write(codec, WM8900_REG_RINVOL,
-		     wm8900_read(codec, WM8900_REG_RINVOL) | 0x100);
-	wm8900_write(codec, WM8900_REG_LOUT1CTL,
-		     wm8900_read(codec, WM8900_REG_LOUT1CTL) | 0x100);
-	wm8900_write(codec, WM8900_REG_ROUT1CTL,
-		     wm8900_read(codec, WM8900_REG_ROUT1CTL) | 0x100);
-	wm8900_write(codec, WM8900_REG_LOUT2CTL,
-		     wm8900_read(codec, WM8900_REG_LOUT2CTL) | 0x100);
-	wm8900_write(codec, WM8900_REG_ROUT2CTL,
-		     wm8900_read(codec, WM8900_REG_ROUT2CTL) | 0x100);
-	wm8900_write(codec, WM8900_REG_LDAC_DV,
-		     wm8900_read(codec, WM8900_REG_LDAC_DV) | 0x100);
-	wm8900_write(codec, WM8900_REG_RDAC_DV,
-		     wm8900_read(codec, WM8900_REG_RDAC_DV) | 0x100);
-	wm8900_write(codec, WM8900_REG_LADC_DV,
-		     wm8900_read(codec, WM8900_REG_LADC_DV) | 0x100);
-	wm8900_write(codec, WM8900_REG_RADC_DV,
-		     wm8900_read(codec, WM8900_REG_RADC_DV) | 0x100);
+	snd_soc_write(codec, WM8900_REG_LINVOL,
+		      snd_soc_read(codec, WM8900_REG_LINVOL) | 0x100);
+	snd_soc_write(codec, WM8900_REG_RINVOL,
+		      snd_soc_read(codec, WM8900_REG_RINVOL) | 0x100);
+	snd_soc_write(codec, WM8900_REG_LOUT1CTL,
+		      snd_soc_read(codec, WM8900_REG_LOUT1CTL) | 0x100);
+	snd_soc_write(codec, WM8900_REG_ROUT1CTL,
+		      snd_soc_read(codec, WM8900_REG_ROUT1CTL) | 0x100);
+	snd_soc_write(codec, WM8900_REG_LOUT2CTL,
+		      snd_soc_read(codec, WM8900_REG_LOUT2CTL) | 0x100);
+	snd_soc_write(codec, WM8900_REG_ROUT2CTL,
+		      snd_soc_read(codec, WM8900_REG_ROUT2CTL) | 0x100);
+	snd_soc_write(codec, WM8900_REG_LDAC_DV,
+		      snd_soc_read(codec, WM8900_REG_LDAC_DV) | 0x100);
+	snd_soc_write(codec, WM8900_REG_RDAC_DV,
+		      snd_soc_read(codec, WM8900_REG_RDAC_DV) | 0x100);
+	snd_soc_write(codec, WM8900_REG_LADC_DV,
+		      snd_soc_read(codec, WM8900_REG_LADC_DV) | 0x100);
+	snd_soc_write(codec, WM8900_REG_RADC_DV,
+		      snd_soc_read(codec, WM8900_REG_RADC_DV) | 0x100);
 
 	/* Set the DAC and mixer output bias */
-	wm8900_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
+	snd_soc_write(codec, WM8900_REG_OUTBIASCTL, 0x81);
 
 	wm8900_dai.dev = &i2c->dev;
 
@@ -1388,6 +1312,21 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8900_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8900_i2c_resume(struct i2c_client *client)
+{
+	return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8900_i2c_suspend NULL
+#define wm8900_i2c_resume NULL
+#endif
+
 static const struct i2c_device_id wm8900_i2c_id[] = {
 	{ "wm8900", 0 },
 	{ }
@@ -1401,6 +1340,8 @@
 	},
 	.probe = wm8900_i2c_probe,
 	.remove = __devexit_p(wm8900_i2c_remove),
+	.suspend = wm8900_i2c_suspend,
+	.resume = wm8900_i2c_resume,
 	.id_table = wm8900_i2c_id,
 };
 
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 8cf571f..fe1307b 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -217,7 +217,6 @@
 	int sysclk;
 
 	/* Reference counts */
-	int charge_pump_users;
 	int class_w_users;
 	int playback_active;
 	int capture_active;
@@ -226,94 +225,18 @@
 	struct snd_pcm_substream *slave_substream;
 };
 
-
-static unsigned int wm8903_read_reg_cache(struct snd_soc_codec *codec,
-						 unsigned int reg)
-{
-	u16 *cache = codec->reg_cache;
-
-	BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults));
-
-	return cache[reg];
-}
-
-static unsigned int wm8903_hw_read(struct snd_soc_codec *codec, u8 reg)
-{
-	struct i2c_msg xfer[2];
-	u16 data;
-	int ret;
-	struct i2c_client *client = codec->control_data;
-
-	/* Write register */
-	xfer[0].addr = client->addr;
-	xfer[0].flags = 0;
-	xfer[0].len = 1;
-	xfer[0].buf = &reg;
-
-	/* Read data */
-	xfer[1].addr = client->addr;
-	xfer[1].flags = I2C_M_RD;
-	xfer[1].len = 2;
-	xfer[1].buf = (u8 *)&data;
-
-	ret = i2c_transfer(client->adapter, xfer, 2);
-	if (ret != 2) {
-		pr_err("i2c_transfer returned %d\n", ret);
-		return 0;
-	}
-
-	return (data >> 8) | ((data & 0xff) << 8);
-}
-
-static unsigned int wm8903_read(struct snd_soc_codec *codec,
-				unsigned int reg)
+static int wm8903_volatile_register(unsigned int reg)
 {
 	switch (reg) {
 	case WM8903_SW_RESET_AND_ID:
 	case WM8903_REVISION_NUMBER:
 	case WM8903_INTERRUPT_STATUS_1:
 	case WM8903_WRITE_SEQUENCER_4:
-		return wm8903_hw_read(codec, reg);
+		return 1;
 
 	default:
-		return wm8903_read_reg_cache(codec, reg);
-	}
-}
-
-static void wm8903_write_reg_cache(struct snd_soc_codec *codec,
-				   u16 reg, unsigned int value)
-{
-	u16 *cache = codec->reg_cache;
-
-	BUG_ON(reg >= ARRAY_SIZE(wm8903_reg_defaults));
-
-	switch (reg) {
-	case WM8903_SW_RESET_AND_ID:
-	case WM8903_REVISION_NUMBER:
-		break;
-
-	default:
-		cache[reg] = value;
-		break;
-	}
-}
-
-static int wm8903_write(struct snd_soc_codec *codec, unsigned int reg,
-			unsigned int value)
-{
-	u8 data[3];
-
-	wm8903_write_reg_cache(codec, reg, value);
-
-	/* Data format is 1 byte of address followed by 2 bytes of data */
-	data[0] = reg;
-	data[1] = (value >> 8) & 0xff;
-	data[2] = value & 0xff;
-
-	if (codec->hw_write(codec->control_data, data, 3) == 2)
 		return 0;
-	else
-		return -EIO;
+	}
 }
 
 static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start)
@@ -324,13 +247,13 @@
 	BUG_ON(start > 48);
 
 	/* Enable the sequencer */
-	reg[0] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_0);
+	reg[0] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_0);
 	reg[0] |= WM8903_WSEQ_ENA;
-	wm8903_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]);
+	snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]);
 
 	dev_dbg(&i2c->dev, "Starting sequence at %d\n", start);
 
-	wm8903_write(codec, WM8903_WRITE_SEQUENCER_3,
+	snd_soc_write(codec, WM8903_WRITE_SEQUENCER_3,
 		     start | WM8903_WSEQ_START);
 
 	/* Wait for it to complete.  If we have the interrupt wired up then
@@ -340,13 +263,13 @@
 	do {
 		msleep(10);
 
-		reg[4] = wm8903_read(codec, WM8903_WRITE_SEQUENCER_4);
+		reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4);
 	} while (reg[4] & WM8903_WSEQ_BUSY);
 
 	dev_dbg(&i2c->dev, "Sequence complete\n");
 
 	/* Disable the sequencer again */
-	wm8903_write(codec, WM8903_WRITE_SEQUENCER_0,
+	snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0,
 		     reg[0] & ~WM8903_WSEQ_ENA);
 
 	return 0;
@@ -358,12 +281,12 @@
 
 	/* There really ought to be something better we can do here :/ */
 	for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
-		cache[i] = wm8903_hw_read(codec, i);
+		cache[i] = codec->hw_read(codec, i);
 }
 
 static void wm8903_reset(struct snd_soc_codec *codec)
 {
-	wm8903_write(codec, WM8903_SW_RESET_AND_ID, 0);
+	snd_soc_write(codec, WM8903_SW_RESET_AND_ID, 0);
 	memcpy(codec->reg_cache, wm8903_reg_defaults,
 	       sizeof(wm8903_reg_defaults));
 }
@@ -373,6 +296,15 @@
 #define WM8903_OUTPUT_INT   0x2
 #define WM8903_OUTPUT_IN    0x1
 
+static int wm8903_cp_event(struct snd_soc_dapm_widget *w,
+			   struct snd_kcontrol *kcontrol, int event)
+{
+	WARN_ON(event != SND_SOC_DAPM_POST_PMU);
+	mdelay(4);
+
+	return 0;
+}
+
 /*
  * Event for headphone and line out amplifier power changes.  Special
  * power up/down sequences are required in order to maximise pop/click
@@ -382,19 +314,20 @@
 			       struct snd_kcontrol *kcontrol, int event)
 {
 	struct snd_soc_codec *codec = w->codec;
-	struct wm8903_priv *wm8903 = codec->private_data;
-	struct i2c_client *i2c = codec->control_data;
 	u16 val;
 	u16 reg;
+	u16 dcs_reg;
+	u16 dcs_bit;
 	int shift;
-	u16 cp_reg = wm8903_read(codec, WM8903_CHARGE_PUMP_0);
 
 	switch (w->reg) {
 	case WM8903_POWER_MANAGEMENT_2:
 		reg = WM8903_ANALOGUE_HP_0;
+		dcs_bit = 0 + w->shift;
 		break;
 	case WM8903_POWER_MANAGEMENT_3:
 		reg = WM8903_ANALOGUE_LINEOUT_0;
+		dcs_bit = 2 + w->shift;
 		break;
 	default:
 		BUG();
@@ -414,67 +347,52 @@
 	}
 
 	if (event & SND_SOC_DAPM_PRE_PMU) {
-		val = wm8903_read(codec, reg);
+		val = snd_soc_read(codec, reg);
 
 		/* Short the output */
 		val &= ~(WM8903_OUTPUT_SHORT << shift);
-		wm8903_write(codec, reg, val);
-
-		wm8903->charge_pump_users++;
-
-		dev_dbg(&i2c->dev, "Charge pump use count now %d\n",
-			wm8903->charge_pump_users);
-
-		if (wm8903->charge_pump_users == 1) {
-			dev_dbg(&i2c->dev, "Enabling charge pump\n");
-			wm8903_write(codec, WM8903_CHARGE_PUMP_0,
-				     cp_reg | WM8903_CP_ENA);
-			mdelay(4);
-		}
+		snd_soc_write(codec, reg, val);
 	}
 
 	if (event & SND_SOC_DAPM_POST_PMU) {
-		val = wm8903_read(codec, reg);
+		val = snd_soc_read(codec, reg);
 
 		val |= (WM8903_OUTPUT_IN << shift);
-		wm8903_write(codec, reg, val);
+		snd_soc_write(codec, reg, val);
 
 		val |= (WM8903_OUTPUT_INT << shift);
-		wm8903_write(codec, reg, val);
+		snd_soc_write(codec, reg, val);
 
 		/* Turn on the output ENA_OUTP */
 		val |= (WM8903_OUTPUT_OUT << shift);
-		wm8903_write(codec, reg, val);
+		snd_soc_write(codec, reg, val);
+
+		/* Enable the DC servo */
+		dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0);
+		dcs_reg |= dcs_bit;
+		snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg);
 
 		/* Remove the short */
 		val |= (WM8903_OUTPUT_SHORT << shift);
-		wm8903_write(codec, reg, val);
+		snd_soc_write(codec, reg, val);
 	}
 
 	if (event & SND_SOC_DAPM_PRE_PMD) {
-		val = wm8903_read(codec, reg);
+		val = snd_soc_read(codec, reg);
 
 		/* Short the output */
 		val &= ~(WM8903_OUTPUT_SHORT << shift);
-		wm8903_write(codec, reg, val);
+		snd_soc_write(codec, reg, val);
+
+		/* Disable the DC servo */
+		dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0);
+		dcs_reg &= ~dcs_bit;
+		snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg);
 
 		/* Then disable the intermediate and output stages */
 		val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT |
 			  WM8903_OUTPUT_IN) << shift);
-		wm8903_write(codec, reg, val);
-	}
-
-	if (event & SND_SOC_DAPM_POST_PMD) {
-		wm8903->charge_pump_users--;
-
-		dev_dbg(&i2c->dev, "Charge pump use count now %d\n",
-			wm8903->charge_pump_users);
-
-		if (wm8903->charge_pump_users == 0) {
-			dev_dbg(&i2c->dev, "Disabling charge pump\n");
-			wm8903_write(codec, WM8903_CHARGE_PUMP_0,
-				     cp_reg & ~WM8903_CP_ENA);
-		}
+		snd_soc_write(codec, reg, val);
 	}
 
 	return 0;
@@ -498,13 +416,13 @@
 	u16 reg;
 	int ret;
 
-	reg = wm8903_read(codec, WM8903_CLASS_W_0);
+	reg = snd_soc_read(codec, WM8903_CLASS_W_0);
 
 	/* Turn it off if we're about to enable bypass */
 	if (ucontrol->value.integer.value[0]) {
 		if (wm8903->class_w_users == 0) {
 			dev_dbg(&i2c->dev, "Disabling Class W\n");
-			wm8903_write(codec, WM8903_CLASS_W_0, reg &
+			snd_soc_write(codec, WM8903_CLASS_W_0, reg &
 				     ~(WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V));
 		}
 		wm8903->class_w_users++;
@@ -517,7 +435,7 @@
 	if (!ucontrol->value.integer.value[0]) {
 		if (wm8903->class_w_users == 1) {
 			dev_dbg(&i2c->dev, "Enabling Class W\n");
-			wm8903_write(codec, WM8903_CLASS_W_0, reg |
+			snd_soc_write(codec, WM8903_CLASS_W_0, reg |
 				     WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);
 		}
 		wm8903->class_w_users--;
@@ -539,6 +457,7 @@
 /* ALSA can only do steps of .01dB */
 static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
 
+static const DECLARE_TLV_DB_SCALE(digital_sidetone_tlv, -3600, 300, 0);
 static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
 
 static const DECLARE_TLV_DB_SCALE(drc_tlv_thresh, 0, 75, 0);
@@ -657,6 +576,16 @@
 	SOC_ENUM_SINGLE(WM8903_ANALOGUE_RIGHT_INPUT_1, 4, 3, rinput_mux_text);
 
 
+static const char *sidetone_text[] = {
+	"None", "Left", "Right"
+};
+
+static const struct soc_enum lsidetone_enum =
+	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 2, 3, sidetone_text);
+
+static const struct soc_enum rsidetone_enum =
+	SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text);
+
 static const struct snd_kcontrol_new wm8903_snd_controls[] = {
 
 /* Input PGAs - No TLV since the scale depends on PGA mode */
@@ -700,6 +629,9 @@
 SOC_ENUM("ADC Companding Mode", adc_companding),
 SOC_SINGLE("ADC Companding Switch", WM8903_AUDIO_INTERFACE_0, 3, 1, 0),
 
+SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8903_DAC_DIGITAL_0, 4, 8,
+	       12, 0, digital_sidetone_tlv),
+
 /* DAC */
 SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8903_DAC_DIGITAL_VOLUME_LEFT,
 		 WM8903_DAC_DIGITAL_VOLUME_RIGHT, 1, 120, 0, digital_tlv),
@@ -707,8 +639,6 @@
 SOC_ENUM("DAC Mute Mode", mute_mode),
 SOC_SINGLE("DAC Mono Switch", WM8903_DAC_DIGITAL_1, 12, 1, 0),
 SOC_ENUM("DAC De-emphasis", dac_deemphasis),
-SOC_SINGLE("DAC Sloping Stopband Filter Switch",
-	   WM8903_DAC_DIGITAL_1, 11, 1, 0),
 SOC_ENUM("DAC Companding Mode", dac_companding),
 SOC_SINGLE("DAC Companding Switch", WM8903_AUDIO_INTERFACE_0, 1, 1, 0),
 
@@ -762,6 +692,12 @@
 static const struct snd_kcontrol_new rinput_inv_mux =
 	SOC_DAPM_ENUM("Right Inverting Input Mux", rinput_inv_enum);
 
+static const struct snd_kcontrol_new lsidetone_mux =
+	SOC_DAPM_ENUM("DACL Sidetone Mux", lsidetone_enum);
+
+static const struct snd_kcontrol_new rsidetone_mux =
+	SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum);
+
 static const struct snd_kcontrol_new left_output_mixer[] = {
 SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
@@ -828,6 +764,9 @@
 SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0),
 SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0),
 
+SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &lsidetone_mux),
+SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &rsidetone_mux),
+
 SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0),
 SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0),
 
@@ -844,26 +783,29 @@
 SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
 		   1, 0, NULL, 0, wm8903_output_event,
 		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+		   SND_SOC_DAPM_PRE_PMD),
 SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
 		   0, 0, NULL, 0, wm8903_output_event,
 		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+		   SND_SOC_DAPM_PRE_PMD),
 
 SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0,
 		   NULL, 0, wm8903_output_event,
 		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+		   SND_SOC_DAPM_PRE_PMD),
 SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0,
 		   NULL, 0, wm8903_output_event,
 		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+		   SND_SOC_DAPM_PRE_PMD),
 
 SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0,
 		 NULL, 0),
 SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0,
 		 NULL, 0),
 
+SND_SOC_DAPM_SUPPLY("Charge Pump", WM8903_CHARGE_PUMP_0, 0, 0,
+		    wm8903_cp_event, SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0),
 };
 
 static const struct snd_soc_dapm_route intercon[] = {
@@ -909,7 +851,19 @@
 	{ "Right Input PGA", NULL, "Right Input Mode Mux" },
 
 	{ "ADCL", NULL, "Left Input PGA" },
+	{ "ADCL", NULL, "CLK_DSP" },
 	{ "ADCR", NULL, "Right Input PGA" },
+	{ "ADCR", NULL, "CLK_DSP" },
+
+	{ "DACL Sidetone", "Left", "ADCL" },
+	{ "DACL Sidetone", "Right", "ADCR" },
+	{ "DACR Sidetone", "Left", "ADCL" },
+	{ "DACR Sidetone", "Right", "ADCR" },
+
+	{ "DACL", NULL, "DACL Sidetone" },
+	{ "DACL", NULL, "CLK_DSP" },
+	{ "DACR", NULL, "DACR Sidetone" },
+	{ "DACR", NULL, "CLK_DSP" },
 
 	{ "Left Output Mixer", "Left Bypass Switch", "Left Input PGA" },
 	{ "Left Output Mixer", "Right Bypass Switch", "Right Input PGA" },
@@ -951,6 +905,11 @@
 
 	{ "ROP", NULL, "Right Speaker PGA" },
 	{ "RON", NULL, "Right Speaker PGA" },
+
+	{ "Left Headphone Output PGA", NULL, "Charge Pump" },
+	{ "Right Headphone Output PGA", NULL, "Charge Pump" },
+	{ "Left Line Output PGA", NULL, "Charge Pump" },
+	{ "Right Line Output PGA", NULL, "Charge Pump" },
 };
 
 static int wm8903_add_widgets(struct snd_soc_codec *codec)
@@ -974,50 +933,55 @@
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 	case SND_SOC_BIAS_PREPARE:
-		reg = wm8903_read(codec, WM8903_VMID_CONTROL_0);
+		reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);
 		reg &= ~(WM8903_VMID_RES_MASK);
 		reg |= WM8903_VMID_RES_50K;
-		wm8903_write(codec, WM8903_VMID_CONTROL_0, reg);
+		snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
-			wm8903_write(codec, WM8903_CLOCK_RATES_2,
+			snd_soc_write(codec, WM8903_CLOCK_RATES_2,
 				     WM8903_CLK_SYS_ENA);
 
+			/* Change DC servo dither level in startup sequence */
+			snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11);
+			snd_soc_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257);
+			snd_soc_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2);
+
 			wm8903_run_sequence(codec, 0);
 			wm8903_sync_reg_cache(codec, codec->reg_cache);
 
 			/* Enable low impedence charge pump output */
-			reg = wm8903_read(codec,
+			reg = snd_soc_read(codec,
 					  WM8903_CONTROL_INTERFACE_TEST_1);
-			wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
+			snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
 				     reg | WM8903_TEST_KEY);
-			reg2 = wm8903_read(codec, WM8903_CHARGE_PUMP_TEST_1);
-			wm8903_write(codec, WM8903_CHARGE_PUMP_TEST_1,
+			reg2 = snd_soc_read(codec, WM8903_CHARGE_PUMP_TEST_1);
+			snd_soc_write(codec, WM8903_CHARGE_PUMP_TEST_1,
 				     reg2 | WM8903_CP_SW_KELVIN_MODE_MASK);
-			wm8903_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
+			snd_soc_write(codec, WM8903_CONTROL_INTERFACE_TEST_1,
 				     reg);
 
 			/* By default no bypass paths are enabled so
 			 * enable Class W support.
 			 */
 			dev_dbg(&i2c->dev, "Enabling Class W\n");
-			wm8903_write(codec, WM8903_CLASS_W_0, reg |
+			snd_soc_write(codec, WM8903_CLASS_W_0, reg |
 				     WM8903_CP_DYN_FREQ | WM8903_CP_DYN_V);
 		}
 
-		reg = wm8903_read(codec, WM8903_VMID_CONTROL_0);
+		reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);
 		reg &= ~(WM8903_VMID_RES_MASK);
 		reg |= WM8903_VMID_RES_250K;
-		wm8903_write(codec, WM8903_VMID_CONTROL_0, reg);
+		snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);
 		break;
 
 	case SND_SOC_BIAS_OFF:
 		wm8903_run_sequence(codec, 32);
-		reg = wm8903_read(codec, WM8903_CLOCK_RATES_2);
+		reg = snd_soc_read(codec, WM8903_CLOCK_RATES_2);
 		reg &= ~WM8903_CLK_SYS_ENA;
-		wm8903_write(codec, WM8903_CLOCK_RATES_2, reg);
+		snd_soc_write(codec, WM8903_CLOCK_RATES_2, reg);
 		break;
 	}
 
@@ -1041,7 +1005,7 @@
 			      unsigned int fmt)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
-	u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1);
+	u16 aif1 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_1);
 
 	aif1 &= ~(WM8903_LRCLK_DIR | WM8903_BCLK_DIR | WM8903_AIF_FMT_MASK |
 		  WM8903_AIF_LRCLK_INV | WM8903_AIF_BCLK_INV);
@@ -1119,7 +1083,7 @@
 		return -EINVAL;
 	}
 
-	wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
+	snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
 
 	return 0;
 }
@@ -1129,14 +1093,14 @@
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 reg;
 
-	reg = wm8903_read(codec, WM8903_DAC_DIGITAL_1);
+	reg = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
 
 	if (mute)
 		reg |= WM8903_DAC_MUTE;
 	else
 		reg &= ~WM8903_DAC_MUTE;
 
-	wm8903_write(codec, WM8903_DAC_DIGITAL_1, reg);
+	snd_soc_write(codec, WM8903_DAC_DIGITAL_1, reg);
 
 	return 0;
 }
@@ -1215,22 +1179,18 @@
 	int div;
 } bclk_divs[] = {
 	{  10,  0 },
-	{  15,  1 },
 	{  20,  2 },
 	{  30,  3 },
 	{  40,  4 },
 	{  50,  5 },
-	{  55,  6 },
 	{  60,  7 },
 	{  80,  8 },
 	{ 100,  9 },
-	{ 110, 10 },
 	{ 120, 11 },
 	{ 160, 12 },
 	{ 200, 13 },
 	{ 220, 14 },
 	{ 240, 15 },
-	{ 250, 16 },
 	{ 300, 17 },
 	{ 320, 18 },
 	{ 440, 19 },
@@ -1277,14 +1237,8 @@
 	if (wm8903->master_substream) {
 		master_runtime = wm8903->master_substream->runtime;
 
-		dev_dbg(&i2c->dev, "Constraining to %d bits at %dHz\n",
-			master_runtime->sample_bits,
-			master_runtime->rate);
-
-		snd_pcm_hw_constraint_minmax(substream->runtime,
-					     SNDRV_PCM_HW_PARAM_RATE,
-					     master_runtime->rate,
-					     master_runtime->rate);
+		dev_dbg(&i2c->dev, "Constraining to %d bits\n",
+			master_runtime->sample_bits);
 
 		snd_pcm_hw_constraint_minmax(substream->runtime,
 					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
@@ -1336,17 +1290,24 @@
 	int cur_val;
 	int clk_sys;
 
-	u16 aif1 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_1);
-	u16 aif2 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_2);
-	u16 aif3 = wm8903_read(codec, WM8903_AUDIO_INTERFACE_3);
-	u16 clock0 = wm8903_read(codec, WM8903_CLOCK_RATES_0);
-	u16 clock1 = wm8903_read(codec, WM8903_CLOCK_RATES_1);
+	u16 aif1 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_1);
+	u16 aif2 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_2);
+	u16 aif3 = snd_soc_read(codec, WM8903_AUDIO_INTERFACE_3);
+	u16 clock0 = snd_soc_read(codec, WM8903_CLOCK_RATES_0);
+	u16 clock1 = snd_soc_read(codec, WM8903_CLOCK_RATES_1);
+	u16 dac_digital1 = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
 
 	if (substream == wm8903->slave_substream) {
 		dev_dbg(&i2c->dev, "Ignoring hw_params for slave substream\n");
 		return 0;
 	}
 
+	/* Enable sloping stopband filter for low sample rates */
+	if (fs <= 24000)
+		dac_digital1 |= WM8903_DAC_SB_FILT;
+	else
+		dac_digital1 &= ~WM8903_DAC_SB_FILT;
+
 	/* Configure sample rate logic for DSP - choose nearest rate */
 	dsp_config = 0;
 	best_val = abs(sample_rates[dsp_config].rate - fs);
@@ -1466,11 +1427,12 @@
 	aif2 |= bclk_divs[bclk_div].div;
 	aif3 |= bclk / fs;
 
-	wm8903_write(codec, WM8903_CLOCK_RATES_0, clock0);
-	wm8903_write(codec, WM8903_CLOCK_RATES_1, clock1);
-	wm8903_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
-	wm8903_write(codec, WM8903_AUDIO_INTERFACE_2, aif2);
-	wm8903_write(codec, WM8903_AUDIO_INTERFACE_3, aif3);
+	snd_soc_write(codec, WM8903_CLOCK_RATES_0, clock0);
+	snd_soc_write(codec, WM8903_CLOCK_RATES_1, clock1);
+	snd_soc_write(codec, WM8903_AUDIO_INTERFACE_1, aif1);
+	snd_soc_write(codec, WM8903_AUDIO_INTERFACE_2, aif2);
+	snd_soc_write(codec, WM8903_AUDIO_INTERFACE_3, aif3);
+	snd_soc_write(codec, WM8903_DAC_DIGITAL_1, dac_digital1);
 
 	return 0;
 }
@@ -1523,6 +1485,7 @@
 		 .formats = WM8903_FORMATS,
 	 },
 	.ops = &wm8903_dai_ops,
+	.symmetric_rates = 1,
 };
 EXPORT_SYMBOL_GPL(wm8903_dai);
 
@@ -1554,7 +1517,7 @@
 	if (tmp_cache) {
 		for (i = 2; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
 			if (tmp_cache[i] != reg_cache[i])
-				wm8903_write(codec, i, tmp_cache[i]);
+				snd_soc_write(codec, i, tmp_cache[i]);
 	} else {
 		dev_err(&i2c->dev, "Failed to allocate temporary cache\n");
 	}
@@ -1585,9 +1548,6 @@
 	codec->dev = &i2c->dev;
 	codec->name = "WM8903";
 	codec->owner = THIS_MODULE;
-	codec->read = wm8903_read;
-	codec->write = wm8903_write;
-	codec->hw_write = (hw_write_t)i2c_master_send;
 	codec->bias_level = SND_SOC_BIAS_OFF;
 	codec->set_bias_level = wm8903_set_bias_level;
 	codec->dai = &wm8903_dai;
@@ -1595,18 +1555,25 @@
 	codec->reg_cache_size = ARRAY_SIZE(wm8903->reg_cache);
 	codec->reg_cache = &wm8903->reg_cache[0];
 	codec->private_data = wm8903;
+	codec->volatile_register = wm8903_volatile_register;
 
 	i2c_set_clientdata(i2c, codec);
 	codec->control_data = i2c;
 
-	val = wm8903_hw_read(codec, WM8903_SW_RESET_AND_ID);
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	val = snd_soc_read(codec, WM8903_SW_RESET_AND_ID);
 	if (val != wm8903_reg_defaults[WM8903_SW_RESET_AND_ID]) {
 		dev_err(&i2c->dev,
 			"Device with ID register %x is not a WM8903\n", val);
 		return -ENODEV;
 	}
 
-	val = wm8903_read(codec, WM8903_REVISION_NUMBER);
+	val = snd_soc_read(codec, WM8903_REVISION_NUMBER);
 	dev_info(&i2c->dev, "WM8903 revision %d\n",
 		 val & WM8903_CHIP_REV_MASK);
 
@@ -1616,35 +1583,35 @@
 	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* Latch volume update bits */
-	val = wm8903_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT);
+	val = snd_soc_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT);
 	val |= WM8903_ADCVU;
-	wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val);
-	wm8903_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val);
+	snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val);
+	snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val);
 
-	val = wm8903_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT);
+	val = snd_soc_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT);
 	val |= WM8903_DACVU;
-	wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val);
-	wm8903_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val);
+	snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val);
+	snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val);
 
-	val = wm8903_read(codec, WM8903_ANALOGUE_OUT1_LEFT);
+	val = snd_soc_read(codec, WM8903_ANALOGUE_OUT1_LEFT);
 	val |= WM8903_HPOUTVU;
-	wm8903_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val);
-	wm8903_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val);
+	snd_soc_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val);
+	snd_soc_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val);
 
-	val = wm8903_read(codec, WM8903_ANALOGUE_OUT2_LEFT);
+	val = snd_soc_read(codec, WM8903_ANALOGUE_OUT2_LEFT);
 	val |= WM8903_LINEOUTVU;
-	wm8903_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val);
-	wm8903_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val);
+	snd_soc_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val);
+	snd_soc_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val);
 
-	val = wm8903_read(codec, WM8903_ANALOGUE_OUT3_LEFT);
+	val = snd_soc_read(codec, WM8903_ANALOGUE_OUT3_LEFT);
 	val |= WM8903_SPKVU;
-	wm8903_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val);
-	wm8903_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val);
+	snd_soc_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val);
+	snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val);
 
 	/* Enable DAC soft mute by default */
-	val = wm8903_read(codec, WM8903_DAC_DIGITAL_1);
+	val = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
 	val |= WM8903_DAC_MUTEMODE;
-	wm8903_write(codec, WM8903_DAC_DIGITAL_1, val);
+	snd_soc_write(codec, WM8903_DAC_DIGITAL_1, val);
 
 	wm8903_dai.dev = &i2c->dev;
 	wm8903_codec = codec;
@@ -1688,6 +1655,21 @@
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int wm8903_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8903_i2c_resume(struct i2c_client *client)
+{
+	return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8903_i2c_suspend NULL
+#define wm8903_i2c_resume NULL
+#endif
+
 /* i2c codec control layer */
 static const struct i2c_device_id wm8903_i2c_id[] = {
        { "wm8903", 0 },
@@ -1702,6 +1684,8 @@
 	},
 	.probe    = wm8903_i2c_probe,
 	.remove   = __devexit_p(wm8903_i2c_remove),
+	.suspend  = wm8903_i2c_suspend,
+	.resume   = wm8903_i2c_resume,
 	.id_table = wm8903_i2c_id,
 };
 
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
new file mode 100644
index 0000000..da97aae
--- /dev/null
+++ b/sound/soc/codecs/wm8940.c
@@ -0,0 +1,933 @@
+/*
+ * wm8940.c  --  WM8940 ALSA Soc Audio driver
+ *
+ * Author: Jonathan Cameron <jic23@cam.ac.uk>
+ *
+ * Based on wm8510.c
+ *    Copyright  2006 Wolfson Microelectronics PLC.
+ *    Author:  Liam Girdwood <lrg@slimlogic.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Not currently handled:
+ * Notch filter control
+ * AUXMode (inverting vs mixer)
+ * No means to obtain current gain if alc enabled.
+ * No use made of gpio
+ * Fast VMID discharge for power down
+ * Soft Start
+ * DLR and ALR Swaps not enabled
+ * Digital Sidetone not supported
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8940.h"
+
+struct wm8940_priv {
+	unsigned int sysclk;
+	u16 reg_cache[WM8940_CACHEREGNUM];
+	struct snd_soc_codec codec;
+};
+
+static u16 wm8940_reg_defaults[] = {
+	0x8940, /* Soft Reset */
+	0x0000, /* Power 1 */
+	0x0000, /* Power 2 */
+	0x0000, /* Power 3 */
+	0x0010, /* Interface Control */
+	0x0000, /* Companding Control */
+	0x0140, /* Clock Control */
+	0x0000, /* Additional Controls */
+	0x0000, /* GPIO Control */
+	0x0002, /* Auto Increment Control */
+	0x0000, /* DAC Control */
+	0x00FF, /* DAC Volume */
+	0,
+	0,
+	0x0100, /* ADC Control */
+	0x00FF, /* ADC Volume */
+	0x0000, /* Notch Filter 1 Control 1 */
+	0x0000, /* Notch Filter 1 Control 2 */
+	0x0000, /* Notch Filter 2 Control 1 */
+	0x0000, /* Notch Filter 2 Control 2 */
+	0x0000, /* Notch Filter 3 Control 1 */
+	0x0000, /* Notch Filter 3 Control 2 */
+	0x0000, /* Notch Filter 4 Control 1 */
+	0x0000, /* Notch Filter 4 Control 2 */
+	0x0032, /* DAC Limit Control 1 */
+	0x0000, /* DAC Limit Control 2 */
+	0,
+	0,
+	0,
+	0,
+	0,
+	0,
+	0x0038, /* ALC Control 1 */
+	0x000B, /* ALC Control 2 */
+	0x0032, /* ALC Control 3 */
+	0x0000, /* Noise Gate */
+	0x0041, /* PLLN */
+	0x000C, /* PLLK1 */
+	0x0093, /* PLLK2 */
+	0x00E9, /* PLLK3 */
+	0,
+	0,
+	0x0030, /* ALC Control 4 */
+	0,
+	0x0002, /* Input Control */
+	0x0050, /* PGA Gain */
+	0,
+	0x0002, /* ADC Boost Control */
+	0,
+	0x0002, /* Output Control */
+	0x0000, /* Speaker Mixer Control */
+	0,
+	0,
+	0,
+	0x0079, /* Speaker Volume */
+	0,
+	0x0000, /* Mono Mixer Control */
+};
+
+static const char *wm8940_companding[] = { "Off", "NC", "u-law", "A-law" };
+static const struct soc_enum wm8940_adc_companding_enum
+= SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 1, 4, wm8940_companding);
+static const struct soc_enum wm8940_dac_companding_enum
+= SOC_ENUM_SINGLE(WM8940_COMPANDINGCTL, 3, 4, wm8940_companding);
+
+static const char *wm8940_alc_mode_text[] = {"ALC", "Limiter"};
+static const struct soc_enum wm8940_alc_mode_enum
+= SOC_ENUM_SINGLE(WM8940_ALC3, 8, 2, wm8940_alc_mode_text);
+
+static const char *wm8940_mic_bias_level_text[] = {"0.9", "0.65"};
+static const struct soc_enum wm8940_mic_bias_level_enum
+= SOC_ENUM_SINGLE(WM8940_INPUTCTL, 8, 2, wm8940_mic_bias_level_text);
+
+static const char *wm8940_filter_mode_text[] = {"Audio", "Application"};
+static const struct soc_enum wm8940_filter_mode_enum
+= SOC_ENUM_SINGLE(WM8940_ADC, 7, 2, wm8940_filter_mode_text);
+
+static DECLARE_TLV_DB_SCALE(wm8940_spk_vol_tlv, -5700, 100, 1);
+static DECLARE_TLV_DB_SCALE(wm8940_att_tlv, -1000, 1000, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_pga_vol_tlv, -1200, 75, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_alc_min_tlv, -1200, 600, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_alc_max_tlv, 675, 600, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_alc_tar_tlv, -2250, 50, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_lim_boost_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_lim_thresh_tlv, -600, 100, 0);
+static DECLARE_TLV_DB_SCALE(wm8940_adc_tlv, -12750, 50, 1);
+static DECLARE_TLV_DB_SCALE(wm8940_capture_boost_vol_tlv, 0, 2000, 0);
+
+static const struct snd_kcontrol_new wm8940_snd_controls[] = {
+	SOC_SINGLE("Digital Loopback Switch", WM8940_COMPANDINGCTL,
+		   6, 1, 0),
+	SOC_ENUM("DAC Companding", wm8940_dac_companding_enum),
+	SOC_ENUM("ADC Companding", wm8940_adc_companding_enum),
+
+	SOC_ENUM("ALC Mode", wm8940_alc_mode_enum),
+	SOC_SINGLE("ALC Switch", WM8940_ALC1, 8, 1, 0),
+	SOC_SINGLE_TLV("ALC Capture Max Gain", WM8940_ALC1,
+		       3, 7, 1, wm8940_alc_max_tlv),
+	SOC_SINGLE_TLV("ALC Capture Min Gain", WM8940_ALC1,
+		       0, 7, 0, wm8940_alc_min_tlv),
+	SOC_SINGLE_TLV("ALC Capture Target", WM8940_ALC2,
+		       0, 14, 0, wm8940_alc_tar_tlv),
+	SOC_SINGLE("ALC Capture Hold", WM8940_ALC2, 4, 10, 0),
+	SOC_SINGLE("ALC Capture Decay", WM8940_ALC3, 4, 10, 0),
+	SOC_SINGLE("ALC Capture Attach", WM8940_ALC3, 0, 10, 0),
+	SOC_SINGLE("ALC ZC Switch", WM8940_ALC4, 1, 1, 0),
+	SOC_SINGLE("ALC Capture Noise Gate Switch", WM8940_NOISEGATE,
+		   3, 1, 0),
+	SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8940_NOISEGATE,
+		   0, 7, 0),
+
+	SOC_SINGLE("DAC Playback Limiter Switch", WM8940_DACLIM1, 8, 1, 0),
+	SOC_SINGLE("DAC Playback Limiter Attack", WM8940_DACLIM1, 0, 9, 0),
+	SOC_SINGLE("DAC Playback Limiter Decay", WM8940_DACLIM1, 4, 11, 0),
+	SOC_SINGLE_TLV("DAC Playback Limiter Threshold", WM8940_DACLIM2,
+		       4, 9, 1, wm8940_lim_thresh_tlv),
+	SOC_SINGLE_TLV("DAC Playback Limiter Boost", WM8940_DACLIM2,
+		       0, 12, 0, wm8940_lim_boost_tlv),
+
+	SOC_SINGLE("Capture PGA ZC Switch", WM8940_PGAGAIN, 7, 1, 0),
+	SOC_SINGLE_TLV("Capture PGA Volume", WM8940_PGAGAIN,
+		       0, 63, 0, wm8940_pga_vol_tlv),
+	SOC_SINGLE_TLV("Digital Playback Volume", WM8940_DACVOL,
+		       0, 255, 0, wm8940_adc_tlv),
+	SOC_SINGLE_TLV("Digital Capture Volume", WM8940_ADCVOL,
+		       0, 255, 0, wm8940_adc_tlv),
+	SOC_ENUM("Mic Bias Level", wm8940_mic_bias_level_enum),
+	SOC_SINGLE_TLV("Capture Boost Volue", WM8940_ADCBOOST,
+		       8, 1, 0, wm8940_capture_boost_vol_tlv),
+	SOC_SINGLE_TLV("Speaker Playback Volume", WM8940_SPKVOL,
+		       0, 63, 0, wm8940_spk_vol_tlv),
+	SOC_SINGLE("Speaker Playback Switch", WM8940_SPKVOL,  6, 1, 1),
+
+	SOC_SINGLE_TLV("Speaker Mixer Line Bypass Volume", WM8940_SPKVOL,
+		       8, 1, 1, wm8940_att_tlv),
+	SOC_SINGLE("Speaker Playback ZC Switch", WM8940_SPKVOL, 7, 1, 0),
+
+	SOC_SINGLE("Mono Out Switch", WM8940_MONOMIX, 6, 1, 1),
+	SOC_SINGLE_TLV("Mono Mixer Line Bypass Volume", WM8940_MONOMIX,
+		       7, 1, 1, wm8940_att_tlv),
+
+	SOC_SINGLE("High Pass Filter Switch", WM8940_ADC, 8, 1, 0),
+	SOC_ENUM("High Pass Filter Mode", wm8940_filter_mode_enum),
+	SOC_SINGLE("High Pass Filter Cut Off", WM8940_ADC, 4, 7, 0),
+	SOC_SINGLE("ADC Inversion Switch", WM8940_ADC, 0, 1, 0),
+	SOC_SINGLE("DAC Inversion Switch", WM8940_DAC, 0, 1, 0),
+	SOC_SINGLE("DAC Auto Mute Switch", WM8940_DAC, 2, 1, 0),
+	SOC_SINGLE("ZC Timeout Clock Switch", WM8940_ADDCNTRL, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8940_speaker_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_SPKMIX, 1, 1, 0),
+	SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_SPKMIX, 5, 1, 0),
+	SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_SPKMIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8940_mono_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Line Bypass Switch", WM8940_MONOMIX, 1, 1, 0),
+	SOC_DAPM_SINGLE("Aux Playback Switch", WM8940_MONOMIX, 2, 1, 0),
+	SOC_DAPM_SINGLE("PCM Playback Switch", WM8940_MONOMIX, 0, 1, 0),
+};
+
+static DECLARE_TLV_DB_SCALE(wm8940_boost_vol_tlv, -1500, 300, 1);
+static const struct snd_kcontrol_new wm8940_input_boost_controls[] = {
+	SOC_DAPM_SINGLE("Mic PGA Switch", WM8940_PGAGAIN, 6, 1, 1),
+	SOC_DAPM_SINGLE_TLV("Aux Volume", WM8940_ADCBOOST,
+			    0, 7, 0, wm8940_boost_vol_tlv),
+	SOC_DAPM_SINGLE_TLV("Mic Volume", WM8940_ADCBOOST,
+			    4, 7, 0, wm8940_boost_vol_tlv),
+};
+
+static const struct snd_kcontrol_new wm8940_micpga_controls[] = {
+	SOC_DAPM_SINGLE("AUX Switch", WM8940_INPUTCTL, 2, 1, 0),
+	SOC_DAPM_SINGLE("MICP Switch", WM8940_INPUTCTL, 0, 1, 0),
+	SOC_DAPM_SINGLE("MICN Switch", WM8940_INPUTCTL, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8940_dapm_widgets[] = {
+	SND_SOC_DAPM_MIXER("Speaker Mixer", WM8940_POWER3, 2, 0,
+			   &wm8940_speaker_mixer_controls[0],
+			   ARRAY_SIZE(wm8940_speaker_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Mono Mixer", WM8940_POWER3, 3, 0,
+			   &wm8940_mono_mixer_controls[0],
+			   ARRAY_SIZE(wm8940_mono_mixer_controls)),
+	SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8940_POWER3, 0, 0),
+
+	SND_SOC_DAPM_PGA("SpkN Out", WM8940_POWER3, 5, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("SpkP Out", WM8940_POWER3, 6, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Mono Out", WM8940_POWER3, 7, 0, NULL, 0),
+	SND_SOC_DAPM_OUTPUT("MONOOUT"),
+	SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+	SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+
+	SND_SOC_DAPM_PGA("Aux Input", WM8940_POWER1, 6, 0, NULL, 0),
+	SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8940_POWER2, 0, 0),
+	SND_SOC_DAPM_MIXER("Mic PGA", WM8940_POWER2, 2, 0,
+			   &wm8940_micpga_controls[0],
+			   ARRAY_SIZE(wm8940_micpga_controls)),
+	SND_SOC_DAPM_MIXER("Boost Mixer", WM8940_POWER2, 4, 0,
+			   &wm8940_input_boost_controls[0],
+			   ARRAY_SIZE(wm8940_input_boost_controls)),
+	SND_SOC_DAPM_MICBIAS("Mic Bias", WM8940_POWER1, 4, 0),
+
+	SND_SOC_DAPM_INPUT("MICN"),
+	SND_SOC_DAPM_INPUT("MICP"),
+	SND_SOC_DAPM_INPUT("AUX"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Mono output mixer */
+	{"Mono Mixer", "PCM Playback Switch", "DAC"},
+	{"Mono Mixer", "Aux Playback Switch", "Aux Input"},
+	{"Mono Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+	/* Speaker output mixer */
+	{"Speaker Mixer", "PCM Playback Switch", "DAC"},
+	{"Speaker Mixer", "Aux Playback Switch", "Aux Input"},
+	{"Speaker Mixer", "Line Bypass Switch", "Boost Mixer"},
+
+	/* Outputs */
+	{"Mono Out", NULL, "Mono Mixer"},
+	{"MONOOUT", NULL, "Mono Out"},
+	{"SpkN Out", NULL, "Speaker Mixer"},
+	{"SpkP Out", NULL, "Speaker Mixer"},
+	{"SPKOUTN", NULL, "SpkN Out"},
+	{"SPKOUTP", NULL, "SpkP Out"},
+
+	/*  Microphone PGA */
+	{"Mic PGA", "MICN Switch", "MICN"},
+	{"Mic PGA", "MICP Switch", "MICP"},
+	{"Mic PGA", "AUX Switch", "AUX"},
+
+	/* Boost Mixer */
+	{"Boost Mixer", "Mic PGA Switch", "Mic PGA"},
+	{"Boost Mixer", "Mic Volume",  "MICP"},
+	{"Boost Mixer", "Aux Volume", "Aux Input"},
+
+	{"ADC", NULL, "Boost Mixer"},
+};
+
+static int wm8940_add_widgets(struct snd_soc_codec *codec)
+{
+	int ret;
+
+	ret = snd_soc_dapm_new_controls(codec, wm8940_dapm_widgets,
+					ARRAY_SIZE(wm8940_dapm_widgets));
+	if (ret)
+		goto error_ret;
+	ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+	if (ret)
+		goto error_ret;
+	ret = snd_soc_dapm_new_widgets(codec);
+
+error_ret:
+	return ret;
+}
+
+#define wm8940_reset(c) snd_soc_write(c, WM8940_SOFTRESET, 0);
+
+static int wm8940_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			      unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFE67;
+	u16 clk = snd_soc_read(codec, WM8940_CLOCK) & 0x1fe;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		clk |= 1;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_soc_write(codec, WM8940_CLOCK, clk);
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= (2 << 3);
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= (1 << 3);
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= (3 << 3);
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		iface |= (3 << 3) | (1 << 7);
+		break;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= (1 << 7);
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= (1 << 8);
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= (1 << 8) | (1 << 7);
+		break;
+	}
+
+	snd_soc_write(codec, WM8940_IFACE, iface);
+
+	return 0;
+}
+
+static int wm8940_i2s_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	u16 iface = snd_soc_read(codec, WM8940_IFACE) & 0xFD9F;
+	u16 addcntrl = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFF1;
+	u16 companding =  snd_soc_read(codec,
+						WM8940_COMPANDINGCTL) & 0xFFDF;
+	int ret;
+
+	/* LoutR control */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE
+	    && params_channels(params) == 2)
+		iface |= (1 << 9);
+
+	switch (params_rate(params)) {
+	case SNDRV_PCM_RATE_8000:
+		addcntrl |= (0x5 << 1);
+		break;
+	case SNDRV_PCM_RATE_11025:
+		addcntrl |= (0x4 << 1);
+		break;
+	case SNDRV_PCM_RATE_16000:
+		addcntrl |= (0x3 << 1);
+		break;
+	case SNDRV_PCM_RATE_22050:
+		addcntrl |= (0x2 << 1);
+		break;
+	case SNDRV_PCM_RATE_32000:
+		addcntrl |= (0x1 << 1);
+		break;
+	case SNDRV_PCM_RATE_44100:
+	case SNDRV_PCM_RATE_48000:
+		break;
+	}
+	ret = snd_soc_write(codec, WM8940_ADDCNTRL, addcntrl);
+	if (ret)
+		goto error_ret;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		companding = companding | (1 << 5);
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= (1 << 5);
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= (2 << 5);
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface |= (3 << 5);
+		break;
+	}
+	ret = snd_soc_write(codec, WM8940_COMPANDINGCTL, companding);
+	if (ret)
+		goto error_ret;
+	ret = snd_soc_write(codec, WM8940_IFACE, iface);
+
+error_ret:
+	return ret;
+}
+
+static int wm8940_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = snd_soc_read(codec, WM8940_DAC) & 0xffbf;
+
+	if (mute)
+		mute_reg |= 0x40;
+
+	return snd_soc_write(codec, WM8940_DAC, mute_reg);
+}
+
+static int wm8940_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	u16 val;
+	u16 pwr_reg = snd_soc_read(codec, WM8940_POWER1) & 0x1F0;
+	int ret = 0;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		/* ensure bufioen and biasen */
+		pwr_reg |= (1 << 2) | (1 << 3);
+		/* Enable thermal shutdown */
+		val = snd_soc_read(codec, WM8940_OUTPUTCTL);
+		ret = snd_soc_write(codec, WM8940_OUTPUTCTL, val | 0x2);
+		if (ret)
+			break;
+		/* set vmid to 75k */
+		ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		/* ensure bufioen and biasen */
+		pwr_reg |= (1 << 2) | (1 << 3);
+		ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x1);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		/* ensure bufioen and biasen */
+		pwr_reg |= (1 << 2) | (1 << 3);
+		/* set vmid to 300k for standby */
+		ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg | 0x2);
+		break;
+	case SND_SOC_BIAS_OFF:
+		ret = snd_soc_write(codec, WM8940_POWER1, pwr_reg);
+		break;
+	}
+
+	return ret;
+}
+
+struct pll_ {
+	unsigned int pre_scale:2;
+	unsigned int n:4;
+	unsigned int k;
+};
+
+static struct pll_ pll_div;
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+static void pll_factors(unsigned int target, unsigned int source)
+{
+	unsigned long long Kpart;
+	unsigned int K, Ndiv, Nmod;
+	/* The left shift ist to avoid accuracy loss when right shifting */
+	Ndiv = target / source;
+
+	if (Ndiv > 12) {
+		source <<= 1;
+		/* Multiply by 2 */
+		pll_div.pre_scale = 0;
+		Ndiv = target / source;
+	} else if (Ndiv < 3) {
+		source >>= 2;
+		/* Divide by 4 */
+		pll_div.pre_scale = 3;
+		Ndiv = target / source;
+	} else if (Ndiv < 6) {
+		source >>= 1;
+		/* divide by 2 */
+		pll_div.pre_scale = 2;
+		Ndiv = target / source;
+	} else
+		pll_div.pre_scale = 1;
+
+	if ((Ndiv < 6) || (Ndiv > 12))
+		printk(KERN_WARNING
+			"WM8940 N value %d outwith recommended range!d\n",
+			Ndiv);
+
+	pll_div.n = Ndiv;
+	Nmod = target % source;
+	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, source);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	/* Check if we need to round */
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	K /= 10;
+
+	pll_div.k = K;
+}
+
+/* Untested at the moment */
+static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai,
+		int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+
+	/* Turn off PLL */
+	reg = snd_soc_read(codec, WM8940_POWER1);
+	snd_soc_write(codec, WM8940_POWER1, reg & 0x1df);
+
+	if (freq_in == 0 || freq_out == 0) {
+		/* Clock CODEC directly from MCLK */
+		reg = snd_soc_read(codec, WM8940_CLOCK);
+		snd_soc_write(codec, WM8940_CLOCK, reg & 0x0ff);
+		/* Pll power down */
+		snd_soc_write(codec, WM8940_PLLN, (1 << 7));
+		return 0;
+	}
+
+	/* Pll is followed by a frequency divide by 4 */
+	pll_factors(freq_out*4, freq_in);
+	if (pll_div.k)
+		snd_soc_write(codec, WM8940_PLLN,
+			     (pll_div.pre_scale << 4) | pll_div.n | (1 << 6));
+	else /* No factional component */
+		snd_soc_write(codec, WM8940_PLLN,
+			     (pll_div.pre_scale << 4) | pll_div.n);
+	snd_soc_write(codec, WM8940_PLLK1, pll_div.k >> 18);
+	snd_soc_write(codec, WM8940_PLLK2, (pll_div.k >> 9) & 0x1ff);
+	snd_soc_write(codec, WM8940_PLLK3, pll_div.k & 0x1ff);
+	/* Enable the PLL */
+	reg = snd_soc_read(codec, WM8940_POWER1);
+	snd_soc_write(codec, WM8940_POWER1, reg | 0x020);
+
+	/* Run CODEC from PLL instead of MCLK */
+	reg = snd_soc_read(codec, WM8940_CLOCK);
+	snd_soc_write(codec, WM8940_CLOCK, reg | 0x100);
+
+	return 0;
+}
+
+static int wm8940_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+				 int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8940_priv *wm8940 = codec->private_data;
+
+	switch (freq) {
+	case 11289600:
+	case 12000000:
+	case 12288000:
+	case 16934400:
+	case 18432000:
+		wm8940->sysclk = freq;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int wm8940_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+				 int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+	int ret = 0;
+
+	switch (div_id) {
+	case WM8940_BCLKDIV:
+		reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFFEF3;
+		ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 2));
+		break;
+	case WM8940_MCLKDIV:
+		reg = snd_soc_read(codec, WM8940_CLOCK) & 0xFF1F;
+		ret = snd_soc_write(codec, WM8940_CLOCK, reg | (div << 5));
+		break;
+	case WM8940_OPCLKDIV:
+		reg = snd_soc_read(codec, WM8940_ADDCNTRL) & 0xFFCF;
+		ret = snd_soc_write(codec, WM8940_ADDCNTRL, reg | (div << 4));
+		break;
+	}
+	return ret;
+}
+
+#define WM8940_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8940_FORMATS (SNDRV_PCM_FMTBIT_S8 |				\
+			SNDRV_PCM_FMTBIT_S16_LE |			\
+			SNDRV_PCM_FMTBIT_S20_3LE |			\
+			SNDRV_PCM_FMTBIT_S24_LE |			\
+			SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm8940_dai_ops = {
+	.hw_params = wm8940_i2s_hw_params,
+	.set_sysclk = wm8940_set_dai_sysclk,
+	.digital_mute = wm8940_mute,
+	.set_fmt = wm8940_set_dai_fmt,
+	.set_clkdiv = wm8940_set_dai_clkdiv,
+	.set_pll = wm8940_set_dai_pll,
+};
+
+struct snd_soc_dai wm8940_dai = {
+	.name = "WM8940",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8940_RATES,
+		.formats = WM8940_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8940_RATES,
+		.formats = WM8940_FORMATS,
+	},
+	.ops = &wm8940_dai_ops,
+	.symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8940_dai);
+
+static int wm8940_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	return wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF);
+}
+
+static int wm8940_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	int i;
+	int ret;
+	u8 data[3];
+	u16 *cache = codec->reg_cache;
+
+	/* Sync reg_cache with the hardware
+	 * Could use auto incremented writes to speed this up
+	 */
+	for (i = 0; i < ARRAY_SIZE(wm8940_reg_defaults); i++) {
+		data[0] = i;
+		data[1] = (cache[i] & 0xFF00) >> 8;
+		data[2] = cache[i] & 0x00FF;
+		ret = codec->hw_write(codec->control_data, data, 3);
+		if (ret < 0)
+			goto error_ret;
+		else if (ret != 3) {
+			ret = -EIO;
+			goto error_ret;
+		}
+	}
+	ret = wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	if (ret)
+		goto error_ret;
+	ret = wm8940_set_bias_level(codec, codec->suspend_bias_level);
+
+error_ret:
+	return ret;
+}
+
+static struct snd_soc_codec *wm8940_codec;
+
+static int wm8940_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+
+	int ret = 0;
+
+	if (wm8940_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8940_codec;
+	codec = wm8940_codec;
+
+	mutex_init(&codec->mutex);
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	ret = snd_soc_add_controls(codec, wm8940_snd_controls,
+			     ARRAY_SIZE(wm8940_snd_controls));
+	if (ret)
+		goto error_free_pcms;
+	ret = wm8940_add_widgets(codec);
+	if (ret)
+		goto error_free_pcms;
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
+		goto error_free_pcms;
+	}
+
+	return ret;
+
+error_free_pcms:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+static int wm8940_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8940 = {
+	.probe = wm8940_probe,
+	.remove = wm8940_remove,
+	.suspend = wm8940_suspend,
+	.resume = wm8940_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8940);
+
+static int wm8940_register(struct wm8940_priv *wm8940,
+			   enum snd_soc_control_type control)
+{
+	struct wm8940_setup_data *pdata = wm8940->codec.dev->platform_data;
+	struct snd_soc_codec *codec = &wm8940->codec;
+	int ret;
+	u16 reg;
+	if (wm8940_codec) {
+		dev_err(codec->dev, "Another WM8940 is registered\n");
+		return -EINVAL;
+	}
+
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8940;
+	codec->name = "WM8940";
+	codec->owner = THIS_MODULE;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8940_set_bias_level;
+	codec->dai = &wm8940_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = ARRAY_SIZE(wm8940_reg_defaults);
+	codec->reg_cache = &wm8940->reg_cache;
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, control);
+	if (ret == 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	memcpy(codec->reg_cache, wm8940_reg_defaults,
+	       sizeof(wm8940_reg_defaults));
+
+	ret = wm8940_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		return ret;
+	}
+
+	wm8940_dai.dev = codec->dev;
+
+	wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	ret = snd_soc_write(codec, WM8940_POWER1, 0x180);
+	if (ret < 0)
+		return ret;
+
+	if (!pdata)
+		dev_warn(codec->dev, "No platform data supplied\n");
+	else {
+		reg = snd_soc_read(codec, WM8940_OUTPUTCTL);
+		ret = snd_soc_write(codec, WM8940_OUTPUTCTL, reg | pdata->vroi);
+		if (ret < 0)
+			return ret;
+	}
+
+
+	wm8940_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_register_dai(&wm8940_dai);
+	if (ret) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void wm8940_unregister(struct wm8940_priv *wm8940)
+{
+	wm8940_set_bias_level(&wm8940->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dai(&wm8940_dai);
+	snd_soc_unregister_codec(&wm8940->codec);
+	kfree(wm8940);
+	wm8940_codec = NULL;
+}
+
+static int wm8940_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct wm8940_priv *wm8940;
+	struct snd_soc_codec *codec;
+
+	wm8940 = kzalloc(sizeof *wm8940, GFP_KERNEL);
+	if (wm8940 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8940->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+	i2c_set_clientdata(i2c, wm8940);
+	codec->control_data = i2c;
+	codec->dev = &i2c->dev;
+
+	return wm8940_register(wm8940, SND_SOC_I2C);
+}
+
+static int __devexit wm8940_i2c_remove(struct i2c_client *client)
+{
+	struct wm8940_priv *wm8940 = i2c_get_clientdata(client);
+
+	wm8940_unregister(wm8940);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8940_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8940_i2c_resume(struct i2c_client *client)
+{
+	return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8940_i2c_suspend NULL
+#define wm8940_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm8940_i2c_id[] = {
+	{ "wm8940", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
+
+static struct i2c_driver wm8940_i2c_driver = {
+	.driver = {
+		.name = "WM8940 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.probe = wm8940_i2c_probe,
+	.remove = __devexit_p(wm8940_i2c_remove),
+	.suspend = wm8940_i2c_suspend,
+	.resume = wm8940_i2c_resume,
+	.id_table = wm8940_i2c_id,
+};
+
+static int __init wm8940_modinit(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&wm8940_i2c_driver);
+	if (ret)
+		printk(KERN_ERR "Failed to register WM8940 I2C driver: %d\n",
+		       ret);
+	return ret;
+}
+module_init(wm8940_modinit);
+
+static void __exit wm8940_exit(void)
+{
+	i2c_del_driver(&wm8940_i2c_driver);
+}
+module_exit(wm8940_exit);
+
+MODULE_DESCRIPTION("ASoC WM8940 driver");
+MODULE_AUTHOR("Jonathan Cameron");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8940.h b/sound/soc/codecs/wm8940.h
new file mode 100644
index 0000000..8410eed
--- /dev/null
+++ b/sound/soc/codecs/wm8940.h
@@ -0,0 +1,104 @@
+/*
+ * wm8940.h -- WM8940 Soc Audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8940_H
+#define _WM8940_H
+
+struct wm8940_setup_data {
+	/* Vref to analogue output resistance */
+#define WM8940_VROI_1K 0
+#define WM8940_VROI_30K 1
+	unsigned int vroi:1;
+};
+extern struct snd_soc_dai wm8940_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8940;
+
+/* WM8940 register space */
+#define WM8940_SOFTRESET	0x00
+#define WM8940_POWER1		0x01
+#define WM8940_POWER2		0x02
+#define WM8940_POWER3		0x03
+#define WM8940_IFACE		0x04
+#define WM8940_COMPANDINGCTL	0x05
+#define WM8940_CLOCK		0x06
+#define WM8940_ADDCNTRL		0x07
+#define WM8940_GPIO		0x08
+#define WM8940_CTLINT		0x09
+#define WM8940_DAC		0x0A
+#define WM8940_DACVOL		0x0B
+
+#define WM8940_ADC		0x0E
+#define WM8940_ADCVOL		0x0F
+#define WM8940_NOTCH1		0x10
+#define WM8940_NOTCH2		0x11
+#define WM8940_NOTCH3		0x12
+#define WM8940_NOTCH4		0x13
+#define WM8940_NOTCH5		0x14
+#define WM8940_NOTCH6		0x15
+#define WM8940_NOTCH7		0x16
+#define WM8940_NOTCH8		0x17
+#define WM8940_DACLIM1		0x18
+#define WM8940_DACLIM2		0x19
+
+#define WM8940_ALC1		0x20
+#define WM8940_ALC2		0x21
+#define WM8940_ALC3		0x22
+#define WM8940_NOISEGATE	0x23
+#define WM8940_PLLN		0x24
+#define WM8940_PLLK1		0x25
+#define WM8940_PLLK2		0x26
+#define WM8940_PLLK3		0x27
+
+#define WM8940_ALC4		0x2A
+
+#define WM8940_INPUTCTL		0x2C
+#define WM8940_PGAGAIN		0x2D
+
+#define WM8940_ADCBOOST		0x2F
+
+#define WM8940_OUTPUTCTL	0x31
+#define WM8940_SPKMIX		0x32
+
+#define WM8940_SPKVOL		0x36
+
+#define WM8940_MONOMIX		0x38
+
+#define WM8940_CACHEREGNUM  0x57
+
+
+/* Clock divider Id's */
+#define WM8940_BCLKDIV 0
+#define WM8940_MCLKDIV 1
+#define WM8940_OPCLKDIV 2
+
+/* MCLK clock dividers */
+#define WM8940_MCLKDIV_1	0
+#define WM8940_MCLKDIV_1_5	1
+#define WM8940_MCLKDIV_2	2
+#define WM8940_MCLKDIV_3	3
+#define WM8940_MCLKDIV_4	4
+#define WM8940_MCLKDIV_6	5
+#define WM8940_MCLKDIV_8	6
+#define WM8940_MCLKDIV_12	7
+
+/* BCLK clock dividers */
+#define WM8940_BCLKDIV_1 0
+#define WM8940_BCLKDIV_2 1
+#define WM8940_BCLKDIV_4 2
+#define WM8940_BCLKDIV_8 3
+#define WM8940_BCLKDIV_16 4
+#define WM8940_BCLKDIV_32 5
+
+/* PLL Out Dividers */
+#define WM8940_OPCLKDIV_1 0
+#define WM8940_OPCLKDIV_2 1
+#define WM8940_OPCLKDIV_3 2
+#define WM8940_OPCLKDIV_4 3
+
+#endif /* _WM8940_H */
+
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
new file mode 100644
index 0000000..f59703b
--- /dev/null
+++ b/sound/soc/codecs/wm8960.c
@@ -0,0 +1,942 @@
+/*
+ * wm8960.c  --  WM8960 ALSA SoC Audio driver
+ *
+ * Author: Liam Girdwood
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8960.h"
+
+#define AUDIO_NAME "wm8960"
+
+struct snd_soc_codec_device soc_codec_dev_wm8960;
+
+/* R25 - Power 1 */
+#define WM8960_VREF      0x40
+
+/* R28 - Anti-pop 1 */
+#define WM8960_POBCTRL   0x80
+#define WM8960_BUFDCOPEN 0x10
+#define WM8960_BUFIOEN   0x08
+#define WM8960_SOFT_ST   0x04
+#define WM8960_HPSTBY    0x01
+
+/* R29 - Anti-pop 2 */
+#define WM8960_DISOP     0x40
+
+/*
+ * wm8960 register cache
+ * We can't read the WM8960 register space when we are
+ * using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8960_reg[WM8960_CACHEREGNUM] = {
+	0x0097, 0x0097, 0x0000, 0x0000,
+	0x0000, 0x0008, 0x0000, 0x000a,
+	0x01c0, 0x0000, 0x00ff, 0x00ff,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x007b, 0x0100, 0x0032,
+	0x0000, 0x00c3, 0x00c3, 0x01c0,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0100, 0x0100, 0x0050, 0x0050,
+	0x0050, 0x0050, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0040, 0x0000,
+	0x0000, 0x0050, 0x0050, 0x0000,
+	0x0002, 0x0037, 0x004d, 0x0080,
+	0x0008, 0x0031, 0x0026, 0x00e9,
+};
+
+struct wm8960_priv {
+	u16 reg_cache[WM8960_CACHEREGNUM];
+	struct snd_soc_codec codec;
+};
+
+#define wm8960_reset(c)	snd_soc_write(c, WM8960_RESET, 0)
+
+/* enumerated controls */
+static const char *wm8960_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
+	"Right Inverted", "Stereo Inversion"};
+static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"};
+static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"};
+static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"};
+static const char *wm8960_alcmode[] = {"ALC", "Limiter"};
+
+static const struct soc_enum wm8960_enum[] = {
+	SOC_ENUM_SINGLE(WM8960_DACCTL1, 1, 4, wm8960_deemph),
+	SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity),
+	SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity),
+	SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff),
+	SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff),
+	SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc),
+	SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode),
+};
+
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9700, 50, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12700, 50, 1);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+
+static const struct snd_kcontrol_new wm8960_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL,
+		 0, 63, 0, adc_tlv),
+SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL,
+	6, 1, 0),
+SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL,
+	7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC,
+		 0, 255, 0, dac_tlv),
+
+SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1,
+		 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1,
+	7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2,
+		 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2,
+	7, 1, 0),
+SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0),
+SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0),
+
+SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0),
+SOC_ENUM("ADC Polarity", wm8960_enum[1]),
+SOC_ENUM("Playback De-emphasis", wm8960_enum[0]),
+SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0),
+
+SOC_ENUM("DAC Polarity", wm8960_enum[2]),
+
+SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[3]),
+SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[4]),
+SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0),
+SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0),
+
+SOC_ENUM("ALC Function", wm8960_enum[5]),
+SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0),
+SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1),
+SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0),
+SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0),
+SOC_ENUM("ALC Mode", wm8960_enum[6]),
+SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
+
+SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
+SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
+
+SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
+	0, 127, 0),
+
+SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume",
+	       WM8960_BYPASS1, 4, 7, 1, bypass_tlv),
+SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume",
+	       WM8960_LOUTMIX, 4, 7, 1, bypass_tlv),
+SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume",
+	       WM8960_BYPASS2, 4, 7, 1, bypass_tlv),
+SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume",
+	       WM8960_ROUTMIX, 4, 7, 1, bypass_tlv),
+};
+
+static const struct snd_kcontrol_new wm8960_lin_boost[] = {
+SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
+SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_lin[] = {
+SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_rin_boost[] = {
+SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0),
+SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_rin[] = {
+SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_loutput_mixer[] = {
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0),
+SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0),
+SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_routput_mixer[] = {
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0),
+SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0),
+SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8960_mono_out[] = {
+SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("LINPUT1"),
+SND_SOC_DAPM_INPUT("RINPUT1"),
+SND_SOC_DAPM_INPUT("LINPUT2"),
+SND_SOC_DAPM_INPUT("RINPUT2"),
+SND_SOC_DAPM_INPUT("LINPUT3"),
+SND_SOC_DAPM_INPUT("RINPUT3"),
+
+SND_SOC_DAPM_MICBIAS("MICB", WM8960_POWER1, 1, 0),
+
+SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,
+		   wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),
+SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0,
+		   wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)),
+
+SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0,
+		   wm8960_lin, ARRAY_SIZE(wm8960_lin)),
+SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0,
+		   wm8960_rin, ARRAY_SIZE(wm8960_rin)),
+
+SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER2, 3, 0),
+SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER2, 2, 0),
+
+SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0),
+SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0,
+	&wm8960_loutput_mixer[0],
+	ARRAY_SIZE(wm8960_loutput_mixer)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0,
+	&wm8960_routput_mixer[0],
+	ARRAY_SIZE(wm8960_routput_mixer)),
+
+SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0,
+	&wm8960_mono_out[0],
+	ARRAY_SIZE(wm8960_mono_out)),
+
+SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("SPK_LP"),
+SND_SOC_DAPM_OUTPUT("SPK_LN"),
+SND_SOC_DAPM_OUTPUT("HP_L"),
+SND_SOC_DAPM_OUTPUT("HP_R"),
+SND_SOC_DAPM_OUTPUT("SPK_RP"),
+SND_SOC_DAPM_OUTPUT("SPK_RN"),
+SND_SOC_DAPM_OUTPUT("OUT3"),
+};
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+	{ "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
+	{ "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
+	{ "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
+
+	{ "Left Input Mixer", "Boost Switch", "Left Boost Mixer", },
+	{ "Left Input Mixer", NULL, "LINPUT1", },  /* Really Boost Switch */
+	{ "Left Input Mixer", NULL, "LINPUT2" },
+	{ "Left Input Mixer", NULL, "LINPUT3" },
+
+	{ "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" },
+	{ "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" },
+	{ "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" },
+
+	{ "Right Input Mixer", "Boost Switch", "Right Boost Mixer", },
+	{ "Right Input Mixer", NULL, "RINPUT1", },  /* Really Boost Switch */
+	{ "Right Input Mixer", NULL, "RINPUT2" },
+	{ "Right Input Mixer", NULL, "LINPUT3" },
+
+	{ "Left ADC", NULL, "Left Input Mixer" },
+	{ "Right ADC", NULL, "Right Input Mixer" },
+
+	{ "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" },
+	{ "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer"} ,
+	{ "Left Output Mixer", "PCM Playback Switch", "Left DAC" },
+
+	{ "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" },
+	{ "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" } ,
+	{ "Right Output Mixer", "PCM Playback Switch", "Right DAC" },
+
+	{ "Mono Output Mixer", "Left Switch", "Left Output Mixer" },
+	{ "Mono Output Mixer", "Right Switch", "Right Output Mixer" },
+
+	{ "LOUT1 PGA", NULL, "Left Output Mixer" },
+	{ "ROUT1 PGA", NULL, "Right Output Mixer" },
+
+	{ "HP_L", NULL, "LOUT1 PGA" },
+	{ "HP_R", NULL, "ROUT1 PGA" },
+
+	{ "Left Speaker PGA", NULL, "Left Output Mixer" },
+	{ "Right Speaker PGA", NULL, "Right Output Mixer" },
+
+	{ "Left Speaker Output", NULL, "Left Speaker PGA" },
+	{ "Right Speaker Output", NULL, "Right Speaker PGA" },
+
+	{ "SPK_LN", NULL, "Left Speaker Output" },
+	{ "SPK_LP", NULL, "Left Speaker Output" },
+	{ "SPK_RN", NULL, "Right Speaker Output" },
+	{ "SPK_RP", NULL, "Right Speaker Output" },
+
+	{ "OUT3", NULL, "Mono Output Mixer", }
+};
+
+static int wm8960_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8960_dapm_widgets,
+				  ARRAY_SIZE(wm8960_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = 0;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		iface |= 0x0040;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= 0x0002;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x0001;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x0003;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		iface |= 0x0013;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= 0x0090;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= 0x0080;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x0010;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* set iface */
+	snd_soc_write(codec, WM8960_IFACE1, iface);
+	return 0;
+}
+
+static int wm8960_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3;
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= 0x0004;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= 0x0008;
+		break;
+	}
+
+	/* set iface */
+	snd_soc_write(codec, WM8960_IFACE1, iface);
+	return 0;
+}
+
+static int wm8960_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = snd_soc_read(codec, WM8960_DACCTL1) & 0xfff7;
+
+	if (mute)
+		snd_soc_write(codec, WM8960_DACCTL1, mute_reg | 0x8);
+	else
+		snd_soc_write(codec, WM8960_DACCTL1, mute_reg);
+	return 0;
+}
+
+static int wm8960_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	struct wm8960_data *pdata = codec->dev->platform_data;
+	u16 reg;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		/* Set VMID to 2x50k */
+		reg = snd_soc_read(codec, WM8960_POWER1);
+		reg &= ~0x180;
+		reg |= 0x80;
+		snd_soc_write(codec, WM8960_POWER1, reg);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* Enable anti-pop features */
+			snd_soc_write(codec, WM8960_APOP1,
+				     WM8960_POBCTRL | WM8960_SOFT_ST |
+				     WM8960_BUFDCOPEN | WM8960_BUFIOEN);
+
+			/* Discharge HP output */
+			reg = WM8960_DISOP;
+			if (pdata)
+				reg |= pdata->dres << 4;
+			snd_soc_write(codec, WM8960_APOP2, reg);
+
+			msleep(400);
+
+			snd_soc_write(codec, WM8960_APOP2, 0);
+
+			/* Enable & ramp VMID at 2x50k */
+			reg = snd_soc_read(codec, WM8960_POWER1);
+			reg |= 0x80;
+			snd_soc_write(codec, WM8960_POWER1, reg);
+			msleep(100);
+
+			/* Enable VREF */
+			snd_soc_write(codec, WM8960_POWER1, reg | WM8960_VREF);
+
+			/* Disable anti-pop features */
+			snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN);
+		}
+
+		/* Set VMID to 2x250k */
+		reg = snd_soc_read(codec, WM8960_POWER1);
+		reg &= ~0x180;
+		reg |= 0x100;
+		snd_soc_write(codec, WM8960_POWER1, reg);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		/* Enable anti-pop features */
+		snd_soc_write(codec, WM8960_APOP1,
+			     WM8960_POBCTRL | WM8960_SOFT_ST |
+			     WM8960_BUFDCOPEN | WM8960_BUFIOEN);
+
+		/* Disable VMID and VREF, let them discharge */
+		snd_soc_write(codec, WM8960_POWER1, 0);
+		msleep(600);
+
+		snd_soc_write(codec, WM8960_APOP1, 0);
+		break;
+	}
+
+	codec->bias_level = level;
+
+	return 0;
+}
+
+/* PLL divisors */
+struct _pll_div {
+	u32 pre_div:1;
+	u32 n:4;
+	u32 k:24;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static int pll_factors(unsigned int source, unsigned int target,
+		       struct _pll_div *pll_div)
+{
+	unsigned long long Kpart;
+	unsigned int K, Ndiv, Nmod;
+
+	pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target);
+
+	/* Scale up target to PLL operating frequency */
+	target *= 4;
+
+	Ndiv = target / source;
+	if (Ndiv < 6) {
+		source >>= 1;
+		pll_div->pre_div = 1;
+		Ndiv = target / source;
+	} else
+		pll_div->pre_div = 0;
+
+	if ((Ndiv < 6) || (Ndiv > 12)) {
+		pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv);
+		return -EINVAL;
+	}
+
+	pll_div->n = Ndiv;
+	Nmod = target % source;
+	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, source);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	/* Check if we need to round */
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	K /= 10;
+
+	pll_div->k = K;
+
+	pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n",
+		 pll_div->n, pll_div->k, pll_div->pre_div);
+
+	return 0;
+}
+
+static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
+		int pll_id, unsigned int freq_in, unsigned int freq_out)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+	static struct _pll_div pll_div;
+	int ret;
+
+	if (freq_in && freq_out) {
+		ret = pll_factors(freq_in, freq_out, &pll_div);
+		if (ret != 0)
+			return ret;
+	}
+
+	/* Disable the PLL: even if we are changing the frequency the
+	 * PLL needs to be disabled while we do so. */
+	snd_soc_write(codec, WM8960_CLOCK1,
+		     snd_soc_read(codec, WM8960_CLOCK1) & ~1);
+	snd_soc_write(codec, WM8960_POWER2,
+		     snd_soc_read(codec, WM8960_POWER2) & ~1);
+
+	if (!freq_in || !freq_out)
+		return 0;
+
+	reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f;
+	reg |= pll_div.pre_div << 4;
+	reg |= pll_div.n;
+
+	if (pll_div.k) {
+		reg |= 0x20;
+
+		snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
+		snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
+		snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
+	}
+	snd_soc_write(codec, WM8960_PLL1, reg);
+
+	/* Turn it on */
+	snd_soc_write(codec, WM8960_POWER2,
+		     snd_soc_read(codec, WM8960_POWER2) | 1);
+	msleep(250);
+	snd_soc_write(codec, WM8960_CLOCK1,
+		     snd_soc_read(codec, WM8960_CLOCK1) | 1);
+
+	return 0;
+}
+
+static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+		int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+
+	switch (div_id) {
+	case WM8960_SYSCLKSEL:
+		reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1fe;
+		snd_soc_write(codec, WM8960_CLOCK1, reg | div);
+		break;
+	case WM8960_SYSCLKDIV:
+		reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9;
+		snd_soc_write(codec, WM8960_CLOCK1, reg | div);
+		break;
+	case WM8960_DACDIV:
+		reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7;
+		snd_soc_write(codec, WM8960_CLOCK1, reg | div);
+		break;
+	case WM8960_OPCLKDIV:
+		reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f;
+		snd_soc_write(codec, WM8960_PLL1, reg | div);
+		break;
+	case WM8960_DCLKDIV:
+		reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f;
+		snd_soc_write(codec, WM8960_CLOCK2, reg | div);
+		break;
+	case WM8960_TOCLKSEL:
+		reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd;
+		snd_soc_write(codec, WM8960_ADDCTL1, reg | div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#define WM8960_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8960_FORMATS \
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+	SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8960_dai_ops = {
+	.hw_params = wm8960_hw_params,
+	.digital_mute = wm8960_mute,
+	.set_fmt = wm8960_set_dai_fmt,
+	.set_clkdiv = wm8960_set_dai_clkdiv,
+	.set_pll = wm8960_set_dai_pll,
+};
+
+struct snd_soc_dai wm8960_dai = {
+	.name = "WM8960",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8960_RATES,
+		.formats = WM8960_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8960_RATES,
+		.formats = WM8960_FORMATS,},
+	.ops = &wm8960_dai_ops,
+	.symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8960_dai);
+
+static int wm8960_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm8960_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8960_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	int i;
+	u8 data[2];
+	u16 *cache = codec->reg_cache;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < ARRAY_SIZE(wm8960_reg); i++) {
+		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+
+	wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	wm8960_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+static struct snd_soc_codec *wm8960_codec;
+
+static int wm8960_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8960_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8960_codec;
+	codec = wm8960_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, wm8960_snd_controls,
+			     ARRAY_SIZE(wm8960_snd_controls));
+	wm8960_add_widgets(codec);
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+/* power down chip */
+static int wm8960_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8960 = {
+	.probe = 	wm8960_probe,
+	.remove = 	wm8960_remove,
+	.suspend = 	wm8960_suspend,
+	.resume =	wm8960_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8960);
+
+static int wm8960_register(struct wm8960_priv *wm8960,
+			   enum snd_soc_control_type control)
+{
+	struct wm8960_data *pdata = wm8960->codec.dev->platform_data;
+	struct snd_soc_codec *codec = &wm8960->codec;
+	int ret;
+	u16 reg;
+
+	if (wm8960_codec) {
+		dev_err(codec->dev, "Another WM8960 is registered\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (!pdata) {
+		dev_warn(codec->dev, "No platform data supplied\n");
+	} else {
+		if (pdata->dres > WM8960_DRES_MAX) {
+			dev_err(codec->dev, "Invalid DRES: %d\n", pdata->dres);
+			pdata->dres = 0;
+		}
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8960;
+	codec->name = "WM8960";
+	codec->owner = THIS_MODULE;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8960_set_bias_level;
+	codec->dai = &wm8960_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = WM8960_CACHEREGNUM;
+	codec->reg_cache = &wm8960->reg_cache;
+
+	memcpy(codec->reg_cache, wm8960_reg, sizeof(wm8960_reg));
+
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	ret = wm8960_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		goto err;
+	}
+
+	wm8960_dai.dev = codec->dev;
+
+	wm8960_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* Latch the update bits */
+	reg = snd_soc_read(codec, WM8960_LINVOL);
+	snd_soc_write(codec, WM8960_LINVOL, reg | 0x100);
+	reg = snd_soc_read(codec, WM8960_RINVOL);
+	snd_soc_write(codec, WM8960_RINVOL, reg | 0x100);
+	reg = snd_soc_read(codec, WM8960_LADC);
+	snd_soc_write(codec, WM8960_LADC, reg | 0x100);
+	reg = snd_soc_read(codec, WM8960_RADC);
+	snd_soc_write(codec, WM8960_RADC, reg | 0x100);
+	reg = snd_soc_read(codec, WM8960_LDAC);
+	snd_soc_write(codec, WM8960_LDAC, reg | 0x100);
+	reg = snd_soc_read(codec, WM8960_RDAC);
+	snd_soc_write(codec, WM8960_RDAC, reg | 0x100);
+	reg = snd_soc_read(codec, WM8960_LOUT1);
+	snd_soc_write(codec, WM8960_LOUT1, reg | 0x100);
+	reg = snd_soc_read(codec, WM8960_ROUT1);
+	snd_soc_write(codec, WM8960_ROUT1, reg | 0x100);
+	reg = snd_soc_read(codec, WM8960_LOUT2);
+	snd_soc_write(codec, WM8960_LOUT2, reg | 0x100);
+	reg = snd_soc_read(codec, WM8960_ROUT2);
+	snd_soc_write(codec, WM8960_ROUT2, reg | 0x100);
+
+	wm8960_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto err;
+	}
+
+	ret = snd_soc_register_dai(&wm8960_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		goto err_codec;
+	}
+
+	return 0;
+
+err_codec:
+	snd_soc_unregister_codec(codec);
+err:
+	kfree(wm8960);
+	return ret;
+}
+
+static void wm8960_unregister(struct wm8960_priv *wm8960)
+{
+	wm8960_set_bias_level(&wm8960->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dai(&wm8960_dai);
+	snd_soc_unregister_codec(&wm8960->codec);
+	kfree(wm8960);
+	wm8960_codec = NULL;
+}
+
+static __devinit int wm8960_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8960_priv *wm8960;
+	struct snd_soc_codec *codec;
+
+	wm8960 = kzalloc(sizeof(struct wm8960_priv), GFP_KERNEL);
+	if (wm8960 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8960->codec;
+
+	i2c_set_clientdata(i2c, wm8960);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+
+	return wm8960_register(wm8960, SND_SOC_I2C);
+}
+
+static __devexit int wm8960_i2c_remove(struct i2c_client *client)
+{
+	struct wm8960_priv *wm8960 = i2c_get_clientdata(client);
+	wm8960_unregister(wm8960);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8960_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8960_i2c_resume(struct i2c_client *client)
+{
+	return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8960_i2c_suspend NULL
+#define wm8960_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm8960_i2c_id[] = {
+	{ "wm8960", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
+
+static struct i2c_driver wm8960_i2c_driver = {
+	.driver = {
+		.name = "WM8960 I2C Codec",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm8960_i2c_probe,
+	.remove =   __devexit_p(wm8960_i2c_remove),
+	.suspend =  wm8960_i2c_suspend,
+	.resume =   wm8960_i2c_resume,
+	.id_table = wm8960_i2c_id,
+};
+
+static int __init wm8960_modinit(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&wm8960_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8960 I2C driver: %d\n",
+		       ret);
+	}
+
+	return ret;
+}
+module_init(wm8960_modinit);
+
+static void __exit wm8960_exit(void)
+{
+	i2c_del_driver(&wm8960_i2c_driver);
+}
+module_exit(wm8960_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8960 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8960.h b/sound/soc/codecs/wm8960.h
new file mode 100644
index 0000000..c9af56c
--- /dev/null
+++ b/sound/soc/codecs/wm8960.h
@@ -0,0 +1,127 @@
+/*
+ * wm8960.h  --  WM8960 Soc Audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8960_H
+#define _WM8960_H
+
+/* WM8960 register space */
+
+
+#define WM8960_CACHEREGNUM 	56
+
+#define WM8960_LINVOL		0x0
+#define WM8960_RINVOL		0x1
+#define WM8960_LOUT1		0x2
+#define WM8960_ROUT1		0x3
+#define WM8960_CLOCK1		0x4
+#define WM8960_DACCTL1		0x5
+#define WM8960_DACCTL2		0x6
+#define WM8960_IFACE1		0x7
+#define WM8960_CLOCK2		0x8
+#define WM8960_IFACE2		0x9
+#define WM8960_LDAC		0xa
+#define WM8960_RDAC		0xb
+
+#define WM8960_RESET		0xf
+#define WM8960_3D		0x10
+#define WM8960_ALC1		0x11
+#define WM8960_ALC2		0x12
+#define WM8960_ALC3		0x13
+#define WM8960_NOISEG		0x14
+#define WM8960_LADC		0x15
+#define WM8960_RADC		0x16
+#define WM8960_ADDCTL1		0x17
+#define WM8960_ADDCTL2		0x18
+#define WM8960_POWER1		0x19
+#define WM8960_POWER2		0x1a
+#define WM8960_ADDCTL3		0x1b
+#define WM8960_APOP1		0x1c
+#define WM8960_APOP2		0x1d
+
+#define WM8960_LINPATH		0x20
+#define WM8960_RINPATH		0x21
+#define WM8960_LOUTMIX		0x22
+
+#define WM8960_ROUTMIX		0x25
+#define WM8960_MONOMIX1		0x26
+#define WM8960_MONOMIX2		0x27
+#define WM8960_LOUT2		0x28
+#define WM8960_ROUT2		0x29
+#define WM8960_MONO		0x2a
+#define WM8960_INBMIX1		0x2b
+#define WM8960_INBMIX2		0x2c
+#define WM8960_BYPASS1		0x2d
+#define WM8960_BYPASS2		0x2e
+#define WM8960_POWER3		0x2f
+#define WM8960_ADDCTL4		0x30
+#define WM8960_CLASSD1		0x31
+
+#define WM8960_CLASSD3		0x33
+#define WM8960_PLL1		0x34
+#define WM8960_PLL2		0x35
+#define WM8960_PLL3		0x36
+#define WM8960_PLL4		0x37
+
+
+/*
+ * WM8960 Clock dividers
+ */
+#define WM8960_SYSCLKDIV 		0
+#define WM8960_DACDIV			1
+#define WM8960_OPCLKDIV			2
+#define WM8960_DCLKDIV			3
+#define WM8960_TOCLKSEL			4
+#define WM8960_SYSCLKSEL		5
+
+#define WM8960_SYSCLK_DIV_1		(0 << 1)
+#define WM8960_SYSCLK_DIV_2		(2 << 1)
+
+#define WM8960_SYSCLK_MCLK		(0 << 0)
+#define WM8960_SYSCLK_PLL		(1 << 0)
+
+#define WM8960_DAC_DIV_1		(0 << 3)
+#define WM8960_DAC_DIV_1_5		(1 << 3)
+#define WM8960_DAC_DIV_2		(2 << 3)
+#define WM8960_DAC_DIV_3		(3 << 3)
+#define WM8960_DAC_DIV_4		(4 << 3)
+#define WM8960_DAC_DIV_5_5		(5 << 3)
+#define WM8960_DAC_DIV_6		(6 << 3)
+
+#define WM8960_DCLK_DIV_1_5		(0 << 6)
+#define WM8960_DCLK_DIV_2		(1 << 6)
+#define WM8960_DCLK_DIV_3		(2 << 6)
+#define WM8960_DCLK_DIV_4		(3 << 6)
+#define WM8960_DCLK_DIV_6		(4 << 6)
+#define WM8960_DCLK_DIV_8		(5 << 6)
+#define WM8960_DCLK_DIV_12		(6 << 6)
+#define WM8960_DCLK_DIV_16		(7 << 6)
+
+#define WM8960_TOCLK_F19		(0 << 1)
+#define WM8960_TOCLK_F21		(1 << 1)
+
+#define WM8960_OPCLK_DIV_1		(0 << 0)
+#define WM8960_OPCLK_DIV_2		(1 << 0)
+#define WM8960_OPCLK_DIV_3		(2 << 0)
+#define WM8960_OPCLK_DIV_4		(3 << 0)
+#define WM8960_OPCLK_DIV_5_5		(4 << 0)
+#define WM8960_OPCLK_DIV_6		(5 << 0)
+
+extern struct snd_soc_dai wm8960_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8960;
+
+#define WM8960_DRES_400R 0
+#define WM8960_DRES_200R 1
+#define WM8960_DRES_600R 2
+#define WM8960_DRES_150R 3
+#define WM8960_DRES_MAX  3
+
+struct wm8960_data {
+	int dres;
+};
+
+#endif
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
new file mode 100644
index 0000000..5030320
--- /dev/null
+++ b/sound/soc/codecs/wm8961.c
@@ -0,0 +1,1265 @@
+/*
+ * wm8961.c  --  WM8961 ALSA SoC Audio driver
+ *
+ * Author: Mark Brown
+ *
+ * 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.
+ *
+ * Currently unimplemented features:
+ *  - ALC
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8961.h"
+
+#define WM8961_MAX_REGISTER                     0xFC
+
+static u16 wm8961_reg_defaults[] = {
+	0x009F,     /* R0   - Left Input volume */
+	0x009F,     /* R1   - Right Input volume */
+	0x0000,     /* R2   - LOUT1 volume */
+	0x0000,     /* R3   - ROUT1 volume */
+	0x0020,     /* R4   - Clocking1 */
+	0x0008,     /* R5   - ADC & DAC Control 1 */
+	0x0000,     /* R6   - ADC & DAC Control 2 */
+	0x000A,     /* R7   - Audio Interface 0 */
+	0x01F4,     /* R8   - Clocking2 */
+	0x0000,     /* R9   - Audio Interface 1 */
+	0x00FF,     /* R10  - Left DAC volume */
+	0x00FF,     /* R11  - Right DAC volume */
+	0x0000,     /* R12 */
+	0x0000,     /* R13 */
+	0x0040,     /* R14  - Audio Interface 2 */
+	0x0000,     /* R15  - Software Reset */
+	0x0000,     /* R16 */
+	0x007B,     /* R17  - ALC1 */
+	0x0000,     /* R18  - ALC2 */
+	0x0032,     /* R19  - ALC3 */
+	0x0000,     /* R20  - Noise Gate */
+	0x00C0,     /* R21  - Left ADC volume */
+	0x00C0,     /* R22  - Right ADC volume */
+	0x0120,     /* R23  - Additional control(1) */
+	0x0000,     /* R24  - Additional control(2) */
+	0x0000,     /* R25  - Pwr Mgmt (1) */
+	0x0000,     /* R26  - Pwr Mgmt (2) */
+	0x0000,     /* R27  - Additional Control (3) */
+	0x0000,     /* R28  - Anti-pop */
+	0x0000,     /* R29 */
+	0x005F,     /* R30  - Clocking 3 */
+	0x0000,     /* R31 */
+	0x0000,     /* R32  - ADCL signal path */
+	0x0000,     /* R33  - ADCR signal path */
+	0x0000,     /* R34 */
+	0x0000,     /* R35 */
+	0x0000,     /* R36 */
+	0x0000,     /* R37 */
+	0x0000,     /* R38 */
+	0x0000,     /* R39 */
+	0x0000,     /* R40  - LOUT2 volume */
+	0x0000,     /* R41  - ROUT2 volume */
+	0x0000,     /* R42 */
+	0x0000,     /* R43 */
+	0x0000,     /* R44 */
+	0x0000,     /* R45 */
+	0x0000,     /* R46 */
+	0x0000,     /* R47  - Pwr Mgmt (3) */
+	0x0023,     /* R48  - Additional Control (4) */
+	0x0000,     /* R49  - Class D Control 1 */
+	0x0000,     /* R50 */
+	0x0003,     /* R51  - Class D Control 2 */
+	0x0000,     /* R52 */
+	0x0000,     /* R53 */
+	0x0000,     /* R54 */
+	0x0000,     /* R55 */
+	0x0106,     /* R56  - Clocking 4 */
+	0x0000,     /* R57  - DSP Sidetone 0 */
+	0x0000,     /* R58  - DSP Sidetone 1 */
+	0x0000,     /* R59 */
+	0x0000,     /* R60  - DC Servo 0 */
+	0x0000,     /* R61  - DC Servo 1 */
+	0x0000,     /* R62 */
+	0x015E,     /* R63  - DC Servo 3 */
+	0x0010,     /* R64 */
+	0x0010,     /* R65  - DC Servo 5 */
+	0x0000,     /* R66 */
+	0x0001,     /* R67 */
+	0x0003,     /* R68  - Analogue PGA Bias */
+	0x0000,     /* R69  - Analogue HP 0 */
+	0x0060,     /* R70 */
+	0x01FB,     /* R71  - Analogue HP 2 */
+	0x0000,     /* R72  - Charge Pump 1 */
+	0x0065,     /* R73 */
+	0x005F,     /* R74 */
+	0x0059,     /* R75 */
+	0x006B,     /* R76 */
+	0x0038,     /* R77 */
+	0x000C,     /* R78 */
+	0x000A,     /* R79 */
+	0x006B,     /* R80 */
+	0x0000,     /* R81 */
+	0x0000,     /* R82  - Charge Pump B */
+	0x0087,     /* R83 */
+	0x0000,     /* R84 */
+	0x005C,     /* R85 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87  - Write Sequencer 1 */
+	0x0000,     /* R88  - Write Sequencer 2 */
+	0x0000,     /* R89  - Write Sequencer 3 */
+	0x0000,     /* R90  - Write Sequencer 4 */
+	0x0000,     /* R91  - Write Sequencer 5 */
+	0x0000,     /* R92  - Write Sequencer 6 */
+	0x0000,     /* R93  - Write Sequencer 7 */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0000,     /* R96 */
+	0x0000,     /* R97 */
+	0x0000,     /* R98 */
+	0x0000,     /* R99 */
+	0x0000,     /* R100 */
+	0x0000,     /* R101 */
+	0x0000,     /* R102 */
+	0x0000,     /* R103 */
+	0x0000,     /* R104 */
+	0x0000,     /* R105 */
+	0x0000,     /* R106 */
+	0x0000,     /* R107 */
+	0x0000,     /* R108 */
+	0x0000,     /* R109 */
+	0x0000,     /* R110 */
+	0x0000,     /* R111 */
+	0x0000,     /* R112 */
+	0x0000,     /* R113 */
+	0x0000,     /* R114 */
+	0x0000,     /* R115 */
+	0x0000,     /* R116 */
+	0x0000,     /* R117 */
+	0x0000,     /* R118 */
+	0x0000,     /* R119 */
+	0x0000,     /* R120 */
+	0x0000,     /* R121 */
+	0x0000,     /* R122 */
+	0x0000,     /* R123 */
+	0x0000,     /* R124 */
+	0x0000,     /* R125 */
+	0x0000,     /* R126 */
+	0x0000,     /* R127 */
+	0x0000,     /* R128 */
+	0x0000,     /* R129 */
+	0x0000,     /* R130 */
+	0x0000,     /* R131 */
+	0x0000,     /* R132 */
+	0x0000,     /* R133 */
+	0x0000,     /* R134 */
+	0x0000,     /* R135 */
+	0x0000,     /* R136 */
+	0x0000,     /* R137 */
+	0x0000,     /* R138 */
+	0x0000,     /* R139 */
+	0x0000,     /* R140 */
+	0x0000,     /* R141 */
+	0x0000,     /* R142 */
+	0x0000,     /* R143 */
+	0x0000,     /* R144 */
+	0x0000,     /* R145 */
+	0x0000,     /* R146 */
+	0x0000,     /* R147 */
+	0x0000,     /* R148 */
+	0x0000,     /* R149 */
+	0x0000,     /* R150 */
+	0x0000,     /* R151 */
+	0x0000,     /* R152 */
+	0x0000,     /* R153 */
+	0x0000,     /* R154 */
+	0x0000,     /* R155 */
+	0x0000,     /* R156 */
+	0x0000,     /* R157 */
+	0x0000,     /* R158 */
+	0x0000,     /* R159 */
+	0x0000,     /* R160 */
+	0x0000,     /* R161 */
+	0x0000,     /* R162 */
+	0x0000,     /* R163 */
+	0x0000,     /* R164 */
+	0x0000,     /* R165 */
+	0x0000,     /* R166 */
+	0x0000,     /* R167 */
+	0x0000,     /* R168 */
+	0x0000,     /* R169 */
+	0x0000,     /* R170 */
+	0x0000,     /* R171 */
+	0x0000,     /* R172 */
+	0x0000,     /* R173 */
+	0x0000,     /* R174 */
+	0x0000,     /* R175 */
+	0x0000,     /* R176 */
+	0x0000,     /* R177 */
+	0x0000,     /* R178 */
+	0x0000,     /* R179 */
+	0x0000,     /* R180 */
+	0x0000,     /* R181 */
+	0x0000,     /* R182 */
+	0x0000,     /* R183 */
+	0x0000,     /* R184 */
+	0x0000,     /* R185 */
+	0x0000,     /* R186 */
+	0x0000,     /* R187 */
+	0x0000,     /* R188 */
+	0x0000,     /* R189 */
+	0x0000,     /* R190 */
+	0x0000,     /* R191 */
+	0x0000,     /* R192 */
+	0x0000,     /* R193 */
+	0x0000,     /* R194 */
+	0x0000,     /* R195 */
+	0x0030,     /* R196 */
+	0x0006,     /* R197 */
+	0x0000,     /* R198 */
+	0x0060,     /* R199 */
+	0x0000,     /* R200 */
+	0x003F,     /* R201 */
+	0x0000,     /* R202 */
+	0x0000,     /* R203 */
+	0x0000,     /* R204 */
+	0x0001,     /* R205 */
+	0x0000,     /* R206 */
+	0x0181,     /* R207 */
+	0x0005,     /* R208 */
+	0x0008,     /* R209 */
+	0x0008,     /* R210 */
+	0x0000,     /* R211 */
+	0x013B,     /* R212 */
+	0x0000,     /* R213 */
+	0x0000,     /* R214 */
+	0x0000,     /* R215 */
+	0x0000,     /* R216 */
+	0x0070,     /* R217 */
+	0x0000,     /* R218 */
+	0x0000,     /* R219 */
+	0x0000,     /* R220 */
+	0x0000,     /* R221 */
+	0x0000,     /* R222 */
+	0x0003,     /* R223 */
+	0x0000,     /* R224 */
+	0x0000,     /* R225 */
+	0x0001,     /* R226 */
+	0x0008,     /* R227 */
+	0x0000,     /* R228 */
+	0x0000,     /* R229 */
+	0x0000,     /* R230 */
+	0x0000,     /* R231 */
+	0x0004,     /* R232 */
+	0x0000,     /* R233 */
+	0x0000,     /* R234 */
+	0x0000,     /* R235 */
+	0x0000,     /* R236 */
+	0x0000,     /* R237 */
+	0x0080,     /* R238 */
+	0x0000,     /* R239 */
+	0x0000,     /* R240 */
+	0x0000,     /* R241 */
+	0x0000,     /* R242 */
+	0x0000,     /* R243 */
+	0x0000,     /* R244 */
+	0x0052,     /* R245 */
+	0x0110,     /* R246 */
+	0x0040,     /* R247 */
+	0x0000,     /* R248 */
+	0x0030,     /* R249 */
+	0x0000,     /* R250 */
+	0x0000,     /* R251 */
+	0x0001,     /* R252 - General test 1 */
+};
+
+struct wm8961_priv {
+	struct snd_soc_codec codec;
+	int sysclk;
+	u16 reg_cache[WM8961_MAX_REGISTER];
+};
+
+static int wm8961_volatile_register(unsigned int reg)
+{
+	switch (reg) {
+	case WM8961_SOFTWARE_RESET:
+	case WM8961_WRITE_SEQUENCER_7:
+	case WM8961_DC_SERVO_1:
+		return 1;
+
+	default:
+		return 0;
+	}
+}
+
+static int wm8961_reset(struct snd_soc_codec *codec)
+{
+	return snd_soc_write(codec, WM8961_SOFTWARE_RESET, 0);
+}
+
+/*
+ * The headphone output supports special anti-pop sequences giving
+ * silent power up and power down.
+ */
+static int wm8961_hp_event(struct snd_soc_dapm_widget *w,
+			   struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	u16 hp_reg = snd_soc_read(codec, WM8961_ANALOGUE_HP_0);
+	u16 cp_reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_1);
+	u16 pwr_reg = snd_soc_read(codec, WM8961_PWR_MGMT_2);
+	u16 dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1);
+	int timeout = 500;
+
+	if (event & SND_SOC_DAPM_POST_PMU) {
+		/* Make sure the output is shorted */
+		hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
+		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+		/* Enable the charge pump */
+		cp_reg |= WM8961_CP_ENA;
+		snd_soc_write(codec, WM8961_CHARGE_PUMP_1, cp_reg);
+		mdelay(5);
+
+		/* Enable the PGA */
+		pwr_reg |= WM8961_LOUT1_PGA | WM8961_ROUT1_PGA;
+		snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+		/* Enable the amplifier */
+		hp_reg |= WM8961_HPR_ENA | WM8961_HPL_ENA;
+		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+		/* Second stage enable */
+		hp_reg |= WM8961_HPR_ENA_DLY | WM8961_HPL_ENA_DLY;
+		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+		/* Enable the DC servo & trigger startup */
+		dcs_reg |=
+			WM8961_DCS_ENA_CHAN_HPR | WM8961_DCS_TRIG_STARTUP_HPR |
+			WM8961_DCS_ENA_CHAN_HPL | WM8961_DCS_TRIG_STARTUP_HPL;
+		dev_dbg(codec->dev, "Enabling DC servo\n");
+
+		snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);
+		do {
+			msleep(1);
+			dcs_reg = snd_soc_read(codec, WM8961_DC_SERVO_1);
+		} while (--timeout &&
+			 dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
+				WM8961_DCS_TRIG_STARTUP_HPL));
+		if (dcs_reg & (WM8961_DCS_TRIG_STARTUP_HPR |
+			       WM8961_DCS_TRIG_STARTUP_HPL))
+			dev_err(codec->dev, "DC servo timed out\n");
+		else
+			dev_dbg(codec->dev, "DC servo startup complete\n");
+
+		/* Enable the output stage */
+		hp_reg |= WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP;
+		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+		/* Remove the short on the output stage */
+		hp_reg |= WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT;
+		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+	}
+
+	if (event & SND_SOC_DAPM_PRE_PMD) {
+		/* Short the output */
+		hp_reg &= ~(WM8961_HPR_RMV_SHORT | WM8961_HPL_RMV_SHORT);
+		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+		/* Disable the output stage */
+		hp_reg &= ~(WM8961_HPR_ENA_OUTP | WM8961_HPL_ENA_OUTP);
+		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+		/* Disable DC offset cancellation */
+		dcs_reg &= ~(WM8961_DCS_ENA_CHAN_HPR |
+			     WM8961_DCS_ENA_CHAN_HPL);
+		snd_soc_write(codec, WM8961_DC_SERVO_1, dcs_reg);
+
+		/* Finish up */
+		hp_reg &= ~(WM8961_HPR_ENA_DLY | WM8961_HPR_ENA |
+			    WM8961_HPL_ENA_DLY | WM8961_HPL_ENA);
+		snd_soc_write(codec, WM8961_ANALOGUE_HP_0, hp_reg);
+
+		/* Disable the PGA */
+		pwr_reg &= ~(WM8961_LOUT1_PGA | WM8961_ROUT1_PGA);
+		snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+		/* Disable the charge pump */
+		dev_dbg(codec->dev, "Disabling charge pump\n");
+		snd_soc_write(codec, WM8961_CHARGE_PUMP_1,
+			     cp_reg & ~WM8961_CP_ENA);
+	}
+
+	return 0;
+}
+
+static int wm8961_spk_event(struct snd_soc_dapm_widget *w,
+			    struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	u16 pwr_reg = snd_soc_read(codec, WM8961_PWR_MGMT_2);
+	u16 spk_reg = snd_soc_read(codec, WM8961_CLASS_D_CONTROL_1);
+
+	if (event & SND_SOC_DAPM_POST_PMU) {
+		/* Enable the PGA */
+		pwr_reg |= WM8961_SPKL_PGA | WM8961_SPKR_PGA;
+		snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+
+		/* Enable the amplifier */
+		spk_reg |= WM8961_SPKL_ENA | WM8961_SPKR_ENA;
+		snd_soc_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg);
+	}
+
+	if (event & SND_SOC_DAPM_PRE_PMD) {
+		/* Enable the amplifier */
+		spk_reg &= ~(WM8961_SPKL_ENA | WM8961_SPKR_ENA);
+		snd_soc_write(codec, WM8961_CLASS_D_CONTROL_1, spk_reg);
+
+		/* Enable the PGA */
+		pwr_reg &= ~(WM8961_SPKL_PGA | WM8961_SPKR_PGA);
+		snd_soc_write(codec, WM8961_PWR_MGMT_2, pwr_reg);
+	}
+
+	return 0;
+}
+
+static const char *adc_hpf_text[] = {
+	"Hi-fi", "Voice 1", "Voice 2", "Voice 3",
+};
+
+static const struct soc_enum adc_hpf =
+	SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_2, 7, 4, adc_hpf_text);
+
+static const char *dac_deemph_text[] = {
+	"None", "32kHz", "44.1kHz", "48kHz",
+};
+
+static const struct soc_enum dac_deemph =
+	SOC_ENUM_SINGLE(WM8961_ADC_DAC_CONTROL_1, 1, 4, dac_deemph_text);
+
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(hp_sec_tlv, -700, 100, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
+static unsigned int boost_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 0, TLV_DB_SCALE_ITEM(0,  0, 0),
+	1, 1, TLV_DB_SCALE_ITEM(13, 0, 0),
+	2, 2, TLV_DB_SCALE_ITEM(20, 0, 0),
+	3, 3, TLV_DB_SCALE_ITEM(29, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -2325, 75, 0);
+
+static const struct snd_kcontrol_new wm8961_snd_controls[] = {
+SOC_DOUBLE_R_TLV("Headphone Volume", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME,
+		 0, 127, 0, out_tlv),
+SOC_DOUBLE_TLV("Headphone Secondary Volume", WM8961_ANALOGUE_HP_2,
+	       6, 3, 7, 0, hp_sec_tlv),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8961_LOUT1_VOLUME, WM8961_ROUT1_VOLUME,
+	     7, 1, 0),
+
+SOC_DOUBLE_R_TLV("Speaker Volume", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME,
+		 0, 127, 0, out_tlv),
+SOC_DOUBLE_R("Speaker ZC Switch", WM8961_LOUT2_VOLUME, WM8961_ROUT2_VOLUME,
+	   7, 1, 0),
+SOC_SINGLE("Speaker AC Gain", WM8961_CLASS_D_CONTROL_2, 0, 7, 0),
+
+SOC_SINGLE("DAC x128 OSR Switch", WM8961_ADC_DAC_CONTROL_2, 0, 1, 0),
+SOC_ENUM("DAC Deemphasis", dac_deemph),
+SOC_SINGLE("DAC Soft Mute Switch", WM8961_ADC_DAC_CONTROL_2, 3, 1, 0),
+
+SOC_DOUBLE_R_TLV("Sidetone Volume", WM8961_DSP_SIDETONE_0,
+		 WM8961_DSP_SIDETONE_1, 4, 12, 0, sidetone_tlv),
+
+SOC_SINGLE("ADC High Pass Filter Switch", WM8961_ADC_DAC_CONTROL_1, 0, 1, 0),
+SOC_ENUM("ADC High Pass Filter Mode", adc_hpf),
+
+SOC_DOUBLE_R_TLV("Capture Volume",
+		 WM8961_LEFT_ADC_VOLUME, WM8961_RIGHT_ADC_VOLUME,
+		 1, 119, 0, adc_tlv),
+SOC_DOUBLE_R_TLV("Capture Boost Volume",
+		 WM8961_ADCL_SIGNAL_PATH, WM8961_ADCR_SIGNAL_PATH,
+		 4, 3, 0, boost_tlv),
+SOC_DOUBLE_R_TLV("Capture PGA Volume",
+		 WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+		 0, 62, 0, pga_tlv),
+SOC_DOUBLE_R("Capture PGA ZC Switch",
+	     WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+	     6, 1, 1),
+SOC_DOUBLE_R("Capture PGA Switch",
+	     WM8961_LEFT_INPUT_VOLUME, WM8961_RIGHT_INPUT_VOLUME,
+	     7, 1, 1),
+};
+
+static const char *sidetone_text[] = {
+	"None", "Left", "Right"
+};
+
+static const struct soc_enum dacl_sidetone =
+	SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_0, 2, 3, sidetone_text);
+
+static const struct soc_enum dacr_sidetone =
+	SOC_ENUM_SINGLE(WM8961_DSP_SIDETONE_1, 2, 3, sidetone_text);
+
+static const struct snd_kcontrol_new dacl_mux =
+	SOC_DAPM_ENUM("DACL Sidetone", dacl_sidetone);
+
+static const struct snd_kcontrol_new dacr_mux =
+	SOC_DAPM_ENUM("DACR Sidetone", dacr_sidetone);
+
+static const struct snd_soc_dapm_widget wm8961_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("LINPUT"),
+SND_SOC_DAPM_INPUT("RINPUT"),
+
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8961_CLOCKING2, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Left Input", WM8961_PWR_MGMT_1, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Input", WM8961_PWR_MGMT_1, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", WM8961_PWR_MGMT_1, 3, 0),
+SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", WM8961_PWR_MGMT_1, 2, 0),
+
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8961_PWR_MGMT_1, 1, 0),
+
+SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &dacl_mux),
+SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &dacr_mux),
+
+SND_SOC_DAPM_DAC("DACL", "HiFi Playback", WM8961_PWR_MGMT_2, 8, 0),
+SND_SOC_DAPM_DAC("DACR", "HiFi Playback", WM8961_PWR_MGMT_2, 7, 0),
+
+/* Handle as a mono path for DCS */
+SND_SOC_DAPM_PGA_E("Headphone Output", SND_SOC_NOPM,
+		   4, 0, NULL, 0, wm8961_hp_event,
+		   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("Speaker Output", SND_SOC_NOPM,
+		   4, 0, NULL, 0, wm8961_spk_event,
+		   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("HP_L"),
+SND_SOC_DAPM_OUTPUT("HP_R"),
+SND_SOC_DAPM_OUTPUT("SPK_LN"),
+SND_SOC_DAPM_OUTPUT("SPK_LP"),
+SND_SOC_DAPM_OUTPUT("SPK_RN"),
+SND_SOC_DAPM_OUTPUT("SPK_RP"),
+};
+
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+	{ "DACL", NULL, "CLK_DSP" },
+	{ "DACL", NULL, "DACL Sidetone" },
+	{ "DACR", NULL, "CLK_DSP" },
+	{ "DACR", NULL, "DACR Sidetone" },
+
+	{ "DACL Sidetone", "Left", "ADCL" },
+	{ "DACL Sidetone", "Right", "ADCR" },
+
+	{ "DACR Sidetone", "Left", "ADCL" },
+	{ "DACR Sidetone", "Right", "ADCR" },
+
+	{ "HP_L", NULL, "Headphone Output" },
+	{ "HP_R", NULL, "Headphone Output" },
+	{ "Headphone Output", NULL, "DACL" },
+	{ "Headphone Output", NULL, "DACR" },
+
+	{ "SPK_LN", NULL, "Speaker Output" },
+	{ "SPK_LP", NULL, "Speaker Output" },
+	{ "SPK_RN", NULL, "Speaker Output" },
+	{ "SPK_RP", NULL, "Speaker Output" },
+
+	{ "Speaker Output", NULL, "DACL" },
+	{ "Speaker Output", NULL, "DACR" },
+
+	{ "ADCL", NULL, "Left Input" },
+	{ "ADCL", NULL, "CLK_DSP" },
+	{ "ADCR", NULL, "Right Input" },
+	{ "ADCR", NULL, "CLK_DSP" },
+
+	{ "Left Input", NULL, "LINPUT" },
+	{ "Right Input", NULL, "RINPUT" },
+
+};
+
+/* Values for CLK_SYS_RATE */
+static struct {
+	int ratio;
+	u16 val;
+} wm8961_clk_sys_ratio[] = {
+	{  64,  0 },
+	{  128, 1 },
+	{  192, 2 },
+	{  256, 3 },
+	{  384, 4 },
+	{  512, 5 },
+	{  768, 6 },
+	{ 1024, 7 },
+	{ 1408, 8 },
+	{ 1536, 9 },
+};
+
+/* Values for SAMPLE_RATE */
+static struct {
+	int rate;
+	u16 val;
+} wm8961_srate[] = {
+	{ 48000, 0 },
+	{ 44100, 0 },
+	{ 32000, 1 },
+	{ 22050, 2 },
+	{ 24000, 2 },
+	{ 16000, 3 },
+	{ 11250, 4 },
+	{ 12000, 4 },
+	{  8000, 5 },
+};
+
+static int wm8961_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8961_priv *wm8961 = codec->private_data;
+	int i, best, target, fs;
+	u16 reg;
+
+	fs = params_rate(params);
+
+	if (!wm8961->sysclk) {
+		dev_err(codec->dev, "MCLK has not been specified\n");
+		return -EINVAL;
+	}
+
+	/* Find the closest sample rate for the filters */
+	best = 0;
+	for (i = 0; i < ARRAY_SIZE(wm8961_srate); i++) {
+		if (abs(wm8961_srate[i].rate - fs) <
+		    abs(wm8961_srate[best].rate - fs))
+			best = i;
+	}
+	reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_3);
+	reg &= ~WM8961_SAMPLE_RATE_MASK;
+	reg |= wm8961_srate[best].val;
+	snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_3, reg);
+	dev_dbg(codec->dev, "Selected SRATE %dHz for %dHz\n",
+		wm8961_srate[best].rate, fs);
+
+	/* Select a CLK_SYS/fs ratio equal to or higher than required */
+	target = wm8961->sysclk / fs;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && target < 64) {
+		dev_err(codec->dev,
+			"SYSCLK must be at least 64*fs for DAC\n");
+		return -EINVAL;
+	}
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && target < 256) {
+		dev_err(codec->dev,
+			"SYSCLK must be at least 256*fs for ADC\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(wm8961_clk_sys_ratio); i++) {
+		if (wm8961_clk_sys_ratio[i].ratio >= target)
+			break;
+	}
+	if (i == ARRAY_SIZE(wm8961_clk_sys_ratio)) {
+		dev_err(codec->dev, "Unable to generate CLK_SYS_RATE\n");
+		return -EINVAL;
+	}
+	dev_dbg(codec->dev, "Selected CLK_SYS_RATE of %d for %d/%d=%d\n",
+		wm8961_clk_sys_ratio[i].ratio, wm8961->sysclk, fs,
+		wm8961->sysclk / fs);
+
+	reg = snd_soc_read(codec, WM8961_CLOCKING_4);
+	reg &= ~WM8961_CLK_SYS_RATE_MASK;
+	reg |= wm8961_clk_sys_ratio[i].val << WM8961_CLK_SYS_RATE_SHIFT;
+	snd_soc_write(codec, WM8961_CLOCKING_4, reg);
+
+	reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0);
+	reg &= ~WM8961_WL_MASK;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		reg |= 1 << WM8961_WL_SHIFT;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		reg |= 2 << WM8961_WL_SHIFT;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		reg |= 3 << WM8961_WL_SHIFT;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, reg);
+
+	/* Sloping stop-band filter is recommended for <= 24kHz */
+	reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2);
+	if (fs <= 24000)
+		reg |= WM8961_DACSLOPE;
+	else
+		reg &= WM8961_DACSLOPE;
+	snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);
+
+	return 0;
+}
+
+static int wm8961_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+			     unsigned int freq,
+			     int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8961_priv *wm8961 = codec->private_data;
+	u16 reg = snd_soc_read(codec, WM8961_CLOCKING1);
+
+	if (freq > 33000000) {
+		dev_err(codec->dev, "MCLK must be <33MHz\n");
+		return -EINVAL;
+	}
+
+	if (freq > 16500000) {
+		dev_dbg(codec->dev, "Using MCLK/2 for %dHz MCLK\n", freq);
+		reg |= WM8961_MCLKDIV;
+		freq /= 2;
+	} else {
+		dev_dbg(codec->dev, "Using MCLK/1 for %dHz MCLK\n", freq);
+		reg &= WM8961_MCLKDIV;
+	}
+
+	snd_soc_write(codec, WM8961_CLOCKING1, reg);
+
+	wm8961->sysclk = freq;
+
+	return 0;
+}
+
+static int wm8961_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 aif = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_0);
+
+	aif &= ~(WM8961_BCLKINV | WM8961_LRP |
+		 WM8961_MS | WM8961_FORMAT_MASK);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		aif |= WM8961_MS;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+
+	case SND_SOC_DAIFMT_LEFT_J:
+		aif |= 1;
+		break;
+
+	case SND_SOC_DAIFMT_I2S:
+		aif |= 2;
+		break;
+
+	case SND_SOC_DAIFMT_DSP_B:
+		aif |= WM8961_LRP;
+	case SND_SOC_DAIFMT_DSP_A:
+		aif |= 3;
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+		case SND_SOC_DAIFMT_IB_NF:
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		aif |= WM8961_LRP;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		aif |= WM8961_BCLKINV;
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		aif |= WM8961_BCLKINV | WM8961_LRP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return snd_soc_write(codec, WM8961_AUDIO_INTERFACE_0, aif);
+}
+
+static int wm8961_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 reg = snd_soc_read(codec, WM8961_ADDITIONAL_CONTROL_2);
+
+	if (tristate)
+		reg |= WM8961_TRIS;
+	else
+		reg &= ~WM8961_TRIS;
+
+	return snd_soc_write(codec, WM8961_ADDITIONAL_CONTROL_2, reg);
+}
+
+static int wm8961_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_1);
+
+	if (mute)
+		reg |= WM8961_DACMU;
+	else
+		reg &= ~WM8961_DACMU;
+
+	msleep(17);
+
+	return snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_1, reg);
+}
+
+static int wm8961_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 reg;
+
+	switch (div_id) {
+	case WM8961_BCLK:
+		reg = snd_soc_read(codec, WM8961_CLOCKING2);
+		reg &= ~WM8961_BCLKDIV_MASK;
+		reg |= div;
+		snd_soc_write(codec, WM8961_CLOCKING2, reg);
+		break;
+
+	case WM8961_LRCLK:
+		reg = snd_soc_read(codec, WM8961_AUDIO_INTERFACE_2);
+		reg &= ~WM8961_LRCLK_RATE_MASK;
+		reg |= div;
+		snd_soc_write(codec, WM8961_AUDIO_INTERFACE_2, reg);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm8961_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	u16 reg;
+
+	/* This is all slightly unusual since we have no bypass paths
+	 * and the output amplifier structure means we can just slam
+	 * the biases straight up rather than having to ramp them
+	 * slowly.
+	 */
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		if (codec->bias_level == SND_SOC_BIAS_STANDBY) {
+			/* Enable bias generation */
+			reg = snd_soc_read(codec, WM8961_ANTI_POP);
+			reg |= WM8961_BUFIOEN | WM8961_BUFDCOPEN;
+			snd_soc_write(codec, WM8961_ANTI_POP, reg);
+
+			/* VMID=2*50k, VREF */
+			reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+			reg &= ~WM8961_VMIDSEL_MASK;
+			reg |= (1 << WM8961_VMIDSEL_SHIFT) | WM8961_VREF;
+			snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+		}
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_PREPARE) {
+			/* VREF off */
+			reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+			reg &= ~WM8961_VREF;
+			snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+
+			/* Bias generation off */
+			reg = snd_soc_read(codec, WM8961_ANTI_POP);
+			reg &= ~(WM8961_BUFIOEN | WM8961_BUFDCOPEN);
+			snd_soc_write(codec, WM8961_ANTI_POP, reg);
+
+			/* VMID off */
+			reg = snd_soc_read(codec, WM8961_PWR_MGMT_1);
+			reg &= ~WM8961_VMIDSEL_MASK;
+			snd_soc_write(codec, WM8961_PWR_MGMT_1, reg);
+		}
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		break;
+	}
+
+	codec->bias_level = level;
+
+	return 0;
+}
+
+
+#define WM8961_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8961_FORMATS \
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+	SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8961_dai_ops = {
+	.hw_params = wm8961_hw_params,
+	.set_sysclk = wm8961_set_sysclk,
+	.set_fmt = wm8961_set_fmt,
+	.digital_mute = wm8961_digital_mute,
+	.set_tristate = wm8961_set_tristate,
+	.set_clkdiv = wm8961_set_clkdiv,
+};
+
+struct snd_soc_dai wm8961_dai = {
+	.name = "WM8961",
+	.playback = {
+		.stream_name = "HiFi Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8961_RATES,
+		.formats = WM8961_FORMATS,},
+	.capture = {
+		.stream_name = "HiFi Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8961_RATES,
+		.formats = WM8961_FORMATS,},
+	.ops = &wm8961_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm8961_dai);
+
+
+static struct snd_soc_codec *wm8961_codec;
+
+static int wm8961_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8961_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8961_codec;
+	codec = wm8961_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, wm8961_snd_controls,
+				ARRAY_SIZE(wm8961_snd_controls));
+	snd_soc_dapm_new_controls(codec, wm8961_dapm_widgets,
+				  ARRAY_SIZE(wm8961_dapm_widgets));
+	snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+	snd_soc_dapm_new_widgets(codec);
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+static int wm8961_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8961_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int wm8961_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	u16 *reg_cache = codec->reg_cache;
+	int i;
+
+	for (i = 0; i < codec->reg_cache_size; i++) {
+		if (i == WM8961_SOFTWARE_RESET)
+			continue;
+
+		snd_soc_write(codec, i, reg_cache[i]);
+	}
+
+	wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+#else
+#define wm8961_suspend NULL
+#define wm8961_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_wm8961 = {
+	.probe = 	wm8961_probe,
+	.remove = 	wm8961_remove,
+	.suspend =	wm8961_suspend,
+	.resume =	wm8961_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8961);
+
+static int wm8961_register(struct wm8961_priv *wm8961)
+{
+	struct snd_soc_codec *codec = &wm8961->codec;
+	int ret;
+	u16 reg;
+
+	if (wm8961_codec) {
+		dev_err(codec->dev, "Another WM8961 is registered\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8961;
+	codec->name = "WM8961";
+	codec->owner = THIS_MODULE;
+	codec->dai = &wm8961_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = ARRAY_SIZE(wm8961->reg_cache);
+	codec->reg_cache = &wm8961->reg_cache;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8961_set_bias_level;
+	codec->volatile_register = wm8961_volatile_register;
+
+	memcpy(codec->reg_cache, wm8961_reg_defaults,
+	       sizeof(wm8961_reg_defaults));
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	reg = snd_soc_read(codec, WM8961_SOFTWARE_RESET);
+	if (reg != 0x1801) {
+		dev_err(codec->dev, "Device is not a WM8961: ID=0x%x\n", reg);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* This isn't volatile - readback doesn't correspond to write */
+	reg = codec->hw_read(codec, WM8961_RIGHT_INPUT_VOLUME);
+	dev_info(codec->dev, "WM8961 family %d revision %c\n",
+		 (reg & WM8961_DEVICE_ID_MASK) >> WM8961_DEVICE_ID_SHIFT,
+		 ((reg & WM8961_CHIP_REV_MASK) >> WM8961_CHIP_REV_SHIFT)
+		 + 'A');
+
+	ret = wm8961_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		return ret;
+	}
+
+	/* Enable class W */
+	reg = snd_soc_read(codec, WM8961_CHARGE_PUMP_B);
+	reg |= WM8961_CP_DYN_PWR_MASK;
+	snd_soc_write(codec, WM8961_CHARGE_PUMP_B, reg);
+
+	/* Latch volume update bits (right channel only, we always
+	 * write both out) and default ZC on. */
+	reg = snd_soc_read(codec, WM8961_ROUT1_VOLUME);
+	snd_soc_write(codec, WM8961_ROUT1_VOLUME,
+		     reg | WM8961_LO1ZC | WM8961_OUT1VU);
+	snd_soc_write(codec, WM8961_LOUT1_VOLUME, reg | WM8961_LO1ZC);
+	reg = snd_soc_read(codec, WM8961_ROUT2_VOLUME);
+	snd_soc_write(codec, WM8961_ROUT2_VOLUME,
+		     reg | WM8961_SPKRZC | WM8961_SPKVU);
+	snd_soc_write(codec, WM8961_LOUT2_VOLUME, reg | WM8961_SPKLZC);
+
+	reg = snd_soc_read(codec, WM8961_RIGHT_ADC_VOLUME);
+	snd_soc_write(codec, WM8961_RIGHT_ADC_VOLUME, reg | WM8961_ADCVU);
+	reg = snd_soc_read(codec, WM8961_RIGHT_INPUT_VOLUME);
+	snd_soc_write(codec, WM8961_RIGHT_INPUT_VOLUME, reg | WM8961_IPVU);
+
+	/* Use soft mute by default */
+	reg = snd_soc_read(codec, WM8961_ADC_DAC_CONTROL_2);
+	reg |= WM8961_DACSMM;
+	snd_soc_write(codec, WM8961_ADC_DAC_CONTROL_2, reg);
+
+	/* Use automatic clocking mode by default; for now this is all
+	 * we support.
+	 */
+	reg = snd_soc_read(codec, WM8961_CLOCKING_3);
+	reg &= ~WM8961_MANUAL_MODE;
+	snd_soc_write(codec, WM8961_CLOCKING_3, reg);
+
+	wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	wm8961_dai.dev = codec->dev;
+
+	wm8961_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_register_dai(&wm8961_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		return ret;
+	}
+
+	return 0;
+
+err:
+	kfree(wm8961);
+	return ret;
+}
+
+static void wm8961_unregister(struct wm8961_priv *wm8961)
+{
+	wm8961_set_bias_level(&wm8961->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dai(&wm8961_dai);
+	snd_soc_unregister_codec(&wm8961->codec);
+	kfree(wm8961);
+	wm8961_codec = NULL;
+}
+
+static __devinit int wm8961_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8961_priv *wm8961;
+	struct snd_soc_codec *codec;
+
+	wm8961 = kzalloc(sizeof(struct wm8961_priv), GFP_KERNEL);
+	if (wm8961 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8961->codec;
+
+	i2c_set_clientdata(i2c, wm8961);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+
+	return wm8961_register(wm8961);
+}
+
+static __devexit int wm8961_i2c_remove(struct i2c_client *client)
+{
+	struct wm8961_priv *wm8961 = i2c_get_clientdata(client);
+	wm8961_unregister(wm8961);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8961_i2c_suspend(struct i2c_client *client, pm_message_t state)
+{
+	return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8961_i2c_resume(struct i2c_client *client)
+{
+	return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8961_i2c_suspend NULL
+#define wm8961_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm8961_i2c_id[] = {
+	{ "wm8961", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id);
+
+static struct i2c_driver wm8961_i2c_driver = {
+	.driver = {
+		.name = "wm8961",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm8961_i2c_probe,
+	.remove =   __devexit_p(wm8961_i2c_remove),
+	.suspend =  wm8961_i2c_suspend,
+	.resume =   wm8961_i2c_resume,
+	.id_table = wm8961_i2c_id,
+};
+
+static int __init wm8961_modinit(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&wm8961_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8961 I2C driver: %d\n",
+		       ret);
+	}
+
+	return ret;
+}
+module_init(wm8961_modinit);
+
+static void __exit wm8961_exit(void)
+{
+	i2c_del_driver(&wm8961_i2c_driver);
+}
+module_exit(wm8961_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8961 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8961.h b/sound/soc/codecs/wm8961.h
new file mode 100644
index 0000000..5513bfd7
--- /dev/null
+++ b/sound/soc/codecs/wm8961.h
@@ -0,0 +1,866 @@
+/*
+ * wm8961.h  --  WM8961 Soc Audio driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM8961_H
+#define _WM8961_H
+
+#include <sound/soc.h>
+
+extern struct snd_soc_codec_device soc_codec_dev_wm8961;
+extern struct snd_soc_dai wm8961_dai;
+
+#define WM8961_BCLK  1
+#define WM8961_LRCLK 2
+
+#define WM8961_BCLK_DIV_1    0
+#define WM8961_BCLK_DIV_1_5  1
+#define WM8961_BCLK_DIV_2    2
+#define WM8961_BCLK_DIV_3    3
+#define WM8961_BCLK_DIV_4    4
+#define WM8961_BCLK_DIV_5_5  5
+#define WM8961_BCLK_DIV_6    6
+#define WM8961_BCLK_DIV_8    7
+#define WM8961_BCLK_DIV_11   8
+#define WM8961_BCLK_DIV_12   9
+#define WM8961_BCLK_DIV_16  10
+#define WM8961_BCLK_DIV_24  11
+#define WM8961_BCLK_DIV_32  13
+
+
+/*
+ * Register values.
+ */
+#define WM8961_LEFT_INPUT_VOLUME                0x00
+#define WM8961_RIGHT_INPUT_VOLUME               0x01
+#define WM8961_LOUT1_VOLUME                     0x02
+#define WM8961_ROUT1_VOLUME                     0x03
+#define WM8961_CLOCKING1                        0x04
+#define WM8961_ADC_DAC_CONTROL_1                0x05
+#define WM8961_ADC_DAC_CONTROL_2                0x06
+#define WM8961_AUDIO_INTERFACE_0                0x07
+#define WM8961_CLOCKING2                        0x08
+#define WM8961_AUDIO_INTERFACE_1                0x09
+#define WM8961_LEFT_DAC_VOLUME                  0x0A
+#define WM8961_RIGHT_DAC_VOLUME                 0x0B
+#define WM8961_AUDIO_INTERFACE_2                0x0E
+#define WM8961_SOFTWARE_RESET                   0x0F
+#define WM8961_ALC1                             0x11
+#define WM8961_ALC2                             0x12
+#define WM8961_ALC3                             0x13
+#define WM8961_NOISE_GATE                       0x14
+#define WM8961_LEFT_ADC_VOLUME                  0x15
+#define WM8961_RIGHT_ADC_VOLUME                 0x16
+#define WM8961_ADDITIONAL_CONTROL_1             0x17
+#define WM8961_ADDITIONAL_CONTROL_2             0x18
+#define WM8961_PWR_MGMT_1                       0x19
+#define WM8961_PWR_MGMT_2                       0x1A
+#define WM8961_ADDITIONAL_CONTROL_3             0x1B
+#define WM8961_ANTI_POP                         0x1C
+#define WM8961_CLOCKING_3                       0x1E
+#define WM8961_ADCL_SIGNAL_PATH                 0x20
+#define WM8961_ADCR_SIGNAL_PATH                 0x21
+#define WM8961_LOUT2_VOLUME                     0x28
+#define WM8961_ROUT2_VOLUME                     0x29
+#define WM8961_PWR_MGMT_3                       0x2F
+#define WM8961_ADDITIONAL_CONTROL_4             0x30
+#define WM8961_CLASS_D_CONTROL_1                0x31
+#define WM8961_CLASS_D_CONTROL_2                0x33
+#define WM8961_CLOCKING_4                       0x38
+#define WM8961_DSP_SIDETONE_0                   0x39
+#define WM8961_DSP_SIDETONE_1                   0x3A
+#define WM8961_DC_SERVO_0                       0x3C
+#define WM8961_DC_SERVO_1                       0x3D
+#define WM8961_DC_SERVO_3                       0x3F
+#define WM8961_DC_SERVO_5                       0x41
+#define WM8961_ANALOGUE_PGA_BIAS                0x44
+#define WM8961_ANALOGUE_HP_0                    0x45
+#define WM8961_ANALOGUE_HP_2                    0x47
+#define WM8961_CHARGE_PUMP_1                    0x48
+#define WM8961_CHARGE_PUMP_B                    0x52
+#define WM8961_WRITE_SEQUENCER_1                0x57
+#define WM8961_WRITE_SEQUENCER_2                0x58
+#define WM8961_WRITE_SEQUENCER_3                0x59
+#define WM8961_WRITE_SEQUENCER_4                0x5A
+#define WM8961_WRITE_SEQUENCER_5                0x5B
+#define WM8961_WRITE_SEQUENCER_6                0x5C
+#define WM8961_WRITE_SEQUENCER_7                0x5D
+#define WM8961_GENERAL_TEST_1                   0xFC
+
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Left Input volume
+ */
+#define WM8961_IPVU                             0x0100  /* IPVU */
+#define WM8961_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8961_IPVU_SHIFT                            8  /* IPVU */
+#define WM8961_IPVU_WIDTH                            1  /* IPVU */
+#define WM8961_LINMUTE                          0x0080  /* LINMUTE */
+#define WM8961_LINMUTE_MASK                     0x0080  /* LINMUTE */
+#define WM8961_LINMUTE_SHIFT                         7  /* LINMUTE */
+#define WM8961_LINMUTE_WIDTH                         1  /* LINMUTE */
+#define WM8961_LIZC                             0x0040  /* LIZC */
+#define WM8961_LIZC_MASK                        0x0040  /* LIZC */
+#define WM8961_LIZC_SHIFT                            6  /* LIZC */
+#define WM8961_LIZC_WIDTH                            1  /* LIZC */
+#define WM8961_LINVOL_MASK                      0x003F  /* LINVOL - [5:0] */
+#define WM8961_LINVOL_SHIFT                          0  /* LINVOL - [5:0] */
+#define WM8961_LINVOL_WIDTH                          6  /* LINVOL - [5:0] */
+
+/*
+ * R1 (0x01) - Right Input volume
+ */
+#define WM8961_DEVICE_ID_MASK                   0xF000  /* DEVICE_ID - [15:12] */
+#define WM8961_DEVICE_ID_SHIFT                      12  /* DEVICE_ID - [15:12] */
+#define WM8961_DEVICE_ID_WIDTH                       4  /* DEVICE_ID - [15:12] */
+#define WM8961_CHIP_REV_MASK                    0x0E00  /* CHIP_REV - [11:9] */
+#define WM8961_CHIP_REV_SHIFT                        9  /* CHIP_REV - [11:9] */
+#define WM8961_CHIP_REV_WIDTH                        3  /* CHIP_REV - [11:9] */
+#define WM8961_IPVU                             0x0100  /* IPVU */
+#define WM8961_IPVU_MASK                        0x0100  /* IPVU */
+#define WM8961_IPVU_SHIFT                            8  /* IPVU */
+#define WM8961_IPVU_WIDTH                            1  /* IPVU */
+#define WM8961_RINMUTE                          0x0080  /* RINMUTE */
+#define WM8961_RINMUTE_MASK                     0x0080  /* RINMUTE */
+#define WM8961_RINMUTE_SHIFT                         7  /* RINMUTE */
+#define WM8961_RINMUTE_WIDTH                         1  /* RINMUTE */
+#define WM8961_RIZC                             0x0040  /* RIZC */
+#define WM8961_RIZC_MASK                        0x0040  /* RIZC */
+#define WM8961_RIZC_SHIFT                            6  /* RIZC */
+#define WM8961_RIZC_WIDTH                            1  /* RIZC */
+#define WM8961_RINVOL_MASK                      0x003F  /* RINVOL - [5:0] */
+#define WM8961_RINVOL_SHIFT                          0  /* RINVOL - [5:0] */
+#define WM8961_RINVOL_WIDTH                          6  /* RINVOL - [5:0] */
+
+/*
+ * R2 (0x02) - LOUT1 volume
+ */
+#define WM8961_OUT1VU                           0x0100  /* OUT1VU */
+#define WM8961_OUT1VU_MASK                      0x0100  /* OUT1VU */
+#define WM8961_OUT1VU_SHIFT                          8  /* OUT1VU */
+#define WM8961_OUT1VU_WIDTH                          1  /* OUT1VU */
+#define WM8961_LO1ZC                            0x0080  /* LO1ZC */
+#define WM8961_LO1ZC_MASK                       0x0080  /* LO1ZC */
+#define WM8961_LO1ZC_SHIFT                           7  /* LO1ZC */
+#define WM8961_LO1ZC_WIDTH                           1  /* LO1ZC */
+#define WM8961_LOUT1VOL_MASK                    0x007F  /* LOUT1VOL - [6:0] */
+#define WM8961_LOUT1VOL_SHIFT                        0  /* LOUT1VOL - [6:0] */
+#define WM8961_LOUT1VOL_WIDTH                        7  /* LOUT1VOL - [6:0] */
+
+/*
+ * R3 (0x03) - ROUT1 volume
+ */
+#define WM8961_OUT1VU                           0x0100  /* OUT1VU */
+#define WM8961_OUT1VU_MASK                      0x0100  /* OUT1VU */
+#define WM8961_OUT1VU_SHIFT                          8  /* OUT1VU */
+#define WM8961_OUT1VU_WIDTH                          1  /* OUT1VU */
+#define WM8961_RO1ZC                            0x0080  /* RO1ZC */
+#define WM8961_RO1ZC_MASK                       0x0080  /* RO1ZC */
+#define WM8961_RO1ZC_SHIFT                           7  /* RO1ZC */
+#define WM8961_RO1ZC_WIDTH                           1  /* RO1ZC */
+#define WM8961_ROUT1VOL_MASK                    0x007F  /* ROUT1VOL - [6:0] */
+#define WM8961_ROUT1VOL_SHIFT                        0  /* ROUT1VOL - [6:0] */
+#define WM8961_ROUT1VOL_WIDTH                        7  /* ROUT1VOL - [6:0] */
+
+/*
+ * R4 (0x04) - Clocking1
+ */
+#define WM8961_ADCDIV_MASK                      0x01C0  /* ADCDIV - [8:6] */
+#define WM8961_ADCDIV_SHIFT                          6  /* ADCDIV - [8:6] */
+#define WM8961_ADCDIV_WIDTH                          3  /* ADCDIV - [8:6] */
+#define WM8961_DACDIV_MASK                      0x0038  /* DACDIV - [5:3] */
+#define WM8961_DACDIV_SHIFT                          3  /* DACDIV - [5:3] */
+#define WM8961_DACDIV_WIDTH                          3  /* DACDIV - [5:3] */
+#define WM8961_MCLKDIV                          0x0004  /* MCLKDIV */
+#define WM8961_MCLKDIV_MASK                     0x0004  /* MCLKDIV */
+#define WM8961_MCLKDIV_SHIFT                         2  /* MCLKDIV */
+#define WM8961_MCLKDIV_WIDTH                         1  /* MCLKDIV */
+
+/*
+ * R5 (0x05) - ADC & DAC Control 1
+ */
+#define WM8961_ADCPOL_MASK                      0x0060  /* ADCPOL - [6:5] */
+#define WM8961_ADCPOL_SHIFT                          5  /* ADCPOL - [6:5] */
+#define WM8961_ADCPOL_WIDTH                          2  /* ADCPOL - [6:5] */
+#define WM8961_DACMU                            0x0008  /* DACMU */
+#define WM8961_DACMU_MASK                       0x0008  /* DACMU */
+#define WM8961_DACMU_SHIFT                           3  /* DACMU */
+#define WM8961_DACMU_WIDTH                           1  /* DACMU */
+#define WM8961_DEEMPH_MASK                      0x0006  /* DEEMPH - [2:1] */
+#define WM8961_DEEMPH_SHIFT                          1  /* DEEMPH - [2:1] */
+#define WM8961_DEEMPH_WIDTH                          2  /* DEEMPH - [2:1] */
+#define WM8961_ADCHPD                           0x0001  /* ADCHPD */
+#define WM8961_ADCHPD_MASK                      0x0001  /* ADCHPD */
+#define WM8961_ADCHPD_SHIFT                          0  /* ADCHPD */
+#define WM8961_ADCHPD_WIDTH                          1  /* ADCHPD */
+
+/*
+ * R6 (0x06) - ADC & DAC Control 2
+ */
+#define WM8961_ADC_HPF_CUT_MASK                 0x0180  /* ADC_HPF_CUT - [8:7] */
+#define WM8961_ADC_HPF_CUT_SHIFT                     7  /* ADC_HPF_CUT - [8:7] */
+#define WM8961_ADC_HPF_CUT_WIDTH                     2  /* ADC_HPF_CUT - [8:7] */
+#define WM8961_DACPOL_MASK                      0x0060  /* DACPOL - [6:5] */
+#define WM8961_DACPOL_SHIFT                          5  /* DACPOL - [6:5] */
+#define WM8961_DACPOL_WIDTH                          2  /* DACPOL - [6:5] */
+#define WM8961_DACSMM                           0x0008  /* DACSMM */
+#define WM8961_DACSMM_MASK                      0x0008  /* DACSMM */
+#define WM8961_DACSMM_SHIFT                          3  /* DACSMM */
+#define WM8961_DACSMM_WIDTH                          1  /* DACSMM */
+#define WM8961_DACMR                            0x0004  /* DACMR */
+#define WM8961_DACMR_MASK                       0x0004  /* DACMR */
+#define WM8961_DACMR_SHIFT                           2  /* DACMR */
+#define WM8961_DACMR_WIDTH                           1  /* DACMR */
+#define WM8961_DACSLOPE                         0x0002  /* DACSLOPE */
+#define WM8961_DACSLOPE_MASK                    0x0002  /* DACSLOPE */
+#define WM8961_DACSLOPE_SHIFT                        1  /* DACSLOPE */
+#define WM8961_DACSLOPE_WIDTH                        1  /* DACSLOPE */
+#define WM8961_DAC_OSR128                       0x0001  /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_MASK                  0x0001  /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_SHIFT                      0  /* DAC_OSR128 */
+#define WM8961_DAC_OSR128_WIDTH                      1  /* DAC_OSR128 */
+
+/*
+ * R7 (0x07) - Audio Interface 0
+ */
+#define WM8961_ALRSWAP                          0x0100  /* ALRSWAP */
+#define WM8961_ALRSWAP_MASK                     0x0100  /* ALRSWAP */
+#define WM8961_ALRSWAP_SHIFT                         8  /* ALRSWAP */
+#define WM8961_ALRSWAP_WIDTH                         1  /* ALRSWAP */
+#define WM8961_BCLKINV                          0x0080  /* BCLKINV */
+#define WM8961_BCLKINV_MASK                     0x0080  /* BCLKINV */
+#define WM8961_BCLKINV_SHIFT                         7  /* BCLKINV */
+#define WM8961_BCLKINV_WIDTH                         1  /* BCLKINV */
+#define WM8961_MS                               0x0040  /* MS */
+#define WM8961_MS_MASK                          0x0040  /* MS */
+#define WM8961_MS_SHIFT                              6  /* MS */
+#define WM8961_MS_WIDTH                              1  /* MS */
+#define WM8961_DLRSWAP                          0x0020  /* DLRSWAP */
+#define WM8961_DLRSWAP_MASK                     0x0020  /* DLRSWAP */
+#define WM8961_DLRSWAP_SHIFT                         5  /* DLRSWAP */
+#define WM8961_DLRSWAP_WIDTH                         1  /* DLRSWAP */
+#define WM8961_LRP                              0x0010  /* LRP */
+#define WM8961_LRP_MASK                         0x0010  /* LRP */
+#define WM8961_LRP_SHIFT                             4  /* LRP */
+#define WM8961_LRP_WIDTH                             1  /* LRP */
+#define WM8961_WL_MASK                          0x000C  /* WL - [3:2] */
+#define WM8961_WL_SHIFT                              2  /* WL - [3:2] */
+#define WM8961_WL_WIDTH                              2  /* WL - [3:2] */
+#define WM8961_FORMAT_MASK                      0x0003  /* FORMAT - [1:0] */
+#define WM8961_FORMAT_SHIFT                          0  /* FORMAT - [1:0] */
+#define WM8961_FORMAT_WIDTH                          2  /* FORMAT - [1:0] */
+
+/*
+ * R8 (0x08) - Clocking2
+ */
+#define WM8961_DCLKDIV_MASK                     0x01C0  /* DCLKDIV - [8:6] */
+#define WM8961_DCLKDIV_SHIFT                         6  /* DCLKDIV - [8:6] */
+#define WM8961_DCLKDIV_WIDTH                         3  /* DCLKDIV - [8:6] */
+#define WM8961_CLK_SYS_ENA                      0x0020  /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_MASK                 0x0020  /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_SHIFT                     5  /* CLK_SYS_ENA */
+#define WM8961_CLK_SYS_ENA_WIDTH                     1  /* CLK_SYS_ENA */
+#define WM8961_CLK_DSP_ENA                      0x0010  /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_MASK                 0x0010  /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_SHIFT                     4  /* CLK_DSP_ENA */
+#define WM8961_CLK_DSP_ENA_WIDTH                     1  /* CLK_DSP_ENA */
+#define WM8961_BCLKDIV_MASK                     0x000F  /* BCLKDIV - [3:0] */
+#define WM8961_BCLKDIV_SHIFT                         0  /* BCLKDIV - [3:0] */
+#define WM8961_BCLKDIV_WIDTH                         4  /* BCLKDIV - [3:0] */
+
+/*
+ * R9 (0x09) - Audio Interface 1
+ */
+#define WM8961_DACCOMP_MASK                     0x0018  /* DACCOMP - [4:3] */
+#define WM8961_DACCOMP_SHIFT                         3  /* DACCOMP - [4:3] */
+#define WM8961_DACCOMP_WIDTH                         2  /* DACCOMP - [4:3] */
+#define WM8961_ADCCOMP_MASK                     0x0006  /* ADCCOMP - [2:1] */
+#define WM8961_ADCCOMP_SHIFT                         1  /* ADCCOMP - [2:1] */
+#define WM8961_ADCCOMP_WIDTH                         2  /* ADCCOMP - [2:1] */
+#define WM8961_LOOPBACK                         0x0001  /* LOOPBACK */
+#define WM8961_LOOPBACK_MASK                    0x0001  /* LOOPBACK */
+#define WM8961_LOOPBACK_SHIFT                        0  /* LOOPBACK */
+#define WM8961_LOOPBACK_WIDTH                        1  /* LOOPBACK */
+
+/*
+ * R10 (0x0A) - Left DAC volume
+ */
+#define WM8961_DACVU                            0x0100  /* DACVU */
+#define WM8961_DACVU_MASK                       0x0100  /* DACVU */
+#define WM8961_DACVU_SHIFT                           8  /* DACVU */
+#define WM8961_DACVU_WIDTH                           1  /* DACVU */
+#define WM8961_LDACVOL_MASK                     0x00FF  /* LDACVOL - [7:0] */
+#define WM8961_LDACVOL_SHIFT                         0  /* LDACVOL - [7:0] */
+#define WM8961_LDACVOL_WIDTH                         8  /* LDACVOL - [7:0] */
+
+/*
+ * R11 (0x0B) - Right DAC volume
+ */
+#define WM8961_DACVU                            0x0100  /* DACVU */
+#define WM8961_DACVU_MASK                       0x0100  /* DACVU */
+#define WM8961_DACVU_SHIFT                           8  /* DACVU */
+#define WM8961_DACVU_WIDTH                           1  /* DACVU */
+#define WM8961_RDACVOL_MASK                     0x00FF  /* RDACVOL - [7:0] */
+#define WM8961_RDACVOL_SHIFT                         0  /* RDACVOL - [7:0] */
+#define WM8961_RDACVOL_WIDTH                         8  /* RDACVOL - [7:0] */
+
+/*
+ * R14 (0x0E) - Audio Interface 2
+ */
+#define WM8961_LRCLK_RATE_MASK                  0x01FF  /* LRCLK_RATE - [8:0] */
+#define WM8961_LRCLK_RATE_SHIFT                      0  /* LRCLK_RATE - [8:0] */
+#define WM8961_LRCLK_RATE_WIDTH                      9  /* LRCLK_RATE - [8:0] */
+
+/*
+ * R15 (0x0F) - Software Reset
+ */
+#define WM8961_SW_RST_DEV_ID1_MASK              0xFFFF  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8961_SW_RST_DEV_ID1_SHIFT                  0  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM8961_SW_RST_DEV_ID1_WIDTH                 16  /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R17 (0x11) - ALC1
+ */
+#define WM8961_ALCSEL_MASK                      0x0180  /* ALCSEL - [8:7] */
+#define WM8961_ALCSEL_SHIFT                          7  /* ALCSEL - [8:7] */
+#define WM8961_ALCSEL_WIDTH                          2  /* ALCSEL - [8:7] */
+#define WM8961_MAXGAIN_MASK                     0x0070  /* MAXGAIN - [6:4] */
+#define WM8961_MAXGAIN_SHIFT                         4  /* MAXGAIN - [6:4] */
+#define WM8961_MAXGAIN_WIDTH                         3  /* MAXGAIN - [6:4] */
+#define WM8961_ALCL_MASK                        0x000F  /* ALCL - [3:0] */
+#define WM8961_ALCL_SHIFT                            0  /* ALCL - [3:0] */
+#define WM8961_ALCL_WIDTH                            4  /* ALCL - [3:0] */
+
+/*
+ * R18 (0x12) - ALC2
+ */
+#define WM8961_ALCZC                            0x0080  /* ALCZC */
+#define WM8961_ALCZC_MASK                       0x0080  /* ALCZC */
+#define WM8961_ALCZC_SHIFT                           7  /* ALCZC */
+#define WM8961_ALCZC_WIDTH                           1  /* ALCZC */
+#define WM8961_MINGAIN_MASK                     0x0070  /* MINGAIN - [6:4] */
+#define WM8961_MINGAIN_SHIFT                         4  /* MINGAIN - [6:4] */
+#define WM8961_MINGAIN_WIDTH                         3  /* MINGAIN - [6:4] */
+#define WM8961_HLD_MASK                         0x000F  /* HLD - [3:0] */
+#define WM8961_HLD_SHIFT                             0  /* HLD - [3:0] */
+#define WM8961_HLD_WIDTH                             4  /* HLD - [3:0] */
+
+/*
+ * R19 (0x13) - ALC3
+ */
+#define WM8961_ALCMODE                          0x0100  /* ALCMODE */
+#define WM8961_ALCMODE_MASK                     0x0100  /* ALCMODE */
+#define WM8961_ALCMODE_SHIFT                         8  /* ALCMODE */
+#define WM8961_ALCMODE_WIDTH                         1  /* ALCMODE */
+#define WM8961_DCY_MASK                         0x00F0  /* DCY - [7:4] */
+#define WM8961_DCY_SHIFT                             4  /* DCY - [7:4] */
+#define WM8961_DCY_WIDTH                             4  /* DCY - [7:4] */
+#define WM8961_ATK_MASK                         0x000F  /* ATK - [3:0] */
+#define WM8961_ATK_SHIFT                             0  /* ATK - [3:0] */
+#define WM8961_ATK_WIDTH                             4  /* ATK - [3:0] */
+
+/*
+ * R20 (0x14) - Noise Gate
+ */
+#define WM8961_NGTH_MASK                        0x00F8  /* NGTH - [7:3] */
+#define WM8961_NGTH_SHIFT                            3  /* NGTH - [7:3] */
+#define WM8961_NGTH_WIDTH                            5  /* NGTH - [7:3] */
+#define WM8961_NGG                              0x0002  /* NGG */
+#define WM8961_NGG_MASK                         0x0002  /* NGG */
+#define WM8961_NGG_SHIFT                             1  /* NGG */
+#define WM8961_NGG_WIDTH                             1  /* NGG */
+#define WM8961_NGAT                             0x0001  /* NGAT */
+#define WM8961_NGAT_MASK                        0x0001  /* NGAT */
+#define WM8961_NGAT_SHIFT                            0  /* NGAT */
+#define WM8961_NGAT_WIDTH                            1  /* NGAT */
+
+/*
+ * R21 (0x15) - Left ADC volume
+ */
+#define WM8961_ADCVU                            0x0100  /* ADCVU */
+#define WM8961_ADCVU_MASK                       0x0100  /* ADCVU */
+#define WM8961_ADCVU_SHIFT                           8  /* ADCVU */
+#define WM8961_ADCVU_WIDTH                           1  /* ADCVU */
+#define WM8961_LADCVOL_MASK                     0x00FF  /* LADCVOL - [7:0] */
+#define WM8961_LADCVOL_SHIFT                         0  /* LADCVOL - [7:0] */
+#define WM8961_LADCVOL_WIDTH                         8  /* LADCVOL - [7:0] */
+
+/*
+ * R22 (0x16) - Right ADC volume
+ */
+#define WM8961_ADCVU                            0x0100  /* ADCVU */
+#define WM8961_ADCVU_MASK                       0x0100  /* ADCVU */
+#define WM8961_ADCVU_SHIFT                           8  /* ADCVU */
+#define WM8961_ADCVU_WIDTH                           1  /* ADCVU */
+#define WM8961_RADCVOL_MASK                     0x00FF  /* RADCVOL - [7:0] */
+#define WM8961_RADCVOL_SHIFT                         0  /* RADCVOL - [7:0] */
+#define WM8961_RADCVOL_WIDTH                         8  /* RADCVOL - [7:0] */
+
+/*
+ * R23 (0x17) - Additional control(1)
+ */
+#define WM8961_TSDEN                            0x0100  /* TSDEN */
+#define WM8961_TSDEN_MASK                       0x0100  /* TSDEN */
+#define WM8961_TSDEN_SHIFT                           8  /* TSDEN */
+#define WM8961_TSDEN_WIDTH                           1  /* TSDEN */
+#define WM8961_DMONOMIX                         0x0010  /* DMONOMIX */
+#define WM8961_DMONOMIX_MASK                    0x0010  /* DMONOMIX */
+#define WM8961_DMONOMIX_SHIFT                        4  /* DMONOMIX */
+#define WM8961_DMONOMIX_WIDTH                        1  /* DMONOMIX */
+#define WM8961_TOEN                             0x0001  /* TOEN */
+#define WM8961_TOEN_MASK                        0x0001  /* TOEN */
+#define WM8961_TOEN_SHIFT                            0  /* TOEN */
+#define WM8961_TOEN_WIDTH                            1  /* TOEN */
+
+/*
+ * R24 (0x18) - Additional control(2)
+ */
+#define WM8961_TRIS                             0x0008  /* TRIS */
+#define WM8961_TRIS_MASK                        0x0008  /* TRIS */
+#define WM8961_TRIS_SHIFT                            3  /* TRIS */
+#define WM8961_TRIS_WIDTH                            1  /* TRIS */
+
+/*
+ * R25 (0x19) - Pwr Mgmt (1)
+ */
+#define WM8961_VMIDSEL_MASK                     0x0180  /* VMIDSEL - [8:7] */
+#define WM8961_VMIDSEL_SHIFT                         7  /* VMIDSEL - [8:7] */
+#define WM8961_VMIDSEL_WIDTH                         2  /* VMIDSEL - [8:7] */
+#define WM8961_VREF                             0x0040  /* VREF */
+#define WM8961_VREF_MASK                        0x0040  /* VREF */
+#define WM8961_VREF_SHIFT                            6  /* VREF */
+#define WM8961_VREF_WIDTH                            1  /* VREF */
+#define WM8961_AINL                             0x0020  /* AINL */
+#define WM8961_AINL_MASK                        0x0020  /* AINL */
+#define WM8961_AINL_SHIFT                            5  /* AINL */
+#define WM8961_AINL_WIDTH                            1  /* AINL */
+#define WM8961_AINR                             0x0010  /* AINR */
+#define WM8961_AINR_MASK                        0x0010  /* AINR */
+#define WM8961_AINR_SHIFT                            4  /* AINR */
+#define WM8961_AINR_WIDTH                            1  /* AINR */
+#define WM8961_ADCL                             0x0008  /* ADCL */
+#define WM8961_ADCL_MASK                        0x0008  /* ADCL */
+#define WM8961_ADCL_SHIFT                            3  /* ADCL */
+#define WM8961_ADCL_WIDTH                            1  /* ADCL */
+#define WM8961_ADCR                             0x0004  /* ADCR */
+#define WM8961_ADCR_MASK                        0x0004  /* ADCR */
+#define WM8961_ADCR_SHIFT                            2  /* ADCR */
+#define WM8961_ADCR_WIDTH                            1  /* ADCR */
+#define WM8961_MICB                             0x0002  /* MICB */
+#define WM8961_MICB_MASK                        0x0002  /* MICB */
+#define WM8961_MICB_SHIFT                            1  /* MICB */
+#define WM8961_MICB_WIDTH                            1  /* MICB */
+
+/*
+ * R26 (0x1A) - Pwr Mgmt (2)
+ */
+#define WM8961_DACL                             0x0100  /* DACL */
+#define WM8961_DACL_MASK                        0x0100  /* DACL */
+#define WM8961_DACL_SHIFT                            8  /* DACL */
+#define WM8961_DACL_WIDTH                            1  /* DACL */
+#define WM8961_DACR                             0x0080  /* DACR */
+#define WM8961_DACR_MASK                        0x0080  /* DACR */
+#define WM8961_DACR_SHIFT                            7  /* DACR */
+#define WM8961_DACR_WIDTH                            1  /* DACR */
+#define WM8961_LOUT1_PGA                        0x0040  /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_MASK                   0x0040  /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_SHIFT                       6  /* LOUT1_PGA */
+#define WM8961_LOUT1_PGA_WIDTH                       1  /* LOUT1_PGA */
+#define WM8961_ROUT1_PGA                        0x0020  /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_MASK                   0x0020  /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_SHIFT                       5  /* ROUT1_PGA */
+#define WM8961_ROUT1_PGA_WIDTH                       1  /* ROUT1_PGA */
+#define WM8961_SPKL_PGA                         0x0010  /* SPKL_PGA */
+#define WM8961_SPKL_PGA_MASK                    0x0010  /* SPKL_PGA */
+#define WM8961_SPKL_PGA_SHIFT                        4  /* SPKL_PGA */
+#define WM8961_SPKL_PGA_WIDTH                        1  /* SPKL_PGA */
+#define WM8961_SPKR_PGA                         0x0008  /* SPKR_PGA */
+#define WM8961_SPKR_PGA_MASK                    0x0008  /* SPKR_PGA */
+#define WM8961_SPKR_PGA_SHIFT                        3  /* SPKR_PGA */
+#define WM8961_SPKR_PGA_WIDTH                        1  /* SPKR_PGA */
+
+/*
+ * R27 (0x1B) - Additional Control (3)
+ */
+#define WM8961_SAMPLE_RATE_MASK                 0x0007  /* SAMPLE_RATE - [2:0] */
+#define WM8961_SAMPLE_RATE_SHIFT                     0  /* SAMPLE_RATE - [2:0] */
+#define WM8961_SAMPLE_RATE_WIDTH                     3  /* SAMPLE_RATE - [2:0] */
+
+/*
+ * R28 (0x1C) - Anti-pop
+ */
+#define WM8961_BUFDCOPEN                        0x0010  /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_MASK                   0x0010  /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_SHIFT                       4  /* BUFDCOPEN */
+#define WM8961_BUFDCOPEN_WIDTH                       1  /* BUFDCOPEN */
+#define WM8961_BUFIOEN                          0x0008  /* BUFIOEN */
+#define WM8961_BUFIOEN_MASK                     0x0008  /* BUFIOEN */
+#define WM8961_BUFIOEN_SHIFT                         3  /* BUFIOEN */
+#define WM8961_BUFIOEN_WIDTH                         1  /* BUFIOEN */
+#define WM8961_SOFT_ST                          0x0004  /* SOFT_ST */
+#define WM8961_SOFT_ST_MASK                     0x0004  /* SOFT_ST */
+#define WM8961_SOFT_ST_SHIFT                         2  /* SOFT_ST */
+#define WM8961_SOFT_ST_WIDTH                         1  /* SOFT_ST */
+
+/*
+ * R30 (0x1E) - Clocking 3
+ */
+#define WM8961_CLK_TO_DIV_MASK                  0x0180  /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_TO_DIV_SHIFT                      7  /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_TO_DIV_WIDTH                      2  /* CLK_TO_DIV - [8:7] */
+#define WM8961_CLK_256K_DIV_MASK                0x007E  /* CLK_256K_DIV - [6:1] */
+#define WM8961_CLK_256K_DIV_SHIFT                    1  /* CLK_256K_DIV - [6:1] */
+#define WM8961_CLK_256K_DIV_WIDTH                    6  /* CLK_256K_DIV - [6:1] */
+#define WM8961_MANUAL_MODE                      0x0001  /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_MASK                 0x0001  /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_SHIFT                     0  /* MANUAL_MODE */
+#define WM8961_MANUAL_MODE_WIDTH                     1  /* MANUAL_MODE */
+
+/*
+ * R32 (0x20) - ADCL signal path
+ */
+#define WM8961_LMICBOOST_MASK                   0x0030  /* LMICBOOST - [5:4] */
+#define WM8961_LMICBOOST_SHIFT                       4  /* LMICBOOST - [5:4] */
+#define WM8961_LMICBOOST_WIDTH                       2  /* LMICBOOST - [5:4] */
+
+/*
+ * R33 (0x21) - ADCR signal path
+ */
+#define WM8961_RMICBOOST_MASK                   0x0030  /* RMICBOOST - [5:4] */
+#define WM8961_RMICBOOST_SHIFT                       4  /* RMICBOOST - [5:4] */
+#define WM8961_RMICBOOST_WIDTH                       2  /* RMICBOOST - [5:4] */
+
+/*
+ * R40 (0x28) - LOUT2 volume
+ */
+#define WM8961_SPKVU                            0x0100  /* SPKVU */
+#define WM8961_SPKVU_MASK                       0x0100  /* SPKVU */
+#define WM8961_SPKVU_SHIFT                           8  /* SPKVU */
+#define WM8961_SPKVU_WIDTH                           1  /* SPKVU */
+#define WM8961_SPKLZC                           0x0080  /* SPKLZC */
+#define WM8961_SPKLZC_MASK                      0x0080  /* SPKLZC */
+#define WM8961_SPKLZC_SHIFT                          7  /* SPKLZC */
+#define WM8961_SPKLZC_WIDTH                          1  /* SPKLZC */
+#define WM8961_SPKLVOL_MASK                     0x007F  /* SPKLVOL - [6:0] */
+#define WM8961_SPKLVOL_SHIFT                         0  /* SPKLVOL - [6:0] */
+#define WM8961_SPKLVOL_WIDTH                         7  /* SPKLVOL - [6:0] */
+
+/*
+ * R41 (0x29) - ROUT2 volume
+ */
+#define WM8961_SPKVU                            0x0100  /* SPKVU */
+#define WM8961_SPKVU_MASK                       0x0100  /* SPKVU */
+#define WM8961_SPKVU_SHIFT                           8  /* SPKVU */
+#define WM8961_SPKVU_WIDTH                           1  /* SPKVU */
+#define WM8961_SPKRZC                           0x0080  /* SPKRZC */
+#define WM8961_SPKRZC_MASK                      0x0080  /* SPKRZC */
+#define WM8961_SPKRZC_SHIFT                          7  /* SPKRZC */
+#define WM8961_SPKRZC_WIDTH                          1  /* SPKRZC */
+#define WM8961_SPKRVOL_MASK                     0x007F  /* SPKRVOL - [6:0] */
+#define WM8961_SPKRVOL_SHIFT                         0  /* SPKRVOL - [6:0] */
+#define WM8961_SPKRVOL_WIDTH                         7  /* SPKRVOL - [6:0] */
+
+/*
+ * R47 (0x2F) - Pwr Mgmt (3)
+ */
+#define WM8961_TEMP_SHUT                        0x0002  /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_MASK                   0x0002  /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_SHIFT                       1  /* TEMP_SHUT */
+#define WM8961_TEMP_SHUT_WIDTH                       1  /* TEMP_SHUT */
+#define WM8961_TEMP_WARN                        0x0001  /* TEMP_WARN */
+#define WM8961_TEMP_WARN_MASK                   0x0001  /* TEMP_WARN */
+#define WM8961_TEMP_WARN_SHIFT                       0  /* TEMP_WARN */
+#define WM8961_TEMP_WARN_WIDTH                       1  /* TEMP_WARN */
+
+/*
+ * R48 (0x30) - Additional Control (4)
+ */
+#define WM8961_TSENSEN                          0x0002  /* TSENSEN */
+#define WM8961_TSENSEN_MASK                     0x0002  /* TSENSEN */
+#define WM8961_TSENSEN_SHIFT                         1  /* TSENSEN */
+#define WM8961_TSENSEN_WIDTH                         1  /* TSENSEN */
+#define WM8961_MBSEL                            0x0001  /* MBSEL */
+#define WM8961_MBSEL_MASK                       0x0001  /* MBSEL */
+#define WM8961_MBSEL_SHIFT                           0  /* MBSEL */
+#define WM8961_MBSEL_WIDTH                           1  /* MBSEL */
+
+/*
+ * R49 (0x31) - Class D Control 1
+ */
+#define WM8961_SPKR_ENA                         0x0080  /* SPKR_ENA */
+#define WM8961_SPKR_ENA_MASK                    0x0080  /* SPKR_ENA */
+#define WM8961_SPKR_ENA_SHIFT                        7  /* SPKR_ENA */
+#define WM8961_SPKR_ENA_WIDTH                        1  /* SPKR_ENA */
+#define WM8961_SPKL_ENA                         0x0040  /* SPKL_ENA */
+#define WM8961_SPKL_ENA_MASK                    0x0040  /* SPKL_ENA */
+#define WM8961_SPKL_ENA_SHIFT                        6  /* SPKL_ENA */
+#define WM8961_SPKL_ENA_WIDTH                        1  /* SPKL_ENA */
+
+/*
+ * R51 (0x33) - Class D Control 2
+ */
+#define WM8961_CLASSD_ACGAIN_MASK               0x0007  /* CLASSD_ACGAIN - [2:0] */
+#define WM8961_CLASSD_ACGAIN_SHIFT                   0  /* CLASSD_ACGAIN - [2:0] */
+#define WM8961_CLASSD_ACGAIN_WIDTH                   3  /* CLASSD_ACGAIN - [2:0] */
+
+/*
+ * R56 (0x38) - Clocking 4
+ */
+#define WM8961_CLK_DCS_DIV_MASK                 0x01E0  /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_DCS_DIV_SHIFT                     5  /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_DCS_DIV_WIDTH                     4  /* CLK_DCS_DIV - [8:5] */
+#define WM8961_CLK_SYS_RATE_MASK                0x001E  /* CLK_SYS_RATE - [4:1] */
+#define WM8961_CLK_SYS_RATE_SHIFT                    1  /* CLK_SYS_RATE - [4:1] */
+#define WM8961_CLK_SYS_RATE_WIDTH                    4  /* CLK_SYS_RATE - [4:1] */
+
+/*
+ * R57 (0x39) - DSP Sidetone 0
+ */
+#define WM8961_ADCR_DAC_SVOL_MASK               0x00F0  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADCR_DAC_SVOL_SHIFT                   4  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADCR_DAC_SVOL_WIDTH                   4  /* ADCR_DAC_SVOL - [7:4] */
+#define WM8961_ADC_TO_DACR_MASK                 0x000C  /* ADC_TO_DACR - [3:2] */
+#define WM8961_ADC_TO_DACR_SHIFT                     2  /* ADC_TO_DACR - [3:2] */
+#define WM8961_ADC_TO_DACR_WIDTH                     2  /* ADC_TO_DACR - [3:2] */
+
+/*
+ * R58 (0x3A) - DSP Sidetone 1
+ */
+#define WM8961_ADCL_DAC_SVOL_MASK               0x00F0  /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADCL_DAC_SVOL_SHIFT                   4  /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADCL_DAC_SVOL_WIDTH                   4  /* ADCL_DAC_SVOL - [7:4] */
+#define WM8961_ADC_TO_DACL_MASK                 0x000C  /* ADC_TO_DACL - [3:2] */
+#define WM8961_ADC_TO_DACL_SHIFT                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8961_ADC_TO_DACL_WIDTH                     2  /* ADC_TO_DACL - [3:2] */
+
+/*
+ * R60 (0x3C) - DC Servo 0
+ */
+#define WM8961_DCS_ENA_CHAN_INL                 0x0080  /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_MASK            0x0080  /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_SHIFT                7  /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_ENA_CHAN_INL_WIDTH                1  /* DCS_ENA_CHAN_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL             0x0040  /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_MASK        0x0040  /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_SHIFT            6  /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_STARTUP_INL_WIDTH            1  /* DCS_TRIG_STARTUP_INL */
+#define WM8961_DCS_TRIG_SERIES_INL              0x0010  /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_MASK         0x0010  /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_SHIFT             4  /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_TRIG_SERIES_INL_WIDTH             1  /* DCS_TRIG_SERIES_INL */
+#define WM8961_DCS_ENA_CHAN_INR                 0x0008  /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_MASK            0x0008  /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_SHIFT                3  /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_ENA_CHAN_INR_WIDTH                1  /* DCS_ENA_CHAN_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR             0x0004  /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_MASK        0x0004  /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_SHIFT            2  /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_STARTUP_INR_WIDTH            1  /* DCS_TRIG_STARTUP_INR */
+#define WM8961_DCS_TRIG_SERIES_INR              0x0001  /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_MASK         0x0001  /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_SHIFT             0  /* DCS_TRIG_SERIES_INR */
+#define WM8961_DCS_TRIG_SERIES_INR_WIDTH             1  /* DCS_TRIG_SERIES_INR */
+
+/*
+ * R61 (0x3D) - DC Servo 1
+ */
+#define WM8961_DCS_ENA_CHAN_HPL                 0x0080  /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_MASK            0x0080  /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_SHIFT                7  /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_ENA_CHAN_HPL_WIDTH                1  /* DCS_ENA_CHAN_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL             0x0040  /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_MASK        0x0040  /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_SHIFT            6  /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_STARTUP_HPL_WIDTH            1  /* DCS_TRIG_STARTUP_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL              0x0010  /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_MASK         0x0010  /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_SHIFT             4  /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_TRIG_SERIES_HPL_WIDTH             1  /* DCS_TRIG_SERIES_HPL */
+#define WM8961_DCS_ENA_CHAN_HPR                 0x0008  /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_MASK            0x0008  /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_SHIFT                3  /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_ENA_CHAN_HPR_WIDTH                1  /* DCS_ENA_CHAN_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR             0x0004  /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_MASK        0x0004  /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_SHIFT            2  /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_STARTUP_HPR_WIDTH            1  /* DCS_TRIG_STARTUP_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR              0x0001  /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_MASK         0x0001  /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_SHIFT             0  /* DCS_TRIG_SERIES_HPR */
+#define WM8961_DCS_TRIG_SERIES_HPR_WIDTH             1  /* DCS_TRIG_SERIES_HPR */
+
+/*
+ * R63 (0x3F) - DC Servo 3
+ */
+#define WM8961_DCS_FILT_BW_SERIES_MASK          0x0030  /* DCS_FILT_BW_SERIES - [5:4] */
+#define WM8961_DCS_FILT_BW_SERIES_SHIFT              4  /* DCS_FILT_BW_SERIES - [5:4] */
+#define WM8961_DCS_FILT_BW_SERIES_WIDTH              2  /* DCS_FILT_BW_SERIES - [5:4] */
+
+/*
+ * R65 (0x41) - DC Servo 5
+ */
+#define WM8961_DCS_SERIES_NO_HP_MASK            0x007F  /* DCS_SERIES_NO_HP - [6:0] */
+#define WM8961_DCS_SERIES_NO_HP_SHIFT                0  /* DCS_SERIES_NO_HP - [6:0] */
+#define WM8961_DCS_SERIES_NO_HP_WIDTH                7  /* DCS_SERIES_NO_HP - [6:0] */
+
+/*
+ * R68 (0x44) - Analogue PGA Bias
+ */
+#define WM8961_HP_PGAS_BIAS_MASK                0x0007  /* HP_PGAS_BIAS - [2:0] */
+#define WM8961_HP_PGAS_BIAS_SHIFT                    0  /* HP_PGAS_BIAS - [2:0] */
+#define WM8961_HP_PGAS_BIAS_WIDTH                    3  /* HP_PGAS_BIAS - [2:0] */
+
+/*
+ * R69 (0x45) - Analogue HP 0
+ */
+#define WM8961_HPL_RMV_SHORT                    0x0080  /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_MASK               0x0080  /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_SHIFT                   7  /* HPL_RMV_SHORT */
+#define WM8961_HPL_RMV_SHORT_WIDTH                   1  /* HPL_RMV_SHORT */
+#define WM8961_HPL_ENA_OUTP                     0x0040  /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_MASK                0x0040  /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_SHIFT                    6  /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_OUTP_WIDTH                    1  /* HPL_ENA_OUTP */
+#define WM8961_HPL_ENA_DLY                      0x0020  /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_MASK                 0x0020  /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_SHIFT                     5  /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA_DLY_WIDTH                     1  /* HPL_ENA_DLY */
+#define WM8961_HPL_ENA                          0x0010  /* HPL_ENA */
+#define WM8961_HPL_ENA_MASK                     0x0010  /* HPL_ENA */
+#define WM8961_HPL_ENA_SHIFT                         4  /* HPL_ENA */
+#define WM8961_HPL_ENA_WIDTH                         1  /* HPL_ENA */
+#define WM8961_HPR_RMV_SHORT                    0x0008  /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_MASK               0x0008  /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_SHIFT                   3  /* HPR_RMV_SHORT */
+#define WM8961_HPR_RMV_SHORT_WIDTH                   1  /* HPR_RMV_SHORT */
+#define WM8961_HPR_ENA_OUTP                     0x0004  /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_MASK                0x0004  /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_SHIFT                    2  /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_OUTP_WIDTH                    1  /* HPR_ENA_OUTP */
+#define WM8961_HPR_ENA_DLY                      0x0002  /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_MASK                 0x0002  /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_SHIFT                     1  /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA_DLY_WIDTH                     1  /* HPR_ENA_DLY */
+#define WM8961_HPR_ENA                          0x0001  /* HPR_ENA */
+#define WM8961_HPR_ENA_MASK                     0x0001  /* HPR_ENA */
+#define WM8961_HPR_ENA_SHIFT                         0  /* HPR_ENA */
+#define WM8961_HPR_ENA_WIDTH                         1  /* HPR_ENA */
+
+/*
+ * R71 (0x47) - Analogue HP 2
+ */
+#define WM8961_HPL_VOL_MASK                     0x01C0  /* HPL_VOL - [8:6] */
+#define WM8961_HPL_VOL_SHIFT                         6  /* HPL_VOL - [8:6] */
+#define WM8961_HPL_VOL_WIDTH                         3  /* HPL_VOL - [8:6] */
+#define WM8961_HPR_VOL_MASK                     0x0038  /* HPR_VOL - [5:3] */
+#define WM8961_HPR_VOL_SHIFT                         3  /* HPR_VOL - [5:3] */
+#define WM8961_HPR_VOL_WIDTH                         3  /* HPR_VOL - [5:3] */
+#define WM8961_HP_BIAS_BOOST_MASK               0x0007  /* HP_BIAS_BOOST - [2:0] */
+#define WM8961_HP_BIAS_BOOST_SHIFT                   0  /* HP_BIAS_BOOST - [2:0] */
+#define WM8961_HP_BIAS_BOOST_WIDTH                   3  /* HP_BIAS_BOOST - [2:0] */
+
+/*
+ * R72 (0x48) - Charge Pump 1
+ */
+#define WM8961_CP_ENA                           0x0001  /* CP_ENA */
+#define WM8961_CP_ENA_MASK                      0x0001  /* CP_ENA */
+#define WM8961_CP_ENA_SHIFT                          0  /* CP_ENA */
+#define WM8961_CP_ENA_WIDTH                          1  /* CP_ENA */
+
+/*
+ * R82 (0x52) - Charge Pump B
+ */
+#define WM8961_CP_DYN_PWR_MASK                  0x0003  /* CP_DYN_PWR - [1:0] */
+#define WM8961_CP_DYN_PWR_SHIFT                      0  /* CP_DYN_PWR - [1:0] */
+#define WM8961_CP_DYN_PWR_WIDTH                      2  /* CP_DYN_PWR - [1:0] */
+
+/*
+ * R87 (0x57) - Write Sequencer 1
+ */
+#define WM8961_WSEQ_ENA                         0x0020  /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_MASK                    0x0020  /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_SHIFT                        5  /* WSEQ_ENA */
+#define WM8961_WSEQ_ENA_WIDTH                        1  /* WSEQ_ENA */
+#define WM8961_WSEQ_WRITE_INDEX_MASK            0x001F  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8961_WSEQ_WRITE_INDEX_SHIFT                0  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8961_WSEQ_WRITE_INDEX_WIDTH                5  /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R88 (0x58) - Write Sequencer 2
+ */
+#define WM8961_WSEQ_EOS                         0x0100  /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_MASK                    0x0100  /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_SHIFT                        8  /* WSEQ_EOS */
+#define WM8961_WSEQ_EOS_WIDTH                        1  /* WSEQ_EOS */
+#define WM8961_WSEQ_ADDR_MASK                   0x00FF  /* WSEQ_ADDR - [7:0] */
+#define WM8961_WSEQ_ADDR_SHIFT                       0  /* WSEQ_ADDR - [7:0] */
+#define WM8961_WSEQ_ADDR_WIDTH                       8  /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R89 (0x59) - Write Sequencer 3
+ */
+#define WM8961_WSEQ_DATA_MASK                   0x00FF  /* WSEQ_DATA - [7:0] */
+#define WM8961_WSEQ_DATA_SHIFT                       0  /* WSEQ_DATA - [7:0] */
+#define WM8961_WSEQ_DATA_WIDTH                       8  /* WSEQ_DATA - [7:0] */
+
+/*
+ * R90 (0x5A) - Write Sequencer 4
+ */
+#define WM8961_WSEQ_ABORT                       0x0100  /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_MASK                  0x0100  /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_SHIFT                      8  /* WSEQ_ABORT */
+#define WM8961_WSEQ_ABORT_WIDTH                      1  /* WSEQ_ABORT */
+#define WM8961_WSEQ_START                       0x0080  /* WSEQ_START */
+#define WM8961_WSEQ_START_MASK                  0x0080  /* WSEQ_START */
+#define WM8961_WSEQ_START_SHIFT                      7  /* WSEQ_START */
+#define WM8961_WSEQ_START_WIDTH                      1  /* WSEQ_START */
+#define WM8961_WSEQ_START_INDEX_MASK            0x003F  /* WSEQ_START_INDEX - [5:0] */
+#define WM8961_WSEQ_START_INDEX_SHIFT                0  /* WSEQ_START_INDEX - [5:0] */
+#define WM8961_WSEQ_START_INDEX_WIDTH                6  /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R91 (0x5B) - Write Sequencer 5
+ */
+#define WM8961_WSEQ_DATA_WIDTH_MASK             0x0070  /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_WIDTH_SHIFT                 4  /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_WIDTH_WIDTH                 3  /* WSEQ_DATA_WIDTH - [6:4] */
+#define WM8961_WSEQ_DATA_START_MASK             0x000F  /* WSEQ_DATA_START - [3:0] */
+#define WM8961_WSEQ_DATA_START_SHIFT                 0  /* WSEQ_DATA_START - [3:0] */
+#define WM8961_WSEQ_DATA_START_WIDTH                 4  /* WSEQ_DATA_START - [3:0] */
+
+/*
+ * R92 (0x5C) - Write Sequencer 6
+ */
+#define WM8961_WSEQ_DELAY_MASK                  0x000F  /* WSEQ_DELAY - [3:0] */
+#define WM8961_WSEQ_DELAY_SHIFT                      0  /* WSEQ_DELAY - [3:0] */
+#define WM8961_WSEQ_DELAY_WIDTH                      4  /* WSEQ_DELAY - [3:0] */
+
+/*
+ * R93 (0x5D) - Write Sequencer 7
+ */
+#define WM8961_WSEQ_BUSY                        0x0001  /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_MASK                   0x0001  /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_SHIFT                       0  /* WSEQ_BUSY */
+#define WM8961_WSEQ_BUSY_WIDTH                       1  /* WSEQ_BUSY */
+
+/*
+ * R252 (0xFC) - General test 1
+ */
+#define WM8961_ARA_ENA                          0x0002  /* ARA_ENA */
+#define WM8961_ARA_ENA_MASK                     0x0002  /* ARA_ENA */
+#define WM8961_ARA_ENA_SHIFT                         1  /* ARA_ENA */
+#define WM8961_ARA_ENA_WIDTH                         1  /* ARA_ENA */
+#define WM8961_AUTO_INC                         0x0001  /* AUTO_INC */
+#define WM8961_AUTO_INC_MASK                    0x0001  /* AUTO_INC */
+#define WM8961_AUTO_INC_SHIFT                        0  /* AUTO_INC */
+#define WM8961_AUTO_INC_WIDTH                        1  /* AUTO_INC */
+
+#endif
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index 032dca2..d66efb0 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -59,44 +59,7 @@
 	0x0079, 0x0079, 0x0079,          /* 40 */
 };
 
-static inline unsigned int wm8971_read_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg)
-{
-	u16 *cache = codec->reg_cache;
-	if (reg < WM8971_REG_COUNT)
-		return cache[reg];
-
-	return -1;
-}
-
-static inline void wm8971_write_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg, unsigned int value)
-{
-	u16 *cache = codec->reg_cache;
-	if (reg < WM8971_REG_COUNT)
-		cache[reg] = value;
-}
-
-static int wm8971_write(struct snd_soc_codec *codec, unsigned int reg,
-	unsigned int value)
-{
-	u8 data[2];
-
-	/* data is
-	 *   D15..D9 WM8753 register offset
-	 *   D8...D0 register data
-	 */
-	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
-	data[1] = value & 0x00ff;
-
-	wm8971_write_reg_cache (codec, reg, value);
-	if (codec->hw_write(codec->control_data, data, 2) == 2)
-		return 0;
-	else
-		return -EIO;
-}
-
-#define wm8971_reset(c)	wm8971_write(c, WM8971_RESET, 0)
+#define wm8971_reset(c)	snd_soc_write(c, WM8971_RESET, 0)
 
 /* WM8971 Controls */
 static const char *wm8971_bass[] = { "Linear Control", "Adaptive Boost" };
@@ -521,7 +484,7 @@
 		return -EINVAL;
 	}
 
-	wm8971_write(codec, WM8971_IFACE, iface);
+	snd_soc_write(codec, WM8971_IFACE, iface);
 	return 0;
 }
 
@@ -533,8 +496,8 @@
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
 	struct wm8971_priv *wm8971 = codec->private_data;
-	u16 iface = wm8971_read_reg_cache(codec, WM8971_IFACE) & 0x1f3;
-	u16 srate = wm8971_read_reg_cache(codec, WM8971_SRATE) & 0x1c0;
+	u16 iface = snd_soc_read(codec, WM8971_IFACE) & 0x1f3;
+	u16 srate = snd_soc_read(codec, WM8971_SRATE) & 0x1c0;
 	int coeff = get_coeff(wm8971->sysclk, params_rate(params));
 
 	/* bit size */
@@ -553,9 +516,9 @@
 	}
 
 	/* set iface & srate */
-	wm8971_write(codec, WM8971_IFACE, iface);
+	snd_soc_write(codec, WM8971_IFACE, iface);
 	if (coeff >= 0)
-		wm8971_write(codec, WM8971_SRATE, srate |
+		snd_soc_write(codec, WM8971_SRATE, srate |
 			(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
 
 	return 0;
@@ -564,33 +527,33 @@
 static int wm8971_mute(struct snd_soc_dai *dai, int mute)
 {
 	struct snd_soc_codec *codec = dai->codec;
-	u16 mute_reg = wm8971_read_reg_cache(codec, WM8971_ADCDAC) & 0xfff7;
+	u16 mute_reg = snd_soc_read(codec, WM8971_ADCDAC) & 0xfff7;
 
 	if (mute)
-		wm8971_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
+		snd_soc_write(codec, WM8971_ADCDAC, mute_reg | 0x8);
 	else
-		wm8971_write(codec, WM8971_ADCDAC, mute_reg);
+		snd_soc_write(codec, WM8971_ADCDAC, mute_reg);
 	return 0;
 }
 
 static int wm8971_set_bias_level(struct snd_soc_codec *codec,
 	enum snd_soc_bias_level level)
 {
-	u16 pwr_reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
+	u16 pwr_reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
 
 	switch (level) {
 	case SND_SOC_BIAS_ON:
 		/* set vmid to 50k and unmute dac */
-		wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
+		snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x00c1);
 		break;
 	case SND_SOC_BIAS_PREPARE:
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		/* mute dac and set vmid to 500k, enable VREF */
-		wm8971_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
+		snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140);
 		break;
 	case SND_SOC_BIAS_OFF:
-		wm8971_write(codec, WM8971_PWR1, 0x0001);
+		snd_soc_write(codec, WM8971_PWR1, 0x0001);
 		break;
 	}
 	codec->bias_level = level;
@@ -667,8 +630,8 @@
 
 	/* charge wm8971 caps */
 	if (codec->suspend_bias_level == SND_SOC_BIAS_ON) {
-		reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
-		wm8971_write(codec, WM8971_PWR1, reg | 0x01c0);
+		reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
+		snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0);
 		codec->bias_level = SND_SOC_BIAS_ON;
 		queue_delayed_work(wm8971_workq, &codec->delayed_work,
 			msecs_to_jiffies(1000));
@@ -677,15 +640,14 @@
 	return 0;
 }
 
-static int wm8971_init(struct snd_soc_device *socdev)
+static int wm8971_init(struct snd_soc_device *socdev,
+		       enum snd_soc_control_type control)
 {
 	struct snd_soc_codec *codec = socdev->card->codec;
 	int reg, ret = 0;
 
 	codec->name = "WM8971";
 	codec->owner = THIS_MODULE;
-	codec->read = wm8971_read_reg_cache;
-	codec->write = wm8971_write;
 	codec->set_bias_level = wm8971_set_bias_level;
 	codec->dai = &wm8971_dai;
 	codec->reg_cache_size = ARRAY_SIZE(wm8971_reg);
@@ -695,42 +657,48 @@
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
 
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8971: failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
 	wm8971_reset(codec);
 
 	/* register pcms */
 	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
 	if (ret < 0) {
 		printk(KERN_ERR "wm8971: failed to create pcms\n");
-		goto pcm_err;
+		goto err;
 	}
 
 	/* charge output caps - set vmid to 5k for quick power up */
-	reg = wm8971_read_reg_cache(codec, WM8971_PWR1) & 0xfe3e;
-	wm8971_write(codec, WM8971_PWR1, reg | 0x01c0);
+	reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e;
+	snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0);
 	codec->bias_level = SND_SOC_BIAS_STANDBY;
 	queue_delayed_work(wm8971_workq, &codec->delayed_work,
 		msecs_to_jiffies(1000));
 
 	/* set the update bits */
-	reg = wm8971_read_reg_cache(codec, WM8971_LDAC);
-	wm8971_write(codec, WM8971_LDAC, reg | 0x0100);
-	reg = wm8971_read_reg_cache(codec, WM8971_RDAC);
-	wm8971_write(codec, WM8971_RDAC, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8971_LDAC);
+	snd_soc_write(codec, WM8971_LDAC, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8971_RDAC);
+	snd_soc_write(codec, WM8971_RDAC, reg | 0x0100);
 
-	reg = wm8971_read_reg_cache(codec, WM8971_LOUT1V);
-	wm8971_write(codec, WM8971_LOUT1V, reg | 0x0100);
-	reg = wm8971_read_reg_cache(codec, WM8971_ROUT1V);
-	wm8971_write(codec, WM8971_ROUT1V, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8971_LOUT1V);
+	snd_soc_write(codec, WM8971_LOUT1V, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8971_ROUT1V);
+	snd_soc_write(codec, WM8971_ROUT1V, reg | 0x0100);
 
-	reg = wm8971_read_reg_cache(codec, WM8971_LOUT2V);
-	wm8971_write(codec, WM8971_LOUT2V, reg | 0x0100);
-	reg = wm8971_read_reg_cache(codec, WM8971_ROUT2V);
-	wm8971_write(codec, WM8971_ROUT2V, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8971_LOUT2V);
+	snd_soc_write(codec, WM8971_LOUT2V, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8971_ROUT2V);
+	snd_soc_write(codec, WM8971_ROUT2V, reg | 0x0100);
 
-	reg = wm8971_read_reg_cache(codec, WM8971_LINVOL);
-	wm8971_write(codec, WM8971_LINVOL, reg | 0x0100);
-	reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
-	wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8971_LINVOL);
+	snd_soc_write(codec, WM8971_LINVOL, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8971_RINVOL);
+	snd_soc_write(codec, WM8971_RINVOL, reg | 0x0100);
 
 	snd_soc_add_controls(codec, wm8971_snd_controls,
 				ARRAY_SIZE(wm8971_snd_controls));
@@ -745,7 +713,7 @@
 card_err:
 	snd_soc_free_pcms(socdev);
 	snd_soc_dapm_free(socdev);
-pcm_err:
+err:
 	kfree(codec->reg_cache);
 	return ret;
 }
@@ -767,7 +735,7 @@
 
 	codec->control_data = i2c;
 
-	ret = wm8971_init(socdev);
+	ret = wm8971_init(socdev, SND_SOC_I2C);
 	if (ret < 0)
 		pr_err("failed to initialise WM8971\n");
 
@@ -877,7 +845,6 @@
 
 #if defined (CONFIG_I2C) || defined (CONFIG_I2C_MODULE)
 	if (setup->i2c_address) {
-		codec->hw_write = (hw_write_t)i2c_master_send;
 		ret = wm8971_add_i2c_device(pdev, setup);
 	}
 #endif
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
new file mode 100644
index 0000000..1c86535
--- /dev/null
+++ b/sound/soc/codecs/wm8988.c
@@ -0,0 +1,1069 @@
+/*
+ * wm8988.c -- WM8988 ALSA SoC audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "wm8988.h"
+
+/*
+ * wm8988 register cache
+ * We can't read the WM8988 register space when we
+ * are using 2 wire for device control, so we cache them instead.
+ */
+static const u16 wm8988_reg[] = {
+	0x0097, 0x0097, 0x0079, 0x0079,  /*  0 */
+	0x0000, 0x0008, 0x0000, 0x000a,  /*  4 */
+	0x0000, 0x0000, 0x00ff, 0x00ff,  /*  8 */
+	0x000f, 0x000f, 0x0000, 0x0000,  /* 12 */
+	0x0000, 0x007b, 0x0000, 0x0032,  /* 16 */
+	0x0000, 0x00c3, 0x00c3, 0x00c0,  /* 20 */
+	0x0000, 0x0000, 0x0000, 0x0000,  /* 24 */
+	0x0000, 0x0000, 0x0000, 0x0000,  /* 28 */
+	0x0000, 0x0000, 0x0050, 0x0050,  /* 32 */
+	0x0050, 0x0050, 0x0050, 0x0050,  /* 36 */
+	0x0079, 0x0079, 0x0079,          /* 40 */
+};
+
+/* codec private data */
+struct wm8988_priv {
+	unsigned int sysclk;
+	struct snd_soc_codec codec;
+	struct snd_pcm_hw_constraint_list *sysclk_constraints;
+	u16 reg_cache[WM8988_NUM_REG];
+};
+
+
+#define wm8988_reset(c)	snd_soc_write(c, WM8988_RESET, 0)
+
+/*
+ * WM8988 Controls
+ */
+
+static const char *bass_boost_txt[] = {"Linear Control", "Adaptive Boost"};
+static const struct soc_enum bass_boost =
+	SOC_ENUM_SINGLE(WM8988_BASS, 7, 2, bass_boost_txt);
+
+static const char *bass_filter_txt[] = { "130Hz @ 48kHz", "200Hz @ 48kHz" };
+static const struct soc_enum bass_filter =
+	SOC_ENUM_SINGLE(WM8988_BASS, 6, 2, bass_filter_txt);
+
+static const char *treble_txt[] = {"8kHz", "4kHz"};
+static const struct soc_enum treble =
+	SOC_ENUM_SINGLE(WM8988_TREBLE, 6, 2, treble_txt);
+
+static const char *stereo_3d_lc_txt[] = {"200Hz", "500Hz"};
+static const struct soc_enum stereo_3d_lc =
+	SOC_ENUM_SINGLE(WM8988_3D, 5, 2, stereo_3d_lc_txt);
+
+static const char *stereo_3d_uc_txt[] = {"2.2kHz", "1.5kHz"};
+static const struct soc_enum stereo_3d_uc =
+	SOC_ENUM_SINGLE(WM8988_3D, 6, 2, stereo_3d_uc_txt);
+
+static const char *stereo_3d_func_txt[] = {"Capture", "Playback"};
+static const struct soc_enum stereo_3d_func =
+	SOC_ENUM_SINGLE(WM8988_3D, 7, 2, stereo_3d_func_txt);
+
+static const char *alc_func_txt[] = {"Off", "Right", "Left", "Stereo"};
+static const struct soc_enum alc_func =
+	SOC_ENUM_SINGLE(WM8988_ALC1, 7, 4, alc_func_txt);
+
+static const char *ng_type_txt[] = {"Constant PGA Gain",
+				    "Mute ADC Output"};
+static const struct soc_enum ng_type =
+	SOC_ENUM_SINGLE(WM8988_NGATE, 1, 2, ng_type_txt);
+
+static const char *deemph_txt[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const struct soc_enum deemph =
+	SOC_ENUM_SINGLE(WM8988_ADCDAC, 1, 4, deemph_txt);
+
+static const char *adcpol_txt[] = {"Normal", "L Invert", "R Invert",
+				   "L + R Invert"};
+static const struct soc_enum adcpol =
+	SOC_ENUM_SINGLE(WM8988_ADCDAC, 5, 4, adcpol_txt);
+
+static const DECLARE_TLV_DB_SCALE(pga_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1);
+static const DECLARE_TLV_DB_SCALE(bypass_tlv, -1500, 300, 0);
+
+static const struct snd_kcontrol_new wm8988_snd_controls[] = {
+
+SOC_ENUM("Bass Boost", bass_boost),
+SOC_ENUM("Bass Filter", bass_filter),
+SOC_SINGLE("Bass Volume", WM8988_BASS, 0, 15, 1),
+
+SOC_SINGLE("Treble Volume", WM8988_TREBLE, 0, 15, 0),
+SOC_ENUM("Treble Cut-off", treble),
+
+SOC_SINGLE("3D Switch", WM8988_3D, 0, 1, 0),
+SOC_SINGLE("3D Volume", WM8988_3D, 1, 15, 0),
+SOC_ENUM("3D Lower Cut-off", stereo_3d_lc),
+SOC_ENUM("3D Upper Cut-off", stereo_3d_uc),
+SOC_ENUM("3D Mode", stereo_3d_func),
+
+SOC_SINGLE("ALC Capture Target Volume", WM8988_ALC1, 0, 7, 0),
+SOC_SINGLE("ALC Capture Max Volume", WM8988_ALC1, 4, 7, 0),
+SOC_ENUM("ALC Capture Function", alc_func),
+SOC_SINGLE("ALC Capture ZC Switch", WM8988_ALC2, 7, 1, 0),
+SOC_SINGLE("ALC Capture Hold Time", WM8988_ALC2, 0, 15, 0),
+SOC_SINGLE("ALC Capture Decay Time", WM8988_ALC3, 4, 15, 0),
+SOC_SINGLE("ALC Capture Attack Time", WM8988_ALC3, 0, 15, 0),
+SOC_SINGLE("ALC Capture NG Threshold", WM8988_NGATE, 3, 31, 0),
+SOC_ENUM("ALC Capture NG Type", ng_type),
+SOC_SINGLE("ALC Capture NG Switch", WM8988_NGATE, 0, 1, 0),
+
+SOC_SINGLE("ZC Timeout Switch", WM8988_ADCTL1, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Digital Volume", WM8988_LADC, WM8988_RADC,
+		 0, 255, 0, adc_tlv),
+SOC_DOUBLE_R_TLV("Capture Volume", WM8988_LINVOL, WM8988_RINVOL,
+		 0, 63, 0, pga_tlv),
+SOC_DOUBLE_R("Capture ZC Switch", WM8988_LINVOL, WM8988_RINVOL, 6, 1, 0),
+SOC_DOUBLE_R("Capture Switch", WM8988_LINVOL, WM8988_RINVOL, 7, 1, 1),
+
+SOC_ENUM("Playback De-emphasis", deemph),
+
+SOC_ENUM("Capture Polarity", adcpol),
+SOC_SINGLE("Playback 6dB Attenuate", WM8988_ADCDAC, 7, 1, 0),
+SOC_SINGLE("Capture 6dB Attenuate", WM8988_ADCDAC, 8, 1, 0),
+
+SOC_DOUBLE_R_TLV("PCM Volume", WM8988_LDAC, WM8988_RDAC, 0, 255, 0, dac_tlv),
+
+SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", WM8988_LOUTM1, 4, 7, 1,
+	       bypass_tlv),
+SOC_SINGLE_TLV("Left Mixer Right Bypass Volume", WM8988_LOUTM2, 4, 7, 1,
+	       bypass_tlv),
+SOC_SINGLE_TLV("Right Mixer Left Bypass Volume", WM8988_ROUTM1, 4, 7, 1,
+	       bypass_tlv),
+SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", WM8988_ROUTM2, 4, 7, 1,
+	       bypass_tlv),
+
+SOC_DOUBLE_R("Output 1 Playback ZC Switch", WM8988_LOUT1V,
+	     WM8988_ROUT1V, 7, 1, 0),
+SOC_DOUBLE_R_TLV("Output 1 Playback Volume", WM8988_LOUT1V, WM8988_ROUT1V,
+		 0, 127, 0, out_tlv),
+
+SOC_DOUBLE_R("Output 2 Playback ZC Switch", WM8988_LOUT2V,
+	     WM8988_ROUT2V, 7, 1, 0),
+SOC_DOUBLE_R_TLV("Output 2 Playback Volume", WM8988_LOUT2V, WM8988_ROUT2V,
+		 0, 127, 0, out_tlv),
+
+};
+
+/*
+ * DAPM Controls
+ */
+
+static int wm8988_lrc_control(struct snd_soc_dapm_widget *w,
+			      struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	u16 adctl2 = snd_soc_read(codec, WM8988_ADCTL2);
+
+	/* Use the DAC to gate LRC if active, otherwise use ADC */
+	if (snd_soc_read(codec, WM8988_PWR2) & 0x180)
+		adctl2 &= ~0x4;
+	else
+		adctl2 |= 0x4;
+
+	return snd_soc_write(codec, WM8988_ADCTL2, adctl2);
+}
+
+static const char *wm8988_line_texts[] = {
+	"Line 1", "Line 2", "PGA", "Differential"};
+
+static const unsigned int wm8988_line_values[] = {
+	0, 1, 3, 4};
+
+static const struct soc_enum wm8988_lline_enum =
+	SOC_VALUE_ENUM_SINGLE(WM8988_LOUTM1, 0, 7,
+			      ARRAY_SIZE(wm8988_line_texts),
+			      wm8988_line_texts,
+			      wm8988_line_values);
+static const struct snd_kcontrol_new wm8988_left_line_controls =
+	SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum);
+
+static const struct soc_enum wm8988_rline_enum =
+	SOC_VALUE_ENUM_SINGLE(WM8988_ROUTM1, 0, 7,
+			      ARRAY_SIZE(wm8988_line_texts),
+			      wm8988_line_texts,
+			      wm8988_line_values);
+static const struct snd_kcontrol_new wm8988_right_line_controls =
+	SOC_DAPM_VALUE_ENUM("Route", wm8988_lline_enum);
+
+/* Left Mixer */
+static const struct snd_kcontrol_new wm8988_left_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Playback Switch", WM8988_LOUTM1, 8, 1, 0),
+	SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_LOUTM1, 7, 1, 0),
+	SOC_DAPM_SINGLE("Right Playback Switch", WM8988_LOUTM2, 8, 1, 0),
+	SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_LOUTM2, 7, 1, 0),
+};
+
+/* Right Mixer */
+static const struct snd_kcontrol_new wm8988_right_mixer_controls[] = {
+	SOC_DAPM_SINGLE("Left Playback Switch", WM8988_ROUTM1, 8, 1, 0),
+	SOC_DAPM_SINGLE("Left Bypass Switch", WM8988_ROUTM1, 7, 1, 0),
+	SOC_DAPM_SINGLE("Playback Switch", WM8988_ROUTM2, 8, 1, 0),
+	SOC_DAPM_SINGLE("Right Bypass Switch", WM8988_ROUTM2, 7, 1, 0),
+};
+
+static const char *wm8988_pga_sel[] = {"Line 1", "Line 2", "Differential"};
+static const unsigned int wm8988_pga_val[] = { 0, 1, 3 };
+
+/* Left PGA Mux */
+static const struct soc_enum wm8988_lpga_enum =
+	SOC_VALUE_ENUM_SINGLE(WM8988_LADCIN, 6, 3,
+			      ARRAY_SIZE(wm8988_pga_sel),
+			      wm8988_pga_sel,
+			      wm8988_pga_val);
+static const struct snd_kcontrol_new wm8988_left_pga_controls =
+	SOC_DAPM_VALUE_ENUM("Route", wm8988_lpga_enum);
+
+/* Right PGA Mux */
+static const struct soc_enum wm8988_rpga_enum =
+	SOC_VALUE_ENUM_SINGLE(WM8988_RADCIN, 6, 3,
+			      ARRAY_SIZE(wm8988_pga_sel),
+			      wm8988_pga_sel,
+			      wm8988_pga_val);
+static const struct snd_kcontrol_new wm8988_right_pga_controls =
+	SOC_DAPM_VALUE_ENUM("Route", wm8988_rpga_enum);
+
+/* Differential Mux */
+static const char *wm8988_diff_sel[] = {"Line 1", "Line 2"};
+static const struct soc_enum diffmux =
+	SOC_ENUM_SINGLE(WM8988_ADCIN, 8, 2, wm8988_diff_sel);
+static const struct snd_kcontrol_new wm8988_diffmux_controls =
+	SOC_DAPM_ENUM("Route", diffmux);
+
+/* Mono ADC Mux */
+static const char *wm8988_mono_mux[] = {"Stereo", "Mono (Left)",
+	"Mono (Right)", "Digital Mono"};
+static const struct soc_enum monomux =
+	SOC_ENUM_SINGLE(WM8988_ADCIN, 6, 4, wm8988_mono_mux);
+static const struct snd_kcontrol_new wm8988_monomux_controls =
+	SOC_DAPM_ENUM("Route", monomux);
+
+static const struct snd_soc_dapm_widget wm8988_dapm_widgets[] = {
+	SND_SOC_DAPM_MICBIAS("Mic Bias", WM8988_PWR1, 1, 0),
+
+	SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
+		&wm8988_diffmux_controls),
+	SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
+		&wm8988_monomux_controls),
+	SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
+		&wm8988_monomux_controls),
+
+	SND_SOC_DAPM_MUX("Left PGA Mux", WM8988_PWR1, 5, 0,
+		&wm8988_left_pga_controls),
+	SND_SOC_DAPM_MUX("Right PGA Mux", WM8988_PWR1, 4, 0,
+		&wm8988_right_pga_controls),
+
+	SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
+		&wm8988_left_line_controls),
+	SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
+		&wm8988_right_line_controls),
+
+	SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8988_PWR1, 2, 0),
+	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8988_PWR1, 3, 0),
+
+	SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8988_PWR2, 7, 0),
+	SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8988_PWR2, 8, 0),
+
+	SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+		&wm8988_left_mixer_controls[0],
+		ARRAY_SIZE(wm8988_left_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+		&wm8988_right_mixer_controls[0],
+		ARRAY_SIZE(wm8988_right_mixer_controls)),
+
+	SND_SOC_DAPM_PGA("Right Out 2", WM8988_PWR2, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Left Out 2", WM8988_PWR2, 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Right Out 1", WM8988_PWR2, 5, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Left Out 1", WM8988_PWR2, 6, 0, NULL, 0),
+
+	SND_SOC_DAPM_POST("LRC control", wm8988_lrc_control),
+
+	SND_SOC_DAPM_OUTPUT("LOUT1"),
+	SND_SOC_DAPM_OUTPUT("ROUT1"),
+	SND_SOC_DAPM_OUTPUT("LOUT2"),
+	SND_SOC_DAPM_OUTPUT("ROUT2"),
+	SND_SOC_DAPM_OUTPUT("VREF"),
+
+	SND_SOC_DAPM_INPUT("LINPUT1"),
+	SND_SOC_DAPM_INPUT("LINPUT2"),
+	SND_SOC_DAPM_INPUT("RINPUT1"),
+	SND_SOC_DAPM_INPUT("RINPUT2"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+	{ "Left Line Mux", "Line 1", "LINPUT1" },
+	{ "Left Line Mux", "Line 2", "LINPUT2" },
+	{ "Left Line Mux", "PGA", "Left PGA Mux" },
+	{ "Left Line Mux", "Differential", "Differential Mux" },
+
+	{ "Right Line Mux", "Line 1", "RINPUT1" },
+	{ "Right Line Mux", "Line 2", "RINPUT2" },
+	{ "Right Line Mux", "PGA", "Right PGA Mux" },
+	{ "Right Line Mux", "Differential", "Differential Mux" },
+
+	{ "Left PGA Mux", "Line 1", "LINPUT1" },
+	{ "Left PGA Mux", "Line 2", "LINPUT2" },
+	{ "Left PGA Mux", "Differential", "Differential Mux" },
+
+	{ "Right PGA Mux", "Line 1", "RINPUT1" },
+	{ "Right PGA Mux", "Line 2", "RINPUT2" },
+	{ "Right PGA Mux", "Differential", "Differential Mux" },
+
+	{ "Differential Mux", "Line 1", "LINPUT1" },
+	{ "Differential Mux", "Line 1", "RINPUT1" },
+	{ "Differential Mux", "Line 2", "LINPUT2" },
+	{ "Differential Mux", "Line 2", "RINPUT2" },
+
+	{ "Left ADC Mux", "Stereo", "Left PGA Mux" },
+	{ "Left ADC Mux", "Mono (Left)", "Left PGA Mux" },
+	{ "Left ADC Mux", "Digital Mono", "Left PGA Mux" },
+
+	{ "Right ADC Mux", "Stereo", "Right PGA Mux" },
+	{ "Right ADC Mux", "Mono (Right)", "Right PGA Mux" },
+	{ "Right ADC Mux", "Digital Mono", "Right PGA Mux" },
+
+	{ "Left ADC", NULL, "Left ADC Mux" },
+	{ "Right ADC", NULL, "Right ADC Mux" },
+
+	{ "Left Line Mux", "Line 1", "LINPUT1" },
+	{ "Left Line Mux", "Line 2", "LINPUT2" },
+	{ "Left Line Mux", "PGA", "Left PGA Mux" },
+	{ "Left Line Mux", "Differential", "Differential Mux" },
+
+	{ "Right Line Mux", "Line 1", "RINPUT1" },
+	{ "Right Line Mux", "Line 2", "RINPUT2" },
+	{ "Right Line Mux", "PGA", "Right PGA Mux" },
+	{ "Right Line Mux", "Differential", "Differential Mux" },
+
+	{ "Left Mixer", "Playback Switch", "Left DAC" },
+	{ "Left Mixer", "Left Bypass Switch", "Left Line Mux" },
+	{ "Left Mixer", "Right Playback Switch", "Right DAC" },
+	{ "Left Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+	{ "Right Mixer", "Left Playback Switch", "Left DAC" },
+	{ "Right Mixer", "Left Bypass Switch", "Left Line Mux" },
+	{ "Right Mixer", "Playback Switch", "Right DAC" },
+	{ "Right Mixer", "Right Bypass Switch", "Right Line Mux" },
+
+	{ "Left Out 1", NULL, "Left Mixer" },
+	{ "LOUT1", NULL, "Left Out 1" },
+	{ "Right Out 1", NULL, "Right Mixer" },
+	{ "ROUT1", NULL, "Right Out 1" },
+
+	{ "Left Out 2", NULL, "Left Mixer" },
+	{ "LOUT2", NULL, "Left Out 2" },
+	{ "Right Out 2", NULL, "Right Mixer" },
+	{ "ROUT2", NULL, "Right Out 2" },
+};
+
+struct _coeff_div {
+	u32 mclk;
+	u32 rate;
+	u16 fs;
+	u8 sr:5;
+	u8 usb:1;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+	/* 8k */
+	{12288000, 8000, 1536, 0x6, 0x0},
+	{11289600, 8000, 1408, 0x16, 0x0},
+	{18432000, 8000, 2304, 0x7, 0x0},
+	{16934400, 8000, 2112, 0x17, 0x0},
+	{12000000, 8000, 1500, 0x6, 0x1},
+
+	/* 11.025k */
+	{11289600, 11025, 1024, 0x18, 0x0},
+	{16934400, 11025, 1536, 0x19, 0x0},
+	{12000000, 11025, 1088, 0x19, 0x1},
+
+	/* 16k */
+	{12288000, 16000, 768, 0xa, 0x0},
+	{18432000, 16000, 1152, 0xb, 0x0},
+	{12000000, 16000, 750, 0xa, 0x1},
+
+	/* 22.05k */
+	{11289600, 22050, 512, 0x1a, 0x0},
+	{16934400, 22050, 768, 0x1b, 0x0},
+	{12000000, 22050, 544, 0x1b, 0x1},
+
+	/* 32k */
+	{12288000, 32000, 384, 0xc, 0x0},
+	{18432000, 32000, 576, 0xd, 0x0},
+	{12000000, 32000, 375, 0xa, 0x1},
+
+	/* 44.1k */
+	{11289600, 44100, 256, 0x10, 0x0},
+	{16934400, 44100, 384, 0x11, 0x0},
+	{12000000, 44100, 272, 0x11, 0x1},
+
+	/* 48k */
+	{12288000, 48000, 256, 0x0, 0x0},
+	{18432000, 48000, 384, 0x1, 0x0},
+	{12000000, 48000, 250, 0x0, 0x1},
+
+	/* 88.2k */
+	{11289600, 88200, 128, 0x1e, 0x0},
+	{16934400, 88200, 192, 0x1f, 0x0},
+	{12000000, 88200, 136, 0x1f, 0x1},
+
+	/* 96k */
+	{12288000, 96000, 128, 0xe, 0x0},
+	{18432000, 96000, 192, 0xf, 0x0},
+	{12000000, 96000, 125, 0xe, 0x1},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+		if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+/* The set of rates we can generate from the above for each SYSCLK */
+
+static unsigned int rates_12288[] = {
+	8000, 12000, 16000, 24000, 24000, 32000, 48000, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_12288 = {
+	.count	= ARRAY_SIZE(rates_12288),
+	.list	= rates_12288,
+};
+
+static unsigned int rates_112896[] = {
+	8000, 11025, 22050, 44100,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_112896 = {
+	.count	= ARRAY_SIZE(rates_112896),
+	.list	= rates_112896,
+};
+
+static unsigned int rates_12[] = {
+	8000, 11025, 12000, 16000, 22050, 2400, 32000, 41100, 48000,
+	48000, 88235, 96000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_12 = {
+	.count	= ARRAY_SIZE(rates_12),
+	.list	= rates_12,
+};
+
+/*
+ * Note that this should be called from init rather than from hw_params.
+ */
+static int wm8988_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8988_priv *wm8988 = codec->private_data;
+
+	switch (freq) {
+	case 11289600:
+	case 18432000:
+	case 22579200:
+	case 36864000:
+		wm8988->sysclk_constraints = &constraints_112896;
+		wm8988->sysclk = freq;
+		return 0;
+
+	case 12288000:
+	case 16934400:
+	case 24576000:
+	case 33868800:
+		wm8988->sysclk_constraints = &constraints_12288;
+		wm8988->sysclk = freq;
+		return 0;
+
+	case 12000000:
+	case 24000000:
+		wm8988->sysclk_constraints = &constraints_12;
+		wm8988->sysclk = freq;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int wm8988_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = 0;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		iface = 0x0040;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface |= 0x0002;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x0001;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x0003;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		iface |= 0x0013;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		iface |= 0x0090;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= 0x0080;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x0010;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, WM8988_IFACE, iface);
+	return 0;
+}
+
+static int wm8988_pcm_startup(struct snd_pcm_substream *substream,
+			      struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8988_priv *wm8988 = codec->private_data;
+
+	/* The set of sample rates that can be supported depends on the
+	 * MCLK supplied to the CODEC - enforce this.
+	 */
+	if (!wm8988->sysclk) {
+		dev_err(codec->dev,
+			"No MCLK configured, call set_sysclk() on init\n");
+		return -EINVAL;
+	}
+
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+				   SNDRV_PCM_HW_PARAM_RATE,
+				   wm8988->sysclk_constraints);
+
+	return 0;
+}
+
+static int wm8988_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct wm8988_priv *wm8988 = codec->private_data;
+	u16 iface = snd_soc_read(codec, WM8988_IFACE) & 0x1f3;
+	u16 srate = snd_soc_read(codec, WM8988_SRATE) & 0x180;
+	int coeff;
+
+	coeff = get_coeff(wm8988->sysclk, params_rate(params));
+	if (coeff < 0) {
+		coeff = get_coeff(wm8988->sysclk / 2, params_rate(params));
+		srate |= 0x40;
+	}
+	if (coeff < 0) {
+		dev_err(codec->dev,
+			"Unable to configure sample rate %dHz with %dHz MCLK\n",
+			params_rate(params), wm8988->sysclk);
+		return coeff;
+	}
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= 0x0004;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= 0x0008;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface |= 0x000c;
+		break;
+	}
+
+	/* set iface & srate */
+	snd_soc_write(codec, WM8988_IFACE, iface);
+	if (coeff >= 0)
+		snd_soc_write(codec, WM8988_SRATE, srate |
+			(coeff_div[coeff].sr << 1) | coeff_div[coeff].usb);
+
+	return 0;
+}
+
+static int wm8988_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = snd_soc_read(codec, WM8988_ADCDAC) & 0xfff7;
+
+	if (mute)
+		snd_soc_write(codec, WM8988_ADCDAC, mute_reg | 0x8);
+	else
+		snd_soc_write(codec, WM8988_ADCDAC, mute_reg);
+	return 0;
+}
+
+static int wm8988_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	u16 pwr_reg = snd_soc_read(codec, WM8988_PWR1) & ~0x1c1;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		/* VREF, VMID=2x50k, digital enabled */
+		snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x00c0);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* VREF, VMID=2x5k */
+			snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x1c1);
+
+			/* Charge caps */
+			msleep(100);
+		}
+
+		/* VREF, VMID=2*500k, digital stopped */
+		snd_soc_write(codec, WM8988_PWR1, pwr_reg | 0x0141);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		snd_soc_write(codec, WM8988_PWR1, 0x0000);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8988_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM8988_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+	SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8988_ops = {
+	.startup = wm8988_pcm_startup,
+	.hw_params = wm8988_pcm_hw_params,
+	.set_fmt = wm8988_set_dai_fmt,
+	.set_sysclk = wm8988_set_dai_sysclk,
+	.digital_mute = wm8988_mute,
+};
+
+struct snd_soc_dai wm8988_dai = {
+	.name = "WM8988",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8988_RATES,
+		.formats = WM8988_FORMATS,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8988_RATES,
+		.formats = WM8988_FORMATS,
+	 },
+	.ops = &wm8988_ops,
+	.symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8988_dai);
+
+static int wm8988_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8988_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	int i;
+	u8 data[2];
+	u16 *cache = codec->reg_cache;
+
+	/* Sync reg_cache with the hardware */
+	for (i = 0; i < WM8988_NUM_REG; i++) {
+		if (i == WM8988_RESET)
+			continue;
+		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+
+	wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+
+static struct snd_soc_codec *wm8988_codec;
+
+static int wm8988_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8988_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8988_codec;
+	codec = wm8988_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, wm8988_snd_controls,
+				ARRAY_SIZE(wm8988_snd_controls));
+	snd_soc_dapm_new_controls(codec, wm8988_dapm_widgets,
+				  ARRAY_SIZE(wm8988_dapm_widgets));
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+	snd_soc_dapm_new_widgets(codec);
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+static int wm8988_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8988 = {
+	.probe = 	wm8988_probe,
+	.remove = 	wm8988_remove,
+	.suspend = 	wm8988_suspend,
+	.resume =	wm8988_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8988);
+
+static int wm8988_register(struct wm8988_priv *wm8988,
+			   enum snd_soc_control_type control)
+{
+	struct snd_soc_codec *codec = &wm8988->codec;
+	int ret;
+	u16 reg;
+
+	if (wm8988_codec) {
+		dev_err(codec->dev, "Another WM8988 is registered\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8988;
+	codec->name = "WM8988";
+	codec->owner = THIS_MODULE;
+	codec->dai = &wm8988_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = ARRAY_SIZE(wm8988->reg_cache);
+	codec->reg_cache = &wm8988->reg_cache;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8988_set_bias_level;
+
+	memcpy(codec->reg_cache, wm8988_reg,
+	       sizeof(wm8988_reg));
+
+	ret = snd_soc_codec_set_cache_io(codec, 7, 9, control);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	ret = wm8988_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		goto err;
+	}
+
+	/* set the update bits (we always update left then right) */
+	reg = snd_soc_read(codec, WM8988_RADC);
+	snd_soc_write(codec, WM8988_RADC, reg | 0x100);
+	reg = snd_soc_read(codec, WM8988_RDAC);
+	snd_soc_write(codec, WM8988_RDAC, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8988_ROUT1V);
+	snd_soc_write(codec, WM8988_ROUT1V, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8988_ROUT2V);
+	snd_soc_write(codec, WM8988_ROUT2V, reg | 0x0100);
+	reg = snd_soc_read(codec, WM8988_RINVOL);
+	snd_soc_write(codec, WM8988_RINVOL, reg | 0x0100);
+
+	wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_STANDBY);
+
+	wm8988_dai.dev = codec->dev;
+
+	wm8988_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto err;
+	}
+
+	ret = snd_soc_register_dai(&wm8988_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		goto err_codec;
+	}
+
+	return 0;
+
+err_codec:
+	snd_soc_unregister_codec(codec);
+err:
+	kfree(wm8988);
+	return ret;
+}
+
+static void wm8988_unregister(struct wm8988_priv *wm8988)
+{
+	wm8988_set_bias_level(&wm8988->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dai(&wm8988_dai);
+	snd_soc_unregister_codec(&wm8988->codec);
+	kfree(wm8988);
+	wm8988_codec = NULL;
+}
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static int wm8988_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct wm8988_priv *wm8988;
+	struct snd_soc_codec *codec;
+
+	wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL);
+	if (wm8988 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8988->codec;
+
+	i2c_set_clientdata(i2c, wm8988);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+
+	return wm8988_register(wm8988, SND_SOC_I2C);
+}
+
+static int wm8988_i2c_remove(struct i2c_client *client)
+{
+	struct wm8988_priv *wm8988 = i2c_get_clientdata(client);
+	wm8988_unregister(wm8988);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8988_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm8988_i2c_resume(struct i2c_client *client)
+{
+	return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm8988_i2c_suspend NULL
+#define wm8988_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm8988_i2c_id[] = {
+	{ "wm8988", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id);
+
+static struct i2c_driver wm8988_i2c_driver = {
+	.driver = {
+		.name = "WM8988",
+		.owner = THIS_MODULE,
+	},
+	.probe = wm8988_i2c_probe,
+	.remove = wm8988_i2c_remove,
+	.suspend = wm8988_i2c_suspend,
+	.resume = wm8988_i2c_resume,
+	.id_table = wm8988_i2c_id,
+};
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit wm8988_spi_probe(struct spi_device *spi)
+{
+	struct wm8988_priv *wm8988;
+	struct snd_soc_codec *codec;
+
+	wm8988 = kzalloc(sizeof(struct wm8988_priv), GFP_KERNEL);
+	if (wm8988 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8988->codec;
+	codec->control_data = spi;
+	codec->dev = &spi->dev;
+
+	spi->dev.driver_data = wm8988;
+
+	return wm8988_register(wm8988, SND_SOC_SPI);
+}
+
+static int __devexit wm8988_spi_remove(struct spi_device *spi)
+{
+	struct wm8988_priv *wm8988 = spi->dev.driver_data;
+
+	wm8988_unregister(wm8988);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm8988_spi_suspend(struct spi_device *spi, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&spi->dev);
+}
+
+static int wm8988_spi_resume(struct spi_device *spi)
+{
+	return snd_soc_resume_device(&spi->dev);
+}
+#else
+#define wm8988_spi_suspend NULL
+#define wm8988_spi_resume NULL
+#endif
+
+static struct spi_driver wm8988_spi_driver = {
+	.driver = {
+		.name	= "wm8988",
+		.bus	= &spi_bus_type,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= wm8988_spi_probe,
+	.remove		= __devexit_p(wm8988_spi_remove),
+	.suspend	= wm8988_spi_suspend,
+	.resume		= wm8988_spi_resume,
+};
+#endif
+
+static int __init wm8988_modinit(void)
+{
+	int ret;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&wm8988_i2c_driver);
+	if (ret != 0)
+		pr_err("WM8988: Unable to register I2C driver: %d\n", ret);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	ret = spi_register_driver(&wm8988_spi_driver);
+	if (ret != 0)
+		pr_err("WM8988: Unable to register SPI driver: %d\n", ret);
+#endif
+	return ret;
+}
+module_init(wm8988_modinit);
+
+static void __exit wm8988_exit(void)
+{
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&wm8988_i2c_driver);
+#endif
+#if defined(CONFIG_SPI_MASTER)
+	spi_unregister_driver(&wm8988_spi_driver);
+#endif
+}
+module_exit(wm8988_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8988 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8988.h b/sound/soc/codecs/wm8988.h
new file mode 100644
index 0000000..4552d37
--- /dev/null
+++ b/sound/soc/codecs/wm8988.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <richard@openedhand.com>
+ *
+ * Based on WM8753.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef _WM8988_H
+#define _WM8988_H
+
+/* WM8988 register space */
+
+#define WM8988_LINVOL    0x00
+#define WM8988_RINVOL    0x01
+#define WM8988_LOUT1V    0x02
+#define WM8988_ROUT1V    0x03
+#define WM8988_ADCDAC    0x05
+#define WM8988_IFACE     0x07
+#define WM8988_SRATE     0x08
+#define WM8988_LDAC      0x0a
+#define WM8988_RDAC      0x0b
+#define WM8988_BASS      0x0c
+#define WM8988_TREBLE    0x0d
+#define WM8988_RESET     0x0f
+#define WM8988_3D        0x10
+#define WM8988_ALC1      0x11
+#define WM8988_ALC2      0x12
+#define WM8988_ALC3      0x13
+#define WM8988_NGATE     0x14
+#define WM8988_LADC      0x15
+#define WM8988_RADC      0x16
+#define WM8988_ADCTL1    0x17
+#define WM8988_ADCTL2    0x18
+#define WM8988_PWR1      0x19
+#define WM8988_PWR2      0x1a
+#define WM8988_ADCTL3    0x1b
+#define WM8988_ADCIN     0x1f
+#define WM8988_LADCIN    0x20
+#define WM8988_RADCIN    0x21
+#define WM8988_LOUTM1    0x22
+#define WM8988_LOUTM2    0x23
+#define WM8988_ROUTM1    0x24
+#define WM8988_ROUTM2    0x25
+#define WM8988_LOUT2V    0x28
+#define WM8988_ROUT2V    0x29
+#define WM8988_LPPB      0x43
+#define WM8988_NUM_REG   0x44
+
+#define WM8988_SYSCLK	0
+
+extern struct snd_soc_dai wm8988_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8988;
+
+#endif
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 40cd274..2d702db 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -108,53 +108,7 @@
 	0x0000,	    /* R63 - Driver internal */
 };
 
-/*
- * read wm8990 register cache
- */
-static inline unsigned int wm8990_read_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg)
-{
-	u16 *cache = codec->reg_cache;
-	BUG_ON(reg >= ARRAY_SIZE(wm8990_reg));
-	return cache[reg];
-}
-
-/*
- * write wm8990 register cache
- */
-static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
-	unsigned int reg, unsigned int value)
-{
-	u16 *cache = codec->reg_cache;
-
-	/* Reset register and reserved registers are uncached */
-	if (reg == 0 || reg >= ARRAY_SIZE(wm8990_reg))
-		return;
-
-	cache[reg] = value;
-}
-
-/*
- * write to the wm8990 register space
- */
-static int wm8990_write(struct snd_soc_codec *codec, unsigned int reg,
-	unsigned int value)
-{
-	u8 data[3];
-
-	data[0] = reg & 0xFF;
-	data[1] = (value >> 8) & 0xFF;
-	data[2] = value & 0xFF;
-
-	wm8990_write_reg_cache(codec, reg, value);
-
-	if (codec->hw_write(codec->control_data, data, 3) == 2)
-		return 0;
-	else
-		return -EIO;
-}
-
-#define wm8990_reset(c) wm8990_write(c, WM8990_RESET, 0)
+#define wm8990_reset(c) snd_soc_write(c, WM8990_RESET, 0)
 
 static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600);
 
@@ -187,8 +141,8 @@
 		return ret;
 
 	/* now hit the volume update bits (always bit 8) */
-	val = wm8990_read_reg_cache(codec, reg);
-	return wm8990_write(codec, reg, val | 0x0100);
+	val = snd_soc_read(codec, reg);
+	return snd_soc_write(codec, reg, val | 0x0100);
 }
 
 #define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
@@ -427,8 +381,8 @@
 {
 	u16 reg, fakepower;
 
-	reg = wm8990_read_reg_cache(w->codec, WM8990_POWER_MANAGEMENT_2);
-	fakepower = wm8990_read_reg_cache(w->codec, WM8990_INTDRIVBITS);
+	reg = snd_soc_read(w->codec, WM8990_POWER_MANAGEMENT_2);
+	fakepower = snd_soc_read(w->codec, WM8990_INTDRIVBITS);
 
 	if (fakepower & ((1 << WM8990_INMIXL_PWR_BIT) |
 		(1 << WM8990_AINLMUX_PWR_BIT))) {
@@ -443,7 +397,7 @@
 	} else {
 		reg &= ~WM8990_AINL_ENA;
 	}
-	wm8990_write(w->codec, WM8990_POWER_MANAGEMENT_2, reg);
+	snd_soc_write(w->codec, WM8990_POWER_MANAGEMENT_2, reg);
 
 	return 0;
 }
@@ -457,7 +411,7 @@
 
 	switch (reg_shift) {
 	case WM8990_SPEAKER_MIXER | (WM8990_LDSPK_BIT << 8) :
-		reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER1);
+		reg = snd_soc_read(w->codec, WM8990_OUTPUT_MIXER1);
 		if (reg & WM8990_LDLO) {
 			printk(KERN_WARNING
 			"Cannot set as Output Mixer 1 LDLO Set\n");
@@ -465,7 +419,7 @@
 		}
 		break;
 	case WM8990_SPEAKER_MIXER | (WM8990_RDSPK_BIT << 8):
-		reg = wm8990_read_reg_cache(w->codec, WM8990_OUTPUT_MIXER2);
+		reg = snd_soc_read(w->codec, WM8990_OUTPUT_MIXER2);
 		if (reg & WM8990_RDRO) {
 			printk(KERN_WARNING
 			"Cannot set as Output Mixer 2 RDRO Set\n");
@@ -473,7 +427,7 @@
 		}
 		break;
 	case WM8990_OUTPUT_MIXER1 | (WM8990_LDLO_BIT << 8):
-		reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+		reg = snd_soc_read(w->codec, WM8990_SPEAKER_MIXER);
 		if (reg & WM8990_LDSPK) {
 			printk(KERN_WARNING
 			"Cannot set as Speaker Mixer LDSPK Set\n");
@@ -481,7 +435,7 @@
 		}
 		break;
 	case WM8990_OUTPUT_MIXER2 | (WM8990_RDRO_BIT << 8):
-		reg = wm8990_read_reg_cache(w->codec, WM8990_SPEAKER_MIXER);
+		reg = snd_soc_read(w->codec, WM8990_SPEAKER_MIXER);
 		if (reg & WM8990_RDSPK) {
 			printk(KERN_WARNING
 			"Cannot set as Speaker Mixer RDSPK Set\n");
@@ -998,7 +952,7 @@
 
 	if ((Ndiv < 6) || (Ndiv > 12))
 		printk(KERN_WARNING
-		"WM8990 N value outwith recommended range! N = %d\n", Ndiv);
+		"WM8990 N value outwith recommended range! N = %u\n", Ndiv);
 
 	pll_div->n = Ndiv;
 	Nmod = target % source;
@@ -1029,24 +983,24 @@
 		pll_factors(&pll_div, freq_out * 4, freq_in);
 
 		/* Turn on PLL */
-		reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+		reg = snd_soc_read(codec, WM8990_POWER_MANAGEMENT_2);
 		reg |= WM8990_PLL_ENA;
-		wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+		snd_soc_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
 
 		/* sysclk comes from PLL */
-		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2);
-		wm8990_write(codec, WM8990_CLOCKING_2, reg | WM8990_SYSCLK_SRC);
+		reg = snd_soc_read(codec, WM8990_CLOCKING_2);
+		snd_soc_write(codec, WM8990_CLOCKING_2, reg | WM8990_SYSCLK_SRC);
 
 		/* set up N , fractional mode and pre-divisor if neccessary */
-		wm8990_write(codec, WM8990_PLL1, pll_div.n | WM8990_SDM |
+		snd_soc_write(codec, WM8990_PLL1, pll_div.n | WM8990_SDM |
 			(pll_div.div2?WM8990_PRESCALE:0));
-		wm8990_write(codec, WM8990_PLL2, (u8)(pll_div.k>>8));
-		wm8990_write(codec, WM8990_PLL3, (u8)(pll_div.k & 0xFF));
+		snd_soc_write(codec, WM8990_PLL2, (u8)(pll_div.k>>8));
+		snd_soc_write(codec, WM8990_PLL3, (u8)(pll_div.k & 0xFF));
 	} else {
 		/* Turn on PLL */
-		reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
+		reg = snd_soc_read(codec, WM8990_POWER_MANAGEMENT_2);
 		reg &= ~WM8990_PLL_ENA;
-		wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
+		snd_soc_write(codec, WM8990_POWER_MANAGEMENT_2, reg);
 	}
 	return 0;
 }
@@ -1073,8 +1027,8 @@
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 audio1, audio3;
 
-	audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
-	audio3 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_3);
+	audio1 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_1);
+	audio3 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_3);
 
 	/* set master/slave audio interface */
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -1115,8 +1069,8 @@
 		return -EINVAL;
 	}
 
-	wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
-	wm8990_write(codec, WM8990_AUDIO_INTERFACE_3, audio3);
+	snd_soc_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+	snd_soc_write(codec, WM8990_AUDIO_INTERFACE_3, audio3);
 	return 0;
 }
 
@@ -1128,24 +1082,24 @@
 
 	switch (div_id) {
 	case WM8990_MCLK_DIV:
-		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+		reg = snd_soc_read(codec, WM8990_CLOCKING_2) &
 			~WM8990_MCLK_DIV_MASK;
-		wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+		snd_soc_write(codec, WM8990_CLOCKING_2, reg | div);
 		break;
 	case WM8990_DACCLK_DIV:
-		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+		reg = snd_soc_read(codec, WM8990_CLOCKING_2) &
 			~WM8990_DAC_CLKDIV_MASK;
-		wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+		snd_soc_write(codec, WM8990_CLOCKING_2, reg | div);
 		break;
 	case WM8990_ADCCLK_DIV:
-		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_2) &
+		reg = snd_soc_read(codec, WM8990_CLOCKING_2) &
 			~WM8990_ADC_CLKDIV_MASK;
-		wm8990_write(codec, WM8990_CLOCKING_2, reg | div);
+		snd_soc_write(codec, WM8990_CLOCKING_2, reg | div);
 		break;
 	case WM8990_BCLK_DIV:
-		reg = wm8990_read_reg_cache(codec, WM8990_CLOCKING_1) &
+		reg = snd_soc_read(codec, WM8990_CLOCKING_1) &
 			~WM8990_BCLK_DIV_MASK;
-		wm8990_write(codec, WM8990_CLOCKING_1, reg | div);
+		snd_soc_write(codec, WM8990_CLOCKING_1, reg | div);
 		break;
 	default:
 		return -EINVAL;
@@ -1164,7 +1118,7 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_codec *codec = socdev->card->codec;
-	u16 audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
+	u16 audio1 = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_1);
 
 	audio1 &= ~WM8990_AIF_WL_MASK;
 	/* bit size */
@@ -1182,7 +1136,7 @@
 		break;
 	}
 
-	wm8990_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
+	snd_soc_write(codec, WM8990_AUDIO_INTERFACE_1, audio1);
 	return 0;
 }
 
@@ -1191,12 +1145,12 @@
 	struct snd_soc_codec *codec = dai->codec;
 	u16 val;
 
-	val  = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE;
+	val  = snd_soc_read(codec, WM8990_DAC_CTRL) & ~WM8990_DAC_MUTE;
 
 	if (mute)
-		wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+		snd_soc_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
 	else
-		wm8990_write(codec, WM8990_DAC_CTRL, val);
+		snd_soc_write(codec, WM8990_DAC_CTRL, val);
 
 	return 0;
 }
@@ -1212,21 +1166,21 @@
 
 	case SND_SOC_BIAS_PREPARE:
 		/* VMID=2*50k */
-		val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
+		val = snd_soc_read(codec, WM8990_POWER_MANAGEMENT_1) &
 			~WM8990_VMID_MODE_MASK;
-		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x2);
+		snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x2);
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
 		if (codec->bias_level == SND_SOC_BIAS_OFF) {
 			/* Enable all output discharge bits */
-			wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+			snd_soc_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
 				WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
 				WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
 				WM8990_DIS_ROUT);
 
 			/* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
-			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+			snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
 				     WM8990_BUFDCOPEN | WM8990_POBCTRL |
 				     WM8990_VMIDTOG);
 
@@ -1234,83 +1188,83 @@
 			msleep(msecs_to_jiffies(300));
 
 			/* Disable VMIDTOG */
-			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+			snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
 				     WM8990_BUFDCOPEN | WM8990_POBCTRL);
 
 			/* disable all output discharge bits */
-			wm8990_write(codec, WM8990_ANTIPOP1, 0);
+			snd_soc_write(codec, WM8990_ANTIPOP1, 0);
 
 			/* Enable outputs */
-			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00);
+			snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1b00);
 
 			msleep(msecs_to_jiffies(50));
 
 			/* Enable VMID at 2x50k */
-			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02);
+			snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f02);
 
 			msleep(msecs_to_jiffies(100));
 
 			/* Enable VREF */
-			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+			snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
 
 			msleep(msecs_to_jiffies(600));
 
 			/* Enable BUFIOEN */
-			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+			snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
 				     WM8990_BUFDCOPEN | WM8990_POBCTRL |
 				     WM8990_BUFIOEN);
 
 			/* Disable outputs */
-			wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x3);
+			snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x3);
 
 			/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
-			wm8990_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
+			snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_BUFIOEN);
 
 			/* Enable workaround for ADC clocking issue. */
-			wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0x2);
-			wm8990_write(codec, WM8990_EXT_CTL1, 0xa003);
-			wm8990_write(codec, WM8990_EXT_ACCESS_ENA, 0);
+			snd_soc_write(codec, WM8990_EXT_ACCESS_ENA, 0x2);
+			snd_soc_write(codec, WM8990_EXT_CTL1, 0xa003);
+			snd_soc_write(codec, WM8990_EXT_ACCESS_ENA, 0);
 		}
 
 		/* VMID=2*250k */
-		val = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_1) &
+		val = snd_soc_read(codec, WM8990_POWER_MANAGEMENT_1) &
 			~WM8990_VMID_MODE_MASK;
-		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x4);
+		snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, val | 0x4);
 		break;
 
 	case SND_SOC_BIAS_OFF:
 		/* Enable POBCTRL and SOFT_ST */
-		wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+		snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
 			WM8990_POBCTRL | WM8990_BUFIOEN);
 
 		/* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
-		wm8990_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
+		snd_soc_write(codec, WM8990_ANTIPOP2, WM8990_SOFTST |
 			WM8990_BUFDCOPEN | WM8990_POBCTRL |
 			WM8990_BUFIOEN);
 
 		/* mute DAC */
-		val = wm8990_read_reg_cache(codec, WM8990_DAC_CTRL);
-		wm8990_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
+		val = snd_soc_read(codec, WM8990_DAC_CTRL);
+		snd_soc_write(codec, WM8990_DAC_CTRL, val | WM8990_DAC_MUTE);
 
 		/* Enable any disabled outputs */
-		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
+		snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f03);
 
 		/* Disable VMID */
-		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01);
+		snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x1f01);
 
 		msleep(msecs_to_jiffies(300));
 
 		/* Enable all output discharge bits */
-		wm8990_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
+		snd_soc_write(codec, WM8990_ANTIPOP1, WM8990_DIS_LLINE |
 			WM8990_DIS_RLINE | WM8990_DIS_OUT3 |
 			WM8990_DIS_OUT4 | WM8990_DIS_LOUT |
 			WM8990_DIS_ROUT);
 
 		/* Disable VREF */
-		wm8990_write(codec, WM8990_POWER_MANAGEMENT_1, 0x0);
+		snd_soc_write(codec, WM8990_POWER_MANAGEMENT_1, 0x0);
 
 		/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
-		wm8990_write(codec, WM8990_ANTIPOP2, 0x0);
+		snd_soc_write(codec, WM8990_ANTIPOP2, 0x0);
 		break;
 	}
 
@@ -1411,8 +1365,6 @@
 
 	codec->name = "WM8990";
 	codec->owner = THIS_MODULE;
-	codec->read = wm8990_read_reg_cache;
-	codec->write = wm8990_write;
 	codec->set_bias_level = wm8990_set_bias_level;
 	codec->dai = &wm8990_dai;
 	codec->num_dai = 2;
@@ -1422,6 +1374,12 @@
 	if (codec->reg_cache == NULL)
 		return -ENOMEM;
 
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
+	if (ret < 0) {
+		printk(KERN_ERR "wm8990: failed to set cache I/O: %d\n", ret);
+		goto pcm_err;
+	}
+
 	wm8990_reset(codec);
 
 	/* register pcms */
@@ -1435,18 +1393,18 @@
 	codec->bias_level = SND_SOC_BIAS_OFF;
 	wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
-	reg = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_4);
-	wm8990_write(codec, WM8990_AUDIO_INTERFACE_4, reg | WM8990_ALRCGPIO1);
+	reg = snd_soc_read(codec, WM8990_AUDIO_INTERFACE_4);
+	snd_soc_write(codec, WM8990_AUDIO_INTERFACE_4, reg | WM8990_ALRCGPIO1);
 
-	reg = wm8990_read_reg_cache(codec, WM8990_GPIO1_GPIO2) &
+	reg = snd_soc_read(codec, WM8990_GPIO1_GPIO2) &
 		~WM8990_GPIO1_SEL_MASK;
-	wm8990_write(codec, WM8990_GPIO1_GPIO2, reg | 1);
+	snd_soc_write(codec, WM8990_GPIO1_GPIO2, reg | 1);
 
-	reg = wm8990_read_reg_cache(codec, WM8990_POWER_MANAGEMENT_2);
-	wm8990_write(codec, WM8990_POWER_MANAGEMENT_2, reg | WM8990_OPCLK_ENA);
+	reg = snd_soc_read(codec, WM8990_POWER_MANAGEMENT_2);
+	snd_soc_write(codec, WM8990_POWER_MANAGEMENT_2, reg | WM8990_OPCLK_ENA);
 
-	wm8990_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
-	wm8990_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+	snd_soc_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+	snd_soc_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
 
 	snd_soc_add_controls(codec, wm8990_snd_controls,
 				ARRAY_SIZE(wm8990_snd_controls));
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
new file mode 100644
index 0000000..cd15669
--- /dev/null
+++ b/sound/soc/codecs/wm8993.c
@@ -0,0 +1,1534 @@
+/*
+ * wm8993.c -- WM8993 ALSA SoC audio driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/wm8993.h>
+
+#include "wm8993.h"
+#include "wm_hubs.h"
+
+static u16 wm8993_reg_defaults[WM8993_REGISTER_COUNT] = {
+	0x8993,     /* R0   - Software Reset */
+	0x0000,     /* R1   - Power Management (1) */
+	0x6000,     /* R2   - Power Management (2) */
+	0x0000,     /* R3   - Power Management (3) */
+	0x4050,     /* R4   - Audio Interface (1) */
+	0x4000,     /* R5   - Audio Interface (2) */
+	0x01C8,     /* R6   - Clocking 1 */
+	0x0000,     /* R7   - Clocking 2 */
+	0x0000,     /* R8   - Audio Interface (3) */
+	0x0040,     /* R9   - Audio Interface (4) */
+	0x0004,     /* R10  - DAC CTRL */
+	0x00C0,     /* R11  - Left DAC Digital Volume */
+	0x00C0,     /* R12  - Right DAC Digital Volume */
+	0x0000,     /* R13  - Digital Side Tone */
+	0x0300,     /* R14  - ADC CTRL */
+	0x00C0,     /* R15  - Left ADC Digital Volume */
+	0x00C0,     /* R16  - Right ADC Digital Volume */
+	0x0000,     /* R17 */
+	0x0000,     /* R18  - GPIO CTRL 1 */
+	0x0010,     /* R19  - GPIO1 */
+	0x0000,     /* R20  - IRQ_DEBOUNCE */
+	0x0000,     /* R21 */
+	0x8000,     /* R22  - GPIOCTRL 2 */
+	0x0800,     /* R23  - GPIO_POL */
+	0x008B,     /* R24  - Left Line Input 1&2 Volume */
+	0x008B,     /* R25  - Left Line Input 3&4 Volume */
+	0x008B,     /* R26  - Right Line Input 1&2 Volume */
+	0x008B,     /* R27  - Right Line Input 3&4 Volume */
+	0x006D,     /* R28  - Left Output Volume */
+	0x006D,     /* R29  - Right Output Volume */
+	0x0066,     /* R30  - Line Outputs Volume */
+	0x0020,     /* R31  - HPOUT2 Volume */
+	0x0079,     /* R32  - Left OPGA Volume */
+	0x0079,     /* R33  - Right OPGA Volume */
+	0x0003,     /* R34  - SPKMIXL Attenuation */
+	0x0003,     /* R35  - SPKMIXR Attenuation */
+	0x0011,     /* R36  - SPKOUT Mixers */
+	0x0100,     /* R37  - SPKOUT Boost */
+	0x0079,     /* R38  - Speaker Volume Left */
+	0x0079,     /* R39  - Speaker Volume Right */
+	0x0000,     /* R40  - Input Mixer2 */
+	0x0000,     /* R41  - Input Mixer3 */
+	0x0000,     /* R42  - Input Mixer4 */
+	0x0000,     /* R43  - Input Mixer5 */
+	0x0000,     /* R44  - Input Mixer6 */
+	0x0000,     /* R45  - Output Mixer1 */
+	0x0000,     /* R46  - Output Mixer2 */
+	0x0000,     /* R47  - Output Mixer3 */
+	0x0000,     /* R48  - Output Mixer4 */
+	0x0000,     /* R49  - Output Mixer5 */
+	0x0000,     /* R50  - Output Mixer6 */
+	0x0000,     /* R51  - HPOUT2 Mixer */
+	0x0000,     /* R52  - Line Mixer1 */
+	0x0000,     /* R53  - Line Mixer2 */
+	0x0000,     /* R54  - Speaker Mixer */
+	0x0000,     /* R55  - Additional Control */
+	0x0000,     /* R56  - AntiPOP1 */
+	0x0000,     /* R57  - AntiPOP2 */
+	0x0000,     /* R58  - MICBIAS */
+	0x0000,     /* R59 */
+	0x0000,     /* R60  - FLL Control 1 */
+	0x0000,     /* R61  - FLL Control 2 */
+	0x0000,     /* R62  - FLL Control 3 */
+	0x2EE0,     /* R63  - FLL Control 4 */
+	0x0002,     /* R64  - FLL Control 5 */
+	0x2287,     /* R65  - Clocking 3 */
+	0x025F,     /* R66  - Clocking 4 */
+	0x0000,     /* R67  - MW Slave Control */
+	0x0000,     /* R68 */
+	0x0002,     /* R69  - Bus Control 1 */
+	0x0000,     /* R70  - Write Sequencer 0 */
+	0x0000,     /* R71  - Write Sequencer 1 */
+	0x0000,     /* R72  - Write Sequencer 2 */
+	0x0000,     /* R73  - Write Sequencer 3 */
+	0x0000,     /* R74  - Write Sequencer 4 */
+	0x0000,     /* R75  - Write Sequencer 5 */
+	0x1F25,     /* R76  - Charge Pump 1 */
+	0x0000,     /* R77 */
+	0x0000,     /* R78 */
+	0x0000,     /* R79 */
+	0x0000,     /* R80 */
+	0x0000,     /* R81  - Class W 0 */
+	0x0000,     /* R82 */
+	0x0000,     /* R83 */
+	0x0000,     /* R84  - DC Servo 0 */
+	0x054A,     /* R85  - DC Servo 1 */
+	0x0000,     /* R86 */
+	0x0000,     /* R87  - DC Servo 3 */
+	0x0000,     /* R88  - DC Servo Readback 0 */
+	0x0000,     /* R89  - DC Servo Readback 1 */
+	0x0000,     /* R90  - DC Servo Readback 2 */
+	0x0000,     /* R91 */
+	0x0000,     /* R92 */
+	0x0000,     /* R93 */
+	0x0000,     /* R94 */
+	0x0000,     /* R95 */
+	0x0100,     /* R96  - Analogue HP 0 */
+	0x0000,     /* R97 */
+	0x0000,     /* R98  - EQ1 */
+	0x000C,     /* R99  - EQ2 */
+	0x000C,     /* R100 - EQ3 */
+	0x000C,     /* R101 - EQ4 */
+	0x000C,     /* R102 - EQ5 */
+	0x000C,     /* R103 - EQ6 */
+	0x0FCA,     /* R104 - EQ7 */
+	0x0400,     /* R105 - EQ8 */
+	0x00D8,     /* R106 - EQ9 */
+	0x1EB5,     /* R107 - EQ10 */
+	0xF145,     /* R108 - EQ11 */
+	0x0B75,     /* R109 - EQ12 */
+	0x01C5,     /* R110 - EQ13 */
+	0x1C58,     /* R111 - EQ14 */
+	0xF373,     /* R112 - EQ15 */
+	0x0A54,     /* R113 - EQ16 */
+	0x0558,     /* R114 - EQ17 */
+	0x168E,     /* R115 - EQ18 */
+	0xF829,     /* R116 - EQ19 */
+	0x07AD,     /* R117 - EQ20 */
+	0x1103,     /* R118 - EQ21 */
+	0x0564,     /* R119 - EQ22 */
+	0x0559,     /* R120 - EQ23 */
+	0x4000,     /* R121 - EQ24 */
+	0x0000,     /* R122 - Digital Pulls */
+	0x0F08,     /* R123 - DRC Control 1 */
+	0x0000,     /* R124 - DRC Control 2 */
+	0x0080,     /* R125 - DRC Control 3 */
+	0x0000,     /* R126 - DRC Control 4 */
+};
+
+static struct {
+	int ratio;
+	int clk_sys_rate;
+} clk_sys_rates[] = {
+	{ 64,   0 },
+	{ 128,  1 },
+	{ 192,  2 },
+	{ 256,  3 },
+	{ 384,  4 },
+	{ 512,  5 },
+	{ 768,  6 },
+	{ 1024, 7 },
+	{ 1408, 8 },
+	{ 1536, 9 },
+};
+
+static struct {
+	int rate;
+	int sample_rate;
+} sample_rates[] = {
+	{ 8000,  0  },
+	{ 11025, 1  },
+	{ 12000, 1  },
+	{ 16000, 2  },
+	{ 22050, 3  },
+	{ 24000, 3  },
+	{ 32000, 4  },
+	{ 44100, 5  },
+	{ 48000, 5  },
+};
+
+static struct {
+	int div; /* *10 due to .5s */
+	int bclk_div;
+} bclk_divs[] = {
+	{ 10,  0  },
+	{ 15,  1  },
+	{ 20,  2  },
+	{ 30,  3  },
+	{ 40,  4  },
+	{ 55,  5  },
+	{ 60,  6  },
+	{ 80,  7  },
+	{ 110, 8  },
+	{ 120, 9  },
+	{ 160, 10 },
+	{ 220, 11 },
+	{ 240, 12 },
+	{ 320, 13 },
+	{ 440, 14 },
+	{ 480, 15 },
+};
+
+struct wm8993_priv {
+	u16 reg_cache[WM8993_REGISTER_COUNT];
+	struct wm8993_platform_data pdata;
+	struct snd_soc_codec codec;
+	int master;
+	int sysclk_source;
+	unsigned int mclk_rate;
+	unsigned int sysclk_rate;
+	unsigned int fs;
+	unsigned int bclk;
+	int class_w_users;
+	unsigned int fll_fref;
+	unsigned int fll_fout;
+};
+
+static unsigned int wm8993_read_hw(struct snd_soc_codec *codec, u8 reg)
+{
+	struct i2c_msg xfer[2];
+	u16 data;
+	int ret;
+	struct i2c_client *i2c = codec->control_data;
+
+	/* Write register */
+	xfer[0].addr = i2c->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &reg;
+
+	/* Read data */
+	xfer[1].addr = i2c->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = 2;
+	xfer[1].buf = (u8 *)&data;
+
+	ret = i2c_transfer(i2c->adapter, xfer, 2);
+	if (ret != 2) {
+		dev_err(codec->dev, "Failed to read 0x%x: %d\n", reg, ret);
+		return 0;
+	}
+
+	return (data >> 8) | ((data & 0xff) << 8);
+}
+
+static int wm8993_volatile(unsigned int reg)
+{
+	switch (reg) {
+	case WM8993_SOFTWARE_RESET:
+	case WM8993_DC_SERVO_0:
+	case WM8993_DC_SERVO_READBACK_0:
+	case WM8993_DC_SERVO_READBACK_1:
+	case WM8993_DC_SERVO_READBACK_2:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static unsigned int wm8993_read(struct snd_soc_codec *codec,
+				unsigned int reg)
+{
+	u16 *reg_cache = codec->reg_cache;
+
+	BUG_ON(reg > WM8993_MAX_REGISTER);
+
+	if (wm8993_volatile(reg))
+		return wm8993_read_hw(codec, reg);
+	else
+		return reg_cache[reg];
+}
+
+static int wm8993_write(struct snd_soc_codec *codec, unsigned int reg,
+			unsigned int value)
+{
+	u16 *reg_cache = codec->reg_cache;
+	u8 data[3];
+	int ret;
+
+	BUG_ON(reg > WM8993_MAX_REGISTER);
+
+	/* data is
+	 *   D15..D9 WM8993 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = reg;
+	data[1] = value >> 8;
+	data[2] = value & 0x00ff;
+
+	if (!wm8993_volatile(reg))
+		reg_cache[reg] = value;
+
+	ret = codec->hw_write(codec->control_data, data, 3);
+
+	if (ret == 3)
+		return 0;
+	if (ret < 0)
+		return ret;
+	return -EIO;
+}
+
+struct _fll_div {
+	u16 fll_fratio;
+	u16 fll_outdiv;
+	u16 fll_clk_ref_div;
+	u16 n;
+	u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static struct {
+	unsigned int min;
+	unsigned int max;
+	u16 fll_fratio;
+	int ratio;
+} fll_fratios[] = {
+	{       0,    64000, 4, 16 },
+	{   64000,   128000, 3,  8 },
+	{  128000,   256000, 2,  4 },
+	{  256000,  1000000, 1,  2 },
+	{ 1000000, 13500000, 0,  1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+		       unsigned int Fout)
+{
+	u64 Kpart;
+	unsigned int K, Ndiv, Nmod, target;
+	unsigned int div;
+	int i;
+
+	/* Fref must be <=13.5MHz */
+	div = 1;
+	fll_div->fll_clk_ref_div = 0;
+	while ((Fref / div) > 13500000) {
+		div *= 2;
+		fll_div->fll_clk_ref_div++;
+
+		if (div > 8) {
+			pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+			       Fref);
+			return -EINVAL;
+		}
+	}
+
+	pr_debug("Fref=%u Fout=%u\n", Fref, Fout);
+
+	/* Apply the division for our remaining calculations */
+	Fref /= div;
+
+	/* Fvco should be 90-100MHz; don't check the upper bound */
+	div = 0;
+	target = Fout * 2;
+	while (target < 90000000) {
+		div++;
+		target *= 2;
+		if (div > 7) {
+			pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+			       Fout);
+			return -EINVAL;
+		}
+	}
+	fll_div->fll_outdiv = div;
+
+	pr_debug("Fvco=%dHz\n", target);
+
+	/* Find an appropraite FLL_FRATIO and factor it out of the target */
+	for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+		if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+			fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+			target /= fll_fratios[i].ratio;
+			break;
+		}
+	}
+	if (i == ARRAY_SIZE(fll_fratios)) {
+		pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+		return -EINVAL;
+	}
+
+	/* Now, calculate N.K */
+	Ndiv = target / Fref;
+
+	fll_div->n = Ndiv;
+	Nmod = target % Fref;
+	pr_debug("Nmod=%d\n", Nmod);
+
+	/* Calculate fractional part - scale up so we can round. */
+	Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, Fref);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	fll_div->k = K / 10;
+
+	pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n",
+		 fll_div->n, fll_div->k,
+		 fll_div->fll_fratio, fll_div->fll_outdiv,
+		 fll_div->fll_clk_ref_div);
+
+	return 0;
+}
+
+static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id,
+			  unsigned int Fref, unsigned int Fout)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8993_priv *wm8993 = codec->private_data;
+	u16 reg1, reg4, reg5;
+	struct _fll_div fll_div;
+	int ret;
+
+	/* Any change? */
+	if (Fref == wm8993->fll_fref && Fout == wm8993->fll_fout)
+		return 0;
+
+	/* Disable the FLL */
+	if (Fout == 0) {
+		dev_dbg(codec->dev, "FLL disabled\n");
+		wm8993->fll_fref = 0;
+		wm8993->fll_fout = 0;
+
+		reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1);
+		reg1 &= ~WM8993_FLL_ENA;
+		wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+		return 0;
+	}
+
+	ret = fll_factors(&fll_div, Fref, Fout);
+	if (ret != 0)
+		return ret;
+
+	reg5 = wm8993_read(codec, WM8993_FLL_CONTROL_5);
+	reg5 &= ~WM8993_FLL_CLK_SRC_MASK;
+
+	switch (fll_id) {
+	case WM8993_FLL_MCLK:
+		break;
+
+	case WM8993_FLL_LRCLK:
+		reg5 |= 1;
+		break;
+
+	case WM8993_FLL_BCLK:
+		reg5 |= 2;
+		break;
+
+	default:
+		dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+		return -EINVAL;
+	}
+
+	/* Any FLL configuration change requires that the FLL be
+	 * disabled first. */
+	reg1 = wm8993_read(codec, WM8993_FLL_CONTROL_1);
+	reg1 &= ~WM8993_FLL_ENA;
+	wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+	/* Apply the configuration */
+	if (fll_div.k)
+		reg1 |= WM8993_FLL_FRAC_MASK;
+	else
+		reg1 &= ~WM8993_FLL_FRAC_MASK;
+	wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1);
+
+	wm8993_write(codec, WM8993_FLL_CONTROL_2,
+		     (fll_div.fll_outdiv << WM8993_FLL_OUTDIV_SHIFT) |
+		     (fll_div.fll_fratio << WM8993_FLL_FRATIO_SHIFT));
+	wm8993_write(codec, WM8993_FLL_CONTROL_3, fll_div.k);
+
+	reg4 = wm8993_read(codec, WM8993_FLL_CONTROL_4);
+	reg4 &= ~WM8993_FLL_N_MASK;
+	reg4 |= fll_div.n << WM8993_FLL_N_SHIFT;
+	wm8993_write(codec, WM8993_FLL_CONTROL_4, reg4);
+
+	reg5 &= ~WM8993_FLL_CLK_REF_DIV_MASK;
+	reg5 |= fll_div.fll_clk_ref_div << WM8993_FLL_CLK_REF_DIV_SHIFT;
+	wm8993_write(codec, WM8993_FLL_CONTROL_5, reg5);
+
+	/* Enable the FLL */
+	wm8993_write(codec, WM8993_FLL_CONTROL_1, reg1 | WM8993_FLL_ENA);
+
+	dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
+
+	wm8993->fll_fref = Fref;
+	wm8993->fll_fout = Fout;
+
+	return 0;
+}
+
+static int configure_clock(struct snd_soc_codec *codec)
+{
+	struct wm8993_priv *wm8993 = codec->private_data;
+	unsigned int reg;
+
+	/* This should be done on init() for bypass paths */
+	switch (wm8993->sysclk_source) {
+	case WM8993_SYSCLK_MCLK:
+		dev_dbg(codec->dev, "Using %dHz MCLK\n", wm8993->mclk_rate);
+
+		reg = wm8993_read(codec, WM8993_CLOCKING_2);
+		reg &= ~WM8993_SYSCLK_SRC;
+		if (wm8993->mclk_rate > 13500000) {
+			reg |= WM8993_MCLK_DIV;
+			wm8993->sysclk_rate = wm8993->mclk_rate / 2;
+		} else {
+			reg &= ~WM8993_MCLK_DIV;
+			wm8993->sysclk_rate = wm8993->mclk_rate;
+		}
+		reg &= ~WM8993_MCLK_DIV;
+		reg &= ~(WM8993_MCLK_DIV | WM8993_SYSCLK_SRC);
+		wm8993_write(codec, WM8993_CLOCKING_2, reg);
+		break;
+
+	case WM8993_SYSCLK_FLL:
+		dev_dbg(codec->dev, "Using %dHz FLL clock\n",
+			wm8993->fll_fout);
+
+		reg = wm8993_read(codec, WM8993_CLOCKING_2);
+		reg |= WM8993_SYSCLK_SRC;
+		if (wm8993->fll_fout > 13500000) {
+			reg |= WM8993_MCLK_DIV;
+			wm8993->sysclk_rate = wm8993->fll_fout / 2;
+		} else {
+			reg &= ~WM8993_MCLK_DIV;
+			wm8993->sysclk_rate = wm8993->fll_fout;
+		}
+		wm8993_write(codec, WM8993_CLOCKING_2, reg);
+		break;
+
+	default:
+		dev_err(codec->dev, "System clock not configured\n");
+		return -EINVAL;
+	}
+
+	dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm8993->sysclk_rate);
+
+	return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(sidetone_tlv, -3600, 300, 0);
+static const DECLARE_TLV_DB_SCALE(drc_comp_threash, -4500, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_comp_amp, -2250, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0);
+static const unsigned int drc_max_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 2, TLV_DB_SCALE_ITEM(1200, 600, 0),
+	3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -1800, 300, 0);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(dac_boost_tlv, 0, 600, 0);
+
+static const char *dac_deemph_text[] = {
+	"None",
+	"32kHz",
+	"44.1kHz",
+	"48kHz",
+};
+
+static const struct soc_enum dac_deemph =
+	SOC_ENUM_SINGLE(WM8993_DAC_CTRL, 4, 4, dac_deemph_text);
+
+static const char *adc_hpf_text[] = {
+	"Hi-Fi",
+	"Voice 1",
+	"Voice 2",
+	"Voice 3",
+};
+
+static const struct soc_enum adc_hpf =
+	SOC_ENUM_SINGLE(WM8993_ADC_CTRL, 5, 4, adc_hpf_text);
+
+static const char *drc_path_text[] = {
+	"ADC",
+	"DAC"
+};
+
+static const struct soc_enum drc_path =
+	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 14, 2, drc_path_text);
+
+static const char *drc_r0_text[] = {
+	"1",
+	"1/2",
+	"1/4",
+	"1/8",
+	"1/16",
+	"0",
+};
+
+static const struct soc_enum drc_r0 =
+	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 8, 6, drc_r0_text);
+
+static const char *drc_r1_text[] = {
+	"1",
+	"1/2",
+	"1/4",
+	"1/8",
+	"0",
+};
+
+static const struct soc_enum drc_r1 =
+	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_4, 13, 5, drc_r1_text);
+
+static const char *drc_attack_text[] = {
+	"Reserved",
+	"181us",
+	"363us",
+	"726us",
+	"1.45ms",
+	"2.9ms",
+	"5.8ms",
+	"11.6ms",
+	"23.2ms",
+	"46.4ms",
+	"92.8ms",
+	"185.6ms",
+};
+
+static const struct soc_enum drc_attack =
+	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_2, 12, 12, drc_attack_text);
+
+static const char *drc_decay_text[] = {
+	"186ms",
+	"372ms",
+	"743ms",
+	"1.49s",
+	"2.97ms",
+	"5.94ms",
+	"11.89ms",
+	"23.78ms",
+	"47.56ms",
+};
+
+static const struct soc_enum drc_decay =
+	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_2, 8, 9, drc_decay_text);
+
+static const char *drc_ff_text[] = {
+	"5 samples",
+	"9 samples",
+};
+
+static const struct soc_enum drc_ff =
+	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 7, 2, drc_ff_text);
+
+static const char *drc_qr_rate_text[] = {
+	"0.725ms",
+	"1.45ms",
+	"5.8ms",
+};
+
+static const struct soc_enum drc_qr_rate =
+	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_3, 0, 3, drc_qr_rate_text);
+
+static const char *drc_smooth_text[] = {
+	"Low",
+	"Medium",
+	"High",
+};
+
+static const struct soc_enum drc_smooth =
+	SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 4, 3, drc_smooth_text);
+
+static const struct snd_kcontrol_new wm8993_snd_controls[] = {
+SOC_DOUBLE_TLV("Digital Sidetone Volume", WM8993_DIGITAL_SIDE_TONE,
+	       5, 9, 12, 0, sidetone_tlv),
+
+SOC_SINGLE("DRC Switch", WM8993_DRC_CONTROL_1, 15, 1, 0),
+SOC_ENUM("DRC Path", drc_path),
+SOC_SINGLE_TLV("DRC Compressor Threashold Volume", WM8993_DRC_CONTROL_2,
+	       2, 60, 1, drc_comp_threash),
+SOC_SINGLE_TLV("DRC Compressor Amplitude Volume", WM8993_DRC_CONTROL_3,
+	       11, 30, 1, drc_comp_amp),
+SOC_ENUM("DRC R0", drc_r0),
+SOC_ENUM("DRC R1", drc_r1),
+SOC_SINGLE_TLV("DRC Minimum Volume", WM8993_DRC_CONTROL_1, 2, 3, 1,
+	       drc_min_tlv),
+SOC_SINGLE_TLV("DRC Maximum Volume", WM8993_DRC_CONTROL_1, 0, 3, 0,
+	       drc_max_tlv),
+SOC_ENUM("DRC Attack Rate", drc_attack),
+SOC_ENUM("DRC Decay Rate", drc_decay),
+SOC_ENUM("DRC FF Delay", drc_ff),
+SOC_SINGLE("DRC Anti-clip Switch", WM8993_DRC_CONTROL_1, 9, 1, 0),
+SOC_SINGLE("DRC Quick Release Switch", WM8993_DRC_CONTROL_1, 10, 1, 0),
+SOC_SINGLE_TLV("DRC Quick Release Volume", WM8993_DRC_CONTROL_3, 2, 3, 0,
+	       drc_qr_tlv),
+SOC_ENUM("DRC Quick Release Rate", drc_qr_rate),
+SOC_SINGLE("DRC Smoothing Switch", WM8993_DRC_CONTROL_1, 11, 1, 0),
+SOC_SINGLE("DRC Smoothing Hysteresis Switch", WM8993_DRC_CONTROL_1, 8, 1, 0),
+SOC_ENUM("DRC Smoothing Hysteresis Threashold", drc_smooth),
+SOC_SINGLE_TLV("DRC Startup Volume", WM8993_DRC_CONTROL_4, 8, 18, 0,
+	       drc_startup_tlv),
+
+SOC_SINGLE("EQ Switch", WM8993_EQ1, 0, 1, 0),
+
+SOC_DOUBLE_R_TLV("Capture Volume", WM8993_LEFT_ADC_DIGITAL_VOLUME,
+		 WM8993_RIGHT_ADC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv),
+SOC_SINGLE("ADC High Pass Filter Switch", WM8993_ADC_CTRL, 8, 1, 0),
+SOC_ENUM("ADC High Pass Filter Mode", adc_hpf),
+
+SOC_DOUBLE_R_TLV("Playback Volume", WM8993_LEFT_DAC_DIGITAL_VOLUME,
+		 WM8993_RIGHT_DAC_DIGITAL_VOLUME, 1, 96, 0, digital_tlv),
+SOC_SINGLE_TLV("Playback Boost Volume", WM8993_AUDIO_INTERFACE_2, 10, 3, 0,
+	       dac_boost_tlv),
+SOC_ENUM("DAC Deemphasis", dac_deemph),
+
+SOC_SINGLE_TLV("SPKL DAC Volume", WM8993_SPKMIXL_ATTENUATION,
+	       2, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("SPKR DAC Volume", WM8993_SPKMIXR_ATTENUATION,
+	       2, 1, 1, wm_hubs_spkmix_tlv),
+};
+
+static const struct snd_kcontrol_new wm8993_eq_controls[] = {
+SOC_SINGLE_TLV("EQ1 Volume", WM8993_EQ2, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Volume", WM8993_EQ3, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Volume", WM8993_EQ4, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Volume", WM8993_EQ5, 0, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ5 Volume", WM8993_EQ6, 0, 24, 0, eq_tlv),
+};
+
+static int clk_sys_event(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		return configure_clock(codec);
+
+	case SND_SOC_DAPM_POST_PMD:
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * When used with DAC outputs only the WM8993 charge pump supports
+ * operation in class W mode, providing very low power consumption
+ * when used with digital sources.  Enable and disable this mode
+ * automatically depending on the mixer configuration.
+ *
+ * Currently the only supported paths are the direct DAC->headphone
+ * paths (which provide minimum power consumption anyway).
+ */
+static int class_w_put(struct snd_kcontrol *kcontrol,
+		       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
+	struct snd_soc_codec *codec = widget->codec;
+	struct wm8993_priv *wm8993 = codec->private_data;
+	int ret;
+
+	/* Turn it off if we're using the main output mixer */
+	if (ucontrol->value.integer.value[0] == 0) {
+		if (wm8993->class_w_users == 0) {
+			dev_dbg(codec->dev, "Disabling Class W\n");
+			snd_soc_update_bits(codec, WM8993_CLASS_W_0,
+					    WM8993_CP_DYN_FREQ |
+					    WM8993_CP_DYN_V,
+					    0);
+		}
+		wm8993->class_w_users++;
+	}
+
+	/* Implement the change */
+	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+	/* Enable it if we're using the direct DAC path */
+	if (ucontrol->value.integer.value[0] == 1) {
+		if (wm8993->class_w_users == 1) {
+			dev_dbg(codec->dev, "Enabling Class W\n");
+			snd_soc_update_bits(codec, WM8993_CLASS_W_0,
+					    WM8993_CP_DYN_FREQ |
+					    WM8993_CP_DYN_V,
+					    WM8993_CP_DYN_FREQ |
+					    WM8993_CP_DYN_V);
+		}
+		wm8993->class_w_users--;
+	}
+
+	dev_dbg(codec->dev, "Indirect DAC use count now %d\n",
+		wm8993->class_w_users);
+
+	return ret;
+}
+
+#define SOC_DAPM_ENUM_W(xname, xenum) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_enum_double, \
+	.get = snd_soc_dapm_get_enum_double, \
+	.put = class_w_put, \
+	.private_value = (unsigned long)&xenum }
+
+static const char *hp_mux_text[] = {
+	"Mixer",
+	"DAC",
+};
+
+static const struct soc_enum hpl_enum =
+	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER1, 8, 2, hp_mux_text);
+
+static const struct snd_kcontrol_new hpl_mux =
+	SOC_DAPM_ENUM_W("Left Headphone Mux", hpl_enum);
+
+static const struct soc_enum hpr_enum =
+	SOC_ENUM_SINGLE(WM8993_OUTPUT_MIXER2, 8, 2, hp_mux_text);
+
+static const struct snd_kcontrol_new hpr_mux =
+	SOC_DAPM_ENUM_W("Right Headphone Mux", hpr_enum);
+
+static const struct snd_kcontrol_new left_speaker_mixer[] = {
+SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),
+SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 3, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_mixer[] = {
+SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
+SOC_DAPM_SINGLE("IN1RP Switch", WM8993_SPEAKER_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8993_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8993_BUS_CONTROL_1, 1, 0, clk_sys_event,
+		    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("TOCLK", WM8993_CLOCKING_1, 14, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8993_CLOCKING_3, 0, 0, NULL, 0),
+
+
+SND_SOC_DAPM_ADC("ADCL", "Capture", WM8993_POWER_MANAGEMENT_2, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", "Capture", WM8993_POWER_MANAGEMENT_2, 0, 0),
+
+SND_SOC_DAPM_DAC("DACL", "Playback", WM8993_POWER_MANAGEMENT_3, 1, 0),
+SND_SOC_DAPM_DAC("DACR", "Playback", WM8993_POWER_MANAGEMENT_3, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux),
+SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux),
+
+SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,
+		   left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
+SND_SOC_DAPM_MIXER("SPKR", WM8993_POWER_MANAGEMENT_3, 9, 0,
+		   right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
+
+};
+
+static const struct snd_soc_dapm_route routes[] = {
+	{ "ADCL", NULL, "CLK_SYS" },
+	{ "ADCL", NULL, "CLK_DSP" },
+	{ "ADCR", NULL, "CLK_SYS" },
+	{ "ADCR", NULL, "CLK_DSP" },
+
+	{ "DACL", NULL, "CLK_SYS" },
+	{ "DACL", NULL, "CLK_DSP" },
+	{ "DACR", NULL, "CLK_SYS" },
+	{ "DACR", NULL, "CLK_DSP" },
+
+	{ "Left Output Mixer", "DAC Switch", "DACL" },
+
+	{ "Right Output Mixer", "DAC Switch", "DACR" },
+
+	{ "Left Output PGA", NULL, "CLK_SYS" },
+
+	{ "Right Output PGA", NULL, "CLK_SYS" },
+
+	{ "SPKL", "DAC Switch", "DACL" },
+	{ "SPKL", NULL, "CLK_SYS" },
+
+	{ "SPKR", "DAC Switch", "DACR" },
+	{ "SPKR", NULL, "CLK_SYS" },
+
+	{ "Left Headphone Mux", "DAC", "DACL" },
+	{ "Right Headphone Mux", "DAC", "DACR" },
+};
+
+static int wm8993_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	struct wm8993_priv *wm8993 = codec->private_data;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+		/* VMID=2*40k */
+		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+				    WM8993_VMID_SEL_MASK, 0x2);
+		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2,
+				    WM8993_TSHUT_ENA, WM8993_TSHUT_ENA);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* Bring up VMID with fast soft start */
+			snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+					    WM8993_STARTUP_BIAS_ENA |
+					    WM8993_VMID_BUF_ENA |
+					    WM8993_VMID_RAMP_MASK |
+					    WM8993_BIAS_SRC,
+					    WM8993_STARTUP_BIAS_ENA |
+					    WM8993_VMID_BUF_ENA |
+					    WM8993_VMID_RAMP_MASK |
+					    WM8993_BIAS_SRC);
+
+			/* If either line output is single ended we
+			 * need the VMID buffer */
+			if (!wm8993->pdata.lineout1_diff ||
+			    !wm8993->pdata.lineout2_diff)
+				snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+						 WM8993_LINEOUT_VMID_BUF_ENA,
+						 WM8993_LINEOUT_VMID_BUF_ENA);
+
+			/* VMID=2*40k */
+			snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+					    WM8993_VMID_SEL_MASK |
+					    WM8993_BIAS_ENA,
+					    WM8993_BIAS_ENA | 0x2);
+			msleep(32);
+
+			/* Switch to normal bias */
+			snd_soc_update_bits(codec, WM8993_ANTIPOP2,
+					    WM8993_BIAS_SRC |
+					    WM8993_STARTUP_BIAS_ENA, 0);
+		}
+
+		/* VMID=2*240k */
+		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+				    WM8993_VMID_SEL_MASK, 0x4);
+
+		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_2,
+				    WM8993_TSHUT_ENA, 0);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		snd_soc_update_bits(codec, WM8993_ANTIPOP1,
+				    WM8993_LINEOUT_VMID_BUF_ENA, 0);
+
+		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+				    WM8993_VMID_SEL_MASK | WM8993_BIAS_ENA,
+				    0);
+		break;
+	}
+
+	codec->bias_level = level;
+
+	return 0;
+}
+
+static int wm8993_set_sysclk(struct snd_soc_dai *codec_dai,
+			     int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm8993_priv *wm8993 = codec->private_data;
+
+	switch (clk_id) {
+	case WM8993_SYSCLK_MCLK:
+		wm8993->mclk_rate = freq;
+	case WM8993_SYSCLK_FLL:
+		wm8993->sysclk_source = clk_id;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm8993_set_dai_fmt(struct snd_soc_dai *dai,
+			      unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8993_priv *wm8993 = codec->private_data;
+	unsigned int aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1);
+	unsigned int aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4);
+
+	aif1 &= ~(WM8993_BCLK_DIR | WM8993_AIF_BCLK_INV |
+		  WM8993_AIF_LRCLK_INV | WM8993_AIF_FMT_MASK);
+	aif4 &= ~WM8993_LRCLK_DIR;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		wm8993->master = 0;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		aif4 |= WM8993_LRCLK_DIR;
+		wm8993->master = 1;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		aif1 |= WM8993_BCLK_DIR;
+		wm8993->master = 1;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		aif1 |= WM8993_BCLK_DIR;
+		aif4 |= WM8993_LRCLK_DIR;
+		wm8993->master = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_B:
+		aif1 |= WM8993_AIF_LRCLK_INV;
+	case SND_SOC_DAIFMT_DSP_A:
+		aif1 |= 0x18;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		aif1 |= 0x10;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		aif1 |= 0x8;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+	case SND_SOC_DAIFMT_DSP_B:
+		/* frame inversion not valid for DSP modes */
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif1 |= WM8993_AIF_BCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_RIGHT_J:
+	case SND_SOC_DAIFMT_LEFT_J:
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_IF:
+			aif1 |= WM8993_AIF_BCLK_INV | WM8993_AIF_LRCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif1 |= WM8993_AIF_BCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_NB_IF:
+			aif1 |= WM8993_AIF_LRCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+	wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+
+	return 0;
+}
+
+static int wm8993_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm8993_priv *wm8993 = codec->private_data;
+	int ret, i, best, best_val, cur_val;
+	unsigned int clocking1, clocking3, aif1, aif4;
+
+	clocking1 = wm8993_read(codec, WM8993_CLOCKING_1);
+	clocking1 &= ~WM8993_BCLK_DIV_MASK;
+
+	clocking3 = wm8993_read(codec, WM8993_CLOCKING_3);
+	clocking3 &= ~(WM8993_CLK_SYS_RATE_MASK | WM8993_SAMPLE_RATE_MASK);
+
+	aif1 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_1);
+	aif1 &= ~WM8993_AIF_WL_MASK;
+
+	aif4 = wm8993_read(codec, WM8993_AUDIO_INTERFACE_4);
+	aif4 &= ~WM8993_LRCLK_RATE_MASK;
+
+	/* What BCLK do we need? */
+	wm8993->fs = params_rate(params);
+	wm8993->bclk = 2 * wm8993->fs;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		wm8993->bclk *= 16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		wm8993->bclk *= 20;
+		aif1 |= 0x8;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		wm8993->bclk *= 24;
+		aif1 |= 0x10;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		wm8993->bclk *= 32;
+		aif1 |= 0x18;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm8993->bclk);
+
+	ret = configure_clock(codec);
+	if (ret != 0)
+		return ret;
+
+	/* Select nearest CLK_SYS_RATE */
+	best = 0;
+	best_val = abs((wm8993->sysclk_rate / clk_sys_rates[0].ratio)
+		       - wm8993->fs);
+	for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) {
+		cur_val = abs((wm8993->sysclk_rate /
+			       clk_sys_rates[i].ratio) - wm8993->fs);;
+		if (cur_val < best_val) {
+			best = i;
+			best_val = cur_val;
+		}
+	}
+	dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+		clk_sys_rates[best].ratio);
+	clocking3 |= (clk_sys_rates[best].clk_sys_rate
+		      << WM8993_CLK_SYS_RATE_SHIFT);
+
+	/* SAMPLE_RATE */
+	best = 0;
+	best_val = abs(wm8993->fs - sample_rates[0].rate);
+	for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+		/* Closest match */
+		cur_val = abs(wm8993->fs - sample_rates[i].rate);
+		if (cur_val < best_val) {
+			best = i;
+			best_val = cur_val;
+		}
+	}
+	dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+		sample_rates[best].rate);
+	clocking3 |= (sample_rates[best].sample_rate
+		      << WM8993_SAMPLE_RATE_SHIFT);
+
+	/* BCLK_DIV */
+	best = 0;
+	best_val = INT_MAX;
+	for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+		cur_val = ((wm8993->sysclk_rate * 10) / bclk_divs[i].div)
+			- wm8993->bclk;
+		if (cur_val < 0) /* Table is sorted */
+			break;
+		if (cur_val < best_val) {
+			best = i;
+			best_val = cur_val;
+		}
+	}
+	wm8993->bclk = (wm8993->sysclk_rate * 10) / bclk_divs[best].div;
+	dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+		bclk_divs[best].div, wm8993->bclk);
+	clocking1 |= bclk_divs[best].bclk_div << WM8993_BCLK_DIV_SHIFT;
+
+	/* LRCLK is a simple fraction of BCLK */
+	dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm8993->bclk / wm8993->fs);
+	aif4 |= wm8993->bclk / wm8993->fs;
+
+	wm8993_write(codec, WM8993_CLOCKING_1, clocking1);
+	wm8993_write(codec, WM8993_CLOCKING_3, clocking3);
+	wm8993_write(codec, WM8993_AUDIO_INTERFACE_1, aif1);
+	wm8993_write(codec, WM8993_AUDIO_INTERFACE_4, aif4);
+
+	/* ReTune Mobile? */
+	if (wm8993->pdata.num_retune_configs) {
+		u16 eq1 = wm8993_read(codec, WM8993_EQ1);
+		struct wm8993_retune_mobile_setting *s;
+
+		best = 0;
+		best_val = abs(wm8993->pdata.retune_configs[0].rate
+			       - wm8993->fs);
+		for (i = 0; i < wm8993->pdata.num_retune_configs; i++) {
+			cur_val = abs(wm8993->pdata.retune_configs[i].rate
+				      - wm8993->fs);
+			if (cur_val < best_val) {
+				best_val = cur_val;
+				best = i;
+			}
+		}
+		s = &wm8993->pdata.retune_configs[best];
+
+		dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
+			s->name, s->rate);
+
+		/* Disable EQ while we reconfigure */
+		snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, 0);
+
+		for (i = 1; i < ARRAY_SIZE(s->config); i++)
+			wm8993_write(codec, WM8993_EQ1 + i, s->config[i]);
+
+		snd_soc_update_bits(codec, WM8993_EQ1, WM8993_EQ_ENA, eq1);
+	}
+
+	return 0;
+}
+
+static int wm8993_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	unsigned int reg;
+
+	reg = wm8993_read(codec, WM8993_DAC_CTRL);
+
+	if (mute)
+		reg |= WM8993_DAC_MUTE;
+	else
+		reg &= ~WM8993_DAC_MUTE;
+
+	wm8993_write(codec, WM8993_DAC_CTRL, reg);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops wm8993_ops = {
+	.set_sysclk = wm8993_set_sysclk,
+	.set_fmt = wm8993_set_dai_fmt,
+	.hw_params = wm8993_hw_params,
+	.digital_mute = wm8993_digital_mute,
+	.set_pll = wm8993_set_fll,
+};
+
+#define WM8993_RATES SNDRV_PCM_RATE_8000_48000
+
+#define WM8993_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+			SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S24_LE |\
+			SNDRV_PCM_FMTBIT_S32_LE)
+
+struct snd_soc_dai wm8993_dai = {
+	.name = "WM8993",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM8993_RATES,
+		.formats = WM8993_FORMATS,
+	},
+	.capture = {
+		 .stream_name = "Capture",
+		 .channels_min = 1,
+		 .channels_max = 2,
+		 .rates = WM8993_RATES,
+		 .formats = WM8993_FORMATS,
+	 },
+	.ops = &wm8993_ops,
+	.symmetric_rates = 1,
+};
+EXPORT_SYMBOL_GPL(wm8993_dai);
+
+static struct snd_soc_codec *wm8993_codec;
+
+static int wm8993_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	struct wm8993_priv *wm8993;
+	int ret = 0;
+
+	if (!wm8993_codec) {
+		dev_err(&pdev->dev, "I2C device not yet probed\n");
+		goto err;
+	}
+
+	socdev->card->codec = wm8993_codec;
+	codec = wm8993_codec;
+	wm8993 = codec->private_data;
+
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms\n");
+		goto err;
+	}
+
+	snd_soc_add_controls(codec, wm8993_snd_controls,
+			     ARRAY_SIZE(wm8993_snd_controls));
+	if (wm8993->pdata.num_retune_configs != 0) {
+		dev_dbg(codec->dev, "Using ReTune Mobile\n");
+	} else {
+		dev_dbg(codec->dev, "No ReTune Mobile, using normal EQ\n");
+		snd_soc_add_controls(codec, wm8993_eq_controls,
+				     ARRAY_SIZE(wm8993_eq_controls));
+	}
+
+	snd_soc_dapm_new_controls(codec, wm8993_dapm_widgets,
+				  ARRAY_SIZE(wm8993_dapm_widgets));
+	wm_hubs_add_analogue_controls(codec);
+
+	snd_soc_dapm_add_routes(codec, routes, ARRAY_SIZE(routes));
+	wm_hubs_add_analogue_routes(codec, wm8993->pdata.lineout1_diff,
+				    wm8993->pdata.lineout2_diff);
+
+	snd_soc_dapm_new_widgets(codec);
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card\n");
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+err:
+	return ret;
+}
+
+static int wm8993_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_wm8993 = {
+	.probe = 	wm8993_probe,
+	.remove = 	wm8993_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8993);
+
+static int wm8993_i2c_probe(struct i2c_client *i2c,
+			    const struct i2c_device_id *id)
+{
+	struct wm8993_priv *wm8993;
+	struct snd_soc_codec *codec;
+	unsigned int val;
+	int ret;
+
+	if (wm8993_codec) {
+		dev_err(&i2c->dev, "A WM8993 is already registered\n");
+		return -EINVAL;
+	}
+
+	wm8993 = kzalloc(sizeof(struct wm8993_priv), GFP_KERNEL);
+	if (wm8993 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8993->codec;
+	if (i2c->dev.platform_data)
+		memcpy(&wm8993->pdata, i2c->dev.platform_data,
+		       sizeof(wm8993->pdata));
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->name = "WM8993";
+	codec->read = wm8993_read;
+	codec->write = wm8993_write;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+	codec->reg_cache = wm8993->reg_cache;
+	codec->reg_cache_size = ARRAY_SIZE(wm8993->reg_cache);
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8993_set_bias_level;
+	codec->dai = &wm8993_dai;
+	codec->num_dai = 1;
+	codec->private_data = wm8993;
+
+	memcpy(wm8993->reg_cache, wm8993_reg_defaults,
+	       sizeof(wm8993->reg_cache));
+
+	i2c_set_clientdata(i2c, wm8993);
+	codec->control_data = i2c;
+	wm8993_codec = codec;
+
+	codec->dev = &i2c->dev;
+
+	val = wm8993_read_hw(codec, WM8993_SOFTWARE_RESET);
+	if (val != wm8993_reg_defaults[WM8993_SOFTWARE_RESET]) {
+		dev_err(codec->dev, "Invalid ID register value %x\n", val);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = wm8993_write(codec, WM8993_SOFTWARE_RESET, 0xffff);
+	if (ret != 0)
+		goto err;
+
+	/* By default we're using the output mixers */
+	wm8993->class_w_users = 2;
+
+	/* Latch volume update bits and default ZC on */
+	snd_soc_update_bits(codec, WM8993_RIGHT_DAC_DIGITAL_VOLUME,
+			    WM8993_DAC_VU, WM8993_DAC_VU);
+	snd_soc_update_bits(codec, WM8993_RIGHT_ADC_DIGITAL_VOLUME,
+			    WM8993_ADC_VU, WM8993_ADC_VU);
+
+	/* Manualy manage the HPOUT sequencing for independent stereo
+	 * control. */
+	snd_soc_update_bits(codec, WM8993_ANALOGUE_HP_0,
+			    WM8993_HPOUT1_AUTO_PU, 0);
+
+	/* Use automatic clock configuration */
+	snd_soc_update_bits(codec, WM8993_CLOCKING_4, WM8993_SR_MODE, 0);
+
+	if (!wm8993->pdata.lineout1_diff)
+		snd_soc_update_bits(codec, WM8993_LINE_MIXER1,
+				    WM8993_LINEOUT1_MODE,
+				    WM8993_LINEOUT1_MODE);
+	if (!wm8993->pdata.lineout2_diff)
+		snd_soc_update_bits(codec, WM8993_LINE_MIXER2,
+				    WM8993_LINEOUT2_MODE,
+				    WM8993_LINEOUT2_MODE);
+
+	if (wm8993->pdata.lineout1fb)
+		snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+				    WM8993_LINEOUT1_FB, WM8993_LINEOUT1_FB);
+
+	if (wm8993->pdata.lineout2fb)
+		snd_soc_update_bits(codec, WM8993_ADDITIONAL_CONTROL,
+				    WM8993_LINEOUT2_FB, WM8993_LINEOUT2_FB);
+
+	/* Apply the microphone bias/detection configuration - the
+	 * platform data is directly applicable to the register. */
+	snd_soc_update_bits(codec, WM8993_MICBIAS,
+			    WM8993_JD_SCTHR_MASK | WM8993_JD_THR_MASK |
+			    WM8993_MICB1_LVL | WM8993_MICB2_LVL,
+			    wm8993->pdata.jd_scthr << WM8993_JD_SCTHR_SHIFT |
+			    wm8993->pdata.jd_thr << WM8993_JD_THR_SHIFT |
+			    wm8993->pdata.micbias1_lvl |
+			    wm8993->pdata.micbias1_lvl << 1);
+
+	ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	if (ret != 0)
+		goto err;
+
+	wm8993_dai.dev = codec->dev;
+
+	ret = snd_soc_register_dai(&wm8993_dai);
+	if (ret != 0)
+		goto err_bias;
+
+	ret = snd_soc_register_codec(codec);
+
+	return 0;
+
+err_bias:
+	wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
+err:
+	wm8993_codec = NULL;
+	kfree(wm8993);
+	return ret;
+}
+
+static int wm8993_i2c_remove(struct i2c_client *client)
+{
+	struct wm8993_priv *wm8993 = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&wm8993->codec);
+	snd_soc_unregister_dai(&wm8993_dai);
+
+	wm8993_set_bias_level(&wm8993->codec, SND_SOC_BIAS_OFF);
+	kfree(wm8993);
+
+	return 0;
+}
+
+static const struct i2c_device_id wm8993_i2c_id[] = {
+	{ "wm8993", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8993_i2c_id);
+
+static struct i2c_driver wm8993_i2c_driver = {
+	.driver = {
+		.name = "WM8993",
+		.owner = THIS_MODULE,
+	},
+	.probe = wm8993_i2c_probe,
+	.remove = wm8993_i2c_remove,
+	.id_table = wm8993_i2c_id,
+};
+
+
+static int __init wm8993_modinit(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&wm8993_i2c_driver);
+	if (ret != 0)
+		pr_err("WM8993: Unable to register I2C driver: %d\n", ret);
+
+	return ret;
+}
+module_init(wm8993_modinit);
+
+static void __exit wm8993_exit(void)
+{
+	i2c_del_driver(&wm8993_i2c_driver);
+}
+module_exit(wm8993_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM8993 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8993.h b/sound/soc/codecs/wm8993.h
new file mode 100644
index 0000000..30e71ca
--- /dev/null
+++ b/sound/soc/codecs/wm8993.h
@@ -0,0 +1,2132 @@
+#ifndef WM8993_H
+#define WM8993_H
+
+extern struct snd_soc_dai wm8993_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8993;
+
+#define WM8993_SYSCLK_MCLK     1
+#define WM8993_SYSCLK_FLL      2
+
+#define WM8993_FLL_MCLK  1
+#define WM8993_FLL_BCLK  2
+#define WM8993_FLL_LRCLK 3
+
+/*
+ * Register values.
+ */
+#define WM8993_SOFTWARE_RESET                   0x00
+#define WM8993_POWER_MANAGEMENT_1               0x01
+#define WM8993_POWER_MANAGEMENT_2               0x02
+#define WM8993_POWER_MANAGEMENT_3               0x03
+#define WM8993_AUDIO_INTERFACE_1                0x04
+#define WM8993_AUDIO_INTERFACE_2                0x05
+#define WM8993_CLOCKING_1                       0x06
+#define WM8993_CLOCKING_2                       0x07
+#define WM8993_AUDIO_INTERFACE_3                0x08
+#define WM8993_AUDIO_INTERFACE_4                0x09
+#define WM8993_DAC_CTRL                         0x0A
+#define WM8993_LEFT_DAC_DIGITAL_VOLUME          0x0B
+#define WM8993_RIGHT_DAC_DIGITAL_VOLUME         0x0C
+#define WM8993_DIGITAL_SIDE_TONE                0x0D
+#define WM8993_ADC_CTRL                         0x0E
+#define WM8993_LEFT_ADC_DIGITAL_VOLUME          0x0F
+#define WM8993_RIGHT_ADC_DIGITAL_VOLUME         0x10
+#define WM8993_GPIO_CTRL_1                      0x12
+#define WM8993_GPIO1                            0x13
+#define WM8993_IRQ_DEBOUNCE                     0x14
+#define WM8993_GPIOCTRL_2                       0x16
+#define WM8993_GPIO_POL                         0x17
+#define WM8993_LEFT_LINE_INPUT_1_2_VOLUME       0x18
+#define WM8993_LEFT_LINE_INPUT_3_4_VOLUME       0x19
+#define WM8993_RIGHT_LINE_INPUT_1_2_VOLUME      0x1A
+#define WM8993_RIGHT_LINE_INPUT_3_4_VOLUME      0x1B
+#define WM8993_LEFT_OUTPUT_VOLUME               0x1C
+#define WM8993_RIGHT_OUTPUT_VOLUME              0x1D
+#define WM8993_LINE_OUTPUTS_VOLUME              0x1E
+#define WM8993_HPOUT2_VOLUME                    0x1F
+#define WM8993_LEFT_OPGA_VOLUME                 0x20
+#define WM8993_RIGHT_OPGA_VOLUME                0x21
+#define WM8993_SPKMIXL_ATTENUATION              0x22
+#define WM8993_SPKMIXR_ATTENUATION              0x23
+#define WM8993_SPKOUT_MIXERS                    0x24
+#define WM8993_SPKOUT_BOOST                     0x25
+#define WM8993_SPEAKER_VOLUME_LEFT              0x26
+#define WM8993_SPEAKER_VOLUME_RIGHT             0x27
+#define WM8993_INPUT_MIXER2                     0x28
+#define WM8993_INPUT_MIXER3                     0x29
+#define WM8993_INPUT_MIXER4                     0x2A
+#define WM8993_INPUT_MIXER5                     0x2B
+#define WM8993_INPUT_MIXER6                     0x2C
+#define WM8993_OUTPUT_MIXER1                    0x2D
+#define WM8993_OUTPUT_MIXER2                    0x2E
+#define WM8993_OUTPUT_MIXER3                    0x2F
+#define WM8993_OUTPUT_MIXER4                    0x30
+#define WM8993_OUTPUT_MIXER5                    0x31
+#define WM8993_OUTPUT_MIXER6                    0x32
+#define WM8993_HPOUT2_MIXER                     0x33
+#define WM8993_LINE_MIXER1                      0x34
+#define WM8993_LINE_MIXER2                      0x35
+#define WM8993_SPEAKER_MIXER                    0x36
+#define WM8993_ADDITIONAL_CONTROL               0x37
+#define WM8993_ANTIPOP1                         0x38
+#define WM8993_ANTIPOP2                         0x39
+#define WM8993_MICBIAS                          0x3A
+#define WM8993_FLL_CONTROL_1                    0x3C
+#define WM8993_FLL_CONTROL_2                    0x3D
+#define WM8993_FLL_CONTROL_3                    0x3E
+#define WM8993_FLL_CONTROL_4                    0x3F
+#define WM8993_FLL_CONTROL_5                    0x40
+#define WM8993_CLOCKING_3                       0x41
+#define WM8993_CLOCKING_4                       0x42
+#define WM8993_MW_SLAVE_CONTROL                 0x43
+#define WM8993_BUS_CONTROL_1                    0x45
+#define WM8993_WRITE_SEQUENCER_0                0x46
+#define WM8993_WRITE_SEQUENCER_1                0x47
+#define WM8993_WRITE_SEQUENCER_2                0x48
+#define WM8993_WRITE_SEQUENCER_3                0x49
+#define WM8993_WRITE_SEQUENCER_4                0x4A
+#define WM8993_WRITE_SEQUENCER_5                0x4B
+#define WM8993_CHARGE_PUMP_1                    0x4C
+#define WM8993_CLASS_W_0                        0x51
+#define WM8993_DC_SERVO_0                       0x54
+#define WM8993_DC_SERVO_1                       0x55
+#define WM8993_DC_SERVO_3                       0x57
+#define WM8993_DC_SERVO_READBACK_0              0x58
+#define WM8993_DC_SERVO_READBACK_1              0x59
+#define WM8993_DC_SERVO_READBACK_2              0x5A
+#define WM8993_ANALOGUE_HP_0                    0x60
+#define WM8993_EQ1                              0x62
+#define WM8993_EQ2                              0x63
+#define WM8993_EQ3                              0x64
+#define WM8993_EQ4                              0x65
+#define WM8993_EQ5                              0x66
+#define WM8993_EQ6                              0x67
+#define WM8993_EQ7                              0x68
+#define WM8993_EQ8                              0x69
+#define WM8993_EQ9                              0x6A
+#define WM8993_EQ10                             0x6B
+#define WM8993_EQ11                             0x6C
+#define WM8993_EQ12                             0x6D
+#define WM8993_EQ13                             0x6E
+#define WM8993_EQ14                             0x6F
+#define WM8993_EQ15                             0x70
+#define WM8993_EQ16                             0x71
+#define WM8993_EQ17                             0x72
+#define WM8993_EQ18                             0x73
+#define WM8993_EQ19                             0x74
+#define WM8993_EQ20                             0x75
+#define WM8993_EQ21                             0x76
+#define WM8993_EQ22                             0x77
+#define WM8993_EQ23                             0x78
+#define WM8993_EQ24                             0x79
+#define WM8993_DIGITAL_PULLS                    0x7A
+#define WM8993_DRC_CONTROL_1                    0x7B
+#define WM8993_DRC_CONTROL_2                    0x7C
+#define WM8993_DRC_CONTROL_3                    0x7D
+#define WM8993_DRC_CONTROL_4                    0x7E
+
+#define WM8993_REGISTER_COUNT                   0x7F
+#define WM8993_MAX_REGISTER                     0x7E
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM8993_SW_RESET_MASK                    0xFFFF  /* SW_RESET - [15:0] */
+#define WM8993_SW_RESET_SHIFT                        0  /* SW_RESET - [15:0] */
+#define WM8993_SW_RESET_WIDTH                       16  /* SW_RESET - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8993_SPKOUTR_ENA                      0x2000  /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_MASK                 0x2000  /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_SHIFT                    13  /* SPKOUTR_ENA */
+#define WM8993_SPKOUTR_ENA_WIDTH                     1  /* SPKOUTR_ENA */
+#define WM8993_SPKOUTL_ENA                      0x1000  /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_MASK                 0x1000  /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_SHIFT                    12  /* SPKOUTL_ENA */
+#define WM8993_SPKOUTL_ENA_WIDTH                     1  /* SPKOUTL_ENA */
+#define WM8993_HPOUT2_ENA                       0x0800  /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_MASK                  0x0800  /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_SHIFT                     11  /* HPOUT2_ENA */
+#define WM8993_HPOUT2_ENA_WIDTH                      1  /* HPOUT2_ENA */
+#define WM8993_HPOUT1L_ENA                      0x0200  /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_MASK                 0x0200  /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_SHIFT                     9  /* HPOUT1L_ENA */
+#define WM8993_HPOUT1L_ENA_WIDTH                     1  /* HPOUT1L_ENA */
+#define WM8993_HPOUT1R_ENA                      0x0100  /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_MASK                 0x0100  /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_SHIFT                     8  /* HPOUT1R_ENA */
+#define WM8993_HPOUT1R_ENA_WIDTH                     1  /* HPOUT1R_ENA */
+#define WM8993_MICB2_ENA                        0x0020  /* MICB2_ENA */
+#define WM8993_MICB2_ENA_MASK                   0x0020  /* MICB2_ENA */
+#define WM8993_MICB2_ENA_SHIFT                       5  /* MICB2_ENA */
+#define WM8993_MICB2_ENA_WIDTH                       1  /* MICB2_ENA */
+#define WM8993_MICB1_ENA                        0x0010  /* MICB1_ENA */
+#define WM8993_MICB1_ENA_MASK                   0x0010  /* MICB1_ENA */
+#define WM8993_MICB1_ENA_SHIFT                       4  /* MICB1_ENA */
+#define WM8993_MICB1_ENA_WIDTH                       1  /* MICB1_ENA */
+#define WM8993_VMID_SEL_MASK                    0x0006  /* VMID_SEL - [2:1] */
+#define WM8993_VMID_SEL_SHIFT                        1  /* VMID_SEL - [2:1] */
+#define WM8993_VMID_SEL_WIDTH                        2  /* VMID_SEL - [2:1] */
+#define WM8993_BIAS_ENA                         0x0001  /* BIAS_ENA */
+#define WM8993_BIAS_ENA_MASK                    0x0001  /* BIAS_ENA */
+#define WM8993_BIAS_ENA_SHIFT                        0  /* BIAS_ENA */
+#define WM8993_BIAS_ENA_WIDTH                        1  /* BIAS_ENA */
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8993_TSHUT_ENA                        0x4000  /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_MASK                   0x4000  /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_SHIFT                      14  /* TSHUT_ENA */
+#define WM8993_TSHUT_ENA_WIDTH                       1  /* TSHUT_ENA */
+#define WM8993_TSHUT_OPDIS                      0x2000  /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_MASK                 0x2000  /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_SHIFT                    13  /* TSHUT_OPDIS */
+#define WM8993_TSHUT_OPDIS_WIDTH                     1  /* TSHUT_OPDIS */
+#define WM8993_OPCLK_ENA                        0x0800  /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_MASK                   0x0800  /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_SHIFT                      11  /* OPCLK_ENA */
+#define WM8993_OPCLK_ENA_WIDTH                       1  /* OPCLK_ENA */
+#define WM8993_MIXINL_ENA                       0x0200  /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_MASK                  0x0200  /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_SHIFT                      9  /* MIXINL_ENA */
+#define WM8993_MIXINL_ENA_WIDTH                      1  /* MIXINL_ENA */
+#define WM8993_MIXINR_ENA                       0x0100  /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_MASK                  0x0100  /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_SHIFT                      8  /* MIXINR_ENA */
+#define WM8993_MIXINR_ENA_WIDTH                      1  /* MIXINR_ENA */
+#define WM8993_IN2L_ENA                         0x0080  /* IN2L_ENA */
+#define WM8993_IN2L_ENA_MASK                    0x0080  /* IN2L_ENA */
+#define WM8993_IN2L_ENA_SHIFT                        7  /* IN2L_ENA */
+#define WM8993_IN2L_ENA_WIDTH                        1  /* IN2L_ENA */
+#define WM8993_IN1L_ENA                         0x0040  /* IN1L_ENA */
+#define WM8993_IN1L_ENA_MASK                    0x0040  /* IN1L_ENA */
+#define WM8993_IN1L_ENA_SHIFT                        6  /* IN1L_ENA */
+#define WM8993_IN1L_ENA_WIDTH                        1  /* IN1L_ENA */
+#define WM8993_IN2R_ENA                         0x0020  /* IN2R_ENA */
+#define WM8993_IN2R_ENA_MASK                    0x0020  /* IN2R_ENA */
+#define WM8993_IN2R_ENA_SHIFT                        5  /* IN2R_ENA */
+#define WM8993_IN2R_ENA_WIDTH                        1  /* IN2R_ENA */
+#define WM8993_IN1R_ENA                         0x0010  /* IN1R_ENA */
+#define WM8993_IN1R_ENA_MASK                    0x0010  /* IN1R_ENA */
+#define WM8993_IN1R_ENA_SHIFT                        4  /* IN1R_ENA */
+#define WM8993_IN1R_ENA_WIDTH                        1  /* IN1R_ENA */
+#define WM8993_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8993_ADCL_ENA_MASK                    0x0002  /* ADCL_ENA */
+#define WM8993_ADCL_ENA_SHIFT                        1  /* ADCL_ENA */
+#define WM8993_ADCL_ENA_WIDTH                        1  /* ADCL_ENA */
+#define WM8993_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8993_ADCR_ENA_MASK                    0x0001  /* ADCR_ENA */
+#define WM8993_ADCR_ENA_SHIFT                        0  /* ADCR_ENA */
+#define WM8993_ADCR_ENA_WIDTH                        1  /* ADCR_ENA */
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8993_LINEOUT1N_ENA                    0x2000  /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_MASK               0x2000  /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_SHIFT                  13  /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1N_ENA_WIDTH                   1  /* LINEOUT1N_ENA */
+#define WM8993_LINEOUT1P_ENA                    0x1000  /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_MASK               0x1000  /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_SHIFT                  12  /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT1P_ENA_WIDTH                   1  /* LINEOUT1P_ENA */
+#define WM8993_LINEOUT2N_ENA                    0x0800  /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_MASK               0x0800  /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_SHIFT                  11  /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2N_ENA_WIDTH                   1  /* LINEOUT2N_ENA */
+#define WM8993_LINEOUT2P_ENA                    0x0400  /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_MASK               0x0400  /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_SHIFT                  10  /* LINEOUT2P_ENA */
+#define WM8993_LINEOUT2P_ENA_WIDTH                   1  /* LINEOUT2P_ENA */
+#define WM8993_SPKRVOL_ENA                      0x0200  /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_MASK                 0x0200  /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_SHIFT                     9  /* SPKRVOL_ENA */
+#define WM8993_SPKRVOL_ENA_WIDTH                     1  /* SPKRVOL_ENA */
+#define WM8993_SPKLVOL_ENA                      0x0100  /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_MASK                 0x0100  /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_SHIFT                     8  /* SPKLVOL_ENA */
+#define WM8993_SPKLVOL_ENA_WIDTH                     1  /* SPKLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA                   0x0080  /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_MASK              0x0080  /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_SHIFT                  7  /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTLVOL_ENA_WIDTH                  1  /* MIXOUTLVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA                   0x0040  /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_MASK              0x0040  /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_SHIFT                  6  /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTRVOL_ENA_WIDTH                  1  /* MIXOUTRVOL_ENA */
+#define WM8993_MIXOUTL_ENA                      0x0020  /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_MASK                 0x0020  /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_SHIFT                     5  /* MIXOUTL_ENA */
+#define WM8993_MIXOUTL_ENA_WIDTH                     1  /* MIXOUTL_ENA */
+#define WM8993_MIXOUTR_ENA                      0x0010  /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_MASK                 0x0010  /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_SHIFT                     4  /* MIXOUTR_ENA */
+#define WM8993_MIXOUTR_ENA_WIDTH                     1  /* MIXOUTR_ENA */
+#define WM8993_DACL_ENA                         0x0002  /* DACL_ENA */
+#define WM8993_DACL_ENA_MASK                    0x0002  /* DACL_ENA */
+#define WM8993_DACL_ENA_SHIFT                        1  /* DACL_ENA */
+#define WM8993_DACL_ENA_WIDTH                        1  /* DACL_ENA */
+#define WM8993_DACR_ENA                         0x0001  /* DACR_ENA */
+#define WM8993_DACR_ENA_MASK                    0x0001  /* DACR_ENA */
+#define WM8993_DACR_ENA_SHIFT                        0  /* DACR_ENA */
+#define WM8993_DACR_ENA_WIDTH                        1  /* DACR_ENA */
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8993_AIFADCL_SRC                      0x8000  /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_MASK                 0x8000  /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_SHIFT                    15  /* AIFADCL_SRC */
+#define WM8993_AIFADCL_SRC_WIDTH                     1  /* AIFADCL_SRC */
+#define WM8993_AIFADCR_SRC                      0x4000  /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_MASK                 0x4000  /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_SHIFT                    14  /* AIFADCR_SRC */
+#define WM8993_AIFADCR_SRC_WIDTH                     1  /* AIFADCR_SRC */
+#define WM8993_AIFADC_TDM                       0x2000  /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_MASK                  0x2000  /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_SHIFT                     13  /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_WIDTH                      1  /* AIFADC_TDM */
+#define WM8993_AIFADC_TDM_CHAN                  0x1000  /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_MASK             0x1000  /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_SHIFT                12  /* AIFADC_TDM_CHAN */
+#define WM8993_AIFADC_TDM_CHAN_WIDTH                 1  /* AIFADC_TDM_CHAN */
+#define WM8993_BCLK_DIR                         0x0200  /* BCLK_DIR */
+#define WM8993_BCLK_DIR_MASK                    0x0200  /* BCLK_DIR */
+#define WM8993_BCLK_DIR_SHIFT                        9  /* BCLK_DIR */
+#define WM8993_BCLK_DIR_WIDTH                        1  /* BCLK_DIR */
+#define WM8993_AIF_BCLK_INV                     0x0100  /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_MASK                0x0100  /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_SHIFT                    8  /* AIF_BCLK_INV */
+#define WM8993_AIF_BCLK_INV_WIDTH                    1  /* AIF_BCLK_INV */
+#define WM8993_AIF_LRCLK_INV                    0x0080  /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_MASK               0x0080  /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_SHIFT                   7  /* AIF_LRCLK_INV */
+#define WM8993_AIF_LRCLK_INV_WIDTH                   1  /* AIF_LRCLK_INV */
+#define WM8993_AIF_WL_MASK                      0x0060  /* AIF_WL - [6:5] */
+#define WM8993_AIF_WL_SHIFT                          5  /* AIF_WL - [6:5] */
+#define WM8993_AIF_WL_WIDTH                          2  /* AIF_WL - [6:5] */
+#define WM8993_AIF_FMT_MASK                     0x0018  /* AIF_FMT - [4:3] */
+#define WM8993_AIF_FMT_SHIFT                         3  /* AIF_FMT - [4:3] */
+#define WM8993_AIF_FMT_WIDTH                         2  /* AIF_FMT - [4:3] */
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8993_AIFDACL_SRC                      0x8000  /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_MASK                 0x8000  /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_SHIFT                    15  /* AIFDACL_SRC */
+#define WM8993_AIFDACL_SRC_WIDTH                     1  /* AIFDACL_SRC */
+#define WM8993_AIFDACR_SRC                      0x4000  /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_MASK                 0x4000  /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_SHIFT                    14  /* AIFDACR_SRC */
+#define WM8993_AIFDACR_SRC_WIDTH                     1  /* AIFDACR_SRC */
+#define WM8993_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_MASK                  0x2000  /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_SHIFT                     13  /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_WIDTH                      1  /* AIFDAC_TDM */
+#define WM8993_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_MASK             0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_SHIFT                12  /* AIFDAC_TDM_CHAN */
+#define WM8993_AIFDAC_TDM_CHAN_WIDTH                 1  /* AIFDAC_TDM_CHAN */
+#define WM8993_DAC_BOOST_MASK                   0x0C00  /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_BOOST_SHIFT                      10  /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_BOOST_WIDTH                       2  /* DAC_BOOST - [11:10] */
+#define WM8993_DAC_COMP                         0x0010  /* DAC_COMP */
+#define WM8993_DAC_COMP_MASK                    0x0010  /* DAC_COMP */
+#define WM8993_DAC_COMP_SHIFT                        4  /* DAC_COMP */
+#define WM8993_DAC_COMP_WIDTH                        1  /* DAC_COMP */
+#define WM8993_DAC_COMPMODE                     0x0008  /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_MASK                0x0008  /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_SHIFT                    3  /* DAC_COMPMODE */
+#define WM8993_DAC_COMPMODE_WIDTH                    1  /* DAC_COMPMODE */
+#define WM8993_ADC_COMP                         0x0004  /* ADC_COMP */
+#define WM8993_ADC_COMP_MASK                    0x0004  /* ADC_COMP */
+#define WM8993_ADC_COMP_SHIFT                        2  /* ADC_COMP */
+#define WM8993_ADC_COMP_WIDTH                        1  /* ADC_COMP */
+#define WM8993_ADC_COMPMODE                     0x0002  /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_MASK                0x0002  /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_SHIFT                    1  /* ADC_COMPMODE */
+#define WM8993_ADC_COMPMODE_WIDTH                    1  /* ADC_COMPMODE */
+#define WM8993_LOOPBACK                         0x0001  /* LOOPBACK */
+#define WM8993_LOOPBACK_MASK                    0x0001  /* LOOPBACK */
+#define WM8993_LOOPBACK_SHIFT                        0  /* LOOPBACK */
+#define WM8993_LOOPBACK_WIDTH                        1  /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking 1
+ */
+#define WM8993_TOCLK_RATE                       0x8000  /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_MASK                  0x8000  /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_SHIFT                     15  /* TOCLK_RATE */
+#define WM8993_TOCLK_RATE_WIDTH                      1  /* TOCLK_RATE */
+#define WM8993_TOCLK_ENA                        0x4000  /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_MASK                   0x4000  /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_SHIFT                      14  /* TOCLK_ENA */
+#define WM8993_TOCLK_ENA_WIDTH                       1  /* TOCLK_ENA */
+#define WM8993_OPCLK_DIV_MASK                   0x1E00  /* OPCLK_DIV - [12:9] */
+#define WM8993_OPCLK_DIV_SHIFT                       9  /* OPCLK_DIV - [12:9] */
+#define WM8993_OPCLK_DIV_WIDTH                       4  /* OPCLK_DIV - [12:9] */
+#define WM8993_DCLK_DIV_MASK                    0x01C0  /* DCLK_DIV - [8:6] */
+#define WM8993_DCLK_DIV_SHIFT                        6  /* DCLK_DIV - [8:6] */
+#define WM8993_DCLK_DIV_WIDTH                        3  /* DCLK_DIV - [8:6] */
+#define WM8993_BCLK_DIV_MASK                    0x001E  /* BCLK_DIV - [4:1] */
+#define WM8993_BCLK_DIV_SHIFT                        1  /* BCLK_DIV - [4:1] */
+#define WM8993_BCLK_DIV_WIDTH                        4  /* BCLK_DIV - [4:1] */
+
+/*
+ * R7 (0x07) - Clocking 2
+ */
+#define WM8993_MCLK_SRC                         0x8000  /* MCLK_SRC */
+#define WM8993_MCLK_SRC_MASK                    0x8000  /* MCLK_SRC */
+#define WM8993_MCLK_SRC_SHIFT                       15  /* MCLK_SRC */
+#define WM8993_MCLK_SRC_WIDTH                        1  /* MCLK_SRC */
+#define WM8993_SYSCLK_SRC                       0x4000  /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_MASK                  0x4000  /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_SHIFT                     14  /* SYSCLK_SRC */
+#define WM8993_SYSCLK_SRC_WIDTH                      1  /* SYSCLK_SRC */
+#define WM8993_MCLK_DIV                         0x1000  /* MCLK_DIV */
+#define WM8993_MCLK_DIV_MASK                    0x1000  /* MCLK_DIV */
+#define WM8993_MCLK_DIV_SHIFT                       12  /* MCLK_DIV */
+#define WM8993_MCLK_DIV_WIDTH                        1  /* MCLK_DIV */
+#define WM8993_MCLK_INV                         0x0400  /* MCLK_INV */
+#define WM8993_MCLK_INV_MASK                    0x0400  /* MCLK_INV */
+#define WM8993_MCLK_INV_SHIFT                       10  /* MCLK_INV */
+#define WM8993_MCLK_INV_WIDTH                        1  /* MCLK_INV */
+#define WM8993_ADC_DIV_MASK                     0x00E0  /* ADC_DIV - [7:5] */
+#define WM8993_ADC_DIV_SHIFT                         5  /* ADC_DIV - [7:5] */
+#define WM8993_ADC_DIV_WIDTH                         3  /* ADC_DIV - [7:5] */
+#define WM8993_DAC_DIV_MASK                     0x001C  /* DAC_DIV - [4:2] */
+#define WM8993_DAC_DIV_SHIFT                         2  /* DAC_DIV - [4:2] */
+#define WM8993_DAC_DIV_WIDTH                         3  /* DAC_DIV - [4:2] */
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8993_AIF_MSTR1                        0x8000  /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_MASK                   0x8000  /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_SHIFT                      15  /* AIF_MSTR1 */
+#define WM8993_AIF_MSTR1_WIDTH                       1  /* AIF_MSTR1 */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8993_AIF_TRIS                         0x2000  /* AIF_TRIS */
+#define WM8993_AIF_TRIS_MASK                    0x2000  /* AIF_TRIS */
+#define WM8993_AIF_TRIS_SHIFT                       13  /* AIF_TRIS */
+#define WM8993_AIF_TRIS_WIDTH                        1  /* AIF_TRIS */
+#define WM8993_LRCLK_DIR                        0x0800  /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_MASK                   0x0800  /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_SHIFT                      11  /* LRCLK_DIR */
+#define WM8993_LRCLK_DIR_WIDTH                       1  /* LRCLK_DIR */
+#define WM8993_LRCLK_RATE_MASK                  0x07FF  /* LRCLK_RATE - [10:0] */
+#define WM8993_LRCLK_RATE_SHIFT                      0  /* LRCLK_RATE - [10:0] */
+#define WM8993_LRCLK_RATE_WIDTH                     11  /* LRCLK_RATE - [10:0] */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8993_DAC_OSR128                       0x2000  /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_MASK                  0x2000  /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_SHIFT                     13  /* DAC_OSR128 */
+#define WM8993_DAC_OSR128_WIDTH                      1  /* DAC_OSR128 */
+#define WM8993_DAC_MONO                         0x0200  /* DAC_MONO */
+#define WM8993_DAC_MONO_MASK                    0x0200  /* DAC_MONO */
+#define WM8993_DAC_MONO_SHIFT                        9  /* DAC_MONO */
+#define WM8993_DAC_MONO_WIDTH                        1  /* DAC_MONO */
+#define WM8993_DAC_SB_FILT                      0x0100  /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_MASK                 0x0100  /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_SHIFT                     8  /* DAC_SB_FILT */
+#define WM8993_DAC_SB_FILT_WIDTH                     1  /* DAC_SB_FILT */
+#define WM8993_DAC_MUTERATE                     0x0080  /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_MASK                0x0080  /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_SHIFT                    7  /* DAC_MUTERATE */
+#define WM8993_DAC_MUTERATE_WIDTH                    1  /* DAC_MUTERATE */
+#define WM8993_DAC_UNMUTE_RAMP                  0x0040  /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_MASK             0x0040  /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_SHIFT                 6  /* DAC_UNMUTE_RAMP */
+#define WM8993_DAC_UNMUTE_RAMP_WIDTH                 1  /* DAC_UNMUTE_RAMP */
+#define WM8993_DEEMPH_MASK                      0x0030  /* DEEMPH - [5:4] */
+#define WM8993_DEEMPH_SHIFT                          4  /* DEEMPH - [5:4] */
+#define WM8993_DEEMPH_WIDTH                          2  /* DEEMPH - [5:4] */
+#define WM8993_DAC_MUTE                         0x0004  /* DAC_MUTE */
+#define WM8993_DAC_MUTE_MASK                    0x0004  /* DAC_MUTE */
+#define WM8993_DAC_MUTE_SHIFT                        2  /* DAC_MUTE */
+#define WM8993_DAC_MUTE_WIDTH                        1  /* DAC_MUTE */
+#define WM8993_DACL_DATINV                      0x0002  /* DACL_DATINV */
+#define WM8993_DACL_DATINV_MASK                 0x0002  /* DACL_DATINV */
+#define WM8993_DACL_DATINV_SHIFT                     1  /* DACL_DATINV */
+#define WM8993_DACL_DATINV_WIDTH                     1  /* DACL_DATINV */
+#define WM8993_DACR_DATINV                      0x0001  /* DACR_DATINV */
+#define WM8993_DACR_DATINV_MASK                 0x0001  /* DACR_DATINV */
+#define WM8993_DACR_DATINV_SHIFT                     0  /* DACR_DATINV */
+#define WM8993_DACR_DATINV_WIDTH                     1  /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8993_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8993_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8993_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8993_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8993_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8993_DACL_VOL_SHIFT                        0  /* DACL_VOL - [7:0] */
+#define WM8993_DACL_VOL_WIDTH                        8  /* DACL_VOL - [7:0] */
+
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8993_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8993_DAC_VU_MASK                      0x0100  /* DAC_VU */
+#define WM8993_DAC_VU_SHIFT                          8  /* DAC_VU */
+#define WM8993_DAC_VU_WIDTH                          1  /* DAC_VU */
+#define WM8993_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8993_DACR_VOL_SHIFT                        0  /* DACR_VOL - [7:0] */
+#define WM8993_DACR_VOL_WIDTH                        8  /* DACR_VOL - [7:0] */
+
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8993_ADCL_DAC_SVOL_MASK               0x1E00  /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCL_DAC_SVOL_SHIFT                   9  /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCL_DAC_SVOL_WIDTH                   4  /* ADCL_DAC_SVOL - [12:9] */
+#define WM8993_ADCR_DAC_SVOL_MASK               0x01E0  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADCR_DAC_SVOL_SHIFT                   5  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADCR_DAC_SVOL_WIDTH                   4  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8993_ADC_TO_DACL_MASK                 0x000C  /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACL_SHIFT                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACL_WIDTH                     2  /* ADC_TO_DACL - [3:2] */
+#define WM8993_ADC_TO_DACR_MASK                 0x0003  /* ADC_TO_DACR - [1:0] */
+#define WM8993_ADC_TO_DACR_SHIFT                     0  /* ADC_TO_DACR - [1:0] */
+#define WM8993_ADC_TO_DACR_WIDTH                     2  /* ADC_TO_DACR - [1:0] */
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8993_ADC_OSR128                       0x0200  /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_MASK                  0x0200  /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_SHIFT                      9  /* ADC_OSR128 */
+#define WM8993_ADC_OSR128_WIDTH                      1  /* ADC_OSR128 */
+#define WM8993_ADC_HPF                          0x0100  /* ADC_HPF */
+#define WM8993_ADC_HPF_MASK                     0x0100  /* ADC_HPF */
+#define WM8993_ADC_HPF_SHIFT                         8  /* ADC_HPF */
+#define WM8993_ADC_HPF_WIDTH                         1  /* ADC_HPF */
+#define WM8993_ADC_HPF_CUT_MASK                 0x0060  /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADC_HPF_CUT_SHIFT                     5  /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADC_HPF_CUT_WIDTH                     2  /* ADC_HPF_CUT - [6:5] */
+#define WM8993_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_MASK                 0x0002  /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_SHIFT                     1  /* ADCL_DATINV */
+#define WM8993_ADCL_DATINV_WIDTH                     1  /* ADCL_DATINV */
+#define WM8993_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_MASK                 0x0001  /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_SHIFT                     0  /* ADCR_DATINV */
+#define WM8993_ADCR_DATINV_WIDTH                     1  /* ADCR_DATINV */
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8993_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8993_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8993_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8993_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8993_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8993_ADCL_VOL_SHIFT                        0  /* ADCL_VOL - [7:0] */
+#define WM8993_ADCL_VOL_WIDTH                        8  /* ADCL_VOL - [7:0] */
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8993_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8993_ADC_VU_MASK                      0x0100  /* ADC_VU */
+#define WM8993_ADC_VU_SHIFT                          8  /* ADC_VU */
+#define WM8993_ADC_VU_WIDTH                          1  /* ADC_VU */
+#define WM8993_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8993_ADCR_VOL_SHIFT                        0  /* ADCR_VOL - [7:0] */
+#define WM8993_ADCR_VOL_WIDTH                        8  /* ADCR_VOL - [7:0] */
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8993_JD2_SC_EINT                      0x8000  /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_MASK                 0x8000  /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_SHIFT                    15  /* JD2_SC_EINT */
+#define WM8993_JD2_SC_EINT_WIDTH                     1  /* JD2_SC_EINT */
+#define WM8993_JD2_EINT                         0x4000  /* JD2_EINT */
+#define WM8993_JD2_EINT_MASK                    0x4000  /* JD2_EINT */
+#define WM8993_JD2_EINT_SHIFT                       14  /* JD2_EINT */
+#define WM8993_JD2_EINT_WIDTH                        1  /* JD2_EINT */
+#define WM8993_WSEQ_EINT                        0x2000  /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_MASK                   0x2000  /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_SHIFT                      13  /* WSEQ_EINT */
+#define WM8993_WSEQ_EINT_WIDTH                       1  /* WSEQ_EINT */
+#define WM8993_IRQ                              0x1000  /* IRQ */
+#define WM8993_IRQ_MASK                         0x1000  /* IRQ */
+#define WM8993_IRQ_SHIFT                            12  /* IRQ */
+#define WM8993_IRQ_WIDTH                             1  /* IRQ */
+#define WM8993_TEMPOK_EINT                      0x0800  /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_MASK                 0x0800  /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_SHIFT                    11  /* TEMPOK_EINT */
+#define WM8993_TEMPOK_EINT_WIDTH                     1  /* TEMPOK_EINT */
+#define WM8993_JD1_SC_EINT                      0x0400  /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_MASK                 0x0400  /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_SHIFT                    10  /* JD1_SC_EINT */
+#define WM8993_JD1_SC_EINT_WIDTH                     1  /* JD1_SC_EINT */
+#define WM8993_JD1_EINT                         0x0200  /* JD1_EINT */
+#define WM8993_JD1_EINT_MASK                    0x0200  /* JD1_EINT */
+#define WM8993_JD1_EINT_SHIFT                        9  /* JD1_EINT */
+#define WM8993_JD1_EINT_WIDTH                        1  /* JD1_EINT */
+#define WM8993_FLL_LOCK_EINT                    0x0100  /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_MASK               0x0100  /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_SHIFT                   8  /* FLL_LOCK_EINT */
+#define WM8993_FLL_LOCK_EINT_WIDTH                   1  /* FLL_LOCK_EINT */
+#define WM8993_GPI8_EINT                        0x0080  /* GPI8_EINT */
+#define WM8993_GPI8_EINT_MASK                   0x0080  /* GPI8_EINT */
+#define WM8993_GPI8_EINT_SHIFT                       7  /* GPI8_EINT */
+#define WM8993_GPI8_EINT_WIDTH                       1  /* GPI8_EINT */
+#define WM8993_GPI7_EINT                        0x0040  /* GPI7_EINT */
+#define WM8993_GPI7_EINT_MASK                   0x0040  /* GPI7_EINT */
+#define WM8993_GPI7_EINT_SHIFT                       6  /* GPI7_EINT */
+#define WM8993_GPI7_EINT_WIDTH                       1  /* GPI7_EINT */
+#define WM8993_GPIO1_EINT                       0x0001  /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_MASK                  0x0001  /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_SHIFT                      0  /* GPIO1_EINT */
+#define WM8993_GPIO1_EINT_WIDTH                      1  /* GPIO1_EINT */
+
+/*
+ * R19 (0x13) - GPIO1
+ */
+#define WM8993_GPIO1_PU                         0x0020  /* GPIO1_PU */
+#define WM8993_GPIO1_PU_MASK                    0x0020  /* GPIO1_PU */
+#define WM8993_GPIO1_PU_SHIFT                        5  /* GPIO1_PU */
+#define WM8993_GPIO1_PU_WIDTH                        1  /* GPIO1_PU */
+#define WM8993_GPIO1_PD                         0x0010  /* GPIO1_PD */
+#define WM8993_GPIO1_PD_MASK                    0x0010  /* GPIO1_PD */
+#define WM8993_GPIO1_PD_SHIFT                        4  /* GPIO1_PD */
+#define WM8993_GPIO1_PD_WIDTH                        1  /* GPIO1_PD */
+#define WM8993_GPIO1_SEL_MASK                   0x000F  /* GPIO1_SEL - [3:0] */
+#define WM8993_GPIO1_SEL_SHIFT                       0  /* GPIO1_SEL - [3:0] */
+#define WM8993_GPIO1_SEL_WIDTH                       4  /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - IRQ_DEBOUNCE
+ */
+#define WM8993_JD2_SC_DB                        0x8000  /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_MASK                   0x8000  /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_SHIFT                      15  /* JD2_SC_DB */
+#define WM8993_JD2_SC_DB_WIDTH                       1  /* JD2_SC_DB */
+#define WM8993_JD2_DB                           0x4000  /* JD2_DB */
+#define WM8993_JD2_DB_MASK                      0x4000  /* JD2_DB */
+#define WM8993_JD2_DB_SHIFT                         14  /* JD2_DB */
+#define WM8993_JD2_DB_WIDTH                          1  /* JD2_DB */
+#define WM8993_WSEQ_DB                          0x2000  /* WSEQ_DB */
+#define WM8993_WSEQ_DB_MASK                     0x2000  /* WSEQ_DB */
+#define WM8993_WSEQ_DB_SHIFT                        13  /* WSEQ_DB */
+#define WM8993_WSEQ_DB_WIDTH                         1  /* WSEQ_DB */
+#define WM8993_TEMPOK_DB                        0x0800  /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_MASK                   0x0800  /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_SHIFT                      11  /* TEMPOK_DB */
+#define WM8993_TEMPOK_DB_WIDTH                       1  /* TEMPOK_DB */
+#define WM8993_JD1_SC_DB                        0x0400  /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_MASK                   0x0400  /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_SHIFT                      10  /* JD1_SC_DB */
+#define WM8993_JD1_SC_DB_WIDTH                       1  /* JD1_SC_DB */
+#define WM8993_JD1_DB                           0x0200  /* JD1_DB */
+#define WM8993_JD1_DB_MASK                      0x0200  /* JD1_DB */
+#define WM8993_JD1_DB_SHIFT                          9  /* JD1_DB */
+#define WM8993_JD1_DB_WIDTH                          1  /* JD1_DB */
+#define WM8993_FLL_LOCK_DB                      0x0100  /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_MASK                 0x0100  /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_SHIFT                     8  /* FLL_LOCK_DB */
+#define WM8993_FLL_LOCK_DB_WIDTH                     1  /* FLL_LOCK_DB */
+#define WM8993_GPI8_DB                          0x0080  /* GPI8_DB */
+#define WM8993_GPI8_DB_MASK                     0x0080  /* GPI8_DB */
+#define WM8993_GPI8_DB_SHIFT                         7  /* GPI8_DB */
+#define WM8993_GPI8_DB_WIDTH                         1  /* GPI8_DB */
+#define WM8993_GPI7_DB                          0x0008  /* GPI7_DB */
+#define WM8993_GPI7_DB_MASK                     0x0008  /* GPI7_DB */
+#define WM8993_GPI7_DB_SHIFT                         3  /* GPI7_DB */
+#define WM8993_GPI7_DB_WIDTH                         1  /* GPI7_DB */
+#define WM8993_GPIO1_DB                         0x0001  /* GPIO1_DB */
+#define WM8993_GPIO1_DB_MASK                    0x0001  /* GPIO1_DB */
+#define WM8993_GPIO1_DB_SHIFT                        0  /* GPIO1_DB */
+#define WM8993_GPIO1_DB_WIDTH                        1  /* GPIO1_DB */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8993_IM_JD2_EINT                      0x2000  /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_MASK                 0x2000  /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_SHIFT                    13  /* IM_JD2_EINT */
+#define WM8993_IM_JD2_EINT_WIDTH                     1  /* IM_JD2_EINT */
+#define WM8993_IM_JD2_SC_EINT                   0x1000  /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_MASK              0x1000  /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_SHIFT                 12  /* IM_JD2_SC_EINT */
+#define WM8993_IM_JD2_SC_EINT_WIDTH                  1  /* IM_JD2_SC_EINT */
+#define WM8993_IM_TEMPOK_EINT                   0x0800  /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_MASK              0x0800  /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_SHIFT                 11  /* IM_TEMPOK_EINT */
+#define WM8993_IM_TEMPOK_EINT_WIDTH                  1  /* IM_TEMPOK_EINT */
+#define WM8993_IM_JD1_SC_EINT                   0x0400  /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_MASK              0x0400  /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_SHIFT                 10  /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_SC_EINT_WIDTH                  1  /* IM_JD1_SC_EINT */
+#define WM8993_IM_JD1_EINT                      0x0200  /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_MASK                 0x0200  /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_SHIFT                     9  /* IM_JD1_EINT */
+#define WM8993_IM_JD1_EINT_WIDTH                     1  /* IM_JD1_EINT */
+#define WM8993_IM_FLL_LOCK_EINT                 0x0100  /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_MASK            0x0100  /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_SHIFT                8  /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_FLL_LOCK_EINT_WIDTH                1  /* IM_FLL_LOCK_EINT */
+#define WM8993_IM_GPI8_EINT                     0x0040  /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_MASK                0x0040  /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_SHIFT                    6  /* IM_GPI8_EINT */
+#define WM8993_IM_GPI8_EINT_WIDTH                    1  /* IM_GPI8_EINT */
+#define WM8993_IM_GPIO1_EINT                    0x0020  /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_MASK               0x0020  /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_SHIFT                   5  /* IM_GPIO1_EINT */
+#define WM8993_IM_GPIO1_EINT_WIDTH                   1  /* IM_GPIO1_EINT */
+#define WM8993_GPI8_ENA                         0x0010  /* GPI8_ENA */
+#define WM8993_GPI8_ENA_MASK                    0x0010  /* GPI8_ENA */
+#define WM8993_GPI8_ENA_SHIFT                        4  /* GPI8_ENA */
+#define WM8993_GPI8_ENA_WIDTH                        1  /* GPI8_ENA */
+#define WM8993_IM_GPI7_EINT                     0x0004  /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_MASK                0x0004  /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_SHIFT                    2  /* IM_GPI7_EINT */
+#define WM8993_IM_GPI7_EINT_WIDTH                    1  /* IM_GPI7_EINT */
+#define WM8993_IM_WSEQ_EINT                     0x0002  /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_MASK                0x0002  /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_SHIFT                    1  /* IM_WSEQ_EINT */
+#define WM8993_IM_WSEQ_EINT_WIDTH                    1  /* IM_WSEQ_EINT */
+#define WM8993_GPI7_ENA                         0x0001  /* GPI7_ENA */
+#define WM8993_GPI7_ENA_MASK                    0x0001  /* GPI7_ENA */
+#define WM8993_GPI7_ENA_SHIFT                        0  /* GPI7_ENA */
+#define WM8993_GPI7_ENA_WIDTH                        1  /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8993_JD2_SC_POL                       0x8000  /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_MASK                  0x8000  /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_SHIFT                     15  /* JD2_SC_POL */
+#define WM8993_JD2_SC_POL_WIDTH                      1  /* JD2_SC_POL */
+#define WM8993_JD2_POL                          0x4000  /* JD2_POL */
+#define WM8993_JD2_POL_MASK                     0x4000  /* JD2_POL */
+#define WM8993_JD2_POL_SHIFT                        14  /* JD2_POL */
+#define WM8993_JD2_POL_WIDTH                         1  /* JD2_POL */
+#define WM8993_WSEQ_POL                         0x2000  /* WSEQ_POL */
+#define WM8993_WSEQ_POL_MASK                    0x2000  /* WSEQ_POL */
+#define WM8993_WSEQ_POL_SHIFT                       13  /* WSEQ_POL */
+#define WM8993_WSEQ_POL_WIDTH                        1  /* WSEQ_POL */
+#define WM8993_IRQ_POL                          0x1000  /* IRQ_POL */
+#define WM8993_IRQ_POL_MASK                     0x1000  /* IRQ_POL */
+#define WM8993_IRQ_POL_SHIFT                        12  /* IRQ_POL */
+#define WM8993_IRQ_POL_WIDTH                         1  /* IRQ_POL */
+#define WM8993_TEMPOK_POL                       0x0800  /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_MASK                  0x0800  /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_SHIFT                     11  /* TEMPOK_POL */
+#define WM8993_TEMPOK_POL_WIDTH                      1  /* TEMPOK_POL */
+#define WM8993_JD1_SC_POL                       0x0400  /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_MASK                  0x0400  /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_SHIFT                     10  /* JD1_SC_POL */
+#define WM8993_JD1_SC_POL_WIDTH                      1  /* JD1_SC_POL */
+#define WM8993_JD1_POL                          0x0200  /* JD1_POL */
+#define WM8993_JD1_POL_MASK                     0x0200  /* JD1_POL */
+#define WM8993_JD1_POL_SHIFT                         9  /* JD1_POL */
+#define WM8993_JD1_POL_WIDTH                         1  /* JD1_POL */
+#define WM8993_FLL_LOCK_POL                     0x0100  /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_MASK                0x0100  /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_SHIFT                    8  /* FLL_LOCK_POL */
+#define WM8993_FLL_LOCK_POL_WIDTH                    1  /* FLL_LOCK_POL */
+#define WM8993_GPI8_POL                         0x0080  /* GPI8_POL */
+#define WM8993_GPI8_POL_MASK                    0x0080  /* GPI8_POL */
+#define WM8993_GPI8_POL_SHIFT                        7  /* GPI8_POL */
+#define WM8993_GPI8_POL_WIDTH                        1  /* GPI8_POL */
+#define WM8993_GPI7_POL                         0x0040  /* GPI7_POL */
+#define WM8993_GPI7_POL_MASK                    0x0040  /* GPI7_POL */
+#define WM8993_GPI7_POL_SHIFT                        6  /* GPI7_POL */
+#define WM8993_GPI7_POL_WIDTH                        1  /* GPI7_POL */
+#define WM8993_GPIO1_POL                        0x0001  /* GPIO1_POL */
+#define WM8993_GPIO1_POL_MASK                   0x0001  /* GPIO1_POL */
+#define WM8993_GPIO1_POL_SHIFT                       0  /* GPIO1_POL */
+#define WM8993_GPIO1_POL_WIDTH                       1  /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8993_IN1_VU                           0x0100  /* IN1_VU */
+#define WM8993_IN1_VU_MASK                      0x0100  /* IN1_VU */
+#define WM8993_IN1_VU_SHIFT                          8  /* IN1_VU */
+#define WM8993_IN1_VU_WIDTH                          1  /* IN1_VU */
+#define WM8993_IN1L_MUTE                        0x0080  /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_MASK                   0x0080  /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_SHIFT                       7  /* IN1L_MUTE */
+#define WM8993_IN1L_MUTE_WIDTH                       1  /* IN1L_MUTE */
+#define WM8993_IN1L_ZC                          0x0040  /* IN1L_ZC */
+#define WM8993_IN1L_ZC_MASK                     0x0040  /* IN1L_ZC */
+#define WM8993_IN1L_ZC_SHIFT                         6  /* IN1L_ZC */
+#define WM8993_IN1L_ZC_WIDTH                         1  /* IN1L_ZC */
+#define WM8993_IN1L_VOL_MASK                    0x001F  /* IN1L_VOL - [4:0] */
+#define WM8993_IN1L_VOL_SHIFT                        0  /* IN1L_VOL - [4:0] */
+#define WM8993_IN1L_VOL_WIDTH                        5  /* IN1L_VOL - [4:0] */
+
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8993_IN2_VU                           0x0100  /* IN2_VU */
+#define WM8993_IN2_VU_MASK                      0x0100  /* IN2_VU */
+#define WM8993_IN2_VU_SHIFT                          8  /* IN2_VU */
+#define WM8993_IN2_VU_WIDTH                          1  /* IN2_VU */
+#define WM8993_IN2L_MUTE                        0x0080  /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_MASK                   0x0080  /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_SHIFT                       7  /* IN2L_MUTE */
+#define WM8993_IN2L_MUTE_WIDTH                       1  /* IN2L_MUTE */
+#define WM8993_IN2L_ZC                          0x0040  /* IN2L_ZC */
+#define WM8993_IN2L_ZC_MASK                     0x0040  /* IN2L_ZC */
+#define WM8993_IN2L_ZC_SHIFT                         6  /* IN2L_ZC */
+#define WM8993_IN2L_ZC_WIDTH                         1  /* IN2L_ZC */
+#define WM8993_IN2L_VOL_MASK                    0x001F  /* IN2L_VOL - [4:0] */
+#define WM8993_IN2L_VOL_SHIFT                        0  /* IN2L_VOL - [4:0] */
+#define WM8993_IN2L_VOL_WIDTH                        5  /* IN2L_VOL - [4:0] */
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8993_IN1_VU                           0x0100  /* IN1_VU */
+#define WM8993_IN1_VU_MASK                      0x0100  /* IN1_VU */
+#define WM8993_IN1_VU_SHIFT                          8  /* IN1_VU */
+#define WM8993_IN1_VU_WIDTH                          1  /* IN1_VU */
+#define WM8993_IN1R_MUTE                        0x0080  /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_MASK                   0x0080  /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_SHIFT                       7  /* IN1R_MUTE */
+#define WM8993_IN1R_MUTE_WIDTH                       1  /* IN1R_MUTE */
+#define WM8993_IN1R_ZC                          0x0040  /* IN1R_ZC */
+#define WM8993_IN1R_ZC_MASK                     0x0040  /* IN1R_ZC */
+#define WM8993_IN1R_ZC_SHIFT                         6  /* IN1R_ZC */
+#define WM8993_IN1R_ZC_WIDTH                         1  /* IN1R_ZC */
+#define WM8993_IN1R_VOL_MASK                    0x001F  /* IN1R_VOL - [4:0] */
+#define WM8993_IN1R_VOL_SHIFT                        0  /* IN1R_VOL - [4:0] */
+#define WM8993_IN1R_VOL_WIDTH                        5  /* IN1R_VOL - [4:0] */
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8993_IN2_VU                           0x0100  /* IN2_VU */
+#define WM8993_IN2_VU_MASK                      0x0100  /* IN2_VU */
+#define WM8993_IN2_VU_SHIFT                          8  /* IN2_VU */
+#define WM8993_IN2_VU_WIDTH                          1  /* IN2_VU */
+#define WM8993_IN2R_MUTE                        0x0080  /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_MASK                   0x0080  /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_SHIFT                       7  /* IN2R_MUTE */
+#define WM8993_IN2R_MUTE_WIDTH                       1  /* IN2R_MUTE */
+#define WM8993_IN2R_ZC                          0x0040  /* IN2R_ZC */
+#define WM8993_IN2R_ZC_MASK                     0x0040  /* IN2R_ZC */
+#define WM8993_IN2R_ZC_SHIFT                         6  /* IN2R_ZC */
+#define WM8993_IN2R_ZC_WIDTH                         1  /* IN2R_ZC */
+#define WM8993_IN2R_VOL_MASK                    0x001F  /* IN2R_VOL - [4:0] */
+#define WM8993_IN2R_VOL_SHIFT                        0  /* IN2R_VOL - [4:0] */
+#define WM8993_IN2R_VOL_WIDTH                        5  /* IN2R_VOL - [4:0] */
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8993_HPOUT1_VU                        0x0100  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_MASK                   0x0100  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_SHIFT                       8  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_WIDTH                       1  /* HPOUT1_VU */
+#define WM8993_HPOUT1L_ZC                       0x0080  /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_MASK                  0x0080  /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_SHIFT                      7  /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_ZC_WIDTH                      1  /* HPOUT1L_ZC */
+#define WM8993_HPOUT1L_MUTE_N                   0x0040  /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_MASK              0x0040  /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_SHIFT                  6  /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_MUTE_N_WIDTH                  1  /* HPOUT1L_MUTE_N */
+#define WM8993_HPOUT1L_VOL_MASK                 0x003F  /* HPOUT1L_VOL - [5:0] */
+#define WM8993_HPOUT1L_VOL_SHIFT                     0  /* HPOUT1L_VOL - [5:0] */
+#define WM8993_HPOUT1L_VOL_WIDTH                     6  /* HPOUT1L_VOL - [5:0] */
+
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8993_HPOUT1_VU                        0x0100  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_MASK                   0x0100  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_SHIFT                       8  /* HPOUT1_VU */
+#define WM8993_HPOUT1_VU_WIDTH                       1  /* HPOUT1_VU */
+#define WM8993_HPOUT1R_ZC                       0x0080  /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_MASK                  0x0080  /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_SHIFT                      7  /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_ZC_WIDTH                      1  /* HPOUT1R_ZC */
+#define WM8993_HPOUT1R_MUTE_N                   0x0040  /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_MASK              0x0040  /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_SHIFT                  6  /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_MUTE_N_WIDTH                  1  /* HPOUT1R_MUTE_N */
+#define WM8993_HPOUT1R_VOL_MASK                 0x003F  /* HPOUT1R_VOL - [5:0] */
+#define WM8993_HPOUT1R_VOL_SHIFT                     0  /* HPOUT1R_VOL - [5:0] */
+#define WM8993_HPOUT1R_VOL_WIDTH                     6  /* HPOUT1R_VOL - [5:0] */
+
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8993_LINEOUT1N_MUTE                   0x0040  /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_MASK              0x0040  /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_SHIFT                  6  /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1N_MUTE_WIDTH                  1  /* LINEOUT1N_MUTE */
+#define WM8993_LINEOUT1P_MUTE                   0x0020  /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_MASK              0x0020  /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_SHIFT                  5  /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1P_MUTE_WIDTH                  1  /* LINEOUT1P_MUTE */
+#define WM8993_LINEOUT1_VOL                     0x0010  /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_MASK                0x0010  /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_SHIFT                    4  /* LINEOUT1_VOL */
+#define WM8993_LINEOUT1_VOL_WIDTH                    1  /* LINEOUT1_VOL */
+#define WM8993_LINEOUT2N_MUTE                   0x0004  /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_MASK              0x0004  /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_SHIFT                  2  /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2N_MUTE_WIDTH                  1  /* LINEOUT2N_MUTE */
+#define WM8993_LINEOUT2P_MUTE                   0x0002  /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_MASK              0x0002  /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_SHIFT                  1  /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2P_MUTE_WIDTH                  1  /* LINEOUT2P_MUTE */
+#define WM8993_LINEOUT2_VOL                     0x0001  /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_MASK                0x0001  /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_SHIFT                    0  /* LINEOUT2_VOL */
+#define WM8993_LINEOUT2_VOL_WIDTH                    1  /* LINEOUT2_VOL */
+
+/*
+ * R31 (0x1F) - HPOUT2 Volume
+ */
+#define WM8993_HPOUT2_MUTE                      0x0020  /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_MASK                 0x0020  /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_SHIFT                     5  /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_MUTE_WIDTH                     1  /* HPOUT2_MUTE */
+#define WM8993_HPOUT2_VOL                       0x0010  /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_MASK                  0x0010  /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_SHIFT                      4  /* HPOUT2_VOL */
+#define WM8993_HPOUT2_VOL_WIDTH                      1  /* HPOUT2_VOL */
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8993_MIXOUT_VU                        0x0100  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_MASK                   0x0100  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_SHIFT                       8  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_WIDTH                       1  /* MIXOUT_VU */
+#define WM8993_MIXOUTL_ZC                       0x0080  /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_MASK                  0x0080  /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_SHIFT                      7  /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_ZC_WIDTH                      1  /* MIXOUTL_ZC */
+#define WM8993_MIXOUTL_MUTE_N                   0x0040  /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_MASK              0x0040  /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_SHIFT                  6  /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_MUTE_N_WIDTH                  1  /* MIXOUTL_MUTE_N */
+#define WM8993_MIXOUTL_VOL_MASK                 0x003F  /* MIXOUTL_VOL - [5:0] */
+#define WM8993_MIXOUTL_VOL_SHIFT                     0  /* MIXOUTL_VOL - [5:0] */
+#define WM8993_MIXOUTL_VOL_WIDTH                     6  /* MIXOUTL_VOL - [5:0] */
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8993_MIXOUT_VU                        0x0100  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_MASK                   0x0100  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_SHIFT                       8  /* MIXOUT_VU */
+#define WM8993_MIXOUT_VU_WIDTH                       1  /* MIXOUT_VU */
+#define WM8993_MIXOUTR_ZC                       0x0080  /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_MASK                  0x0080  /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_SHIFT                      7  /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_ZC_WIDTH                      1  /* MIXOUTR_ZC */
+#define WM8993_MIXOUTR_MUTE_N                   0x0040  /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_MASK              0x0040  /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_SHIFT                  6  /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_MUTE_N_WIDTH                  1  /* MIXOUTR_MUTE_N */
+#define WM8993_MIXOUTR_VOL_MASK                 0x003F  /* MIXOUTR_VOL - [5:0] */
+#define WM8993_MIXOUTR_VOL_SHIFT                     0  /* MIXOUTR_VOL - [5:0] */
+#define WM8993_MIXOUTR_VOL_WIDTH                     6  /* MIXOUTR_VOL - [5:0] */
+
+/*
+ * R34 (0x22) - SPKMIXL Attenuation
+ */
+#define WM8993_MIXINL_SPKMIXL_VOL               0x0020  /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_MASK          0x0020  /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_SHIFT              5  /* MIXINL_SPKMIXL_VOL */
+#define WM8993_MIXINL_SPKMIXL_VOL_WIDTH              1  /* MIXINL_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL                0x0010  /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_MASK           0x0010  /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_SHIFT               4  /* IN1LP_SPKMIXL_VOL */
+#define WM8993_IN1LP_SPKMIXL_VOL_WIDTH               1  /* IN1LP_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL              0x0008  /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_MASK         0x0008  /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_SHIFT             3  /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_MIXOUTL_SPKMIXL_VOL_WIDTH             1  /* MIXOUTL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL                 0x0004  /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_MASK            0x0004  /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_SHIFT                2  /* DACL_SPKMIXL_VOL */
+#define WM8993_DACL_SPKMIXL_VOL_WIDTH                1  /* DACL_SPKMIXL_VOL */
+#define WM8993_SPKMIXL_VOL_MASK                 0x0003  /* SPKMIXL_VOL - [1:0] */
+#define WM8993_SPKMIXL_VOL_SHIFT                     0  /* SPKMIXL_VOL - [1:0] */
+#define WM8993_SPKMIXL_VOL_WIDTH                     2  /* SPKMIXL_VOL - [1:0] */
+
+/*
+ * R35 (0x23) - SPKMIXR Attenuation
+ */
+#define WM8993_SPKOUT_CLASSAB_MODE              0x0100  /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_MASK         0x0100  /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_SHIFT             8  /* SPKOUT_CLASSAB_MODE */
+#define WM8993_SPKOUT_CLASSAB_MODE_WIDTH             1  /* SPKOUT_CLASSAB_MODE */
+#define WM8993_MIXINR_SPKMIXR_VOL               0x0020  /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_MASK          0x0020  /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_SHIFT              5  /* MIXINR_SPKMIXR_VOL */
+#define WM8993_MIXINR_SPKMIXR_VOL_WIDTH              1  /* MIXINR_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL                0x0010  /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_MASK           0x0010  /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_SHIFT               4  /* IN1RP_SPKMIXR_VOL */
+#define WM8993_IN1RP_SPKMIXR_VOL_WIDTH               1  /* IN1RP_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL              0x0008  /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_MASK         0x0008  /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_SHIFT             3  /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_MIXOUTR_SPKMIXR_VOL_WIDTH             1  /* MIXOUTR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL                 0x0004  /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_MASK            0x0004  /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_SHIFT                2  /* DACR_SPKMIXR_VOL */
+#define WM8993_DACR_SPKMIXR_VOL_WIDTH                1  /* DACR_SPKMIXR_VOL */
+#define WM8993_SPKMIXR_VOL_MASK                 0x0003  /* SPKMIXR_VOL - [1:0] */
+#define WM8993_SPKMIXR_VOL_SHIFT                     0  /* SPKMIXR_VOL - [1:0] */
+#define WM8993_SPKMIXR_VOL_WIDTH                     2  /* SPKMIXR_VOL - [1:0] */
+
+/*
+ * R36 (0x24) - SPKOUT Mixers
+ */
+#define WM8993_VRX_TO_SPKOUTL                   0x0020  /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_MASK              0x0020  /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_SHIFT                  5  /* VRX_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTL_WIDTH                  1  /* VRX_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL               0x0010  /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_MASK          0x0010  /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_SHIFT              4  /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXL_TO_SPKOUTL_WIDTH              1  /* SPKMIXL_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL               0x0008  /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_MASK          0x0008  /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_SHIFT              3  /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_SPKMIXR_TO_SPKOUTL_WIDTH              1  /* SPKMIXR_TO_SPKOUTL */
+#define WM8993_VRX_TO_SPKOUTR                   0x0004  /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_MASK              0x0004  /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_SHIFT                  2  /* VRX_TO_SPKOUTR */
+#define WM8993_VRX_TO_SPKOUTR_WIDTH                  1  /* VRX_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR               0x0002  /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_MASK          0x0002  /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_SHIFT              1  /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXL_TO_SPKOUTR_WIDTH              1  /* SPKMIXL_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR               0x0001  /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_MASK          0x0001  /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_SHIFT              0  /* SPKMIXR_TO_SPKOUTR */
+#define WM8993_SPKMIXR_TO_SPKOUTR_WIDTH              1  /* SPKMIXR_TO_SPKOUTR */
+
+/*
+ * R37 (0x25) - SPKOUT Boost
+ */
+#define WM8993_SPKOUTL_BOOST_MASK               0x0038  /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTL_BOOST_SHIFT                   3  /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTL_BOOST_WIDTH                   3  /* SPKOUTL_BOOST - [5:3] */
+#define WM8993_SPKOUTR_BOOST_MASK               0x0007  /* SPKOUTR_BOOST - [2:0] */
+#define WM8993_SPKOUTR_BOOST_SHIFT                   0  /* SPKOUTR_BOOST - [2:0] */
+#define WM8993_SPKOUTR_BOOST_WIDTH                   3  /* SPKOUTR_BOOST - [2:0] */
+
+/*
+ * R38 (0x26) - Speaker Volume Left
+ */
+#define WM8993_SPKOUT_VU                        0x0100  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_MASK                   0x0100  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_SHIFT                       8  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_WIDTH                       1  /* SPKOUT_VU */
+#define WM8993_SPKOUTL_ZC                       0x0080  /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_MASK                  0x0080  /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_SHIFT                      7  /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_ZC_WIDTH                      1  /* SPKOUTL_ZC */
+#define WM8993_SPKOUTL_MUTE_N                   0x0040  /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_MASK              0x0040  /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_SHIFT                  6  /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_MUTE_N_WIDTH                  1  /* SPKOUTL_MUTE_N */
+#define WM8993_SPKOUTL_VOL_MASK                 0x003F  /* SPKOUTL_VOL - [5:0] */
+#define WM8993_SPKOUTL_VOL_SHIFT                     0  /* SPKOUTL_VOL - [5:0] */
+#define WM8993_SPKOUTL_VOL_WIDTH                     6  /* SPKOUTL_VOL - [5:0] */
+
+/*
+ * R39 (0x27) - Speaker Volume Right
+ */
+#define WM8993_SPKOUT_VU                        0x0100  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_MASK                   0x0100  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_SHIFT                       8  /* SPKOUT_VU */
+#define WM8993_SPKOUT_VU_WIDTH                       1  /* SPKOUT_VU */
+#define WM8993_SPKOUTR_ZC                       0x0080  /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_MASK                  0x0080  /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_SHIFT                      7  /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_ZC_WIDTH                      1  /* SPKOUTR_ZC */
+#define WM8993_SPKOUTR_MUTE_N                   0x0040  /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_MASK              0x0040  /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_SHIFT                  6  /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_MUTE_N_WIDTH                  1  /* SPKOUTR_MUTE_N */
+#define WM8993_SPKOUTR_VOL_MASK                 0x003F  /* SPKOUTR_VOL - [5:0] */
+#define WM8993_SPKOUTR_VOL_SHIFT                     0  /* SPKOUTR_VOL - [5:0] */
+#define WM8993_SPKOUTR_VOL_WIDTH                     6  /* SPKOUTR_VOL - [5:0] */
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8993_IN2LP_TO_IN2L                    0x0080  /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_MASK               0x0080  /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_SHIFT                   7  /* IN2LP_TO_IN2L */
+#define WM8993_IN2LP_TO_IN2L_WIDTH                   1  /* IN2LP_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L                    0x0040  /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_MASK               0x0040  /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_SHIFT                   6  /* IN2LN_TO_IN2L */
+#define WM8993_IN2LN_TO_IN2L_WIDTH                   1  /* IN2LN_TO_IN2L */
+#define WM8993_IN1LP_TO_IN1L                    0x0020  /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_MASK               0x0020  /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_SHIFT                   5  /* IN1LP_TO_IN1L */
+#define WM8993_IN1LP_TO_IN1L_WIDTH                   1  /* IN1LP_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L                    0x0010  /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_MASK               0x0010  /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_SHIFT                   4  /* IN1LN_TO_IN1L */
+#define WM8993_IN1LN_TO_IN1L_WIDTH                   1  /* IN1LN_TO_IN1L */
+#define WM8993_IN2RP_TO_IN2R                    0x0008  /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_MASK               0x0008  /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_SHIFT                   3  /* IN2RP_TO_IN2R */
+#define WM8993_IN2RP_TO_IN2R_WIDTH                   1  /* IN2RP_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R                    0x0004  /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_MASK               0x0004  /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_SHIFT                   2  /* IN2RN_TO_IN2R */
+#define WM8993_IN2RN_TO_IN2R_WIDTH                   1  /* IN2RN_TO_IN2R */
+#define WM8993_IN1RP_TO_IN1R                    0x0002  /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_MASK               0x0002  /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_SHIFT                   1  /* IN1RP_TO_IN1R */
+#define WM8993_IN1RP_TO_IN1R_WIDTH                   1  /* IN1RP_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R                    0x0001  /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_MASK               0x0001  /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_SHIFT                   0  /* IN1RN_TO_IN1R */
+#define WM8993_IN1RN_TO_IN1R_WIDTH                   1  /* IN1RN_TO_IN1R */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8993_IN2L_TO_MIXINL                   0x0100  /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_MASK              0x0100  /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_SHIFT                  8  /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_TO_MIXINL_WIDTH                  1  /* IN2L_TO_MIXINL */
+#define WM8993_IN2L_MIXINL_VOL                  0x0080  /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_MASK             0x0080  /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_SHIFT                 7  /* IN2L_MIXINL_VOL */
+#define WM8993_IN2L_MIXINL_VOL_WIDTH                 1  /* IN2L_MIXINL_VOL */
+#define WM8993_IN1L_TO_MIXINL                   0x0020  /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_MASK              0x0020  /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_SHIFT                  5  /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_TO_MIXINL_WIDTH                  1  /* IN1L_TO_MIXINL */
+#define WM8993_IN1L_MIXINL_VOL                  0x0010  /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_MASK             0x0010  /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_SHIFT                 4  /* IN1L_MIXINL_VOL */
+#define WM8993_IN1L_MIXINL_VOL_WIDTH                 1  /* IN1L_MIXINL_VOL */
+#define WM8993_MIXOUTL_MIXINL_VOL_MASK          0x0007  /* MIXOUTL_MIXINL_VOL - [2:0] */
+#define WM8993_MIXOUTL_MIXINL_VOL_SHIFT              0  /* MIXOUTL_MIXINL_VOL - [2:0] */
+#define WM8993_MIXOUTL_MIXINL_VOL_WIDTH              3  /* MIXOUTL_MIXINL_VOL - [2:0] */
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8993_IN2R_TO_MIXINR                   0x0100  /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_MASK              0x0100  /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_SHIFT                  8  /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_TO_MIXINR_WIDTH                  1  /* IN2R_TO_MIXINR */
+#define WM8993_IN2R_MIXINR_VOL                  0x0080  /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_MASK             0x0080  /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_SHIFT                 7  /* IN2R_MIXINR_VOL */
+#define WM8993_IN2R_MIXINR_VOL_WIDTH                 1  /* IN2R_MIXINR_VOL */
+#define WM8993_IN1R_TO_MIXINR                   0x0020  /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_MASK              0x0020  /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_SHIFT                  5  /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_TO_MIXINR_WIDTH                  1  /* IN1R_TO_MIXINR */
+#define WM8993_IN1R_MIXINR_VOL                  0x0010  /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_MASK             0x0010  /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_SHIFT                 4  /* IN1R_MIXINR_VOL */
+#define WM8993_IN1R_MIXINR_VOL_WIDTH                 1  /* IN1R_MIXINR_VOL */
+#define WM8993_MIXOUTR_MIXINR_VOL_MASK          0x0007  /* MIXOUTR_MIXINR_VOL - [2:0] */
+#define WM8993_MIXOUTR_MIXINR_VOL_SHIFT              0  /* MIXOUTR_MIXINR_VOL - [2:0] */
+#define WM8993_MIXOUTR_MIXINR_VOL_WIDTH              3  /* MIXOUTR_MIXINR_VOL - [2:0] */
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8993_IN1LP_MIXINL_VOL_MASK            0x01C0  /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_IN1LP_MIXINL_VOL_SHIFT                6  /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_IN1LP_MIXINL_VOL_WIDTH                3  /* IN1LP_MIXINL_VOL - [8:6] */
+#define WM8993_VRX_MIXINL_VOL_MASK              0x0007  /* VRX_MIXINL_VOL - [2:0] */
+#define WM8993_VRX_MIXINL_VOL_SHIFT                  0  /* VRX_MIXINL_VOL - [2:0] */
+#define WM8993_VRX_MIXINL_VOL_WIDTH                  3  /* VRX_MIXINL_VOL - [2:0] */
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8993_IN1RP_MIXINR_VOL_MASK            0x01C0  /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_IN1RP_MIXINR_VOL_SHIFT                6  /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_IN1RP_MIXINR_VOL_WIDTH                3  /* IN1RP_MIXINR_VOL - [8:6] */
+#define WM8993_VRX_MIXINR_VOL_MASK              0x0007  /* VRX_MIXINR_VOL - [2:0] */
+#define WM8993_VRX_MIXINR_VOL_SHIFT                  0  /* VRX_MIXINR_VOL - [2:0] */
+#define WM8993_VRX_MIXINR_VOL_WIDTH                  3  /* VRX_MIXINR_VOL - [2:0] */
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8993_DACL_TO_HPOUT1L                  0x0100  /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_MASK             0x0100  /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_SHIFT                 8  /* DACL_TO_HPOUT1L */
+#define WM8993_DACL_TO_HPOUT1L_WIDTH                 1  /* DACL_TO_HPOUT1L */
+#define WM8993_MIXINR_TO_MIXOUTL                0x0080  /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_MASK           0x0080  /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_SHIFT               7  /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINR_TO_MIXOUTL_WIDTH               1  /* MIXINR_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL                0x0040  /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_MASK           0x0040  /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_SHIFT               6  /* MIXINL_TO_MIXOUTL */
+#define WM8993_MIXINL_TO_MIXOUTL_WIDTH               1  /* MIXINL_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL                 0x0020  /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_MASK            0x0020  /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_SHIFT                5  /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2RN_TO_MIXOUTL_WIDTH                1  /* IN2RN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL                 0x0010  /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_MASK            0x0010  /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_SHIFT                4  /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN2LN_TO_MIXOUTL_WIDTH                1  /* IN2LN_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL                  0x0008  /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_MASK             0x0008  /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_SHIFT                 3  /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1R_TO_MIXOUTL_WIDTH                 1  /* IN1R_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL                  0x0004  /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_MASK             0x0004  /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_SHIFT                 2  /* IN1L_TO_MIXOUTL */
+#define WM8993_IN1L_TO_MIXOUTL_WIDTH                 1  /* IN1L_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL                 0x0002  /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_MASK            0x0002  /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_SHIFT                1  /* IN2LP_TO_MIXOUTL */
+#define WM8993_IN2LP_TO_MIXOUTL_WIDTH                1  /* IN2LP_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL                  0x0001  /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_MASK             0x0001  /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_SHIFT                 0  /* DACL_TO_MIXOUTL */
+#define WM8993_DACL_TO_MIXOUTL_WIDTH                 1  /* DACL_TO_MIXOUTL */
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8993_DACR_TO_HPOUT1R                  0x0100  /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_MASK             0x0100  /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_SHIFT                 8  /* DACR_TO_HPOUT1R */
+#define WM8993_DACR_TO_HPOUT1R_WIDTH                 1  /* DACR_TO_HPOUT1R */
+#define WM8993_MIXINL_TO_MIXOUTR                0x0080  /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_MASK           0x0080  /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_SHIFT               7  /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINL_TO_MIXOUTR_WIDTH               1  /* MIXINL_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR                0x0040  /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_MASK           0x0040  /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_SHIFT               6  /* MIXINR_TO_MIXOUTR */
+#define WM8993_MIXINR_TO_MIXOUTR_WIDTH               1  /* MIXINR_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR                 0x0020  /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_MASK            0x0020  /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_SHIFT                5  /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2LN_TO_MIXOUTR_WIDTH                1  /* IN2LN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR                 0x0010  /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_MASK            0x0010  /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_SHIFT                4  /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN2RN_TO_MIXOUTR_WIDTH                1  /* IN2RN_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR                  0x0008  /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_MASK             0x0008  /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_SHIFT                 3  /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1L_TO_MIXOUTR_WIDTH                 1  /* IN1L_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR                  0x0004  /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_MASK             0x0004  /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_SHIFT                 2  /* IN1R_TO_MIXOUTR */
+#define WM8993_IN1R_TO_MIXOUTR_WIDTH                 1  /* IN1R_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR                 0x0002  /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_MASK            0x0002  /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_SHIFT                1  /* IN2RP_TO_MIXOUTR */
+#define WM8993_IN2RP_TO_MIXOUTR_WIDTH                1  /* IN2RP_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR                  0x0001  /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_MASK             0x0001  /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_SHIFT                 0  /* DACR_TO_MIXOUTR */
+#define WM8993_DACR_TO_MIXOUTR_WIDTH                 1  /* DACR_TO_MIXOUTR */
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8993_IN2LP_MIXOUTL_VOL_MASK           0x0E00  /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LP_MIXOUTL_VOL_SHIFT               9  /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LP_MIXOUTL_VOL_WIDTH               3  /* IN2LP_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2LN_MIXOUTL_VOL_MASK           0x01C0  /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTL_VOL_SHIFT               6  /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTL_VOL_WIDTH               3  /* IN2LN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN1R_MIXOUTL_VOL_MASK            0x0038  /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTL_VOL_SHIFT                3  /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTL_VOL_WIDTH                3  /* IN1R_MIXOUTL_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTL_VOL_MASK            0x0007  /* IN1L_MIXOUTL_VOL - [2:0] */
+#define WM8993_IN1L_MIXOUTL_VOL_SHIFT                0  /* IN1L_MIXOUTL_VOL - [2:0] */
+#define WM8993_IN1L_MIXOUTL_VOL_WIDTH                3  /* IN1L_MIXOUTL_VOL - [2:0] */
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8993_IN2RP_MIXOUTR_VOL_MASK           0x0E00  /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RP_MIXOUTR_VOL_SHIFT               9  /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RP_MIXOUTR_VOL_WIDTH               3  /* IN2RP_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2RN_MIXOUTR_VOL_MASK           0x01C0  /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTR_VOL_SHIFT               6  /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTR_VOL_WIDTH               3  /* IN2RN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN1L_MIXOUTR_VOL_MASK            0x0038  /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTR_VOL_SHIFT                3  /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1L_MIXOUTR_VOL_WIDTH                3  /* IN1L_MIXOUTR_VOL - [5:3] */
+#define WM8993_IN1R_MIXOUTR_VOL_MASK            0x0007  /* IN1R_MIXOUTR_VOL - [2:0] */
+#define WM8993_IN1R_MIXOUTR_VOL_SHIFT                0  /* IN1R_MIXOUTR_VOL - [2:0] */
+#define WM8993_IN1R_MIXOUTR_VOL_WIDTH                3  /* IN1R_MIXOUTR_VOL - [2:0] */
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8993_DACL_MIXOUTL_VOL_MASK            0x0E00  /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_DACL_MIXOUTL_VOL_SHIFT                9  /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_DACL_MIXOUTL_VOL_WIDTH                3  /* DACL_MIXOUTL_VOL - [11:9] */
+#define WM8993_IN2RN_MIXOUTL_VOL_MASK           0x01C0  /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTL_VOL_SHIFT               6  /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_IN2RN_MIXOUTL_VOL_WIDTH               3  /* IN2RN_MIXOUTL_VOL - [8:6] */
+#define WM8993_MIXINR_MIXOUTL_VOL_MASK          0x0038  /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTL_VOL_SHIFT              3  /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTL_VOL_WIDTH              3  /* MIXINR_MIXOUTL_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTL_VOL_MASK          0x0007  /* MIXINL_MIXOUTL_VOL - [2:0] */
+#define WM8993_MIXINL_MIXOUTL_VOL_SHIFT              0  /* MIXINL_MIXOUTL_VOL - [2:0] */
+#define WM8993_MIXINL_MIXOUTL_VOL_WIDTH              3  /* MIXINL_MIXOUTL_VOL - [2:0] */
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8993_DACR_MIXOUTR_VOL_MASK            0x0E00  /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_DACR_MIXOUTR_VOL_SHIFT                9  /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_DACR_MIXOUTR_VOL_WIDTH                3  /* DACR_MIXOUTR_VOL - [11:9] */
+#define WM8993_IN2LN_MIXOUTR_VOL_MASK           0x01C0  /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTR_VOL_SHIFT               6  /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_IN2LN_MIXOUTR_VOL_WIDTH               3  /* IN2LN_MIXOUTR_VOL - [8:6] */
+#define WM8993_MIXINL_MIXOUTR_VOL_MASK          0x0038  /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTR_VOL_SHIFT              3  /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINL_MIXOUTR_VOL_WIDTH              3  /* MIXINL_MIXOUTR_VOL - [5:3] */
+#define WM8993_MIXINR_MIXOUTR_VOL_MASK          0x0007  /* MIXINR_MIXOUTR_VOL - [2:0] */
+#define WM8993_MIXINR_MIXOUTR_VOL_SHIFT              0  /* MIXINR_MIXOUTR_VOL - [2:0] */
+#define WM8993_MIXINR_MIXOUTR_VOL_WIDTH              3  /* MIXINR_MIXOUTR_VOL - [2:0] */
+
+/*
+ * R51 (0x33) - HPOUT2 Mixer
+ */
+#define WM8993_VRX_TO_HPOUT2                    0x0020  /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_MASK               0x0020  /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_SHIFT                   5  /* VRX_TO_HPOUT2 */
+#define WM8993_VRX_TO_HPOUT2_WIDTH                   1  /* VRX_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2             0x0010  /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_MASK        0x0010  /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_SHIFT            4  /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTLVOL_TO_HPOUT2_WIDTH            1  /* MIXOUTLVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2             0x0008  /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_MASK        0x0008  /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_SHIFT            3  /* MIXOUTRVOL_TO_HPOUT2 */
+#define WM8993_MIXOUTRVOL_TO_HPOUT2_WIDTH            1  /* MIXOUTRVOL_TO_HPOUT2 */
+
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8993_MIXOUTL_TO_LINEOUT1N             0x0040  /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_MASK        0x0040  /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_SHIFT            6  /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTL_TO_LINEOUT1N_WIDTH            1  /* MIXOUTL_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N             0x0020  /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_MASK        0x0020  /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_SHIFT            5  /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_MIXOUTR_TO_LINEOUT1N_WIDTH            1  /* MIXOUTR_TO_LINEOUT1N */
+#define WM8993_LINEOUT1_MODE                    0x0010  /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_MASK               0x0010  /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_SHIFT                   4  /* LINEOUT1_MODE */
+#define WM8993_LINEOUT1_MODE_WIDTH                   1  /* LINEOUT1_MODE */
+#define WM8993_IN1R_TO_LINEOUT1P                0x0004  /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_MASK           0x0004  /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_SHIFT               2  /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1R_TO_LINEOUT1P_WIDTH               1  /* IN1R_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P                0x0002  /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_MASK           0x0002  /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_SHIFT               1  /* IN1L_TO_LINEOUT1P */
+#define WM8993_IN1L_TO_LINEOUT1P_WIDTH               1  /* IN1L_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P             0x0001  /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_MASK        0x0001  /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_SHIFT            0  /* MIXOUTL_TO_LINEOUT1P */
+#define WM8993_MIXOUTL_TO_LINEOUT1P_WIDTH            1  /* MIXOUTL_TO_LINEOUT1P */
+
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8993_MIXOUTR_TO_LINEOUT2N             0x0040  /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_MASK        0x0040  /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_SHIFT            6  /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTR_TO_LINEOUT2N_WIDTH            1  /* MIXOUTR_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N             0x0020  /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_MASK        0x0020  /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_SHIFT            5  /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_MIXOUTL_TO_LINEOUT2N_WIDTH            1  /* MIXOUTL_TO_LINEOUT2N */
+#define WM8993_LINEOUT2_MODE                    0x0010  /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_MASK               0x0010  /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_SHIFT                   4  /* LINEOUT2_MODE */
+#define WM8993_LINEOUT2_MODE_WIDTH                   1  /* LINEOUT2_MODE */
+#define WM8993_IN1L_TO_LINEOUT2P                0x0004  /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_MASK           0x0004  /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_SHIFT               2  /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1L_TO_LINEOUT2P_WIDTH               1  /* IN1L_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P                0x0002  /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_MASK           0x0002  /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_SHIFT               1  /* IN1R_TO_LINEOUT2P */
+#define WM8993_IN1R_TO_LINEOUT2P_WIDTH               1  /* IN1R_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P             0x0001  /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_MASK        0x0001  /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_SHIFT            0  /* MIXOUTR_TO_LINEOUT2P */
+#define WM8993_MIXOUTR_TO_LINEOUT2P_WIDTH            1  /* MIXOUTR_TO_LINEOUT2P */
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8993_SPKAB_REF_SEL                    0x0100  /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_MASK               0x0100  /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_SHIFT                   8  /* SPKAB_REF_SEL */
+#define WM8993_SPKAB_REF_SEL_WIDTH                   1  /* SPKAB_REF_SEL */
+#define WM8993_MIXINL_TO_SPKMIXL                0x0080  /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_MASK           0x0080  /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_SHIFT               7  /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINL_TO_SPKMIXL_WIDTH               1  /* MIXINL_TO_SPKMIXL */
+#define WM8993_MIXINR_TO_SPKMIXR                0x0040  /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_MASK           0x0040  /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_SHIFT               6  /* MIXINR_TO_SPKMIXR */
+#define WM8993_MIXINR_TO_SPKMIXR_WIDTH               1  /* MIXINR_TO_SPKMIXR */
+#define WM8993_IN1LP_TO_SPKMIXL                 0x0020  /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_MASK            0x0020  /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_SHIFT                5  /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1LP_TO_SPKMIXL_WIDTH                1  /* IN1LP_TO_SPKMIXL */
+#define WM8993_IN1RP_TO_SPKMIXR                 0x0010  /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_MASK            0x0010  /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_SHIFT                4  /* IN1RP_TO_SPKMIXR */
+#define WM8993_IN1RP_TO_SPKMIXR_WIDTH                1  /* IN1RP_TO_SPKMIXR */
+#define WM8993_MIXOUTL_TO_SPKMIXL               0x0008  /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_MASK          0x0008  /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_SHIFT              3  /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTL_TO_SPKMIXL_WIDTH              1  /* MIXOUTL_TO_SPKMIXL */
+#define WM8993_MIXOUTR_TO_SPKMIXR               0x0004  /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_MASK          0x0004  /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_SHIFT              2  /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_MIXOUTR_TO_SPKMIXR_WIDTH              1  /* MIXOUTR_TO_SPKMIXR */
+#define WM8993_DACL_TO_SPKMIXL                  0x0002  /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_MASK             0x0002  /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_SHIFT                 1  /* DACL_TO_SPKMIXL */
+#define WM8993_DACL_TO_SPKMIXL_WIDTH                 1  /* DACL_TO_SPKMIXL */
+#define WM8993_DACR_TO_SPKMIXR                  0x0001  /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_MASK             0x0001  /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_SHIFT                 0  /* DACR_TO_SPKMIXR */
+#define WM8993_DACR_TO_SPKMIXR_WIDTH                 1  /* DACR_TO_SPKMIXR */
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8993_LINEOUT1_FB                      0x0080  /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_MASK                 0x0080  /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_SHIFT                     7  /* LINEOUT1_FB */
+#define WM8993_LINEOUT1_FB_WIDTH                     1  /* LINEOUT1_FB */
+#define WM8993_LINEOUT2_FB                      0x0040  /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_MASK                 0x0040  /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_SHIFT                     6  /* LINEOUT2_FB */
+#define WM8993_LINEOUT2_FB_WIDTH                     1  /* LINEOUT2_FB */
+#define WM8993_VROI                             0x0001  /* VROI */
+#define WM8993_VROI_MASK                        0x0001  /* VROI */
+#define WM8993_VROI_SHIFT                            0  /* VROI */
+#define WM8993_VROI_WIDTH                            1  /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8993_LINEOUT_VMID_BUF_ENA             0x0080  /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_MASK        0x0080  /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_SHIFT            7  /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_LINEOUT_VMID_BUF_ENA_WIDTH            1  /* LINEOUT_VMID_BUF_ENA */
+#define WM8993_HPOUT2_IN_ENA                    0x0040  /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_MASK               0x0040  /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_SHIFT                   6  /* HPOUT2_IN_ENA */
+#define WM8993_HPOUT2_IN_ENA_WIDTH                   1  /* HPOUT2_IN_ENA */
+#define WM8993_LINEOUT1_DISCH                   0x0020  /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_MASK              0x0020  /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_SHIFT                  5  /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT1_DISCH_WIDTH                  1  /* LINEOUT1_DISCH */
+#define WM8993_LINEOUT2_DISCH                   0x0010  /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_MASK              0x0010  /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_SHIFT                  4  /* LINEOUT2_DISCH */
+#define WM8993_LINEOUT2_DISCH_WIDTH                  1  /* LINEOUT2_DISCH */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8993_VMID_RAMP_MASK                   0x0060  /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_RAMP_SHIFT                       5  /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_RAMP_WIDTH                       2  /* VMID_RAMP - [6:5] */
+#define WM8993_VMID_BUF_ENA                     0x0008  /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_MASK                0x0008  /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_SHIFT                    3  /* VMID_BUF_ENA */
+#define WM8993_VMID_BUF_ENA_WIDTH                    1  /* VMID_BUF_ENA */
+#define WM8993_STARTUP_BIAS_ENA                 0x0004  /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_MASK            0x0004  /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_SHIFT                2  /* STARTUP_BIAS_ENA */
+#define WM8993_STARTUP_BIAS_ENA_WIDTH                1  /* STARTUP_BIAS_ENA */
+#define WM8993_BIAS_SRC                         0x0002  /* BIAS_SRC */
+#define WM8993_BIAS_SRC_MASK                    0x0002  /* BIAS_SRC */
+#define WM8993_BIAS_SRC_SHIFT                        1  /* BIAS_SRC */
+#define WM8993_BIAS_SRC_WIDTH                        1  /* BIAS_SRC */
+#define WM8993_VMID_DISCH                       0x0001  /* VMID_DISCH */
+#define WM8993_VMID_DISCH_MASK                  0x0001  /* VMID_DISCH */
+#define WM8993_VMID_DISCH_SHIFT                      0  /* VMID_DISCH */
+#define WM8993_VMID_DISCH_WIDTH                      1  /* VMID_DISCH */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8993_JD_SCTHR_MASK                    0x00C0  /* JD_SCTHR - [7:6] */
+#define WM8993_JD_SCTHR_SHIFT                        6  /* JD_SCTHR - [7:6] */
+#define WM8993_JD_SCTHR_WIDTH                        2  /* JD_SCTHR - [7:6] */
+#define WM8993_JD_THR_MASK                      0x0030  /* JD_THR - [5:4] */
+#define WM8993_JD_THR_SHIFT                          4  /* JD_THR - [5:4] */
+#define WM8993_JD_THR_WIDTH                          2  /* JD_THR - [5:4] */
+#define WM8993_JD_ENA                           0x0004  /* JD_ENA */
+#define WM8993_JD_ENA_MASK                      0x0004  /* JD_ENA */
+#define WM8993_JD_ENA_SHIFT                          2  /* JD_ENA */
+#define WM8993_JD_ENA_WIDTH                          1  /* JD_ENA */
+#define WM8993_MICB2_LVL                        0x0002  /* MICB2_LVL */
+#define WM8993_MICB2_LVL_MASK                   0x0002  /* MICB2_LVL */
+#define WM8993_MICB2_LVL_SHIFT                       1  /* MICB2_LVL */
+#define WM8993_MICB2_LVL_WIDTH                       1  /* MICB2_LVL */
+#define WM8993_MICB1_LVL                        0x0001  /* MICB1_LVL */
+#define WM8993_MICB1_LVL_MASK                   0x0001  /* MICB1_LVL */
+#define WM8993_MICB1_LVL_SHIFT                       0  /* MICB1_LVL */
+#define WM8993_MICB1_LVL_WIDTH                       1  /* MICB1_LVL */
+
+/*
+ * R60 (0x3C) - FLL Control 1
+ */
+#define WM8993_FLL_FRAC                         0x0004  /* FLL_FRAC */
+#define WM8993_FLL_FRAC_MASK                    0x0004  /* FLL_FRAC */
+#define WM8993_FLL_FRAC_SHIFT                        2  /* FLL_FRAC */
+#define WM8993_FLL_FRAC_WIDTH                        1  /* FLL_FRAC */
+#define WM8993_FLL_OSC_ENA                      0x0002  /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_MASK                 0x0002  /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_SHIFT                     1  /* FLL_OSC_ENA */
+#define WM8993_FLL_OSC_ENA_WIDTH                     1  /* FLL_OSC_ENA */
+#define WM8993_FLL_ENA                          0x0001  /* FLL_ENA */
+#define WM8993_FLL_ENA_MASK                     0x0001  /* FLL_ENA */
+#define WM8993_FLL_ENA_SHIFT                         0  /* FLL_ENA */
+#define WM8993_FLL_ENA_WIDTH                         1  /* FLL_ENA */
+
+/*
+ * R61 (0x3D) - FLL Control 2
+ */
+#define WM8993_FLL_OUTDIV_MASK                  0x0700  /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_OUTDIV_SHIFT                      8  /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_OUTDIV_WIDTH                      3  /* FLL_OUTDIV - [10:8] */
+#define WM8993_FLL_CTRL_RATE_MASK               0x0070  /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_CTRL_RATE_SHIFT                   4  /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_CTRL_RATE_WIDTH                   3  /* FLL_CTRL_RATE - [6:4] */
+#define WM8993_FLL_FRATIO_MASK                  0x0007  /* FLL_FRATIO - [2:0] */
+#define WM8993_FLL_FRATIO_SHIFT                      0  /* FLL_FRATIO - [2:0] */
+#define WM8993_FLL_FRATIO_WIDTH                      3  /* FLL_FRATIO - [2:0] */
+
+/*
+ * R62 (0x3E) - FLL Control 3
+ */
+#define WM8993_FLL_K_MASK                       0xFFFF  /* FLL_K - [15:0] */
+#define WM8993_FLL_K_SHIFT                           0  /* FLL_K - [15:0] */
+#define WM8993_FLL_K_WIDTH                          16  /* FLL_K - [15:0] */
+
+/*
+ * R63 (0x3F) - FLL Control 4
+ */
+#define WM8993_FLL_N_MASK                       0x7FE0  /* FLL_N - [14:5] */
+#define WM8993_FLL_N_SHIFT                           5  /* FLL_N - [14:5] */
+#define WM8993_FLL_N_WIDTH                          10  /* FLL_N - [14:5] */
+#define WM8993_FLL_GAIN_MASK                    0x000F  /* FLL_GAIN - [3:0] */
+#define WM8993_FLL_GAIN_SHIFT                        0  /* FLL_GAIN - [3:0] */
+#define WM8993_FLL_GAIN_WIDTH                        4  /* FLL_GAIN - [3:0] */
+
+/*
+ * R64 (0x40) - FLL Control 5
+ */
+#define WM8993_FLL_FRC_NCO_VAL_MASK             0x1F80  /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO_VAL_SHIFT                 7  /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO_VAL_WIDTH                 6  /* FLL_FRC_NCO_VAL - [12:7] */
+#define WM8993_FLL_FRC_NCO                      0x0040  /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_MASK                 0x0040  /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_SHIFT                     6  /* FLL_FRC_NCO */
+#define WM8993_FLL_FRC_NCO_WIDTH                     1  /* FLL_FRC_NCO */
+#define WM8993_FLL_CLK_REF_DIV_MASK             0x0018  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_REF_DIV_SHIFT                 3  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_REF_DIV_WIDTH                 2  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM8993_FLL_CLK_SRC_MASK                 0x0003  /* FLL_CLK_SRC - [1:0] */
+#define WM8993_FLL_CLK_SRC_SHIFT                     0  /* FLL_CLK_SRC - [1:0] */
+#define WM8993_FLL_CLK_SRC_WIDTH                     2  /* FLL_CLK_SRC - [1:0] */
+
+/*
+ * R65 (0x41) - Clocking 3
+ */
+#define WM8993_CLK_DCS_DIV_MASK                 0x3C00  /* CLK_DCS_DIV - [13:10] */
+#define WM8993_CLK_DCS_DIV_SHIFT                    10  /* CLK_DCS_DIV - [13:10] */
+#define WM8993_CLK_DCS_DIV_WIDTH                     4  /* CLK_DCS_DIV - [13:10] */
+#define WM8993_SAMPLE_RATE_MASK                 0x0380  /* SAMPLE_RATE - [9:7] */
+#define WM8993_SAMPLE_RATE_SHIFT                     7  /* SAMPLE_RATE - [9:7] */
+#define WM8993_SAMPLE_RATE_WIDTH                     3  /* SAMPLE_RATE - [9:7] */
+#define WM8993_CLK_SYS_RATE_MASK                0x001E  /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_SYS_RATE_SHIFT                    1  /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_SYS_RATE_WIDTH                    4  /* CLK_SYS_RATE - [4:1] */
+#define WM8993_CLK_DSP_ENA                      0x0001  /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_MASK                 0x0001  /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_SHIFT                     0  /* CLK_DSP_ENA */
+#define WM8993_CLK_DSP_ENA_WIDTH                     1  /* CLK_DSP_ENA */
+
+/*
+ * R66 (0x42) - Clocking 4
+ */
+#define WM8993_DAC_DIV4                         0x0200  /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_MASK                    0x0200  /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_SHIFT                        9  /* DAC_DIV4 */
+#define WM8993_DAC_DIV4_WIDTH                        1  /* DAC_DIV4 */
+#define WM8993_CLK_256K_DIV_MASK                0x007E  /* CLK_256K_DIV - [6:1] */
+#define WM8993_CLK_256K_DIV_SHIFT                    1  /* CLK_256K_DIV - [6:1] */
+#define WM8993_CLK_256K_DIV_WIDTH                    6  /* CLK_256K_DIV - [6:1] */
+#define WM8993_SR_MODE                          0x0001  /* SR_MODE */
+#define WM8993_SR_MODE_MASK                     0x0001  /* SR_MODE */
+#define WM8993_SR_MODE_SHIFT                         0  /* SR_MODE */
+#define WM8993_SR_MODE_WIDTH                         1  /* SR_MODE */
+
+/*
+ * R67 (0x43) - MW Slave Control
+ */
+#define WM8993_MASK_WRITE_ENA                   0x0001  /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_MASK              0x0001  /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_SHIFT                  0  /* MASK_WRITE_ENA */
+#define WM8993_MASK_WRITE_ENA_WIDTH                  1  /* MASK_WRITE_ENA */
+
+/*
+ * R69 (0x45) - Bus Control 1
+ */
+#define WM8993_CLK_SYS_ENA                      0x0002  /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_MASK                 0x0002  /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_SHIFT                     1  /* CLK_SYS_ENA */
+#define WM8993_CLK_SYS_ENA_WIDTH                     1  /* CLK_SYS_ENA */
+
+/*
+ * R70 (0x46) - Write Sequencer 0
+ */
+#define WM8993_WSEQ_ENA                         0x0100  /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_MASK                    0x0100  /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_SHIFT                        8  /* WSEQ_ENA */
+#define WM8993_WSEQ_ENA_WIDTH                        1  /* WSEQ_ENA */
+#define WM8993_WSEQ_WRITE_INDEX_MASK            0x001F  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8993_WSEQ_WRITE_INDEX_SHIFT                0  /* WSEQ_WRITE_INDEX - [4:0] */
+#define WM8993_WSEQ_WRITE_INDEX_WIDTH                5  /* WSEQ_WRITE_INDEX - [4:0] */
+
+/*
+ * R71 (0x47) - Write Sequencer 1
+ */
+#define WM8993_WSEQ_DATA_WIDTH_MASK             0x7000  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_WIDTH_SHIFT                12  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_WIDTH_WIDTH                 3  /* WSEQ_DATA_WIDTH - [14:12] */
+#define WM8993_WSEQ_DATA_START_MASK             0x0F00  /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_DATA_START_SHIFT                 8  /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_DATA_START_WIDTH                 4  /* WSEQ_DATA_START - [11:8] */
+#define WM8993_WSEQ_ADDR_MASK                   0x00FF  /* WSEQ_ADDR - [7:0] */
+#define WM8993_WSEQ_ADDR_SHIFT                       0  /* WSEQ_ADDR - [7:0] */
+#define WM8993_WSEQ_ADDR_WIDTH                       8  /* WSEQ_ADDR - [7:0] */
+
+/*
+ * R72 (0x48) - Write Sequencer 2
+ */
+#define WM8993_WSEQ_EOS                         0x4000  /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_MASK                    0x4000  /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_SHIFT                       14  /* WSEQ_EOS */
+#define WM8993_WSEQ_EOS_WIDTH                        1  /* WSEQ_EOS */
+#define WM8993_WSEQ_DELAY_MASK                  0x0F00  /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DELAY_SHIFT                      8  /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DELAY_WIDTH                      4  /* WSEQ_DELAY - [11:8] */
+#define WM8993_WSEQ_DATA_MASK                   0x00FF  /* WSEQ_DATA - [7:0] */
+#define WM8993_WSEQ_DATA_SHIFT                       0  /* WSEQ_DATA - [7:0] */
+#define WM8993_WSEQ_DATA_WIDTH                       8  /* WSEQ_DATA - [7:0] */
+
+/*
+ * R73 (0x49) - Write Sequencer 3
+ */
+#define WM8993_WSEQ_ABORT                       0x0200  /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_MASK                  0x0200  /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_SHIFT                      9  /* WSEQ_ABORT */
+#define WM8993_WSEQ_ABORT_WIDTH                      1  /* WSEQ_ABORT */
+#define WM8993_WSEQ_START                       0x0100  /* WSEQ_START */
+#define WM8993_WSEQ_START_MASK                  0x0100  /* WSEQ_START */
+#define WM8993_WSEQ_START_SHIFT                      8  /* WSEQ_START */
+#define WM8993_WSEQ_START_WIDTH                      1  /* WSEQ_START */
+#define WM8993_WSEQ_START_INDEX_MASK            0x003F  /* WSEQ_START_INDEX - [5:0] */
+#define WM8993_WSEQ_START_INDEX_SHIFT                0  /* WSEQ_START_INDEX - [5:0] */
+#define WM8993_WSEQ_START_INDEX_WIDTH                6  /* WSEQ_START_INDEX - [5:0] */
+
+/*
+ * R74 (0x4A) - Write Sequencer 4
+ */
+#define WM8993_WSEQ_BUSY                        0x0001  /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_MASK                   0x0001  /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_SHIFT                       0  /* WSEQ_BUSY */
+#define WM8993_WSEQ_BUSY_WIDTH                       1  /* WSEQ_BUSY */
+
+/*
+ * R75 (0x4B) - Write Sequencer 5
+ */
+#define WM8993_WSEQ_CURRENT_INDEX_MASK          0x003F  /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM8993_WSEQ_CURRENT_INDEX_SHIFT              0  /* WSEQ_CURRENT_INDEX - [5:0] */
+#define WM8993_WSEQ_CURRENT_INDEX_WIDTH              6  /* WSEQ_CURRENT_INDEX - [5:0] */
+
+/*
+ * R76 (0x4C) - Charge Pump 1
+ */
+#define WM8993_CP_ENA                           0x8000  /* CP_ENA */
+#define WM8993_CP_ENA_MASK                      0x8000  /* CP_ENA */
+#define WM8993_CP_ENA_SHIFT                         15  /* CP_ENA */
+#define WM8993_CP_ENA_WIDTH                          1  /* CP_ENA */
+
+/*
+ * R81 (0x51) - Class W 0
+ */
+#define WM8993_CP_DYN_FREQ                      0x0002  /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_MASK                 0x0002  /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_SHIFT                     1  /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_FREQ_WIDTH                     1  /* CP_DYN_FREQ */
+#define WM8993_CP_DYN_V                         0x0001  /* CP_DYN_V */
+#define WM8993_CP_DYN_V_MASK                    0x0001  /* CP_DYN_V */
+#define WM8993_CP_DYN_V_SHIFT                        0  /* CP_DYN_V */
+#define WM8993_CP_DYN_V_WIDTH                        1  /* CP_DYN_V */
+
+/*
+ * R84 (0x54) - DC Servo 0
+ */
+#define WM8993_DCS_TRIG_SINGLE_1                0x2000  /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_MASK           0x2000  /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_SHIFT              13  /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_1_WIDTH               1  /* DCS_TRIG_SINGLE_1 */
+#define WM8993_DCS_TRIG_SINGLE_0                0x1000  /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_MASK           0x1000  /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_SHIFT              12  /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SINGLE_0_WIDTH               1  /* DCS_TRIG_SINGLE_0 */
+#define WM8993_DCS_TRIG_SERIES_1                0x0200  /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_MASK           0x0200  /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_SHIFT               9  /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_1_WIDTH               1  /* DCS_TRIG_SERIES_1 */
+#define WM8993_DCS_TRIG_SERIES_0                0x0100  /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_MASK           0x0100  /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_SHIFT               8  /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_SERIES_0_WIDTH               1  /* DCS_TRIG_SERIES_0 */
+#define WM8993_DCS_TRIG_STARTUP_1               0x0020  /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_MASK          0x0020  /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_SHIFT              5  /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_1_WIDTH              1  /* DCS_TRIG_STARTUP_1 */
+#define WM8993_DCS_TRIG_STARTUP_0               0x0010  /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_MASK          0x0010  /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_SHIFT              4  /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_STARTUP_0_WIDTH              1  /* DCS_TRIG_STARTUP_0 */
+#define WM8993_DCS_TRIG_DAC_WR_1                0x0008  /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_MASK           0x0008  /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_SHIFT               3  /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_1_WIDTH               1  /* DCS_TRIG_DAC_WR_1 */
+#define WM8993_DCS_TRIG_DAC_WR_0                0x0004  /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_MASK           0x0004  /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_SHIFT               2  /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_TRIG_DAC_WR_0_WIDTH               1  /* DCS_TRIG_DAC_WR_0 */
+#define WM8993_DCS_ENA_CHAN_1                   0x0002  /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_MASK              0x0002  /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_SHIFT                  1  /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_1_WIDTH                  1  /* DCS_ENA_CHAN_1 */
+#define WM8993_DCS_ENA_CHAN_0                   0x0001  /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_MASK              0x0001  /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_SHIFT                  0  /* DCS_ENA_CHAN_0 */
+#define WM8993_DCS_ENA_CHAN_0_WIDTH                  1  /* DCS_ENA_CHAN_0 */
+
+/*
+ * R85 (0x55) - DC Servo 1
+ */
+#define WM8993_DCS_SERIES_NO_01_MASK            0x0FE0  /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_SERIES_NO_01_SHIFT                5  /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_SERIES_NO_01_WIDTH                7  /* DCS_SERIES_NO_01 - [11:5] */
+#define WM8993_DCS_TIMER_PERIOD_01_MASK         0x000F  /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8993_DCS_TIMER_PERIOD_01_SHIFT             0  /* DCS_TIMER_PERIOD_01 - [3:0] */
+#define WM8993_DCS_TIMER_PERIOD_01_WIDTH             4  /* DCS_TIMER_PERIOD_01 - [3:0] */
+
+/*
+ * R87 (0x57) - DC Servo 3
+ */
+#define WM8993_DCS_DAC_WR_VAL_1_MASK            0xFF00  /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_1_SHIFT                8  /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_1_WIDTH                8  /* DCS_DAC_WR_VAL_1 - [15:8] */
+#define WM8993_DCS_DAC_WR_VAL_0_MASK            0x00FF  /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8993_DCS_DAC_WR_VAL_0_SHIFT                0  /* DCS_DAC_WR_VAL_0 - [7:0] */
+#define WM8993_DCS_DAC_WR_VAL_0_WIDTH                8  /* DCS_DAC_WR_VAL_0 - [7:0] */
+
+/*
+ * R88 (0x58) - DC Servo Readback 0
+ */
+#define WM8993_DCS_DATAPATH_BUSY                0x4000  /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_MASK           0x4000  /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_SHIFT              14  /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_DATAPATH_BUSY_WIDTH               1  /* DCS_DATAPATH_BUSY */
+#define WM8993_DCS_CHANNEL_MASK                 0x3000  /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CHANNEL_SHIFT                    12  /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CHANNEL_WIDTH                     2  /* DCS_CHANNEL - [13:12] */
+#define WM8993_DCS_CAL_COMPLETE_MASK            0x0300  /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_CAL_COMPLETE_SHIFT                8  /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_CAL_COMPLETE_WIDTH                2  /* DCS_CAL_COMPLETE - [9:8] */
+#define WM8993_DCS_DAC_WR_COMPLETE_MASK         0x0030  /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_DAC_WR_COMPLETE_SHIFT             4  /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_DAC_WR_COMPLETE_WIDTH             2  /* DCS_DAC_WR_COMPLETE - [5:4] */
+#define WM8993_DCS_STARTUP_COMPLETE_MASK        0x0003  /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM8993_DCS_STARTUP_COMPLETE_SHIFT            0  /* DCS_STARTUP_COMPLETE - [1:0] */
+#define WM8993_DCS_STARTUP_COMPLETE_WIDTH            2  /* DCS_STARTUP_COMPLETE - [1:0] */
+
+/*
+ * R89 (0x59) - DC Servo Readback 1
+ */
+#define WM8993_DCS_INTEG_CHAN_1_MASK            0x00FF  /* DCS_INTEG_CHAN_1 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_1_SHIFT                0  /* DCS_INTEG_CHAN_1 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_1_WIDTH                8  /* DCS_INTEG_CHAN_1 - [7:0] */
+
+/*
+ * R90 (0x5A) - DC Servo Readback 2
+ */
+#define WM8993_DCS_INTEG_CHAN_0_MASK            0x00FF  /* DCS_INTEG_CHAN_0 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_0_SHIFT                0  /* DCS_INTEG_CHAN_0 - [7:0] */
+#define WM8993_DCS_INTEG_CHAN_0_WIDTH                8  /* DCS_INTEG_CHAN_0 - [7:0] */
+
+/*
+ * R96 (0x60) - Analogue HP 0
+ */
+#define WM8993_HPOUT1_AUTO_PU                   0x0100  /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_MASK              0x0100  /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_SHIFT                  8  /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1_AUTO_PU_WIDTH                  1  /* HPOUT1_AUTO_PU */
+#define WM8993_HPOUT1L_RMV_SHORT                0x0080  /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_MASK           0x0080  /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_SHIFT               7  /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_RMV_SHORT_WIDTH               1  /* HPOUT1L_RMV_SHORT */
+#define WM8993_HPOUT1L_OUTP                     0x0040  /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_MASK                0x0040  /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_SHIFT                    6  /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_OUTP_WIDTH                    1  /* HPOUT1L_OUTP */
+#define WM8993_HPOUT1L_DLY                      0x0020  /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_MASK                 0x0020  /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_SHIFT                     5  /* HPOUT1L_DLY */
+#define WM8993_HPOUT1L_DLY_WIDTH                     1  /* HPOUT1L_DLY */
+#define WM8993_HPOUT1R_RMV_SHORT                0x0008  /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_MASK           0x0008  /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_SHIFT               3  /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_RMV_SHORT_WIDTH               1  /* HPOUT1R_RMV_SHORT */
+#define WM8993_HPOUT1R_OUTP                     0x0004  /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_MASK                0x0004  /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_SHIFT                    2  /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_OUTP_WIDTH                    1  /* HPOUT1R_OUTP */
+#define WM8993_HPOUT1R_DLY                      0x0002  /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_MASK                 0x0002  /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_SHIFT                     1  /* HPOUT1R_DLY */
+#define WM8993_HPOUT1R_DLY_WIDTH                     1  /* HPOUT1R_DLY */
+
+/*
+ * R98 (0x62) - EQ1
+ */
+#define WM8993_EQ_ENA                           0x0001  /* EQ_ENA */
+#define WM8993_EQ_ENA_MASK                      0x0001  /* EQ_ENA */
+#define WM8993_EQ_ENA_SHIFT                          0  /* EQ_ENA */
+#define WM8993_EQ_ENA_WIDTH                          1  /* EQ_ENA */
+
+/*
+ * R99 (0x63) - EQ2
+ */
+#define WM8993_EQ_B1_GAIN_MASK                  0x001F  /* EQ_B1_GAIN - [4:0] */
+#define WM8993_EQ_B1_GAIN_SHIFT                      0  /* EQ_B1_GAIN - [4:0] */
+#define WM8993_EQ_B1_GAIN_WIDTH                      5  /* EQ_B1_GAIN - [4:0] */
+
+/*
+ * R100 (0x64) - EQ3
+ */
+#define WM8993_EQ_B2_GAIN_MASK                  0x001F  /* EQ_B2_GAIN - [4:0] */
+#define WM8993_EQ_B2_GAIN_SHIFT                      0  /* EQ_B2_GAIN - [4:0] */
+#define WM8993_EQ_B2_GAIN_WIDTH                      5  /* EQ_B2_GAIN - [4:0] */
+
+/*
+ * R101 (0x65) - EQ4
+ */
+#define WM8993_EQ_B3_GAIN_MASK                  0x001F  /* EQ_B3_GAIN - [4:0] */
+#define WM8993_EQ_B3_GAIN_SHIFT                      0  /* EQ_B3_GAIN - [4:0] */
+#define WM8993_EQ_B3_GAIN_WIDTH                      5  /* EQ_B3_GAIN - [4:0] */
+
+/*
+ * R102 (0x66) - EQ5
+ */
+#define WM8993_EQ_B4_GAIN_MASK                  0x001F  /* EQ_B4_GAIN - [4:0] */
+#define WM8993_EQ_B4_GAIN_SHIFT                      0  /* EQ_B4_GAIN - [4:0] */
+#define WM8993_EQ_B4_GAIN_WIDTH                      5  /* EQ_B4_GAIN - [4:0] */
+
+/*
+ * R103 (0x67) - EQ6
+ */
+#define WM8993_EQ_B5_GAIN_MASK                  0x001F  /* EQ_B5_GAIN - [4:0] */
+#define WM8993_EQ_B5_GAIN_SHIFT                      0  /* EQ_B5_GAIN - [4:0] */
+#define WM8993_EQ_B5_GAIN_WIDTH                      5  /* EQ_B5_GAIN - [4:0] */
+
+/*
+ * R104 (0x68) - EQ7
+ */
+#define WM8993_EQ_B1_A_MASK                     0xFFFF  /* EQ_B1_A - [15:0] */
+#define WM8993_EQ_B1_A_SHIFT                         0  /* EQ_B1_A - [15:0] */
+#define WM8993_EQ_B1_A_WIDTH                        16  /* EQ_B1_A - [15:0] */
+
+/*
+ * R105 (0x69) - EQ8
+ */
+#define WM8993_EQ_B1_B_MASK                     0xFFFF  /* EQ_B1_B - [15:0] */
+#define WM8993_EQ_B1_B_SHIFT                         0  /* EQ_B1_B - [15:0] */
+#define WM8993_EQ_B1_B_WIDTH                        16  /* EQ_B1_B - [15:0] */
+
+/*
+ * R106 (0x6A) - EQ9
+ */
+#define WM8993_EQ_B1_PG_MASK                    0xFFFF  /* EQ_B1_PG - [15:0] */
+#define WM8993_EQ_B1_PG_SHIFT                        0  /* EQ_B1_PG - [15:0] */
+#define WM8993_EQ_B1_PG_WIDTH                       16  /* EQ_B1_PG - [15:0] */
+
+/*
+ * R107 (0x6B) - EQ10
+ */
+#define WM8993_EQ_B2_A_MASK                     0xFFFF  /* EQ_B2_A - [15:0] */
+#define WM8993_EQ_B2_A_SHIFT                         0  /* EQ_B2_A - [15:0] */
+#define WM8993_EQ_B2_A_WIDTH                        16  /* EQ_B2_A - [15:0] */
+
+/*
+ * R108 (0x6C) - EQ11
+ */
+#define WM8993_EQ_B2_B_MASK                     0xFFFF  /* EQ_B2_B - [15:0] */
+#define WM8993_EQ_B2_B_SHIFT                         0  /* EQ_B2_B - [15:0] */
+#define WM8993_EQ_B2_B_WIDTH                        16  /* EQ_B2_B - [15:0] */
+
+/*
+ * R109 (0x6D) - EQ12
+ */
+#define WM8993_EQ_B2_C_MASK                     0xFFFF  /* EQ_B2_C - [15:0] */
+#define WM8993_EQ_B2_C_SHIFT                         0  /* EQ_B2_C - [15:0] */
+#define WM8993_EQ_B2_C_WIDTH                        16  /* EQ_B2_C - [15:0] */
+
+/*
+ * R110 (0x6E) - EQ13
+ */
+#define WM8993_EQ_B2_PG_MASK                    0xFFFF  /* EQ_B2_PG - [15:0] */
+#define WM8993_EQ_B2_PG_SHIFT                        0  /* EQ_B2_PG - [15:0] */
+#define WM8993_EQ_B2_PG_WIDTH                       16  /* EQ_B2_PG - [15:0] */
+
+/*
+ * R111 (0x6F) - EQ14
+ */
+#define WM8993_EQ_B3_A_MASK                     0xFFFF  /* EQ_B3_A - [15:0] */
+#define WM8993_EQ_B3_A_SHIFT                         0  /* EQ_B3_A - [15:0] */
+#define WM8993_EQ_B3_A_WIDTH                        16  /* EQ_B3_A - [15:0] */
+
+/*
+ * R112 (0x70) - EQ15
+ */
+#define WM8993_EQ_B3_B_MASK                     0xFFFF  /* EQ_B3_B - [15:0] */
+#define WM8993_EQ_B3_B_SHIFT                         0  /* EQ_B3_B - [15:0] */
+#define WM8993_EQ_B3_B_WIDTH                        16  /* EQ_B3_B - [15:0] */
+
+/*
+ * R113 (0x71) - EQ16
+ */
+#define WM8993_EQ_B3_C_MASK                     0xFFFF  /* EQ_B3_C - [15:0] */
+#define WM8993_EQ_B3_C_SHIFT                         0  /* EQ_B3_C - [15:0] */
+#define WM8993_EQ_B3_C_WIDTH                        16  /* EQ_B3_C - [15:0] */
+
+/*
+ * R114 (0x72) - EQ17
+ */
+#define WM8993_EQ_B3_PG_MASK                    0xFFFF  /* EQ_B3_PG - [15:0] */
+#define WM8993_EQ_B3_PG_SHIFT                        0  /* EQ_B3_PG - [15:0] */
+#define WM8993_EQ_B3_PG_WIDTH                       16  /* EQ_B3_PG - [15:0] */
+
+/*
+ * R115 (0x73) - EQ18
+ */
+#define WM8993_EQ_B4_A_MASK                     0xFFFF  /* EQ_B4_A - [15:0] */
+#define WM8993_EQ_B4_A_SHIFT                         0  /* EQ_B4_A - [15:0] */
+#define WM8993_EQ_B4_A_WIDTH                        16  /* EQ_B4_A - [15:0] */
+
+/*
+ * R116 (0x74) - EQ19
+ */
+#define WM8993_EQ_B4_B_MASK                     0xFFFF  /* EQ_B4_B - [15:0] */
+#define WM8993_EQ_B4_B_SHIFT                         0  /* EQ_B4_B - [15:0] */
+#define WM8993_EQ_B4_B_WIDTH                        16  /* EQ_B4_B - [15:0] */
+
+/*
+ * R117 (0x75) - EQ20
+ */
+#define WM8993_EQ_B4_C_MASK                     0xFFFF  /* EQ_B4_C - [15:0] */
+#define WM8993_EQ_B4_C_SHIFT                         0  /* EQ_B4_C - [15:0] */
+#define WM8993_EQ_B4_C_WIDTH                        16  /* EQ_B4_C - [15:0] */
+
+/*
+ * R118 (0x76) - EQ21
+ */
+#define WM8993_EQ_B4_PG_MASK                    0xFFFF  /* EQ_B4_PG - [15:0] */
+#define WM8993_EQ_B4_PG_SHIFT                        0  /* EQ_B4_PG - [15:0] */
+#define WM8993_EQ_B4_PG_WIDTH                       16  /* EQ_B4_PG - [15:0] */
+
+/*
+ * R119 (0x77) - EQ22
+ */
+#define WM8993_EQ_B5_A_MASK                     0xFFFF  /* EQ_B5_A - [15:0] */
+#define WM8993_EQ_B5_A_SHIFT                         0  /* EQ_B5_A - [15:0] */
+#define WM8993_EQ_B5_A_WIDTH                        16  /* EQ_B5_A - [15:0] */
+
+/*
+ * R120 (0x78) - EQ23
+ */
+#define WM8993_EQ_B5_B_MASK                     0xFFFF  /* EQ_B5_B - [15:0] */
+#define WM8993_EQ_B5_B_SHIFT                         0  /* EQ_B5_B - [15:0] */
+#define WM8993_EQ_B5_B_WIDTH                        16  /* EQ_B5_B - [15:0] */
+
+/*
+ * R121 (0x79) - EQ24
+ */
+#define WM8993_EQ_B5_PG_MASK                    0xFFFF  /* EQ_B5_PG - [15:0] */
+#define WM8993_EQ_B5_PG_SHIFT                        0  /* EQ_B5_PG - [15:0] */
+#define WM8993_EQ_B5_PG_WIDTH                       16  /* EQ_B5_PG - [15:0] */
+
+/*
+ * R122 (0x7A) - Digital Pulls
+ */
+#define WM8993_MCLK_PU                          0x0080  /* MCLK_PU */
+#define WM8993_MCLK_PU_MASK                     0x0080  /* MCLK_PU */
+#define WM8993_MCLK_PU_SHIFT                         7  /* MCLK_PU */
+#define WM8993_MCLK_PU_WIDTH                         1  /* MCLK_PU */
+#define WM8993_MCLK_PD                          0x0040  /* MCLK_PD */
+#define WM8993_MCLK_PD_MASK                     0x0040  /* MCLK_PD */
+#define WM8993_MCLK_PD_SHIFT                         6  /* MCLK_PD */
+#define WM8993_MCLK_PD_WIDTH                         1  /* MCLK_PD */
+#define WM8993_DACDAT_PU                        0x0020  /* DACDAT_PU */
+#define WM8993_DACDAT_PU_MASK                   0x0020  /* DACDAT_PU */
+#define WM8993_DACDAT_PU_SHIFT                       5  /* DACDAT_PU */
+#define WM8993_DACDAT_PU_WIDTH                       1  /* DACDAT_PU */
+#define WM8993_DACDAT_PD                        0x0010  /* DACDAT_PD */
+#define WM8993_DACDAT_PD_MASK                   0x0010  /* DACDAT_PD */
+#define WM8993_DACDAT_PD_SHIFT                       4  /* DACDAT_PD */
+#define WM8993_DACDAT_PD_WIDTH                       1  /* DACDAT_PD */
+#define WM8993_LRCLK_PU                         0x0008  /* LRCLK_PU */
+#define WM8993_LRCLK_PU_MASK                    0x0008  /* LRCLK_PU */
+#define WM8993_LRCLK_PU_SHIFT                        3  /* LRCLK_PU */
+#define WM8993_LRCLK_PU_WIDTH                        1  /* LRCLK_PU */
+#define WM8993_LRCLK_PD                         0x0004  /* LRCLK_PD */
+#define WM8993_LRCLK_PD_MASK                    0x0004  /* LRCLK_PD */
+#define WM8993_LRCLK_PD_SHIFT                        2  /* LRCLK_PD */
+#define WM8993_LRCLK_PD_WIDTH                        1  /* LRCLK_PD */
+#define WM8993_BCLK_PU                          0x0002  /* BCLK_PU */
+#define WM8993_BCLK_PU_MASK                     0x0002  /* BCLK_PU */
+#define WM8993_BCLK_PU_SHIFT                         1  /* BCLK_PU */
+#define WM8993_BCLK_PU_WIDTH                         1  /* BCLK_PU */
+#define WM8993_BCLK_PD                          0x0001  /* BCLK_PD */
+#define WM8993_BCLK_PD_MASK                     0x0001  /* BCLK_PD */
+#define WM8993_BCLK_PD_SHIFT                         0  /* BCLK_PD */
+#define WM8993_BCLK_PD_WIDTH                         1  /* BCLK_PD */
+
+/*
+ * R123 (0x7B) - DRC Control 1
+ */
+#define WM8993_DRC_ENA                          0x8000  /* DRC_ENA */
+#define WM8993_DRC_ENA_MASK                     0x8000  /* DRC_ENA */
+#define WM8993_DRC_ENA_SHIFT                        15  /* DRC_ENA */
+#define WM8993_DRC_ENA_WIDTH                         1  /* DRC_ENA */
+#define WM8993_DRC_DAC_PATH                     0x4000  /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_MASK                0x4000  /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_SHIFT                   14  /* DRC_DAC_PATH */
+#define WM8993_DRC_DAC_PATH_WIDTH                    1  /* DRC_DAC_PATH */
+#define WM8993_DRC_SMOOTH_ENA                   0x0800  /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_MASK              0x0800  /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_SHIFT                 11  /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_SMOOTH_ENA_WIDTH                  1  /* DRC_SMOOTH_ENA */
+#define WM8993_DRC_QR_ENA                       0x0400  /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_MASK                  0x0400  /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_SHIFT                     10  /* DRC_QR_ENA */
+#define WM8993_DRC_QR_ENA_WIDTH                      1  /* DRC_QR_ENA */
+#define WM8993_DRC_ANTICLIP_ENA                 0x0200  /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_MASK            0x0200  /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_SHIFT                9  /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_ANTICLIP_ENA_WIDTH                1  /* DRC_ANTICLIP_ENA */
+#define WM8993_DRC_HYST_ENA                     0x0100  /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_MASK                0x0100  /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_SHIFT                    8  /* DRC_HYST_ENA */
+#define WM8993_DRC_HYST_ENA_WIDTH                    1  /* DRC_HYST_ENA */
+#define WM8993_DRC_THRESH_HYST_MASK             0x0030  /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_THRESH_HYST_SHIFT                 4  /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_THRESH_HYST_WIDTH                 2  /* DRC_THRESH_HYST - [5:4] */
+#define WM8993_DRC_MINGAIN_MASK                 0x000C  /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MINGAIN_SHIFT                     2  /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MINGAIN_WIDTH                     2  /* DRC_MINGAIN - [3:2] */
+#define WM8993_DRC_MAXGAIN_MASK                 0x0003  /* DRC_MAXGAIN - [1:0] */
+#define WM8993_DRC_MAXGAIN_SHIFT                     0  /* DRC_MAXGAIN - [1:0] */
+#define WM8993_DRC_MAXGAIN_WIDTH                     2  /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R124 (0x7C) - DRC Control 2
+ */
+#define WM8993_DRC_ATTACK_RATE_MASK             0xF000  /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_ATTACK_RATE_SHIFT                12  /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_ATTACK_RATE_WIDTH                 4  /* DRC_ATTACK_RATE - [15:12] */
+#define WM8993_DRC_DECAY_RATE_MASK              0x0F00  /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_DECAY_RATE_SHIFT                  8  /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_DECAY_RATE_WIDTH                  4  /* DRC_DECAY_RATE - [11:8] */
+#define WM8993_DRC_THRESH_COMP_MASK             0x00FC  /* DRC_THRESH_COMP - [7:2] */
+#define WM8993_DRC_THRESH_COMP_SHIFT                 2  /* DRC_THRESH_COMP - [7:2] */
+#define WM8993_DRC_THRESH_COMP_WIDTH                 6  /* DRC_THRESH_COMP - [7:2] */
+
+/*
+ * R125 (0x7D) - DRC Control 3
+ */
+#define WM8993_DRC_AMP_COMP_MASK                0xF800  /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_AMP_COMP_SHIFT                   11  /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_AMP_COMP_WIDTH                    5  /* DRC_AMP_COMP - [15:11] */
+#define WM8993_DRC_R0_SLOPE_COMP_MASK           0x0700  /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_R0_SLOPE_COMP_SHIFT               8  /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_R0_SLOPE_COMP_WIDTH               3  /* DRC_R0_SLOPE_COMP - [10:8] */
+#define WM8993_DRC_FF_DELAY                     0x0080  /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_MASK                0x0080  /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_SHIFT                    7  /* DRC_FF_DELAY */
+#define WM8993_DRC_FF_DELAY_WIDTH                    1  /* DRC_FF_DELAY */
+#define WM8993_DRC_THRESH_QR_MASK               0x000C  /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_THRESH_QR_SHIFT                   2  /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_THRESH_QR_WIDTH                   2  /* DRC_THRESH_QR - [3:2] */
+#define WM8993_DRC_RATE_QR_MASK                 0x0003  /* DRC_RATE_QR - [1:0] */
+#define WM8993_DRC_RATE_QR_SHIFT                     0  /* DRC_RATE_QR - [1:0] */
+#define WM8993_DRC_RATE_QR_WIDTH                     2  /* DRC_RATE_QR - [1:0] */
+
+/*
+ * R126 (0x7E) - DRC Control 4
+ */
+#define WM8993_DRC_R1_SLOPE_COMP_MASK           0xE000  /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_R1_SLOPE_COMP_SHIFT              13  /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_R1_SLOPE_COMP_WIDTH               3  /* DRC_R1_SLOPE_COMP - [15:13] */
+#define WM8993_DRC_STARTUP_GAIN_MASK            0x1F00  /* DRC_STARTUP_GAIN - [12:8] */
+#define WM8993_DRC_STARTUP_GAIN_SHIFT                8  /* DRC_STARTUP_GAIN - [12:8] */
+#define WM8993_DRC_STARTUP_GAIN_WIDTH                5  /* DRC_STARTUP_GAIN - [12:8] */
+
+#endif
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
new file mode 100644
index 0000000..c64e55a
--- /dev/null
+++ b/sound/soc/codecs/wm9081.c
@@ -0,0 +1,1511 @@
+/*
+ * wm9081.c  --  WM9081 ALSA SoC Audio driver
+ *
+ * Author: Mark Brown
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <sound/wm9081.h>
+#include "wm9081.h"
+
+static u16 wm9081_reg_defaults[] = {
+	0x0000,     /* R0  - Software Reset */
+	0x0000,     /* R1 */
+	0x00B9,     /* R2  - Analogue Lineout */
+	0x00B9,     /* R3  - Analogue Speaker PGA */
+	0x0001,     /* R4  - VMID Control */
+	0x0068,     /* R5  - Bias Control 1 */
+	0x0000,     /* R6 */
+	0x0000,     /* R7  - Analogue Mixer */
+	0x0000,     /* R8  - Anti Pop Control */
+	0x01DB,     /* R9  - Analogue Speaker 1 */
+	0x0018,     /* R10 - Analogue Speaker 2 */
+	0x0180,     /* R11 - Power Management */
+	0x0000,     /* R12 - Clock Control 1 */
+	0x0038,     /* R13 - Clock Control 2 */
+	0x4000,     /* R14 - Clock Control 3 */
+	0x0000,     /* R15 */
+	0x0000,     /* R16 - FLL Control 1 */
+	0x0200,     /* R17 - FLL Control 2 */
+	0x0000,     /* R18 - FLL Control 3 */
+	0x0204,     /* R19 - FLL Control 4 */
+	0x0000,     /* R20 - FLL Control 5 */
+	0x0000,     /* R21 */
+	0x0000,     /* R22 - Audio Interface 1 */
+	0x0002,     /* R23 - Audio Interface 2 */
+	0x0008,     /* R24 - Audio Interface 3 */
+	0x0022,     /* R25 - Audio Interface 4 */
+	0x0000,     /* R26 - Interrupt Status */
+	0x0006,     /* R27 - Interrupt Status Mask */
+	0x0000,     /* R28 - Interrupt Polarity */
+	0x0000,     /* R29 - Interrupt Control */
+	0x00C0,     /* R30 - DAC Digital 1 */
+	0x0008,     /* R31 - DAC Digital 2 */
+	0x09AF,     /* R32 - DRC 1 */
+	0x4201,     /* R33 - DRC 2 */
+	0x0000,     /* R34 - DRC 3 */
+	0x0000,     /* R35 - DRC 4 */
+	0x0000,     /* R36 */
+	0x0000,     /* R37 */
+	0x0000,     /* R38 - Write Sequencer 1 */
+	0x0000,     /* R39 - Write Sequencer 2 */
+	0x0002,     /* R40 - MW Slave 1 */
+	0x0000,     /* R41 */
+	0x0000,     /* R42 - EQ 1 */
+	0x0000,     /* R43 - EQ 2 */
+	0x0FCA,     /* R44 - EQ 3 */
+	0x0400,     /* R45 - EQ 4 */
+	0x00B8,     /* R46 - EQ 5 */
+	0x1EB5,     /* R47 - EQ 6 */
+	0xF145,     /* R48 - EQ 7 */
+	0x0B75,     /* R49 - EQ 8 */
+	0x01C5,     /* R50 - EQ 9 */
+	0x169E,     /* R51 - EQ 10 */
+	0xF829,     /* R52 - EQ 11 */
+	0x07AD,     /* R53 - EQ 12 */
+	0x1103,     /* R54 - EQ 13 */
+	0x1C58,     /* R55 - EQ 14 */
+	0xF373,     /* R56 - EQ 15 */
+	0x0A54,     /* R57 - EQ 16 */
+	0x0558,     /* R58 - EQ 17 */
+	0x0564,     /* R59 - EQ 18 */
+	0x0559,     /* R60 - EQ 19 */
+	0x4000,     /* R61 - EQ 20 */
+};
+
+static struct {
+	int ratio;
+	int clk_sys_rate;
+} clk_sys_rates[] = {
+	{ 64,   0 },
+	{ 128,  1 },
+	{ 192,  2 },
+	{ 256,  3 },
+	{ 384,  4 },
+	{ 512,  5 },
+	{ 768,  6 },
+	{ 1024, 7 },
+	{ 1408, 8 },
+	{ 1536, 9 },
+};
+
+static struct {
+	int rate;
+	int sample_rate;
+} sample_rates[] = {
+	{ 8000,  0  },
+	{ 11025, 1  },
+	{ 12000, 2  },
+	{ 16000, 3  },
+	{ 22050, 4  },
+	{ 24000, 5  },
+	{ 32000, 6  },
+	{ 44100, 7  },
+	{ 48000, 8  },
+	{ 88200, 9  },
+	{ 96000, 10 },
+};
+
+static struct {
+	int div; /* *10 due to .5s */
+	int bclk_div;
+} bclk_divs[] = {
+	{ 10,  0  },
+	{ 15,  1  },
+	{ 20,  2  },
+	{ 30,  3  },
+	{ 40,  4  },
+	{ 50,  5  },
+	{ 55,  6  },
+	{ 60,  7  },
+	{ 80,  8  },
+	{ 100, 9  },
+	{ 110, 10 },
+	{ 120, 11 },
+	{ 160, 12 },
+	{ 200, 13 },
+	{ 220, 14 },
+	{ 240, 15 },
+	{ 250, 16 },
+	{ 300, 17 },
+	{ 320, 18 },
+	{ 440, 19 },
+	{ 480, 20 },
+};
+
+struct wm9081_priv {
+	struct snd_soc_codec codec;
+	u16 reg_cache[WM9081_MAX_REGISTER + 1];
+	int sysclk_source;
+	int mclk_rate;
+	int sysclk_rate;
+	int fs;
+	int bclk;
+	int master;
+	int fll_fref;
+	int fll_fout;
+	int tdm_width;
+	struct wm9081_retune_mobile_config *retune;
+};
+
+static int wm9081_volatile_register(unsigned int reg)
+{
+	switch (reg) {
+	case WM9081_SOFTWARE_RESET:
+		return 1;
+	default:
+		return 0;
+	}
+}
+
+static int wm9081_reset(struct snd_soc_codec *codec)
+{
+	return snd_soc_write(codec, WM9081_SOFTWARE_RESET, 0);
+}
+
+static const DECLARE_TLV_DB_SCALE(drc_in_tlv, -4500, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_out_tlv, -2250, 75, 0);
+static const DECLARE_TLV_DB_SCALE(drc_min_tlv, -1800, 600, 0);
+static unsigned int drc_max_tlv[] = {
+	TLV_DB_RANGE_HEAD(4),
+	0, 0, TLV_DB_SCALE_ITEM(1200, 0, 0),
+	1, 1, TLV_DB_SCALE_ITEM(1800, 0, 0),
+	2, 2, TLV_DB_SCALE_ITEM(2400, 0, 0),
+	3, 3, TLV_DB_SCALE_ITEM(3600, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(drc_qr_tlv, 1200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(drc_startup_tlv, -300, 50, 0);
+
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+
+static const DECLARE_TLV_DB_SCALE(in_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -7200, 75, 1);
+static const DECLARE_TLV_DB_SCALE(out_tlv, -5700, 100, 0);
+
+static const char *drc_high_text[] = {
+	"1",
+	"1/2",
+	"1/4",
+	"1/8",
+	"1/16",
+	"0",
+};
+
+static const struct soc_enum drc_high =
+	SOC_ENUM_SINGLE(WM9081_DRC_3, 3, 6, drc_high_text);
+
+static const char *drc_low_text[] = {
+	"1",
+	"1/2",
+	"1/4",
+	"1/8",
+	"0",
+};
+
+static const struct soc_enum drc_low =
+	SOC_ENUM_SINGLE(WM9081_DRC_3, 0, 5, drc_low_text);
+
+static const char *drc_atk_text[] = {
+	"181us",
+	"181us",
+	"363us",
+	"726us",
+	"1.45ms",
+	"2.9ms",
+	"5.8ms",
+	"11.6ms",
+	"23.2ms",
+	"46.4ms",
+	"92.8ms",
+	"185.6ms",
+};
+
+static const struct soc_enum drc_atk =
+	SOC_ENUM_SINGLE(WM9081_DRC_2, 12, 12, drc_atk_text);
+
+static const char *drc_dcy_text[] = {
+	"186ms",
+	"372ms",
+	"743ms",
+	"1.49s",
+	"2.97s",
+	"5.94s",
+	"11.89s",
+	"23.78s",
+	"47.56s",
+};
+
+static const struct soc_enum drc_dcy =
+	SOC_ENUM_SINGLE(WM9081_DRC_2, 8, 9, drc_dcy_text);
+
+static const char *drc_qr_dcy_text[] = {
+	"0.725ms",
+	"1.45ms",
+	"5.8ms",
+};
+
+static const struct soc_enum drc_qr_dcy =
+	SOC_ENUM_SINGLE(WM9081_DRC_2, 4, 3, drc_qr_dcy_text);
+
+static const char *dac_deemph_text[] = {
+	"None",
+	"32kHz",
+	"44.1kHz",
+	"48kHz",
+};
+
+static const struct soc_enum dac_deemph =
+	SOC_ENUM_SINGLE(WM9081_DAC_DIGITAL_2, 1, 4, dac_deemph_text);
+
+static const char *speaker_mode_text[] = {
+	"Class D",
+	"Class AB",
+};
+
+static const struct soc_enum speaker_mode =
+	SOC_ENUM_SINGLE(WM9081_ANALOGUE_SPEAKER_2, 6, 2, speaker_mode_text);
+
+static int speaker_mode_get(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int reg;
+
+	reg = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2);
+	if (reg & WM9081_SPK_MODE)
+		ucontrol->value.integer.value[0] = 1;
+	else
+		ucontrol->value.integer.value[0] = 0;
+
+	return 0;
+}
+
+/*
+ * Stop any attempts to change speaker mode while the speaker is enabled.
+ *
+ * We also have some special anti-pop controls dependant on speaker
+ * mode which must be changed along with the mode.
+ */
+static int speaker_mode_put(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int reg_pwr = snd_soc_read(codec, WM9081_POWER_MANAGEMENT);
+	unsigned int reg2 = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_2);
+
+	/* Are we changing anything? */
+	if (ucontrol->value.integer.value[0] ==
+	    ((reg2 & WM9081_SPK_MODE) != 0))
+		return 0;
+
+	/* Don't try to change modes while enabled */
+	if (reg_pwr & WM9081_SPK_ENA)
+		return -EINVAL;
+
+	if (ucontrol->value.integer.value[0]) {
+		/* Class AB */
+		reg2 &= ~(WM9081_SPK_INV_MUTE | WM9081_OUT_SPK_CTRL);
+		reg2 |= WM9081_SPK_MODE;
+	} else {
+		/* Class D */
+		reg2 |= WM9081_SPK_INV_MUTE | WM9081_OUT_SPK_CTRL;
+		reg2 &= ~WM9081_SPK_MODE;
+	}
+
+	snd_soc_write(codec, WM9081_ANALOGUE_SPEAKER_2, reg2);
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new wm9081_snd_controls[] = {
+SOC_SINGLE_TLV("IN1 Volume", WM9081_ANALOGUE_MIXER, 1, 1, 1, in_tlv),
+SOC_SINGLE_TLV("IN2 Volume", WM9081_ANALOGUE_MIXER, 3, 1, 1, in_tlv),
+
+SOC_SINGLE_TLV("Playback Volume", WM9081_DAC_DIGITAL_1, 1, 96, 0, dac_tlv),
+
+SOC_SINGLE("LINEOUT Switch", WM9081_ANALOGUE_LINEOUT, 7, 1, 1),
+SOC_SINGLE("LINEOUT ZC Switch", WM9081_ANALOGUE_LINEOUT, 6, 1, 0),
+SOC_SINGLE_TLV("LINEOUT Volume", WM9081_ANALOGUE_LINEOUT, 0, 63, 0, out_tlv),
+
+SOC_SINGLE("DRC Switch", WM9081_DRC_1, 15, 1, 0),
+SOC_ENUM("DRC High Slope", drc_high),
+SOC_ENUM("DRC Low Slope", drc_low),
+SOC_SINGLE_TLV("DRC Input Volume", WM9081_DRC_4, 5, 60, 1, drc_in_tlv),
+SOC_SINGLE_TLV("DRC Output Volume", WM9081_DRC_4, 0, 30, 1, drc_out_tlv),
+SOC_SINGLE_TLV("DRC Minimum Volume", WM9081_DRC_2, 2, 3, 1, drc_min_tlv),
+SOC_SINGLE_TLV("DRC Maximum Volume", WM9081_DRC_2, 0, 3, 0, drc_max_tlv),
+SOC_ENUM("DRC Attack", drc_atk),
+SOC_ENUM("DRC Decay", drc_dcy),
+SOC_SINGLE("DRC Quick Release Switch", WM9081_DRC_1, 2, 1, 0),
+SOC_SINGLE_TLV("DRC Quick Release Volume", WM9081_DRC_2, 6, 3, 0, drc_qr_tlv),
+SOC_ENUM("DRC Quick Release Decay", drc_qr_dcy),
+SOC_SINGLE_TLV("DRC Startup Volume", WM9081_DRC_1, 6, 18, 0, drc_startup_tlv),
+
+SOC_SINGLE("EQ Switch", WM9081_EQ_1, 0, 1, 0),
+
+SOC_SINGLE("Speaker DC Volume", WM9081_ANALOGUE_SPEAKER_1, 3, 5, 0),
+SOC_SINGLE("Speaker AC Volume", WM9081_ANALOGUE_SPEAKER_1, 0, 5, 0),
+SOC_SINGLE("Speaker Switch", WM9081_ANALOGUE_SPEAKER_PGA, 7, 1, 1),
+SOC_SINGLE("Speaker ZC Switch", WM9081_ANALOGUE_SPEAKER_PGA, 6, 1, 0),
+SOC_SINGLE_TLV("Speaker Volume", WM9081_ANALOGUE_SPEAKER_PGA, 0, 63, 0,
+	       out_tlv),
+SOC_ENUM("DAC Deemphasis", dac_deemph),
+SOC_ENUM_EXT("Speaker Mode", speaker_mode, speaker_mode_get, speaker_mode_put),
+};
+
+static const struct snd_kcontrol_new wm9081_eq_controls[] = {
+SOC_SINGLE_TLV("EQ1 Volume", WM9081_EQ_1, 11, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 Volume", WM9081_EQ_1, 6, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 Volume", WM9081_EQ_1, 1, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 Volume", WM9081_EQ_2, 11, 24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ5 Volume", WM9081_EQ_2, 6, 24, 0, eq_tlv),
+};
+
+static const struct snd_kcontrol_new mixer[] = {
+SOC_DAPM_SINGLE("IN1 Switch", WM9081_ANALOGUE_MIXER, 0, 1, 0),
+SOC_DAPM_SINGLE("IN2 Switch", WM9081_ANALOGUE_MIXER, 2, 1, 0),
+SOC_DAPM_SINGLE("Playback Switch", WM9081_ANALOGUE_MIXER, 4, 1, 0),
+};
+
+static int speaker_event(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	unsigned int reg = snd_soc_read(codec, WM9081_POWER_MANAGEMENT);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		reg |= WM9081_SPK_ENA;
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		reg &= ~WM9081_SPK_ENA;
+		break;
+	}
+
+	snd_soc_write(codec, WM9081_POWER_MANAGEMENT, reg);
+
+	return 0;
+}
+
+struct _fll_div {
+	u16 fll_fratio;
+	u16 fll_outdiv;
+	u16 fll_clk_ref_div;
+	u16 n;
+	u16 k;
+};
+
+/* The size in bits of the FLL divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_FLL_SIZE ((1 << 16) * 10)
+
+static struct {
+	unsigned int min;
+	unsigned int max;
+	u16 fll_fratio;
+	int ratio;
+} fll_fratios[] = {
+	{       0,    64000, 4, 16 },
+	{   64000,   128000, 3,  8 },
+	{  128000,   256000, 2,  4 },
+	{  256000,  1000000, 1,  2 },
+	{ 1000000, 13500000, 0,  1 },
+};
+
+static int fll_factors(struct _fll_div *fll_div, unsigned int Fref,
+		       unsigned int Fout)
+{
+	u64 Kpart;
+	unsigned int K, Ndiv, Nmod, target;
+	unsigned int div;
+	int i;
+
+	/* Fref must be <=13.5MHz */
+	div = 1;
+	while ((Fref / div) > 13500000) {
+		div *= 2;
+
+		if (div > 8) {
+			pr_err("Can't scale %dMHz input down to <=13.5MHz\n",
+			       Fref);
+			return -EINVAL;
+		}
+	}
+	fll_div->fll_clk_ref_div = div / 2;
+
+	pr_debug("Fref=%u Fout=%u\n", Fref, Fout);
+
+	/* Apply the division for our remaining calculations */
+	Fref /= div;
+
+	/* Fvco should be 90-100MHz; don't check the upper bound */
+	div = 0;
+	target = Fout * 2;
+	while (target < 90000000) {
+		div++;
+		target *= 2;
+		if (div > 7) {
+			pr_err("Unable to find FLL_OUTDIV for Fout=%uHz\n",
+			       Fout);
+			return -EINVAL;
+		}
+	}
+	fll_div->fll_outdiv = div;
+
+	pr_debug("Fvco=%dHz\n", target);
+
+	/* Find an appropraite FLL_FRATIO and factor it out of the target */
+	for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+		if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+			fll_div->fll_fratio = fll_fratios[i].fll_fratio;
+			target /= fll_fratios[i].ratio;
+			break;
+		}
+	}
+	if (i == ARRAY_SIZE(fll_fratios)) {
+		pr_err("Unable to find FLL_FRATIO for Fref=%uHz\n", Fref);
+		return -EINVAL;
+	}
+
+	/* Now, calculate N.K */
+	Ndiv = target / Fref;
+
+	fll_div->n = Ndiv;
+	Nmod = target % Fref;
+	pr_debug("Nmod=%d\n", Nmod);
+
+	/* Calculate fractional part - scale up so we can round. */
+	Kpart = FIXED_FLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, Fref);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	fll_div->k = K / 10;
+
+	pr_debug("N=%x K=%x FLL_FRATIO=%x FLL_OUTDIV=%x FLL_CLK_REF_DIV=%x\n",
+		 fll_div->n, fll_div->k,
+		 fll_div->fll_fratio, fll_div->fll_outdiv,
+		 fll_div->fll_clk_ref_div);
+
+	return 0;
+}
+
+static int wm9081_set_fll(struct snd_soc_codec *codec, int fll_id,
+			  unsigned int Fref, unsigned int Fout)
+{
+	struct wm9081_priv *wm9081 = codec->private_data;
+	u16 reg1, reg4, reg5;
+	struct _fll_div fll_div;
+	int ret;
+	int clk_sys_reg;
+
+	/* Any change? */
+	if (Fref == wm9081->fll_fref && Fout == wm9081->fll_fout)
+		return 0;
+
+	/* Disable the FLL */
+	if (Fout == 0) {
+		dev_dbg(codec->dev, "FLL disabled\n");
+		wm9081->fll_fref = 0;
+		wm9081->fll_fout = 0;
+
+		return 0;
+	}
+
+	ret = fll_factors(&fll_div, Fref, Fout);
+	if (ret != 0)
+		return ret;
+
+	reg5 = snd_soc_read(codec, WM9081_FLL_CONTROL_5);
+	reg5 &= ~WM9081_FLL_CLK_SRC_MASK;
+
+	switch (fll_id) {
+	case WM9081_SYSCLK_FLL_MCLK:
+		reg5 |= 0x1;
+		break;
+
+	default:
+		dev_err(codec->dev, "Unknown FLL ID %d\n", fll_id);
+		return -EINVAL;
+	}
+
+	/* Disable CLK_SYS while we reconfigure */
+	clk_sys_reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_3);
+	if (clk_sys_reg & WM9081_CLK_SYS_ENA)
+		snd_soc_write(codec, WM9081_CLOCK_CONTROL_3,
+			     clk_sys_reg & ~WM9081_CLK_SYS_ENA);
+
+	/* Any FLL configuration change requires that the FLL be
+	 * disabled first. */
+	reg1 = snd_soc_read(codec, WM9081_FLL_CONTROL_1);
+	reg1 &= ~WM9081_FLL_ENA;
+	snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1);
+
+	/* Apply the configuration */
+	if (fll_div.k)
+		reg1 |= WM9081_FLL_FRAC_MASK;
+	else
+		reg1 &= ~WM9081_FLL_FRAC_MASK;
+	snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1);
+
+	snd_soc_write(codec, WM9081_FLL_CONTROL_2,
+		     (fll_div.fll_outdiv << WM9081_FLL_OUTDIV_SHIFT) |
+		     (fll_div.fll_fratio << WM9081_FLL_FRATIO_SHIFT));
+	snd_soc_write(codec, WM9081_FLL_CONTROL_3, fll_div.k);
+
+	reg4 = snd_soc_read(codec, WM9081_FLL_CONTROL_4);
+	reg4 &= ~WM9081_FLL_N_MASK;
+	reg4 |= fll_div.n << WM9081_FLL_N_SHIFT;
+	snd_soc_write(codec, WM9081_FLL_CONTROL_4, reg4);
+
+	reg5 &= ~WM9081_FLL_CLK_REF_DIV_MASK;
+	reg5 |= fll_div.fll_clk_ref_div << WM9081_FLL_CLK_REF_DIV_SHIFT;
+	snd_soc_write(codec, WM9081_FLL_CONTROL_5, reg5);
+
+	/* Enable the FLL */
+	snd_soc_write(codec, WM9081_FLL_CONTROL_1, reg1 | WM9081_FLL_ENA);
+
+	/* Then bring CLK_SYS up again if it was disabled */
+	if (clk_sys_reg & WM9081_CLK_SYS_ENA)
+		snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, clk_sys_reg);
+
+	dev_dbg(codec->dev, "FLL enabled at %dHz->%dHz\n", Fref, Fout);
+
+	wm9081->fll_fref = Fref;
+	wm9081->fll_fout = Fout;
+
+	return 0;
+}
+
+static int configure_clock(struct snd_soc_codec *codec)
+{
+	struct wm9081_priv *wm9081 = codec->private_data;
+	int new_sysclk, i, target;
+	unsigned int reg;
+	int ret = 0;
+	int mclkdiv = 0;
+	int fll = 0;
+
+	switch (wm9081->sysclk_source) {
+	case WM9081_SYSCLK_MCLK:
+		if (wm9081->mclk_rate > 12225000) {
+			mclkdiv = 1;
+			wm9081->sysclk_rate = wm9081->mclk_rate / 2;
+		} else {
+			wm9081->sysclk_rate = wm9081->mclk_rate;
+		}
+		wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK, 0, 0);
+		break;
+
+	case WM9081_SYSCLK_FLL_MCLK:
+		/* If we have a sample rate calculate a CLK_SYS that
+		 * gives us a suitable DAC configuration, plus BCLK.
+		 * Ideally we would check to see if we can clock
+		 * directly from MCLK and only use the FLL if this is
+		 * not the case, though care must be taken with free
+		 * running mode.
+		 */
+		if (wm9081->master && wm9081->bclk) {
+			/* Make sure we can generate CLK_SYS and BCLK
+			 * and that we've got 3MHz for optimal
+			 * performance. */
+			for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) {
+				target = wm9081->fs * clk_sys_rates[i].ratio;
+				new_sysclk = target;
+				if (target >= wm9081->bclk &&
+				    target > 3000000)
+					break;
+			}
+
+			if (i == ARRAY_SIZE(clk_sys_rates))
+				return -EINVAL;
+
+		} else if (wm9081->fs) {
+			for (i = 0; i < ARRAY_SIZE(clk_sys_rates); i++) {
+				new_sysclk = clk_sys_rates[i].ratio
+					* wm9081->fs;
+				if (new_sysclk > 3000000)
+					break;
+			}
+
+			if (i == ARRAY_SIZE(clk_sys_rates))
+				return -EINVAL;
+
+		} else {
+			new_sysclk = 12288000;
+		}
+
+		ret = wm9081_set_fll(codec, WM9081_SYSCLK_FLL_MCLK,
+				     wm9081->mclk_rate, new_sysclk);
+		if (ret == 0) {
+			wm9081->sysclk_rate = new_sysclk;
+
+			/* Switch SYSCLK over to FLL */
+			fll = 1;
+		} else {
+			wm9081->sysclk_rate = wm9081->mclk_rate;
+		}
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_1);
+	if (mclkdiv)
+		reg |= WM9081_MCLKDIV2;
+	else
+		reg &= ~WM9081_MCLKDIV2;
+	snd_soc_write(codec, WM9081_CLOCK_CONTROL_1, reg);
+
+	reg = snd_soc_read(codec, WM9081_CLOCK_CONTROL_3);
+	if (fll)
+		reg |= WM9081_CLK_SRC_SEL;
+	else
+		reg &= ~WM9081_CLK_SRC_SEL;
+	snd_soc_write(codec, WM9081_CLOCK_CONTROL_3, reg);
+
+	dev_dbg(codec->dev, "CLK_SYS is %dHz\n", wm9081->sysclk_rate);
+
+	return ret;
+}
+
+static int clk_sys_event(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct wm9081_priv *wm9081 = codec->private_data;
+
+	/* This should be done on init() for bypass paths */
+	switch (wm9081->sysclk_source) {
+	case WM9081_SYSCLK_MCLK:
+		dev_dbg(codec->dev, "Using %dHz MCLK\n", wm9081->mclk_rate);
+		break;
+	case WM9081_SYSCLK_FLL_MCLK:
+		dev_dbg(codec->dev, "Using %dHz MCLK with FLL\n",
+			wm9081->mclk_rate);
+		break;
+	default:
+		dev_err(codec->dev, "System clock not configured\n");
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		configure_clock(codec);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		/* Disable the FLL if it's running */
+		wm9081_set_fll(codec, 0, 0, 0);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget wm9081_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1"),
+SND_SOC_DAPM_INPUT("IN2"),
+
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM9081_POWER_MANAGEMENT, 0, 0),
+
+SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0,
+			     mixer, ARRAY_SIZE(mixer)),
+
+SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0,
+		   speaker_event,
+		   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_OUTPUT("LINEOUT"),
+SND_SOC_DAPM_OUTPUT("SPKN"),
+SND_SOC_DAPM_OUTPUT("SPKP"),
+
+SND_SOC_DAPM_SUPPLY("CLK_SYS", WM9081_CLOCK_CONTROL_3, 0, 0, clk_sys_event,
+		    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_SUPPLY("CLK_DSP", WM9081_CLOCK_CONTROL_3, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("TOCLK", WM9081_CLOCK_CONTROL_3, 2, 0, NULL, 0),
+};
+
+
+static const struct snd_soc_dapm_route audio_paths[] = {
+	{ "DAC", NULL, "CLK_SYS" },
+	{ "DAC", NULL, "CLK_DSP" },
+
+	{ "Mixer", "IN1 Switch", "IN1" },
+	{ "Mixer", "IN2 Switch", "IN2" },
+	{ "Mixer", "Playback Switch", "DAC" },
+
+	{ "LINEOUT PGA", NULL, "Mixer" },
+	{ "LINEOUT PGA", NULL, "TOCLK" },
+	{ "LINEOUT PGA", NULL, "CLK_SYS" },
+
+	{ "LINEOUT", NULL, "LINEOUT PGA" },
+
+	{ "Speaker PGA", NULL, "Mixer" },
+	{ "Speaker PGA", NULL, "TOCLK" },
+	{ "Speaker PGA", NULL, "CLK_SYS" },
+
+	{ "SPKN", NULL, "Speaker PGA" },
+	{ "SPKP", NULL, "Speaker PGA" },
+};
+
+static int wm9081_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	u16 reg;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		/* VMID=2*40k */
+		reg = snd_soc_read(codec, WM9081_VMID_CONTROL);
+		reg &= ~WM9081_VMID_SEL_MASK;
+		reg |= 0x2;
+		snd_soc_write(codec, WM9081_VMID_CONTROL, reg);
+
+		/* Normal bias current */
+		reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
+		reg &= ~WM9081_STBY_BIAS_ENA;
+		snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		/* Initial cold start */
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* Disable LINEOUT discharge */
+			reg = snd_soc_read(codec, WM9081_ANTI_POP_CONTROL);
+			reg &= ~WM9081_LINEOUT_DISCH;
+			snd_soc_write(codec, WM9081_ANTI_POP_CONTROL, reg);
+
+			/* Select startup bias source */
+			reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
+			reg |= WM9081_BIAS_SRC | WM9081_BIAS_ENA;
+			snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
+
+			/* VMID 2*4k; Soft VMID ramp enable */
+			reg = snd_soc_read(codec, WM9081_VMID_CONTROL);
+			reg |= WM9081_VMID_RAMP | 0x6;
+			snd_soc_write(codec, WM9081_VMID_CONTROL, reg);
+
+			mdelay(100);
+
+			/* Normal bias enable & soft start off */
+			reg |= WM9081_BIAS_ENA;
+			reg &= ~WM9081_VMID_RAMP;
+			snd_soc_write(codec, WM9081_VMID_CONTROL, reg);
+
+			/* Standard bias source */
+			reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
+			reg &= ~WM9081_BIAS_SRC;
+			snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
+		}
+
+		/* VMID 2*240k */
+		reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
+		reg &= ~WM9081_VMID_SEL_MASK;
+		reg |= 0x40;
+		snd_soc_write(codec, WM9081_VMID_CONTROL, reg);
+
+		/* Standby bias current on */
+		reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
+		reg |= WM9081_STBY_BIAS_ENA;
+		snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		/* Startup bias source */
+		reg = snd_soc_read(codec, WM9081_BIAS_CONTROL_1);
+		reg |= WM9081_BIAS_SRC;
+		snd_soc_write(codec, WM9081_BIAS_CONTROL_1, reg);
+
+		/* Disable VMID and biases with soft ramping */
+		reg = snd_soc_read(codec, WM9081_VMID_CONTROL);
+		reg &= ~(WM9081_VMID_SEL_MASK | WM9081_BIAS_ENA);
+		reg |= WM9081_VMID_RAMP;
+		snd_soc_write(codec, WM9081_VMID_CONTROL, reg);
+
+		/* Actively discharge LINEOUT */
+		reg = snd_soc_read(codec, WM9081_ANTI_POP_CONTROL);
+		reg |= WM9081_LINEOUT_DISCH;
+		snd_soc_write(codec, WM9081_ANTI_POP_CONTROL, reg);
+		break;
+	}
+
+	codec->bias_level = level;
+
+	return 0;
+}
+
+static int wm9081_set_dai_fmt(struct snd_soc_dai *dai,
+			      unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm9081_priv *wm9081 = codec->private_data;
+	unsigned int aif2 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_2);
+
+	aif2 &= ~(WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV |
+		  WM9081_BCLK_DIR | WM9081_LRCLK_DIR | WM9081_AIF_FMT_MASK);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		wm9081->master = 0;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		aif2 |= WM9081_LRCLK_DIR;
+		wm9081->master = 1;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		aif2 |= WM9081_BCLK_DIR;
+		wm9081->master = 1;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		aif2 |= WM9081_LRCLK_DIR | WM9081_BCLK_DIR;
+		wm9081->master = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_B:
+		aif2 |= WM9081_AIF_LRCLK_INV;
+	case SND_SOC_DAIFMT_DSP_A:
+		aif2 |= 0x3;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		aif2 |= 0x2;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		aif2 |= 0x1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+	case SND_SOC_DAIFMT_DSP_B:
+		/* frame inversion not valid for DSP modes */
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif2 |= WM9081_AIF_BCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_RIGHT_J:
+	case SND_SOC_DAIFMT_LEFT_J:
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_IF:
+			aif2 |= WM9081_AIF_BCLK_INV | WM9081_AIF_LRCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif2 |= WM9081_AIF_BCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_NB_IF:
+			aif2 |= WM9081_AIF_LRCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);
+
+	return 0;
+}
+
+static int wm9081_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm9081_priv *wm9081 = codec->private_data;
+	int ret, i, best, best_val, cur_val;
+	unsigned int clk_ctrl2, aif1, aif2, aif3, aif4;
+
+	clk_ctrl2 = snd_soc_read(codec, WM9081_CLOCK_CONTROL_2);
+	clk_ctrl2 &= ~(WM9081_CLK_SYS_RATE_MASK | WM9081_SAMPLE_RATE_MASK);
+
+	aif1 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_1);
+
+	aif2 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_2);
+	aif2 &= ~WM9081_AIF_WL_MASK;
+
+	aif3 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_3);
+	aif3 &= ~WM9081_BCLK_DIV_MASK;
+
+	aif4 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_4);
+	aif4 &= ~WM9081_LRCLK_RATE_MASK;
+
+	wm9081->fs = params_rate(params);
+
+	if (wm9081->tdm_width) {
+		/* If TDM is set up then that fixes our BCLK. */
+		int slots = ((aif1 & WM9081_AIFDAC_TDM_MODE_MASK) >>
+			     WM9081_AIFDAC_TDM_MODE_SHIFT) + 1;
+
+		wm9081->bclk = wm9081->fs * wm9081->tdm_width * slots;
+	} else {
+		/* Otherwise work out a BCLK from the sample size */
+		wm9081->bclk = 2 * wm9081->fs;
+
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+			wm9081->bclk *= 16;
+			break;
+		case SNDRV_PCM_FORMAT_S20_3LE:
+			wm9081->bclk *= 20;
+			aif2 |= 0x4;
+			break;
+		case SNDRV_PCM_FORMAT_S24_LE:
+			wm9081->bclk *= 24;
+			aif2 |= 0x8;
+			break;
+		case SNDRV_PCM_FORMAT_S32_LE:
+			wm9081->bclk *= 32;
+			aif2 |= 0xc;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	dev_dbg(codec->dev, "Target BCLK is %dHz\n", wm9081->bclk);
+
+	ret = configure_clock(codec);
+	if (ret != 0)
+		return ret;
+
+	/* Select nearest CLK_SYS_RATE */
+	best = 0;
+	best_val = abs((wm9081->sysclk_rate / clk_sys_rates[0].ratio)
+		       - wm9081->fs);
+	for (i = 1; i < ARRAY_SIZE(clk_sys_rates); i++) {
+		cur_val = abs((wm9081->sysclk_rate /
+			       clk_sys_rates[i].ratio) - wm9081->fs);;
+		if (cur_val < best_val) {
+			best = i;
+			best_val = cur_val;
+		}
+	}
+	dev_dbg(codec->dev, "Selected CLK_SYS_RATIO of %d\n",
+		clk_sys_rates[best].ratio);
+	clk_ctrl2 |= (clk_sys_rates[best].clk_sys_rate
+		      << WM9081_CLK_SYS_RATE_SHIFT);
+
+	/* SAMPLE_RATE */
+	best = 0;
+	best_val = abs(wm9081->fs - sample_rates[0].rate);
+	for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+		/* Closest match */
+		cur_val = abs(wm9081->fs - sample_rates[i].rate);
+		if (cur_val < best_val) {
+			best = i;
+			best_val = cur_val;
+		}
+	}
+	dev_dbg(codec->dev, "Selected SAMPLE_RATE of %dHz\n",
+		sample_rates[best].rate);
+	clk_ctrl2 |= (sample_rates[best].sample_rate
+			<< WM9081_SAMPLE_RATE_SHIFT);
+
+	/* BCLK_DIV */
+	best = 0;
+	best_val = INT_MAX;
+	for (i = 0; i < ARRAY_SIZE(bclk_divs); i++) {
+		cur_val = ((wm9081->sysclk_rate * 10) / bclk_divs[i].div)
+			- wm9081->bclk;
+		if (cur_val < 0) /* Table is sorted */
+			break;
+		if (cur_val < best_val) {
+			best = i;
+			best_val = cur_val;
+		}
+	}
+	wm9081->bclk = (wm9081->sysclk_rate * 10) / bclk_divs[best].div;
+	dev_dbg(codec->dev, "Selected BCLK_DIV of %d for %dHz BCLK\n",
+		bclk_divs[best].div, wm9081->bclk);
+	aif3 |= bclk_divs[best].bclk_div;
+
+	/* LRCLK is a simple fraction of BCLK */
+	dev_dbg(codec->dev, "LRCLK_RATE is %d\n", wm9081->bclk / wm9081->fs);
+	aif4 |= wm9081->bclk / wm9081->fs;
+
+	/* Apply a ReTune Mobile configuration if it's in use */
+	if (wm9081->retune) {
+		struct wm9081_retune_mobile_config *retune = wm9081->retune;
+		struct wm9081_retune_mobile_setting *s;
+		int eq1;
+
+		best = 0;
+		best_val = abs(retune->configs[0].rate - wm9081->fs);
+		for (i = 0; i < retune->num_configs; i++) {
+			cur_val = abs(retune->configs[i].rate - wm9081->fs);
+			if (cur_val < best_val) {
+				best_val = cur_val;
+				best = i;
+			}
+		}
+		s = &retune->configs[best];
+
+		dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
+			s->name, s->rate);
+
+		/* If the EQ is enabled then disable it while we write out */
+		eq1 = snd_soc_read(codec, WM9081_EQ_1) & WM9081_EQ_ENA;
+		if (eq1 & WM9081_EQ_ENA)
+			snd_soc_write(codec, WM9081_EQ_1, 0);
+
+		/* Write out the other values */
+		for (i = 1; i < ARRAY_SIZE(s->config); i++)
+			snd_soc_write(codec, WM9081_EQ_1 + i, s->config[i]);
+
+		eq1 |= (s->config[0] & ~WM9081_EQ_ENA);
+		snd_soc_write(codec, WM9081_EQ_1, eq1);
+	}
+
+	snd_soc_write(codec, WM9081_CLOCK_CONTROL_2, clk_ctrl2);
+	snd_soc_write(codec, WM9081_AUDIO_INTERFACE_2, aif2);
+	snd_soc_write(codec, WM9081_AUDIO_INTERFACE_3, aif3);
+	snd_soc_write(codec, WM9081_AUDIO_INTERFACE_4, aif4);
+
+	return 0;
+}
+
+static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	unsigned int reg;
+
+	reg = snd_soc_read(codec, WM9081_DAC_DIGITAL_2);
+
+	if (mute)
+		reg |= WM9081_DAC_MUTE;
+	else
+		reg &= ~WM9081_DAC_MUTE;
+
+	snd_soc_write(codec, WM9081_DAC_DIGITAL_2, reg);
+
+	return 0;
+}
+
+static int wm9081_set_sysclk(struct snd_soc_dai *codec_dai,
+			     int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct wm9081_priv *wm9081 = codec->private_data;
+
+	switch (clk_id) {
+	case WM9081_SYSCLK_MCLK:
+	case WM9081_SYSCLK_FLL_MCLK:
+		wm9081->sysclk_source = clk_id;
+		wm9081->mclk_rate = freq;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
+	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wm9081_priv *wm9081 = codec->private_data;
+	unsigned int aif1 = snd_soc_read(codec, WM9081_AUDIO_INTERFACE_1);
+
+	aif1 &= ~(WM9081_AIFDAC_TDM_SLOT_MASK | WM9081_AIFDAC_TDM_MODE_MASK);
+
+	if (slots < 0 || slots > 4)
+		return -EINVAL;
+
+	wm9081->tdm_width = slot_width;
+
+	if (slots == 0)
+		slots = 1;
+
+	aif1 |= (slots - 1) << WM9081_AIFDAC_TDM_MODE_SHIFT;
+
+	switch (rx_mask) {
+	case 1:
+		break;
+	case 2:
+		aif1 |= 0x10;
+		break;
+	case 4:
+		aif1 |= 0x20;
+		break;
+	case 8:
+		aif1 |= 0x30;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, WM9081_AUDIO_INTERFACE_1, aif1);
+
+	return 0;
+}
+
+#define WM9081_RATES SNDRV_PCM_RATE_8000_96000
+
+#define WM9081_FORMATS \
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+	 SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops wm9081_dai_ops = {
+	.hw_params = wm9081_hw_params,
+	.set_sysclk = wm9081_set_sysclk,
+	.set_fmt = wm9081_set_dai_fmt,
+	.digital_mute = wm9081_digital_mute,
+	.set_tdm_slot = wm9081_set_tdm_slot,
+};
+
+/* We report two channels because the CODEC processes a stereo signal, even
+ * though it is only capable of handling a mono output.
+ */
+struct snd_soc_dai wm9081_dai = {
+	.name = "WM9081",
+	.playback = {
+		.stream_name = "HiFi Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = WM9081_RATES,
+		.formats = WM9081_FORMATS,
+	},
+	.ops = &wm9081_dai_ops,
+};
+EXPORT_SYMBOL_GPL(wm9081_dai);
+
+
+static struct snd_soc_codec *wm9081_codec;
+
+static int wm9081_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	struct wm9081_priv *wm9081;
+	int ret = 0;
+
+	if (wm9081_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm9081_codec;
+	codec = wm9081_codec;
+	wm9081 = codec->private_data;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, wm9081_snd_controls,
+			     ARRAY_SIZE(wm9081_snd_controls));
+	if (!wm9081->retune) {
+		dev_dbg(codec->dev,
+			"No ReTune Mobile data, using normal EQ\n");
+		snd_soc_add_controls(codec, wm9081_eq_controls,
+				     ARRAY_SIZE(wm9081_eq_controls));
+	}
+
+	snd_soc_dapm_new_controls(codec, wm9081_dapm_widgets,
+				  ARRAY_SIZE(wm9081_dapm_widgets));
+	snd_soc_dapm_add_routes(codec, audio_paths, ARRAY_SIZE(audio_paths));
+	snd_soc_dapm_new_widgets(codec);
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+static int wm9081_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm9081_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+
+	wm9081_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int wm9081_resume(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec = socdev->card->codec;
+	u16 *reg_cache = codec->reg_cache;
+	int i;
+
+	for (i = 0; i < codec->reg_cache_size; i++) {
+		if (i == WM9081_SOFTWARE_RESET)
+			continue;
+
+		snd_soc_write(codec, i, reg_cache[i]);
+	}
+
+	wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+#else
+#define wm9081_suspend NULL
+#define wm9081_resume NULL
+#endif
+
+struct snd_soc_codec_device soc_codec_dev_wm9081 = {
+	.probe = 	wm9081_probe,
+	.remove = 	wm9081_remove,
+	.suspend =	wm9081_suspend,
+	.resume =	wm9081_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm9081);
+
+static int wm9081_register(struct wm9081_priv *wm9081,
+			   enum snd_soc_control_type control)
+{
+	struct snd_soc_codec *codec = &wm9081->codec;
+	int ret;
+	u16 reg;
+
+	if (wm9081_codec) {
+		dev_err(codec->dev, "Another WM9081 is registered\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm9081;
+	codec->name = "WM9081";
+	codec->owner = THIS_MODULE;
+	codec->dai = &wm9081_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = ARRAY_SIZE(wm9081->reg_cache);
+	codec->reg_cache = &wm9081->reg_cache;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm9081_set_bias_level;
+	codec->volatile_register = wm9081_volatile_register;
+
+	memcpy(codec->reg_cache, wm9081_reg_defaults,
+	       sizeof(wm9081_reg_defaults));
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, control);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	reg = snd_soc_read(codec, WM9081_SOFTWARE_RESET);
+	if (reg != 0x9081) {
+		dev_err(codec->dev, "Device is not a WM9081: ID=0x%x\n", reg);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = wm9081_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		return ret;
+	}
+
+	wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	/* Enable zero cross by default */
+	reg = snd_soc_read(codec, WM9081_ANALOGUE_LINEOUT);
+	snd_soc_write(codec, WM9081_ANALOGUE_LINEOUT, reg | WM9081_LINEOUTZC);
+	reg = snd_soc_read(codec, WM9081_ANALOGUE_SPEAKER_PGA);
+	snd_soc_write(codec, WM9081_ANALOGUE_SPEAKER_PGA,
+		     reg | WM9081_SPKPGAZC);
+
+	wm9081_dai.dev = codec->dev;
+
+	wm9081_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_register_dai(&wm9081_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		return ret;
+	}
+
+	return 0;
+
+err:
+	kfree(wm9081);
+	return ret;
+}
+
+static void wm9081_unregister(struct wm9081_priv *wm9081)
+{
+	wm9081_set_bias_level(&wm9081->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dai(&wm9081_dai);
+	snd_soc_unregister_codec(&wm9081->codec);
+	kfree(wm9081);
+	wm9081_codec = NULL;
+}
+
+static __devinit int wm9081_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm9081_priv *wm9081;
+	struct snd_soc_codec *codec;
+
+	wm9081 = kzalloc(sizeof(struct wm9081_priv), GFP_KERNEL);
+	if (wm9081 == NULL)
+		return -ENOMEM;
+
+	codec = &wm9081->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+	wm9081->retune = i2c->dev.platform_data;
+
+	i2c_set_clientdata(i2c, wm9081);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+
+	return wm9081_register(wm9081, SND_SOC_I2C);
+}
+
+static __devexit int wm9081_i2c_remove(struct i2c_client *client)
+{
+	struct wm9081_priv *wm9081 = i2c_get_clientdata(client);
+	wm9081_unregister(wm9081);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm9081_i2c_suspend(struct i2c_client *client, pm_message_t msg)
+{
+	return snd_soc_suspend_device(&client->dev);
+}
+
+static int wm9081_i2c_resume(struct i2c_client *client)
+{
+	return snd_soc_resume_device(&client->dev);
+}
+#else
+#define wm9081_i2c_suspend NULL
+#define wm9081_i2c_resume NULL
+#endif
+
+static const struct i2c_device_id wm9081_i2c_id[] = {
+	{ "wm9081", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id);
+
+static struct i2c_driver wm9081_i2c_driver = {
+	.driver = {
+		.name = "wm9081",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm9081_i2c_probe,
+	.remove =   __devexit_p(wm9081_i2c_remove),
+	.suspend =  wm9081_i2c_suspend,
+	.resume =   wm9081_i2c_resume,
+	.id_table = wm9081_i2c_id,
+};
+
+static int __init wm9081_modinit(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&wm9081_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM9081 I2C driver: %d\n",
+		       ret);
+	}
+
+	return ret;
+}
+module_init(wm9081_modinit);
+
+static void __exit wm9081_exit(void)
+{
+	i2c_del_driver(&wm9081_i2c_driver);
+}
+module_exit(wm9081_exit);
+
+
+MODULE_DESCRIPTION("ASoC WM9081 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm9081.h b/sound/soc/codecs/wm9081.h
new file mode 100644
index 0000000..42d3bc7
--- /dev/null
+++ b/sound/soc/codecs/wm9081.h
@@ -0,0 +1,787 @@
+#ifndef WM9081_H
+#define WM9081_H
+
+/*
+ * wm9081.c  --  WM9081 ALSA SoC Audio driver
+ *
+ * Author: Mark Brown
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <sound/soc.h>
+
+extern struct snd_soc_dai wm9081_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm9081;
+
+/*
+ * SYSCLK sources
+ */
+#define WM9081_SYSCLK_MCLK      1   /* Use MCLK without FLL */
+#define WM9081_SYSCLK_FLL_MCLK  2   /* Use MCLK, enabling FLL if required */
+
+/*
+ * Register values.
+ */
+#define WM9081_SOFTWARE_RESET                   0x00
+#define WM9081_ANALOGUE_LINEOUT                 0x02
+#define WM9081_ANALOGUE_SPEAKER_PGA             0x03
+#define WM9081_VMID_CONTROL                     0x04
+#define WM9081_BIAS_CONTROL_1                   0x05
+#define WM9081_ANALOGUE_MIXER                   0x07
+#define WM9081_ANTI_POP_CONTROL                 0x08
+#define WM9081_ANALOGUE_SPEAKER_1               0x09
+#define WM9081_ANALOGUE_SPEAKER_2               0x0A
+#define WM9081_POWER_MANAGEMENT                 0x0B
+#define WM9081_CLOCK_CONTROL_1                  0x0C
+#define WM9081_CLOCK_CONTROL_2                  0x0D
+#define WM9081_CLOCK_CONTROL_3                  0x0E
+#define WM9081_FLL_CONTROL_1                    0x10
+#define WM9081_FLL_CONTROL_2                    0x11
+#define WM9081_FLL_CONTROL_3                    0x12
+#define WM9081_FLL_CONTROL_4                    0x13
+#define WM9081_FLL_CONTROL_5                    0x14
+#define WM9081_AUDIO_INTERFACE_1                0x16
+#define WM9081_AUDIO_INTERFACE_2                0x17
+#define WM9081_AUDIO_INTERFACE_3                0x18
+#define WM9081_AUDIO_INTERFACE_4                0x19
+#define WM9081_INTERRUPT_STATUS                 0x1A
+#define WM9081_INTERRUPT_STATUS_MASK            0x1B
+#define WM9081_INTERRUPT_POLARITY               0x1C
+#define WM9081_INTERRUPT_CONTROL                0x1D
+#define WM9081_DAC_DIGITAL_1                    0x1E
+#define WM9081_DAC_DIGITAL_2                    0x1F
+#define WM9081_DRC_1                            0x20
+#define WM9081_DRC_2                            0x21
+#define WM9081_DRC_3                            0x22
+#define WM9081_DRC_4                            0x23
+#define WM9081_WRITE_SEQUENCER_1                0x26
+#define WM9081_WRITE_SEQUENCER_2                0x27
+#define WM9081_MW_SLAVE_1                       0x28
+#define WM9081_EQ_1                             0x2A
+#define WM9081_EQ_2                             0x2B
+#define WM9081_EQ_3                             0x2C
+#define WM9081_EQ_4                             0x2D
+#define WM9081_EQ_5                             0x2E
+#define WM9081_EQ_6                             0x2F
+#define WM9081_EQ_7                             0x30
+#define WM9081_EQ_8                             0x31
+#define WM9081_EQ_9                             0x32
+#define WM9081_EQ_10                            0x33
+#define WM9081_EQ_11                            0x34
+#define WM9081_EQ_12                            0x35
+#define WM9081_EQ_13                            0x36
+#define WM9081_EQ_14                            0x37
+#define WM9081_EQ_15                            0x38
+#define WM9081_EQ_16                            0x39
+#define WM9081_EQ_17                            0x3A
+#define WM9081_EQ_18                            0x3B
+#define WM9081_EQ_19                            0x3C
+#define WM9081_EQ_20                            0x3D
+
+#define WM9081_REGISTER_COUNT                   55
+#define WM9081_MAX_REGISTER                     0x3D
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Software Reset
+ */
+#define WM9081_SW_RST_DEV_ID1_MASK              0xFFFF  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM9081_SW_RST_DEV_ID1_SHIFT                  0  /* SW_RST_DEV_ID1 - [15:0] */
+#define WM9081_SW_RST_DEV_ID1_WIDTH                 16  /* SW_RST_DEV_ID1 - [15:0] */
+
+/*
+ * R2 (0x02) - Analogue Lineout
+ */
+#define WM9081_LINEOUT_MUTE                     0x0080  /* LINEOUT_MUTE */
+#define WM9081_LINEOUT_MUTE_MASK                0x0080  /* LINEOUT_MUTE */
+#define WM9081_LINEOUT_MUTE_SHIFT                    7  /* LINEOUT_MUTE */
+#define WM9081_LINEOUT_MUTE_WIDTH                    1  /* LINEOUT_MUTE */
+#define WM9081_LINEOUTZC                        0x0040  /* LINEOUTZC */
+#define WM9081_LINEOUTZC_MASK                   0x0040  /* LINEOUTZC */
+#define WM9081_LINEOUTZC_SHIFT                       6  /* LINEOUTZC */
+#define WM9081_LINEOUTZC_WIDTH                       1  /* LINEOUTZC */
+#define WM9081_LINEOUT_VOL_MASK                 0x003F  /* LINEOUT_VOL - [5:0] */
+#define WM9081_LINEOUT_VOL_SHIFT                     0  /* LINEOUT_VOL - [5:0] */
+#define WM9081_LINEOUT_VOL_WIDTH                     6  /* LINEOUT_VOL - [5:0] */
+
+/*
+ * R3 (0x03) - Analogue Speaker PGA
+ */
+#define WM9081_SPKPGA_MUTE                      0x0080  /* SPKPGA_MUTE */
+#define WM9081_SPKPGA_MUTE_MASK                 0x0080  /* SPKPGA_MUTE */
+#define WM9081_SPKPGA_MUTE_SHIFT                     7  /* SPKPGA_MUTE */
+#define WM9081_SPKPGA_MUTE_WIDTH                     1  /* SPKPGA_MUTE */
+#define WM9081_SPKPGAZC                         0x0040  /* SPKPGAZC */
+#define WM9081_SPKPGAZC_MASK                    0x0040  /* SPKPGAZC */
+#define WM9081_SPKPGAZC_SHIFT                        6  /* SPKPGAZC */
+#define WM9081_SPKPGAZC_WIDTH                        1  /* SPKPGAZC */
+#define WM9081_SPKPGA_VOL_MASK                  0x003F  /* SPKPGA_VOL - [5:0] */
+#define WM9081_SPKPGA_VOL_SHIFT                      0  /* SPKPGA_VOL - [5:0] */
+#define WM9081_SPKPGA_VOL_WIDTH                      6  /* SPKPGA_VOL - [5:0] */
+
+/*
+ * R4 (0x04) - VMID Control
+ */
+#define WM9081_VMID_BUF_ENA                     0x0020  /* VMID_BUF_ENA */
+#define WM9081_VMID_BUF_ENA_MASK                0x0020  /* VMID_BUF_ENA */
+#define WM9081_VMID_BUF_ENA_SHIFT                    5  /* VMID_BUF_ENA */
+#define WM9081_VMID_BUF_ENA_WIDTH                    1  /* VMID_BUF_ENA */
+#define WM9081_VMID_RAMP                        0x0008  /* VMID_RAMP */
+#define WM9081_VMID_RAMP_MASK                   0x0008  /* VMID_RAMP */
+#define WM9081_VMID_RAMP_SHIFT                       3  /* VMID_RAMP */
+#define WM9081_VMID_RAMP_WIDTH                       1  /* VMID_RAMP */
+#define WM9081_VMID_SEL_MASK                    0x0006  /* VMID_SEL - [2:1] */
+#define WM9081_VMID_SEL_SHIFT                        1  /* VMID_SEL - [2:1] */
+#define WM9081_VMID_SEL_WIDTH                        2  /* VMID_SEL - [2:1] */
+#define WM9081_VMID_FAST_ST                     0x0001  /* VMID_FAST_ST */
+#define WM9081_VMID_FAST_ST_MASK                0x0001  /* VMID_FAST_ST */
+#define WM9081_VMID_FAST_ST_SHIFT                    0  /* VMID_FAST_ST */
+#define WM9081_VMID_FAST_ST_WIDTH                    1  /* VMID_FAST_ST */
+
+/*
+ * R5 (0x05) - Bias Control 1
+ */
+#define WM9081_BIAS_SRC                         0x0040  /* BIAS_SRC */
+#define WM9081_BIAS_SRC_MASK                    0x0040  /* BIAS_SRC */
+#define WM9081_BIAS_SRC_SHIFT                        6  /* BIAS_SRC */
+#define WM9081_BIAS_SRC_WIDTH                        1  /* BIAS_SRC */
+#define WM9081_STBY_BIAS_LVL                    0x0020  /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_LVL_MASK               0x0020  /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_LVL_SHIFT                   5  /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_LVL_WIDTH                   1  /* STBY_BIAS_LVL */
+#define WM9081_STBY_BIAS_ENA                    0x0010  /* STBY_BIAS_ENA */
+#define WM9081_STBY_BIAS_ENA_MASK               0x0010  /* STBY_BIAS_ENA */
+#define WM9081_STBY_BIAS_ENA_SHIFT                   4  /* STBY_BIAS_ENA */
+#define WM9081_STBY_BIAS_ENA_WIDTH                   1  /* STBY_BIAS_ENA */
+#define WM9081_BIAS_LVL_MASK                    0x000C  /* BIAS_LVL - [3:2] */
+#define WM9081_BIAS_LVL_SHIFT                        2  /* BIAS_LVL - [3:2] */
+#define WM9081_BIAS_LVL_WIDTH                        2  /* BIAS_LVL - [3:2] */
+#define WM9081_BIAS_ENA                         0x0002  /* BIAS_ENA */
+#define WM9081_BIAS_ENA_MASK                    0x0002  /* BIAS_ENA */
+#define WM9081_BIAS_ENA_SHIFT                        1  /* BIAS_ENA */
+#define WM9081_BIAS_ENA_WIDTH                        1  /* BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA                 0x0001  /* STARTUP_BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA_MASK            0x0001  /* STARTUP_BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA_SHIFT                0  /* STARTUP_BIAS_ENA */
+#define WM9081_STARTUP_BIAS_ENA_WIDTH                1  /* STARTUP_BIAS_ENA */
+
+/*
+ * R7 (0x07) - Analogue Mixer
+ */
+#define WM9081_DAC_SEL                          0x0010  /* DAC_SEL */
+#define WM9081_DAC_SEL_MASK                     0x0010  /* DAC_SEL */
+#define WM9081_DAC_SEL_SHIFT                         4  /* DAC_SEL */
+#define WM9081_DAC_SEL_WIDTH                         1  /* DAC_SEL */
+#define WM9081_IN2_VOL                          0x0008  /* IN2_VOL */
+#define WM9081_IN2_VOL_MASK                     0x0008  /* IN2_VOL */
+#define WM9081_IN2_VOL_SHIFT                         3  /* IN2_VOL */
+#define WM9081_IN2_VOL_WIDTH                         1  /* IN2_VOL */
+#define WM9081_IN2_ENA                          0x0004  /* IN2_ENA */
+#define WM9081_IN2_ENA_MASK                     0x0004  /* IN2_ENA */
+#define WM9081_IN2_ENA_SHIFT                         2  /* IN2_ENA */
+#define WM9081_IN2_ENA_WIDTH                         1  /* IN2_ENA */
+#define WM9081_IN1_VOL                          0x0002  /* IN1_VOL */
+#define WM9081_IN1_VOL_MASK                     0x0002  /* IN1_VOL */
+#define WM9081_IN1_VOL_SHIFT                         1  /* IN1_VOL */
+#define WM9081_IN1_VOL_WIDTH                         1  /* IN1_VOL */
+#define WM9081_IN1_ENA                          0x0001  /* IN1_ENA */
+#define WM9081_IN1_ENA_MASK                     0x0001  /* IN1_ENA */
+#define WM9081_IN1_ENA_SHIFT                         0  /* IN1_ENA */
+#define WM9081_IN1_ENA_WIDTH                         1  /* IN1_ENA */
+
+/*
+ * R8 (0x08) - Anti Pop Control
+ */
+#define WM9081_LINEOUT_DISCH                    0x0004  /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_DISCH_MASK               0x0004  /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_DISCH_SHIFT                   2  /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_DISCH_WIDTH                   1  /* LINEOUT_DISCH */
+#define WM9081_LINEOUT_VROI                     0x0002  /* LINEOUT_VROI */
+#define WM9081_LINEOUT_VROI_MASK                0x0002  /* LINEOUT_VROI */
+#define WM9081_LINEOUT_VROI_SHIFT                    1  /* LINEOUT_VROI */
+#define WM9081_LINEOUT_VROI_WIDTH                    1  /* LINEOUT_VROI */
+#define WM9081_LINEOUT_CLAMP                    0x0001  /* LINEOUT_CLAMP */
+#define WM9081_LINEOUT_CLAMP_MASK               0x0001  /* LINEOUT_CLAMP */
+#define WM9081_LINEOUT_CLAMP_SHIFT                   0  /* LINEOUT_CLAMP */
+#define WM9081_LINEOUT_CLAMP_WIDTH                   1  /* LINEOUT_CLAMP */
+
+/*
+ * R9 (0x09) - Analogue Speaker 1
+ */
+#define WM9081_SPK_DCGAIN_MASK                  0x0038  /* SPK_DCGAIN - [5:3] */
+#define WM9081_SPK_DCGAIN_SHIFT                      3  /* SPK_DCGAIN - [5:3] */
+#define WM9081_SPK_DCGAIN_WIDTH                      3  /* SPK_DCGAIN - [5:3] */
+#define WM9081_SPK_ACGAIN_MASK                  0x0007  /* SPK_ACGAIN - [2:0] */
+#define WM9081_SPK_ACGAIN_SHIFT                      0  /* SPK_ACGAIN - [2:0] */
+#define WM9081_SPK_ACGAIN_WIDTH                      3  /* SPK_ACGAIN - [2:0] */
+
+/*
+ * R10 (0x0A) - Analogue Speaker 2
+ */
+#define WM9081_SPK_MODE                         0x0040  /* SPK_MODE */
+#define WM9081_SPK_MODE_MASK                    0x0040  /* SPK_MODE */
+#define WM9081_SPK_MODE_SHIFT                        6  /* SPK_MODE */
+#define WM9081_SPK_MODE_WIDTH                        1  /* SPK_MODE */
+#define WM9081_SPK_INV_MUTE                     0x0010  /* SPK_INV_MUTE */
+#define WM9081_SPK_INV_MUTE_MASK                0x0010  /* SPK_INV_MUTE */
+#define WM9081_SPK_INV_MUTE_SHIFT                    4  /* SPK_INV_MUTE */
+#define WM9081_SPK_INV_MUTE_WIDTH                    1  /* SPK_INV_MUTE */
+#define WM9081_OUT_SPK_CTRL                     0x0008  /* OUT_SPK_CTRL */
+#define WM9081_OUT_SPK_CTRL_MASK                0x0008  /* OUT_SPK_CTRL */
+#define WM9081_OUT_SPK_CTRL_SHIFT                    3  /* OUT_SPK_CTRL */
+#define WM9081_OUT_SPK_CTRL_WIDTH                    1  /* OUT_SPK_CTRL */
+
+/*
+ * R11 (0x0B) - Power Management
+ */
+#define WM9081_TSHUT_ENA                        0x0100  /* TSHUT_ENA */
+#define WM9081_TSHUT_ENA_MASK                   0x0100  /* TSHUT_ENA */
+#define WM9081_TSHUT_ENA_SHIFT                       8  /* TSHUT_ENA */
+#define WM9081_TSHUT_ENA_WIDTH                       1  /* TSHUT_ENA */
+#define WM9081_TSENSE_ENA                       0x0080  /* TSENSE_ENA */
+#define WM9081_TSENSE_ENA_MASK                  0x0080  /* TSENSE_ENA */
+#define WM9081_TSENSE_ENA_SHIFT                      7  /* TSENSE_ENA */
+#define WM9081_TSENSE_ENA_WIDTH                      1  /* TSENSE_ENA */
+#define WM9081_TEMP_SHUT                        0x0040  /* TEMP_SHUT */
+#define WM9081_TEMP_SHUT_MASK                   0x0040  /* TEMP_SHUT */
+#define WM9081_TEMP_SHUT_SHIFT                       6  /* TEMP_SHUT */
+#define WM9081_TEMP_SHUT_WIDTH                       1  /* TEMP_SHUT */
+#define WM9081_LINEOUT_ENA                      0x0010  /* LINEOUT_ENA */
+#define WM9081_LINEOUT_ENA_MASK                 0x0010  /* LINEOUT_ENA */
+#define WM9081_LINEOUT_ENA_SHIFT                     4  /* LINEOUT_ENA */
+#define WM9081_LINEOUT_ENA_WIDTH                     1  /* LINEOUT_ENA */
+#define WM9081_SPKPGA_ENA                       0x0004  /* SPKPGA_ENA */
+#define WM9081_SPKPGA_ENA_MASK                  0x0004  /* SPKPGA_ENA */
+#define WM9081_SPKPGA_ENA_SHIFT                      2  /* SPKPGA_ENA */
+#define WM9081_SPKPGA_ENA_WIDTH                      1  /* SPKPGA_ENA */
+#define WM9081_SPK_ENA                          0x0002  /* SPK_ENA */
+#define WM9081_SPK_ENA_MASK                     0x0002  /* SPK_ENA */
+#define WM9081_SPK_ENA_SHIFT                         1  /* SPK_ENA */
+#define WM9081_SPK_ENA_WIDTH                         1  /* SPK_ENA */
+#define WM9081_DAC_ENA                          0x0001  /* DAC_ENA */
+#define WM9081_DAC_ENA_MASK                     0x0001  /* DAC_ENA */
+#define WM9081_DAC_ENA_SHIFT                         0  /* DAC_ENA */
+#define WM9081_DAC_ENA_WIDTH                         1  /* DAC_ENA */
+
+/*
+ * R12 (0x0C) - Clock Control 1
+ */
+#define WM9081_CLK_OP_DIV_MASK                  0x1C00  /* CLK_OP_DIV - [12:10] */
+#define WM9081_CLK_OP_DIV_SHIFT                     10  /* CLK_OP_DIV - [12:10] */
+#define WM9081_CLK_OP_DIV_WIDTH                      3  /* CLK_OP_DIV - [12:10] */
+#define WM9081_CLK_TO_DIV_MASK                  0x0300  /* CLK_TO_DIV - [9:8] */
+#define WM9081_CLK_TO_DIV_SHIFT                      8  /* CLK_TO_DIV - [9:8] */
+#define WM9081_CLK_TO_DIV_WIDTH                      2  /* CLK_TO_DIV - [9:8] */
+#define WM9081_MCLKDIV2                         0x0080  /* MCLKDIV2 */
+#define WM9081_MCLKDIV2_MASK                    0x0080  /* MCLKDIV2 */
+#define WM9081_MCLKDIV2_SHIFT                        7  /* MCLKDIV2 */
+#define WM9081_MCLKDIV2_WIDTH                        1  /* MCLKDIV2 */
+
+/*
+ * R13 (0x0D) - Clock Control 2
+ */
+#define WM9081_CLK_SYS_RATE_MASK                0x00F0  /* CLK_SYS_RATE - [7:4] */
+#define WM9081_CLK_SYS_RATE_SHIFT                    4  /* CLK_SYS_RATE - [7:4] */
+#define WM9081_CLK_SYS_RATE_WIDTH                    4  /* CLK_SYS_RATE - [7:4] */
+#define WM9081_SAMPLE_RATE_MASK                 0x000F  /* SAMPLE_RATE - [3:0] */
+#define WM9081_SAMPLE_RATE_SHIFT                     0  /* SAMPLE_RATE - [3:0] */
+#define WM9081_SAMPLE_RATE_WIDTH                     4  /* SAMPLE_RATE - [3:0] */
+
+/*
+ * R14 (0x0E) - Clock Control 3
+ */
+#define WM9081_CLK_SRC_SEL                      0x2000  /* CLK_SRC_SEL */
+#define WM9081_CLK_SRC_SEL_MASK                 0x2000  /* CLK_SRC_SEL */
+#define WM9081_CLK_SRC_SEL_SHIFT                    13  /* CLK_SRC_SEL */
+#define WM9081_CLK_SRC_SEL_WIDTH                     1  /* CLK_SRC_SEL */
+#define WM9081_CLK_OP_ENA                       0x0020  /* CLK_OP_ENA */
+#define WM9081_CLK_OP_ENA_MASK                  0x0020  /* CLK_OP_ENA */
+#define WM9081_CLK_OP_ENA_SHIFT                      5  /* CLK_OP_ENA */
+#define WM9081_CLK_OP_ENA_WIDTH                      1  /* CLK_OP_ENA */
+#define WM9081_CLK_TO_ENA                       0x0004  /* CLK_TO_ENA */
+#define WM9081_CLK_TO_ENA_MASK                  0x0004  /* CLK_TO_ENA */
+#define WM9081_CLK_TO_ENA_SHIFT                      2  /* CLK_TO_ENA */
+#define WM9081_CLK_TO_ENA_WIDTH                      1  /* CLK_TO_ENA */
+#define WM9081_CLK_DSP_ENA                      0x0002  /* CLK_DSP_ENA */
+#define WM9081_CLK_DSP_ENA_MASK                 0x0002  /* CLK_DSP_ENA */
+#define WM9081_CLK_DSP_ENA_SHIFT                     1  /* CLK_DSP_ENA */
+#define WM9081_CLK_DSP_ENA_WIDTH                     1  /* CLK_DSP_ENA */
+#define WM9081_CLK_SYS_ENA                      0x0001  /* CLK_SYS_ENA */
+#define WM9081_CLK_SYS_ENA_MASK                 0x0001  /* CLK_SYS_ENA */
+#define WM9081_CLK_SYS_ENA_SHIFT                     0  /* CLK_SYS_ENA */
+#define WM9081_CLK_SYS_ENA_WIDTH                     1  /* CLK_SYS_ENA */
+
+/*
+ * R16 (0x10) - FLL Control 1
+ */
+#define WM9081_FLL_HOLD                         0x0008  /* FLL_HOLD */
+#define WM9081_FLL_HOLD_MASK                    0x0008  /* FLL_HOLD */
+#define WM9081_FLL_HOLD_SHIFT                        3  /* FLL_HOLD */
+#define WM9081_FLL_HOLD_WIDTH                        1  /* FLL_HOLD */
+#define WM9081_FLL_FRAC                         0x0004  /* FLL_FRAC */
+#define WM9081_FLL_FRAC_MASK                    0x0004  /* FLL_FRAC */
+#define WM9081_FLL_FRAC_SHIFT                        2  /* FLL_FRAC */
+#define WM9081_FLL_FRAC_WIDTH                        1  /* FLL_FRAC */
+#define WM9081_FLL_ENA                          0x0001  /* FLL_ENA */
+#define WM9081_FLL_ENA_MASK                     0x0001  /* FLL_ENA */
+#define WM9081_FLL_ENA_SHIFT                         0  /* FLL_ENA */
+#define WM9081_FLL_ENA_WIDTH                         1  /* FLL_ENA */
+
+/*
+ * R17 (0x11) - FLL Control 2
+ */
+#define WM9081_FLL_OUTDIV_MASK                  0x0700  /* FLL_OUTDIV - [10:8] */
+#define WM9081_FLL_OUTDIV_SHIFT                      8  /* FLL_OUTDIV - [10:8] */
+#define WM9081_FLL_OUTDIV_WIDTH                      3  /* FLL_OUTDIV - [10:8] */
+#define WM9081_FLL_CTRL_RATE_MASK               0x0070  /* FLL_CTRL_RATE - [6:4] */
+#define WM9081_FLL_CTRL_RATE_SHIFT                   4  /* FLL_CTRL_RATE - [6:4] */
+#define WM9081_FLL_CTRL_RATE_WIDTH                   3  /* FLL_CTRL_RATE - [6:4] */
+#define WM9081_FLL_FRATIO_MASK                  0x0007  /* FLL_FRATIO - [2:0] */
+#define WM9081_FLL_FRATIO_SHIFT                      0  /* FLL_FRATIO - [2:0] */
+#define WM9081_FLL_FRATIO_WIDTH                      3  /* FLL_FRATIO - [2:0] */
+
+/*
+ * R18 (0x12) - FLL Control 3
+ */
+#define WM9081_FLL_K_MASK                       0xFFFF  /* FLL_K - [15:0] */
+#define WM9081_FLL_K_SHIFT                           0  /* FLL_K - [15:0] */
+#define WM9081_FLL_K_WIDTH                          16  /* FLL_K - [15:0] */
+
+/*
+ * R19 (0x13) - FLL Control 4
+ */
+#define WM9081_FLL_N_MASK                       0x7FE0  /* FLL_N - [14:5] */
+#define WM9081_FLL_N_SHIFT                           5  /* FLL_N - [14:5] */
+#define WM9081_FLL_N_WIDTH                          10  /* FLL_N - [14:5] */
+#define WM9081_FLL_GAIN_MASK                    0x000F  /* FLL_GAIN - [3:0] */
+#define WM9081_FLL_GAIN_SHIFT                        0  /* FLL_GAIN - [3:0] */
+#define WM9081_FLL_GAIN_WIDTH                        4  /* FLL_GAIN - [3:0] */
+
+/*
+ * R20 (0x14) - FLL Control 5
+ */
+#define WM9081_FLL_CLK_REF_DIV_MASK             0x0018  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM9081_FLL_CLK_REF_DIV_SHIFT                 3  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM9081_FLL_CLK_REF_DIV_WIDTH                 2  /* FLL_CLK_REF_DIV - [4:3] */
+#define WM9081_FLL_CLK_SRC_MASK                 0x0003  /* FLL_CLK_SRC - [1:0] */
+#define WM9081_FLL_CLK_SRC_SHIFT                     0  /* FLL_CLK_SRC - [1:0] */
+#define WM9081_FLL_CLK_SRC_WIDTH                     2  /* FLL_CLK_SRC - [1:0] */
+
+/*
+ * R22 (0x16) - Audio Interface 1
+ */
+#define WM9081_AIFDAC_CHAN                      0x0040  /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_CHAN_MASK                 0x0040  /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_CHAN_SHIFT                     6  /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_CHAN_WIDTH                     1  /* AIFDAC_CHAN */
+#define WM9081_AIFDAC_TDM_SLOT_MASK             0x0030  /* AIFDAC_TDM_SLOT - [5:4] */
+#define WM9081_AIFDAC_TDM_SLOT_SHIFT                 4  /* AIFDAC_TDM_SLOT - [5:4] */
+#define WM9081_AIFDAC_TDM_SLOT_WIDTH                 2  /* AIFDAC_TDM_SLOT - [5:4] */
+#define WM9081_AIFDAC_TDM_MODE_MASK             0x000C  /* AIFDAC_TDM_MODE - [3:2] */
+#define WM9081_AIFDAC_TDM_MODE_SHIFT                 2  /* AIFDAC_TDM_MODE - [3:2] */
+#define WM9081_AIFDAC_TDM_MODE_WIDTH                 2  /* AIFDAC_TDM_MODE - [3:2] */
+#define WM9081_DAC_COMP                         0x0002  /* DAC_COMP */
+#define WM9081_DAC_COMP_MASK                    0x0002  /* DAC_COMP */
+#define WM9081_DAC_COMP_SHIFT                        1  /* DAC_COMP */
+#define WM9081_DAC_COMP_WIDTH                        1  /* DAC_COMP */
+#define WM9081_DAC_COMPMODE                     0x0001  /* DAC_COMPMODE */
+#define WM9081_DAC_COMPMODE_MASK                0x0001  /* DAC_COMPMODE */
+#define WM9081_DAC_COMPMODE_SHIFT                    0  /* DAC_COMPMODE */
+#define WM9081_DAC_COMPMODE_WIDTH                    1  /* DAC_COMPMODE */
+
+/*
+ * R23 (0x17) - Audio Interface 2
+ */
+#define WM9081_AIF_TRIS                         0x0200  /* AIF_TRIS */
+#define WM9081_AIF_TRIS_MASK                    0x0200  /* AIF_TRIS */
+#define WM9081_AIF_TRIS_SHIFT                        9  /* AIF_TRIS */
+#define WM9081_AIF_TRIS_WIDTH                        1  /* AIF_TRIS */
+#define WM9081_DAC_DAT_INV                      0x0100  /* DAC_DAT_INV */
+#define WM9081_DAC_DAT_INV_MASK                 0x0100  /* DAC_DAT_INV */
+#define WM9081_DAC_DAT_INV_SHIFT                     8  /* DAC_DAT_INV */
+#define WM9081_DAC_DAT_INV_WIDTH                     1  /* DAC_DAT_INV */
+#define WM9081_AIF_BCLK_INV                     0x0080  /* AIF_BCLK_INV */
+#define WM9081_AIF_BCLK_INV_MASK                0x0080  /* AIF_BCLK_INV */
+#define WM9081_AIF_BCLK_INV_SHIFT                    7  /* AIF_BCLK_INV */
+#define WM9081_AIF_BCLK_INV_WIDTH                    1  /* AIF_BCLK_INV */
+#define WM9081_BCLK_DIR                         0x0040  /* BCLK_DIR */
+#define WM9081_BCLK_DIR_MASK                    0x0040  /* BCLK_DIR */
+#define WM9081_BCLK_DIR_SHIFT                        6  /* BCLK_DIR */
+#define WM9081_BCLK_DIR_WIDTH                        1  /* BCLK_DIR */
+#define WM9081_LRCLK_DIR                        0x0020  /* LRCLK_DIR */
+#define WM9081_LRCLK_DIR_MASK                   0x0020  /* LRCLK_DIR */
+#define WM9081_LRCLK_DIR_SHIFT                       5  /* LRCLK_DIR */
+#define WM9081_LRCLK_DIR_WIDTH                       1  /* LRCLK_DIR */
+#define WM9081_AIF_LRCLK_INV                    0x0010  /* AIF_LRCLK_INV */
+#define WM9081_AIF_LRCLK_INV_MASK               0x0010  /* AIF_LRCLK_INV */
+#define WM9081_AIF_LRCLK_INV_SHIFT                   4  /* AIF_LRCLK_INV */
+#define WM9081_AIF_LRCLK_INV_WIDTH                   1  /* AIF_LRCLK_INV */
+#define WM9081_AIF_WL_MASK                      0x000C  /* AIF_WL - [3:2] */
+#define WM9081_AIF_WL_SHIFT                          2  /* AIF_WL - [3:2] */
+#define WM9081_AIF_WL_WIDTH                          2  /* AIF_WL - [3:2] */
+#define WM9081_AIF_FMT_MASK                     0x0003  /* AIF_FMT - [1:0] */
+#define WM9081_AIF_FMT_SHIFT                         0  /* AIF_FMT - [1:0] */
+#define WM9081_AIF_FMT_WIDTH                         2  /* AIF_FMT - [1:0] */
+
+/*
+ * R24 (0x18) - Audio Interface 3
+ */
+#define WM9081_BCLK_DIV_MASK                    0x001F  /* BCLK_DIV - [4:0] */
+#define WM9081_BCLK_DIV_SHIFT                        0  /* BCLK_DIV - [4:0] */
+#define WM9081_BCLK_DIV_WIDTH                        5  /* BCLK_DIV - [4:0] */
+
+/*
+ * R25 (0x19) - Audio Interface 4
+ */
+#define WM9081_LRCLK_RATE_MASK                  0x07FF  /* LRCLK_RATE - [10:0] */
+#define WM9081_LRCLK_RATE_SHIFT                      0  /* LRCLK_RATE - [10:0] */
+#define WM9081_LRCLK_RATE_WIDTH                     11  /* LRCLK_RATE - [10:0] */
+
+/*
+ * R26 (0x1A) - Interrupt Status
+ */
+#define WM9081_WSEQ_BUSY_EINT                   0x0004  /* WSEQ_BUSY_EINT */
+#define WM9081_WSEQ_BUSY_EINT_MASK              0x0004  /* WSEQ_BUSY_EINT */
+#define WM9081_WSEQ_BUSY_EINT_SHIFT                  2  /* WSEQ_BUSY_EINT */
+#define WM9081_WSEQ_BUSY_EINT_WIDTH                  1  /* WSEQ_BUSY_EINT */
+#define WM9081_TSHUT_EINT                       0x0001  /* TSHUT_EINT */
+#define WM9081_TSHUT_EINT_MASK                  0x0001  /* TSHUT_EINT */
+#define WM9081_TSHUT_EINT_SHIFT                      0  /* TSHUT_EINT */
+#define WM9081_TSHUT_EINT_WIDTH                      1  /* TSHUT_EINT */
+
+/*
+ * R27 (0x1B) - Interrupt Status Mask
+ */
+#define WM9081_IM_WSEQ_BUSY_EINT                0x0004  /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_WSEQ_BUSY_EINT_MASK           0x0004  /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_WSEQ_BUSY_EINT_SHIFT               2  /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_WSEQ_BUSY_EINT_WIDTH               1  /* IM_WSEQ_BUSY_EINT */
+#define WM9081_IM_TSHUT_EINT                    0x0001  /* IM_TSHUT_EINT */
+#define WM9081_IM_TSHUT_EINT_MASK               0x0001  /* IM_TSHUT_EINT */
+#define WM9081_IM_TSHUT_EINT_SHIFT                   0  /* IM_TSHUT_EINT */
+#define WM9081_IM_TSHUT_EINT_WIDTH                   1  /* IM_TSHUT_EINT */
+
+/*
+ * R28 (0x1C) - Interrupt Polarity
+ */
+#define WM9081_TSHUT_INV                        0x0001  /* TSHUT_INV */
+#define WM9081_TSHUT_INV_MASK                   0x0001  /* TSHUT_INV */
+#define WM9081_TSHUT_INV_SHIFT                       0  /* TSHUT_INV */
+#define WM9081_TSHUT_INV_WIDTH                       1  /* TSHUT_INV */
+
+/*
+ * R29 (0x1D) - Interrupt Control
+ */
+#define WM9081_IRQ_POL                          0x8000  /* IRQ_POL */
+#define WM9081_IRQ_POL_MASK                     0x8000  /* IRQ_POL */
+#define WM9081_IRQ_POL_SHIFT                        15  /* IRQ_POL */
+#define WM9081_IRQ_POL_WIDTH                         1  /* IRQ_POL */
+#define WM9081_IRQ_OP_CTRL                      0x0001  /* IRQ_OP_CTRL */
+#define WM9081_IRQ_OP_CTRL_MASK                 0x0001  /* IRQ_OP_CTRL */
+#define WM9081_IRQ_OP_CTRL_SHIFT                     0  /* IRQ_OP_CTRL */
+#define WM9081_IRQ_OP_CTRL_WIDTH                     1  /* IRQ_OP_CTRL */
+
+/*
+ * R30 (0x1E) - DAC Digital 1
+ */
+#define WM9081_DAC_VOL_MASK                     0x00FF  /* DAC_VOL - [7:0] */
+#define WM9081_DAC_VOL_SHIFT                         0  /* DAC_VOL - [7:0] */
+#define WM9081_DAC_VOL_WIDTH                         8  /* DAC_VOL - [7:0] */
+
+/*
+ * R31 (0x1F) - DAC Digital 2
+ */
+#define WM9081_DAC_MUTERATE                     0x0400  /* DAC_MUTERATE */
+#define WM9081_DAC_MUTERATE_MASK                0x0400  /* DAC_MUTERATE */
+#define WM9081_DAC_MUTERATE_SHIFT                   10  /* DAC_MUTERATE */
+#define WM9081_DAC_MUTERATE_WIDTH                    1  /* DAC_MUTERATE */
+#define WM9081_DAC_MUTEMODE                     0x0200  /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTEMODE_MASK                0x0200  /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTEMODE_SHIFT                    9  /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTEMODE_WIDTH                    1  /* DAC_MUTEMODE */
+#define WM9081_DAC_MUTE                         0x0008  /* DAC_MUTE */
+#define WM9081_DAC_MUTE_MASK                    0x0008  /* DAC_MUTE */
+#define WM9081_DAC_MUTE_SHIFT                        3  /* DAC_MUTE */
+#define WM9081_DAC_MUTE_WIDTH                        1  /* DAC_MUTE */
+#define WM9081_DEEMPH_MASK                      0x0006  /* DEEMPH - [2:1] */
+#define WM9081_DEEMPH_SHIFT                          1  /* DEEMPH - [2:1] */
+#define WM9081_DEEMPH_WIDTH                          2  /* DEEMPH - [2:1] */
+
+/*
+ * R32 (0x20) - DRC 1
+ */
+#define WM9081_DRC_ENA                          0x8000  /* DRC_ENA */
+#define WM9081_DRC_ENA_MASK                     0x8000  /* DRC_ENA */
+#define WM9081_DRC_ENA_SHIFT                        15  /* DRC_ENA */
+#define WM9081_DRC_ENA_WIDTH                         1  /* DRC_ENA */
+#define WM9081_DRC_STARTUP_GAIN_MASK            0x07C0  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM9081_DRC_STARTUP_GAIN_SHIFT                6  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM9081_DRC_STARTUP_GAIN_WIDTH                5  /* DRC_STARTUP_GAIN - [10:6] */
+#define WM9081_DRC_FF_DLY                       0x0020  /* DRC_FF_DLY */
+#define WM9081_DRC_FF_DLY_MASK                  0x0020  /* DRC_FF_DLY */
+#define WM9081_DRC_FF_DLY_SHIFT                      5  /* DRC_FF_DLY */
+#define WM9081_DRC_FF_DLY_WIDTH                      1  /* DRC_FF_DLY */
+#define WM9081_DRC_QR                           0x0004  /* DRC_QR */
+#define WM9081_DRC_QR_MASK                      0x0004  /* DRC_QR */
+#define WM9081_DRC_QR_SHIFT                          2  /* DRC_QR */
+#define WM9081_DRC_QR_WIDTH                          1  /* DRC_QR */
+#define WM9081_DRC_ANTICLIP                     0x0002  /* DRC_ANTICLIP */
+#define WM9081_DRC_ANTICLIP_MASK                0x0002  /* DRC_ANTICLIP */
+#define WM9081_DRC_ANTICLIP_SHIFT                    1  /* DRC_ANTICLIP */
+#define WM9081_DRC_ANTICLIP_WIDTH                    1  /* DRC_ANTICLIP */
+
+/*
+ * R33 (0x21) - DRC 2
+ */
+#define WM9081_DRC_ATK_MASK                     0xF000  /* DRC_ATK - [15:12] */
+#define WM9081_DRC_ATK_SHIFT                        12  /* DRC_ATK - [15:12] */
+#define WM9081_DRC_ATK_WIDTH                         4  /* DRC_ATK - [15:12] */
+#define WM9081_DRC_DCY_MASK                     0x0F00  /* DRC_DCY - [11:8] */
+#define WM9081_DRC_DCY_SHIFT                         8  /* DRC_DCY - [11:8] */
+#define WM9081_DRC_DCY_WIDTH                         4  /* DRC_DCY - [11:8] */
+#define WM9081_DRC_QR_THR_MASK                  0x00C0  /* DRC_QR_THR - [7:6] */
+#define WM9081_DRC_QR_THR_SHIFT                      6  /* DRC_QR_THR - [7:6] */
+#define WM9081_DRC_QR_THR_WIDTH                      2  /* DRC_QR_THR - [7:6] */
+#define WM9081_DRC_QR_DCY_MASK                  0x0030  /* DRC_QR_DCY - [5:4] */
+#define WM9081_DRC_QR_DCY_SHIFT                      4  /* DRC_QR_DCY - [5:4] */
+#define WM9081_DRC_QR_DCY_WIDTH                      2  /* DRC_QR_DCY - [5:4] */
+#define WM9081_DRC_MINGAIN_MASK                 0x000C  /* DRC_MINGAIN - [3:2] */
+#define WM9081_DRC_MINGAIN_SHIFT                     2  /* DRC_MINGAIN - [3:2] */
+#define WM9081_DRC_MINGAIN_WIDTH                     2  /* DRC_MINGAIN - [3:2] */
+#define WM9081_DRC_MAXGAIN_MASK                 0x0003  /* DRC_MAXGAIN - [1:0] */
+#define WM9081_DRC_MAXGAIN_SHIFT                     0  /* DRC_MAXGAIN - [1:0] */
+#define WM9081_DRC_MAXGAIN_WIDTH                     2  /* DRC_MAXGAIN - [1:0] */
+
+/*
+ * R34 (0x22) - DRC 3
+ */
+#define WM9081_DRC_HI_COMP_MASK                 0x0038  /* DRC_HI_COMP - [5:3] */
+#define WM9081_DRC_HI_COMP_SHIFT                     3  /* DRC_HI_COMP - [5:3] */
+#define WM9081_DRC_HI_COMP_WIDTH                     3  /* DRC_HI_COMP - [5:3] */
+#define WM9081_DRC_LO_COMP_MASK                 0x0007  /* DRC_LO_COMP - [2:0] */
+#define WM9081_DRC_LO_COMP_SHIFT                     0  /* DRC_LO_COMP - [2:0] */
+#define WM9081_DRC_LO_COMP_WIDTH                     3  /* DRC_LO_COMP - [2:0] */
+
+/*
+ * R35 (0x23) - DRC 4
+ */
+#define WM9081_DRC_KNEE_IP_MASK                 0x07E0  /* DRC_KNEE_IP - [10:5] */
+#define WM9081_DRC_KNEE_IP_SHIFT                     5  /* DRC_KNEE_IP - [10:5] */
+#define WM9081_DRC_KNEE_IP_WIDTH                     6  /* DRC_KNEE_IP - [10:5] */
+#define WM9081_DRC_KNEE_OP_MASK                 0x001F  /* DRC_KNEE_OP - [4:0] */
+#define WM9081_DRC_KNEE_OP_SHIFT                     0  /* DRC_KNEE_OP - [4:0] */
+#define WM9081_DRC_KNEE_OP_WIDTH                     5  /* DRC_KNEE_OP - [4:0] */
+
+/*
+ * R38 (0x26) - Write Sequencer 1
+ */
+#define WM9081_WSEQ_ENA                         0x8000  /* WSEQ_ENA */
+#define WM9081_WSEQ_ENA_MASK                    0x8000  /* WSEQ_ENA */
+#define WM9081_WSEQ_ENA_SHIFT                       15  /* WSEQ_ENA */
+#define WM9081_WSEQ_ENA_WIDTH                        1  /* WSEQ_ENA */
+#define WM9081_WSEQ_ABORT                       0x0200  /* WSEQ_ABORT */
+#define WM9081_WSEQ_ABORT_MASK                  0x0200  /* WSEQ_ABORT */
+#define WM9081_WSEQ_ABORT_SHIFT                      9  /* WSEQ_ABORT */
+#define WM9081_WSEQ_ABORT_WIDTH                      1  /* WSEQ_ABORT */
+#define WM9081_WSEQ_START                       0x0100  /* WSEQ_START */
+#define WM9081_WSEQ_START_MASK                  0x0100  /* WSEQ_START */
+#define WM9081_WSEQ_START_SHIFT                      8  /* WSEQ_START */
+#define WM9081_WSEQ_START_WIDTH                      1  /* WSEQ_START */
+#define WM9081_WSEQ_START_INDEX_MASK            0x007F  /* WSEQ_START_INDEX - [6:0] */
+#define WM9081_WSEQ_START_INDEX_SHIFT                0  /* WSEQ_START_INDEX - [6:0] */
+#define WM9081_WSEQ_START_INDEX_WIDTH                7  /* WSEQ_START_INDEX - [6:0] */
+
+/*
+ * R39 (0x27) - Write Sequencer 2
+ */
+#define WM9081_WSEQ_CURRENT_INDEX_MASK          0x07F0  /* WSEQ_CURRENT_INDEX - [10:4] */
+#define WM9081_WSEQ_CURRENT_INDEX_SHIFT              4  /* WSEQ_CURRENT_INDEX - [10:4] */
+#define WM9081_WSEQ_CURRENT_INDEX_WIDTH              7  /* WSEQ_CURRENT_INDEX - [10:4] */
+#define WM9081_WSEQ_BUSY                        0x0001  /* WSEQ_BUSY */
+#define WM9081_WSEQ_BUSY_MASK                   0x0001  /* WSEQ_BUSY */
+#define WM9081_WSEQ_BUSY_SHIFT                       0  /* WSEQ_BUSY */
+#define WM9081_WSEQ_BUSY_WIDTH                       1  /* WSEQ_BUSY */
+
+/*
+ * R40 (0x28) - MW Slave 1
+ */
+#define WM9081_SPI_CFG                          0x0020  /* SPI_CFG */
+#define WM9081_SPI_CFG_MASK                     0x0020  /* SPI_CFG */
+#define WM9081_SPI_CFG_SHIFT                         5  /* SPI_CFG */
+#define WM9081_SPI_CFG_WIDTH                         1  /* SPI_CFG */
+#define WM9081_SPI_4WIRE                        0x0010  /* SPI_4WIRE */
+#define WM9081_SPI_4WIRE_MASK                   0x0010  /* SPI_4WIRE */
+#define WM9081_SPI_4WIRE_SHIFT                       4  /* SPI_4WIRE */
+#define WM9081_SPI_4WIRE_WIDTH                       1  /* SPI_4WIRE */
+#define WM9081_ARA_ENA                          0x0008  /* ARA_ENA */
+#define WM9081_ARA_ENA_MASK                     0x0008  /* ARA_ENA */
+#define WM9081_ARA_ENA_SHIFT                         3  /* ARA_ENA */
+#define WM9081_ARA_ENA_WIDTH                         1  /* ARA_ENA */
+#define WM9081_AUTO_INC                         0x0002  /* AUTO_INC */
+#define WM9081_AUTO_INC_MASK                    0x0002  /* AUTO_INC */
+#define WM9081_AUTO_INC_SHIFT                        1  /* AUTO_INC */
+#define WM9081_AUTO_INC_WIDTH                        1  /* AUTO_INC */
+
+/*
+ * R42 (0x2A) - EQ 1
+ */
+#define WM9081_EQ_B1_GAIN_MASK                  0xF800  /* EQ_B1_GAIN - [15:11] */
+#define WM9081_EQ_B1_GAIN_SHIFT                     11  /* EQ_B1_GAIN - [15:11] */
+#define WM9081_EQ_B1_GAIN_WIDTH                      5  /* EQ_B1_GAIN - [15:11] */
+#define WM9081_EQ_B2_GAIN_MASK                  0x07C0  /* EQ_B2_GAIN - [10:6] */
+#define WM9081_EQ_B2_GAIN_SHIFT                      6  /* EQ_B2_GAIN - [10:6] */
+#define WM9081_EQ_B2_GAIN_WIDTH                      5  /* EQ_B2_GAIN - [10:6] */
+#define WM9081_EQ_B4_GAIN_MASK                  0x003E  /* EQ_B4_GAIN - [5:1] */
+#define WM9081_EQ_B4_GAIN_SHIFT                      1  /* EQ_B4_GAIN - [5:1] */
+#define WM9081_EQ_B4_GAIN_WIDTH                      5  /* EQ_B4_GAIN - [5:1] */
+#define WM9081_EQ_ENA                           0x0001  /* EQ_ENA */
+#define WM9081_EQ_ENA_MASK                      0x0001  /* EQ_ENA */
+#define WM9081_EQ_ENA_SHIFT                          0  /* EQ_ENA */
+#define WM9081_EQ_ENA_WIDTH                          1  /* EQ_ENA */
+
+/*
+ * R43 (0x2B) - EQ 2
+ */
+#define WM9081_EQ_B3_GAIN_MASK                  0xF800  /* EQ_B3_GAIN - [15:11] */
+#define WM9081_EQ_B3_GAIN_SHIFT                     11  /* EQ_B3_GAIN - [15:11] */
+#define WM9081_EQ_B3_GAIN_WIDTH                      5  /* EQ_B3_GAIN - [15:11] */
+#define WM9081_EQ_B5_GAIN_MASK                  0x07C0  /* EQ_B5_GAIN - [10:6] */
+#define WM9081_EQ_B5_GAIN_SHIFT                      6  /* EQ_B5_GAIN - [10:6] */
+#define WM9081_EQ_B5_GAIN_WIDTH                      5  /* EQ_B5_GAIN - [10:6] */
+
+/*
+ * R44 (0x2C) - EQ 3
+ */
+#define WM9081_EQ_B1_A_MASK                     0xFFFF  /* EQ_B1_A - [15:0] */
+#define WM9081_EQ_B1_A_SHIFT                         0  /* EQ_B1_A - [15:0] */
+#define WM9081_EQ_B1_A_WIDTH                        16  /* EQ_B1_A - [15:0] */
+
+/*
+ * R45 (0x2D) - EQ 4
+ */
+#define WM9081_EQ_B1_B_MASK                     0xFFFF  /* EQ_B1_B - [15:0] */
+#define WM9081_EQ_B1_B_SHIFT                         0  /* EQ_B1_B - [15:0] */
+#define WM9081_EQ_B1_B_WIDTH                        16  /* EQ_B1_B - [15:0] */
+
+/*
+ * R46 (0x2E) - EQ 5
+ */
+#define WM9081_EQ_B1_PG_MASK                    0xFFFF  /* EQ_B1_PG - [15:0] */
+#define WM9081_EQ_B1_PG_SHIFT                        0  /* EQ_B1_PG - [15:0] */
+#define WM9081_EQ_B1_PG_WIDTH                       16  /* EQ_B1_PG - [15:0] */
+
+/*
+ * R47 (0x2F) - EQ 6
+ */
+#define WM9081_EQ_B2_A_MASK                     0xFFFF  /* EQ_B2_A - [15:0] */
+#define WM9081_EQ_B2_A_SHIFT                         0  /* EQ_B2_A - [15:0] */
+#define WM9081_EQ_B2_A_WIDTH                        16  /* EQ_B2_A - [15:0] */
+
+/*
+ * R48 (0x30) - EQ 7
+ */
+#define WM9081_EQ_B2_B_MASK                     0xFFFF  /* EQ_B2_B - [15:0] */
+#define WM9081_EQ_B2_B_SHIFT                         0  /* EQ_B2_B - [15:0] */
+#define WM9081_EQ_B2_B_WIDTH                        16  /* EQ_B2_B - [15:0] */
+
+/*
+ * R49 (0x31) - EQ 8
+ */
+#define WM9081_EQ_B2_C_MASK                     0xFFFF  /* EQ_B2_C - [15:0] */
+#define WM9081_EQ_B2_C_SHIFT                         0  /* EQ_B2_C - [15:0] */
+#define WM9081_EQ_B2_C_WIDTH                        16  /* EQ_B2_C - [15:0] */
+
+/*
+ * R50 (0x32) - EQ 9
+ */
+#define WM9081_EQ_B2_PG_MASK                    0xFFFF  /* EQ_B2_PG - [15:0] */
+#define WM9081_EQ_B2_PG_SHIFT                        0  /* EQ_B2_PG - [15:0] */
+#define WM9081_EQ_B2_PG_WIDTH                       16  /* EQ_B2_PG - [15:0] */
+
+/*
+ * R51 (0x33) - EQ 10
+ */
+#define WM9081_EQ_B4_A_MASK                     0xFFFF  /* EQ_B4_A - [15:0] */
+#define WM9081_EQ_B4_A_SHIFT                         0  /* EQ_B4_A - [15:0] */
+#define WM9081_EQ_B4_A_WIDTH                        16  /* EQ_B4_A - [15:0] */
+
+/*
+ * R52 (0x34) - EQ 11
+ */
+#define WM9081_EQ_B4_B_MASK                     0xFFFF  /* EQ_B4_B - [15:0] */
+#define WM9081_EQ_B4_B_SHIFT                         0  /* EQ_B4_B - [15:0] */
+#define WM9081_EQ_B4_B_WIDTH                        16  /* EQ_B4_B - [15:0] */
+
+/*
+ * R53 (0x35) - EQ 12
+ */
+#define WM9081_EQ_B4_C_MASK                     0xFFFF  /* EQ_B4_C - [15:0] */
+#define WM9081_EQ_B4_C_SHIFT                         0  /* EQ_B4_C - [15:0] */
+#define WM9081_EQ_B4_C_WIDTH                        16  /* EQ_B4_C - [15:0] */
+
+/*
+ * R54 (0x36) - EQ 13
+ */
+#define WM9081_EQ_B4_PG_MASK                    0xFFFF  /* EQ_B4_PG - [15:0] */
+#define WM9081_EQ_B4_PG_SHIFT                        0  /* EQ_B4_PG - [15:0] */
+#define WM9081_EQ_B4_PG_WIDTH                       16  /* EQ_B4_PG - [15:0] */
+
+/*
+ * R55 (0x37) - EQ 14
+ */
+#define WM9081_EQ_B3_A_MASK                     0xFFFF  /* EQ_B3_A - [15:0] */
+#define WM9081_EQ_B3_A_SHIFT                         0  /* EQ_B3_A - [15:0] */
+#define WM9081_EQ_B3_A_WIDTH                        16  /* EQ_B3_A - [15:0] */
+
+/*
+ * R56 (0x38) - EQ 15
+ */
+#define WM9081_EQ_B3_B_MASK                     0xFFFF  /* EQ_B3_B - [15:0] */
+#define WM9081_EQ_B3_B_SHIFT                         0  /* EQ_B3_B - [15:0] */
+#define WM9081_EQ_B3_B_WIDTH                        16  /* EQ_B3_B - [15:0] */
+
+/*
+ * R57 (0x39) - EQ 16
+ */
+#define WM9081_EQ_B3_C_MASK                     0xFFFF  /* EQ_B3_C - [15:0] */
+#define WM9081_EQ_B3_C_SHIFT                         0  /* EQ_B3_C - [15:0] */
+#define WM9081_EQ_B3_C_WIDTH                        16  /* EQ_B3_C - [15:0] */
+
+/*
+ * R58 (0x3A) - EQ 17
+ */
+#define WM9081_EQ_B3_PG_MASK                    0xFFFF  /* EQ_B3_PG - [15:0] */
+#define WM9081_EQ_B3_PG_SHIFT                        0  /* EQ_B3_PG - [15:0] */
+#define WM9081_EQ_B3_PG_WIDTH                       16  /* EQ_B3_PG - [15:0] */
+
+/*
+ * R59 (0x3B) - EQ 18
+ */
+#define WM9081_EQ_B5_A_MASK                     0xFFFF  /* EQ_B5_A - [15:0] */
+#define WM9081_EQ_B5_A_SHIFT                         0  /* EQ_B5_A - [15:0] */
+#define WM9081_EQ_B5_A_WIDTH                        16  /* EQ_B5_A - [15:0] */
+
+/*
+ * R60 (0x3C) - EQ 19
+ */
+#define WM9081_EQ_B5_B_MASK                     0xFFFF  /* EQ_B5_B - [15:0] */
+#define WM9081_EQ_B5_B_SHIFT                         0  /* EQ_B5_B - [15:0] */
+#define WM9081_EQ_B5_B_WIDTH                        16  /* EQ_B5_B - [15:0] */
+
+/*
+ * R61 (0x3D) - EQ 20
+ */
+#define WM9081_EQ_B5_PG_MASK                    0xFFFF  /* EQ_B5_PG - [15:0] */
+#define WM9081_EQ_B5_PG_SHIFT                        0  /* EQ_B5_PG - [15:0] */
+#define WM9081_EQ_B5_PG_WIDTH                       16  /* EQ_B5_PG - [15:0] */
+
+
+#endif
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index c2d1a7a..fa88b46 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -282,14 +282,14 @@
 			.channels_min = 1,
 			.channels_max = 2,
 			.rates = WM9705_AC97_RATES,
-			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.formats = SND_SOC_STD_AC97_FMTS,
 		},
 		.capture = {
 			.stream_name = "HiFi Capture",
 			.channels_min = 1,
 			.channels_max = 2,
 			.rates = WM9705_AC97_RATES,
-			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.formats = SND_SOC_STD_AC97_FMTS,
 		},
 		.ops = &wm9705_dai_ops,
 	},
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 765cf1e..1fd4e88 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -534,13 +534,13 @@
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = WM9712_AC97_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.formats = SND_SOC_STD_AC97_FMTS,},
 	.capture = {
 		.stream_name = "HiFi Capture",
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = WM9712_AC97_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.formats = SND_SOC_STD_AC97_FMTS,},
 	.ops = &wm9712_dai_ops_hifi,
 },
 {
@@ -550,7 +550,7 @@
 		.channels_min = 1,
 		.channels_max = 1,
 		.rates = WM9712_AC97_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.formats = SND_SOC_STD_AC97_FMTS,},
 	.ops = &wm9712_dai_ops_aux,
 }
 };
@@ -585,6 +585,8 @@
 	}
 
 	soc_ac97_ops.reset(codec->ac97);
+	if (soc_ac97_ops.warm_reset)
+		soc_ac97_ops.warm_reset(codec->ac97);
 	if (ac97_read(codec, 0) != wm9712_reg[0])
 		goto err;
 	return 0;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 523bad0..abed37a 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -189,6 +189,26 @@
 SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
 };
 
+static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
+				 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	u16 status, rate;
+
+	BUG_ON(event != SND_SOC_DAPM_PRE_PMD);
+
+	/* Gracefully shut down the voice interface. */
+	status = ac97_read(codec, AC97_EXTENDED_MID) | 0x1000;
+	rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
+	ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
+	schedule_timeout_interruptible(msecs_to_jiffies(1));
+	ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
+	ac97_write(codec, AC97_EXTENDED_MID, status);
+
+	return 0;
+}
+
+
 /* We have to create a fake left and right HP mixers because
  * the codec only has a single control that is shared by both channels.
  * This makes it impossible to determine the audio path using the current
@@ -400,7 +420,8 @@
 SND_SOC_DAPM_MIXER("HP Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
 SND_SOC_DAPM_MIXER("Line Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
 SND_SOC_DAPM_MIXER("Capture Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-SND_SOC_DAPM_DAC("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1),
+SND_SOC_DAPM_DAC_E("Voice DAC", "Voice Playback", AC97_EXTENDED_MID, 12, 1,
+		   wm9713_voice_shutdown, SND_SOC_DAPM_PRE_PMD),
 SND_SOC_DAPM_DAC("Aux DAC", "Aux Playback", AC97_EXTENDED_MID, 11, 1),
 SND_SOC_DAPM_PGA("Left ADC", AC97_EXTENDED_MID, 5, 1, NULL, 0),
 SND_SOC_DAPM_PGA("Right ADC", AC97_EXTENDED_MID, 4, 1, NULL, 0),
@@ -689,7 +710,7 @@
 	Ndiv = target / source;
 	if ((Ndiv < 5) || (Ndiv > 12))
 		printk(KERN_WARNING
-			"WM9713 PLL N value %d out of recommended range!\n",
+			"WM9713 PLL N value %u out of recommended range!\n",
 			Ndiv);
 
 	pll_div->n = Ndiv;
@@ -936,21 +957,6 @@
 	return 0;
 }
 
-static void wm9713_voiceshutdown(struct snd_pcm_substream *substream,
-				 struct snd_soc_dai *dai)
-{
-	struct snd_soc_codec *codec = dai->codec;
-	u16 status, rate;
-
-	/* Gracefully shut down the voice interface. */
-	status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
-	rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
-	ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
-	schedule_timeout_interruptible(msecs_to_jiffies(1));
-	ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
-	ac97_write(codec, AC97_EXTENDED_MID, status);
-}
-
 static int ac97_hifi_prepare(struct snd_pcm_substream *substream,
 			     struct snd_soc_dai *dai)
 {
@@ -1019,7 +1025,6 @@
 
 static struct snd_soc_dai_ops wm9713_dai_ops_voice = {
 	.hw_params	= wm9713_pcm_hw_params,
-	.shutdown	= wm9713_voiceshutdown,
 	.set_clkdiv	= wm9713_set_dai_clkdiv,
 	.set_pll	= wm9713_set_dai_pll,
 	.set_fmt	= wm9713_set_dai_fmt,
@@ -1035,13 +1040,13 @@
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = WM9713_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.formats = SND_SOC_STD_AC97_FMTS,},
 	.capture = {
 		.stream_name = "HiFi Capture",
 		.channels_min = 1,
 		.channels_max = 2,
 		.rates = WM9713_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.formats = SND_SOC_STD_AC97_FMTS,},
 	.ops = &wm9713_dai_ops_hifi,
 	},
 	{
@@ -1051,7 +1056,7 @@
 		.channels_min = 1,
 		.channels_max = 1,
 		.rates = WM9713_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+		.formats = SND_SOC_STD_AC97_FMTS,},
 	.ops = &wm9713_dai_ops_aux,
 	},
 	{
@@ -1069,6 +1074,7 @@
 		.rates = WM9713_PCM_RATES,
 		.formats = WM9713_PCM_FORMATS,},
 	.ops = &wm9713_dai_ops_voice,
+	.symmetric_rates = 1,
 	},
 };
 EXPORT_SYMBOL_GPL(wm9713_dai);
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
new file mode 100644
index 0000000..e8fc474
--- /dev/null
+++ b/sound/soc/codecs/wm_hubs.c
@@ -0,0 +1,758 @@
+/*
+ * wm_hubs.c  --  WM8993/4 common code
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "wm8993.h"
+#include "wm_hubs.h"
+
+const DECLARE_TLV_DB_SCALE(wm_hubs_spkmix_tlv, -300, 300, 0);
+EXPORT_SYMBOL_GPL(wm_hubs_spkmix_tlv);
+
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(inmix_sw_tlv, 0, 3000, 0);
+static const DECLARE_TLV_DB_SCALE(inmix_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(earpiece_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(outmix_tlv, -2100, 300, 0);
+static const DECLARE_TLV_DB_SCALE(spkmixout_tlv, -1800, 600, 1);
+static const DECLARE_TLV_DB_SCALE(outpga_tlv, -5700, 100, 0);
+static const unsigned int spkboost_tlv[] = {
+	TLV_DB_RANGE_HEAD(7),
+	0, 6, TLV_DB_SCALE_ITEM(0, 150, 0),
+	7, 7, TLV_DB_SCALE_ITEM(1200, 0, 0),
+};
+static const DECLARE_TLV_DB_SCALE(line_tlv, -600, 600, 0);
+
+static const char *speaker_ref_text[] = {
+	"SPKVDD/2",
+	"VMID",
+};
+
+static const struct soc_enum speaker_ref =
+	SOC_ENUM_SINGLE(WM8993_SPEAKER_MIXER, 8, 2, speaker_ref_text);
+
+static const char *speaker_mode_text[] = {
+	"Class D",
+	"Class AB",
+};
+
+static const struct soc_enum speaker_mode =
+	SOC_ENUM_SINGLE(WM8993_SPKMIXR_ATTENUATION, 8, 2, speaker_mode_text);
+
+static void wait_for_dc_servo(struct snd_soc_codec *codec)
+{
+	unsigned int reg;
+	int count = 0;
+
+	dev_dbg(codec->dev, "Waiting for DC servo...\n");
+	do {
+		count++;
+		msleep(1);
+		reg = snd_soc_read(codec, WM8993_DC_SERVO_READBACK_0);
+		dev_dbg(codec->dev, "DC servo status: %x\n", reg);
+	} while ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
+		 != WM8993_DCS_CAL_COMPLETE_MASK && count < 1000);
+
+	if ((reg & WM8993_DCS_CAL_COMPLETE_MASK)
+	    != WM8993_DCS_CAL_COMPLETE_MASK)
+		dev_err(codec->dev, "Timed out waiting for DC Servo\n");
+}
+
+/*
+ * Update the DC servo calibration on gain changes
+ */
+static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int ret;
+
+	ret = snd_soc_put_volsw_2r(kcontrol, ucontrol);
+
+	/* Only need to do this if the outputs are active */
+	if (snd_soc_read(codec, WM8993_POWER_MANAGEMENT_1)
+	    & (WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA))
+		snd_soc_update_bits(codec,
+				    WM8993_DC_SERVO_0,
+				    WM8993_DCS_TRIG_SINGLE_0 |
+				    WM8993_DCS_TRIG_SINGLE_1,
+				    WM8993_DCS_TRIG_SINGLE_0 |
+				    WM8993_DCS_TRIG_SINGLE_1);
+
+	return ret;
+}
+
+static const struct snd_kcontrol_new analogue_snd_controls[] = {
+SOC_SINGLE_TLV("IN1L Volume", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 0, 31, 0,
+	       inpga_tlv),
+SOC_SINGLE("IN1L Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1L ZC Switch", WM8993_LEFT_LINE_INPUT_1_2_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("IN1R Volume", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 0, 31, 0,
+	       inpga_tlv),
+SOC_SINGLE("IN1R Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN1R ZC Switch", WM8993_RIGHT_LINE_INPUT_1_2_VOLUME, 7, 1, 0),
+
+
+SOC_SINGLE_TLV("IN2L Volume", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 0, 31, 0,
+	       inpga_tlv),
+SOC_SINGLE("IN2L Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2L ZC Switch", WM8993_LEFT_LINE_INPUT_3_4_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("IN2R Volume", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 0, 31, 0,
+	       inpga_tlv),
+SOC_SINGLE("IN2R Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 7, 1, 1),
+SOC_SINGLE("IN2R ZC Switch", WM8993_RIGHT_LINE_INPUT_3_4_VOLUME, 7, 1, 0),
+
+SOC_SINGLE_TLV("MIXINL IN2L Volume", WM8993_INPUT_MIXER3, 7, 1, 0,
+	       inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINL IN1L Volume", WM8993_INPUT_MIXER3, 4, 1, 0,
+	       inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINL Output Record Volume", WM8993_INPUT_MIXER3, 0, 7, 0,
+	       inmix_tlv),
+SOC_SINGLE_TLV("MIXINL IN1LP Volume", WM8993_INPUT_MIXER5, 6, 7, 0, inmix_tlv),
+SOC_SINGLE_TLV("MIXINL Direct Voice Volume", WM8993_INPUT_MIXER5, 0, 6, 0,
+	       inmix_tlv),
+
+SOC_SINGLE_TLV("MIXINR IN2R Volume", WM8993_INPUT_MIXER4, 7, 1, 0,
+	       inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINR IN1R Volume", WM8993_INPUT_MIXER4, 4, 1, 0,
+	       inmix_sw_tlv),
+SOC_SINGLE_TLV("MIXINR Output Record Volume", WM8993_INPUT_MIXER4, 0, 7, 0,
+	       inmix_tlv),
+SOC_SINGLE_TLV("MIXINR IN1RP Volume", WM8993_INPUT_MIXER6, 6, 7, 0, inmix_tlv),
+SOC_SINGLE_TLV("MIXINR Direct Voice Volume", WM8993_INPUT_MIXER6, 0, 6, 0,
+	       inmix_tlv),
+
+SOC_SINGLE_TLV("Left Output Mixer IN2RN Volume", WM8993_OUTPUT_MIXER5, 6, 7, 1,
+	       outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN2LN Volume", WM8993_OUTPUT_MIXER3, 6, 7, 1,
+	       outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN2LP Volume", WM8993_OUTPUT_MIXER3, 9, 7, 1,
+	       outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN1L Volume", WM8993_OUTPUT_MIXER3, 0, 7, 1,
+	       outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer IN1R Volume", WM8993_OUTPUT_MIXER3, 3, 7, 1,
+	       outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer Right Input Volume",
+	       WM8993_OUTPUT_MIXER5, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer Left Input Volume",
+	       WM8993_OUTPUT_MIXER5, 0, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Left Output Mixer DAC Volume", WM8993_OUTPUT_MIXER5, 9, 7, 1,
+	       outmix_tlv),
+
+SOC_SINGLE_TLV("Right Output Mixer IN2LN Volume",
+	       WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN2RN Volume",
+	       WM8993_OUTPUT_MIXER4, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN1L Volume",
+	       WM8993_OUTPUT_MIXER4, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN1R Volume",
+	       WM8993_OUTPUT_MIXER4, 0, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer IN2RP Volume",
+	       WM8993_OUTPUT_MIXER4, 9, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer Left Input Volume",
+	       WM8993_OUTPUT_MIXER6, 3, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer Right Input Volume",
+	       WM8993_OUTPUT_MIXER6, 6, 7, 1, outmix_tlv),
+SOC_SINGLE_TLV("Right Output Mixer DAC Volume",
+	       WM8993_OUTPUT_MIXER6, 9, 7, 1, outmix_tlv),
+
+SOC_DOUBLE_R_TLV("Output Volume", WM8993_LEFT_OPGA_VOLUME,
+		 WM8993_RIGHT_OPGA_VOLUME, 0, 63, 0, outpga_tlv),
+SOC_DOUBLE_R("Output Switch", WM8993_LEFT_OPGA_VOLUME,
+	     WM8993_RIGHT_OPGA_VOLUME, 6, 1, 0),
+SOC_DOUBLE_R("Output ZC Switch", WM8993_LEFT_OPGA_VOLUME,
+	     WM8993_RIGHT_OPGA_VOLUME, 7, 1, 0),
+
+SOC_SINGLE("Earpiece Switch", WM8993_HPOUT2_VOLUME, 5, 1, 1),
+SOC_SINGLE_TLV("Earpiece Volume", WM8993_HPOUT2_VOLUME, 4, 1, 1, earpiece_tlv),
+
+SOC_SINGLE_TLV("SPKL Input Volume", WM8993_SPKMIXL_ATTENUATION,
+	       5, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKL IN1LP Volume", WM8993_SPKMIXL_ATTENUATION,
+	       4, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKL Output Volume", WM8993_SPKMIXL_ATTENUATION,
+	       3, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_SINGLE_TLV("SPKR Input Volume", WM8993_SPKMIXR_ATTENUATION,
+	       5, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKR IN1RP Volume", WM8993_SPKMIXR_ATTENUATION,
+	       4, 1, 1, wm_hubs_spkmix_tlv),
+SOC_SINGLE_TLV("SPKR Output Volume", WM8993_SPKMIXR_ATTENUATION,
+	       3, 1, 1, wm_hubs_spkmix_tlv),
+
+SOC_DOUBLE_R_TLV("Speaker Mixer Volume",
+		 WM8993_SPKMIXL_ATTENUATION, WM8993_SPKMIXR_ATTENUATION,
+		 0, 3, 1, spkmixout_tlv),
+SOC_DOUBLE_R_TLV("Speaker Volume",
+		 WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+		 0, 63, 0, outpga_tlv),
+SOC_DOUBLE_R("Speaker Switch",
+	     WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+	     6, 1, 0),
+SOC_DOUBLE_R("Speaker ZC Switch",
+	     WM8993_SPEAKER_VOLUME_LEFT, WM8993_SPEAKER_VOLUME_RIGHT,
+	     7, 1, 0),
+SOC_DOUBLE_TLV("Speaker Boost Volume", WM8993_SPKOUT_BOOST, 0, 3, 7, 0,
+	       spkboost_tlv),
+SOC_ENUM("Speaker Reference", speaker_ref),
+SOC_ENUM("Speaker Mode", speaker_mode),
+
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Volume",
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+		 SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.tlv.p = outpga_tlv,
+	.info = snd_soc_info_volsw_2r,
+	.get = snd_soc_get_volsw_2r, .put = wm8993_put_dc_servo,
+	.private_value = (unsigned long)&(struct soc_mixer_control) {
+		.reg = WM8993_LEFT_OUTPUT_VOLUME,
+		.rreg = WM8993_RIGHT_OUTPUT_VOLUME,
+		.shift = 0, .max = 63
+	},
+},
+SOC_DOUBLE_R("Headphone Switch", WM8993_LEFT_OUTPUT_VOLUME,
+	     WM8993_RIGHT_OUTPUT_VOLUME, 6, 1, 0),
+SOC_DOUBLE_R("Headphone ZC Switch", WM8993_LEFT_OUTPUT_VOLUME,
+	     WM8993_RIGHT_OUTPUT_VOLUME, 7, 1, 0),
+
+SOC_SINGLE("LINEOUT1N Switch", WM8993_LINE_OUTPUTS_VOLUME, 6, 1, 1),
+SOC_SINGLE("LINEOUT1P Switch", WM8993_LINE_OUTPUTS_VOLUME, 5, 1, 1),
+SOC_SINGLE_TLV("LINEOUT1 Volume", WM8993_LINE_OUTPUTS_VOLUME, 4, 1, 1,
+	       line_tlv),
+
+SOC_SINGLE("LINEOUT2N Switch", WM8993_LINE_OUTPUTS_VOLUME, 2, 1, 1),
+SOC_SINGLE("LINEOUT2P Switch", WM8993_LINE_OUTPUTS_VOLUME, 1, 1, 1),
+SOC_SINGLE_TLV("LINEOUT2 Volume", WM8993_LINE_OUTPUTS_VOLUME, 0, 1, 1,
+	       line_tlv),
+};
+
+static int hp_event(struct snd_soc_dapm_widget *w,
+		    struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	unsigned int reg = snd_soc_read(codec, WM8993_ANALOGUE_HP_0);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+				    WM8993_CP_ENA, WM8993_CP_ENA);
+
+		msleep(5);
+
+		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+				    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
+				    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA);
+
+		reg |= WM8993_HPOUT1L_DLY | WM8993_HPOUT1R_DLY;
+		snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
+
+		/* Start the DC servo */
+		snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
+				    WM8993_DCS_ENA_CHAN_0 |
+				    WM8993_DCS_ENA_CHAN_1 |
+				    WM8993_DCS_TRIG_STARTUP_1 |
+				    WM8993_DCS_TRIG_STARTUP_0,
+				    WM8993_DCS_ENA_CHAN_0 |
+				    WM8993_DCS_ENA_CHAN_1 |
+				    WM8993_DCS_TRIG_STARTUP_1 |
+				    WM8993_DCS_TRIG_STARTUP_0);
+		wait_for_dc_servo(codec);
+		snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+				    WM8993_DCS_TIMER_PERIOD_01_MASK, 0xa);
+
+		reg |= WM8993_HPOUT1R_OUTP | WM8993_HPOUT1R_RMV_SHORT |
+			WM8993_HPOUT1L_OUTP | WM8993_HPOUT1L_RMV_SHORT;
+		snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		reg &= ~(WM8993_HPOUT1L_RMV_SHORT |
+			 WM8993_HPOUT1L_DLY |
+			 WM8993_HPOUT1L_OUTP |
+			 WM8993_HPOUT1R_RMV_SHORT |
+			 WM8993_HPOUT1R_DLY |
+			 WM8993_HPOUT1R_OUTP);
+
+		snd_soc_update_bits(codec, WM8993_DC_SERVO_1,
+				    WM8993_DCS_TIMER_PERIOD_01_MASK, 0);
+		snd_soc_update_bits(codec, WM8993_DC_SERVO_0,
+				    WM8993_DCS_ENA_CHAN_0 |
+				    WM8993_DCS_ENA_CHAN_1, 0);
+
+		snd_soc_write(codec, WM8993_ANALOGUE_HP_0, reg);
+		snd_soc_update_bits(codec, WM8993_POWER_MANAGEMENT_1,
+				    WM8993_HPOUT1L_ENA | WM8993_HPOUT1R_ENA,
+				    0);
+
+		snd_soc_update_bits(codec, WM8993_CHARGE_PUMP_1,
+				    WM8993_CP_ENA, 0);
+		break;
+	}
+
+	return 0;
+}
+
+static int earpiece_event(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *control, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	u16 reg = snd_soc_read(codec, WM8993_ANTIPOP1) & ~WM8993_HPOUT2_IN_ENA;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		reg |= WM8993_HPOUT2_IN_ENA;
+		snd_soc_write(codec, WM8993_ANTIPOP1, reg);
+		udelay(50);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_write(codec, WM8993_ANTIPOP1, reg);
+		break;
+
+	default:
+		BUG();
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new in1l_pga[] = {
+SOC_DAPM_SINGLE("IN1LP Switch", WM8993_INPUT_MIXER2, 5, 1, 0),
+SOC_DAPM_SINGLE("IN1LN Switch", WM8993_INPUT_MIXER2, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new in1r_pga[] = {
+SOC_DAPM_SINGLE("IN1RP Switch", WM8993_INPUT_MIXER2, 1, 1, 0),
+SOC_DAPM_SINGLE("IN1RN Switch", WM8993_INPUT_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new in2l_pga[] = {
+SOC_DAPM_SINGLE("IN2LP Switch", WM8993_INPUT_MIXER2, 7, 1, 0),
+SOC_DAPM_SINGLE("IN2LN Switch", WM8993_INPUT_MIXER2, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new in2r_pga[] = {
+SOC_DAPM_SINGLE("IN2RP Switch", WM8993_INPUT_MIXER2, 3, 1, 0),
+SOC_DAPM_SINGLE("IN2RN Switch", WM8993_INPUT_MIXER2, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixinl[] = {
+SOC_DAPM_SINGLE("IN2L Switch", WM8993_INPUT_MIXER3, 8, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_INPUT_MIXER3, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new mixinr[] = {
+SOC_DAPM_SINGLE("IN2R Switch", WM8993_INPUT_MIXER4, 8, 1, 0),
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_INPUT_MIXER4, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_output_mixer[] = {
+SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER1, 7, 1, 0),
+SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER1, 6, 1, 0),
+SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER1, 5, 1, 0),
+SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER1, 4, 1, 0),
+SOC_DAPM_SINGLE("IN2LP Switch", WM8993_OUTPUT_MIXER1, 1, 1, 0),
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER1, 3, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER1, 2, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_output_mixer[] = {
+SOC_DAPM_SINGLE("Left Input Switch", WM8993_OUTPUT_MIXER2, 7, 1, 0),
+SOC_DAPM_SINGLE("Right Input Switch", WM8993_OUTPUT_MIXER2, 6, 1, 0),
+SOC_DAPM_SINGLE("IN2LN Switch", WM8993_OUTPUT_MIXER2, 5, 1, 0),
+SOC_DAPM_SINGLE("IN2RN Switch", WM8993_OUTPUT_MIXER2, 4, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_OUTPUT_MIXER2, 3, 1, 0),
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_OUTPUT_MIXER2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2RP Switch", WM8993_OUTPUT_MIXER2, 1, 1, 0),
+SOC_DAPM_SINGLE("DAC Switch", WM8993_OUTPUT_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new earpiece_mixer[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_HPOUT2_MIXER, 5, 1, 0),
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_HPOUT2_MIXER, 4, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_HPOUT2_MIXER, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_speaker_boost[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 5, 1, 0),
+SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 4, 1, 0),
+SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_speaker_boost[] = {
+SOC_DAPM_SINGLE("Direct Voice Switch", WM8993_SPKOUT_MIXERS, 2, 1, 0),
+SOC_DAPM_SINGLE("SPKL Switch", WM8993_SPKOUT_MIXERS, 1, 1, 0),
+SOC_DAPM_SINGLE("SPKR Switch", WM8993_SPKOUT_MIXERS, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line1_mix[] = {
+SOC_DAPM_SINGLE("IN1R Switch", WM8993_LINE_MIXER1, 2, 1, 0),
+SOC_DAPM_SINGLE("IN1L Switch", WM8993_LINE_MIXER1, 1, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line1n_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 6, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER1, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new line1p_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER1, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2_mix[] = {
+SOC_DAPM_SINGLE("IN2R Switch", WM8993_LINE_MIXER2, 2, 1, 0),
+SOC_DAPM_SINGLE("IN2L Switch", WM8993_LINE_MIXER2, 1, 1, 0),
+SOC_DAPM_SINGLE("Output Switch", WM8993_LINE_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2n_mix[] = {
+SOC_DAPM_SINGLE("Left Output Switch", WM8993_LINE_MIXER2, 6, 1, 0),
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new line2p_mix[] = {
+SOC_DAPM_SINGLE("Right Output Switch", WM8993_LINE_MIXER2, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget analogue_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN1LN"),
+SND_SOC_DAPM_INPUT("IN1LP"),
+SND_SOC_DAPM_INPUT("IN2LN"),
+SND_SOC_DAPM_INPUT("IN2LP/VXRN"),
+SND_SOC_DAPM_INPUT("IN1RN"),
+SND_SOC_DAPM_INPUT("IN1RP"),
+SND_SOC_DAPM_INPUT("IN2RN"),
+SND_SOC_DAPM_INPUT("IN2RP/VXRP"),
+
+SND_SOC_DAPM_MICBIAS("MICBIAS2", WM8993_POWER_MANAGEMENT_1, 5, 0),
+SND_SOC_DAPM_MICBIAS("MICBIAS1", WM8993_POWER_MANAGEMENT_1, 4, 0),
+
+SND_SOC_DAPM_MIXER("IN1L PGA", WM8993_POWER_MANAGEMENT_2, 6, 0,
+		   in1l_pga, ARRAY_SIZE(in1l_pga)),
+SND_SOC_DAPM_MIXER("IN1R PGA", WM8993_POWER_MANAGEMENT_2, 4, 0,
+		   in1r_pga, ARRAY_SIZE(in1r_pga)),
+
+SND_SOC_DAPM_MIXER("IN2L PGA", WM8993_POWER_MANAGEMENT_2, 7, 0,
+		   in2l_pga, ARRAY_SIZE(in2l_pga)),
+SND_SOC_DAPM_MIXER("IN2R PGA", WM8993_POWER_MANAGEMENT_2, 5, 0,
+		   in2r_pga, ARRAY_SIZE(in2r_pga)),
+
+/* Dummy widgets to represent differential paths */
+SND_SOC_DAPM_PGA("Direct Voice", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("MIXINL", WM8993_POWER_MANAGEMENT_2, 9, 0,
+		   mixinl, ARRAY_SIZE(mixinl)),
+SND_SOC_DAPM_MIXER("MIXINR", WM8993_POWER_MANAGEMENT_2, 8, 0,
+		   mixinr, ARRAY_SIZE(mixinr)),
+
+SND_SOC_DAPM_ADC("ADCL", "Capture", WM8993_POWER_MANAGEMENT_2, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", "Capture", WM8993_POWER_MANAGEMENT_2, 0, 0),
+
+SND_SOC_DAPM_DAC("DACL", "Playback", WM8993_POWER_MANAGEMENT_3, 1, 0),
+SND_SOC_DAPM_DAC("DACR", "Playback", WM8993_POWER_MANAGEMENT_3, 0, 0),
+
+SND_SOC_DAPM_MIXER("Left Output Mixer", WM8993_POWER_MANAGEMENT_3, 5, 0,
+		   left_output_mixer, ARRAY_SIZE(left_output_mixer)),
+SND_SOC_DAPM_MIXER("Right Output Mixer", WM8993_POWER_MANAGEMENT_3, 4, 0,
+		   right_output_mixer, ARRAY_SIZE(right_output_mixer)),
+
+SND_SOC_DAPM_PGA("Left Output PGA", WM8993_POWER_MANAGEMENT_3, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Right Output PGA", WM8993_POWER_MANAGEMENT_3, 6, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("Headphone PGA", SND_SOC_NOPM, 0, 0,
+		   NULL, 0,
+		   hp_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_MIXER("Earpiece Mixer", SND_SOC_NOPM, 0, 0,
+		   earpiece_mixer, ARRAY_SIZE(earpiece_mixer)),
+SND_SOC_DAPM_PGA_E("Earpiece Driver", WM8993_POWER_MANAGEMENT_1, 11, 0,
+		   NULL, 0, earpiece_event,
+		   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_MIXER("SPKL Boost", SND_SOC_NOPM, 0, 0,
+		   left_speaker_boost, ARRAY_SIZE(left_speaker_boost)),
+SND_SOC_DAPM_MIXER("SPKR Boost", SND_SOC_NOPM, 0, 0,
+		   right_speaker_boost, ARRAY_SIZE(right_speaker_boost)),
+
+SND_SOC_DAPM_PGA("SPKL Driver", WM8993_POWER_MANAGEMENT_1, 12, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("SPKR Driver", WM8993_POWER_MANAGEMENT_1, 13, 0,
+		 NULL, 0),
+
+SND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0,
+		   line1_mix, ARRAY_SIZE(line1_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0,
+		   line2_mix, ARRAY_SIZE(line2_mix)),
+
+SND_SOC_DAPM_MIXER("LINEOUT1N Mixer", SND_SOC_NOPM, 0, 0,
+		   line1n_mix, ARRAY_SIZE(line1n_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT1P Mixer", SND_SOC_NOPM, 0, 0,
+		   line1p_mix, ARRAY_SIZE(line1p_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2N Mixer", SND_SOC_NOPM, 0, 0,
+		   line2n_mix, ARRAY_SIZE(line2n_mix)),
+SND_SOC_DAPM_MIXER("LINEOUT2P Mixer", SND_SOC_NOPM, 0, 0,
+		   line2p_mix, ARRAY_SIZE(line2p_mix)),
+
+SND_SOC_DAPM_PGA("LINEOUT1N Driver", WM8993_POWER_MANAGEMENT_3, 13, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT1P Driver", WM8993_POWER_MANAGEMENT_3, 12, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT2N Driver", WM8993_POWER_MANAGEMENT_3, 11, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("LINEOUT2P Driver", WM8993_POWER_MANAGEMENT_3, 10, 0,
+		 NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2P"),
+SND_SOC_DAPM_OUTPUT("HPOUT2N"),
+SND_SOC_DAPM_OUTPUT("LINEOUT1P"),
+SND_SOC_DAPM_OUTPUT("LINEOUT1N"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2P"),
+SND_SOC_DAPM_OUTPUT("LINEOUT2N"),
+};
+
+static const struct snd_soc_dapm_route analogue_routes[] = {
+	{ "IN1L PGA", "IN1LP Switch", "IN1LP" },
+	{ "IN1L PGA", "IN1LN Switch", "IN1LN" },
+
+	{ "IN1R PGA", "IN1RP Switch", "IN1RP" },
+	{ "IN1R PGA", "IN1RN Switch", "IN1RN" },
+
+	{ "IN2L PGA", "IN2LP Switch", "IN2LP/VXRN" },
+	{ "IN2L PGA", "IN2LN Switch", "IN2LN" },
+
+	{ "IN2R PGA", "IN2RP Switch", "IN2RP/VXRP" },
+	{ "IN2R PGA", "IN2RN Switch", "IN2RN" },
+
+	{ "Direct Voice", NULL, "IN2LP/VXRN" },
+	{ "Direct Voice", NULL, "IN2RP/VXRP" },
+
+	{ "MIXINL", "IN1L Switch", "IN1L PGA" },
+	{ "MIXINL", "IN2L Switch", "IN2L PGA" },
+	{ "MIXINL", NULL, "Direct Voice" },
+	{ "MIXINL", NULL, "IN1LP" },
+	{ "MIXINL", NULL, "Left Output Mixer" },
+
+	{ "MIXINR", "IN1R Switch", "IN1R PGA" },
+	{ "MIXINR", "IN2R Switch", "IN2R PGA" },
+	{ "MIXINR", NULL, "Direct Voice" },
+	{ "MIXINR", NULL, "IN1RP" },
+	{ "MIXINR", NULL, "Right Output Mixer" },
+
+	{ "ADCL", NULL, "MIXINL" },
+	{ "ADCR", NULL, "MIXINR" },
+
+	{ "Left Output Mixer", "Left Input Switch", "MIXINL" },
+	{ "Left Output Mixer", "Right Input Switch", "MIXINR" },
+	{ "Left Output Mixer", "IN2RN Switch", "IN2RN" },
+	{ "Left Output Mixer", "IN2LN Switch", "IN2LN" },
+	{ "Left Output Mixer", "IN2LP Switch", "IN2LP/VXRN" },
+	{ "Left Output Mixer", "IN1L Switch", "IN1L PGA" },
+	{ "Left Output Mixer", "IN1R Switch", "IN1R PGA" },
+
+	{ "Right Output Mixer", "Left Input Switch", "MIXINL" },
+	{ "Right Output Mixer", "Right Input Switch", "MIXINR" },
+	{ "Right Output Mixer", "IN2LN Switch", "IN2LN" },
+	{ "Right Output Mixer", "IN2RN Switch", "IN2RN" },
+	{ "Right Output Mixer", "IN2RP Switch", "IN2RP/VXRP" },
+	{ "Right Output Mixer", "IN1L Switch", "IN1L PGA" },
+	{ "Right Output Mixer", "IN1R Switch", "IN1R PGA" },
+
+	{ "Left Output PGA", NULL, "Left Output Mixer" },
+	{ "Left Output PGA", NULL, "TOCLK" },
+
+	{ "Right Output PGA", NULL, "Right Output Mixer" },
+	{ "Right Output PGA", NULL, "TOCLK" },
+
+	{ "Earpiece Mixer", "Direct Voice Switch", "Direct Voice" },
+	{ "Earpiece Mixer", "Left Output Switch", "Left Output PGA" },
+	{ "Earpiece Mixer", "Right Output Switch", "Right Output PGA" },
+
+	{ "Earpiece Driver", NULL, "Earpiece Mixer" },
+	{ "HPOUT2N", NULL, "Earpiece Driver" },
+	{ "HPOUT2P", NULL, "Earpiece Driver" },
+
+	{ "SPKL", "Input Switch", "MIXINL" },
+	{ "SPKL", "IN1LP Switch", "IN1LP" },
+	{ "SPKL", "Output Switch", "Left Output Mixer" },
+	{ "SPKL", NULL, "TOCLK" },
+
+	{ "SPKR", "Input Switch", "MIXINR" },
+	{ "SPKR", "IN1RP Switch", "IN1RP" },
+	{ "SPKR", "Output Switch", "Right Output Mixer" },
+	{ "SPKR", NULL, "TOCLK" },
+
+	{ "SPKL Boost", "Direct Voice Switch", "Direct Voice" },
+	{ "SPKL Boost", "SPKL Switch", "SPKL" },
+	{ "SPKL Boost", "SPKR Switch", "SPKR" },
+
+	{ "SPKR Boost", "Direct Voice Switch", "Direct Voice" },
+	{ "SPKR Boost", "SPKR Switch", "SPKR" },
+	{ "SPKR Boost", "SPKL Switch", "SPKL" },
+
+	{ "SPKL Driver", NULL, "SPKL Boost" },
+	{ "SPKL Driver", NULL, "CLK_SYS" },
+
+	{ "SPKR Driver", NULL, "SPKR Boost" },
+	{ "SPKR Driver", NULL, "CLK_SYS" },
+
+	{ "SPKOUTLP", NULL, "SPKL Driver" },
+	{ "SPKOUTLN", NULL, "SPKL Driver" },
+	{ "SPKOUTRP", NULL, "SPKR Driver" },
+	{ "SPKOUTRN", NULL, "SPKR Driver" },
+
+	{ "Left Headphone Mux", "Mixer", "Left Output Mixer" },
+	{ "Right Headphone Mux", "Mixer", "Right Output Mixer" },
+
+	{ "Headphone PGA", NULL, "Left Headphone Mux" },
+	{ "Headphone PGA", NULL, "Right Headphone Mux" },
+	{ "Headphone PGA", NULL, "CLK_SYS" },
+
+	{ "HPOUT1L", NULL, "Headphone PGA" },
+	{ "HPOUT1R", NULL, "Headphone PGA" },
+
+	{ "LINEOUT1N", NULL, "LINEOUT1N Driver" },
+	{ "LINEOUT1P", NULL, "LINEOUT1P Driver" },
+	{ "LINEOUT2N", NULL, "LINEOUT2N Driver" },
+	{ "LINEOUT2P", NULL, "LINEOUT2P Driver" },
+};
+
+static const struct snd_soc_dapm_route lineout1_diff_routes[] = {
+	{ "LINEOUT1 Mixer", "IN1L Switch", "IN1L PGA" },
+	{ "LINEOUT1 Mixer", "IN1R Switch", "IN1R PGA" },
+	{ "LINEOUT1 Mixer", "Output Switch", "Left Output Mixer" },
+
+	{ "LINEOUT1N Driver", NULL, "LINEOUT1 Mixer" },
+	{ "LINEOUT1P Driver", NULL, "LINEOUT1 Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout1_se_routes[] = {
+	{ "LINEOUT1N Mixer", "Left Output Switch", "Left Output Mixer" },
+	{ "LINEOUT1N Mixer", "Right Output Switch", "Left Output Mixer" },
+
+	{ "LINEOUT1P Mixer", "Left Output Switch", "Left Output Mixer" },
+
+	{ "LINEOUT1N Driver", NULL, "LINEOUT1N Mixer" },
+	{ "LINEOUT1P Driver", NULL, "LINEOUT1P Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout2_diff_routes[] = {
+	{ "LINEOUT2 Mixer", "IN2L Switch", "IN2L PGA" },
+	{ "LINEOUT2 Mixer", "IN2R Switch", "IN2R PGA" },
+	{ "LINEOUT2 Mixer", "Output Switch", "Right Output Mixer" },
+
+	{ "LINEOUT2N Driver", NULL, "LINEOUT2 Mixer" },
+	{ "LINEOUT2P Driver", NULL, "LINEOUT2 Mixer" },
+};
+
+static const struct snd_soc_dapm_route lineout2_se_routes[] = {
+	{ "LINEOUT2N Mixer", "Left Output Switch", "Left Output Mixer" },
+	{ "LINEOUT2N Mixer", "Right Output Switch", "Left Output Mixer" },
+
+	{ "LINEOUT2P Mixer", "Right Output Switch", "Right Output Mixer" },
+
+	{ "LINEOUT2N Driver", NULL, "LINEOUT2N Mixer" },
+	{ "LINEOUT2P Driver", NULL, "LINEOUT2P Mixer" },
+};
+
+int wm_hubs_add_analogue_controls(struct snd_soc_codec *codec)
+{
+	/* Latch volume update bits & default ZC on */
+	snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_1_2_VOLUME,
+			    WM8993_IN1_VU, WM8993_IN1_VU);
+	snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_1_2_VOLUME,
+			    WM8993_IN1_VU, WM8993_IN1_VU);
+	snd_soc_update_bits(codec, WM8993_LEFT_LINE_INPUT_3_4_VOLUME,
+			    WM8993_IN2_VU, WM8993_IN2_VU);
+	snd_soc_update_bits(codec, WM8993_RIGHT_LINE_INPUT_3_4_VOLUME,
+			    WM8993_IN2_VU, WM8993_IN2_VU);
+
+	snd_soc_update_bits(codec, WM8993_SPEAKER_VOLUME_RIGHT,
+			    WM8993_SPKOUT_VU, WM8993_SPKOUT_VU);
+
+	snd_soc_update_bits(codec, WM8993_LEFT_OUTPUT_VOLUME,
+			    WM8993_HPOUT1L_ZC, WM8993_HPOUT1L_ZC);
+	snd_soc_update_bits(codec, WM8993_RIGHT_OUTPUT_VOLUME,
+			    WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC,
+			    WM8993_HPOUT1_VU | WM8993_HPOUT1R_ZC);
+
+	snd_soc_update_bits(codec, WM8993_LEFT_OPGA_VOLUME,
+			    WM8993_MIXOUTL_ZC, WM8993_MIXOUTL_ZC);
+	snd_soc_update_bits(codec, WM8993_RIGHT_OPGA_VOLUME,
+			    WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU,
+			    WM8993_MIXOUTR_ZC | WM8993_MIXOUT_VU);
+
+	snd_soc_add_controls(codec, analogue_snd_controls,
+			     ARRAY_SIZE(analogue_snd_controls));
+
+	snd_soc_dapm_new_controls(codec, analogue_dapm_widgets,
+				  ARRAY_SIZE(analogue_dapm_widgets));
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_controls);
+
+int wm_hubs_add_analogue_routes(struct snd_soc_codec *codec,
+				int lineout1_diff, int lineout2_diff)
+{
+	snd_soc_dapm_add_routes(codec, analogue_routes,
+				ARRAY_SIZE(analogue_routes));
+
+	if (lineout1_diff)
+		snd_soc_dapm_add_routes(codec,
+					lineout1_diff_routes,
+					ARRAY_SIZE(lineout1_diff_routes));
+	else
+		snd_soc_dapm_add_routes(codec,
+					lineout1_se_routes,
+					ARRAY_SIZE(lineout1_se_routes));
+
+	if (lineout2_diff)
+		snd_soc_dapm_add_routes(codec,
+					lineout2_diff_routes,
+					ARRAY_SIZE(lineout2_diff_routes));
+	else
+		snd_soc_dapm_add_routes(codec,
+					lineout2_se_routes,
+					ARRAY_SIZE(lineout2_se_routes));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(wm_hubs_add_analogue_routes);
+
+MODULE_DESCRIPTION("Shared support for Wolfson hubs products");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm_hubs.h b/sound/soc/codecs/wm_hubs.h
new file mode 100644
index 0000000..ec09cb6
--- /dev/null
+++ b/sound/soc/codecs/wm_hubs.h
@@ -0,0 +1,24 @@
+/*
+ * wm_hubs.h  --  WM899x common code
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM_HUBS_H
+#define _WM_HUBS_H
+
+struct snd_soc_codec;
+
+extern const unsigned int wm_hubs_spkmix_tlv[];
+
+extern int wm_hubs_add_analogue_controls(struct snd_soc_codec *);
+extern int wm_hubs_add_analogue_routes(struct snd_soc_codec *, int, int);
+
+#endif
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 411a710..4dfd4ad 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -9,6 +9,9 @@
 config SND_DAVINCI_SOC_I2S
 	tristate
 
+config SND_DAVINCI_SOC_MCASP
+	tristate
+
 config SND_DAVINCI_SOC_EVM
 	tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM"
 	depends on SND_DAVINCI_SOC
@@ -19,6 +22,16 @@
 	  Say Y if you want to add support for SoC audio on TI
 	  DaVinci DM6446 or DM355 EVM platforms.
 
+config  SND_DM6467_SOC_EVM
+	tristate "SoC Audio support for DaVinci DM6467 EVM"
+	depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM
+	select SND_DAVINCI_SOC_MCASP
+	select SND_SOC_TLV320AIC3X
+	select SND_SOC_SPDIF
+
+	help
+	  Say Y if you want to add support for SoC audio on TI
+
 config SND_DAVINCI_SOC_SFFSDR
 	tristate "SoC Audio support for SFFSDR"
 	depends on SND_DAVINCI_SOC && MACH_SFFSDR
@@ -28,3 +41,23 @@
 	help
 	  Say Y if you want to add support for SoC audio on
 	  Lyrtech SFFSDR board.
+
+config  SND_DA830_SOC_EVM
+	tristate "SoC Audio support for DA830/OMAP-L137 EVM"
+	depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM
+	select SND_DAVINCI_SOC_MCASP
+	select SND_SOC_TLV320AIC3X
+
+	help
+	  Say Y if you want to add support for SoC audio on TI
+	  DA830/OMAP-L137 EVM
+
+config  SND_DA850_SOC_EVM
+	tristate "SoC Audio support for DA850/OMAP-L138 EVM"
+	depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM
+	select SND_DAVINCI_SOC_MCASP
+	select SND_SOC_TLV320AIC3X
+	help
+	  Say Y if you want to add support for SoC audio on TI
+	  DA850/OMAP-L138 EVM
+
diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile
index ca8bae1..a6939d7 100644
--- a/sound/soc/davinci/Makefile
+++ b/sound/soc/davinci/Makefile
@@ -1,13 +1,18 @@
 # DAVINCI Platform Support
 snd-soc-davinci-objs := davinci-pcm.o
 snd-soc-davinci-i2s-objs := davinci-i2s.o
+snd-soc-davinci-mcasp-objs:= davinci-mcasp.o
 
 obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o
 obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
+obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o
 
 # DAVINCI Machine Support
 snd-soc-evm-objs := davinci-evm.o
 snd-soc-sffsdr-objs := davinci-sffsdr.o
 
 obj-$(CONFIG_SND_DAVINCI_SOC_EVM) += snd-soc-evm.o
+obj-$(CONFIG_SND_DM6467_SOC_EVM) += snd-soc-evm.o
+obj-$(CONFIG_SND_DA830_SOC_EVM) += snd-soc-evm.o
+obj-$(CONFIG_SND_DA850_SOC_EVM) += snd-soc-evm.o
 obj-$(CONFIG_SND_DAVINCI_SOC_SFFSDR) += snd-soc-sffsdr.o
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 58fd1cb..46c1b0c 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -27,9 +27,10 @@
 #include <mach/mux.h>
 
 #include "../codecs/tlv320aic3x.h"
+#include "../codecs/spdif_transciever.h"
 #include "davinci-pcm.h"
 #include "davinci-i2s.h"
-
+#include "davinci-mcasp.h"
 
 #define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
 		SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF)
@@ -43,7 +44,7 @@
 	unsigned sysclk;
 
 	/* ASP1 on DM355 EVM is clocked by an external oscillator */
-	if (machine_is_davinci_dm355_evm())
+	if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm())
 		sysclk = 27000000;
 
 	/* ASP0 in DM6446 EVM is clocked by U55, as configured by
@@ -53,6 +54,10 @@
 	else if (machine_is_davinci_evm())
 		sysclk = 12288000;
 
+	else if (machine_is_davinci_da830_evm() ||
+				machine_is_davinci_da850_evm())
+		sysclk = 24576000;
+
 	else
 		return -EINVAL;
 
@@ -144,6 +149,32 @@
 	.ops = &evm_ops,
 };
 
+static struct snd_soc_dai_link dm6467_evm_dai[] = {
+	{
+		.name = "TLV320AIC3X",
+		.stream_name = "AIC3X",
+		.cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_I2S_DAI],
+		.codec_dai = &aic3x_dai,
+		.init = evm_aic3x_init,
+		.ops = &evm_ops,
+	},
+	{
+		.name = "McASP",
+		.stream_name = "spdif",
+		.cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_DIT_DAI],
+		.codec_dai = &dit_stub_dai,
+		.ops = &evm_ops,
+	},
+};
+static struct snd_soc_dai_link da8xx_evm_dai = {
+	.name = "TLV320AIC3X",
+	.stream_name = "AIC3X",
+	.cpu_dai = &davinci_mcasp_dai[DAVINCI_MCASP_I2S_DAI],
+	.codec_dai = &aic3x_dai,
+	.init = evm_aic3x_init,
+	.ops = &evm_ops,
+};
+
 /* davinci-evm audio machine driver */
 static struct snd_soc_card snd_soc_card_evm = {
 	.name = "DaVinci EVM",
@@ -152,12 +183,45 @@
 	.num_links = 1,
 };
 
+/* davinci dm6467 evm audio machine driver */
+static struct snd_soc_card dm6467_snd_soc_card_evm = {
+	.name = "DaVinci DM6467 EVM",
+	.platform = &davinci_soc_platform,
+	.dai_link = dm6467_evm_dai,
+	.num_links = ARRAY_SIZE(dm6467_evm_dai),
+};
+
+static struct snd_soc_card da830_snd_soc_card = {
+	.name = "DA830/OMAP-L137 EVM",
+	.dai_link = &da8xx_evm_dai,
+	.platform = &davinci_soc_platform,
+	.num_links = 1,
+};
+
+static struct snd_soc_card da850_snd_soc_card = {
+	.name = "DA850/OMAP-L138 EVM",
+	.dai_link = &da8xx_evm_dai,
+	.platform = &davinci_soc_platform,
+	.num_links = 1,
+};
+
 /* evm audio private data */
 static struct aic3x_setup_data evm_aic3x_setup = {
 	.i2c_bus = 1,
 	.i2c_address = 0x1b,
 };
 
+/* dm6467 evm audio private data */
+static struct aic3x_setup_data dm6467_evm_aic3x_setup = {
+       .i2c_bus = 1,
+       .i2c_address = 0x18,
+};
+
+static struct aic3x_setup_data da8xx_evm_aic3x_setup = {
+	.i2c_bus = 1,
+	.i2c_address = 0x18,
+};
+
 /* evm audio subsystem */
 static struct snd_soc_device evm_snd_devdata = {
 	.card = &snd_soc_card_evm,
@@ -165,60 +229,49 @@
 	.codec_data = &evm_aic3x_setup,
 };
 
-/* DM6446 EVM uses ASP0; line-out is a pair of RCA jacks */
-static struct resource evm_snd_resources[] = {
-	{
-		.start = DAVINCI_ASP0_BASE,
-		.end = DAVINCI_ASP0_BASE + SZ_8K - 1,
-		.flags = IORESOURCE_MEM,
-	},
+/* evm audio subsystem */
+static struct snd_soc_device dm6467_evm_snd_devdata = {
+	.card = &dm6467_snd_soc_card_evm,
+	.codec_dev = &soc_codec_dev_aic3x,
+	.codec_data = &dm6467_evm_aic3x_setup,
 };
 
-static struct evm_snd_platform_data evm_snd_data = {
-	.tx_dma_ch	= DAVINCI_DMA_ASP0_TX,
-	.rx_dma_ch	= DAVINCI_DMA_ASP0_RX,
+/* evm audio subsystem */
+static struct snd_soc_device da830_evm_snd_devdata = {
+	.card = &da830_snd_soc_card,
+	.codec_dev = &soc_codec_dev_aic3x,
+	.codec_data = &da8xx_evm_aic3x_setup,
 };
 
-/* DM335 EVM uses ASP1; line-out is a stereo mini-jack */
-static struct resource dm335evm_snd_resources[] = {
-	{
-		.start = DAVINCI_ASP1_BASE,
-		.end = DAVINCI_ASP1_BASE + SZ_8K - 1,
-		.flags = IORESOURCE_MEM,
-	},
-};
-
-static struct evm_snd_platform_data dm335evm_snd_data = {
-	.tx_dma_ch	= DAVINCI_DMA_ASP1_TX,
-	.rx_dma_ch	= DAVINCI_DMA_ASP1_RX,
+static struct snd_soc_device da850_evm_snd_devdata = {
+	.card		= &da850_snd_soc_card,
+	.codec_dev	= &soc_codec_dev_aic3x,
+	.codec_data	= &da8xx_evm_aic3x_setup,
 };
 
 static struct platform_device *evm_snd_device;
 
 static int __init evm_init(void)
 {
-	struct resource *resources;
-	unsigned num_resources;
-	struct evm_snd_platform_data *data;
+	struct snd_soc_device *evm_snd_dev_data;
 	int index;
 	int ret;
 
 	if (machine_is_davinci_evm()) {
-		davinci_cfg_reg(DM644X_MCBSP);
-
-		resources = evm_snd_resources;
-		num_resources = ARRAY_SIZE(evm_snd_resources);
-		data = &evm_snd_data;
+		evm_snd_dev_data = &evm_snd_devdata;
 		index = 0;
 	} else if (machine_is_davinci_dm355_evm()) {
-		/* we don't use ASP1 IRQs, or we'd need to mux them ... */
-		davinci_cfg_reg(DM355_EVT8_ASP1_TX);
-		davinci_cfg_reg(DM355_EVT9_ASP1_RX);
-
-		resources = dm335evm_snd_resources;
-		num_resources = ARRAY_SIZE(dm335evm_snd_resources);
-		data = &dm335evm_snd_data;
+		evm_snd_dev_data = &evm_snd_devdata;
 		index = 1;
+	} else if (machine_is_davinci_dm6467_evm()) {
+		evm_snd_dev_data = &dm6467_evm_snd_devdata;
+		index = 0;
+	} else if (machine_is_davinci_da830_evm()) {
+		evm_snd_dev_data = &da830_evm_snd_devdata;
+		index = 1;
+	} else if (machine_is_davinci_da850_evm()) {
+		evm_snd_dev_data = &da850_evm_snd_devdata;
+		index = 0;
 	} else
 		return -EINVAL;
 
@@ -226,17 +279,8 @@
 	if (!evm_snd_device)
 		return -ENOMEM;
 
-	platform_set_drvdata(evm_snd_device, &evm_snd_devdata);
-	evm_snd_devdata.dev = &evm_snd_device->dev;
-	platform_device_add_data(evm_snd_device, data, sizeof(*data));
-
-	ret = platform_device_add_resources(evm_snd_device, resources,
-			num_resources);
-	if (ret) {
-		platform_device_put(evm_snd_device);
-		return ret;
-	}
-
+	platform_set_drvdata(evm_snd_device, evm_snd_dev_data);
+	evm_snd_dev_data->dev = &evm_snd_device->dev;
 	ret = platform_device_add(evm_snd_device);
 	if (ret)
 		platform_device_put(evm_snd_device);
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c
index b1ea52f..12a6c54 100644
--- a/sound/soc/davinci/davinci-i2s.c
+++ b/sound/soc/davinci/davinci-i2s.c
@@ -22,6 +22,8 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 
+#include <mach/asp.h>
+
 #include "davinci-pcm.h"
 
 
@@ -63,6 +65,7 @@
 #define DAVINCI_MCBSP_RCR_RWDLEN1(v)	((v) << 5)
 #define DAVINCI_MCBSP_RCR_RFRLEN1(v)	((v) << 8)
 #define DAVINCI_MCBSP_RCR_RDATDLY(v)	((v) << 16)
+#define DAVINCI_MCBSP_RCR_RFIG		(1 << 18)
 #define DAVINCI_MCBSP_RCR_RWDLEN2(v)	((v) << 21)
 
 #define DAVINCI_MCBSP_XCR_XWDLEN1(v)	((v) << 5)
@@ -85,14 +88,6 @@
 #define DAVINCI_MCBSP_PCR_FSRM		(1 << 10)
 #define DAVINCI_MCBSP_PCR_FSXM		(1 << 11)
 
-#define MOD_REG_BIT(val, mask, set) do { \
-	if (set) { \
-		val |= mask; \
-	} else { \
-		val &= ~mask; \
-	} \
-} while (0)
-
 enum {
 	DAVINCI_MCBSP_WORD_8 = 0,
 	DAVINCI_MCBSP_WORD_12,
@@ -112,6 +107,10 @@
 
 struct davinci_mcbsp_dev {
 	void __iomem			*base;
+#define MOD_DSP_A	0
+#define MOD_DSP_B	1
+	int				mode;
+	u32				pcr;
 	struct clk			*clk;
 	struct davinci_pcm_dma_params	*dma_params[2];
 };
@@ -127,96 +126,100 @@
 	return __raw_readl(dev->base + reg);
 }
 
-static void davinci_mcbsp_start(struct snd_pcm_substream *substream)
+static void toggle_clock(struct davinci_mcbsp_dev *dev, int playback)
+{
+	u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP : DAVINCI_MCBSP_PCR_CLKRP;
+	/* The clock needs to toggle to complete reset.
+	 * So, fake it by toggling the clk polarity.
+	 */
+	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr ^ m);
+	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr);
+}
+
+static void davinci_mcbsp_start(struct davinci_mcbsp_dev *dev,
+		struct snd_pcm_substream *substream)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
 	struct snd_soc_device *socdev = rtd->socdev;
 	struct snd_soc_platform *platform = socdev->card->platform;
-	u32 w;
-	int ret;
+	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	u32 spcr;
+	u32 mask = playback ? DAVINCI_MCBSP_SPCR_XRST : DAVINCI_MCBSP_SPCR_RRST;
+	spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+	if (spcr & mask) {
+		/* start off disabled */
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG,
+				spcr & ~mask);
+		toggle_clock(dev, playback);
+	}
+	if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM |
+			DAVINCI_MCBSP_PCR_CLKXM | DAVINCI_MCBSP_PCR_CLKRM)) {
+		/* Start the sample generator */
+		spcr |= DAVINCI_MCBSP_SPCR_GRST;
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+	}
 
-	/* Start the sample generator and enable transmitter/receiver */
-	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-	MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST, 1);
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
-
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+	if (playback) {
 		/* Stop the DMA to avoid data loss */
 		/* while the transmitter is out of reset to handle XSYNCERR */
 		if (platform->pcm_ops->trigger) {
-			ret = platform->pcm_ops->trigger(substream,
+			int ret = platform->pcm_ops->trigger(substream,
 				SNDRV_PCM_TRIGGER_STOP);
 			if (ret < 0)
 				printk(KERN_DEBUG "Playback DMA stop failed\n");
 		}
 
 		/* Enable the transmitter */
-		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+		spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+		spcr |= DAVINCI_MCBSP_SPCR_XRST;
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
 
 		/* wait for any unexpected frame sync error to occur */
 		udelay(100);
 
 		/* Disable the transmitter to clear any outstanding XSYNCERR */
-		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+		spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+		spcr &= ~DAVINCI_MCBSP_SPCR_XRST;
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+		toggle_clock(dev, playback);
 
 		/* Restart the DMA */
 		if (platform->pcm_ops->trigger) {
-			ret = platform->pcm_ops->trigger(substream,
+			int ret = platform->pcm_ops->trigger(substream,
 				SNDRV_PCM_TRIGGER_START);
 			if (ret < 0)
 				printk(KERN_DEBUG "Playback DMA start failed\n");
 		}
-		/* Enable the transmitter */
-		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 1);
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
-
-	} else {
-
-		/* Enable the reciever */
-		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 1);
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
 	}
 
+	/* Enable transmitter or receiver */
+	spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+	spcr |= mask;
 
-	/* Start frame sync */
-	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-	MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_FRST, 1);
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+	if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM)) {
+		/* Start frame sync */
+		spcr |= DAVINCI_MCBSP_SPCR_FRST;
+	}
+	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
 }
 
-static void davinci_mcbsp_stop(struct snd_pcm_substream *substream)
+static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int playback)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
-	u32 w;
+	u32 spcr;
 
 	/* Reset transmitter/receiver and sample rate/frame sync generators */
-	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
-	MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_GRST |
-		       DAVINCI_MCBSP_SPCR_FRST, 0);
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_XRST, 0);
-	else
-		MOD_REG_BIT(w, DAVINCI_MCBSP_SPCR_RRST, 0);
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+	spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+	spcr &= ~(DAVINCI_MCBSP_SPCR_GRST | DAVINCI_MCBSP_SPCR_FRST);
+	spcr &= playback ? ~DAVINCI_MCBSP_SPCR_XRST : ~DAVINCI_MCBSP_SPCR_RRST;
+	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+	toggle_clock(dev, playback);
 }
 
 static int davinci_i2s_startup(struct snd_pcm_substream *substream,
-			       struct snd_soc_dai *dai)
+			       struct snd_soc_dai *cpu_dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
-	struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
-
+	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
 	cpu_dai->dma_data = dev->dma_params[substream->stream];
-
 	return 0;
 }
 
@@ -228,12 +231,11 @@
 	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
 	unsigned int pcr;
 	unsigned int srgr;
-	unsigned int rcr;
-	unsigned int xcr;
 	srgr = DAVINCI_MCBSP_SRGR_FSGM |
 		DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) |
 		DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
 
+	/* set master/slave audio interface */
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
 	case SND_SOC_DAIFMT_CBS_CFS:
 		/* cpu is master */
@@ -258,11 +260,8 @@
 		return -EINVAL;
 	}
 
-	rcr = DAVINCI_MCBSP_RCR_RFRLEN1(1);
-	xcr = DAVINCI_MCBSP_XCR_XFIG | DAVINCI_MCBSP_XCR_XFRLEN1(1);
+	/* interface format */
 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
-	case SND_SOC_DAIFMT_DSP_B:
-		break;
 	case SND_SOC_DAIFMT_I2S:
 		/* Davinci doesn't support TRUE I2S, but some codecs will have
 		 * the left and right channels contiguous. This allows
@@ -282,8 +281,10 @@
 		 */
 		fmt ^= SND_SOC_DAIFMT_NB_IF;
 	case SND_SOC_DAIFMT_DSP_A:
-		rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
-		xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+		dev->mode = MOD_DSP_A;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		dev->mode = MOD_DSP_B;
 		break;
 	default:
 		printk(KERN_ERR "%s:bad format\n", __func__);
@@ -343,9 +344,8 @@
 		return -EINVAL;
 	}
 	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
+	dev->pcr = pcr;
 	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr);
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
 	return 0;
 }
 
@@ -353,31 +353,40 @@
 				 struct snd_pcm_hw_params *params,
 				 struct snd_soc_dai *dai)
 {
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct davinci_pcm_dma_params *dma_params = rtd->dai->cpu_dai->dma_data;
-	struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data;
+	struct davinci_pcm_dma_params *dma_params = dai->dma_data;
+	struct davinci_mcbsp_dev *dev = dai->private_data;
 	struct snd_interval *i = NULL;
 	int mcbsp_word_length;
-	u32 w;
+	unsigned int rcr, xcr, srgr;
+	u32 spcr;
 
 	/* general line settings */
-	w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+	spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
 	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-		w |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+		spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
 	} else {
-		w |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, w);
+		spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
 	}
 
 	i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
-	w = DAVINCI_MCBSP_SRGR_FSGM;
-	MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1), 1);
+	srgr = DAVINCI_MCBSP_SRGR_FSGM;
+	srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1);
 
 	i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS);
-	MOD_REG_BIT(w, DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1), 1);
-	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, w);
+	srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1);
+	davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
 
+	rcr = DAVINCI_MCBSP_RCR_RFIG;
+	xcr = DAVINCI_MCBSP_XCR_XFIG;
+	if (dev->mode == MOD_DSP_B) {
+		rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0);
+		xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0);
+	} else {
+		rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
+		xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
+	}
 	/* Determine xfer data type */
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S8:
@@ -397,18 +406,31 @@
 		return -EINVAL;
 	}
 
-	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_RCR_REG);
-		MOD_REG_BIT(w, DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
-			       DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length), 1);
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, w);
+	dma_params->acnt  = dma_params->data_type;
+	rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1);
+	xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1);
 
-	} else {
-		w = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_XCR_REG);
-		MOD_REG_BIT(w, DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
-			       DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length), 1);
-		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, w);
+	rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) |
+		DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length);
+	xcr |= DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) |
+		DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length);
 
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr);
+	else
+		davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr);
+	return 0;
+}
+
+static int davinci_i2s_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct davinci_mcbsp_dev *dev = dai->private_data;
+	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	davinci_mcbsp_stop(dev, playback);
+	if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0) {
+		/* codec is master */
+		davinci_mcbsp_start(dev, substream);
 	}
 	return 0;
 }
@@ -416,35 +438,72 @@
 static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
 			       struct snd_soc_dai *dai)
 {
+	struct davinci_mcbsp_dev *dev = dai->private_data;
 	int ret = 0;
+	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	if ((dev->pcr & DAVINCI_MCBSP_PCR_FSXM) == 0)
+		return 0;	/* return if codec is master */
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		davinci_mcbsp_start(substream);
+		davinci_mcbsp_start(dev, substream);
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		davinci_mcbsp_stop(substream);
+		davinci_mcbsp_stop(dev, playback);
 		break;
 	default:
 		ret = -EINVAL;
 	}
-
 	return ret;
 }
 
-static int davinci_i2s_probe(struct platform_device *pdev,
-			     struct snd_soc_dai *dai)
+static void davinci_i2s_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
 {
-	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct snd_soc_card *card = socdev->card;
-	struct snd_soc_dai *cpu_dai = card->dai_link->cpu_dai;
+	struct davinci_mcbsp_dev *dev = dai->private_data;
+	int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+	davinci_mcbsp_stop(dev, playback);
+}
+
+#define DAVINCI_I2S_RATES	SNDRV_PCM_RATE_8000_96000
+
+static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
+	.startup 	= davinci_i2s_startup,
+	.shutdown	= davinci_i2s_shutdown,
+	.prepare	= davinci_i2s_prepare,
+	.trigger	= davinci_i2s_trigger,
+	.hw_params	= davinci_i2s_hw_params,
+	.set_fmt	= davinci_i2s_set_dai_fmt,
+
+};
+
+struct snd_soc_dai davinci_i2s_dai = {
+	.name = "davinci-i2s",
+	.id = 0,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = DAVINCI_I2S_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = DAVINCI_I2S_RATES,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.ops = &davinci_i2s_dai_ops,
+
+};
+EXPORT_SYMBOL_GPL(davinci_i2s_dai);
+
+static int davinci_i2s_probe(struct platform_device *pdev)
+{
+	struct snd_platform_data *pdata = pdev->dev.platform_data;
 	struct davinci_mcbsp_dev *dev;
-	struct resource *mem, *ioarea;
-	struct evm_snd_platform_data *pdata;
+	struct resource *mem, *ioarea, *res;
 	int ret;
 
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -466,8 +525,6 @@
 		goto err_release_region;
 	}
 
-	cpu_dai->private_data = dev;
-
 	dev->clk = clk_get(&pdev->dev, NULL);
 	if (IS_ERR(dev->clk)) {
 		ret = -ENODEV;
@@ -476,18 +533,37 @@
 	clk_enable(dev->clk);
 
 	dev->base = (void __iomem *)IO_ADDRESS(mem->start);
-	pdata = pdev->dev.platform_data;
 
 	dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &davinci_i2s_pcm_out;
-	dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = pdata->tx_dma_ch;
 	dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->dma_addr =
 	    (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG);
 
 	dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &davinci_i2s_pcm_in;
-	dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = pdata->rx_dma_ch;
 	dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->dma_addr =
 	    (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG);
 
+	/* first TX, then RX */
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no DMA resource\n");
+		ret = -ENXIO;
+		goto err_free_mem;
+	}
+	dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "no DMA resource\n");
+		ret = -ENXIO;
+		goto err_free_mem;
+	}
+	dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = res->start;
+
+	davinci_i2s_dai.private_data = dev;
+	ret = snd_soc_register_dai(&davinci_i2s_dai);
+	if (ret != 0)
+		goto err_free_mem;
+
 	return 0;
 
 err_free_mem:
@@ -498,62 +574,40 @@
 	return ret;
 }
 
-static void davinci_i2s_remove(struct platform_device *pdev,
-			       struct snd_soc_dai *dai)
+static int davinci_i2s_remove(struct platform_device *pdev)
 {
-	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
-	struct snd_soc_card *card = socdev->card;
-	struct snd_soc_dai *cpu_dai = card->dai_link->cpu_dai;
-	struct davinci_mcbsp_dev *dev = cpu_dai->private_data;
+	struct davinci_mcbsp_dev *dev = davinci_i2s_dai.private_data;
 	struct resource *mem;
 
+	snd_soc_unregister_dai(&davinci_i2s_dai);
 	clk_disable(dev->clk);
 	clk_put(dev->clk);
 	dev->clk = NULL;
-
 	kfree(dev);
-
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	release_mem_region(mem->start, (mem->end - mem->start) + 1);
+
+	return 0;
 }
 
-#define DAVINCI_I2S_RATES	SNDRV_PCM_RATE_8000_96000
-
-static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
-	.startup	= davinci_i2s_startup,
-	.trigger	= davinci_i2s_trigger,
-	.hw_params	= davinci_i2s_hw_params,
-	.set_fmt	= davinci_i2s_set_dai_fmt,
+static struct platform_driver davinci_mcbsp_driver = {
+	.probe		= davinci_i2s_probe,
+	.remove		= davinci_i2s_remove,
+	.driver		= {
+		.name	= "davinci-asp",
+		.owner	= THIS_MODULE,
+	},
 };
 
-struct snd_soc_dai davinci_i2s_dai = {
-	.name = "davinci-i2s",
-	.id = 0,
-	.probe = davinci_i2s_probe,
-	.remove = davinci_i2s_remove,
-	.playback = {
-		.channels_min = 2,
-		.channels_max = 2,
-		.rates = DAVINCI_I2S_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
-	.capture = {
-		.channels_min = 2,
-		.channels_max = 2,
-		.rates = DAVINCI_I2S_RATES,
-		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
-	.ops = &davinci_i2s_dai_ops,
-};
-EXPORT_SYMBOL_GPL(davinci_i2s_dai);
-
 static int __init davinci_i2s_init(void)
 {
-	return snd_soc_register_dai(&davinci_i2s_dai);
+	return platform_driver_register(&davinci_mcbsp_driver);
 }
 module_init(davinci_i2s_init);
 
 static void __exit davinci_i2s_exit(void)
 {
-	snd_soc_unregister_dai(&davinci_i2s_dai);
+	platform_driver_unregister(&davinci_mcbsp_driver);
 }
 module_exit(davinci_i2s_exit);
 
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
new file mode 100644
index 0000000..eca22d7
--- /dev/null
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -0,0 +1,973 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI DAVINCI processor
+ *
+ * Multi-channel Audio Serial Port Driver
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ *         Suresh Rajashekara <suresh.r@ti.com>
+ *         Steve Chen <schen@.mvista.com>
+ *
+ * Copyright:   (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright:   (C) 2009  Texas Instruments, India
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "davinci-pcm.h"
+#include "davinci-mcasp.h"
+
+/*
+ * McASP register definitions
+ */
+#define DAVINCI_MCASP_PID_REG		0x00
+#define DAVINCI_MCASP_PWREMUMGT_REG	0x04
+
+#define DAVINCI_MCASP_PFUNC_REG		0x10
+#define DAVINCI_MCASP_PDIR_REG		0x14
+#define DAVINCI_MCASP_PDOUT_REG		0x18
+#define DAVINCI_MCASP_PDSET_REG		0x1c
+
+#define DAVINCI_MCASP_PDCLR_REG		0x20
+
+#define DAVINCI_MCASP_TLGC_REG		0x30
+#define DAVINCI_MCASP_TLMR_REG		0x34
+
+#define DAVINCI_MCASP_GBLCTL_REG	0x44
+#define DAVINCI_MCASP_AMUTE_REG		0x48
+#define DAVINCI_MCASP_LBCTL_REG		0x4c
+
+#define DAVINCI_MCASP_TXDITCTL_REG	0x50
+
+#define DAVINCI_MCASP_GBLCTLR_REG	0x60
+#define DAVINCI_MCASP_RXMASK_REG	0x64
+#define DAVINCI_MCASP_RXFMT_REG		0x68
+#define DAVINCI_MCASP_RXFMCTL_REG	0x6c
+
+#define DAVINCI_MCASP_ACLKRCTL_REG	0x70
+#define DAVINCI_MCASP_AHCLKRCTL_REG	0x74
+#define DAVINCI_MCASP_RXTDM_REG		0x78
+#define DAVINCI_MCASP_EVTCTLR_REG	0x7c
+
+#define DAVINCI_MCASP_RXSTAT_REG	0x80
+#define DAVINCI_MCASP_RXTDMSLOT_REG	0x84
+#define DAVINCI_MCASP_RXCLKCHK_REG	0x88
+#define DAVINCI_MCASP_REVTCTL_REG	0x8c
+
+#define DAVINCI_MCASP_GBLCTLX_REG	0xa0
+#define DAVINCI_MCASP_TXMASK_REG	0xa4
+#define DAVINCI_MCASP_TXFMT_REG		0xa8
+#define DAVINCI_MCASP_TXFMCTL_REG	0xac
+
+#define DAVINCI_MCASP_ACLKXCTL_REG	0xb0
+#define DAVINCI_MCASP_AHCLKXCTL_REG	0xb4
+#define DAVINCI_MCASP_TXTDM_REG		0xb8
+#define DAVINCI_MCASP_EVTCTLX_REG	0xbc
+
+#define DAVINCI_MCASP_TXSTAT_REG	0xc0
+#define DAVINCI_MCASP_TXTDMSLOT_REG	0xc4
+#define DAVINCI_MCASP_TXCLKCHK_REG	0xc8
+#define DAVINCI_MCASP_XEVTCTL_REG	0xcc
+
+/* Left(even TDM Slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRA_REG	0x100
+/* Right(odd TDM slot) Channel Status Register File */
+#define DAVINCI_MCASP_DITCSRB_REG	0x118
+/* Left(even TDM slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRA_REG	0x130
+/* Right(odd TDM Slot) User Data Register File */
+#define DAVINCI_MCASP_DITUDRB_REG	0x148
+
+/* Serializer n Control Register */
+#define DAVINCI_MCASP_XRSRCTL_BASE_REG	0x180
+#define DAVINCI_MCASP_XRSRCTL_REG(n)	(DAVINCI_MCASP_XRSRCTL_BASE_REG + \
+						(n << 2))
+
+/* Transmit Buffer for Serializer n */
+#define DAVINCI_MCASP_TXBUF_REG		0x200
+/* Receive Buffer for Serializer n */
+#define DAVINCI_MCASP_RXBUF_REG		0x280
+
+/* McASP FIFO Registers */
+#define DAVINCI_MCASP_WFIFOCTL		(0x1010)
+#define DAVINCI_MCASP_WFIFOSTS		(0x1014)
+#define DAVINCI_MCASP_RFIFOCTL		(0x1018)
+#define DAVINCI_MCASP_RFIFOSTS		(0x101C)
+
+/*
+ * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management
+ *     Register Bits
+ */
+#define MCASP_FREE	BIT(0)
+#define MCASP_SOFT	BIT(1)
+
+/*
+ * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits
+ */
+#define AXR(n)		(1<<n)
+#define PFUNC_AMUTE	BIT(25)
+#define ACLKX		BIT(26)
+#define AHCLKX		BIT(27)
+#define AFSX		BIT(28)
+#define ACLKR		BIT(29)
+#define AHCLKR		BIT(30)
+#define AFSR		BIT(31)
+
+/*
+ * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits
+ */
+#define AXR(n)		(1<<n)
+#define PDIR_AMUTE	BIT(25)
+#define ACLKX		BIT(26)
+#define AHCLKX		BIT(27)
+#define AFSX		BIT(28)
+#define ACLKR		BIT(29)
+#define AHCLKR		BIT(30)
+#define AFSR		BIT(31)
+
+/*
+ * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits
+ */
+#define DITEN	BIT(0)	/* Transmit DIT mode enable/disable */
+#define VA	BIT(2)
+#define VB	BIT(3)
+
+/*
+ * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits
+ */
+#define TXROT(val)	(val)
+#define TXSEL		BIT(3)
+#define TXSSZ(val)	(val<<4)
+#define TXPBIT(val)	(val<<8)
+#define TXPAD(val)	(val<<13)
+#define TXORD		BIT(15)
+#define FSXDLY(val)	(val<<16)
+
+/*
+ * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits
+ */
+#define RXROT(val)	(val)
+#define RXSEL		BIT(3)
+#define RXSSZ(val)	(val<<4)
+#define RXPBIT(val)	(val<<8)
+#define RXPAD(val)	(val<<13)
+#define RXORD		BIT(15)
+#define FSRDLY(val)	(val<<16)
+
+/*
+ * DAVINCI_MCASP_TXFMCTL_REG -  Transmit Frame Control Register Bits
+ */
+#define FSXPOL		BIT(0)
+#define AFSXE		BIT(1)
+#define FSXDUR		BIT(4)
+#define FSXMOD(val)	(val<<7)
+
+/*
+ * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits
+ */
+#define FSRPOL		BIT(0)
+#define AFSRE		BIT(1)
+#define FSRDUR		BIT(4)
+#define FSRMOD(val)	(val<<7)
+
+/*
+ * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits
+ */
+#define ACLKXDIV(val)	(val)
+#define ACLKXE		BIT(5)
+#define TX_ASYNC	BIT(6)
+#define ACLKXPOL	BIT(7)
+
+/*
+ * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits
+ */
+#define ACLKRDIV(val)	(val)
+#define ACLKRE		BIT(5)
+#define RX_ASYNC	BIT(6)
+#define ACLKRPOL	BIT(7)
+
+/*
+ * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control
+ *     Register Bits
+ */
+#define AHCLKXDIV(val)	(val)
+#define AHCLKXPOL	BIT(14)
+#define AHCLKXE		BIT(15)
+
+/*
+ * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control
+ *     Register Bits
+ */
+#define AHCLKRDIV(val)	(val)
+#define AHCLKRPOL	BIT(14)
+#define AHCLKRE		BIT(15)
+
+/*
+ * DAVINCI_MCASP_XRSRCTL_BASE_REG -  Serializer Control Register Bits
+ */
+#define MODE(val)	(val)
+#define DISMOD		(val)(val<<2)
+#define TXSTATE		BIT(4)
+#define RXSTATE		BIT(5)
+
+/*
+ * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits
+ */
+#define LBEN		BIT(0)
+#define LBORD		BIT(1)
+#define LBGENMODE(val)	(val<<2)
+
+/*
+ * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration
+ */
+#define TXTDMS(n)	(1<<n)
+
+/*
+ * DAVINCI_MCASP_RXTDMSLOT_REG - Receive TDM Slot Register configuration
+ */
+#define RXTDMS(n)	(1<<n)
+
+/*
+ * DAVINCI_MCASP_GBLCTL_REG -  Global Control Register Bits
+ */
+#define RXCLKRST	BIT(0)	/* Receiver Clock Divider Reset */
+#define RXHCLKRST	BIT(1)	/* Receiver High Frequency Clock Divider */
+#define RXSERCLR	BIT(2)	/* Receiver Serializer Clear */
+#define RXSMRST		BIT(3)	/* Receiver State Machine Reset */
+#define RXFSRST		BIT(4)	/* Frame Sync Generator Reset */
+#define TXCLKRST	BIT(8)	/* Transmitter Clock Divider Reset */
+#define TXHCLKRST	BIT(9)	/* Transmitter High Frequency Clock Divider*/
+#define TXSERCLR	BIT(10)	/* Transmit Serializer Clear */
+#define TXSMRST		BIT(11)	/* Transmitter State Machine Reset */
+#define TXFSRST		BIT(12)	/* Frame Sync Generator Reset */
+
+/*
+ * DAVINCI_MCASP_AMUTE_REG -  Mute Control Register Bits
+ */
+#define MUTENA(val)	(val)
+#define MUTEINPOL	BIT(2)
+#define MUTEINENA	BIT(3)
+#define MUTEIN		BIT(4)
+#define MUTER		BIT(5)
+#define MUTEX		BIT(6)
+#define MUTEFSR		BIT(7)
+#define MUTEFSX		BIT(8)
+#define MUTEBADCLKR	BIT(9)
+#define MUTEBADCLKX	BIT(10)
+#define MUTERXDMAERR	BIT(11)
+#define MUTETXDMAERR	BIT(12)
+
+/*
+ * DAVINCI_MCASP_REVTCTL_REG - Receiver DMA Event Control Register bits
+ */
+#define RXDATADMADIS	BIT(0)
+
+/*
+ * DAVINCI_MCASP_XEVTCTL_REG - Transmitter DMA Event Control Register bits
+ */
+#define TXDATADMADIS	BIT(0)
+
+/*
+ * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
+ */
+#define FIFO_ENABLE	BIT(16)
+#define NUMEVT_MASK	(0xFF << 8)
+#define NUMDMA_MASK	(0xFF)
+
+#define DAVINCI_MCASP_NUM_SERIALIZER	16
+
+static inline void mcasp_set_bits(void __iomem *reg, u32 val)
+{
+	__raw_writel(__raw_readl(reg) | val, reg);
+}
+
+static inline void mcasp_clr_bits(void __iomem *reg, u32 val)
+{
+	__raw_writel((__raw_readl(reg) & ~(val)), reg);
+}
+
+static inline void mcasp_mod_bits(void __iomem *reg, u32 val, u32 mask)
+{
+	__raw_writel((__raw_readl(reg) & ~mask) | val, reg);
+}
+
+static inline void mcasp_set_reg(void __iomem *reg, u32 val)
+{
+	__raw_writel(val, reg);
+}
+
+static inline u32 mcasp_get_reg(void __iomem *reg)
+{
+	return (unsigned int)__raw_readl(reg);
+}
+
+static inline void mcasp_set_ctl_reg(void __iomem *regs, u32 val)
+{
+	int i = 0;
+
+	mcasp_set_bits(regs, val);
+
+	/* programming GBLCTL needs to read back from GBLCTL and verfiy */
+	/* loop count is to avoid the lock-up */
+	for (i = 0; i < 1000; i++) {
+		if ((mcasp_get_reg(regs) & val) == val)
+			break;
+	}
+
+	if (i == 1000 && ((mcasp_get_reg(regs) & val) != val))
+		printk(KERN_ERR "GBLCTL write error\n");
+}
+
+static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
+						struct snd_soc_dai *cpu_dai)
+{
+	struct davinci_audio_dev *dev = cpu_dai->private_data;
+	cpu_dai->dma_data = dev->dma_params[substream->stream];
+	return 0;
+}
+
+static void mcasp_start_rx(struct davinci_audio_dev *dev)
+{
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0);
+
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_RXBUF_REG, 0);
+
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
+}
+
+static void mcasp_start_tx(struct davinci_audio_dev *dev)
+{
+	u8 offset = 0, i;
+	u32 cnt;
+
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
+
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+	mcasp_set_ctl_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
+	for (i = 0; i < dev->num_serializer; i++) {
+		if (dev->serial_dir[i] == TX_MODE) {
+			offset = i;
+			break;
+		}
+	}
+
+	/* wait for TX ready */
+	cnt = 0;
+	while (!(mcasp_get_reg(dev->base + DAVINCI_MCASP_XRSRCTL_REG(offset)) &
+		 TXSTATE) && (cnt < 100000))
+		cnt++;
+
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXBUF_REG, 0);
+}
+
+static void davinci_mcasp_start(struct davinci_audio_dev *dev, int stream)
+{
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		mcasp_start_tx(dev);
+	else
+		mcasp_start_rx(dev);
+
+	/* enable FIFO */
+	if (dev->txnumevt)
+		mcasp_set_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
+
+	if (dev->rxnumevt)
+		mcasp_set_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
+}
+
+static void mcasp_stop_rx(struct davinci_audio_dev *dev)
+{
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLR_REG, 0);
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+}
+
+static void mcasp_stop_tx(struct davinci_audio_dev *dev)
+{
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_GBLCTLX_REG, 0);
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+}
+
+static void davinci_mcasp_stop(struct davinci_audio_dev *dev, int stream)
+{
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		mcasp_stop_tx(dev);
+	else
+		mcasp_stop_rx(dev);
+
+	/* disable FIFO */
+	if (dev->txnumevt)
+		mcasp_clr_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
+
+	if (dev->rxnumevt)
+		mcasp_clr_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
+}
+
+static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+					 unsigned int fmt)
+{
+	struct davinci_audio_dev *dev = cpu_dai->private_data;
+	void __iomem *base = dev->base;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		/* codec is clock and frame slave */
+		mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+		mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+		mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+		mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+		mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x7 << 26));
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		/* codec is clock master and frame slave */
+		mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+		mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+		mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+		mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+		mcasp_set_bits(base + DAVINCI_MCASP_PDIR_REG, (0x2d << 26));
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		/* codec is clock and frame master */
+		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE);
+		mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, AFSXE);
+
+		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE);
+		mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, AFSRE);
+
+		mcasp_clr_bits(base + DAVINCI_MCASP_PDIR_REG, (0x3f << 26));
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_IB_NF:
+		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+		mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+		mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+		mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+		break;
+
+	case SND_SOC_DAIFMT_NB_IF:
+		mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+		mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+		mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+		break;
+
+	case SND_SOC_DAIFMT_IB_IF:
+		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+		mcasp_set_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+		mcasp_set_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+		mcasp_set_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+		break;
+
+	case SND_SOC_DAIFMT_NB_NF:
+		mcasp_set_bits(base + DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL);
+		mcasp_clr_bits(base + DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
+
+		mcasp_clr_bits(base + DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL);
+		mcasp_clr_bits(base + DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int davinci_config_channel_size(struct davinci_audio_dev *dev,
+				       int channel_size)
+{
+	u32 fmt = 0;
+
+	switch (channel_size) {
+	case DAVINCI_AUDIO_WORD_8:
+		fmt = 0x03;
+		break;
+
+	case DAVINCI_AUDIO_WORD_12:
+		fmt = 0x05;
+		break;
+
+	case DAVINCI_AUDIO_WORD_16:
+		fmt = 0x07;
+		break;
+
+	case DAVINCI_AUDIO_WORD_20:
+		fmt = 0x09;
+		break;
+
+	case DAVINCI_AUDIO_WORD_24:
+		fmt = 0x0B;
+		break;
+
+	case DAVINCI_AUDIO_WORD_28:
+		fmt = 0x0D;
+		break;
+
+	case DAVINCI_AUDIO_WORD_32:
+		fmt = 0x0F;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMT_REG,
+					RXSSZ(fmt), RXSSZ(0x0F));
+	mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
+					TXSSZ(fmt), TXSSZ(0x0F));
+	return 0;
+}
+
+static void davinci_hw_common_param(struct davinci_audio_dev *dev, int stream)
+{
+	int i;
+	u8 tx_ser = 0;
+	u8 rx_ser = 0;
+
+	/* Default configuration */
+	mcasp_set_bits(dev->base + DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT);
+
+	/* All PINS as McASP */
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_PFUNC_REG, 0x00000000);
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		mcasp_set_reg(dev->base + DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+		mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG,
+				TXDATADMADIS);
+	} else {
+		mcasp_set_reg(dev->base + DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+		mcasp_clr_bits(dev->base + DAVINCI_MCASP_REVTCTL_REG,
+				RXDATADMADIS);
+	}
+
+	for (i = 0; i < dev->num_serializer; i++) {
+		mcasp_set_bits(dev->base + DAVINCI_MCASP_XRSRCTL_REG(i),
+					dev->serial_dir[i]);
+		if (dev->serial_dir[i] == TX_MODE) {
+			mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
+					AXR(i));
+			tx_ser++;
+		} else if (dev->serial_dir[i] == RX_MODE) {
+			mcasp_clr_bits(dev->base + DAVINCI_MCASP_PDIR_REG,
+					AXR(i));
+			rx_ser++;
+		}
+	}
+
+	if (dev->txnumevt && stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (dev->txnumevt * tx_ser > 64)
+			dev->txnumevt = 1;
+
+		mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, tx_ser,
+								NUMDMA_MASK);
+		mcasp_mod_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
+				((dev->txnumevt * tx_ser) << 8), NUMEVT_MASK);
+		mcasp_set_bits(dev->base + DAVINCI_MCASP_WFIFOCTL, FIFO_ENABLE);
+	}
+
+	if (dev->rxnumevt && stream == SNDRV_PCM_STREAM_CAPTURE) {
+		if (dev->rxnumevt * rx_ser > 64)
+			dev->rxnumevt = 1;
+
+		mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, rx_ser,
+								NUMDMA_MASK);
+		mcasp_mod_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
+				((dev->rxnumevt * rx_ser) << 8), NUMEVT_MASK);
+		mcasp_set_bits(dev->base + DAVINCI_MCASP_RFIFOCTL, FIFO_ENABLE);
+	}
+}
+
+static void davinci_hw_param(struct davinci_audio_dev *dev, int stream)
+{
+	int i, active_slots;
+	u32 mask = 0;
+
+	active_slots = (dev->tdm_slots > 31) ? 32 : dev->tdm_slots;
+	for (i = 0; i < active_slots; i++)
+		mask |= (1 << i);
+
+	mcasp_clr_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		/* bit stream is MSB first  with no delay */
+		/* DSP_B mode */
+		mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG,
+				AHCLKXE);
+		mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, mask);
+		mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG, TXORD);
+
+		if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
+			mcasp_mod_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
+					FSXMOD(dev->tdm_slots), FSXMOD(0x1FF));
+		else
+			printk(KERN_ERR "playback tdm slot %d not supported\n",
+				dev->tdm_slots);
+
+		mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0xFFFFFFFF);
+		mcasp_clr_bits(dev->base + DAVINCI_MCASP_TXFMCTL_REG, FSXDUR);
+	} else {
+		/* bit stream is MSB first with no delay */
+		/* DSP_B mode */
+		mcasp_set_bits(dev->base + DAVINCI_MCASP_RXFMT_REG, RXORD);
+		mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKRCTL_REG,
+				AHCLKRE);
+		mcasp_set_reg(dev->base + DAVINCI_MCASP_RXTDM_REG, mask);
+
+		if ((dev->tdm_slots >= 2) || (dev->tdm_slots <= 32))
+			mcasp_mod_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG,
+					FSRMOD(dev->tdm_slots), FSRMOD(0x1FF));
+		else
+			printk(KERN_ERR "capture tdm slot %d not supported\n",
+				dev->tdm_slots);
+
+		mcasp_set_reg(dev->base + DAVINCI_MCASP_RXMASK_REG, 0xFFFFFFFF);
+		mcasp_clr_bits(dev->base + DAVINCI_MCASP_RXFMCTL_REG, FSRDUR);
+	}
+}
+
+/* S/PDIF */
+static void davinci_hw_dit_param(struct davinci_audio_dev *dev)
+{
+	/* Set the PDIR for Serialiser as output */
+	mcasp_set_bits(dev->base + DAVINCI_MCASP_PDIR_REG, AFSX);
+
+	/* TXMASK for 24 bits */
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXMASK_REG, 0x00FFFFFF);
+
+	/* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0
+	   and LSB first */
+	mcasp_set_bits(dev->base + DAVINCI_MCASP_TXFMT_REG,
+						TXROT(6) | TXSSZ(15));
+
+	/* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXFMCTL_REG,
+						AFSXE | FSXMOD(0x180));
+
+	/* Set the TX tdm : for all the slots */
+	mcasp_set_reg(dev->base + DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF);
+
+	/* Set the TX clock controls : div = 1 and internal */
+	mcasp_set_bits(dev->base + DAVINCI_MCASP_ACLKXCTL_REG,
+						ACLKXE | TX_ASYNC);
+
+	mcasp_clr_bits(dev->base + DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS);
+
+	/* Only 44100 and 48000 are valid, both have the same setting */
+	mcasp_set_bits(dev->base + DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3));
+
+	/* Enable the DIT */
+	mcasp_set_bits(dev->base + DAVINCI_MCASP_TXDITCTL_REG, DITEN);
+}
+
+static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
+					struct snd_pcm_hw_params *params,
+					struct snd_soc_dai *cpu_dai)
+{
+	struct davinci_audio_dev *dev = cpu_dai->private_data;
+	struct davinci_pcm_dma_params *dma_params =
+					dev->dma_params[substream->stream];
+	int word_length;
+	u8 numevt;
+
+	davinci_hw_common_param(dev, substream->stream);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		numevt = dev->txnumevt;
+	else
+		numevt = dev->rxnumevt;
+
+	if (!numevt)
+		numevt = 1;
+
+	if (dev->op_mode == DAVINCI_MCASP_DIT_MODE)
+		davinci_hw_dit_param(dev);
+	else
+		davinci_hw_param(dev, substream->stream);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		dma_params->data_type = 1;
+		word_length = DAVINCI_AUDIO_WORD_8;
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+		dma_params->data_type = 2;
+		word_length = DAVINCI_AUDIO_WORD_16;
+		break;
+
+	case SNDRV_PCM_FORMAT_S32_LE:
+		dma_params->data_type = 4;
+		word_length = DAVINCI_AUDIO_WORD_32;
+		break;
+
+	default:
+		printk(KERN_WARNING "davinci-mcasp: unsupported PCM format");
+		return -EINVAL;
+	}
+
+	if (dev->version == MCASP_VERSION_2) {
+		dma_params->data_type *= numevt;
+		dma_params->acnt = 4 * numevt;
+	} else
+		dma_params->acnt = dma_params->data_type;
+
+	davinci_config_channel_size(dev, word_length);
+
+	return 0;
+}
+
+static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
+				     int cmd, struct snd_soc_dai *cpu_dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct davinci_audio_dev *dev = rtd->dai->cpu_dai->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		davinci_mcasp_start(dev, substream->stream);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		davinci_mcasp_stop(dev, substream->stream);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
+	.startup 	= davinci_mcasp_startup,
+	.trigger	= davinci_mcasp_trigger,
+	.hw_params	= davinci_mcasp_hw_params,
+	.set_fmt	= davinci_mcasp_set_dai_fmt,
+
+};
+
+struct snd_soc_dai davinci_mcasp_dai[] = {
+	{
+		.name 		= "davinci-i2s",
+		.id 		= 0,
+		.playback	= {
+			.channels_min	= 2,
+			.channels_max 	= 2,
+			.rates 		= DAVINCI_MCASP_RATES,
+			.formats 	= SNDRV_PCM_FMTBIT_S8 |
+						SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.capture 	= {
+			.channels_min 	= 2,
+			.channels_max 	= 2,
+			.rates 		= DAVINCI_MCASP_RATES,
+			.formats	= SNDRV_PCM_FMTBIT_S8 |
+						SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S32_LE,
+		},
+		.ops 		= &davinci_mcasp_dai_ops,
+
+	},
+	{
+		.name 		= "davinci-dit",
+		.id 		= 1,
+		.playback 	= {
+			.channels_min	= 1,
+			.channels_max	= 384,
+			.rates		= DAVINCI_MCASP_RATES,
+			.formats	= SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops 		= &davinci_mcasp_dai_ops,
+	},
+
+};
+EXPORT_SYMBOL_GPL(davinci_mcasp_dai);
+
+static int davinci_mcasp_probe(struct platform_device *pdev)
+{
+	struct davinci_pcm_dma_params *dma_data;
+	struct resource *mem, *ioarea, *res;
+	struct snd_platform_data *pdata;
+	struct davinci_audio_dev *dev;
+	int count = 0;
+	int ret = 0;
+
+	dev = kzalloc(sizeof(struct davinci_audio_dev), GFP_KERNEL);
+	if (!dev)
+		return	-ENOMEM;
+
+	dma_data = kzalloc(sizeof(struct davinci_pcm_dma_params) * 2,
+								GFP_KERNEL);
+	if (!dma_data) {
+		ret = -ENOMEM;
+		goto err_release_dev;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "no mem resource?\n");
+		ret = -ENODEV;
+		goto err_release_data;
+	}
+
+	ioarea = request_mem_region(mem->start,
+			(mem->end - mem->start) + 1, pdev->name);
+	if (!ioarea) {
+		dev_err(&pdev->dev, "Audio region already claimed\n");
+		ret = -EBUSY;
+		goto err_release_data;
+	}
+
+	pdata = pdev->dev.platform_data;
+	dev->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(dev->clk)) {
+		ret = -ENODEV;
+		goto err_release_region;
+	}
+
+	clk_enable(dev->clk);
+
+	dev->base = (void __iomem *)IO_ADDRESS(mem->start);
+	dev->op_mode = pdata->op_mode;
+	dev->tdm_slots = pdata->tdm_slots;
+	dev->num_serializer = pdata->num_serializer;
+	dev->serial_dir = pdata->serial_dir;
+	dev->codec_fmt = pdata->codec_fmt;
+	dev->version = pdata->version;
+	dev->txnumevt = pdata->txnumevt;
+	dev->rxnumevt = pdata->rxnumevt;
+
+	dma_data[count].name = "I2S PCM Stereo out";
+	dma_data[count].eventq_no = pdata->eventq_no;
+	dma_data[count].dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
+							io_v2p(dev->base));
+	dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &dma_data[count];
+
+	/* first TX, then RX */
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no DMA resource\n");
+		goto err_release_region;
+	}
+
+	dma_data[count].channel = res->start;
+	count++;
+	dma_data[count].name = "I2S PCM Stereo in";
+	dma_data[count].eventq_no = pdata->eventq_no;
+	dma_data[count].dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
+							io_v2p(dev->base));
+	dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &dma_data[count];
+
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "no DMA resource\n");
+		goto err_release_region;
+	}
+
+	dma_data[count].channel = res->start;
+	davinci_mcasp_dai[pdata->op_mode].private_data = dev;
+	davinci_mcasp_dai[pdata->op_mode].dev = &pdev->dev;
+	ret = snd_soc_register_dai(&davinci_mcasp_dai[pdata->op_mode]);
+
+	if (ret != 0)
+		goto err_release_region;
+	return 0;
+
+err_release_region:
+	release_mem_region(mem->start, (mem->end - mem->start) + 1);
+err_release_data:
+	kfree(dma_data);
+err_release_dev:
+	kfree(dev);
+
+	return ret;
+}
+
+static int davinci_mcasp_remove(struct platform_device *pdev)
+{
+	struct snd_platform_data *pdata = pdev->dev.platform_data;
+	struct davinci_pcm_dma_params *dma_data;
+	struct davinci_audio_dev *dev;
+	struct resource *mem;
+
+	snd_soc_unregister_dai(&davinci_mcasp_dai[pdata->op_mode]);
+	dev = davinci_mcasp_dai[pdata->op_mode].private_data;
+	clk_disable(dev->clk);
+	clk_put(dev->clk);
+	dev->clk = NULL;
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(mem->start, (mem->end - mem->start) + 1);
+
+	dma_data = dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK];
+	kfree(dma_data);
+	kfree(dev);
+
+	return 0;
+}
+
+static struct platform_driver davinci_mcasp_driver = {
+	.probe		= davinci_mcasp_probe,
+	.remove		= davinci_mcasp_remove,
+	.driver		= {
+		.name	= "davinci-mcasp",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init davinci_mcasp_init(void)
+{
+	return platform_driver_register(&davinci_mcasp_driver);
+}
+module_init(davinci_mcasp_init);
+
+static void __exit davinci_mcasp_exit(void)
+{
+	platform_driver_unregister(&davinci_mcasp_driver);
+}
+module_exit(davinci_mcasp_exit);
+
+MODULE_AUTHOR("Steve Chen");
+MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
new file mode 100644
index 0000000..554354c
--- /dev/null
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -0,0 +1,60 @@
+/*
+ * ALSA SoC McASP Audio Layer for TI DAVINCI processor
+ *
+ * MCASP related definitions
+ *
+ * Author: Nirmal Pandey <n-pandey@ti.com>,
+ *         Suresh Rajashekara <suresh.r@ti.com>
+ *         Steve Chen <schen@.mvista.com>
+ *
+ * Copyright:   (C) 2009 MontaVista Software, Inc., <source@mvista.com>
+ * Copyright:   (C) 2009  Texas Instruments, India
+ *
+ * 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 DAVINCI_MCASP_H
+#define DAVINCI_MCASP_H
+
+#include <linux/io.h>
+#include <mach/asp.h>
+#include "davinci-pcm.h"
+
+extern struct snd_soc_dai davinci_mcasp_dai[];
+
+#define DAVINCI_MCASP_RATES	SNDRV_PCM_RATE_8000_96000
+#define DAVINCI_MCASP_I2S_DAI	0
+#define DAVINCI_MCASP_DIT_DAI	1
+
+enum {
+	DAVINCI_AUDIO_WORD_8 = 0,
+	DAVINCI_AUDIO_WORD_12,
+	DAVINCI_AUDIO_WORD_16,
+	DAVINCI_AUDIO_WORD_20,
+	DAVINCI_AUDIO_WORD_24,
+	DAVINCI_AUDIO_WORD_32,
+	DAVINCI_AUDIO_WORD_28,  /* This is only valid for McASP */
+};
+
+struct davinci_audio_dev {
+	void __iomem *base;
+	int sample_rate;
+	struct clk *clk;
+	struct davinci_pcm_dma_params *dma_params[2];
+	unsigned int codec_fmt;
+
+	/* McASP specific data */
+	int	tdm_slots;
+	u8	op_mode;
+	u8	num_serializer;
+	u8	*serial_dir;
+	u8	version;
+
+	/* McASP FIFO related */
+	u8	txnumevt;
+	u8	rxnumevt;
+};
+
+#endif	/* DAVINCI_MCASP_H */
diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c
index a059965..091dacb 100644
--- a/sound/soc/davinci/davinci-pcm.c
+++ b/sound/soc/davinci/davinci-pcm.c
@@ -67,6 +67,7 @@
 	dma_addr_t src, dst;
 	unsigned short src_bidx, dst_bidx;
 	unsigned int data_type;
+	unsigned short acnt;
 	unsigned int count;
 
 	period_size = snd_pcm_lib_period_bytes(substream);
@@ -91,11 +92,12 @@
 		dst_bidx = data_type;
 	}
 
+	acnt = prtd->params->acnt;
 	edma_set_src(lch, src, INCR, W8BIT);
 	edma_set_dest(lch, dst, INCR, W8BIT);
 	edma_set_src_index(lch, src_bidx, 0);
 	edma_set_dest_index(lch, dst_bidx, 0);
-	edma_set_transfer_params(lch, data_type, count, 1, 0, ASYNC);
+	edma_set_transfer_params(lch, acnt, count, 1, 0, ASYNC);
 
 	prtd->period++;
 	if (unlikely(prtd->period >= runtime->periods))
@@ -206,6 +208,7 @@
 	/* Copy self-linked parameter RAM entry into master channel */
 	edma_read_slot(prtd->slave_lch, &temp);
 	edma_write_slot(prtd->master_lch, &temp);
+	davinci_pcm_enqueue_dma(substream);
 
 	return 0;
 }
@@ -243,6 +246,11 @@
 	int ret = 0;
 
 	snd_soc_set_runtime_hwparams(substream, &davinci_pcm_hardware);
+	/* ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(runtime,
+						SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
 
 	prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL);
 	if (prtd == NULL)
diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h
index 62cb4eb..63d9625 100644
--- a/sound/soc/davinci/davinci-pcm.h
+++ b/sound/soc/davinci/davinci-pcm.h
@@ -12,17 +12,20 @@
 #ifndef _DAVINCI_PCM_H
 #define _DAVINCI_PCM_H
 
+#include <mach/edma.h>
+#include <mach/asp.h>
+
+
 struct davinci_pcm_dma_params {
-	char *name;		/* stream identifier */
-	int channel;		/* sync dma channel ID */
-	dma_addr_t dma_addr;	/* device physical address for DMA */
-	unsigned int data_type;	/* xfer data type */
+	char *name;			/* stream identifier */
+	int channel;			/* sync dma channel ID */
+	unsigned short acnt;
+	dma_addr_t dma_addr;		/* device physical address for DMA */
+	enum dma_event_q eventq_no;	/* event queue number */
+	unsigned char data_type;	/* xfer data type */
+	unsigned char convert_mono_stereo;
 };
 
-struct evm_snd_platform_data {
-	int tx_dma_ch;
-	int rx_dma_ch;
-};
 
 extern struct snd_soc_platform davinci_soc_platform;
 
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 9fc9082..8cb65cc 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,5 +1,8 @@
 config SND_SOC_OF_SIMPLE
 	tristate
+	
+config SND_MPC52xx_DMA
+	tristate
 
 # ASoC platform support for the Freescale MPC8610 SOC.  This compiles drivers
 # for the SSI and the Elo DMA controller.  You will still need to select
@@ -22,7 +25,34 @@
 config SND_SOC_MPC5200_I2S
 	tristate "Freescale MPC5200 PSC in I2S mode driver"
 	depends on PPC_MPC52xx && PPC_BESTCOMM
-	select SND_SOC_OF_SIMPLE
+	select SND_MPC52xx_DMA
 	select PPC_BESTCOMM_GEN_BD
 	help
 	  Say Y here to support the MPC5200 PSCs in I2S mode.
+
+config SND_SOC_MPC5200_AC97
+	tristate "Freescale MPC5200 PSC in AC97 mode driver"
+	depends on PPC_MPC52xx && PPC_BESTCOMM
+	select SND_SOC_AC97_BUS
+	select SND_MPC52xx_DMA
+	select PPC_BESTCOMM_GEN_BD
+	help
+	  Say Y here to support the MPC5200 PSCs in AC97 mode.
+
+config SND_MPC52xx_SOC_PCM030
+	tristate "SoC AC97 Audio support for Phytec pcm030 and WM9712"
+	depends on PPC_MPC5200_SIMPLE
+	select SND_SOC_MPC5200_AC97
+	select SND_SOC_WM9712
+	help
+	  Say Y if you want to add support for sound on the Phytec pcm030
+	  baseboard.
+
+config SND_MPC52xx_SOC_EFIKA
+	tristate "SoC AC97 Audio support for bbplan Efika and STAC9766"
+	depends on PPC_EFIKA
+	select SND_SOC_MPC5200_AC97
+	select SND_SOC_STAC9766
+	help
+	  Say Y if you want to add support for sound on the Efika.
+
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index f85134c..a83a739 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -10,5 +10,12 @@
 snd-soc-fsl-dma-objs := fsl_dma.o
 obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o
 
+# MPC5200 Platform Support
+obj-$(CONFIG_SND_MPC52xx_DMA) += mpc5200_dma.o
 obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o
+obj-$(CONFIG_SND_SOC_MPC5200_AC97) += mpc5200_psc_ac97.o
+
+# MPC5200 Machine Support
+obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o
+obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o
 
diff --git a/sound/soc/fsl/efika-audio-fabric.c b/sound/soc/fsl/efika-audio-fabric.c
new file mode 100644
index 0000000..85b0e75
--- /dev/null
+++ b/sound/soc/fsl/efika-audio-fabric.c
@@ -0,0 +1,90 @@
+/*
+ * Efika driver for the PSC of the Freescale MPC52xx
+ * configured as AC97 interface
+ *
+ * Copyright 2008 Jon Smirl, Digispeaker
+ * Author: Jon Smirl <jonsmirl@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.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-of-simple.h>
+
+#include "mpc5200_dma.h"
+#include "mpc5200_psc_ac97.h"
+#include "../codecs/stac9766.h"
+
+static struct snd_soc_device device;
+static struct snd_soc_card card;
+
+static struct snd_soc_dai_link efika_fabric_dai[] = {
+{
+	.name = "AC97",
+	.stream_name = "AC97 Analog",
+	.codec_dai = &stac9766_dai[STAC9766_DAI_AC97_ANALOG],
+	.cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL],
+},
+{
+	.name = "AC97",
+	.stream_name = "AC97 IEC958",
+	.codec_dai = &stac9766_dai[STAC9766_DAI_AC97_DIGITAL],
+	.cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF],
+},
+};
+
+static __init int efika_fabric_init(void)
+{
+	struct platform_device *pdev;
+	int rc;
+
+	if (!machine_is_compatible("bplan,efika"))
+		return -ENODEV;
+
+	card.platform = &mpc5200_audio_dma_platform;
+	card.name = "Efika";
+	card.dai_link = efika_fabric_dai;
+	card.num_links = ARRAY_SIZE(efika_fabric_dai);
+
+	device.card = &card;
+	device.codec_dev = &soc_codec_dev_stac9766;
+
+	pdev = platform_device_alloc("soc-audio", 1);
+	if (!pdev) {
+		pr_err("efika_fabric_init: platform_device_alloc() failed\n");
+		return -ENODEV;
+	}
+
+	platform_set_drvdata(pdev, &device);
+	device.dev = &pdev->dev;
+
+	rc = platform_device_add(pdev);
+	if (rc) {
+		pr_err("efika_fabric_init: platform_device_add() failed\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+module_init(efika_fabric_init);
+
+
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_DESCRIPTION(DRV_NAME ": mpc5200 Efika fabric driver");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 3711d84..93f0f38 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -375,18 +375,14 @@
 		struct snd_pcm_runtime *first_runtime =
 			ssi_private->first_stream->runtime;
 
-		if (!first_runtime->rate || !first_runtime->sample_bits) {
+		if (!first_runtime->sample_bits) {
 			dev_err(substream->pcm->card->dev,
-				"set sample rate and size in %s stream first\n",
+				"set sample size in %s stream first\n",
 				substream->stream == SNDRV_PCM_STREAM_PLAYBACK
 				? "capture" : "playback");
 			return -EAGAIN;
 		}
 
-		snd_pcm_hw_constraint_minmax(substream->runtime,
-			SNDRV_PCM_HW_PARAM_RATE,
-			first_runtime->rate, first_runtime->rate);
-
 		/* If we're in synchronous mode, then we need to constrain
 		 * the sample size as well.  We don't support independent sample
 		 * rates in asynchronous mode.
@@ -674,7 +670,7 @@
 	ssi_private->dev = ssi_info->dev;
 	ssi_private->asynchronous = ssi_info->asynchronous;
 
-	ssi_private->dev->driver_data = fsl_ssi_dai;
+	dev_set_drvdata(ssi_private->dev, fsl_ssi_dai);
 
 	/* Initialize the the device_attribute structure */
 	dev_attr->attr.name = "ssi-stats";
@@ -693,6 +689,7 @@
 	fsl_ssi_dai->name = ssi_private->name;
 	fsl_ssi_dai->id = ssi_info->id;
 	fsl_ssi_dai->dev = ssi_info->dev;
+	fsl_ssi_dai->symmetric_rates = 1;
 
 	ret = snd_soc_register_dai(fsl_ssi_dai);
 	if (ret != 0) {
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
new file mode 100644
index 0000000..9ff62e3
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -0,0 +1,582 @@
+/*
+ * Freescale MPC5200 PSC DMA
+ * ALSA SoC Platform driver
+ *
+ * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ * Copyright (C) 2009 Jon Smirl, Digispeaker
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+
+#include <sound/soc.h>
+
+#include <sysdev/bestcomm/bestcomm.h>
+#include <sysdev/bestcomm/gen_bd.h>
+#include <asm/mpc52xx_psc.h>
+
+#include "mpc5200_dma.h"
+
+/*
+ * Interrupt handlers
+ */
+static irqreturn_t psc_dma_status_irq(int irq, void *_psc_dma)
+{
+	struct psc_dma *psc_dma = _psc_dma;
+	struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+	u16 isr;
+
+	isr = in_be16(&regs->mpc52xx_psc_isr);
+
+	/* Playback underrun error */
+	if (psc_dma->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP))
+		psc_dma->stats.underrun_count++;
+
+	/* Capture overrun error */
+	if (psc_dma->capture.active && (isr & MPC52xx_PSC_IMR_ORERR))
+		psc_dma->stats.overrun_count++;
+
+	out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * psc_dma_bcom_enqueue_next_buffer - Enqueue another audio buffer
+ * @s: pointer to stream private data structure
+ *
+ * Enqueues another audio period buffer into the bestcomm queue.
+ *
+ * Note: The routine must only be called when there is space available in
+ * the queue.  Otherwise the enqueue will fail and the audio ring buffer
+ * will get out of sync
+ */
+static void psc_dma_bcom_enqueue_next_buffer(struct psc_dma_stream *s)
+{
+	struct bcom_bd *bd;
+
+	/* Prepare and enqueue the next buffer descriptor */
+	bd = bcom_prepare_next_buffer(s->bcom_task);
+	bd->status = s->period_bytes;
+	bd->data[0] = s->period_next_pt;
+	bcom_submit_next_buffer(s->bcom_task, NULL);
+
+	/* Update for next period */
+	s->period_next_pt += s->period_bytes;
+	if (s->period_next_pt >= s->period_end)
+		s->period_next_pt = s->period_start;
+}
+
+static void psc_dma_bcom_enqueue_tx(struct psc_dma_stream *s)
+{
+	if (s->appl_ptr > s->runtime->control->appl_ptr) {
+		/*
+		 * In this case s->runtime->control->appl_ptr has wrapped around.
+		 * Play the data to the end of the boundary, then wrap our own
+		 * appl_ptr back around.
+		 */
+		while (s->appl_ptr < s->runtime->boundary) {
+			if (bcom_queue_full(s->bcom_task))
+				return;
+
+			s->appl_ptr += s->period_size;
+
+			psc_dma_bcom_enqueue_next_buffer(s);
+		}
+		s->appl_ptr -= s->runtime->boundary;
+	}
+
+	while (s->appl_ptr < s->runtime->control->appl_ptr) {
+
+		if (bcom_queue_full(s->bcom_task))
+			return;
+
+		s->appl_ptr += s->period_size;
+
+		psc_dma_bcom_enqueue_next_buffer(s);
+	}
+}
+
+/* Bestcomm DMA irq handler */
+static irqreturn_t psc_dma_bcom_irq_tx(int irq, void *_psc_dma_stream)
+{
+	struct psc_dma_stream *s = _psc_dma_stream;
+
+	spin_lock(&s->psc_dma->lock);
+	/* For each finished period, dequeue the completed period buffer
+	 * and enqueue a new one in it's place. */
+	while (bcom_buffer_done(s->bcom_task)) {
+		bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+
+		s->period_current_pt += s->period_bytes;
+		if (s->period_current_pt >= s->period_end)
+			s->period_current_pt = s->period_start;
+	}
+	psc_dma_bcom_enqueue_tx(s);
+	spin_unlock(&s->psc_dma->lock);
+
+	/* If the stream is active, then also inform the PCM middle layer
+	 * of the period finished event. */
+	if (s->active)
+		snd_pcm_period_elapsed(s->stream);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t psc_dma_bcom_irq_rx(int irq, void *_psc_dma_stream)
+{
+	struct psc_dma_stream *s = _psc_dma_stream;
+
+	spin_lock(&s->psc_dma->lock);
+	/* For each finished period, dequeue the completed period buffer
+	 * and enqueue a new one in it's place. */
+	while (bcom_buffer_done(s->bcom_task)) {
+		bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
+
+		s->period_current_pt += s->period_bytes;
+		if (s->period_current_pt >= s->period_end)
+			s->period_current_pt = s->period_start;
+
+		psc_dma_bcom_enqueue_next_buffer(s);
+	}
+	spin_unlock(&s->psc_dma->lock);
+
+	/* If the stream is active, then also inform the PCM middle layer
+	 * of the period finished event. */
+	if (s->active)
+		snd_pcm_period_elapsed(s->stream);
+
+	return IRQ_HANDLED;
+}
+
+static int psc_dma_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_set_runtime_buffer(substream, NULL);
+	return 0;
+}
+
+/**
+ * psc_dma_trigger: start and stop the DMA transfer.
+ *
+ * This function is called by ALSA to start, stop, pause, and resume the DMA
+ * transfer of data.
+ */
+static int psc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct psc_dma_stream *s;
+	struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+	u16 imr;
+	unsigned long flags;
+	int i;
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+		s = &psc_dma->capture;
+	else
+		s = &psc_dma->playback;
+
+	dev_dbg(psc_dma->dev, "psc_dma_trigger(substream=%p, cmd=%i)"
+		" stream_id=%i\n",
+		substream, cmd, substream->pstr->stream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		s->period_bytes = frames_to_bytes(runtime,
+						  runtime->period_size);
+		s->period_start = virt_to_phys(runtime->dma_area);
+		s->period_end = s->period_start +
+				(s->period_bytes * runtime->periods);
+		s->period_next_pt = s->period_start;
+		s->period_current_pt = s->period_start;
+		s->period_size = runtime->period_size;
+		s->active = 1;
+
+		/* track appl_ptr so that we have a better chance of detecting
+		 * end of stream and not over running it.
+		 */
+		s->runtime = runtime;
+		s->appl_ptr = s->runtime->control->appl_ptr -
+				(runtime->period_size * runtime->periods);
+
+		/* Fill up the bestcomm bd queue and enable DMA.
+		 * This will begin filling the PSC's fifo.
+		 */
+		spin_lock_irqsave(&psc_dma->lock, flags);
+
+		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+			bcom_gen_bd_rx_reset(s->bcom_task);
+			for (i = 0; i < runtime->periods; i++)
+				if (!bcom_queue_full(s->bcom_task))
+					psc_dma_bcom_enqueue_next_buffer(s);
+		} else {
+			bcom_gen_bd_tx_reset(s->bcom_task);
+			psc_dma_bcom_enqueue_tx(s);
+		}
+
+		bcom_enable(s->bcom_task);
+		spin_unlock_irqrestore(&psc_dma->lock, flags);
+
+		out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
+
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		s->active = 0;
+
+		spin_lock_irqsave(&psc_dma->lock, flags);
+		bcom_disable(s->bcom_task);
+		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+			bcom_gen_bd_rx_reset(s->bcom_task);
+		else
+			bcom_gen_bd_tx_reset(s->bcom_task);
+		spin_unlock_irqrestore(&psc_dma->lock, flags);
+
+		break;
+
+	default:
+		dev_dbg(psc_dma->dev, "invalid command\n");
+		return -EINVAL;
+	}
+
+	/* Update interrupt enable settings */
+	imr = 0;
+	if (psc_dma->playback.active)
+		imr |= MPC52xx_PSC_IMR_TXEMP;
+	if (psc_dma->capture.active)
+		imr |= MPC52xx_PSC_IMR_ORERR;
+	out_be16(&regs->isr_imr.imr, psc_dma->imr | imr);
+
+	return 0;
+}
+
+
+/* ---------------------------------------------------------------------
+ * The PSC DMA 'ASoC platform' driver
+ *
+ * Can be referenced by an 'ASoC machine' driver
+ * This driver only deals with the audio bus; it doesn't have any
+ * interaction with the attached codec
+ */
+
+static const struct snd_pcm_hardware psc_dma_hardware = {
+	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+		SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_BATCH,
+	.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
+		SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
+	.rate_min = 8000,
+	.rate_max = 48000,
+	.channels_min = 1,
+	.channels_max = 2,
+	.period_bytes_max	= 1024 * 1024,
+	.period_bytes_min	= 32,
+	.periods_min		= 2,
+	.periods_max		= 256,
+	.buffer_bytes_max	= 2 * 1024 * 1024,
+	.fifo_size		= 512,
+};
+
+static int psc_dma_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+	struct psc_dma_stream *s;
+	int rc;
+
+	dev_dbg(psc_dma->dev, "psc_dma_open(substream=%p)\n", substream);
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+		s = &psc_dma->capture;
+	else
+		s = &psc_dma->playback;
+
+	snd_soc_set_runtime_hwparams(substream, &psc_dma_hardware);
+
+	rc = snd_pcm_hw_constraint_integer(runtime,
+		SNDRV_PCM_HW_PARAM_PERIODS);
+	if (rc < 0) {
+		dev_err(substream->pcm->card->dev, "invalid buffer size\n");
+		return rc;
+	}
+
+	s->stream = substream;
+	return 0;
+}
+
+static int psc_dma_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+	struct psc_dma_stream *s;
+
+	dev_dbg(psc_dma->dev, "psc_dma_close(substream=%p)\n", substream);
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+		s = &psc_dma->capture;
+	else
+		s = &psc_dma->playback;
+
+	if (!psc_dma->playback.active &&
+	    !psc_dma->capture.active) {
+
+		/* Disable all interrupts and reset the PSC */
+		out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
+		out_8(&psc_dma->psc_regs->command, 4 << 4); /* reset error */
+	}
+	s->stream = NULL;
+	return 0;
+}
+
+static snd_pcm_uframes_t
+psc_dma_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+	struct psc_dma_stream *s;
+	dma_addr_t count;
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+		s = &psc_dma->capture;
+	else
+		s = &psc_dma->playback;
+
+	count = s->period_current_pt - s->period_start;
+
+	return bytes_to_frames(substream->runtime, count);
+}
+
+static int
+psc_dma_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params)
+{
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	return 0;
+}
+
+static struct snd_pcm_ops psc_dma_ops = {
+	.open		= psc_dma_open,
+	.close		= psc_dma_close,
+	.hw_free	= psc_dma_hw_free,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.pointer	= psc_dma_pointer,
+	.trigger	= psc_dma_trigger,
+	.hw_params	= psc_dma_hw_params,
+};
+
+static u64 psc_dma_dmamask = 0xffffffff;
+static int psc_dma_new(struct snd_card *card, struct snd_soc_dai *dai,
+			   struct snd_pcm *pcm)
+{
+	struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+	struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+	size_t size = psc_dma_hardware.buffer_bytes_max;
+	int rc = 0;
+
+	dev_dbg(rtd->socdev->dev, "psc_dma_new(card=%p, dai=%p, pcm=%p)\n",
+		card, dai, pcm);
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &psc_dma_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (pcm->streams[0].substream) {
+		rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
+				size, &pcm->streams[0].substream->dma_buffer);
+		if (rc)
+			goto playback_alloc_err;
+	}
+
+	if (pcm->streams[1].substream) {
+		rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
+				size, &pcm->streams[1].substream->dma_buffer);
+		if (rc)
+			goto capture_alloc_err;
+	}
+
+	if (rtd->socdev->card->codec->ac97)
+		rtd->socdev->card->codec->ac97->private_data = psc_dma;
+
+	return 0;
+
+ capture_alloc_err:
+	if (pcm->streams[0].substream)
+		snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
+
+ playback_alloc_err:
+	dev_err(card->dev, "Cannot allocate buffer(s)\n");
+
+	return -ENOMEM;
+}
+
+static void psc_dma_free(struct snd_pcm *pcm)
+{
+	struct snd_soc_pcm_runtime *rtd = pcm->private_data;
+	struct snd_pcm_substream *substream;
+	int stream;
+
+	dev_dbg(rtd->socdev->dev, "psc_dma_free(pcm=%p)\n", pcm);
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (substream) {
+			snd_dma_free_pages(&substream->dma_buffer);
+			substream->dma_buffer.area = NULL;
+			substream->dma_buffer.addr = 0;
+		}
+	}
+}
+
+struct snd_soc_platform mpc5200_audio_dma_platform = {
+	.name		= "mpc5200-psc-audio",
+	.pcm_ops	= &psc_dma_ops,
+	.pcm_new	= &psc_dma_new,
+	.pcm_free	= &psc_dma_free,
+};
+EXPORT_SYMBOL_GPL(mpc5200_audio_dma_platform);
+
+int mpc5200_audio_dma_create(struct of_device *op)
+{
+	phys_addr_t fifo;
+	struct psc_dma *psc_dma;
+	struct resource res;
+	int size, irq, rc;
+	const __be32 *prop;
+	void __iomem *regs;
+
+	/* Fetch the registers and IRQ of the PSC */
+	irq = irq_of_parse_and_map(op->node, 0);
+	if (of_address_to_resource(op->node, 0, &res)) {
+		dev_err(&op->dev, "Missing reg property\n");
+		return -ENODEV;
+	}
+	regs = ioremap(res.start, 1 + res.end - res.start);
+	if (!regs) {
+		dev_err(&op->dev, "Could not map registers\n");
+		return -ENODEV;
+	}
+
+	/* Allocate and initialize the driver private data */
+	psc_dma = kzalloc(sizeof *psc_dma, GFP_KERNEL);
+	if (!psc_dma) {
+		iounmap(regs);
+		return -ENOMEM;
+	}
+
+	/* Get the PSC ID */
+	prop = of_get_property(op->node, "cell-index", &size);
+	if (!prop || size < sizeof *prop)
+		return -ENODEV;
+
+	spin_lock_init(&psc_dma->lock);
+	mutex_init(&psc_dma->mutex);
+	psc_dma->id = be32_to_cpu(*prop);
+	psc_dma->irq = irq;
+	psc_dma->psc_regs = regs;
+	psc_dma->fifo_regs = regs + sizeof *psc_dma->psc_regs;
+	psc_dma->dev = &op->dev;
+	psc_dma->playback.psc_dma = psc_dma;
+	psc_dma->capture.psc_dma = psc_dma;
+	snprintf(psc_dma->name, sizeof psc_dma->name, "PSC%u", psc_dma->id);
+
+	/* Find the address of the fifo data registers and setup the
+	 * DMA tasks */
+	fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32);
+	psc_dma->capture.bcom_task =
+		bcom_psc_gen_bd_rx_init(psc_dma->id, 10, fifo, 512);
+	psc_dma->playback.bcom_task =
+		bcom_psc_gen_bd_tx_init(psc_dma->id, 10, fifo);
+	if (!psc_dma->capture.bcom_task ||
+	    !psc_dma->playback.bcom_task) {
+		dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
+		iounmap(regs);
+		kfree(psc_dma);
+		return -ENODEV;
+	}
+
+	/* Disable all interrupts and reset the PSC */
+	out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
+	 /* reset receiver */
+	out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_RX);
+	 /* reset transmitter */
+	out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_TX);
+	 /* reset error */
+	out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_RST_ERR_STAT);
+	 /* reset mode */
+	out_8(&psc_dma->psc_regs->command, MPC52xx_PSC_SEL_MODE_REG_1);
+
+	/* Set up mode register;
+	 * First write: RxRdy (FIFO Alarm) generates rx FIFO irq
+	 * Second write: register Normal mode for non loopback
+	 */
+	out_8(&psc_dma->psc_regs->mode, 0);
+	out_8(&psc_dma->psc_regs->mode, 0);
+
+	/* Set the TX and RX fifo alarm thresholds */
+	out_be16(&psc_dma->fifo_regs->rfalarm, 0x100);
+	out_8(&psc_dma->fifo_regs->rfcntl, 0x4);
+	out_be16(&psc_dma->fifo_regs->tfalarm, 0x100);
+	out_8(&psc_dma->fifo_regs->tfcntl, 0x7);
+
+	/* Lookup the IRQ numbers */
+	psc_dma->playback.irq =
+		bcom_get_task_irq(psc_dma->playback.bcom_task);
+	psc_dma->capture.irq =
+		bcom_get_task_irq(psc_dma->capture.bcom_task);
+
+	rc = request_irq(psc_dma->irq, &psc_dma_status_irq, IRQF_SHARED,
+			 "psc-dma-status", psc_dma);
+	rc |= request_irq(psc_dma->capture.irq,
+			  &psc_dma_bcom_irq_rx, IRQF_SHARED,
+			  "psc-dma-capture", &psc_dma->capture);
+	rc |= request_irq(psc_dma->playback.irq,
+			  &psc_dma_bcom_irq_tx, IRQF_SHARED,
+			  "psc-dma-playback", &psc_dma->playback);
+	if (rc) {
+		free_irq(psc_dma->irq, psc_dma);
+		free_irq(psc_dma->capture.irq,
+			 &psc_dma->capture);
+		free_irq(psc_dma->playback.irq,
+			 &psc_dma->playback);
+		return -ENODEV;
+	}
+
+	/* Save what we've done so it can be found again later */
+	dev_set_drvdata(&op->dev, psc_dma);
+
+	/* Tell the ASoC OF helpers about it */
+	return snd_soc_register_platform(&mpc5200_audio_dma_platform);
+}
+EXPORT_SYMBOL_GPL(mpc5200_audio_dma_create);
+
+int mpc5200_audio_dma_destroy(struct of_device *op)
+{
+	struct psc_dma *psc_dma = dev_get_drvdata(&op->dev);
+
+	dev_dbg(&op->dev, "mpc5200_audio_dma_destroy()\n");
+
+	snd_soc_unregister_platform(&mpc5200_audio_dma_platform);
+
+	bcom_gen_bd_rx_release(psc_dma->capture.bcom_task);
+	bcom_gen_bd_tx_release(psc_dma->playback.bcom_task);
+
+	/* Release irqs */
+	free_irq(psc_dma->irq, psc_dma);
+	free_irq(psc_dma->capture.irq, &psc_dma->capture);
+	free_irq(psc_dma->playback.irq, &psc_dma->playback);
+
+	iounmap(psc_dma->psc_regs);
+	kfree(psc_dma);
+	dev_set_drvdata(&op->dev, NULL);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mpc5200_audio_dma_destroy);
+
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("Freescale MPC5200 PSC in DMA mode ASoC Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/mpc5200_dma.h b/sound/soc/fsl/mpc5200_dma.h
new file mode 100644
index 0000000..8d396bb
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_dma.h
@@ -0,0 +1,81 @@
+/*
+ * Freescale MPC5200 Audio DMA driver
+ */
+
+#ifndef __SOUND_SOC_FSL_MPC5200_DMA_H__
+#define __SOUND_SOC_FSL_MPC5200_DMA_H__
+
+#define PSC_STREAM_NAME_LEN 32
+
+/**
+ * psc_ac97_stream - Data specific to a single stream (playback or capture)
+ * @active:		flag indicating if the stream is active
+ * @psc_dma:		pointer back to parent psc_dma data structure
+ * @bcom_task:		bestcomm task structure
+ * @irq:		irq number for bestcomm task
+ * @period_start:	physical address of start of DMA region
+ * @period_end:		physical address of end of DMA region
+ * @period_next_pt:	physical address of next DMA buffer to enqueue
+ * @period_bytes:	size of DMA period in bytes
+ */
+struct psc_dma_stream {
+	struct snd_pcm_runtime *runtime;
+	snd_pcm_uframes_t appl_ptr;
+
+	int active;
+	struct psc_dma *psc_dma;
+	struct bcom_task *bcom_task;
+	int irq;
+	struct snd_pcm_substream *stream;
+	dma_addr_t period_start;
+	dma_addr_t period_end;
+	dma_addr_t period_next_pt;
+	dma_addr_t period_current_pt;
+	int period_bytes;
+	int period_size;
+};
+
+/**
+ * psc_dma - Private driver data
+ * @name: short name for this device ("PSC0", "PSC1", etc)
+ * @psc_regs: pointer to the PSC's registers
+ * @fifo_regs: pointer to the PSC's FIFO registers
+ * @irq: IRQ of this PSC
+ * @dev: struct device pointer
+ * @dai: the CPU DAI for this device
+ * @sicr: Base value used in serial interface control register; mode is ORed
+ *        with this value.
+ * @playback: Playback stream context data
+ * @capture: Capture stream context data
+ */
+struct psc_dma {
+	char name[32];
+	struct mpc52xx_psc __iomem *psc_regs;
+	struct mpc52xx_psc_fifo __iomem *fifo_regs;
+	unsigned int irq;
+	struct device *dev;
+	spinlock_t lock;
+	struct mutex mutex;
+	u32 sicr;
+	uint sysclk;
+	int imr;
+	int id;
+	unsigned int slots;
+
+	/* per-stream data */
+	struct psc_dma_stream playback;
+	struct psc_dma_stream capture;
+
+	/* Statistics */
+	struct {
+		unsigned long overrun_count;
+		unsigned long underrun_count;
+	} stats;
+};
+
+int mpc5200_audio_dma_create(struct of_device *op);
+int mpc5200_audio_dma_destroy(struct of_device *op);
+
+extern struct snd_soc_platform mpc5200_audio_dma_platform;
+
+#endif /* __SOUND_SOC_FSL_MPC5200_DMA_H__ */
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
new file mode 100644
index 0000000..c4ae3e0
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -0,0 +1,345 @@
+/*
+ * linux/sound/mpc5200-ac97.c -- AC97 support for the Freescale MPC52xx chip.
+ *
+ * Copyright (C) 2009 Jon Smirl, Digispeaker
+ * Author: Jon Smirl <jonsmirl@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/delay.h>
+
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/time.h>
+#include <asm/delay.h>
+#include <asm/mpc52xx_psc.h>
+
+#include "mpc5200_dma.h"
+#include "mpc5200_psc_ac97.h"
+
+#define DRV_NAME "mpc5200-psc-ac97"
+
+/* ALSA only supports a single AC97 device so static is recommend here */
+static struct psc_dma *psc_dma;
+
+static unsigned short psc_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+	int status;
+	unsigned int val;
+
+	mutex_lock(&psc_dma->mutex);
+
+	/* Wait for command send status zero = ready */
+	status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) &
+				MPC52xx_PSC_SR_CMDSEND), 100, 0);
+	if (status == 0) {
+		pr_err("timeout on ac97 bus (rdy)\n");
+		mutex_unlock(&psc_dma->mutex);
+		return -ENODEV;
+	}
+
+	/* Force clear the data valid bit */
+	in_be32(&psc_dma->psc_regs->ac97_data);
+
+	/* Send the read */
+	out_be32(&psc_dma->psc_regs->ac97_cmd, (1<<31) | ((reg & 0x7f) << 24));
+
+	/* Wait for the answer */
+	status = spin_event_timeout((in_be16(&psc_dma->psc_regs->sr_csr.status) &
+				MPC52xx_PSC_SR_DATA_VAL), 100, 0);
+	if (status == 0) {
+		pr_err("timeout on ac97 read (val) %x\n",
+				in_be16(&psc_dma->psc_regs->sr_csr.status));
+		mutex_unlock(&psc_dma->mutex);
+		return -ENODEV;
+	}
+	/* Get the data */
+	val = in_be32(&psc_dma->psc_regs->ac97_data);
+	if (((val >> 24) & 0x7f) != reg) {
+		pr_err("reg echo error on ac97 read\n");
+		mutex_unlock(&psc_dma->mutex);
+		return -ENODEV;
+	}
+	val = (val >> 8) & 0xffff;
+
+	mutex_unlock(&psc_dma->mutex);
+	return (unsigned short) val;
+}
+
+static void psc_ac97_write(struct snd_ac97 *ac97,
+				unsigned short reg, unsigned short val)
+{
+	int status;
+
+	mutex_lock(&psc_dma->mutex);
+
+	/* Wait for command status zero = ready */
+	status = spin_event_timeout(!(in_be16(&psc_dma->psc_regs->sr_csr.status) &
+				MPC52xx_PSC_SR_CMDSEND), 100, 0);
+	if (status == 0) {
+		pr_err("timeout on ac97 bus (write)\n");
+		goto out;
+	}
+	/* Write data */
+	out_be32(&psc_dma->psc_regs->ac97_cmd,
+			((reg & 0x7f) << 24) | (val << 8));
+
+ out:
+	mutex_unlock(&psc_dma->mutex);
+}
+
+static void psc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+	struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+
+	out_be32(&regs->sicr, psc_dma->sicr | MPC52xx_PSC_SICR_AWR);
+	udelay(3);
+	out_be32(&regs->sicr, psc_dma->sicr);
+}
+
+static void psc_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+	struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+
+	/* Do a cold reset */
+	out_8(&regs->op1, MPC52xx_PSC_OP_RES);
+	udelay(10);
+	out_8(&regs->op0, MPC52xx_PSC_OP_RES);
+	msleep(1);
+	psc_ac97_warm_reset(ac97);
+}
+
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read		= psc_ac97_read,
+	.write		= psc_ac97_write,
+	.reset		= psc_ac97_cold_reset,
+	.warm_reset	= psc_ac97_warm_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static int psc_ac97_hw_analog_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *cpu_dai)
+{
+	struct psc_dma *psc_dma = cpu_dai->private_data;
+
+	dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
+		" periods=%i buffer_size=%i  buffer_bytes=%i channels=%i"
+		" rate=%i format=%i\n",
+		__func__, substream, params_period_size(params),
+		params_period_bytes(params), params_periods(params),
+		params_buffer_size(params), params_buffer_bytes(params),
+		params_channels(params), params_rate(params),
+		params_format(params));
+
+
+	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		if (params_channels(params) == 1)
+			psc_dma->slots |= 0x00000100;
+		else
+			psc_dma->slots |= 0x00000300;
+	} else {
+		if (params_channels(params) == 1)
+			psc_dma->slots |= 0x01000000;
+		else
+			psc_dma->slots |= 0x03000000;
+	}
+	out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots);
+
+	return 0;
+}
+
+static int psc_ac97_hw_digital_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params,
+				 struct snd_soc_dai *cpu_dai)
+{
+	struct psc_dma *psc_dma = cpu_dai->private_data;
+
+	if (params_channels(params) == 1)
+		out_be32(&psc_dma->psc_regs->ac97_slots, 0x01000000);
+	else
+		out_be32(&psc_dma->psc_regs->ac97_slots, 0x03000000);
+
+	return 0;
+}
+
+static int psc_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+							struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_STOP:
+		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
+			psc_dma->slots &= 0xFFFF0000;
+		else
+			psc_dma->slots &= 0x0000FFFF;
+
+		out_be32(&psc_dma->psc_regs->ac97_slots, psc_dma->slots);
+		break;
+	}
+	return 0;
+}
+
+static int psc_ac97_probe(struct platform_device *pdev,
+					struct snd_soc_dai *cpu_dai)
+{
+	struct psc_dma *psc_dma = cpu_dai->private_data;
+	struct mpc52xx_psc __iomem *regs = psc_dma->psc_regs;
+
+	/* Go */
+	out_8(&regs->command, MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
+	return 0;
+}
+
+/* ---------------------------------------------------------------------
+ * ALSA SoC Bindings
+ *
+ * - Digital Audio Interface (DAI) template
+ * - create/destroy dai hooks
+ */
+
+/**
+ * psc_ac97_dai_template: template CPU Digital Audio Interface
+ */
+static struct snd_soc_dai_ops psc_ac97_analog_ops = {
+	.hw_params	= psc_ac97_hw_analog_params,
+	.trigger	= psc_ac97_trigger,
+};
+
+static struct snd_soc_dai_ops psc_ac97_digital_ops = {
+	.hw_params	= psc_ac97_hw_digital_params,
+};
+
+struct snd_soc_dai psc_ac97_dai[] = {
+{
+	.name   = "AC97",
+	.ac97_control = 1,
+	.probe	= psc_ac97_probe,
+	.playback = {
+		.channels_min   = 1,
+		.channels_max   = 6,
+		.rates          = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_BE,
+	},
+	.capture = {
+		.channels_min   = 1,
+		.channels_max   = 2,
+		.rates          = SNDRV_PCM_RATE_8000_48000,
+		.formats = SNDRV_PCM_FMTBIT_S32_BE,
+	},
+	.ops = &psc_ac97_analog_ops,
+},
+{
+	.name   = "SPDIF",
+	.ac97_control = 1,
+	.playback = {
+		.channels_min   = 1,
+		.channels_max   = 2,
+		.rates          = SNDRV_PCM_RATE_32000 | \
+			SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE,
+	},
+	.ops = &psc_ac97_digital_ops,
+} };
+EXPORT_SYMBOL_GPL(psc_ac97_dai);
+
+
+
+/* ---------------------------------------------------------------------
+ * OF platform bus binding code:
+ * - Probe/remove operations
+ * - OF device match table
+ */
+static int __devinit psc_ac97_of_probe(struct of_device *op,
+				      const struct of_device_id *match)
+{
+	int rc, i;
+	struct snd_ac97 ac97;
+	struct mpc52xx_psc __iomem *regs;
+
+	rc = mpc5200_audio_dma_create(op);
+	if (rc != 0)
+		return rc;
+
+	for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++)
+		psc_ac97_dai[i].dev = &op->dev;
+
+	rc = snd_soc_register_dais(psc_ac97_dai, ARRAY_SIZE(psc_ac97_dai));
+	if (rc != 0) {
+		dev_err(&op->dev, "Failed to register DAI\n");
+		return rc;
+	}
+
+	psc_dma = dev_get_drvdata(&op->dev);
+	regs = psc_dma->psc_regs;
+	ac97.private_data = psc_dma;
+
+	for (i = 0; i < ARRAY_SIZE(psc_ac97_dai); i++)
+		psc_ac97_dai[i].private_data = psc_dma;
+
+	psc_dma->imr = 0;
+	out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
+
+	/* Configure the serial interface mode to AC97 */
+	psc_dma->sicr = MPC52xx_PSC_SICR_SIM_AC97 | MPC52xx_PSC_SICR_ENAC97;
+	out_be32(&regs->sicr, psc_dma->sicr);
+
+	/* No slots active */
+	out_be32(&regs->ac97_slots, 0x00000000);
+
+	return 0;
+}
+
+static int __devexit psc_ac97_of_remove(struct of_device *op)
+{
+	return mpc5200_audio_dma_destroy(op);
+}
+
+/* Match table for of_platform binding */
+static struct of_device_id psc_ac97_match[] __devinitdata = {
+	{ .compatible = "fsl,mpc5200-psc-ac97", },
+	{ .compatible = "fsl,mpc5200b-psc-ac97", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, psc_ac97_match);
+
+static struct of_platform_driver psc_ac97_driver = {
+	.match_table = psc_ac97_match,
+	.probe = psc_ac97_of_probe,
+	.remove = __devexit_p(psc_ac97_of_remove),
+	.driver = {
+		.name = "mpc5200-psc-ac97",
+		.owner = THIS_MODULE,
+	},
+};
+
+/* ---------------------------------------------------------------------
+ * Module setup and teardown; simply register the of_platform driver
+ * for the PSC in AC97 mode.
+ */
+static int __init psc_ac97_init(void)
+{
+	return of_register_platform_driver(&psc_ac97_driver);
+}
+module_init(psc_ac97_init);
+
+static void __exit psc_ac97_exit(void)
+{
+	of_unregister_platform_driver(&psc_ac97_driver);
+}
+module_exit(psc_ac97_exit);
+
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_DESCRIPTION("mpc5200 AC97 module");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.h b/sound/soc/fsl/mpc5200_psc_ac97.h
new file mode 100644
index 0000000..4bc18c3
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_psc_ac97.h
@@ -0,0 +1,15 @@
+/*
+ * Freescale MPC5200 PSC in AC97 mode
+ * ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ */
+
+#ifndef __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__
+#define __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__
+
+extern struct snd_soc_dai psc_ac97_dai[];
+
+#define MPC5200_AC97_NORMAL 0
+#define MPC5200_AC97_SPDIF 1
+
+#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_AC97_H__ */
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c
index 1111c71..ce8de90 100644
--- a/sound/soc/fsl/mpc5200_psc_i2s.c
+++ b/sound/soc/fsl/mpc5200_psc_i2s.c
@@ -3,31 +3,21 @@
  * ALSA SoC Digital Audio Interface (DAI) driver
  *
  * Copyright (C) 2008 Secret Lab Technologies Ltd.
+ * Copyright (C) 2009 Jon Smirl, Digispeaker
  */
 
-#include <linux/init.h>
 #include <linux/module.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/delay.h>
 #include <linux/of_device.h>
 #include <linux/of_platform.h>
-#include <linux/dma-mapping.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-of-simple.h>
 
-#include <sysdev/bestcomm/bestcomm.h>
-#include <sysdev/bestcomm/gen_bd.h>
 #include <asm/mpc52xx_psc.h>
 
-MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
-MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
-MODULE_LICENSE("GPL");
+#include "mpc5200_psc_i2s.h"
+#include "mpc5200_dma.h"
 
 /**
  * PSC_I2S_RATES: sample rates supported by the I2S
@@ -44,191 +34,17 @@
  * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
  */
 #define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
-			 SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S24_BE | \
-			 SNDRV_PCM_FMTBIT_S32_BE)
-
-/**
- * psc_i2s_stream - Data specific to a single stream (playback or capture)
- * @active:		flag indicating if the stream is active
- * @psc_i2s:		pointer back to parent psc_i2s data structure
- * @bcom_task:		bestcomm task structure
- * @irq:		irq number for bestcomm task
- * @period_start:	physical address of start of DMA region
- * @period_end:		physical address of end of DMA region
- * @period_next_pt:	physical address of next DMA buffer to enqueue
- * @period_bytes:	size of DMA period in bytes
- */
-struct psc_i2s_stream {
-	int active;
-	struct psc_i2s *psc_i2s;
-	struct bcom_task *bcom_task;
-	int irq;
-	struct snd_pcm_substream *stream;
-	dma_addr_t period_start;
-	dma_addr_t period_end;
-	dma_addr_t period_next_pt;
-	dma_addr_t period_current_pt;
-	int period_bytes;
-};
-
-/**
- * psc_i2s - Private driver data
- * @name: short name for this device ("PSC0", "PSC1", etc)
- * @psc_regs: pointer to the PSC's registers
- * @fifo_regs: pointer to the PSC's FIFO registers
- * @irq: IRQ of this PSC
- * @dev: struct device pointer
- * @dai: the CPU DAI for this device
- * @sicr: Base value used in serial interface control register; mode is ORed
- *        with this value.
- * @playback: Playback stream context data
- * @capture: Capture stream context data
- */
-struct psc_i2s {
-	char name[32];
-	struct mpc52xx_psc __iomem *psc_regs;
-	struct mpc52xx_psc_fifo __iomem *fifo_regs;
-	unsigned int irq;
-	struct device *dev;
-	struct snd_soc_dai dai;
-	spinlock_t lock;
-	u32 sicr;
-
-	/* per-stream data */
-	struct psc_i2s_stream playback;
-	struct psc_i2s_stream capture;
-
-	/* Statistics */
-	struct {
-		int overrun_count;
-		int underrun_count;
-	} stats;
-};
-
-/*
- * Interrupt handlers
- */
-static irqreturn_t psc_i2s_status_irq(int irq, void *_psc_i2s)
-{
-	struct psc_i2s *psc_i2s = _psc_i2s;
-	struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
-	u16 isr;
-
-	isr = in_be16(&regs->mpc52xx_psc_isr);
-
-	/* Playback underrun error */
-	if (psc_i2s->playback.active && (isr & MPC52xx_PSC_IMR_TXEMP))
-		psc_i2s->stats.underrun_count++;
-
-	/* Capture overrun error */
-	if (psc_i2s->capture.active && (isr & MPC52xx_PSC_IMR_ORERR))
-		psc_i2s->stats.overrun_count++;
-
-	out_8(&regs->command, 4 << 4);	/* reset the error status */
-
-	return IRQ_HANDLED;
-}
-
-/**
- * psc_i2s_bcom_enqueue_next_buffer - Enqueue another audio buffer
- * @s: pointer to stream private data structure
- *
- * Enqueues another audio period buffer into the bestcomm queue.
- *
- * Note: The routine must only be called when there is space available in
- * the queue.  Otherwise the enqueue will fail and the audio ring buffer
- * will get out of sync
- */
-static void psc_i2s_bcom_enqueue_next_buffer(struct psc_i2s_stream *s)
-{
-	struct bcom_bd *bd;
-
-	/* Prepare and enqueue the next buffer descriptor */
-	bd = bcom_prepare_next_buffer(s->bcom_task);
-	bd->status = s->period_bytes;
-	bd->data[0] = s->period_next_pt;
-	bcom_submit_next_buffer(s->bcom_task, NULL);
-
-	/* Update for next period */
-	s->period_next_pt += s->period_bytes;
-	if (s->period_next_pt >= s->period_end)
-		s->period_next_pt = s->period_start;
-}
-
-/* Bestcomm DMA irq handler */
-static irqreturn_t psc_i2s_bcom_irq(int irq, void *_psc_i2s_stream)
-{
-	struct psc_i2s_stream *s = _psc_i2s_stream;
-
-	/* For each finished period, dequeue the completed period buffer
-	 * and enqueue a new one in it's place. */
-	while (bcom_buffer_done(s->bcom_task)) {
-		bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
-		s->period_current_pt += s->period_bytes;
-		if (s->period_current_pt >= s->period_end)
-			s->period_current_pt = s->period_start;
-		psc_i2s_bcom_enqueue_next_buffer(s);
-		bcom_enable(s->bcom_task);
-	}
-
-	/* If the stream is active, then also inform the PCM middle layer
-	 * of the period finished event. */
-	if (s->active)
-		snd_pcm_period_elapsed(s->stream);
-
-	return IRQ_HANDLED;
-}
-
-/**
- * psc_i2s_startup: create a new substream
- *
- * This is the first function called when a stream is opened.
- *
- * If this is the first stream open, then grab the IRQ and program most of
- * the PSC registers.
- */
-static int psc_i2s_startup(struct snd_pcm_substream *substream,
-			   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-	int rc;
-
-	dev_dbg(psc_i2s->dev, "psc_i2s_startup(substream=%p)\n", substream);
-
-	if (!psc_i2s->playback.active &&
-	    !psc_i2s->capture.active) {
-		/* Setup the IRQs */
-		rc = request_irq(psc_i2s->irq, &psc_i2s_status_irq, IRQF_SHARED,
-				 "psc-i2s-status", psc_i2s);
-		rc |= request_irq(psc_i2s->capture.irq,
-				  &psc_i2s_bcom_irq, IRQF_SHARED,
-				  "psc-i2s-capture", &psc_i2s->capture);
-		rc |= request_irq(psc_i2s->playback.irq,
-				  &psc_i2s_bcom_irq, IRQF_SHARED,
-				  "psc-i2s-playback", &psc_i2s->playback);
-		if (rc) {
-			free_irq(psc_i2s->irq, psc_i2s);
-			free_irq(psc_i2s->capture.irq,
-				 &psc_i2s->capture);
-			free_irq(psc_i2s->playback.irq,
-				 &psc_i2s->playback);
-			return -ENODEV;
-		}
-	}
-
-	return 0;
-}
+			 SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
 
 static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
 				 struct snd_pcm_hw_params *params,
 				 struct snd_soc_dai *dai)
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
+	struct psc_dma *psc_dma = rtd->dai->cpu_dai->private_data;
 	u32 mode;
 
-	dev_dbg(psc_i2s->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
+	dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
 		" periods=%i buffer_size=%i  buffer_bytes=%i\n",
 		__func__, substream, params_period_size(params),
 		params_period_bytes(params), params_periods(params),
@@ -248,174 +64,14 @@
 		mode = MPC52xx_PSC_SICR_SIM_CODEC_32;
 		break;
 	default:
-		dev_dbg(psc_i2s->dev, "invalid format\n");
+		dev_dbg(psc_dma->dev, "invalid format\n");
 		return -EINVAL;
 	}
-	out_be32(&psc_i2s->psc_regs->sicr, psc_i2s->sicr | mode);
-
-	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode);
 
 	return 0;
 }
 
-static int psc_i2s_hw_free(struct snd_pcm_substream *substream,
-			   struct snd_soc_dai *dai)
-{
-	snd_pcm_set_runtime_buffer(substream, NULL);
-	return 0;
-}
-
-/**
- * psc_i2s_trigger: start and stop the DMA transfer.
- *
- * This function is called by ALSA to start, stop, pause, and resume the DMA
- * transfer of data.
- */
-static int psc_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
-			   struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-	struct snd_pcm_runtime *runtime = substream->runtime;
-	struct psc_i2s_stream *s;
-	struct mpc52xx_psc __iomem *regs = psc_i2s->psc_regs;
-	u16 imr;
-	u8 psc_cmd;
-	unsigned long flags;
-
-	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-		s = &psc_i2s->capture;
-	else
-		s = &psc_i2s->playback;
-
-	dev_dbg(psc_i2s->dev, "psc_i2s_trigger(substream=%p, cmd=%i)"
-		" stream_id=%i\n",
-		substream, cmd, substream->pstr->stream);
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		s->period_bytes = frames_to_bytes(runtime,
-						  runtime->period_size);
-		s->period_start = virt_to_phys(runtime->dma_area);
-		s->period_end = s->period_start +
-				(s->period_bytes * runtime->periods);
-		s->period_next_pt = s->period_start;
-		s->period_current_pt = s->period_start;
-		s->active = 1;
-
-		/* First; reset everything */
-		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
-			out_8(&regs->command, MPC52xx_PSC_RST_RX);
-			out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
-		} else {
-			out_8(&regs->command, MPC52xx_PSC_RST_TX);
-			out_8(&regs->command, MPC52xx_PSC_RST_ERR_STAT);
-		}
-
-		/* Next, fill up the bestcomm bd queue and enable DMA.
-		 * This will begin filling the PSC's fifo. */
-		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-			bcom_gen_bd_rx_reset(s->bcom_task);
-		else
-			bcom_gen_bd_tx_reset(s->bcom_task);
-		while (!bcom_queue_full(s->bcom_task))
-			psc_i2s_bcom_enqueue_next_buffer(s);
-		bcom_enable(s->bcom_task);
-
-		/* Due to errata in the i2s mode; need to line up enabling
-		 * the transmitter with a transition on the frame sync
-		 * line */
-
-		spin_lock_irqsave(&psc_i2s->lock, flags);
-		/* first make sure it is low */
-		while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0)
-			;
-		/* then wait for the transition to high */
-		while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0)
-			;
-		/* Finally, enable the PSC.
-		 * Receiver must always be enabled; even when we only want
-		 * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */
-		psc_cmd = MPC52xx_PSC_RX_ENABLE;
-		if (substream->pstr->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			psc_cmd |= MPC52xx_PSC_TX_ENABLE;
-		out_8(&regs->command, psc_cmd);
-		spin_unlock_irqrestore(&psc_i2s->lock, flags);
-
-		break;
-
-	case SNDRV_PCM_TRIGGER_STOP:
-		/* Turn off the PSC */
-		s->active = 0;
-		if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE) {
-			if (!psc_i2s->playback.active) {
-				out_8(&regs->command, 2 << 4);	/* reset rx */
-				out_8(&regs->command, 3 << 4);	/* reset tx */
-				out_8(&regs->command, 4 << 4);	/* reset err */
-			}
-		} else {
-			out_8(&regs->command, 3 << 4);	/* reset tx */
-			out_8(&regs->command, 4 << 4);	/* reset err */
-			if (!psc_i2s->capture.active)
-				out_8(&regs->command, 2 << 4);	/* reset rx */
-		}
-
-		bcom_disable(s->bcom_task);
-		while (!bcom_queue_empty(s->bcom_task))
-			bcom_retrieve_buffer(s->bcom_task, NULL, NULL);
-
-		break;
-
-	default:
-		dev_dbg(psc_i2s->dev, "invalid command\n");
-		return -EINVAL;
-	}
-
-	/* Update interrupt enable settings */
-	imr = 0;
-	if (psc_i2s->playback.active)
-		imr |= MPC52xx_PSC_IMR_TXEMP;
-	if (psc_i2s->capture.active)
-		imr |= MPC52xx_PSC_IMR_ORERR;
-	out_be16(&regs->isr_imr.imr, imr);
-
-	return 0;
-}
-
-/**
- * psc_i2s_shutdown: shutdown the data transfer on a stream
- *
- * Shutdown the PSC if there are no other substreams open.
- */
-static void psc_i2s_shutdown(struct snd_pcm_substream *substream,
-			     struct snd_soc_dai *dai)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-
-	dev_dbg(psc_i2s->dev, "psc_i2s_shutdown(substream=%p)\n", substream);
-
-	/*
-	 * If this is the last active substream, disable the PSC and release
-	 * the IRQ.
-	 */
-	if (!psc_i2s->playback.active &&
-	    !psc_i2s->capture.active) {
-
-		/* Disable all interrupts and reset the PSC */
-		out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0);
-		out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset tx */
-		out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset rx */
-		out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */
-		out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */
-
-		/* Release irqs */
-		free_irq(psc_i2s->irq, psc_i2s);
-		free_irq(psc_i2s->capture.irq, &psc_i2s->capture);
-		free_irq(psc_i2s->playback.irq, &psc_i2s->playback);
-	}
-}
-
 /**
  * psc_i2s_set_sysclk: set the clock frequency and direction
  *
@@ -433,8 +89,8 @@
 static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
 			      int clk_id, unsigned int freq, int dir)
 {
-	struct psc_i2s *psc_i2s = cpu_dai->private_data;
-	dev_dbg(psc_i2s->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
+	struct psc_dma *psc_dma = cpu_dai->private_data;
+	dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
 				cpu_dai, dir);
 	return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
 }
@@ -452,8 +108,8 @@
  */
 static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
 {
-	struct psc_i2s *psc_i2s = cpu_dai->private_data;
-	dev_dbg(psc_i2s->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
+	struct psc_dma *psc_dma = cpu_dai->private_data;
+	dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
 				cpu_dai, format);
 	return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
 }
@@ -469,16 +125,13 @@
  * psc_i2s_dai_template: template CPU Digital Audio Interface
  */
 static struct snd_soc_dai_ops psc_i2s_dai_ops = {
-	.startup	= psc_i2s_startup,
 	.hw_params	= psc_i2s_hw_params,
-	.hw_free	= psc_i2s_hw_free,
-	.shutdown	= psc_i2s_shutdown,
-	.trigger	= psc_i2s_trigger,
 	.set_sysclk	= psc_i2s_set_sysclk,
 	.set_fmt	= psc_i2s_set_fmt,
 };
 
-static struct snd_soc_dai psc_i2s_dai_template = {
+struct snd_soc_dai psc_i2s_dai[] = {{
+	.name   = "I2S",
 	.playback = {
 		.channels_min = 2,
 		.channels_max = 2,
@@ -492,223 +145,8 @@
 		.formats = PSC_I2S_FORMATS,
 	},
 	.ops = &psc_i2s_dai_ops,
-};
-
-/* ---------------------------------------------------------------------
- * The PSC I2S 'ASoC platform' driver
- *
- * Can be referenced by an 'ASoC machine' driver
- * This driver only deals with the audio bus; it doesn't have any
- * interaction with the attached codec
- */
-
-static const struct snd_pcm_hardware psc_i2s_pcm_hardware = {
-	.info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
-		SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
-		SNDRV_PCM_INFO_BATCH,
-	.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |
-		   SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE,
-	.rate_min = 8000,
-	.rate_max = 48000,
-	.channels_min = 2,
-	.channels_max = 2,
-	.period_bytes_max	= 1024 * 1024,
-	.period_bytes_min	= 32,
-	.periods_min		= 2,
-	.periods_max		= 256,
-	.buffer_bytes_max	= 2 * 1024 * 1024,
-	.fifo_size		= 0,
-};
-
-static int psc_i2s_pcm_open(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-	struct psc_i2s_stream *s;
-
-	dev_dbg(psc_i2s->dev, "psc_i2s_pcm_open(substream=%p)\n", substream);
-
-	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-		s = &psc_i2s->capture;
-	else
-		s = &psc_i2s->playback;
-
-	snd_soc_set_runtime_hwparams(substream, &psc_i2s_pcm_hardware);
-
-	s->stream = substream;
-	return 0;
-}
-
-static int psc_i2s_pcm_close(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-	struct psc_i2s_stream *s;
-
-	dev_dbg(psc_i2s->dev, "psc_i2s_pcm_close(substream=%p)\n", substream);
-
-	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-		s = &psc_i2s->capture;
-	else
-		s = &psc_i2s->playback;
-
-	s->stream = NULL;
-	return 0;
-}
-
-static snd_pcm_uframes_t
-psc_i2s_pcm_pointer(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct psc_i2s *psc_i2s = rtd->dai->cpu_dai->private_data;
-	struct psc_i2s_stream *s;
-	dma_addr_t count;
-
-	if (substream->pstr->stream == SNDRV_PCM_STREAM_CAPTURE)
-		s = &psc_i2s->capture;
-	else
-		s = &psc_i2s->playback;
-
-	count = s->period_current_pt - s->period_start;
-
-	return bytes_to_frames(substream->runtime, count);
-}
-
-static struct snd_pcm_ops psc_i2s_pcm_ops = {
-	.open		= psc_i2s_pcm_open,
-	.close		= psc_i2s_pcm_close,
-	.ioctl		= snd_pcm_lib_ioctl,
-	.pointer	= psc_i2s_pcm_pointer,
-};
-
-static u64 psc_i2s_pcm_dmamask = 0xffffffff;
-static int psc_i2s_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
-			   struct snd_pcm *pcm)
-{
-	struct snd_soc_pcm_runtime *rtd = pcm->private_data;
-	size_t size = psc_i2s_pcm_hardware.buffer_bytes_max;
-	int rc = 0;
-
-	dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_new(card=%p, dai=%p, pcm=%p)\n",
-		card, dai, pcm);
-
-	if (!card->dev->dma_mask)
-		card->dev->dma_mask = &psc_i2s_pcm_dmamask;
-	if (!card->dev->coherent_dma_mask)
-		card->dev->coherent_dma_mask = 0xffffffff;
-
-	if (pcm->streams[0].substream) {
-		rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
-					&pcm->streams[0].substream->dma_buffer);
-		if (rc)
-			goto playback_alloc_err;
-	}
-
-	if (pcm->streams[1].substream) {
-		rc = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->dev, size,
-					&pcm->streams[1].substream->dma_buffer);
-		if (rc)
-			goto capture_alloc_err;
-	}
-
-	return 0;
-
- capture_alloc_err:
-	if (pcm->streams[0].substream)
-		snd_dma_free_pages(&pcm->streams[0].substream->dma_buffer);
- playback_alloc_err:
-	dev_err(card->dev, "Cannot allocate buffer(s)\n");
-	return -ENOMEM;
-}
-
-static void psc_i2s_pcm_free(struct snd_pcm *pcm)
-{
-	struct snd_soc_pcm_runtime *rtd = pcm->private_data;
-	struct snd_pcm_substream *substream;
-	int stream;
-
-	dev_dbg(rtd->socdev->dev, "psc_i2s_pcm_free(pcm=%p)\n", pcm);
-
-	for (stream = 0; stream < 2; stream++) {
-		substream = pcm->streams[stream].substream;
-		if (substream) {
-			snd_dma_free_pages(&substream->dma_buffer);
-			substream->dma_buffer.area = NULL;
-			substream->dma_buffer.addr = 0;
-		}
-	}
-}
-
-struct snd_soc_platform psc_i2s_pcm_soc_platform = {
-	.name		= "mpc5200-psc-audio",
-	.pcm_ops	= &psc_i2s_pcm_ops,
-	.pcm_new	= &psc_i2s_pcm_new,
-	.pcm_free	= &psc_i2s_pcm_free,
-};
-
-/* ---------------------------------------------------------------------
- * Sysfs attributes for debugging
- */
-
-static ssize_t psc_i2s_status_show(struct device *dev,
-			   struct device_attribute *attr, char *buf)
-{
-	struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
-
-	return sprintf(buf, "status=%.4x sicr=%.8x rfnum=%i rfstat=0x%.4x "
-			"tfnum=%i tfstat=0x%.4x\n",
-			in_be16(&psc_i2s->psc_regs->sr_csr.status),
-			in_be32(&psc_i2s->psc_regs->sicr),
-			in_be16(&psc_i2s->fifo_regs->rfnum) & 0x1ff,
-			in_be16(&psc_i2s->fifo_regs->rfstat),
-			in_be16(&psc_i2s->fifo_regs->tfnum) & 0x1ff,
-			in_be16(&psc_i2s->fifo_regs->tfstat));
-}
-
-static int *psc_i2s_get_stat_attr(struct psc_i2s *psc_i2s, const char *name)
-{
-	if (strcmp(name, "playback_underrun") == 0)
-		return &psc_i2s->stats.underrun_count;
-	if (strcmp(name, "capture_overrun") == 0)
-		return &psc_i2s->stats.overrun_count;
-
-	return NULL;
-}
-
-static ssize_t psc_i2s_stat_show(struct device *dev,
-				 struct device_attribute *attr, char *buf)
-{
-	struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
-	int *attrib;
-
-	attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
-	if (!attrib)
-		return 0;
-
-	return sprintf(buf, "%i\n", *attrib);
-}
-
-static ssize_t psc_i2s_stat_store(struct device *dev,
-				  struct device_attribute *attr,
-				  const char *buf,
-				  size_t count)
-{
-	struct psc_i2s *psc_i2s = dev_get_drvdata(dev);
-	int *attrib;
-
-	attrib = psc_i2s_get_stat_attr(psc_i2s, attr->attr.name);
-	if (!attrib)
-		return 0;
-
-	*attrib = simple_strtoul(buf, NULL, 0);
-	return count;
-}
-
-static DEVICE_ATTR(status, 0644, psc_i2s_status_show, NULL);
-static DEVICE_ATTR(playback_underrun, 0644, psc_i2s_stat_show,
-			psc_i2s_stat_store);
-static DEVICE_ATTR(capture_overrun, 0644, psc_i2s_stat_show,
-			psc_i2s_stat_store);
+} };
+EXPORT_SYMBOL_GPL(psc_i2s_dai);
 
 /* ---------------------------------------------------------------------
  * OF platform bus binding code:
@@ -718,150 +156,65 @@
 static int __devinit psc_i2s_of_probe(struct of_device *op,
 				      const struct of_device_id *match)
 {
-	phys_addr_t fifo;
-	struct psc_i2s *psc_i2s;
-	struct resource res;
-	int size, psc_id, irq, rc;
-	const __be32 *prop;
-	void __iomem *regs;
+	int rc;
+	struct psc_dma *psc_dma;
+	struct mpc52xx_psc __iomem *regs;
 
-	dev_dbg(&op->dev, "probing psc i2s device\n");
+	rc = mpc5200_audio_dma_create(op);
+	if (rc != 0)
+		return rc;
 
-	/* Get the PSC ID */
-	prop = of_get_property(op->node, "cell-index", &size);
-	if (!prop || size < sizeof *prop)
-		return -ENODEV;
-	psc_id = be32_to_cpu(*prop);
-
-	/* Fetch the registers and IRQ of the PSC */
-	irq = irq_of_parse_and_map(op->node, 0);
-	if (of_address_to_resource(op->node, 0, &res)) {
-		dev_err(&op->dev, "Missing reg property\n");
-		return -ENODEV;
-	}
-	regs = ioremap(res.start, 1 + res.end - res.start);
-	if (!regs) {
-		dev_err(&op->dev, "Could not map registers\n");
-		return -ENODEV;
+	rc = snd_soc_register_dais(psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai));
+	if (rc != 0) {
+		pr_err("Failed to register DAI\n");
+		return 0;
 	}
 
-	/* Allocate and initialize the driver private data */
-	psc_i2s = kzalloc(sizeof *psc_i2s, GFP_KERNEL);
-	if (!psc_i2s) {
-		iounmap(regs);
-		return -ENOMEM;
-	}
-	spin_lock_init(&psc_i2s->lock);
-	psc_i2s->irq = irq;
-	psc_i2s->psc_regs = regs;
-	psc_i2s->fifo_regs = regs + sizeof *psc_i2s->psc_regs;
-	psc_i2s->dev = &op->dev;
-	psc_i2s->playback.psc_i2s = psc_i2s;
-	psc_i2s->capture.psc_i2s = psc_i2s;
-	snprintf(psc_i2s->name, sizeof psc_i2s->name, "PSC%u", psc_id+1);
-
-	/* Fill out the CPU DAI structure */
-	memcpy(&psc_i2s->dai, &psc_i2s_dai_template, sizeof psc_i2s->dai);
-	psc_i2s->dai.private_data = psc_i2s;
-	psc_i2s->dai.name = psc_i2s->name;
-	psc_i2s->dai.id = psc_id;
-
-	/* Find the address of the fifo data registers and setup the
-	 * DMA tasks */
-	fifo = res.start + offsetof(struct mpc52xx_psc, buffer.buffer_32);
-	psc_i2s->capture.bcom_task =
-		bcom_psc_gen_bd_rx_init(psc_id, 10, fifo, 512);
-	psc_i2s->playback.bcom_task =
-		bcom_psc_gen_bd_tx_init(psc_id, 10, fifo);
-	if (!psc_i2s->capture.bcom_task ||
-	    !psc_i2s->playback.bcom_task) {
-		dev_err(&op->dev, "Could not allocate bestcomm tasks\n");
-		iounmap(regs);
-		kfree(psc_i2s);
-		return -ENODEV;
-	}
-
-	/* Disable all interrupts and reset the PSC */
-	out_be16(&psc_i2s->psc_regs->isr_imr.imr, 0);
-	out_8(&psc_i2s->psc_regs->command, 3 << 4); /* reset transmitter */
-	out_8(&psc_i2s->psc_regs->command, 2 << 4); /* reset receiver */
-	out_8(&psc_i2s->psc_regs->command, 1 << 4); /* reset mode */
-	out_8(&psc_i2s->psc_regs->command, 4 << 4); /* reset error */
+	psc_dma = dev_get_drvdata(&op->dev);
+	regs = psc_dma->psc_regs;
 
 	/* Configure the serial interface mode; defaulting to CODEC8 mode */
-	psc_i2s->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
+	psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
 			MPC52xx_PSC_SICR_CLKPOL;
-	if (of_get_property(op->node, "fsl,cellslave", NULL))
-		psc_i2s->sicr |= MPC52xx_PSC_SICR_CELLSLAVE |
-				 MPC52xx_PSC_SICR_GENCLK;
-	out_be32(&psc_i2s->psc_regs->sicr,
-		 psc_i2s->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
+	out_be32(&psc_dma->psc_regs->sicr,
+		 psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
 
 	/* Check for the codec handle.  If it is not present then we
 	 * are done */
 	if (!of_get_property(op->node, "codec-handle", NULL))
 		return 0;
 
-	/* Set up mode register;
-	 * First write: RxRdy (FIFO Alarm) generates rx FIFO irq
-	 * Second write: register Normal mode for non loopback
-	 */
-	out_8(&psc_i2s->psc_regs->mode, 0);
-	out_8(&psc_i2s->psc_regs->mode, 0);
+	/* Due to errata in the dma mode; need to line up enabling
+	 * the transmitter with a transition on the frame sync
+	 * line */
 
-	/* Set the TX and RX fifo alarm thresholds */
-	out_be16(&psc_i2s->fifo_regs->rfalarm, 0x100);
-	out_8(&psc_i2s->fifo_regs->rfcntl, 0x4);
-	out_be16(&psc_i2s->fifo_regs->tfalarm, 0x100);
-	out_8(&psc_i2s->fifo_regs->tfcntl, 0x7);
+	/* first make sure it is low */
+	while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0)
+		;
+	/* then wait for the transition to high */
+	while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0)
+		;
+	/* Finally, enable the PSC.
+	 * Receiver must always be enabled; even when we only want
+	 * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */
 
-	/* Lookup the IRQ numbers */
-	psc_i2s->playback.irq =
-		bcom_get_task_irq(psc_i2s->playback.bcom_task);
-	psc_i2s->capture.irq =
-		bcom_get_task_irq(psc_i2s->capture.bcom_task);
-
-	/* Save what we've done so it can be found again later */
-	dev_set_drvdata(&op->dev, psc_i2s);
-
-	/* Register the SYSFS files */
-	rc = device_create_file(psc_i2s->dev, &dev_attr_status);
-	rc |= device_create_file(psc_i2s->dev, &dev_attr_capture_overrun);
-	rc |= device_create_file(psc_i2s->dev, &dev_attr_playback_underrun);
-	if (rc)
-		dev_info(psc_i2s->dev, "error creating sysfs files\n");
-
-	snd_soc_register_platform(&psc_i2s_pcm_soc_platform);
-
-	/* Tell the ASoC OF helpers about it */
-	of_snd_soc_register_platform(&psc_i2s_pcm_soc_platform, op->node,
-				     &psc_i2s->dai);
+	/* Go */
+	out_8(&psc_dma->psc_regs->command,
+			MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
 
 	return 0;
+
 }
 
 static int __devexit psc_i2s_of_remove(struct of_device *op)
 {
-	struct psc_i2s *psc_i2s = dev_get_drvdata(&op->dev);
-
-	dev_dbg(&op->dev, "psc_i2s_remove()\n");
-
-	snd_soc_unregister_platform(&psc_i2s_pcm_soc_platform);
-
-	bcom_gen_bd_rx_release(psc_i2s->capture.bcom_task);
-	bcom_gen_bd_tx_release(psc_i2s->playback.bcom_task);
-
-	iounmap(psc_i2s->psc_regs);
-	iounmap(psc_i2s->fifo_regs);
-	kfree(psc_i2s);
-	dev_set_drvdata(&op->dev, NULL);
-
-	return 0;
+	return mpc5200_audio_dma_destroy(op);
 }
 
 /* Match table for of_platform binding */
 static struct of_device_id psc_i2s_match[] __devinitdata = {
 	{ .compatible = "fsl,mpc5200-psc-i2s", },
+	{ .compatible = "fsl,mpc5200b-psc-i2s", },
 	{}
 };
 MODULE_DEVICE_TABLE(of, psc_i2s_match);
@@ -892,4 +245,7 @@
 }
 module_exit(psc_i2s_exit);
 
+MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
+MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
+MODULE_LICENSE("GPL");
 
diff --git a/sound/soc/fsl/mpc5200_psc_i2s.h b/sound/soc/fsl/mpc5200_psc_i2s.h
new file mode 100644
index 0000000..ce55e07
--- /dev/null
+++ b/sound/soc/fsl/mpc5200_psc_i2s.h
@@ -0,0 +1,12 @@
+/*
+ * Freescale MPC5200 PSC in I2S mode
+ * ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ */
+
+#ifndef __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__
+#define __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__
+
+extern struct snd_soc_dai psc_i2s_dai[];
+
+#endif /* __SOUND_SOC_FSL_MPC52xx_PSC_I2S_H__ */
diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c
new file mode 100644
index 0000000..8766f7a
--- /dev/null
+++ b/sound/soc/fsl/pcm030-audio-fabric.c
@@ -0,0 +1,90 @@
+/*
+ * Phytec pcm030 driver for the PSC of the Freescale MPC52xx
+ * configured as AC97 interface
+ *
+ * Copyright 2008 Jon Smirl, Digispeaker
+ * Author: Jon Smirl <jonsmirl@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.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-of-simple.h>
+
+#include "mpc5200_dma.h"
+#include "mpc5200_psc_ac97.h"
+#include "../codecs/wm9712.h"
+
+static struct snd_soc_device device;
+static struct snd_soc_card card;
+
+static struct snd_soc_dai_link pcm030_fabric_dai[] = {
+{
+	.name = "AC97",
+	.stream_name = "AC97 Analog",
+	.codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
+	.cpu_dai = &psc_ac97_dai[MPC5200_AC97_NORMAL],
+},
+{
+	.name = "AC97",
+	.stream_name = "AC97 IEC958",
+	.codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
+	.cpu_dai = &psc_ac97_dai[MPC5200_AC97_SPDIF],
+},
+};
+
+static __init int pcm030_fabric_init(void)
+{
+	struct platform_device *pdev;
+	int rc;
+
+	if (!machine_is_compatible("phytec,pcm030"))
+		return -ENODEV;
+
+	card.platform = &mpc5200_audio_dma_platform;
+	card.name = "pcm030";
+	card.dai_link = pcm030_fabric_dai;
+	card.num_links = ARRAY_SIZE(pcm030_fabric_dai);
+
+	device.card = &card;
+	device.codec_dev = &soc_codec_dev_wm9712;
+
+	pdev = platform_device_alloc("soc-audio", 1);
+	if (!pdev) {
+		pr_err("pcm030_fabric_init: platform_device_alloc() failed\n");
+		return -ENODEV;
+	}
+
+	platform_set_drvdata(pdev, &device);
+	device.dev = &pdev->dev;
+
+	rc = platform_device_add(pdev);
+	if (rc) {
+		pr_err("pcm030_fabric_init: platform_device_add() failed\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+module_init(pcm030_fabric_init);
+
+
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_DESCRIPTION(DRV_NAME ": mpc5200 pcm030 fabric driver");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index 675732e..2dee983 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -15,6 +15,14 @@
 	help
 	  Say Y if you want to add support for SoC audio on Nokia N810.
 
+config SND_OMAP_SOC_AMS_DELTA
+	tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
+	depends on SND_OMAP_SOC && MACH_AMS_DELTA
+	select SND_OMAP_SOC_MCBSP
+	select SND_SOC_CX20442
+	help
+	  Say Y if you want to add support for SoC audio on Amstrad Delta.
+
 config SND_OMAP_SOC_OSK5912
 	tristate "SoC Audio support for omap osk5912"
 	depends on SND_OMAP_SOC && MACH_OMAP_OSK && I2C
@@ -39,6 +47,14 @@
 	help
 	  Say Y if you want to add support for SoC audio on the omap2evm board.
 
+config SND_OMAP_SOC_OMAP3EVM
+	tristate "SoC Audio support for OMAP3EVM board"
+	depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3EVM
+	select SND_OMAP_SOC_MCBSP
+	select SND_SOC_TWL4030
+	help
+	  Say Y if you want to add support for SoC audio on the omap3evm board.
+
 config SND_OMAP_SOC_SDP3430
 	tristate "SoC Audio support for Texas Instruments SDP3430"
 	depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_3430SDP
@@ -64,4 +80,11 @@
 	help
 	  Say Y if you want to add support for SoC audio on the Beagleboard.
 
+config SND_OMAP_SOC_ZOOM2
+	tristate "SoC Audio support for Zoom2"
+	depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP_ZOOM2
+	select SND_OMAP_SOC_MCBSP
+	select SND_SOC_TWL4030
+	help
+	  Say Y if you want to add support for Soc audio on Zoom2 board.
 
diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile
index 0c9e4ac..02d6947 100644
--- a/sound/soc/omap/Makefile
+++ b/sound/soc/omap/Makefile
@@ -7,17 +7,23 @@
 
 # OMAP Machine Support
 snd-soc-n810-objs := n810.o
+snd-soc-ams-delta-objs := ams-delta.o
 snd-soc-osk5912-objs := osk5912.o
 snd-soc-overo-objs := overo.o
 snd-soc-omap2evm-objs := omap2evm.o
+snd-soc-omap3evm-objs := omap3evm.o
 snd-soc-sdp3430-objs := sdp3430.o
 snd-soc-omap3pandora-objs := omap3pandora.o
 snd-soc-omap3beagle-objs := omap3beagle.o
+snd-soc-zoom2-objs := zoom2.o
 
 obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
+obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o
 obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
 obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
 obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
+obj-$(CONFIG_MACH_OMAP3EVM) += snd-soc-omap3evm.o
 obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
 obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
+obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c
new file mode 100644
index 0000000..5a5166a
--- /dev/null
+++ b/sound/soc/omap/ams-delta.c
@@ -0,0 +1,646 @@
+/*
+ * ams-delta.c  --  SoC audio for Amstrad E3 (Delta) videophone
+ *
+ * Copyright (C) 2009 Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
+ *
+ * Initially based on sound/soc/omap/osk5912.x
+ * Copyright (C) 2008 Mistral Solutions
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/tty.h>
+
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/board-ams-delta.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/cx20442.h"
+
+
+/* Board specific DAPM widgets */
+ const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = {
+	/* Handset */
+	SND_SOC_DAPM_MIC("Mouthpiece", NULL),
+	SND_SOC_DAPM_HP("Earpiece", NULL),
+	/* Handsfree/Speakerphone */
+	SND_SOC_DAPM_MIC("Microphone", NULL),
+	SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+/* How they are connected to codec pins */
+static const struct snd_soc_dapm_route ams_delta_audio_map[] = {
+	{"TELIN", NULL, "Mouthpiece"},
+	{"Earpiece", NULL, "TELOUT"},
+
+	{"MIC", NULL, "Microphone"},
+	{"Speaker", NULL, "SPKOUT"},
+};
+
+/*
+ * Controls, functional after the modem line discipline is activated.
+ */
+
+/* Virtual switch: audio input/output constellations */
+static const char *ams_delta_audio_mode[] =
+	{"Mixed", "Handset", "Handsfree", "Speakerphone"};
+
+/* Selection <-> pin translation */
+#define AMS_DELTA_MOUTHPIECE	0
+#define AMS_DELTA_EARPIECE	1
+#define AMS_DELTA_MICROPHONE	2
+#define AMS_DELTA_SPEAKER	3
+#define AMS_DELTA_AGC		4
+
+#define AMS_DELTA_MIXED		((1 << AMS_DELTA_EARPIECE) | \
+						(1 << AMS_DELTA_MICROPHONE))
+#define AMS_DELTA_HANDSET	((1 << AMS_DELTA_MOUTHPIECE) | \
+						(1 << AMS_DELTA_EARPIECE))
+#define AMS_DELTA_HANDSFREE	((1 << AMS_DELTA_MICROPHONE) | \
+						(1 << AMS_DELTA_SPEAKER))
+#define AMS_DELTA_SPEAKERPHONE	(AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC))
+
+unsigned short ams_delta_audio_mode_pins[] = {
+	AMS_DELTA_MIXED,
+	AMS_DELTA_HANDSET,
+	AMS_DELTA_HANDSFREE,
+	AMS_DELTA_SPEAKERPHONE,
+};
+
+static unsigned short ams_delta_audio_agc;
+
+static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+	struct soc_enum *control = (struct soc_enum *)kcontrol->private_value;
+	unsigned short pins;
+	int pin, changed = 0;
+
+	/* Refuse any mode changes if we are not able to control the codec. */
+	if (!codec->control_data)
+		return -EUNATCH;
+
+	if (ucontrol->value.enumerated.item[0] >= control->max)
+		return -EINVAL;
+
+	mutex_lock(&codec->mutex);
+
+	/* Translate selection to bitmap */
+	pins = ams_delta_audio_mode_pins[ucontrol->value.enumerated.item[0]];
+
+	/* Setup pins after corresponding bits if changed */
+	pin = !!(pins & (1 << AMS_DELTA_MOUTHPIECE));
+	if (pin != snd_soc_dapm_get_pin_status(codec, "Mouthpiece")) {
+		changed = 1;
+		if (pin)
+			snd_soc_dapm_enable_pin(codec, "Mouthpiece");
+		else
+			snd_soc_dapm_disable_pin(codec, "Mouthpiece");
+	}
+	pin = !!(pins & (1 << AMS_DELTA_EARPIECE));
+	if (pin != snd_soc_dapm_get_pin_status(codec, "Earpiece")) {
+		changed = 1;
+		if (pin)
+			snd_soc_dapm_enable_pin(codec, "Earpiece");
+		else
+			snd_soc_dapm_disable_pin(codec, "Earpiece");
+	}
+	pin = !!(pins & (1 << AMS_DELTA_MICROPHONE));
+	if (pin != snd_soc_dapm_get_pin_status(codec, "Microphone")) {
+		changed = 1;
+		if (pin)
+			snd_soc_dapm_enable_pin(codec, "Microphone");
+		else
+			snd_soc_dapm_disable_pin(codec, "Microphone");
+	}
+	pin = !!(pins & (1 << AMS_DELTA_SPEAKER));
+	if (pin != snd_soc_dapm_get_pin_status(codec, "Speaker")) {
+		changed = 1;
+		if (pin)
+			snd_soc_dapm_enable_pin(codec, "Speaker");
+		else
+			snd_soc_dapm_disable_pin(codec, "Speaker");
+	}
+	pin = !!(pins & (1 << AMS_DELTA_AGC));
+	if (pin != ams_delta_audio_agc) {
+		ams_delta_audio_agc = pin;
+		changed = 1;
+		if (pin)
+			snd_soc_dapm_enable_pin(codec, "AGCIN");
+		else
+			snd_soc_dapm_disable_pin(codec, "AGCIN");
+	}
+	if (changed)
+		snd_soc_dapm_sync(codec);
+
+	mutex_unlock(&codec->mutex);
+
+	return changed;
+}
+
+static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+	unsigned short pins, mode;
+
+	pins = ((snd_soc_dapm_get_pin_status(codec, "Mouthpiece") <<
+							AMS_DELTA_MOUTHPIECE) |
+			(snd_soc_dapm_get_pin_status(codec, "Earpiece") <<
+							AMS_DELTA_EARPIECE));
+	if (pins)
+		pins |= (snd_soc_dapm_get_pin_status(codec, "Microphone") <<
+							AMS_DELTA_MICROPHONE);
+	else
+		pins = ((snd_soc_dapm_get_pin_status(codec, "Microphone") <<
+							AMS_DELTA_MICROPHONE) |
+			(snd_soc_dapm_get_pin_status(codec, "Speaker") <<
+							AMS_DELTA_SPEAKER) |
+			(ams_delta_audio_agc << AMS_DELTA_AGC));
+
+	for (mode = 0; mode < ARRAY_SIZE(ams_delta_audio_mode); mode++)
+		if (pins == ams_delta_audio_mode_pins[mode])
+			break;
+
+	if (mode >= ARRAY_SIZE(ams_delta_audio_mode))
+		return -EINVAL;
+
+	ucontrol->value.enumerated.item[0] = mode;
+
+	return 0;
+}
+
+static const struct soc_enum ams_delta_audio_enum[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ams_delta_audio_mode),
+						ams_delta_audio_mode),
+};
+
+static const struct snd_kcontrol_new ams_delta_audio_controls[] = {
+	SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum[0],
+			ams_delta_get_audio_mode, ams_delta_set_audio_mode),
+};
+
+/* Hook switch */
+static struct snd_soc_jack ams_delta_hook_switch;
+static struct snd_soc_jack_gpio ams_delta_hook_switch_gpios[] = {
+	{
+		.gpio = 4,
+		.name = "hook_switch",
+		.report = SND_JACK_HEADSET,
+		.invert = 1,
+		.debounce_time = 150,
+	}
+};
+
+/* After we are able to control the codec over the modem,
+ * the hook switch can be used for dynamic DAPM reconfiguration. */
+static struct snd_soc_jack_pin ams_delta_hook_switch_pins[] = {
+	/* Handset */
+	{
+		.pin = "Mouthpiece",
+		.mask = SND_JACK_MICROPHONE,
+	},
+	{
+		.pin = "Earpiece",
+		.mask = SND_JACK_HEADPHONE,
+	},
+	/* Handsfree */
+	{
+		.pin = "Microphone",
+		.mask = SND_JACK_MICROPHONE,
+		.invert = 1,
+	},
+	{
+		.pin = "Speaker",
+		.mask = SND_JACK_HEADPHONE,
+		.invert = 1,
+	},
+};
+
+
+/*
+ * Modem line discipline, required for making above controls functional.
+ * Activated from userspace with ldattach, possibly invoked from udev rule.
+ */
+
+/* To actually apply any modem controlled configuration changes to the codec,
+ * we must connect codec DAI pins to the modem for a moment.  Be carefull not
+ * to interfere with our digital mute function that shares the same hardware. */
+static struct timer_list cx81801_timer;
+static bool cx81801_cmd_pending;
+static bool ams_delta_muted;
+static DEFINE_SPINLOCK(ams_delta_lock);
+
+static void cx81801_timeout(unsigned long data)
+{
+	int muted;
+
+	spin_lock(&ams_delta_lock);
+	cx81801_cmd_pending = 0;
+	muted = ams_delta_muted;
+	spin_unlock(&ams_delta_lock);
+
+	/* Reconnect the codec DAI back from the modem to the CPU DAI
+	 * only if digital mute still off */
+	if (!muted)
+		ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_CODEC, 0);
+}
+
+/* Line discipline .open() */
+static int cx81801_open(struct tty_struct *tty)
+{
+	return v253_ops.open(tty);
+}
+
+/* Line discipline .close() */
+static void cx81801_close(struct tty_struct *tty)
+{
+	struct snd_soc_codec *codec = tty->disc_data;
+
+	del_timer_sync(&cx81801_timer);
+
+	v253_ops.close(tty);
+
+	/* Prevent the hook switch from further changing the DAPM pins */
+	INIT_LIST_HEAD(&ams_delta_hook_switch.pins);
+
+	/* Revert back to default audio input/output constellation */
+	snd_soc_dapm_disable_pin(codec, "Mouthpiece");
+	snd_soc_dapm_enable_pin(codec, "Earpiece");
+	snd_soc_dapm_enable_pin(codec, "Microphone");
+	snd_soc_dapm_disable_pin(codec, "Speaker");
+	snd_soc_dapm_disable_pin(codec, "AGCIN");
+	snd_soc_dapm_sync(codec);
+}
+
+/* Line discipline .hangup() */
+static int cx81801_hangup(struct tty_struct *tty)
+{
+	cx81801_close(tty);
+	return 0;
+}
+
+/* Line discipline .recieve_buf() */
+static void cx81801_receive(struct tty_struct *tty,
+				const unsigned char *cp, char *fp, int count)
+{
+	struct snd_soc_codec *codec = tty->disc_data;
+	const unsigned char *c;
+	int apply, ret;
+
+	if (!codec->control_data) {
+		/* First modem response, complete setup procedure */
+
+		/* Initialize timer used for config pulse generation */
+		setup_timer(&cx81801_timer, cx81801_timeout, 0);
+
+		v253_ops.receive_buf(tty, cp, fp, count);
+
+		/* Link hook switch to DAPM pins */
+		ret = snd_soc_jack_add_pins(&ams_delta_hook_switch,
+					ARRAY_SIZE(ams_delta_hook_switch_pins),
+					ams_delta_hook_switch_pins);
+		if (ret)
+			dev_warn(codec->socdev->card->dev,
+				"Failed to link hook switch to DAPM pins, "
+				"will continue with hook switch unlinked.\n");
+
+		return;
+	}
+
+	v253_ops.receive_buf(tty, cp, fp, count);
+
+	for (c = &cp[count - 1]; c >= cp; c--) {
+		if (*c != '\r')
+			continue;
+		/* Complete modem response received, apply config to codec */
+
+		spin_lock_bh(&ams_delta_lock);
+		mod_timer(&cx81801_timer, jiffies + msecs_to_jiffies(150));
+		apply = !ams_delta_muted && !cx81801_cmd_pending;
+		cx81801_cmd_pending = 1;
+		spin_unlock_bh(&ams_delta_lock);
+
+		/* Apply config pulse by connecting the codec to the modem
+		 * if not already done */
+		if (apply)
+			ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_CODEC,
+						AMS_DELTA_LATCH2_MODEM_CODEC);
+		break;
+	}
+}
+
+/* Line discipline .write_wakeup() */
+static void cx81801_wakeup(struct tty_struct *tty)
+{
+	v253_ops.write_wakeup(tty);
+}
+
+static struct tty_ldisc_ops cx81801_ops = {
+	.magic = TTY_LDISC_MAGIC,
+	.name = "cx81801",
+	.owner = THIS_MODULE,
+	.open = cx81801_open,
+	.close = cx81801_close,
+	.hangup = cx81801_hangup,
+	.receive_buf = cx81801_receive,
+	.write_wakeup = cx81801_wakeup,
+};
+
+
+/*
+ * Even if not very usefull, the sound card can still work without any of the
+ * above functonality activated.  You can still control its audio input/output
+ * constellation and speakerphone gain from userspace by issueing AT commands
+ * over the modem port.
+ */
+
+static int ams_delta_hw_params(struct snd_pcm_substream *substream,
+			 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+	/* Set cpu DAI configuration */
+	return snd_soc_dai_set_fmt(rtd->dai->cpu_dai,
+				   SND_SOC_DAIFMT_DSP_A |
+				   SND_SOC_DAIFMT_NB_NF |
+				   SND_SOC_DAIFMT_CBM_CFM);
+}
+
+static struct snd_soc_ops ams_delta_ops = {
+	.hw_params = ams_delta_hw_params,
+};
+
+
+/* Board specific codec bias level control */
+static int ams_delta_set_bias_level(struct snd_soc_card *card,
+					enum snd_soc_bias_level level)
+{
+	struct snd_soc_codec *codec = card->codec;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->bias_level == SND_SOC_BIAS_OFF)
+			ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_NRESET,
+						AMS_DELTA_LATCH2_MODEM_NRESET);
+		break;
+	case SND_SOC_BIAS_OFF:
+		if (codec->bias_level != SND_SOC_BIAS_OFF)
+			ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_NRESET,
+						0);
+	}
+	codec->bias_level = level;
+
+	return 0;
+}
+
+/* Digital mute implemented using modem/CPU multiplexer.
+ * Shares hardware with codec config pulse generation */
+static bool ams_delta_muted = 1;
+
+static int ams_delta_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+	int apply;
+
+	if (ams_delta_muted == mute)
+		return 0;
+
+	spin_lock_bh(&ams_delta_lock);
+	ams_delta_muted = mute;
+	apply = !cx81801_cmd_pending;
+	spin_unlock_bh(&ams_delta_lock);
+
+	if (apply)
+		ams_delta_latch2_write(AMS_DELTA_LATCH2_MODEM_CODEC,
+				mute ? AMS_DELTA_LATCH2_MODEM_CODEC : 0);
+	return 0;
+}
+
+/* Our codec DAI probably doesn't have its own .ops structure */
+static struct snd_soc_dai_ops ams_delta_dai_ops = {
+	.digital_mute = ams_delta_digital_mute,
+};
+
+/* Will be used if the codec ever has its own digital_mute function */
+static int ams_delta_startup(struct snd_pcm_substream *substream)
+{
+	return ams_delta_digital_mute(NULL, 0);
+}
+
+static void ams_delta_shutdown(struct snd_pcm_substream *substream)
+{
+	ams_delta_digital_mute(NULL, 1);
+}
+
+
+/*
+ * Card initialization
+ */
+
+static int ams_delta_cx20442_init(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dai *codec_dai = codec->dai;
+	struct snd_soc_card *card = codec->socdev->card;
+	int ret;
+	/* Codec is ready, now add/activate board specific controls */
+
+	/* Set up digital mute if not provided by the codec */
+	if (!codec_dai->ops) {
+		codec_dai->ops = &ams_delta_dai_ops;
+	} else if (!codec_dai->ops->digital_mute) {
+		codec_dai->ops->digital_mute = ams_delta_digital_mute;
+	} else {
+		ams_delta_ops.startup = ams_delta_startup;
+		ams_delta_ops.shutdown = ams_delta_shutdown;
+	}
+
+	/* Set codec bias level */
+	ams_delta_set_bias_level(card, SND_SOC_BIAS_STANDBY);
+
+	/* Add hook switch - can be used to control the codec from userspace
+	 * even if line discipline fails */
+	ret = snd_soc_jack_new(card, "hook_switch",
+				SND_JACK_HEADSET, &ams_delta_hook_switch);
+	if (ret)
+		dev_warn(card->dev,
+				"Failed to allocate resources for hook switch, "
+				"will continue without one.\n");
+	else {
+		ret = snd_soc_jack_add_gpios(&ams_delta_hook_switch,
+					ARRAY_SIZE(ams_delta_hook_switch_gpios),
+					ams_delta_hook_switch_gpios);
+		if (ret)
+			dev_warn(card->dev,
+				"Failed to set up hook switch GPIO line, "
+				"will continue with hook switch inactive.\n");
+	}
+
+	/* Register optional line discipline for over the modem control */
+	ret = tty_register_ldisc(N_V253, &cx81801_ops);
+	if (ret) {
+		dev_warn(card->dev,
+				"Failed to register line discipline, "
+				"will continue without any controls.\n");
+		return 0;
+	}
+
+	/* Add board specific DAPM widgets and routes */
+	ret = snd_soc_dapm_new_controls(codec, ams_delta_dapm_widgets,
+					ARRAY_SIZE(ams_delta_dapm_widgets));
+	if (ret) {
+		dev_warn(card->dev,
+				"Failed to register DAPM controls, "
+				"will continue without any.\n");
+		return 0;
+	}
+
+	ret = snd_soc_dapm_add_routes(codec, ams_delta_audio_map,
+					ARRAY_SIZE(ams_delta_audio_map));
+	if (ret) {
+		dev_warn(card->dev,
+				"Failed to set up DAPM routes, "
+				"will continue with codec default map.\n");
+		return 0;
+	}
+
+	/* Set up initial pin constellation */
+	snd_soc_dapm_disable_pin(codec, "Mouthpiece");
+	snd_soc_dapm_enable_pin(codec, "Earpiece");
+	snd_soc_dapm_enable_pin(codec, "Microphone");
+	snd_soc_dapm_disable_pin(codec, "Speaker");
+	snd_soc_dapm_disable_pin(codec, "AGCIN");
+	snd_soc_dapm_disable_pin(codec, "AGCOUT");
+	snd_soc_dapm_sync(codec);
+
+	/* Add virtual switch */
+	ret = snd_soc_add_controls(codec, ams_delta_audio_controls,
+					ARRAY_SIZE(ams_delta_audio_controls));
+	if (ret)
+		dev_warn(card->dev,
+				"Failed to register audio mode control, "
+				"will continue without it.\n");
+
+	return 0;
+}
+
+/* DAI glue - connects codec <--> CPU */
+static struct snd_soc_dai_link ams_delta_dai_link = {
+	.name = "CX20442",
+	.stream_name = "CX20442",
+	.cpu_dai = &omap_mcbsp_dai[0],
+	.codec_dai = &cx20442_dai,
+	.init = ams_delta_cx20442_init,
+	.ops = &ams_delta_ops,
+};
+
+/* Audio card driver */
+static struct snd_soc_card ams_delta_audio_card = {
+	.name = "AMS_DELTA",
+	.platform = &omap_soc_platform,
+	.dai_link = &ams_delta_dai_link,
+	.num_links = 1,
+	.set_bias_level = ams_delta_set_bias_level,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device ams_delta_snd_soc_device = {
+	.card = &ams_delta_audio_card,
+	.codec_dev = &cx20442_codec_dev,
+};
+
+/* Module init/exit */
+static struct platform_device *ams_delta_audio_platform_device;
+static struct platform_device *cx20442_platform_device;
+
+static int __init ams_delta_module_init(void)
+{
+	int ret;
+
+	if (!(machine_is_ams_delta()))
+		return -ENODEV;
+
+	ams_delta_audio_platform_device =
+			platform_device_alloc("soc-audio", -1);
+	if (!ams_delta_audio_platform_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(ams_delta_audio_platform_device,
+				&ams_delta_snd_soc_device);
+	ams_delta_snd_soc_device.dev = &ams_delta_audio_platform_device->dev;
+	*(unsigned int *)ams_delta_dai_link.cpu_dai->private_data = OMAP_MCBSP1;
+
+	ret = platform_device_add(ams_delta_audio_platform_device);
+	if (ret)
+		goto err;
+
+	/*
+	 * Codec platform device could be registered from elsewhere (board?),
+	 * but I do it here as it makes sense only if used with the card.
+	 */
+	cx20442_platform_device = platform_device_register_simple("cx20442",
+								-1, NULL, 0);
+	return 0;
+err:
+	platform_device_put(ams_delta_audio_platform_device);
+	return ret;
+}
+module_init(ams_delta_module_init);
+
+static void __exit ams_delta_module_exit(void)
+{
+	struct snd_soc_codec *codec;
+	struct tty_struct *tty;
+
+	if (ams_delta_audio_card.codec) {
+		codec = ams_delta_audio_card.codec;
+
+		if (codec->control_data) {
+			tty = codec->control_data;
+
+			tty_hangup(tty);
+		}
+	}
+
+	if (tty_unregister_ldisc(N_V253) != 0)
+		dev_warn(&ams_delta_audio_platform_device->dev,
+			"failed to unregister V253 line discipline\n");
+
+	snd_soc_jack_free_gpios(&ams_delta_hook_switch,
+			ARRAY_SIZE(ams_delta_hook_switch_gpios),
+			ams_delta_hook_switch_gpios);
+
+	/* Keep modem power on */
+	ams_delta_set_bias_level(&ams_delta_audio_card, SND_SOC_BIAS_STANDBY);
+
+	platform_device_unregister(cx20442_platform_device);
+	platform_device_unregister(ams_delta_audio_platform_device);
+}
+module_exit(ams_delta_module_exit);
+
+MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
+MODULE_DESCRIPTION("ALSA SoC driver for Amstrad E3 (Delta) videophone");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index 91ef179..b60b1df 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -383,10 +383,9 @@
 	clk_set_parent(sys_clkout2_src, func96m_clk);
 	clk_set_rate(sys_clkout2, 12000000);
 
-	if (gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0)
-		BUG();
-	if (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0)
-		BUG();
+	BUG_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) ||
+	       (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0));
+
 	gpio_direction_output(N810_HEADSET_AMP_GPIO, 0);
 	gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0);
 
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 9126142..6a837ff 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -183,21 +183,21 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
-	int err = 0;
+	int err = 0, play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:
 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		if (!mcbsp_data->active++)
-			omap_mcbsp_start(mcbsp_data->bus_id);
+		mcbsp_data->active++;
+		omap_mcbsp_start(mcbsp_data->bus_id, play, !play);
 		break;
 
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		if (!--mcbsp_data->active)
-			omap_mcbsp_stop(mcbsp_data->bus_id);
+		omap_mcbsp_stop(mcbsp_data->bus_id, play, !play);
+		mcbsp_data->active--;
 		break;
 	default:
 		err = -EINVAL;
@@ -215,8 +215,9 @@
 	struct omap_mcbsp_data *mcbsp_data = to_mcbsp(cpu_dai->private_data);
 	struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
 	int dma, bus_id = mcbsp_data->bus_id, id = cpu_dai->id;
-	int wlen, channels;
+	int wlen, channels, wpf;
 	unsigned long port;
+	unsigned int format;
 
 	if (cpu_class_is_omap1()) {
 		dma = omap1_dma_reqs[bus_id][substream->stream];
@@ -244,18 +245,24 @@
 		return 0;
 	}
 
-	channels = params_channels(params);
+	format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+	wpf = channels = params_channels(params);
 	switch (channels) {
 	case 2:
-		/* Use dual-phase frames */
-		regs->rcr2	|= RPHASE;
-		regs->xcr2	|= XPHASE;
+		if (format == SND_SOC_DAIFMT_I2S) {
+			/* Use dual-phase frames */
+			regs->rcr2	|= RPHASE;
+			regs->xcr2	|= XPHASE;
+			/* Set 1 word per (McBSP) frame for phase1 and phase2 */
+			wpf--;
+			regs->rcr2	|= RFRLEN2(wpf - 1);
+			regs->xcr2	|= XFRLEN2(wpf - 1);
+		}
 	case 1:
-		/* Set 1 word per (McBSP) frame */
-		regs->rcr2	|= RFRLEN2(1 - 1);
-		regs->rcr1	|= RFRLEN1(1 - 1);
-		regs->xcr2	|= XFRLEN2(1 - 1);
-		regs->xcr1	|= XFRLEN1(1 - 1);
+	case 4:
+		/* Set word per (McBSP) frame for phase1 */
+		regs->rcr1	|= RFRLEN1(wpf - 1);
+		regs->xcr1	|= XFRLEN1(wpf - 1);
 		break;
 	default:
 		/* Unsupported number of channels */
@@ -277,11 +284,12 @@
 	}
 
 	/* Set FS period and length in terms of bit clock periods */
-	switch (mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	switch (format) {
 	case SND_SOC_DAIFMT_I2S:
-		regs->srgr2	|= FPER(wlen * 2 - 1);
+		regs->srgr2	|= FPER(wlen * channels - 1);
 		regs->srgr1	|= FWID(wlen - 1);
 		break;
+	case SND_SOC_DAIFMT_DSP_A:
 	case SND_SOC_DAIFMT_DSP_B:
 		regs->srgr2	|= FPER(wlen * channels - 1);
 		regs->srgr1	|= FWID(0);
@@ -326,6 +334,13 @@
 		regs->rcr2	|= RDATDLY(1);
 		regs->xcr2	|= XDATDLY(1);
 		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		/* 1-bit data delay */
+		regs->rcr2      |= RDATDLY(1);
+		regs->xcr2      |= XDATDLY(1);
+		/* Invert FS polarity configuration */
+		temp_fmt ^= SND_SOC_DAIFMT_NB_IF;
+		break;
 	case SND_SOC_DAIFMT_DSP_B:
 		/* 0-bit data delay */
 		regs->rcr2      |= RDATDLY(0);
@@ -492,13 +507,13 @@
 	.id = (link_id),					\
 	.playback = {						\
 		.channels_min = 1,				\
-		.channels_max = 2,				\
+		.channels_max = 4,				\
 		.rates = OMAP_MCBSP_RATES,			\
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,		\
 	},							\
 	.capture = {						\
 		.channels_min = 1,				\
-		.channels_max = 2,				\
+		.channels_max = 4,				\
 		.rates = OMAP_MCBSP_RATES,			\
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,		\
 	},							\
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 07cf7f4..c3c931d 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -87,8 +87,10 @@
 	struct omap_pcm_dma_data *dma_data = rtd->dai->cpu_dai->dma_data;
 	int err = 0;
 
+	/* return if this is a bufferless transfer e.g.
+	 * codec <--> BT codec or GSM modem -- lg FIXME */
 	if (!dma_data)
-		return -ENODEV;
+		return 0;
 
 	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 	runtime->dma_bytes = params_buffer_bytes(params);
@@ -134,6 +136,11 @@
 	struct omap_pcm_dma_data *dma_data = prtd->dma_data;
 	struct omap_dma_channel_params dma_params;
 
+	/* return if this is a bufferless transfer e.g.
+	 * codec <--> BT codec or GSM modem -- lg FIXME */
+	if (!prtd->dma_data)
+		return 0;
+
 	memset(&dma_params, 0, sizeof(dma_params));
 	/*
 	 * Note: Regardless of interface data formats supported by OMAP McBSP
@@ -209,12 +216,15 @@
 	dma_addr_t ptr;
 	snd_pcm_uframes_t offset;
 
-	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		ptr = omap_get_dma_src_pos(prtd->dma_ch);
-	else
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
 		ptr = omap_get_dma_dst_pos(prtd->dma_ch);
+		offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+	} else if (!(cpu_is_omap1510())) {
+		ptr = omap_get_dma_src_pos(prtd->dma_ch);
+		offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
+	} else
+		offset = prtd->period_index * runtime->period_size;
 
-	offset = bytes_to_frames(runtime, ptr - runtime->dma_addr);
 	if (offset >= runtime->buffer_size)
 		offset = 0;
 
@@ -320,7 +330,7 @@
 	}
 }
 
-int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+static int omap_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
 		 struct snd_pcm *pcm)
 {
 	int ret = 0;
diff --git a/sound/soc/omap/omap2evm.c b/sound/soc/omap/omap2evm.c
index 0c2322d..027e1a4 100644
--- a/sound/soc/omap/omap2evm.c
+++ b/sound/soc/omap/omap2evm.c
@@ -86,7 +86,7 @@
 	.name = "TWL4030",
 	.stream_name = "TWL4030",
 	.cpu_dai = &omap_mcbsp_dai[0],
-	.codec_dai = &twl4030_dai,
+	.codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
 	.ops = &omap2evm_ops,
 };
 
diff --git a/sound/soc/omap/omap3beagle.c b/sound/soc/omap/omap3beagle.c
index fd24a4a..b0cff9f 100644
--- a/sound/soc/omap/omap3beagle.c
+++ b/sound/soc/omap/omap3beagle.c
@@ -41,23 +41,33 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	unsigned int fmt;
 	int ret;
 
+	switch (params_channels(params)) {
+	case 2: /* Stereo I2S mode */
+		fmt =	SND_SOC_DAIFMT_I2S |
+			SND_SOC_DAIFMT_NB_NF |
+			SND_SOC_DAIFMT_CBM_CFM;
+		break;
+	case 4: /* Four channel TDM mode */
+		fmt =	SND_SOC_DAIFMT_DSP_A |
+			SND_SOC_DAIFMT_IB_NF |
+			SND_SOC_DAIFMT_CBM_CFM;
+		break;
+	default:
+		return -EINVAL;
+	}
+
 	/* Set codec DAI configuration */
-	ret = snd_soc_dai_set_fmt(codec_dai,
-				  SND_SOC_DAIFMT_I2S |
-				  SND_SOC_DAIFMT_NB_NF |
-				  SND_SOC_DAIFMT_CBM_CFM);
+	ret = snd_soc_dai_set_fmt(codec_dai, fmt);
 	if (ret < 0) {
 		printk(KERN_ERR "can't set codec DAI configuration\n");
 		return ret;
 	}
 
 	/* Set cpu DAI configuration */
-	ret = snd_soc_dai_set_fmt(cpu_dai,
-				  SND_SOC_DAIFMT_I2S |
-				  SND_SOC_DAIFMT_NB_NF |
-				  SND_SOC_DAIFMT_CBM_CFM);
+	ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
 	if (ret < 0) {
 		printk(KERN_ERR "can't set cpu DAI configuration\n");
 		return ret;
@@ -83,7 +93,7 @@
 	.name = "TWL4030",
 	.stream_name = "TWL4030",
 	.cpu_dai = &omap_mcbsp_dai[0],
-	.codec_dai = &twl4030_dai,
+	.codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
 	.ops = &omap3beagle_ops,
 };
 
diff --git a/sound/soc/omap/omap3evm.c b/sound/soc/omap/omap3evm.c
new file mode 100644
index 0000000..9114c26
--- /dev/null
+++ b/sound/soc/omap/omap3evm.c
@@ -0,0 +1,147 @@
+/*
+ * omap3evm.c  -- ALSA SoC support for OMAP3 EVM
+ *
+ * Author: Anuj Aggarwal <anuj.aggarwal@ti.com>
+ *
+ * Based on sound/soc/omap/beagle.c by Steve Sakoman
+ *
+ * Copyright (C) 2008 Texas Instruments, Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+static int omap3evm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	int ret;
+
+	/* Set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai,
+				  SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0) {
+		printk(KERN_ERR "Can't set codec DAI configuration\n");
+		return ret;
+	}
+
+	/* Set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai,
+				  SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0) {
+		printk(KERN_ERR "Can't set cpu DAI configuration\n");
+		return ret;
+	}
+
+	/* Set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		printk(KERN_ERR "Can't set codec system clock\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops omap3evm_ops = {
+	.hw_params = omap3evm_hw_params,
+};
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link omap3evm_dai = {
+	.name 		= "TWL4030",
+	.stream_name 	= "TWL4030",
+	.cpu_dai 	= &omap_mcbsp_dai[0],
+	.codec_dai 	= &twl4030_dai[TWL4030_DAI_HIFI],
+	.ops 		= &omap3evm_ops,
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_omap3evm = {
+	.name = "omap3evm",
+	.platform = &omap_soc_platform,
+	.dai_link = &omap3evm_dai,
+	.num_links = 1,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device omap3evm_snd_devdata = {
+	.card = &snd_soc_omap3evm,
+	.codec_dev = &soc_codec_dev_twl4030,
+};
+
+static struct platform_device *omap3evm_snd_device;
+
+static int __init omap3evm_soc_init(void)
+{
+	int ret;
+
+	if (!machine_is_omap3evm()) {
+		pr_err("Not OMAP3 EVM!\n");
+		return -ENODEV;
+	}
+	pr_info("OMAP3 EVM SoC init\n");
+
+	omap3evm_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!omap3evm_snd_device) {
+		printk(KERN_ERR "Platform device allocation failed\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(omap3evm_snd_device, &omap3evm_snd_devdata);
+	omap3evm_snd_devdata.dev = &omap3evm_snd_device->dev;
+	*(unsigned int *)omap3evm_dai.cpu_dai->private_data = 1;
+
+	ret = platform_device_add(omap3evm_snd_device);
+	if (ret)
+		goto err1;
+
+	return 0;
+
+err1:
+	printk(KERN_ERR "Unable to add platform device\n");
+	platform_device_put(omap3evm_snd_device);
+
+	return ret;
+}
+
+static void __exit omap3evm_soc_exit(void)
+{
+	platform_device_unregister(omap3evm_snd_device);
+}
+
+module_init(omap3evm_soc_init);
+module_exit(omap3evm_soc_exit);
+
+MODULE_AUTHOR("Anuj Aggarwal <anuj.aggarwal@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC OMAP3 EVM");
+MODULE_LICENSE("GPLv2");
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c
index fe282d4..ad219aa 100644
--- a/sound/soc/omap/omap3pandora.c
+++ b/sound/soc/omap/omap3pandora.c
@@ -228,14 +228,14 @@
 		.name = "PCM1773",
 		.stream_name = "HiFi Out",
 		.cpu_dai = &omap_mcbsp_dai[0],
-		.codec_dai = &twl4030_dai,
+		.codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
 		.ops = &omap3pandora_out_ops,
 		.init = omap3pandora_out_init,
 	}, {
 		.name = "TWL4030",
 		.stream_name = "Line/Mic In",
 		.cpu_dai = &omap_mcbsp_dai[1],
-		.codec_dai = &twl4030_dai,
+		.codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
 		.ops = &omap3pandora_in_ops,
 		.init = omap3pandora_in_init,
 	}
diff --git a/sound/soc/omap/overo.c b/sound/soc/omap/overo.c
index a72dc4e..ec4f8fd 100644
--- a/sound/soc/omap/overo.c
+++ b/sound/soc/omap/overo.c
@@ -83,7 +83,7 @@
 	.name = "TWL4030",
 	.stream_name = "TWL4030",
 	.cpu_dai = &omap_mcbsp_dai[0],
-	.codec_dai = &twl4030_dai,
+	.codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
 	.ops = &overo_ops,
 };
 
diff --git a/sound/soc/omap/sdp3430.c b/sound/soc/omap/sdp3430.c
index 10f1c86..f7e5b74 100644
--- a/sound/soc/omap/sdp3430.c
+++ b/sound/soc/omap/sdp3430.c
@@ -24,6 +24,7 @@
 
 #include <linux/clk.h>
 #include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
@@ -39,6 +40,9 @@
 #include "omap-pcm.h"
 #include "../codecs/twl4030.h"
 
+#define TWL4030_INTBR_PMBR1	0x0D
+#define EXTMUTE(value)		(value << 2)
+
 static struct snd_soc_card snd_soc_sdp3430;
 
 static int sdp3430_hw_params(struct snd_pcm_substream *substream,
@@ -84,6 +88,49 @@
 	.hw_params = sdp3430_hw_params,
 };
 
+static int sdp3430_hw_voice_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	int ret;
+
+	/* Set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai,
+				SND_SOC_DAIFMT_DSP_A |
+				SND_SOC_DAIFMT_IB_NF |
+				SND_SOC_DAIFMT_CBM_CFM);
+	if (ret) {
+		printk(KERN_ERR "can't set codec DAI configuration\n");
+		return ret;
+	}
+
+	/* Set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai,
+				SND_SOC_DAIFMT_DSP_A |
+				SND_SOC_DAIFMT_IB_NF |
+				SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set cpu DAI configuration\n");
+		return ret;
+	}
+
+	/* Set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+					    SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set codec system clock\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops sdp3430_voice_ops = {
+	.hw_params = sdp3430_hw_voice_params,
+};
+
 /* Headset jack */
 static struct snd_soc_jack hs_jack;
 
@@ -192,28 +239,59 @@
 	return ret;
 }
 
+static int sdp3430_twl4030_voice_init(struct snd_soc_codec *codec)
+{
+	unsigned short reg;
+
+	/* Enable voice interface */
+	reg = codec->read(codec, TWL4030_REG_VOICE_IF);
+	reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN;
+	codec->write(codec, TWL4030_REG_VOICE_IF, reg);
+
+	return 0;
+}
+
+
 /* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link sdp3430_dai = {
-	.name = "TWL4030",
-	.stream_name = "TWL4030",
-	.cpu_dai = &omap_mcbsp_dai[0],
-	.codec_dai = &twl4030_dai,
-	.init = sdp3430_twl4030_init,
-	.ops = &sdp3430_ops,
+static struct snd_soc_dai_link sdp3430_dai[] = {
+	{
+		.name = "TWL4030 I2S",
+		.stream_name = "TWL4030 Audio",
+		.cpu_dai = &omap_mcbsp_dai[0],
+		.codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
+		.init = sdp3430_twl4030_init,
+		.ops = &sdp3430_ops,
+	},
+	{
+		.name = "TWL4030 PCM",
+		.stream_name = "TWL4030 Voice",
+		.cpu_dai = &omap_mcbsp_dai[1],
+		.codec_dai = &twl4030_dai[TWL4030_DAI_VOICE],
+		.init = sdp3430_twl4030_voice_init,
+		.ops = &sdp3430_voice_ops,
+	},
 };
 
 /* Audio machine driver */
 static struct snd_soc_card snd_soc_sdp3430 = {
 	.name = "SDP3430",
 	.platform = &omap_soc_platform,
-	.dai_link = &sdp3430_dai,
-	.num_links = 1,
+	.dai_link = sdp3430_dai,
+	.num_links = ARRAY_SIZE(sdp3430_dai),
+};
+
+/* twl4030 setup */
+static struct twl4030_setup_data twl4030_setup = {
+	.ramp_delay_value = 3,
+	.sysclk = 26000,
+	.hs_extmute = 1,
 };
 
 /* Audio subsystem */
 static struct snd_soc_device sdp3430_snd_devdata = {
 	.card = &snd_soc_sdp3430,
 	.codec_dev = &soc_codec_dev_twl4030,
+	.codec_data = &twl4030_setup,
 };
 
 static struct platform_device *sdp3430_snd_device;
@@ -236,7 +314,12 @@
 
 	platform_set_drvdata(sdp3430_snd_device, &sdp3430_snd_devdata);
 	sdp3430_snd_devdata.dev = &sdp3430_snd_device->dev;
-	*(unsigned int *)sdp3430_dai.cpu_dai->private_data = 1; /* McBSP2 */
+	*(unsigned int *)sdp3430_dai[0].cpu_dai->private_data = 1; /* McBSP2 */
+	*(unsigned int *)sdp3430_dai[1].cpu_dai->private_data = 2; /* McBSP3 */
+
+	/* Set TWL4030 GPIO6 as EXTMUTE signal */
+	twl4030_i2c_write_u8(TWL4030_MODULE_INTBR, EXTMUTE(0x02),
+							TWL4030_MODULE_INTBR);
 
 	ret = platform_device_add(sdp3430_snd_device);
 	if (ret)
diff --git a/sound/soc/omap/zoom2.c b/sound/soc/omap/zoom2.c
new file mode 100644
index 0000000..f90b45f
--- /dev/null
+++ b/sound/soc/omap/zoom2.c
@@ -0,0 +1,314 @@
+/*
+ * zoom2.c  --  SoC audio for Zoom2
+ *
+ * Author: Misael Lopez Cruz <x0052729@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/mcbsp.h>
+
+#include "omap-mcbsp.h"
+#include "omap-pcm.h"
+#include "../codecs/twl4030.h"
+
+#define ZOOM2_HEADSET_MUX_GPIO		(OMAP_MAX_GPIO_LINES + 15)
+#define ZOOM2_HEADSET_EXTMUTE_GPIO	153
+
+static int zoom2_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	int ret;
+
+	/* Set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai,
+				  SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set codec DAI configuration\n");
+		return ret;
+	}
+
+	/* Set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai,
+				  SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_NF |
+				  SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set cpu DAI configuration\n");
+		return ret;
+	}
+
+	/* Set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+					SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set codec system clock\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops zoom2_ops = {
+	.hw_params = zoom2_hw_params,
+};
+
+static int zoom2_hw_voice_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	int ret;
+
+	/* Set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai,
+				SND_SOC_DAIFMT_DSP_A |
+				SND_SOC_DAIFMT_IB_NF |
+				SND_SOC_DAIFMT_CBM_CFM);
+	if (ret) {
+		printk(KERN_ERR "can't set codec DAI configuration\n");
+		return ret;
+	}
+
+	/* Set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai,
+				SND_SOC_DAIFMT_DSP_A |
+				SND_SOC_DAIFMT_IB_NF |
+				SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set cpu DAI configuration\n");
+		return ret;
+	}
+
+	/* Set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000,
+					SND_SOC_CLOCK_IN);
+	if (ret < 0) {
+		printk(KERN_ERR "can't set codec system clock\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops zoom2_voice_ops = {
+	.hw_params = zoom2_hw_voice_params,
+};
+
+/* Zoom2 machine DAPM */
+static const struct snd_soc_dapm_widget zoom2_twl4030_dapm_widgets[] = {
+	SND_SOC_DAPM_MIC("Ext Mic", NULL),
+	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+	SND_SOC_DAPM_LINE("Aux In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* External Mics: MAINMIC, SUBMIC with bias*/
+	{"MAINMIC", NULL, "Mic Bias 1"},
+	{"SUBMIC", NULL, "Mic Bias 2"},
+	{"Mic Bias 1", NULL, "Ext Mic"},
+	{"Mic Bias 2", NULL, "Ext Mic"},
+
+	/* External Speakers: HFL, HFR */
+	{"Ext Spk", NULL, "HFL"},
+	{"Ext Spk", NULL, "HFR"},
+
+	/* Headset Stereophone:  HSOL, HSOR */
+	{"Headset Stereophone", NULL, "HSOL"},
+	{"Headset Stereophone", NULL, "HSOR"},
+
+	/* Headset Mic: HSMIC with bias */
+	{"HSMIC", NULL, "Headset Mic Bias"},
+	{"Headset Mic Bias", NULL, "Headset Mic"},
+
+	/* Aux In: AUXL, AUXR */
+	{"Aux In", NULL, "AUXL"},
+	{"Aux In", NULL, "AUXR"},
+};
+
+static int zoom2_twl4030_init(struct snd_soc_codec *codec)
+{
+	int ret;
+
+	/* Add Zoom2 specific widgets */
+	ret = snd_soc_dapm_new_controls(codec, zoom2_twl4030_dapm_widgets,
+				ARRAY_SIZE(zoom2_twl4030_dapm_widgets));
+	if (ret)
+		return ret;
+
+	/* Set up Zoom2 specific audio path audio_map */
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	/* Zoom2 connected pins */
+	snd_soc_dapm_enable_pin(codec, "Ext Mic");
+	snd_soc_dapm_enable_pin(codec, "Ext Spk");
+	snd_soc_dapm_enable_pin(codec, "Headset Mic");
+	snd_soc_dapm_enable_pin(codec, "Headset Stereophone");
+	snd_soc_dapm_enable_pin(codec, "Aux In");
+
+	/* TWL4030 not connected pins */
+	snd_soc_dapm_nc_pin(codec, "CARKITMIC");
+	snd_soc_dapm_nc_pin(codec, "DIGIMIC0");
+	snd_soc_dapm_nc_pin(codec, "DIGIMIC1");
+
+	snd_soc_dapm_nc_pin(codec, "OUTL");
+	snd_soc_dapm_nc_pin(codec, "OUTR");
+	snd_soc_dapm_nc_pin(codec, "EARPIECE");
+	snd_soc_dapm_nc_pin(codec, "PREDRIVEL");
+	snd_soc_dapm_nc_pin(codec, "PREDRIVER");
+	snd_soc_dapm_nc_pin(codec, "CARKITL");
+	snd_soc_dapm_nc_pin(codec, "CARKITR");
+
+	ret = snd_soc_dapm_sync(codec);
+
+	return ret;
+}
+
+static int zoom2_twl4030_voice_init(struct snd_soc_codec *codec)
+{
+	unsigned short reg;
+
+	/* Enable voice interface */
+	reg = codec->read(codec, TWL4030_REG_VOICE_IF);
+	reg |= TWL4030_VIF_DIN_EN | TWL4030_VIF_DOUT_EN | TWL4030_VIF_EN;
+	codec->write(codec, TWL4030_REG_VOICE_IF, reg);
+
+	return 0;
+}
+
+/* Digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link zoom2_dai[] = {
+	{
+		.name = "TWL4030 I2S",
+		.stream_name = "TWL4030 Audio",
+		.cpu_dai = &omap_mcbsp_dai[0],
+		.codec_dai = &twl4030_dai[TWL4030_DAI_HIFI],
+		.init = zoom2_twl4030_init,
+		.ops = &zoom2_ops,
+	},
+	{
+		.name = "TWL4030 PCM",
+		.stream_name = "TWL4030 Voice",
+		.cpu_dai = &omap_mcbsp_dai[1],
+		.codec_dai = &twl4030_dai[TWL4030_DAI_VOICE],
+		.init = zoom2_twl4030_voice_init,
+		.ops = &zoom2_voice_ops,
+	},
+};
+
+/* Audio machine driver */
+static struct snd_soc_card snd_soc_zoom2 = {
+	.name = "Zoom2",
+	.platform = &omap_soc_platform,
+	.dai_link = zoom2_dai,
+	.num_links = ARRAY_SIZE(zoom2_dai),
+};
+
+/* EXTMUTE callback function */
+void zoom2_set_hs_extmute(int mute)
+{
+	gpio_set_value(ZOOM2_HEADSET_EXTMUTE_GPIO, mute);
+}
+
+/* twl4030 setup */
+static struct twl4030_setup_data twl4030_setup = {
+	.ramp_delay_value = 3,	/* 161 ms */
+	.sysclk = 26000,
+	.hs_extmute = 1,
+	.set_hs_extmute = zoom2_set_hs_extmute,
+};
+
+/* Audio subsystem */
+static struct snd_soc_device zoom2_snd_devdata = {
+	.card = &snd_soc_zoom2,
+	.codec_dev = &soc_codec_dev_twl4030,
+	.codec_data = &twl4030_setup,
+};
+
+static struct platform_device *zoom2_snd_device;
+
+static int __init zoom2_soc_init(void)
+{
+	int ret;
+
+	if (!machine_is_omap_zoom2()) {
+		pr_debug("Not Zoom2!\n");
+		return -ENODEV;
+	}
+	printk(KERN_INFO "Zoom2 SoC init\n");
+
+	zoom2_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!zoom2_snd_device) {
+		printk(KERN_ERR "Platform device allocation failed\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(zoom2_snd_device, &zoom2_snd_devdata);
+	zoom2_snd_devdata.dev = &zoom2_snd_device->dev;
+	*(unsigned int *)zoom2_dai[0].cpu_dai->private_data = 1; /* McBSP2 */
+	*(unsigned int *)zoom2_dai[1].cpu_dai->private_data = 2; /* McBSP3 */
+
+	ret = platform_device_add(zoom2_snd_device);
+	if (ret)
+		goto err1;
+
+	BUG_ON(gpio_request(ZOOM2_HEADSET_MUX_GPIO, "hs_mux") < 0);
+	gpio_direction_output(ZOOM2_HEADSET_MUX_GPIO, 0);
+
+	BUG_ON(gpio_request(ZOOM2_HEADSET_EXTMUTE_GPIO, "ext_mute") < 0);
+	gpio_direction_output(ZOOM2_HEADSET_EXTMUTE_GPIO, 0);
+
+	return 0;
+
+err1:
+	printk(KERN_ERR "Unable to add platform device\n");
+	platform_device_put(zoom2_snd_device);
+
+	return ret;
+}
+module_init(zoom2_soc_init);
+
+static void __exit zoom2_soc_exit(void)
+{
+	gpio_free(ZOOM2_HEADSET_MUX_GPIO);
+	gpio_free(ZOOM2_HEADSET_EXTMUTE_GPIO);
+
+	platform_device_unregister(zoom2_snd_device);
+}
+module_exit(zoom2_soc_exit);
+
+MODULE_AUTHOR("Misael Lopez Cruz <x0052729@ti.com>");
+MODULE_DESCRIPTION("ALSA SoC Zoom2");
+MODULE_LICENSE("GPL");
+
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index ad8a10f..6375b4e 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -89,22 +89,23 @@
 	  Toshiba e800 PDA
 
 config SND_PXA2XX_SOC_EM_X270
-	tristate "SoC Audio support for CompuLab EM-x270"
+	tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300"
 	depends on SND_PXA2XX_SOC && MACH_EM_X270
 	select SND_PXA2XX_SOC_AC97
 	select SND_SOC_WM9712
 	help
 	  Say Y if you want to add support for SoC audio on
-	  CompuLab EM-x270.
+	  CompuLab EM-x270, eXeda and CM-X300 machines.
 
 config SND_PXA2XX_SOC_PALM27X
-	bool "SoC Audio support for Palm T|X, T5 and LifeDrive"
-	depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || MACH_PALMT5)
+	bool "SoC Audio support for Palm T|X, T5, E2 and LifeDrive"
+	depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || \
+			MACH_PALMT5 || MACH_PALMTE2)
 	select SND_PXA2XX_SOC_AC97
 	select SND_SOC_WM9712
 	help
 	  Say Y if you want to add support for SoC audio on
-	  Palm T|X, T5 or LifeDrive handheld computer.
+	  Palm T|X, T5, E2 or LifeDrive handheld computer.
 
 config SND_SOC_ZYLONITE
 	tristate "SoC Audio support for Marvell Zylonite"
@@ -134,3 +135,12 @@
         help
           Say Y if you want to add support for SoC audio on the
           MIO A701.
+
+config SND_PXA2XX_SOC_IMOTE2
+       tristate "SoC Audio support for IMote 2"
+       depends on SND_PXA2XX_SOC && MACH_INTELMOTE2
+       select SND_PXA2XX_SOC_I2S
+       select SND_SOC_WM8940
+       help
+         Say Y if you want to add support for SoC audio on the
+	 IMote 2.
diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile
index 4b90c3c..6e096b4 100644
--- a/sound/soc/pxa/Makefile
+++ b/sound/soc/pxa/Makefile
@@ -22,6 +22,7 @@
 snd-soc-zylonite-objs := zylonite.o
 snd-soc-magician-objs := magician.o
 snd-soc-mioa701-objs := mioa701_wm9713.o
+snd-soc-imote2-objs := imote2.o
 
 obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
 obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
@@ -35,3 +36,4 @@
 obj-$(CONFIG_SND_PXA2XX_SOC_MAGICIAN) += snd-soc-magician.o
 obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
 obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
+obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index d5be2b3..fefe1a5 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -320,38 +320,6 @@
 	.codec_dev = &soc_codec_dev_wm8731,
 };
 
-/*
- * FIXME: This is a temporary bodge to avoid cross-tree merge issues.
- * New drivers should register the wm8731 I2C device in the machine
- * setup code (under arch/arm for ARM systems).
- */
-static int wm8731_i2c_register(void)
-{
-	struct i2c_board_info info;
-	struct i2c_adapter *adapter;
-	struct i2c_client *client;
-
-	memset(&info, 0, sizeof(struct i2c_board_info));
-	info.addr = 0x1b;
-	strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
-
-	adapter = i2c_get_adapter(0);
-	if (!adapter) {
-		printk(KERN_ERR "can't get i2c adapter 0\n");
-		return -ENODEV;
-	}
-
-	client = i2c_new_device(adapter, &info);
-	i2c_put_adapter(adapter);
-	if (!client) {
-		printk(KERN_ERR "can't add i2c device at 0x%x\n",
-			(unsigned int)info.addr);
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
 static struct platform_device *corgi_snd_device;
 
 static int __init corgi_init(void)
@@ -362,10 +330,6 @@
 	      machine_is_husky()))
 		return -ENODEV;
 
-	ret = wm8731_i2c_register();
-	if (ret != 0)
-		return ret;
-
 	corgi_snd_device = platform_device_alloc("soc-audio", -1);
 	if (!corgi_snd_device)
 		return -ENOMEM;
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
index 949be9c..f4756e4 100644
--- a/sound/soc/pxa/em-x270.c
+++ b/sound/soc/pxa/em-x270.c
@@ -1,7 +1,7 @@
 /*
- * em-x270.c  --  SoC audio for EM-X270
+ * SoC audio driver for EM-X270, eXeda and CM-X300
  *
- * Copyright 2007 CompuLab, Ltd.
+ * Copyright 2007, 2009 CompuLab, Ltd.
  *
  * Author: Mike Rapoport <mike@compulab.co.il>
  *
@@ -68,7 +68,8 @@
 {
 	int ret;
 
-	if (!machine_is_em_x270())
+	if (!(machine_is_em_x270() || machine_is_exeda()
+	      || machine_is_cm_x300()))
 		return -ENODEV;
 
 	em_x270_snd_device = platform_device_alloc("soc-audio", -1);
@@ -95,5 +96,5 @@
 
 /* Module information */
 MODULE_AUTHOR("Mike Rapoport");
-MODULE_DESCRIPTION("ALSA SoC EM-X270");
+MODULE_DESCRIPTION("ALSA SoC EM-X270, eXeda and CM-X300");
 MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c
new file mode 100644
index 0000000..405587a
--- /dev/null
+++ b/sound/soc/pxa/imote2.c
@@ -0,0 +1,114 @@
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include <asm/mach-types.h>
+
+#include "../codecs/wm8940.h"
+#include "pxa2xx-i2s.h"
+#include "pxa2xx-pcm.h"
+
+static int imote2_asoc_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	unsigned int clk = 0;
+	int ret;
+
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+	case 48000:
+	case 96000:
+		clk = 12288000;
+		break;
+	case 11025:
+	case 22050:
+	case 44100:
+		clk = 11289600;
+		break;
+	}
+
+	/* set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
+				  | SND_SOC_DAIFMT_NB_NF
+				  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	/* CPU should be clock master */
+	ret = snd_soc_dai_set_fmt(cpu_dai,  SND_SOC_DAIFMT_I2S
+				  | SND_SOC_DAIFMT_NB_NF
+				  | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk,
+				     SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* set the I2S system clock as input (unused) */
+	ret = snd_soc_dai_set_sysclk(cpu_dai, PXA2XX_I2S_SYSCLK, clk,
+		SND_SOC_CLOCK_OUT);
+
+	return ret;
+}
+
+static struct snd_soc_ops imote2_asoc_ops = {
+	.hw_params = imote2_asoc_hw_params,
+};
+
+static struct snd_soc_dai_link imote2_dai = {
+	.name = "WM8940",
+	.stream_name = "WM8940",
+	.cpu_dai = &pxa_i2s_dai,
+	.codec_dai = &wm8940_dai,
+	.ops = &imote2_asoc_ops,
+};
+
+static struct snd_soc_card snd_soc_imote2 = {
+	.name = "Imote2",
+	.platform = &pxa2xx_soc_platform,
+	.dai_link = &imote2_dai,
+	.num_links = 1,
+};
+
+static struct snd_soc_device imote2_snd_devdata = {
+	.card = &snd_soc_imote2,
+	.codec_dev = &soc_codec_dev_wm8940,
+};
+
+static struct platform_device *imote2_snd_device;
+
+static int __init imote2_asoc_init(void)
+{
+	int ret;
+
+	if (!machine_is_intelmote2())
+		return -ENODEV;
+	imote2_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!imote2_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(imote2_snd_device, &imote2_snd_devdata);
+	imote2_snd_devdata.dev = &imote2_snd_device->dev;
+	ret = platform_device_add(imote2_snd_device);
+	if (ret)
+		platform_device_put(imote2_snd_device);
+
+	return ret;
+}
+module_init(imote2_asoc_init);
+
+static void __exit imote2_asoc_exit(void)
+{
+	platform_device_unregister(imote2_snd_device);
+}
+module_exit(imote2_asoc_exit);
+
+MODULE_AUTHOR("Jonathan Cameron");
+MODULE_DESCRIPTION("ALSA SoC Imote 2");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
index 0625c34..9f7c61e 100644
--- a/sound/soc/pxa/magician.c
+++ b/sound/soc/pxa/magician.c
@@ -20,12 +20,14 @@
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
+#include <linux/i2c.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include <sound/uda1380.h>
 
 #include <mach/magician.h>
 #include <asm/mach-types.h>
@@ -106,7 +108,7 @@
 			/* 513156 Hz ~= _2_ * 8000 Hz * 32 (+0.23%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_16;
 			break;
-		case 32:
+		default: /* 32 */
 			/* 1026312 Hz ~= _2_ * 8000 Hz * 64 (+0.23%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_8;
 		}
@@ -118,7 +120,7 @@
 			/* 351375 Hz ~= 11025 Hz * 32 (-0.41%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_4;
 			break;
-		case 32:
+		default: /* 32 */
 			/* 702750 Hz ~= 11025 Hz * 64 (-0.41%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_2;
 		}
@@ -130,7 +132,7 @@
 			/* 702750 Hz ~= 22050 Hz * 32 (-0.41%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_2;
 			break;
-		case 32:
+		default: /* 32 */
 			/* 1405500 Hz ~= 22050 Hz * 64 (-0.41%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_1;
 		}
@@ -142,7 +144,7 @@
 			/* 1405500 Hz ~= 44100 Hz * 32 (-0.41%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_2;
 			break;
-		case 32:
+		default: /* 32 */
 			/* 2811000 Hz ~= 44100 Hz * 64 (-0.41%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_1;
 		}
@@ -154,19 +156,20 @@
 			/* 1529375 Hz ~= 48000 Hz * 32 (-0.44%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_2;
 			break;
-		case 32:
+		default: /* 32 */
 			/* 3058750 Hz ~= 48000 Hz * 64 (-0.44%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_1;
 		}
 		break;
 	case 96000:
+	default:
 		acps = 12235000;
 		switch (width) {
 		case 16:
 			/* 3058750 Hz ~= 96000 Hz * 32 (-0.44%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_1;
 			break;
-		case 32:
+		default: /* 32 */
 			/* 6117500 Hz ~= 96000 Hz * 64 (-0.44%) */
 			acds = PXA_SSP_CLK_AUDIO_DIV_2;
 			div4 = PXA_SSP_CLK_SCDB_1;
@@ -183,11 +186,11 @@
 
 	/* set cpu DAI configuration */
 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
-			SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBS_CFS);
+			SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_CBS_CFS);
 	if (ret < 0)
 		return ret;
 
-	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 1);
+	ret = snd_soc_dai_set_tdm_slot(cpu_dai, 1, 0, 1, width);
 	if (ret < 0)
 		return ret;
 
@@ -446,34 +449,47 @@
 	.platform = &pxa2xx_soc_platform,
 };
 
-/* magician audio private data */
-static struct uda1380_setup_data magician_uda1380_setup = {
-	.i2c_address = 0x18,
-	.dac_clk = UDA1380_DAC_CLK_WSPLL,
-};
-
 /* magician audio subsystem */
 static struct snd_soc_device magician_snd_devdata = {
 	.card = &snd_soc_card_magician,
 	.codec_dev = &soc_codec_dev_uda1380,
-	.codec_data = &magician_uda1380_setup,
 };
 
 static struct platform_device *magician_snd_device;
 
+/*
+ * FIXME: move into magician board file once merged into the pxa tree
+ */
+static struct uda1380_platform_data uda1380_info = {
+	.gpio_power = EGPIO_MAGICIAN_CODEC_POWER,
+	.gpio_reset = EGPIO_MAGICIAN_CODEC_RESET,
+	.dac_clk    = UDA1380_DAC_CLK_WSPLL,
+};
+
+static struct i2c_board_info i2c_board_info[] = {
+	{
+		I2C_BOARD_INFO("uda1380", 0x18),
+		.platform_data = &uda1380_info,
+	},
+};
+
 static int __init magician_init(void)
 {
 	int ret;
+	struct i2c_adapter *adapter;
+	struct i2c_client *client;
 
 	if (!machine_is_magician())
 		return -ENODEV;
 
-	ret = gpio_request(EGPIO_MAGICIAN_CODEC_POWER, "CODEC_POWER");
-	if (ret)
-		goto err_request_power;
-	ret = gpio_request(EGPIO_MAGICIAN_CODEC_RESET, "CODEC_RESET");
-	if (ret)
-		goto err_request_reset;
+	adapter = i2c_get_adapter(0);
+	if (!adapter)
+		return -ENODEV;
+	client = i2c_new_device(adapter, i2c_board_info);
+	i2c_put_adapter(adapter);
+	if (!client)
+		return -ENODEV;
+
 	ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER");
 	if (ret)
 		goto err_request_spk;
@@ -490,14 +506,8 @@
 	if (ret)
 		goto err_request_in_sel1;
 
-	gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 1);
 	gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0);
 
-	/* we may need to have the clock running here - pH5 */
-	gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 1);
-	udelay(5);
-	gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 0);
-
 	magician_snd_device = platform_device_alloc("soc-audio", -1);
 	if (!magician_snd_device) {
 		ret = -ENOMEM;
@@ -525,10 +535,6 @@
 err_request_ep:
 	gpio_free(EGPIO_MAGICIAN_SPK_POWER);
 err_request_spk:
-	gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
-err_request_reset:
-	gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
-err_request_power:
 	return ret;
 }
 
@@ -539,15 +545,12 @@
 	gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0);
 	gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0);
 	gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0);
-	gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 0);
 
 	gpio_free(EGPIO_MAGICIAN_IN_SEL1);
 	gpio_free(EGPIO_MAGICIAN_IN_SEL0);
 	gpio_free(EGPIO_MAGICIAN_MIC_POWER);
 	gpio_free(EGPIO_MAGICIAN_EP_POWER);
 	gpio_free(EGPIO_MAGICIAN_SPK_POWER);
-	gpio_free(EGPIO_MAGICIAN_CODEC_RESET);
-	gpio_free(EGPIO_MAGICIAN_CODEC_POWER);
 }
 
 module_init(magician_init);
diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c
index 44fcc4e..1f96e32 100644
--- a/sound/soc/pxa/palm27x.c
+++ b/sound/soc/pxa/palm27x.c
@@ -17,13 +17,12 @@
 #include <linux/moduleparam.h>
 #include <linux/device.h>
 #include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/irq.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include <sound/jack.h>
 
 #include <asm/mach-types.h>
 #include <mach/audio.h>
@@ -33,90 +32,31 @@
 #include "pxa2xx-pcm.h"
 #include "pxa2xx-ac97.h"
 
-static int palm27x_jack_func = 1;
-static int palm27x_spk_func = 1;
-static int palm27x_ep_gpio = -1;
+static struct snd_soc_jack hs_jack;
 
-static void palm27x_ext_control(struct snd_soc_codec *codec)
-{
-	if (!palm27x_spk_func)
-		snd_soc_dapm_enable_pin(codec, "Speaker");
-	else
-		snd_soc_dapm_disable_pin(codec, "Speaker");
-
-	if (!palm27x_jack_func)
-		snd_soc_dapm_enable_pin(codec, "Headphone Jack");
-	else
-		snd_soc_dapm_disable_pin(codec, "Headphone Jack");
-
-	snd_soc_dapm_sync(codec);
-}
-
-static int palm27x_startup(struct snd_pcm_substream *substream)
-{
-	struct snd_soc_pcm_runtime *rtd = substream->private_data;
-	struct snd_soc_codec *codec = rtd->socdev->card->codec;
-
-	/* check the jack status at stream startup */
-	palm27x_ext_control(codec);
-	return 0;
-}
-
-static struct snd_soc_ops palm27x_ops = {
-	.startup = palm27x_startup,
+/* Headphones jack detection DAPM pins */
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+	{
+		.pin    = "Headphone Jack",
+		.mask   = SND_JACK_HEADPHONE,
+	},
 };
 
-static irqreturn_t palm27x_interrupt(int irq, void *v)
-{
-	palm27x_spk_func = gpio_get_value(palm27x_ep_gpio);
-	palm27x_jack_func = !palm27x_spk_func;
-	return IRQ_HANDLED;
-}
+/* Headphones jack detection gpios */
+static struct snd_soc_jack_gpio hs_jack_gpios[] = {
+	[0] = {
+		/* gpio is set on per-platform basis */
+		.name           = "hp-gpio",
+		.report         = SND_JACK_HEADPHONE,
+		.debounce_time	= 200,
+	},
+};
 
-static int palm27x_get_jack(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	ucontrol->value.integer.value[0] = palm27x_jack_func;
-	return 0;
-}
-
-static int palm27x_set_jack(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-
-	if (palm27x_jack_func == ucontrol->value.integer.value[0])
-		return 0;
-
-	palm27x_jack_func = ucontrol->value.integer.value[0];
-	palm27x_ext_control(codec);
-	return 1;
-}
-
-static int palm27x_get_spk(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	ucontrol->value.integer.value[0] = palm27x_spk_func;
-	return 0;
-}
-
-static int palm27x_set_spk(struct snd_kcontrol *kcontrol,
-	struct snd_ctl_elem_value *ucontrol)
-{
-	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-
-	if (palm27x_spk_func == ucontrol->value.integer.value[0])
-		return 0;
-
-	palm27x_spk_func = ucontrol->value.integer.value[0];
-	palm27x_ext_control(codec);
-	return 1;
-}
-
-/* PalmTX machine dapm widgets */
+/* Palm27x machine dapm widgets */
 static const struct snd_soc_dapm_widget palm27x_dapm_widgets[] = {
 	SND_SOC_DAPM_HP("Headphone Jack", NULL),
-	SND_SOC_DAPM_SPK("Speaker", NULL),
+	SND_SOC_DAPM_SPK("Ext. Speaker", NULL),
+	SND_SOC_DAPM_MIC("Ext. Microphone", NULL),
 };
 
 /* PalmTX audio map */
@@ -126,46 +66,66 @@
 	{"Headphone Jack", NULL, "HPOUTR"},
 
 	/* ext speaker connected to ROUT2, LOUT2 */
-	{"Speaker", NULL, "LOUT2"},
-	{"Speaker", NULL, "ROUT2"},
+	{"Ext. Speaker", NULL, "LOUT2"},
+	{"Ext. Speaker", NULL, "ROUT2"},
+
+	/* mic connected to MIC1 */
+	{"Ext. Microphone", NULL, "MIC1"},
 };
 
-static const char *jack_function[] = {"Headphone", "Off"};
-static const char *spk_function[] = {"On", "Off"};
-static const struct soc_enum palm27x_enum[] = {
-	SOC_ENUM_SINGLE_EXT(2, jack_function),
-	SOC_ENUM_SINGLE_EXT(2, spk_function),
-};
-
-static const struct snd_kcontrol_new palm27x_controls[] = {
-	SOC_ENUM_EXT("Jack Function", palm27x_enum[0], palm27x_get_jack,
-		palm27x_set_jack),
-	SOC_ENUM_EXT("Speaker Function", palm27x_enum[1], palm27x_get_spk,
-		palm27x_set_spk),
-};
+static struct snd_soc_card palm27x_asoc;
 
 static int palm27x_ac97_init(struct snd_soc_codec *codec)
 {
 	int err;
 
-	snd_soc_dapm_nc_pin(codec, "OUT3");
-	snd_soc_dapm_nc_pin(codec, "MONOOUT");
-
-	/* add palm27x specific controls */
-	err = snd_soc_add_controls(codec, palm27x_controls,
-				ARRAY_SIZE(palm27x_controls));
-	if (err < 0)
+	/* add palm27x specific widgets */
+	err = snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets,
+				ARRAY_SIZE(palm27x_dapm_widgets));
+	if (err)
 		return err;
 
-	/* add palm27x specific widgets */
-	snd_soc_dapm_new_controls(codec, palm27x_dapm_widgets,
-				ARRAY_SIZE(palm27x_dapm_widgets));
-
 	/* set up palm27x specific audio path audio_map */
-	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+	err = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+	if (err)
+		return err;
 
-	snd_soc_dapm_sync(codec);
-	return 0;
+	/* connected pins */
+	if (machine_is_palmld())
+		snd_soc_dapm_enable_pin(codec, "MIC1");
+	snd_soc_dapm_enable_pin(codec, "HPOUTL");
+	snd_soc_dapm_enable_pin(codec, "HPOUTR");
+	snd_soc_dapm_enable_pin(codec, "LOUT2");
+	snd_soc_dapm_enable_pin(codec, "ROUT2");
+
+	/* not connected pins */
+	snd_soc_dapm_nc_pin(codec, "OUT3");
+	snd_soc_dapm_nc_pin(codec, "MONOOUT");
+	snd_soc_dapm_nc_pin(codec, "LINEINL");
+	snd_soc_dapm_nc_pin(codec, "LINEINR");
+	snd_soc_dapm_nc_pin(codec, "PCBEEP");
+	snd_soc_dapm_nc_pin(codec, "PHONE");
+	snd_soc_dapm_nc_pin(codec, "MIC2");
+
+	err = snd_soc_dapm_sync(codec);
+	if (err)
+		return err;
+
+	/* Jack detection API stuff */
+	err = snd_soc_jack_new(&palm27x_asoc, "Headphone Jack",
+				SND_JACK_HEADPHONE, &hs_jack);
+	if (err)
+		return err;
+
+	err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
+				hs_jack_pins);
+	if (err)
+		return err;
+
+	err = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
+				hs_jack_gpios);
+
+	return err;
 }
 
 static struct snd_soc_dai_link palm27x_dai[] = {
@@ -175,14 +135,12 @@
 	.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
 	.codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI],
 	.init = palm27x_ac97_init,
-	.ops = &palm27x_ops,
 },
 {
 	.name = "AC97 Aux",
 	.stream_name = "AC97 Aux",
 	.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
 	.codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX],
-	.ops = &palm27x_ops,
 },
 };
 
@@ -205,30 +163,20 @@
 	int ret;
 
 	if (!(machine_is_palmtx() || machine_is_palmt5() ||
-		machine_is_palmld()))
+		machine_is_palmld() || machine_is_palmte2()))
 		return -ENODEV;
 
-	if (pdev->dev.platform_data)
-		palm27x_ep_gpio = ((struct palm27x_asoc_info *)
+	if (!pdev->dev.platform_data) {
+		dev_err(&pdev->dev, "please supply platform_data\n");
+		return -ENODEV;
+	}
+
+	hs_jack_gpios[0].gpio = ((struct palm27x_asoc_info *)
 			(pdev->dev.platform_data))->jack_gpio;
 
-	ret = gpio_request(palm27x_ep_gpio, "Headphone Jack");
-	if (ret)
-		return ret;
-	ret = gpio_direction_input(palm27x_ep_gpio);
-	if (ret)
-		goto err_alloc;
-
-	if (request_irq(gpio_to_irq(palm27x_ep_gpio), palm27x_interrupt,
-			IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-			"Headphone jack", NULL))
-		goto err_alloc;
-
 	palm27x_snd_device = platform_device_alloc("soc-audio", -1);
-	if (!palm27x_snd_device) {
-		ret = -ENOMEM;
-		goto err_dev;
-	}
+	if (!palm27x_snd_device)
+		return -ENOMEM;
 
 	platform_set_drvdata(palm27x_snd_device, &palm27x_snd_devdata);
 	palm27x_snd_devdata.dev = &palm27x_snd_device->dev;
@@ -241,18 +189,12 @@
 
 put_device:
 	platform_device_put(palm27x_snd_device);
-err_dev:
-	free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
-err_alloc:
-	gpio_free(palm27x_ep_gpio);
 
 	return ret;
 }
 
 static int __devexit palm27x_asoc_remove(struct platform_device *pdev)
 {
-	free_irq(gpio_to_irq(palm27x_ep_gpio), NULL);
-	gpio_free(palm27x_ep_gpio);
 	platform_device_unregister(palm27x_snd_device);
 	return 0;
 }
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index a51058f..c5f36e0 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -280,38 +280,6 @@
 	.num_links = 1,
 };
 
-/*
- * FIXME: This is a temporary bodge to avoid cross-tree merge issues.
- * New drivers should register the wm8731 I2C device in the machine
- * setup code (under arch/arm for ARM systems).
- */
-static int wm8731_i2c_register(void)
-{
-	struct i2c_board_info info;
-	struct i2c_adapter *adapter;
-	struct i2c_client *client;
-
-	memset(&info, 0, sizeof(struct i2c_board_info));
-	info.addr = 0x1b;
-	strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
-
-	adapter = i2c_get_adapter(0);
-	if (!adapter) {
-		printk(KERN_ERR "can't get i2c adapter 0\n");
-		return -ENODEV;
-	}
-
-	client = i2c_new_device(adapter, &info);
-	i2c_put_adapter(adapter);
-	if (!client) {
-		printk(KERN_ERR "can't add i2c device at 0x%x\n",
-			(unsigned int)info.addr);
-		return -ENODEV;
-	}
-
-	return 0;
-}
-
 /* poodle audio subsystem */
 static struct snd_soc_device poodle_snd_devdata = {
 	.card = &snd_soc_poodle,
@@ -327,10 +295,6 @@
 	if (!machine_is_poodle())
 		return -ENODEV;
 
-	ret = wm8731_i2c_register();
-	if (ret != 0)
-		return ret;
-
 	locomo_gpio_set_dir(&poodle_locomo_device.dev,
 		POODLE_LOCOMO_GPIO_AMP_ON, 0);
 	/* should we mute HP at startup - burning power ?*/
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 286be31..5b9ed64 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -50,139 +50,6 @@
 #endif
 };
 
-#define PXA2xx_SSP1_BASE	0x41000000
-#define PXA27x_SSP2_BASE	0x41700000
-#define PXA27x_SSP3_BASE	0x41900000
-#define PXA3xx_SSP4_BASE	0x41a00000
-
-static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_out = {
-	.name			= "SSP1 PCM Mono out",
-	.dev_addr		= PXA2xx_SSP1_BASE + SSDR,
-	.drcmr			= &DRCMR(14),
-	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
-				  DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_mono_in = {
-	.name			= "SSP1 PCM Mono in",
-	.dev_addr		= PXA2xx_SSP1_BASE + SSDR,
-	.drcmr			= &DRCMR(13),
-	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
-				  DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_out = {
-	.name			= "SSP1 PCM Stereo out",
-	.dev_addr		= PXA2xx_SSP1_BASE + SSDR,
-	.drcmr			= &DRCMR(14),
-	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
-				  DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp1_pcm_stereo_in = {
-	.name			= "SSP1 PCM Stereo in",
-	.dev_addr		= PXA2xx_SSP1_BASE + SSDR,
-	.drcmr			= &DRCMR(13),
-	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
-				  DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_out = {
-	.name			= "SSP2 PCM Mono out",
-	.dev_addr		= PXA27x_SSP2_BASE + SSDR,
-	.drcmr			= &DRCMR(16),
-	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
-				  DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_mono_in = {
-	.name			= "SSP2 PCM Mono in",
-	.dev_addr		= PXA27x_SSP2_BASE + SSDR,
-	.drcmr			= &DRCMR(15),
-	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
-				  DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_out = {
-	.name			= "SSP2 PCM Stereo out",
-	.dev_addr		= PXA27x_SSP2_BASE + SSDR,
-	.drcmr			= &DRCMR(16),
-	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
-				  DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp2_pcm_stereo_in = {
-	.name			= "SSP2 PCM Stereo in",
-	.dev_addr		= PXA27x_SSP2_BASE + SSDR,
-	.drcmr			= &DRCMR(15),
-	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
-				  DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_out = {
-	.name			= "SSP3 PCM Mono out",
-	.dev_addr		= PXA27x_SSP3_BASE + SSDR,
-	.drcmr			= &DRCMR(67),
-	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
-				  DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_mono_in = {
-	.name			= "SSP3 PCM Mono in",
-	.dev_addr		= PXA27x_SSP3_BASE + SSDR,
-	.drcmr			= &DRCMR(66),
-	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
-				  DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_out = {
-	.name			= "SSP3 PCM Stereo out",
-	.dev_addr		= PXA27x_SSP3_BASE + SSDR,
-	.drcmr			= &DRCMR(67),
-	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
-				  DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp3_pcm_stereo_in = {
-	.name			= "SSP3 PCM Stereo in",
-	.dev_addr		= PXA27x_SSP3_BASE + SSDR,
-	.drcmr			= &DRCMR(66),
-	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
-				  DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_out = {
-	.name			= "SSP4 PCM Mono out",
-	.dev_addr		= PXA3xx_SSP4_BASE + SSDR,
-	.drcmr			= &DRCMR(67),
-	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
-				  DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_mono_in = {
-	.name			= "SSP4 PCM Mono in",
-	.dev_addr		= PXA3xx_SSP4_BASE + SSDR,
-	.drcmr			= &DRCMR(66),
-	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
-				  DCMD_BURST16 | DCMD_WIDTH2,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_out = {
-	.name			= "SSP4 PCM Stereo out",
-	.dev_addr		= PXA3xx_SSP4_BASE + SSDR,
-	.drcmr			= &DRCMR(67),
-	.dcmd			= DCMD_INCSRCADDR | DCMD_FLOWTRG |
-				  DCMD_BURST16 | DCMD_WIDTH4,
-};
-
-static struct pxa2xx_pcm_dma_params pxa_ssp4_pcm_stereo_in = {
-	.name			= "SSP4 PCM Stereo in",
-	.dev_addr		= PXA3xx_SSP4_BASE + SSDR,
-	.drcmr			= &DRCMR(66),
-	.dcmd			= DCMD_INCTRGADDR | DCMD_FLOWSRC |
-				  DCMD_BURST16 | DCMD_WIDTH4,
-};
-
 static void dump_registers(struct ssp_device *ssp)
 {
 	dev_dbg(&ssp->pdev->dev, "SSCR0 0x%08x SSCR1 0x%08x SSTO 0x%08x\n",
@@ -194,25 +61,33 @@
 		 ssp_read_reg(ssp, SSACD));
 }
 
-static struct pxa2xx_pcm_dma_params *ssp_dma_params[4][4] = {
-	{
-		&pxa_ssp1_pcm_mono_out, &pxa_ssp1_pcm_mono_in,
-		&pxa_ssp1_pcm_stereo_out, &pxa_ssp1_pcm_stereo_in,
-	},
-	{
-		&pxa_ssp2_pcm_mono_out, &pxa_ssp2_pcm_mono_in,
-		&pxa_ssp2_pcm_stereo_out, &pxa_ssp2_pcm_stereo_in,
-	},
-	{
-		&pxa_ssp3_pcm_mono_out, &pxa_ssp3_pcm_mono_in,
-		&pxa_ssp3_pcm_stereo_out, &pxa_ssp3_pcm_stereo_in,
-	},
-	{
-		&pxa_ssp4_pcm_mono_out, &pxa_ssp4_pcm_mono_in,
-		&pxa_ssp4_pcm_stereo_out, &pxa_ssp4_pcm_stereo_in,
-	},
+struct pxa2xx_pcm_dma_data {
+	struct pxa2xx_pcm_dma_params params;
+	char name[20];
 };
 
+static struct pxa2xx_pcm_dma_params *
+ssp_get_dma_params(struct ssp_device *ssp, int width4, int out)
+{
+	struct pxa2xx_pcm_dma_data *dma;
+
+	dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL);
+	if (dma == NULL)
+		return NULL;
+
+	snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id,
+			width4 ? "32-bit" : "16-bit", out ? "out" : "in");
+
+	dma->params.name = dma->name;
+	dma->params.drcmr = &DRCMR(out ? ssp->drcmr_tx : ssp->drcmr_rx);
+	dma->params.dcmd = (out ? (DCMD_INCSRCADDR | DCMD_FLOWTRG) :
+				  (DCMD_INCTRGADDR | DCMD_FLOWSRC)) |
+			(width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16;
+	dma->params.dev_addr = ssp->phys_base + SSDR;
+
+	return &dma->params;
+}
+
 static int pxa_ssp_startup(struct snd_pcm_substream *substream,
 			   struct snd_soc_dai *dai)
 {
@@ -227,6 +102,11 @@
 		clk_enable(priv->dev.ssp->clk);
 		ssp_disable(&priv->dev);
 	}
+
+	if (cpu_dai->dma_data) {
+		kfree(cpu_dai->dma_data);
+		cpu_dai->dma_data = NULL;
+	}
 	return ret;
 }
 
@@ -241,6 +121,11 @@
 		ssp_disable(&priv->dev);
 		clk_disable(priv->dev.ssp->clk);
 	}
+
+	if (cpu_dai->dma_data) {
+		kfree(cpu_dai->dma_data);
+		cpu_dai->dma_data = NULL;
+	}
 }
 
 #ifdef CONFIG_PM
@@ -323,7 +208,7 @@
 		~(SSCR0_ECS |  SSCR0_NCS | SSCR0_MOD | SSCR0_ACS);
 
 	dev_dbg(&ssp->pdev->dev,
-		"pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %d\n",
+		"pxa_ssp_set_dai_sysclk id: %d, clk_id %d, freq %u\n",
 		cpu_dai->id, clk_id, freq);
 
 	switch (clk_id) {
@@ -472,7 +357,7 @@
 			ssacd |= (0x6 << 4);
 
 			dev_dbg(&ssp->pdev->dev,
-				"Using SSACDD %x to supply %dHz\n",
+				"Using SSACDD %x to supply %uHz\n",
 				val, freq_out);
 			break;
 		}
@@ -490,21 +375,34 @@
  * Set the active slots in TDM/Network mode
  */
 static int pxa_ssp_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
-	unsigned int mask, int slots)
+	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
 {
 	struct ssp_priv *priv = cpu_dai->private_data;
 	struct ssp_device *ssp = priv->dev.ssp;
 	u32 sscr0;
 
-	sscr0 = ssp_read_reg(ssp, SSCR0) & ~SSCR0_SlotsPerFrm(7);
+	sscr0 = ssp_read_reg(ssp, SSCR0);
+	sscr0 &= ~(SSCR0_MOD | SSCR0_SlotsPerFrm(8) | SSCR0_EDSS | SSCR0_DSS);
 
-	/* set number of active slots */
-	sscr0 |= SSCR0_SlotsPerFrm(slots);
+	/* set slot width */
+	if (slot_width > 16)
+		sscr0 |= SSCR0_EDSS | SSCR0_DataSize(slot_width - 16);
+	else
+		sscr0 |= SSCR0_DataSize(slot_width);
+
+	if (slots > 1) {
+		/* enable network mode */
+		sscr0 |= SSCR0_MOD;
+
+		/* set number of active slots */
+		sscr0 |= SSCR0_SlotsPerFrm(slots);
+
+		/* set active slot mask */
+		ssp_write_reg(ssp, SSTSA, tx_mask);
+		ssp_write_reg(ssp, SSRSA, rx_mask);
+	}
 	ssp_write_reg(ssp, SSCR0, sscr0);
 
-	/* set active slot mask */
-	ssp_write_reg(ssp, SSTSA, mask);
-	ssp_write_reg(ssp, SSRSA, mask);
 	return 0;
 }
 
@@ -572,28 +470,27 @@
 		return -EINVAL;
 	}
 
-	ssp_write_reg(ssp, SSCR0, sscr0);
-	ssp_write_reg(ssp, SSCR1, sscr1);
-	ssp_write_reg(ssp, SSPSP, sspsp);
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		sspsp |= SSPSP_SFRMP;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		sspsp |= SSPSP_SCMODE(2);
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		sspsp |= SSPSP_SCMODE(2) | SSPSP_SFRMP;
+		break;
+	default:
+		return -EINVAL;
+	}
 
 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 	case SND_SOC_DAIFMT_I2S:
 		sscr0 |= SSCR0_PSP;
 		sscr1 |= SSCR1_RWOT | SSCR1_TRAIL;
-
 		/* See hw_params() */
-		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-		case SND_SOC_DAIFMT_NB_NF:
-			sspsp |= SSPSP_SFRMP;
-			break;
-		case SND_SOC_DAIFMT_NB_IF:
-			break;
-		case SND_SOC_DAIFMT_IB_IF:
-			sspsp |= SSPSP_SCMODE(3);
-			break;
-		default:
-			return -EINVAL;
-		}
 		break;
 
 	case SND_SOC_DAIFMT_DSP_A:
@@ -601,16 +498,6 @@
 	case SND_SOC_DAIFMT_DSP_B:
 		sscr0 |= SSCR0_MOD | SSCR0_PSP;
 		sscr1 |= SSCR1_TRAIL | SSCR1_RWOT;
-
-		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
-		case SND_SOC_DAIFMT_NB_NF:
-			sspsp |= SSPSP_SFRMP;
-			break;
-		case SND_SOC_DAIFMT_IB_IF:
-			break;
-		default:
-			return -EINVAL;
-		}
 		break;
 
 	default:
@@ -644,25 +531,23 @@
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct ssp_priv *priv = cpu_dai->private_data;
 	struct ssp_device *ssp = priv->dev.ssp;
-	int dma = 0, chn = params_channels(params);
+	int chn = params_channels(params);
 	u32 sscr0;
 	u32 sspsp;
 	int width = snd_pcm_format_physical_width(params_format(params));
 	int ttsa = ssp_read_reg(ssp, SSTSA) & 0xf;
 
-	/* select correct DMA params */
-	if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
-		dma = 1; /* capture DMA offset is 1,3 */
+	/* generate correct DMA params */
+	if (cpu_dai->dma_data)
+		kfree(cpu_dai->dma_data);
+
 	/* Network mode with one active slot (ttsa == 1) can be used
 	 * to force 16-bit frame width on the wire (for S16_LE), even
 	 * with two channels. Use 16-bit DMA transfers for this case.
 	 */
-	if (((chn == 2) && (ttsa != 1)) || (width == 32))
-		dma += 2; /* 32-bit DMA offset is 2, 16-bit is 0 */
-
-	cpu_dai->dma_data = ssp_dma_params[cpu_dai->id][dma];
-
-	dev_dbg(&ssp->pdev->dev, "pxa_ssp_hw_params: dma %d\n", dma);
+	cpu_dai->dma_data = ssp_get_dma_params(ssp,
+			((chn == 2) && (ttsa != 1)) || (width == 32),
+			substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
 
 	/* we can only change the settings if the port is not in use */
 	if (ssp_read_reg(ssp, SSCR0) & SSCR0_SSE)
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index d9c94d7..7330e5c 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -22,6 +22,7 @@
 #include <mach/hardware.h>
 #include <mach/regs-ac97.h>
 #include <mach/dma.h>
+#include <mach/audio.h>
 
 #include "pxa2xx-pcm.h"
 #include "pxa2xx-ac97.h"
@@ -241,9 +242,18 @@
 static int __devinit pxa2xx_ac97_dev_probe(struct platform_device *pdev)
 {
 	int i;
+	pxa2xx_audio_ops_t *pdata = pdev->dev.platform_data;
 
-	for (i = 0; i < ARRAY_SIZE(pxa_ac97_dai); i++)
+	if (pdev->id >= 0) {
+		dev_err(&pdev->dev, "PXA2xx has only one AC97 port.\n");
+		return -ENXIO;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pxa_ac97_dai); i++) {
 		pxa_ac97_dai[i].dev = &pdev->dev;
+		if (pdata && pdata->codec_pdata)
+			pxa_ac97_dai[i].ac97_pdata = pdata->codec_pdata;
+	}
 
 	/* Punt most of the init to the SoC probe; we may need the machine
 	 * driver to do interesting things with the clocking to get us up
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 2f4b6e4..6b8f655 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -106,10 +106,8 @@
 	if (IS_ERR(clk_i2s))
 		return PTR_ERR(clk_i2s);
 
-	if (!cpu_dai->active) {
-		SACR0 |= SACR0_RST;
+	if (!cpu_dai->active)
 		SACR0 = 0;
-	}
 
 	return 0;
 }
@@ -169,6 +167,7 @@
 
 	BUG_ON(IS_ERR(clk_i2s));
 	clk_enable(clk_i2s);
+	dai->private_data = dai;
 	pxa_i2s_wait();
 
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -178,9 +177,7 @@
 
 	/* is port used by another stream */
 	if (!(SACR0 & SACR0_ENB)) {
-
 		SACR0 = 0;
-		SACR1 = 0;
 		if (pxa_i2s.master)
 			SACR0 |= SACR0_BCKD;
 
@@ -226,6 +223,10 @@
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			SACR1 &= ~SACR1_DRPL;
+		else
+			SACR1 &= ~SACR1_DREC;
 		SACR0 |= SACR0_ENB;
 		break;
 	case SNDRV_PCM_TRIGGER_RESUME:
@@ -252,21 +253,19 @@
 		SAIMR &= ~SAIMR_RFS;
 	}
 
-	if (SACR1 & (SACR1_DREC | SACR1_DRPL)) {
+	if ((SACR1 & (SACR1_DREC | SACR1_DRPL)) == (SACR1_DREC | SACR1_DRPL)) {
 		SACR0 &= ~SACR0_ENB;
 		pxa_i2s_wait();
-		clk_disable(clk_i2s);
+		if (dai->private_data != NULL) {
+			clk_disable(clk_i2s);
+			dai->private_data = NULL;
+		}
 	}
-
-	clk_put(clk_i2s);
 }
 
 #ifdef CONFIG_PM
 static int pxa2xx_i2s_suspend(struct snd_soc_dai *dai)
 {
-	if (!dai->active)
-		return 0;
-
 	/* store registers */
 	pxa_i2s.sacr0 = SACR0;
 	pxa_i2s.sacr1 = SACR1;
@@ -281,16 +280,14 @@
 
 static int pxa2xx_i2s_resume(struct snd_soc_dai *dai)
 {
-	if (!dai->active)
-		return 0;
-
 	pxa_i2s_wait();
 
-	SACR0 = pxa_i2s.sacr0 &= ~SACR0_ENB;
+	SACR0 = pxa_i2s.sacr0 & ~SACR0_ENB;
 	SACR1 = pxa_i2s.sacr1;
 	SAIMR = pxa_i2s.saimr;
 	SADIV = pxa_i2s.sadiv;
-	SACR0 |= SACR0_ENB;
+
+	SACR0 = pxa_i2s.sacr0;
 
 	return 0;
 }
@@ -329,6 +326,7 @@
 		.rates = PXA2XX_I2S_RATES,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
 	.ops = &pxa_i2s_dai_ops,
+	.symmetric_rates = 1,
 };
 
 EXPORT_SYMBOL_GPL(pxa_i2s_dai);
@@ -342,10 +340,24 @@
 		return PTR_ERR(clk_i2s);
 
 	pxa_i2s_dai.dev = &dev->dev;
+	pxa_i2s_dai.private_data = NULL;
 	ret = snd_soc_register_dai(&pxa_i2s_dai);
 	if (ret != 0)
 		clk_put(clk_i2s);
 
+	/*
+	 * PXA Developer's Manual:
+	 * If SACR0[ENB] is toggled in the middle of a normal operation,
+	 * the SACR0[RST] bit must also be set and cleared to reset all
+	 * I2S controller registers.
+	 */
+	SACR0 = SACR0_RST;
+	SACR0 = 0;
+	/* Make sure RPL and REC are disabled */
+	SACR1 = SACR1_DRPL | SACR1_DREC;
+	/* Along with FIFO servicing */
+	SAIMR &= ~(SAIMR_RFS | SAIMR_TFS);
+
 	return ret;
 }
 
diff --git a/sound/soc/s3c24xx/Kconfig b/sound/soc/s3c24xx/Kconfig
index df494d1..808de5c 100644
--- a/sound/soc/s3c24xx/Kconfig
+++ b/sound/soc/s3c24xx/Kconfig
@@ -38,6 +38,15 @@
 	  Say Y if you want to add support for SoC audio on smdk2440
 	  with the WM8753.
 
+config SND_S3C24XX_SOC_NEO1973_GTA02_WM8753
+	tristate "Audio support for the Openmoko Neo FreeRunner (GTA02)"
+	depends on SND_S3C24XX_SOC && MACH_NEO1973_GTA02
+	select SND_S3C24XX_SOC_I2S
+	select SND_SOC_WM8753
+	help
+	  This driver provides audio support for the Openmoko Neo FreeRunner
+	  smartphone.
+	  
 config SND_S3C24XX_SOC_JIVE_WM8750
 	tristate "SoC I2S Audio support for Jive"
 	depends on SND_S3C24XX_SOC && MACH_JIVE
@@ -57,7 +66,7 @@
 
 config SND_S3C24XX_SOC_LN2440SBC_ALC650
 	tristate "SoC AC97 Audio support for LN2440SBC - ALC650"
-	depends on SND_S3C24XX_SOC
+	depends on SND_S3C24XX_SOC && ARCH_S3C2410
 	select SND_S3C2443_SOC_AC97
 	select SND_SOC_AC97_CODEC
 	help
@@ -66,7 +75,7 @@
 
 config SND_S3C24XX_SOC_S3C24XX_UDA134X
 	tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
-       	depends on SND_S3C24XX_SOC
+       	depends on SND_S3C24XX_SOC && ARCH_S3C2410
        	select SND_S3C24XX_SOC_I2S
 	select SND_SOC_L3
        	select SND_SOC_UDA134X
diff --git a/sound/soc/s3c24xx/Makefile b/sound/soc/s3c24xx/Makefile
index 07a93a2..eb219b0 100644
--- a/sound/soc/s3c24xx/Makefile
+++ b/sound/soc/s3c24xx/Makefile
@@ -16,12 +16,14 @@
 # S3C24XX Machine Support
 snd-soc-jive-wm8750-objs := jive_wm8750.o
 snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
+snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o
 snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
 snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
 snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
 
 obj-$(CONFIG_SND_S3C24XX_SOC_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
+obj-$(CONFIG_SND_S3C24XX_SOC_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm8753.o
 obj-$(CONFIG_SND_S3C24XX_SOC_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
 obj-$(CONFIG_SND_S3C24XX_SOC_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
 obj-$(CONFIG_SND_S3C24XX_SOC_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o
diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
new file mode 100644
index 0000000..0c52e36
--- /dev/null
+++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
@@ -0,0 +1,498 @@
+/*
+ * neo1973_gta02_wm8753.c  --  SoC audio for Openmoko Freerunner(GTA02)
+ *
+ * Copyright 2007 Openmoko Inc
+ * Author: Graeme Gregory <graeme@openmoko.org>
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory <linux@wolfsonmicro.com>
+ * Copyright 2009 Wolfson Microelectronics
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <asm/mach-types.h>
+
+#include <plat/regs-iis.h>
+
+#include <mach/regs-clock.h>
+#include <asm/io.h>
+#include <mach/gta02.h>
+#include "../codecs/wm8753.h"
+#include "s3c24xx-pcm.h"
+#include "s3c24xx-i2s.h"
+
+static struct snd_soc_card neo1973_gta02;
+
+static int neo1973_gta02_hifi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	unsigned int pll_out = 0, bclk = 0;
+	int ret = 0;
+	unsigned long iis_clkrate;
+
+	iis_clkrate = s3c24xx_i2s_get_clockrate();
+
+	switch (params_rate(params)) {
+	case 8000:
+	case 16000:
+		pll_out = 12288000;
+		break;
+	case 48000:
+		bclk = WM8753_BCLK_DIV_4;
+		pll_out = 12288000;
+		break;
+	case 96000:
+		bclk = WM8753_BCLK_DIV_2;
+		pll_out = 12288000;
+		break;
+	case 11025:
+		bclk = WM8753_BCLK_DIV_16;
+		pll_out = 11289600;
+		break;
+	case 22050:
+		bclk = WM8753_BCLK_DIV_8;
+		pll_out = 11289600;
+		break;
+	case 44100:
+		bclk = WM8753_BCLK_DIV_4;
+		pll_out = 11289600;
+		break;
+	case 88200:
+		bclk = WM8753_BCLK_DIV_2;
+		pll_out = 11289600;
+		break;
+	}
+
+	/* set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_MCLK, pll_out,
+		SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* set MCLK division for sample rate */
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_MCLK,
+		S3C2410_IISMOD_32FS);
+	if (ret < 0)
+		return ret;
+
+	/* set codec BCLK division for sample rate */
+	ret = snd_soc_dai_set_clkdiv(codec_dai,
+					WM8753_BCLKDIV, bclk);
+	if (ret < 0)
+		return ret;
+
+	/* set prescaler division for sample rate */
+	ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
+		S3C24XX_PRESCALE(4, 4));
+	if (ret < 0)
+		return ret;
+
+	/* codec PLL input is PCLK/4 */
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+		iis_clkrate / 4, pll_out);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int neo1973_gta02_hifi_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+	/* disable the PLL */
+	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0, 0);
+}
+
+/*
+ * Neo1973 WM8753 HiFi DAI opserations.
+ */
+static struct snd_soc_ops neo1973_gta02_hifi_ops = {
+	.hw_params = neo1973_gta02_hifi_hw_params,
+	.hw_free = neo1973_gta02_hifi_hw_free,
+};
+
+static int neo1973_gta02_voice_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;
+	unsigned int pcmdiv = 0;
+	int ret = 0;
+	unsigned long iis_clkrate;
+
+	iis_clkrate = s3c24xx_i2s_get_clockrate();
+
+	if (params_rate(params) != 8000)
+		return -EINVAL;
+	if (params_channels(params) != 1)
+		return -EINVAL;
+
+	pcmdiv = WM8753_PCM_DIV_6; /* 2.048 MHz */
+
+	/* todo: gg check mode (DSP_B) against CSR datasheet */
+	/* set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_DSP_B |
+		SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		return ret;
+
+	/* set the codec system clock for DAC and ADC */
+	ret = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK,
+		12288000, SND_SOC_CLOCK_IN);
+	if (ret < 0)
+		return ret;
+
+	/* set codec PCM division for sample rate */
+	ret = snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV,
+					pcmdiv);
+	if (ret < 0)
+		return ret;
+
+	/* configue and enable PLL for 12.288MHz output */
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2,
+		iis_clkrate / 4, 12288000);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int neo1973_gta02_voice_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+	/* disable the PLL */
+	return snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0, 0);
+}
+
+static struct snd_soc_ops neo1973_gta02_voice_ops = {
+	.hw_params = neo1973_gta02_voice_hw_params,
+	.hw_free = neo1973_gta02_voice_hw_free,
+};
+
+#define LM4853_AMP 1
+#define LM4853_SPK 2
+
+static u8 lm4853_state;
+
+/* This has no effect, it exists only to maintain compatibility with
+ * existing ALSA state files.
+ */
+static int lm4853_set_state(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int val = ucontrol->value.integer.value[0];
+
+	if (val)
+		lm4853_state |= LM4853_AMP;
+	else
+		lm4853_state &= ~LM4853_AMP;
+
+	return 0;
+}
+
+static int lm4853_get_state(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = lm4853_state & LM4853_AMP;
+
+	return 0;
+}
+
+static int lm4853_set_spk(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int val = ucontrol->value.integer.value[0];
+
+	if (val) {
+		lm4853_state |= LM4853_SPK;
+		gpio_set_value(GTA02_GPIO_HP_IN, 0);
+	} else {
+		lm4853_state &= ~LM4853_SPK;
+		gpio_set_value(GTA02_GPIO_HP_IN, 1);
+	}
+
+	return 0;
+}
+
+static int lm4853_get_spk(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = (lm4853_state & LM4853_SPK) >> 1;
+
+	return 0;
+}
+
+static int lm4853_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *k,
+			int event)
+{
+	gpio_set_value(GTA02_GPIO_AMP_SHUT, SND_SOC_DAPM_EVENT_OFF(value));
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget wm8753_dapm_widgets[] = {
+	SND_SOC_DAPM_SPK("Stereo Out", lm4853_event),
+	SND_SOC_DAPM_LINE("GSM Line Out", NULL),
+	SND_SOC_DAPM_LINE("GSM Line In", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Handset Mic", NULL),
+	SND_SOC_DAPM_SPK("Handset Spk", NULL),
+};
+
+
+/* example machine audio_mapnections */
+static const struct snd_soc_dapm_route audio_map[] = {
+
+	/* Connections to the lm4853 amp */
+	{"Stereo Out", NULL, "LOUT1"},
+	{"Stereo Out", NULL, "ROUT1"},
+
+	/* Connections to the GSM Module */
+	{"GSM Line Out", NULL, "MONO1"},
+	{"GSM Line Out", NULL, "MONO2"},
+	{"RXP", NULL, "GSM Line In"},
+	{"RXN", NULL, "GSM Line In"},
+
+	/* Connections to Headset */
+	{"MIC1", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Headset Mic"},
+
+	/* Call Mic */
+	{"MIC2", NULL, "Mic Bias"},
+	{"MIC2N", NULL, "Mic Bias"},
+	{"Mic Bias", NULL, "Handset Mic"},
+
+	/* Call Speaker */
+	{"Handset Spk", NULL, "LOUT2"},
+	{"Handset Spk", NULL, "ROUT2"},
+
+	/* Connect the ALC pins */
+	{"ACIN", NULL, "ACOP"},
+};
+
+static const struct snd_kcontrol_new wm8753_neo1973_gta02_controls[] = {
+	SOC_DAPM_PIN_SWITCH("Stereo Out"),
+	SOC_DAPM_PIN_SWITCH("GSM Line Out"),
+	SOC_DAPM_PIN_SWITCH("GSM Line In"),
+	SOC_DAPM_PIN_SWITCH("Headset Mic"),
+	SOC_DAPM_PIN_SWITCH("Handset Mic"),
+	SOC_DAPM_PIN_SWITCH("Handset Spk"),
+
+	/* This has no effect, it exists only to maintain compatibility with
+	 * existing ALSA state files.
+	 */
+	SOC_SINGLE_EXT("Amp State Switch", 6, 0, 1, 0,
+		lm4853_get_state,
+		lm4853_set_state),
+	SOC_SINGLE_EXT("Amp Spk Switch", 7, 0, 1, 0,
+		lm4853_get_spk,
+		lm4853_set_spk),
+};
+
+/*
+ * This is an example machine initialisation for a wm8753 connected to a
+ * neo1973 GTA02.
+ */
+static int neo1973_gta02_wm8753_init(struct snd_soc_codec *codec)
+{
+	int err;
+
+	/* set up NC codec pins */
+	snd_soc_dapm_nc_pin(codec, "OUT3");
+	snd_soc_dapm_nc_pin(codec, "OUT4");
+	snd_soc_dapm_nc_pin(codec, "LINE1");
+	snd_soc_dapm_nc_pin(codec, "LINE2");
+
+	/* Add neo1973 gta02 specific widgets */
+	snd_soc_dapm_new_controls(codec, wm8753_dapm_widgets,
+				  ARRAY_SIZE(wm8753_dapm_widgets));
+
+	/* add neo1973 gta02 specific controls */
+	err = snd_soc_add_controls(codec, wm8753_neo1973_gta02_controls,
+		ARRAY_SIZE(wm8753_neo1973_gta02_controls));
+
+	if (err < 0)
+		return err;
+
+	/* set up neo1973 gta02 specific audio path audio_map */
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	/* set endpoints to default off mode */
+	snd_soc_dapm_disable_pin(codec, "Stereo Out");
+	snd_soc_dapm_disable_pin(codec, "GSM Line Out");
+	snd_soc_dapm_disable_pin(codec, "GSM Line In");
+	snd_soc_dapm_disable_pin(codec, "Headset Mic");
+	snd_soc_dapm_disable_pin(codec, "Handset Mic");
+	snd_soc_dapm_disable_pin(codec, "Handset Spk");
+
+	snd_soc_dapm_sync(codec);
+
+	return 0;
+}
+
+/*
+ * BT Codec DAI
+ */
+static struct snd_soc_dai bt_dai = {
+	.name = "Bluetooth",
+	.id = 0,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SNDRV_PCM_RATE_8000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,},
+};
+
+static struct snd_soc_dai_link neo1973_gta02_dai[] = {
+{ /* Hifi Playback - for similatious use with voice below */
+	.name = "WM8753",
+	.stream_name = "WM8753 HiFi",
+	.cpu_dai = &s3c24xx_i2s_dai,
+	.codec_dai = &wm8753_dai[WM8753_DAI_HIFI],
+	.init = neo1973_gta02_wm8753_init,
+	.ops = &neo1973_gta02_hifi_ops,
+},
+{ /* Voice via BT */
+	.name = "Bluetooth",
+	.stream_name = "Voice",
+	.cpu_dai = &bt_dai,
+	.codec_dai = &wm8753_dai[WM8753_DAI_VOICE],
+	.ops = &neo1973_gta02_voice_ops,
+},
+};
+
+static struct snd_soc_card neo1973_gta02 = {
+	.name = "neo1973-gta02",
+	.platform = &s3c24xx_soc_platform,
+	.dai_link = neo1973_gta02_dai,
+	.num_links = ARRAY_SIZE(neo1973_gta02_dai),
+};
+
+static struct snd_soc_device neo1973_gta02_snd_devdata = {
+	.card = &neo1973_gta02,
+	.codec_dev = &soc_codec_dev_wm8753,
+};
+
+static struct platform_device *neo1973_gta02_snd_device;
+
+static int __init neo1973_gta02_init(void)
+{
+	int ret;
+
+	if (!machine_is_neo1973_gta02()) {
+		printk(KERN_INFO
+		       "Only GTA02 is supported by this ASoC driver\n");
+		return -ENODEV;
+	}
+
+	/* register bluetooth DAI here */
+	ret = snd_soc_register_dai(&bt_dai);
+	if (ret)
+		return ret;
+
+	neo1973_gta02_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!neo1973_gta02_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(neo1973_gta02_snd_device,
+			&neo1973_gta02_snd_devdata);
+	neo1973_gta02_snd_devdata.dev = &neo1973_gta02_snd_device->dev;
+	ret = platform_device_add(neo1973_gta02_snd_device);
+
+	if (ret) {
+		platform_device_put(neo1973_gta02_snd_device);
+		return ret;
+	}
+
+	/* Initialise GPIOs used by amp */
+	ret = gpio_request(GTA02_GPIO_HP_IN, "GTA02_HP_IN");
+	if (ret) {
+		pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_HP_IN);
+		goto err_unregister_device;
+	}
+
+	ret = gpio_direction_output(GTA02_GPIO_AMP_HP_IN, 1);
+	if (ret) {
+		pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_HP_IN);
+		goto err_free_gpio_hp_in;
+	}
+
+	ret = gpio_request(GTA02_GPIO_AMP_SHUT, "GTA02_AMP_SHUT");
+	if (ret) {
+		pr_err("gta02_wm8753: Failed to register GPIO %d\n", GTA02_GPIO_AMP_SHUT);
+		goto err_free_gpio_hp_in;
+	}
+
+	ret = gpio_direction_output(GTA02_GPIO_AMP_SHUT, 1);
+	if (ret) {
+		pr_err("gta02_wm8753: Failed to configure GPIO %d\n", GTA02_GPIO_AMP_SHUT);
+		goto err_free_gpio_amp_shut;
+	}
+
+	return 0;
+
+err_free_gpio_amp_shut:
+	gpio_free(GTA02_GPIO_AMP_SHUT);
+err_free_gpio_hp_in:
+	gpio_free(GTA02_GPIO_HP_IN);
+err_unregister_device:
+	platform_device_unregister(neo1973_gta02_snd_device);
+	return ret;
+}
+module_init(neo1973_gta02_init);
+
+static void __exit neo1973_gta02_exit(void)
+{
+	snd_soc_unregister_dai(&bt_dai);
+	platform_device_unregister(neo1973_gta02_snd_device);
+	gpio_free(GTA02_GPIO_HP_IN);
+	gpio_free(GTA02_GPIO_AMP_SHUT);
+}
+module_exit(neo1973_gta02_exit);
+
+/* Module information */
+MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org");
+MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 GTA02");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 289fadf..906709e 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -345,9 +345,11 @@
 static int lm4857_get_reg(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
-	int reg = kcontrol->private_value & 0xFF;
-	int shift = (kcontrol->private_value >> 8) & 0x0F;
-	int mask = (kcontrol->private_value >> 16) & 0xFF;
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int reg = mc->reg;
+	int shift = mc->shift;
+	int mask = mc->max;
 
 	pr_debug("Entered %s\n", __func__);
 
@@ -358,9 +360,11 @@
 static int lm4857_set_reg(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol)
 {
-	int reg = kcontrol->private_value & 0xFF;
-	int shift = (kcontrol->private_value >> 8) & 0x0F;
-	int mask = (kcontrol->private_value >> 16) & 0xFF;
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int reg = mc->reg;
+	int shift = mc->shift;
+	int mask = mc->max;
 
 	if (((lm4857_regs[reg] >> shift) & mask) ==
 		ucontrol->value.integer.value[0])
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c
index ab680aa..1a28317 100644
--- a/sound/soc/s3c24xx/s3c-i2s-v2.c
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.c
@@ -37,6 +37,20 @@
 
 #include "s3c-i2s-v2.h"
 
+#undef S3C_IIS_V2_SUPPORTED
+
+#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
+#define S3C_IIS_V2_SUPPORTED
+#endif
+
+#ifdef CONFIG_PLAT_S3C64XX
+#define S3C_IIS_V2_SUPPORTED
+#endif
+
+#ifndef S3C_IIS_V2_SUPPORTED
+#error Unsupported CPU model
+#endif
+
 #define S3C2412_I2S_DEBUG_CON 0
 
 static inline struct s3c_i2sv2_info *to_info(struct snd_soc_dai *cpu_dai)
@@ -75,7 +89,7 @@
 
 
 /* Turn on or off the transmission path. */
-void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
+static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
 {
 	void __iomem *regs = i2s->regs;
 	u32 fic, con, mod;
@@ -105,7 +119,9 @@
 			break;
 
 		default:
-			dev_err(i2s->dev, "TXEN: Invalid MODE in IISMOD\n");
+			dev_err(i2s->dev, "TXEN: Invalid MODE %x in IISMOD\n",
+				mod & S3C2412_IISMOD_MODE_MASK);
+			break;
 		}
 
 		writel(con, regs + S3C2412_IISCON);
@@ -132,7 +148,9 @@
 			break;
 
 		default:
-			dev_err(i2s->dev, "TXDIS: Invalid MODE in IISMOD\n");
+			dev_err(i2s->dev, "TXDIS: Invalid MODE %x in IISMOD\n",
+				mod & S3C2412_IISMOD_MODE_MASK);
+			break;
 		}
 
 		writel(mod, regs + S3C2412_IISMOD);
@@ -143,9 +161,8 @@
 	dbg_showcon(__func__, con);
 	pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
 }
-EXPORT_SYMBOL_GPL(s3c2412_snd_txctrl);
 
-void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
+static void s3c2412_snd_rxctrl(struct s3c_i2sv2_info *i2s, int on)
 {
 	void __iomem *regs = i2s->regs;
 	u32 fic, con, mod;
@@ -175,7 +192,8 @@
 			break;
 
 		default:
-			dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
+			dev_err(i2s->dev, "RXEN: Invalid MODE %x in IISMOD\n",
+				mod & S3C2412_IISMOD_MODE_MASK);
 		}
 
 		writel(mod, regs + S3C2412_IISMOD);
@@ -199,7 +217,8 @@
 			break;
 
 		default:
-			dev_err(i2s->dev, "RXEN: Invalid MODE in IISMOD\n");
+			dev_err(i2s->dev, "RXDIS: Invalid MODE %x in IISMOD\n",
+				mod & S3C2412_IISMOD_MODE_MASK);
 		}
 
 		writel(con, regs + S3C2412_IISCON);
@@ -209,7 +228,6 @@
 	fic = readl(regs + S3C2412_IISFIC);
 	pr_debug("%s: IIS: CON=%x MOD=%x FIC=%x\n", __func__, con, mod, fic);
 }
-EXPORT_SYMBOL_GPL(s3c2412_snd_rxctrl);
 
 /*
  * Wait for the LR signal to allow synchronisation to the L/R clock
@@ -266,7 +284,7 @@
  */
 #define IISMOD_MASTER_MASK (1 << 11)
 #define IISMOD_SLAVE (1 << 11)
-#define IISMOD_MASTER (0x0)
+#define IISMOD_MASTER (0 << 11)
 #endif
 
 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -281,7 +299,7 @@
 		iismod |= IISMOD_MASTER;
 		break;
 	default:
-		pr_debug("unknwon master/slave format\n");
+		pr_err("unknwon master/slave format\n");
 		return -EINVAL;
 	}
 
@@ -298,7 +316,7 @@
 		iismod |= S3C2412_IISMOD_SDF_IIS;
 		break;
 	default:
-		pr_debug("Unknown data format\n");
+		pr_err("Unknown data format\n");
 		return -EINVAL;
 	}
 
@@ -327,6 +345,7 @@
 	iismod = readl(i2s->regs + S3C2412_IISMOD);
 	pr_debug("%s: r: IISMOD: %x\n", __func__, iismod);
 
+#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S8:
 		iismod |= S3C2412_IISMOD_8BIT;
@@ -335,6 +354,25 @@
 		iismod &= ~S3C2412_IISMOD_8BIT;
 		break;
 	}
+#endif
+
+#ifdef CONFIG_PLAT_S3C64XX
+	iismod &= ~0x606;
+	/* Sample size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		/* 8 bit sample, 16fs BCLK */
+		iismod |= 0x2004;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		/* 16 bit sample, 32fs BCLK */
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		/* 24 bit sample, 48fs BCLK */
+		iismod |= 0x4002;
+		break;
+	}
+#endif
 
 	writel(iismod, i2s->regs + S3C2412_IISMOD);
 	pr_debug("%s: w: IISMOD: %x\n", __func__, iismod);
@@ -489,6 +527,8 @@
 	unsigned int best_rate = 0;
 	unsigned int best_deviation = INT_MAX;
 
+	pr_debug("Input clock rate %ldHz\n", clkrate);
+
 	if (fstab == NULL)
 		fstab = iis_fs_tab;
 
@@ -507,7 +547,7 @@
 		actual = clkrate / (fsdiv * div);
 		deviation = actual - rate;
 
-		printk(KERN_DEBUG "%dfs: div %d => result %d, deviation %d\n",
+		printk(KERN_DEBUG "%ufs: div %u => result %u, deviation %d\n",
 		       fsdiv, div, actual, deviation);
 
 		deviation = abs(deviation);
@@ -523,7 +563,7 @@
 			break;
 	}
 
-	printk(KERN_DEBUG "best: fs=%d, div=%d, rate=%d\n",
+	printk(KERN_DEBUG "best: fs=%u, div=%u, rate=%u\n",
 	       best_fs, best_div, best_rate);
 
 	info->fs_div = best_fs;
@@ -539,12 +579,31 @@
 		    unsigned long base)
 {
 	struct device *dev = &pdev->dev;
+	unsigned int iismod;
 
 	i2s->dev = dev;
 
 	/* record our i2s structure for later use in the callbacks */
 	dai->private_data = i2s;
 
+	if (!base) {
+		struct resource *res = platform_get_resource(pdev,
+							     IORESOURCE_MEM,
+							     0);
+		if (!res) {
+			dev_err(dev, "Unable to get register resource\n");
+			return -ENXIO;
+		}
+
+		if (!request_mem_region(res->start, resource_size(res),
+					"s3c64xx-i2s-v4")) {
+			dev_err(dev, "Unable to request register region\n");
+			return -EBUSY;
+		}
+
+		base = res->start;
+	}
+
 	i2s->regs = ioremap(base, 0x100);
 	if (i2s->regs == NULL) {
 		dev_err(dev, "cannot ioremap registers\n");
@@ -560,12 +619,16 @@
 
 	clk_enable(i2s->iis_pclk);
 
+	/* Mark ourselves as in TXRX mode so we can run through our cleanup
+	 * process without warnings. */
+	iismod = readl(i2s->regs + S3C2412_IISMOD);
+	iismod |= S3C2412_IISMOD_MODE_TXRX;
+	writel(iismod, i2s->regs + S3C2412_IISMOD);
 	s3c2412_snd_txctrl(i2s, 0);
 	s3c2412_snd_rxctrl(i2s, 0);
 
 	return 0;
 }
-
 EXPORT_SYMBOL_GPL(s3c_i2sv2_probe);
 
 #ifdef CONFIG_PM
diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c
index b7e0b3f..a587ec4 100644
--- a/sound/soc/s3c24xx/s3c2412-i2s.c
+++ b/sound/soc/s3c24xx/s3c2412-i2s.c
@@ -20,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
 #include <linux/clk.h>
 #include <linux/kernel.h>
 #include <linux/io.h>
@@ -120,7 +121,7 @@
 
 	s3c2412_i2s.iis_cclk = clk_get(&pdev->dev, "i2sclk");
 	if (s3c2412_i2s.iis_cclk == NULL) {
-		pr_debug("failed to get i2sclk clock\n");
+		pr_err("failed to get i2sclk clock\n");
 		iounmap(s3c2412_i2s.regs);
 		return -ENODEV;
 	}
diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c
index 3698f70..bf16f20 100644
--- a/sound/soc/s3c24xx/s3c2443-ac97.c
+++ b/sound/soc/s3c24xx/s3c2443-ac97.c
@@ -19,6 +19,7 @@
 #include <linux/io.h>
 #include <linux/wait.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
 #include <linux/clk.h>
 
 #include <sound/core.h>
@@ -46,7 +47,7 @@
 
 static DECLARE_COMPLETION(ac97_completion);
 static u32 codec_ready;
-static DECLARE_MUTEX(ac97_mutex);
+static DEFINE_MUTEX(ac97_mutex);
 
 static unsigned short s3c2443_ac97_read(struct snd_ac97 *ac97,
 	unsigned short reg)
@@ -55,7 +56,7 @@
 	u32 ac_codec_cmd;
 	u32 stat, addr, data;
 
-	down(&ac97_mutex);
+	mutex_lock(&ac97_mutex);
 
 	codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
 	ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
@@ -78,7 +79,7 @@
 		printk(KERN_ERR "s3c24xx-ac97: req addr = %02x,"
 				" rep addr = %02x\n", reg, addr);
 
-	up(&ac97_mutex);
+	mutex_unlock(&ac97_mutex);
 
 	return (unsigned short)data;
 }
@@ -89,7 +90,7 @@
 	u32 ac_glbctrl;
 	u32 ac_codec_cmd;
 
-	down(&ac97_mutex);
+	mutex_lock(&ac97_mutex);
 
 	codec_ready = S3C_AC97_GLBSTAT_CODECREADY;
 	ac_codec_cmd = readl(s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
@@ -108,7 +109,7 @@
 	ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
 	writel(ac_codec_cmd, s3c24xx_ac97.regs + S3C_AC97_CODEC_CMD);
 
-	up(&ac97_mutex);
+	mutex_unlock(&ac97_mutex);
 
 }
 
diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c
index cc06696..556e35f 100644
--- a/sound/soc/s3c24xx/s3c24xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c24xx-i2s.c
@@ -21,6 +21,8 @@
 #include <linux/clk.h>
 #include <linux/jiffies.h>
 #include <linux/io.h>
+#include <linux/gpio.h>
+
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
diff --git a/sound/soc/s3c24xx/s3c24xx-pcm.c b/sound/soc/s3c24xx/s3c24xx-pcm.c
index 169ddad..eecfa5e 100644
--- a/sound/soc/s3c24xx/s3c24xx-pcm.c
+++ b/sound/soc/s3c24xx/s3c24xx-pcm.c
@@ -218,24 +218,17 @@
 	 * sync to pclk, half-word transfers to the IIS-FIFO. */
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		s3c2410_dma_devconfig(prtd->params->channel,
-				S3C2410_DMASRC_MEM, S3C2410_DISRCC_INC |
-				S3C2410_DISRCC_APB, prtd->params->dma_addr);
-
-		s3c2410_dma_config(prtd->params->channel,
-				prtd->params->dma_size,
-				S3C2410_DCON_SYNC_PCLK |
-				S3C2410_DCON_HANDSHAKE);
+				      S3C2410_DMASRC_MEM,
+				      prtd->params->dma_addr);
 	} else {
-		s3c2410_dma_config(prtd->params->channel,
-				prtd->params->dma_size,
-				S3C2410_DCON_HANDSHAKE |
-				S3C2410_DCON_SYNC_PCLK);
-
 		s3c2410_dma_devconfig(prtd->params->channel,
-					S3C2410_DMASRC_HW, 0x3,
-					prtd->params->dma_addr);
+				      S3C2410_DMASRC_HW,
+				      prtd->params->dma_addr);
 	}
 
+	s3c2410_dma_config(prtd->params->channel,
+			   prtd->params->dma_size);
+
 	/* flush the DMA channel */
 	s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH);
 	prtd->dma_loaded = 0;
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c
index 33c5de7..3c06c40 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.c
@@ -108,48 +108,19 @@
 	return 0;
 }
 
-
-unsigned long s3c64xx_i2s_get_clockrate(struct snd_soc_dai *dai)
+struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai)
 {
 	struct s3c_i2sv2_info *i2s = to_info(dai);
 
-	return clk_get_rate(i2s->iis_cclk);
+	return i2s->iis_cclk;
 }
-EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clockrate);
+EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock);
 
 static int s3c64xx_i2s_probe(struct platform_device *pdev,
 			     struct snd_soc_dai *dai)
 {
-	struct device *dev = &pdev->dev;
-	struct s3c_i2sv2_info *i2s;
-	int ret;
-
-	dev_dbg(dev, "%s: probing dai %d\n", __func__, pdev->id);
-
-	if (pdev->id < 0 || pdev->id > ARRAY_SIZE(s3c64xx_i2s)) {
-		dev_err(dev, "id %d out of range\n", pdev->id);
-		return -EINVAL;
-	}
-
-	i2s = &s3c64xx_i2s[pdev->id];
-
-	ret = s3c_i2sv2_probe(pdev, dai, i2s,
-			      pdev->id ? S3C64XX_PA_IIS1 : S3C64XX_PA_IIS0);
-	if (ret)
-		return ret;
-
-	i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id];
-	i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id];
-
-	i2s->iis_cclk = clk_get(dev, "audio-bus");
-	if (IS_ERR(i2s->iis_cclk)) {
-		dev_err(dev, "failed to get audio-bus");
-		iounmap(i2s->regs);
-		return -ENODEV;
-	}
-
 	/* configure GPIO for i2s port */
-	switch (pdev->id) {
+	switch (dai->id) {
 	case 0:
 		s3c_gpio_cfgpin(S3C64XX_GPD(0), S3C64XX_GPD0_I2S0_CLK);
 		s3c_gpio_cfgpin(S3C64XX_GPD(1), S3C64XX_GPD1_I2S0_CDCLK);
@@ -175,41 +146,122 @@
 	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
 
 #define S3C64XX_I2S_FMTS \
-	(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE)
+	(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
+	 SNDRV_PCM_FMTBIT_S24_LE)
 
 static struct snd_soc_dai_ops s3c64xx_i2s_dai_ops = {
 	.set_sysclk	= s3c64xx_i2s_set_sysclk,	
 };
 
-struct snd_soc_dai s3c64xx_i2s_dai = {
-	.name		= "s3c64xx-i2s",
-	.id		= 0,
-	.probe		= s3c64xx_i2s_probe,
-	.playback = {
-		.channels_min	= 2,
-		.channels_max	= 2,
-		.rates		= S3C64XX_I2S_RATES,
-		.formats	= S3C64XX_I2S_FMTS,
+struct snd_soc_dai s3c64xx_i2s_dai[] = {
+	{
+		.name		= "s3c64xx-i2s",
+		.id		= 0,
+		.probe		= s3c64xx_i2s_probe,
+		.playback = {
+			.channels_min	= 2,
+			.channels_max	= 2,
+			.rates		= S3C64XX_I2S_RATES,
+			.formats	= S3C64XX_I2S_FMTS,
+		},
+		.capture = {
+			 .channels_min	= 2,
+			 .channels_max	= 2,
+			 .rates		= S3C64XX_I2S_RATES,
+			 .formats	= S3C64XX_I2S_FMTS,
+		 },
+		.ops = &s3c64xx_i2s_dai_ops,
+		.symmetric_rates = 1,
 	},
-	.capture = {
-		.channels_min	= 2,
-		.channels_max	= 2,
-		.rates		= S3C64XX_I2S_RATES,
-		.formats	= S3C64XX_I2S_FMTS,
+	{
+		.name		= "s3c64xx-i2s",
+		.id		= 1,
+		.probe		= s3c64xx_i2s_probe,
+		.playback = {
+			.channels_min	= 2,
+			.channels_max	= 2,
+			.rates		= S3C64XX_I2S_RATES,
+			.formats	= S3C64XX_I2S_FMTS,
+		},
+		.capture = {
+			 .channels_min	= 2,
+			 .channels_max	= 2,
+			 .rates		= S3C64XX_I2S_RATES,
+			 .formats	= S3C64XX_I2S_FMTS,
+		 },
+		.ops = &s3c64xx_i2s_dai_ops,
+		.symmetric_rates = 1,
 	},
-	.ops = &s3c64xx_i2s_dai_ops,
 };
 EXPORT_SYMBOL_GPL(s3c64xx_i2s_dai);
 
+static __devinit int s3c64xx_iis_dev_probe(struct platform_device *pdev)
+{
+	struct s3c_i2sv2_info *i2s;
+	struct snd_soc_dai *dai;
+	int ret;
+
+	if (pdev->id >= ARRAY_SIZE(s3c64xx_i2s)) {
+		dev_err(&pdev->dev, "id %d out of range\n", pdev->id);
+		return -EINVAL;
+	}
+
+	i2s = &s3c64xx_i2s[pdev->id];
+	dai = &s3c64xx_i2s_dai[pdev->id];
+	dai->dev = &pdev->dev;
+
+	i2s->dma_capture = &s3c64xx_i2s_pcm_stereo_in[pdev->id];
+	i2s->dma_playback = &s3c64xx_i2s_pcm_stereo_out[pdev->id];
+
+	i2s->iis_cclk = clk_get(&pdev->dev, "audio-bus");
+	if (IS_ERR(i2s->iis_cclk)) {
+		dev_err(&pdev->dev, "failed to get audio-bus\n");
+		ret = PTR_ERR(i2s->iis_cclk);
+		goto err;
+	}
+
+	ret = s3c_i2sv2_probe(pdev, dai, i2s, 0);
+	if (ret)
+		goto err_clk;
+
+	ret = s3c_i2sv2_register_dai(dai);
+	if (ret != 0)
+		goto err_i2sv2;
+
+	return 0;
+
+err_i2sv2:
+	/* Not implemented for I2Sv2 core yet */
+err_clk:
+	clk_put(i2s->iis_cclk);
+err:
+	return ret;
+}
+
+static __devexit int s3c64xx_iis_dev_remove(struct platform_device *pdev)
+{
+	dev_err(&pdev->dev, "Device removal not yet supported\n");
+	return 0;
+}
+
+static struct platform_driver s3c64xx_iis_driver = {
+	.probe  = s3c64xx_iis_dev_probe,
+	.remove = s3c64xx_iis_dev_remove,
+	.driver = {
+		.name = "s3c64xx-iis",
+		.owner = THIS_MODULE,
+	},
+};
+
 static int __init s3c64xx_i2s_init(void)
 {
-	return  s3c_i2sv2_register_dai(&s3c64xx_i2s_dai);
+	return platform_driver_register(&s3c64xx_iis_driver);
 }
 module_init(s3c64xx_i2s_init);
 
 static void __exit s3c64xx_i2s_exit(void)
 {
-	snd_soc_unregister_dai(&s3c64xx_i2s_dai);
+	platform_driver_unregister(&s3c64xx_iis_driver);
 }
 module_exit(s3c64xx_i2s_exit);
 
@@ -217,6 +269,3 @@
 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
 MODULE_DESCRIPTION("S3C64XX I2S SoC Interface");
 MODULE_LICENSE("GPL");
-
-
-
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h
index b7ffe3c..02148ce 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.h
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.h
@@ -15,6 +15,8 @@
 #ifndef __SND_SOC_S3C24XX_S3C64XX_I2S_H
 #define __SND_SOC_S3C24XX_S3C64XX_I2S_H __FILE__
 
+struct clk;
+
 #include "s3c-i2s-v2.h"
 
 #define S3C64XX_DIV_BCLK	S3C_I2SV2_DIV_BCLK
@@ -24,8 +26,8 @@
 #define S3C64XX_CLKSRC_PCLK	(0)
 #define S3C64XX_CLKSRC_MUX	(1)
 
-extern struct snd_soc_dai s3c64xx_i2s_dai;
+extern struct snd_soc_dai s3c64xx_i2s_dai[];
 
-extern unsigned long s3c64xx_i2s_get_clockrate(struct snd_soc_dai *cpu_dai);
+extern struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai);
 
 #endif /* __SND_SOC_S3C24XX_S3C64XX_I2S_H */
diff --git a/sound/soc/s6000/Kconfig b/sound/soc/s6000/Kconfig
new file mode 100644
index 0000000..c74eb3d
--- /dev/null
+++ b/sound/soc/s6000/Kconfig
@@ -0,0 +1,19 @@
+config SND_S6000_SOC
+	tristate "SoC Audio for the Stretch s6000 family"
+	depends on XTENSA_VARIANT_S6000
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  s6000 family chips. You will also need to select the platform
+	  to support below.
+
+config SND_S6000_SOC_I2S
+	tristate
+
+config SND_S6000_SOC_S6IPCAM
+	tristate "SoC Audio support for Stretch 6105 IP Camera"
+	depends on SND_S6000_SOC && XTENSA_PLATFORM_S6105
+	select SND_S6000_SOC_I2S
+	select SND_SOC_TLV320AIC3X
+	help
+	  Say Y if you want to add support for SoC audio on the
+	  Stretch s6105 IP Camera Reference Design.
diff --git a/sound/soc/s6000/Makefile b/sound/soc/s6000/Makefile
new file mode 100644
index 0000000..7a61361
--- /dev/null
+++ b/sound/soc/s6000/Makefile
@@ -0,0 +1,11 @@
+# s6000 Platform Support
+snd-soc-s6000-objs := s6000-pcm.o
+snd-soc-s6000-i2s-objs := s6000-i2s.o
+
+obj-$(CONFIG_SND_S6000_SOC) += snd-soc-s6000.o
+obj-$(CONFIG_SND_S6000_SOC_I2S) += snd-soc-s6000-i2s.o
+
+# s6105 Machine Support
+snd-soc-s6ipcam-objs := s6105-ipcam.o
+
+obj-$(CONFIG_SND_S6000_SOC_S6IPCAM) += snd-soc-s6ipcam.o
diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c
new file mode 100644
index 0000000..c5cda18
--- /dev/null
+++ b/sound/soc/s6000/s6000-i2s.c
@@ -0,0 +1,629 @@
+/*
+ * ALSA SoC I2S Audio Layer for the Stretch S6000 family
+ *
+ * Author:      Daniel Gloeckner, <dg@emlix.com>
+ * Copyright:   (C) 2009 emlix GmbH <info@emlix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+
+#include "s6000-i2s.h"
+#include "s6000-pcm.h"
+
+struct s6000_i2s_dev {
+	dma_addr_t sifbase;
+	u8 __iomem *scbbase;
+	unsigned int wide;
+	unsigned int channel_in;
+	unsigned int channel_out;
+	unsigned int lines_in;
+	unsigned int lines_out;
+	struct s6000_pcm_dma_params dma_params;
+};
+
+#define S6_I2S_INTERRUPT_STATUS	0x00
+#define   S6_I2S_INT_OVERRUN	1
+#define   S6_I2S_INT_UNDERRUN	2
+#define   S6_I2S_INT_ALIGNMENT	4
+#define S6_I2S_INTERRUPT_ENABLE	0x04
+#define S6_I2S_INTERRUPT_RAW	0x08
+#define S6_I2S_INTERRUPT_CLEAR	0x0C
+#define S6_I2S_INTERRUPT_SET	0x10
+#define S6_I2S_MODE		0x20
+#define   S6_I2S_DUAL		0
+#define   S6_I2S_WIDE		1
+#define S6_I2S_TX_DEFAULT	0x24
+#define S6_I2S_DATA_CFG(c)	(0x40 + 0x10 * (c))
+#define   S6_I2S_IN		0
+#define   S6_I2S_OUT		1
+#define   S6_I2S_UNUSED		2
+#define S6_I2S_INTERFACE_CFG(c)	(0x44 + 0x10 * (c))
+#define   S6_I2S_DIV_MASK	0x001fff
+#define   S6_I2S_16BIT		0x000000
+#define   S6_I2S_20BIT		0x002000
+#define   S6_I2S_24BIT		0x004000
+#define   S6_I2S_32BIT		0x006000
+#define   S6_I2S_BITS_MASK	0x006000
+#define   S6_I2S_MEM_16BIT	0x000000
+#define   S6_I2S_MEM_32BIT	0x008000
+#define   S6_I2S_MEM_MASK	0x008000
+#define   S6_I2S_CHANNELS_SHIFT	16
+#define   S6_I2S_CHANNELS_MASK	0x030000
+#define   S6_I2S_SCK_IN		0x000000
+#define   S6_I2S_SCK_OUT	0x040000
+#define   S6_I2S_SCK_DIR	0x040000
+#define   S6_I2S_WS_IN		0x000000
+#define   S6_I2S_WS_OUT		0x080000
+#define   S6_I2S_WS_DIR		0x080000
+#define   S6_I2S_LEFT_FIRST	0x000000
+#define   S6_I2S_RIGHT_FIRST	0x100000
+#define   S6_I2S_FIRST		0x100000
+#define   S6_I2S_CUR_SCK	0x200000
+#define   S6_I2S_CUR_WS		0x400000
+#define S6_I2S_ENABLE(c)	(0x48 + 0x10 * (c))
+#define   S6_I2S_DISABLE_IF	0x02
+#define   S6_I2S_ENABLE_IF	0x03
+#define   S6_I2S_IS_BUSY	0x04
+#define   S6_I2S_DMA_ACTIVE	0x08
+#define   S6_I2S_IS_ENABLED	0x10
+
+#define S6_I2S_NUM_LINES	4
+
+#define S6_I2S_SIF_PORT0	0x0000000
+#define S6_I2S_SIF_PORT1	0x0000080 /* docs say 0x0000010 */
+
+static inline void s6_i2s_write_reg(struct s6000_i2s_dev *dev, int reg, u32 val)
+{
+	writel(val, dev->scbbase + reg);
+}
+
+static inline u32 s6_i2s_read_reg(struct s6000_i2s_dev *dev, int reg)
+{
+	return readl(dev->scbbase + reg);
+}
+
+static inline void s6_i2s_mod_reg(struct s6000_i2s_dev *dev, int reg,
+				  u32 mask, u32 val)
+{
+	val ^= s6_i2s_read_reg(dev, reg) & ~mask;
+	s6_i2s_write_reg(dev, reg, val);
+}
+
+static void s6000_i2s_start_channel(struct s6000_i2s_dev *dev, int channel)
+{
+	int i, j, cur, prev;
+
+	/*
+	 * Wait for WCLK to toggle 5 times before enabling the channel
+	 * s6000 Family Datasheet 3.6.4:
+	 *   "At least two cycles of WS must occur between commands
+	 *    to disable or enable the interface"
+	 */
+	j = 0;
+	prev = ~S6_I2S_CUR_WS;
+	for (i = 1000000; --i && j < 6; ) {
+		cur = s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(channel))
+		       & S6_I2S_CUR_WS;
+		if (prev != cur) {
+			prev = cur;
+			j++;
+		}
+	}
+	if (j < 6)
+		printk(KERN_WARNING "s6000-i2s: timeout waiting for WCLK\n");
+
+	s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_ENABLE_IF);
+}
+
+static void s6000_i2s_stop_channel(struct s6000_i2s_dev *dev, int channel)
+{
+	s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_DISABLE_IF);
+}
+
+static void s6000_i2s_start(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct s6000_i2s_dev *dev = rtd->dai->cpu_dai->private_data;
+	int channel;
+
+	channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+			dev->channel_out : dev->channel_in;
+
+	s6000_i2s_start_channel(dev, channel);
+}
+
+static void s6000_i2s_stop(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct s6000_i2s_dev *dev = rtd->dai->cpu_dai->private_data;
+	int channel;
+
+	channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+			dev->channel_out : dev->channel_in;
+
+	s6000_i2s_stop_channel(dev, channel);
+}
+
+static int s6000_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+			     int after)
+{
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ^ !after)
+			s6000_i2s_start(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (!after)
+			s6000_i2s_stop(substream);
+	}
+	return 0;
+}
+
+static unsigned int s6000_i2s_int_sources(struct s6000_i2s_dev *dev)
+{
+	unsigned int pending;
+	pending = s6_i2s_read_reg(dev, S6_I2S_INTERRUPT_RAW);
+	pending &= S6_I2S_INT_ALIGNMENT |
+		   S6_I2S_INT_UNDERRUN |
+		   S6_I2S_INT_OVERRUN;
+	s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR, pending);
+
+	return pending;
+}
+
+static unsigned int s6000_i2s_check_xrun(struct snd_soc_dai *cpu_dai)
+{
+	struct s6000_i2s_dev *dev = cpu_dai->private_data;
+	unsigned int errors;
+	unsigned int ret;
+
+	errors = s6000_i2s_int_sources(dev);
+	if (likely(!errors))
+		return 0;
+
+	ret = 0;
+	if (errors & S6_I2S_INT_ALIGNMENT)
+		printk(KERN_ERR "s6000-i2s: WCLK misaligned\n");
+	if (errors & S6_I2S_INT_UNDERRUN)
+		ret |= 1 << SNDRV_PCM_STREAM_PLAYBACK;
+	if (errors & S6_I2S_INT_OVERRUN)
+		ret |= 1 << SNDRV_PCM_STREAM_CAPTURE;
+	return ret;
+}
+
+static void s6000_i2s_wait_disabled(struct s6000_i2s_dev *dev)
+{
+	int channel;
+	int n = 50;
+	for (channel = 0; channel < 2; channel++) {
+		while (--n >= 0) {
+			int v = s6_i2s_read_reg(dev, S6_I2S_ENABLE(channel));
+			if ((v & S6_I2S_IS_ENABLED)
+			    || !(v & (S6_I2S_DMA_ACTIVE | S6_I2S_IS_BUSY)))
+				break;
+			udelay(20);
+		}
+	}
+	if (n < 0)
+		printk(KERN_WARNING "s6000-i2s: timeout disabling interfaces");
+}
+
+static int s6000_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+				   unsigned int fmt)
+{
+	struct s6000_i2s_dev *dev = cpu_dai->private_data;
+	u32 w;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		w = S6_I2S_SCK_IN | S6_I2S_WS_IN;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		w = S6_I2S_SCK_OUT | S6_I2S_WS_IN;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		w = S6_I2S_SCK_IN | S6_I2S_WS_OUT;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		w = S6_I2S_SCK_OUT | S6_I2S_WS_OUT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		w |= S6_I2S_LEFT_FIRST;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		w |= S6_I2S_RIGHT_FIRST;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(0),
+		       S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w);
+	s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(1),
+		       S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w);
+
+	return 0;
+}
+
+static int s6000_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
+{
+	struct s6000_i2s_dev *dev = dai->private_data;
+
+	if (!div || (div & 1) || div > (S6_I2S_DIV_MASK + 1) * 2)
+		return -EINVAL;
+
+	s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(div_id),
+		       S6_I2S_DIV_MASK, div / 2 - 1);
+	return 0;
+}
+
+static int s6000_i2s_hw_params(struct snd_pcm_substream *substream,
+			       struct snd_pcm_hw_params *params,
+			       struct snd_soc_dai *dai)
+{
+	struct s6000_i2s_dev *dev = dai->private_data;
+	int interf;
+	u32 w = 0;
+
+	if (dev->wide)
+		interf = 0;
+	else {
+		w |= (((params_channels(params) - 2) / 2)
+		      << S6_I2S_CHANNELS_SHIFT) & S6_I2S_CHANNELS_MASK;
+		interf = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+				? dev->channel_out : dev->channel_in;
+	}
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		w |= S6_I2S_16BIT | S6_I2S_MEM_16BIT;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		w |= S6_I2S_32BIT | S6_I2S_MEM_32BIT;
+		break;
+	default:
+		printk(KERN_WARNING "s6000-i2s: unsupported PCM format %x\n",
+		       params_format(params));
+		return -EINVAL;
+	}
+
+	if (s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(interf))
+	     & S6_I2S_IS_ENABLED) {
+		printk(KERN_ERR "s6000-i2s: interface already enabled\n");
+		return -EBUSY;
+	}
+
+	s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(interf),
+		       S6_I2S_CHANNELS_MASK|S6_I2S_MEM_MASK|S6_I2S_BITS_MASK,
+		       w);
+
+	return 0;
+}
+
+static int s6000_i2s_dai_probe(struct platform_device *pdev,
+			       struct snd_soc_dai *dai)
+{
+	struct s6000_i2s_dev *dev = dai->private_data;
+	struct s6000_snd_platform_data *pdata = pdev->dev.platform_data;
+
+	if (!pdata)
+		return -EINVAL;
+
+	dev->wide = pdata->wide;
+	dev->channel_in = pdata->channel_in;
+	dev->channel_out = pdata->channel_out;
+	dev->lines_in = pdata->lines_in;
+	dev->lines_out = pdata->lines_out;
+
+	s6_i2s_write_reg(dev, S6_I2S_MODE,
+			 dev->wide ? S6_I2S_WIDE : S6_I2S_DUAL);
+
+	if (dev->wide) {
+		int i;
+
+		if (dev->lines_in + dev->lines_out > S6_I2S_NUM_LINES)
+			return -EINVAL;
+
+		dev->channel_in = 0;
+		dev->channel_out = 1;
+		dai->capture.channels_min = 2 * dev->lines_in;
+		dai->capture.channels_max = dai->capture.channels_min;
+		dai->playback.channels_min = 2 * dev->lines_out;
+		dai->playback.channels_max = dai->playback.channels_min;
+
+		for (i = 0; i < dev->lines_out; i++)
+			s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_OUT);
+
+		for (; i < S6_I2S_NUM_LINES - dev->lines_in; i++)
+			s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i),
+					 S6_I2S_UNUSED);
+
+		for (; i < S6_I2S_NUM_LINES; i++)
+			s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_IN);
+	} else {
+		unsigned int cfg[2] = {S6_I2S_UNUSED, S6_I2S_UNUSED};
+
+		if (dev->lines_in > 1 || dev->lines_out > 1)
+			return -EINVAL;
+
+		dai->capture.channels_min = 2 * dev->lines_in;
+		dai->capture.channels_max = 8 * dev->lines_in;
+		dai->playback.channels_min = 2 * dev->lines_out;
+		dai->playback.channels_max = 8 * dev->lines_out;
+
+		if (dev->lines_in)
+			cfg[dev->channel_in] = S6_I2S_IN;
+		if (dev->lines_out)
+			cfg[dev->channel_out] = S6_I2S_OUT;
+
+		s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(0), cfg[0]);
+		s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(1), cfg[1]);
+	}
+
+	if (dev->lines_out) {
+		if (dev->lines_in) {
+			if (!dev->dma_params.dma_out)
+				return -ENODEV;
+		} else {
+			dev->dma_params.dma_out = dev->dma_params.dma_in;
+			dev->dma_params.dma_in = 0;
+		}
+	}
+	dev->dma_params.sif_in = dev->sifbase + (dev->channel_in ?
+					S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0);
+	dev->dma_params.sif_out = dev->sifbase + (dev->channel_out ?
+					S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0);
+	dev->dma_params.same_rate = pdata->same_rate | pdata->wide;
+	return 0;
+}
+
+#define S6000_I2S_RATES	(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
+			 SNDRV_PCM_RATE_8000_192000)
+#define S6000_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops s6000_i2s_dai_ops = {
+	.set_fmt = s6000_i2s_set_dai_fmt,
+	.set_clkdiv = s6000_i2s_set_clkdiv,
+	.hw_params = s6000_i2s_hw_params,
+};
+
+struct snd_soc_dai s6000_i2s_dai = {
+	.name = "s6000-i2s",
+	.id = 0,
+	.probe = s6000_i2s_dai_probe,
+	.playback = {
+		.channels_min = 2,
+		.channels_max = 8,
+		.formats = S6000_I2S_FORMATS,
+		.rates = S6000_I2S_RATES,
+		.rate_min = 0,
+		.rate_max = 1562500,
+	},
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 8,
+		.formats = S6000_I2S_FORMATS,
+		.rates = S6000_I2S_RATES,
+		.rate_min = 0,
+		.rate_max = 1562500,
+	},
+	.ops = &s6000_i2s_dai_ops,
+}
+EXPORT_SYMBOL_GPL(s6000_i2s_dai);
+
+static int __devinit s6000_i2s_probe(struct platform_device *pdev)
+{
+	struct s6000_i2s_dev *dev;
+	struct resource *scbmem, *sifmem, *region, *dma1, *dma2;
+	u8 __iomem *mmio;
+	int ret;
+
+	scbmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!scbmem) {
+		dev_err(&pdev->dev, "no mem resource?\n");
+		ret = -ENODEV;
+		goto err_release_none;
+	}
+
+	region = request_mem_region(scbmem->start,
+				    scbmem->end - scbmem->start + 1,
+				    pdev->name);
+	if (!region) {
+		dev_err(&pdev->dev, "I2S SCB region already claimed\n");
+		ret = -EBUSY;
+		goto err_release_none;
+	}
+
+	mmio = ioremap(scbmem->start, scbmem->end - scbmem->start + 1);
+	if (!mmio) {
+		dev_err(&pdev->dev, "can't ioremap SCB region\n");
+		ret = -ENOMEM;
+		goto err_release_scb;
+	}
+
+	sifmem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!sifmem) {
+		dev_err(&pdev->dev, "no second mem resource?\n");
+		ret = -ENODEV;
+		goto err_release_map;
+	}
+
+	region = request_mem_region(sifmem->start,
+				    sifmem->end - sifmem->start + 1,
+				    pdev->name);
+	if (!region) {
+		dev_err(&pdev->dev, "I2S SIF region already claimed\n");
+		ret = -EBUSY;
+		goto err_release_map;
+	}
+
+	dma1 = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!dma1) {
+		dev_err(&pdev->dev, "no dma resource?\n");
+		ret = -ENODEV;
+		goto err_release_sif;
+	}
+
+	region = request_mem_region(dma1->start, dma1->end - dma1->start + 1,
+				    pdev->name);
+	if (!region) {
+		dev_err(&pdev->dev, "I2S DMA region already claimed\n");
+		ret = -EBUSY;
+		goto err_release_sif;
+	}
+
+	dma2 = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (dma2) {
+		region = request_mem_region(dma2->start,
+					    dma2->end - dma2->start + 1,
+					    pdev->name);
+		if (!region) {
+			dev_err(&pdev->dev,
+				"I2S DMA region already claimed\n");
+			ret = -EBUSY;
+			goto err_release_dma1;
+		}
+	}
+
+	dev = kzalloc(sizeof(struct s6000_i2s_dev), GFP_KERNEL);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto err_release_dma2;
+	}
+
+	s6000_i2s_dai.dev = &pdev->dev;
+	s6000_i2s_dai.private_data = dev;
+	s6000_i2s_dai.dma_data = &dev->dma_params;
+
+	dev->sifbase = sifmem->start;
+	dev->scbbase = mmio;
+
+	s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0);
+	s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR,
+			 S6_I2S_INT_ALIGNMENT |
+			 S6_I2S_INT_UNDERRUN |
+			 S6_I2S_INT_OVERRUN);
+
+	s6000_i2s_stop_channel(dev, 0);
+	s6000_i2s_stop_channel(dev, 1);
+	s6000_i2s_wait_disabled(dev);
+
+	dev->dma_params.check_xrun = s6000_i2s_check_xrun;
+	dev->dma_params.trigger = s6000_i2s_trigger;
+	dev->dma_params.dma_in = dma1->start;
+	dev->dma_params.dma_out = dma2 ? dma2->start : 0;
+	dev->dma_params.irq = platform_get_irq(pdev, 0);
+	if (dev->dma_params.irq < 0) {
+		dev_err(&pdev->dev, "no irq resource?\n");
+		ret = -ENODEV;
+		goto err_release_dev;
+	}
+
+	s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE,
+			 S6_I2S_INT_ALIGNMENT |
+			 S6_I2S_INT_UNDERRUN |
+			 S6_I2S_INT_OVERRUN);
+
+	ret = snd_soc_register_dai(&s6000_i2s_dai);
+	if (ret)
+		goto err_release_dev;
+
+	return 0;
+
+err_release_dev:
+	kfree(dev);
+err_release_dma2:
+	if (dma2)
+		release_mem_region(dma2->start, dma2->end - dma2->start + 1);
+err_release_dma1:
+	release_mem_region(dma1->start, dma1->end - dma1->start + 1);
+err_release_sif:
+	release_mem_region(sifmem->start, (sifmem->end - sifmem->start) + 1);
+err_release_map:
+	iounmap(mmio);
+err_release_scb:
+	release_mem_region(scbmem->start, (scbmem->end - scbmem->start) + 1);
+err_release_none:
+	return ret;
+}
+
+static void __devexit s6000_i2s_remove(struct platform_device *pdev)
+{
+	struct s6000_i2s_dev *dev = s6000_i2s_dai.private_data;
+	struct resource *region;
+	void __iomem *mmio = dev->scbbase;
+
+	snd_soc_unregister_dai(&s6000_i2s_dai);
+
+	s6000_i2s_stop_channel(dev, 0);
+	s6000_i2s_stop_channel(dev, 1);
+
+	s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0);
+	s6000_i2s_dai.private_data = 0;
+	kfree(dev);
+
+	region = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	release_mem_region(region->start, region->end - region->start + 1);
+
+	region = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (region)
+		release_mem_region(region->start,
+				   region->end - region->start + 1);
+
+	region = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(region->start, (region->end - region->start) + 1);
+
+	iounmap(mmio);
+	region = platform_get_resource(pdev, IORESOURCE_IO, 0);
+	release_mem_region(region->start, (region->end - region->start) + 1);
+}
+
+static struct platform_driver s6000_i2s_driver = {
+	.probe  = s6000_i2s_probe,
+	.remove = __devexit_p(s6000_i2s_remove),
+	.driver = {
+		.name   = "s6000-i2s",
+		.owner  = THIS_MODULE,
+	},
+};
+
+static int __init s6000_i2s_init(void)
+{
+	return platform_driver_register(&s6000_i2s_driver);
+}
+module_init(s6000_i2s_init);
+
+static void __exit s6000_i2s_exit(void)
+{
+	platform_driver_unregister(&s6000_i2s_driver);
+}
+module_exit(s6000_i2s_exit);
+
+MODULE_AUTHOR("Daniel Gloeckner");
+MODULE_DESCRIPTION("Stretch s6000 family I2S SoC Interface");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s6000/s6000-i2s.h b/sound/soc/s6000/s6000-i2s.h
new file mode 100644
index 0000000..2375fdf
--- /dev/null
+++ b/sound/soc/s6000/s6000-i2s.h
@@ -0,0 +1,25 @@
+/*
+ * ALSA SoC I2S Audio Layer for the Stretch s6000 family
+ *
+ * Author:      Daniel Gloeckner, <dg@emlix.com>
+ * Copyright:   (C) 2009 emlix GmbH <info@emlix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _S6000_I2S_H
+#define _S6000_I2S_H
+
+extern struct snd_soc_dai s6000_i2s_dai;
+
+struct s6000_snd_platform_data {
+	int lines_in;
+	int lines_out;
+	int channel_in;
+	int channel_out;
+	int wide;
+	int same_rate;
+};
+#endif
diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c
new file mode 100644
index 0000000..83b8028
--- /dev/null
+++ b/sound/soc/s6000/s6000-pcm.c
@@ -0,0 +1,497 @@
+/*
+ * ALSA PCM interface for the Stetch s6000 family
+ *
+ * Author:      Daniel Gloeckner, <dg@emlix.com>
+ * Copyright:   (C) 2009 emlix GmbH <info@emlix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include <asm/dma.h>
+#include <variant/dmac.h>
+
+#include "s6000-pcm.h"
+
+#define S6_PCM_PREALLOCATE_SIZE (96 * 1024)
+#define S6_PCM_PREALLOCATE_MAX  (2048 * 1024)
+
+static struct snd_pcm_hardware s6000_pcm_hardware = {
+	.info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		 SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID |
+		 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_JOINT_DUPLEX),
+	.formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE),
+	.rates = (SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_5512 | \
+		  SNDRV_PCM_RATE_8000_192000),
+	.rate_min = 0,
+	.rate_max = 1562500,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 0x7ffffff0,
+	.period_bytes_min = 16,
+	.period_bytes_max = 0xfffff0,
+	.periods_min = 2,
+	.periods_max = 1024, /* no limit */
+	.fifo_size = 0,
+};
+
+struct s6000_runtime_data {
+	spinlock_t lock;
+	int period;		/* current DMA period */
+};
+
+static void s6000_pcm_enqueue_dma(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct s6000_runtime_data *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+	int channel;
+	unsigned int period_size;
+	unsigned int dma_offset;
+	dma_addr_t dma_pos;
+	dma_addr_t src, dst;
+
+	period_size = snd_pcm_lib_period_bytes(substream);
+	dma_offset = prtd->period * period_size;
+	dma_pos = runtime->dma_addr + dma_offset;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		src = dma_pos;
+		dst = par->sif_out;
+		channel = par->dma_out;
+	} else {
+		src = par->sif_in;
+		dst = dma_pos;
+		channel = par->dma_in;
+	}
+
+	if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel),
+				    DMA_INDEX_CHNL(channel)))
+		return;
+
+	if (s6dmac_fifo_full(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel))) {
+		printk(KERN_ERR "s6000-pcm: fifo full\n");
+		return;
+	}
+
+	BUG_ON(period_size & 15);
+	s6dmac_put_fifo(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel),
+			src, dst, period_size);
+
+	prtd->period++;
+	if (unlikely(prtd->period >= runtime->periods))
+		prtd->period = 0;
+}
+
+static irqreturn_t s6000_pcm_irq(int irq, void *data)
+{
+	struct snd_pcm *pcm = data;
+	struct snd_soc_pcm_runtime *runtime = pcm->private_data;
+	struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
+	struct s6000_runtime_data *prtd;
+	unsigned int has_xrun;
+	int i, ret = IRQ_NONE;
+	u32 channel[2] = {
+		[SNDRV_PCM_STREAM_PLAYBACK] = params->dma_out,
+		[SNDRV_PCM_STREAM_CAPTURE] = params->dma_in
+	};
+
+	has_xrun = params->check_xrun(runtime->dai->cpu_dai);
+
+	for (i = 0; i < ARRAY_SIZE(channel); ++i) {
+		struct snd_pcm_substream *substream = pcm->streams[i].substream;
+		unsigned int pending;
+
+		if (!channel[i])
+			continue;
+
+		if (unlikely(has_xrun & (1 << i)) &&
+		    substream->runtime &&
+		    snd_pcm_running(substream)) {
+			dev_dbg(pcm->dev, "xrun\n");
+			snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+			ret = IRQ_HANDLED;
+		}
+
+		pending = s6dmac_int_sources(DMA_MASK_DMAC(channel[i]),
+					     DMA_INDEX_CHNL(channel[i]));
+
+		if (pending & 1) {
+			ret = IRQ_HANDLED;
+			if (likely(substream->runtime &&
+				   snd_pcm_running(substream))) {
+				snd_pcm_period_elapsed(substream);
+				dev_dbg(pcm->dev, "period elapsed %x %x\n",
+				       s6dmac_cur_src(DMA_MASK_DMAC(channel[i]),
+						   DMA_INDEX_CHNL(channel[i])),
+				       s6dmac_cur_dst(DMA_MASK_DMAC(channel[i]),
+						   DMA_INDEX_CHNL(channel[i])));
+				prtd = substream->runtime->private_data;
+				spin_lock(&prtd->lock);
+				s6000_pcm_enqueue_dma(substream);
+				spin_unlock(&prtd->lock);
+			}
+		}
+
+		if (unlikely(pending & ~7)) {
+			if (pending & (1 << 3))
+				printk(KERN_WARNING
+				       "s6000-pcm: DMA %x Underflow\n",
+				       channel[i]);
+			if (pending & (1 << 4))
+				printk(KERN_WARNING
+				       "s6000-pcm: DMA %x Overflow\n",
+				       channel[i]);
+			if (pending & 0x1e0)
+				printk(KERN_WARNING
+				       "s6000-pcm: DMA %x Master Error "
+				       "(mask %x)\n",
+				       channel[i], pending >> 5);
+
+		}
+	}
+
+	return ret;
+}
+
+static int s6000_pcm_start(struct snd_pcm_substream *substream)
+{
+	struct s6000_runtime_data *prtd = substream->runtime->private_data;
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+	unsigned long flags;
+	int srcinc;
+	u32 dma;
+
+	spin_lock_irqsave(&prtd->lock, flags);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		srcinc = 1;
+		dma = par->dma_out;
+	} else {
+		srcinc = 0;
+		dma = par->dma_in;
+	}
+	s6dmac_enable_chan(DMA_MASK_DMAC(dma), DMA_INDEX_CHNL(dma),
+			   1 /* priority 1 (0 is max) */,
+			   0 /* peripheral requests w/o xfer length mode */,
+			   srcinc /* source address increment */,
+			   srcinc^1 /* destination address increment */,
+			   0 /* chunksize 0 (skip impossible on this dma) */,
+			   0 /* source skip after chunk (impossible) */,
+			   0 /* destination skip after chunk (impossible) */,
+			   4 /* 16 byte burst size */,
+			   -1 /* don't conserve bandwidth */,
+			   0 /* low watermark irq descriptor theshold */,
+			   0 /* disable hardware timestamps */,
+			   1 /* enable channel */);
+
+	s6000_pcm_enqueue_dma(substream);
+	s6000_pcm_enqueue_dma(substream);
+
+	spin_unlock_irqrestore(&prtd->lock, flags);
+
+	return 0;
+}
+
+static int s6000_pcm_stop(struct snd_pcm_substream *substream)
+{
+	struct s6000_runtime_data *prtd = substream->runtime->private_data;
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+	unsigned long flags;
+	u32 channel;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		channel = par->dma_out;
+	else
+		channel = par->dma_in;
+
+	s6dmac_set_terminal_count(DMA_MASK_DMAC(channel),
+				  DMA_INDEX_CHNL(channel), 0);
+
+	spin_lock_irqsave(&prtd->lock, flags);
+
+	s6dmac_disable_chan(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel));
+
+	spin_unlock_irqrestore(&prtd->lock, flags);
+
+	return 0;
+}
+
+static int s6000_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+	int ret;
+
+	ret = par->trigger(substream, cmd, 0);
+	if (ret < 0)
+		return ret;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		ret = s6000_pcm_start(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ret = s6000_pcm_stop(substream);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	if (ret < 0)
+		return ret;
+
+	return par->trigger(substream, cmd, 1);
+}
+
+static int s6000_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct s6000_runtime_data *prtd = substream->runtime->private_data;
+
+	prtd->period = 0;
+
+	return 0;
+}
+
+static snd_pcm_uframes_t s6000_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct s6000_runtime_data *prtd = runtime->private_data;
+	unsigned long flags;
+	unsigned int offset;
+	dma_addr_t count;
+
+	spin_lock_irqsave(&prtd->lock, flags);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		count = s6dmac_cur_src(DMA_MASK_DMAC(par->dma_out),
+				       DMA_INDEX_CHNL(par->dma_out));
+	else
+		count = s6dmac_cur_dst(DMA_MASK_DMAC(par->dma_in),
+				       DMA_INDEX_CHNL(par->dma_in));
+
+	count -= runtime->dma_addr;
+
+	spin_unlock_irqrestore(&prtd->lock, flags);
+
+	offset = bytes_to_frames(runtime, count);
+	if (unlikely(offset >= runtime->buffer_size))
+		offset = 0;
+
+	return offset;
+}
+
+static int s6000_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct s6000_runtime_data *prtd;
+	int ret;
+
+	snd_soc_set_runtime_hwparams(substream, &s6000_pcm_hardware);
+
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16);
+	if (ret < 0)
+		return ret;
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+	if (ret < 0)
+		return ret;
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+
+	if (par->same_rate) {
+		int rate;
+		spin_lock(&par->lock); /* needed? */
+		rate = par->rate;
+		spin_unlock(&par->lock);
+		if (rate != -1) {
+			ret = snd_pcm_hw_constraint_minmax(runtime,
+							SNDRV_PCM_HW_PARAM_RATE,
+							rate, rate);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	prtd = kzalloc(sizeof(struct s6000_runtime_data), GFP_KERNEL);
+	if (prtd == NULL)
+		return -ENOMEM;
+
+	spin_lock_init(&prtd->lock);
+
+	runtime->private_data = prtd;
+
+	return 0;
+}
+
+static int s6000_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct s6000_runtime_data *prtd = runtime->private_data;
+
+	kfree(prtd);
+
+	return 0;
+}
+
+static int s6000_pcm_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+	int ret;
+	ret = snd_pcm_lib_malloc_pages(substream,
+				       params_buffer_bytes(hw_params));
+	if (ret < 0) {
+		printk(KERN_WARNING "s6000-pcm: allocation of memory failed\n");
+		return ret;
+	}
+
+	if (par->same_rate) {
+		spin_lock(&par->lock);
+		if (par->rate == -1 ||
+		    !(par->in_use & ~(1 << substream->stream))) {
+			par->rate = params_rate(hw_params);
+			par->in_use |= 1 << substream->stream;
+		} else if (params_rate(hw_params) != par->rate) {
+			snd_pcm_lib_free_pages(substream);
+			par->in_use &= ~(1 << substream->stream);
+			ret = -EBUSY;
+		}
+		spin_unlock(&par->lock);
+	}
+	return ret;
+}
+
+static int s6000_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
+	struct s6000_pcm_dma_params *par = soc_runtime->dai->cpu_dai->dma_data;
+
+	spin_lock(&par->lock);
+	par->in_use &= ~(1 << substream->stream);
+	if (!par->in_use)
+		par->rate = -1;
+	spin_unlock(&par->lock);
+
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static struct snd_pcm_ops s6000_pcm_ops = {
+	.open = 	s6000_pcm_open,
+	.close = 	s6000_pcm_close,
+	.ioctl = 	snd_pcm_lib_ioctl,
+	.hw_params = 	s6000_pcm_hw_params,
+	.hw_free = 	s6000_pcm_hw_free,
+	.trigger =	s6000_pcm_trigger,
+	.prepare = 	s6000_pcm_prepare,
+	.pointer = 	s6000_pcm_pointer,
+};
+
+static void s6000_pcm_free(struct snd_pcm *pcm)
+{
+	struct snd_soc_pcm_runtime *runtime = pcm->private_data;
+	struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
+
+	free_irq(params->irq, pcm);
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static u64 s6000_pcm_dmamask = DMA_32BIT_MASK;
+
+static int s6000_pcm_new(struct snd_card *card,
+			 struct snd_soc_dai *dai, struct snd_pcm *pcm)
+{
+	struct snd_soc_pcm_runtime *runtime = pcm->private_data;
+	struct s6000_pcm_dma_params *params = runtime->dai->cpu_dai->dma_data;
+	int res;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &s6000_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_32BIT_MASK;
+
+	if (params->dma_in) {
+		s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in),
+				    DMA_INDEX_CHNL(params->dma_in));
+		s6dmac_int_sources(DMA_MASK_DMAC(params->dma_in),
+				   DMA_INDEX_CHNL(params->dma_in));
+	}
+
+	if (params->dma_out) {
+		s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_out),
+				    DMA_INDEX_CHNL(params->dma_out));
+		s6dmac_int_sources(DMA_MASK_DMAC(params->dma_out),
+				   DMA_INDEX_CHNL(params->dma_out));
+	}
+
+	res = request_irq(params->irq, s6000_pcm_irq, IRQF_SHARED,
+			  s6000_soc_platform.name, pcm);
+	if (res) {
+		printk(KERN_ERR "s6000-pcm couldn't get IRQ\n");
+		return res;
+	}
+
+	res = snd_pcm_lib_preallocate_pages_for_all(pcm,
+						    SNDRV_DMA_TYPE_DEV,
+						    card->dev,
+						    S6_PCM_PREALLOCATE_SIZE,
+						    S6_PCM_PREALLOCATE_MAX);
+	if (res)
+		printk(KERN_WARNING "s6000-pcm: preallocation failed\n");
+
+	spin_lock_init(&params->lock);
+	params->in_use = 0;
+	params->rate = -1;
+	return 0;
+}
+
+struct snd_soc_platform s6000_soc_platform = {
+	.name = 	"s6000-audio",
+	.pcm_ops = 	&s6000_pcm_ops,
+	.pcm_new = 	s6000_pcm_new,
+	.pcm_free = 	s6000_pcm_free,
+};
+EXPORT_SYMBOL_GPL(s6000_soc_platform);
+
+static int __init s6000_pcm_init(void)
+{
+	return snd_soc_register_platform(&s6000_soc_platform);
+}
+module_init(s6000_pcm_init);
+
+static void __exit s6000_pcm_exit(void)
+{
+	snd_soc_unregister_platform(&s6000_soc_platform);
+}
+module_exit(s6000_pcm_exit);
+
+MODULE_AUTHOR("Daniel Gloeckner");
+MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/s6000/s6000-pcm.h b/sound/soc/s6000/s6000-pcm.h
new file mode 100644
index 0000000..96f23f6
--- /dev/null
+++ b/sound/soc/s6000/s6000-pcm.h
@@ -0,0 +1,35 @@
+/*
+ * ALSA PCM interface for the Stretch s6000 family
+ *
+ * Author:      Daniel Gloeckner, <dg@emlix.com>
+ * Copyright:   (C) 2009 emlix GmbH <info@emlix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _S6000_PCM_H
+#define _S6000_PCM_H
+
+struct snd_soc_dai;
+struct snd_pcm_substream;
+
+struct s6000_pcm_dma_params {
+	unsigned int (*check_xrun)(struct snd_soc_dai *cpu_dai);
+	int (*trigger)(struct snd_pcm_substream *substream, int cmd, int after);
+	dma_addr_t sif_in;
+	dma_addr_t sif_out;
+	u32 dma_in;
+	u32 dma_out;
+	int irq;
+	int same_rate;
+
+	spinlock_t lock;
+	int in_use;
+	int rate;
+};
+
+extern struct snd_soc_platform s6000_soc_platform;
+
+#endif
diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c
new file mode 100644
index 0000000..b5f95f9
--- /dev/null
+++ b/sound/soc/s6000/s6105-ipcam.c
@@ -0,0 +1,244 @@
+/*
+ * ASoC driver for Stretch s6105 IP camera platform
+ *
+ * Author:      Daniel Gloeckner, <dg@emlix.com>
+ * Copyright:   (C) 2009 emlix GmbH <info@emlix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <variant/dmac.h>
+
+#include "../codecs/tlv320aic3x.h"
+#include "s6000-pcm.h"
+#include "s6000-i2s.h"
+
+#define S6105_CAM_CODEC_CLOCK 12288000
+
+static int s6105_hw_params(struct snd_pcm_substream *substream,
+			   struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	int ret = 0;
+
+	/* set codec DAI configuration */
+	ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+					     SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI configuration */
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
+					   SND_SOC_DAIFMT_NB_NF);
+	if (ret < 0)
+		return ret;
+
+	/* set the codec system clock */
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0, S6105_CAM_CODEC_CLOCK,
+					    SND_SOC_CLOCK_OUT);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct snd_soc_ops s6105_ops = {
+	.hw_params = s6105_hw_params,
+};
+
+/* s6105 machine dapm widgets */
+static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
+	SND_SOC_DAPM_LINE("Audio Out Differential", NULL),
+	SND_SOC_DAPM_LINE("Audio Out Stereo", NULL),
+	SND_SOC_DAPM_LINE("Audio In", NULL),
+};
+
+/* s6105 machine audio_mapnections to the codec pins */
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Audio Out connected to HPLOUT, HPLCOM, HPROUT */
+	{"Audio Out Differential", NULL, "HPLOUT"},
+	{"Audio Out Differential", NULL, "HPLCOM"},
+	{"Audio Out Stereo", NULL, "HPLOUT"},
+	{"Audio Out Stereo", NULL, "HPROUT"},
+
+	/* Audio In connected to LINE1L, LINE1R */
+	{"LINE1L", NULL, "Audio In"},
+	{"LINE1R", NULL, "Audio In"},
+};
+
+static int output_type_info(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item) {
+		uinfo->value.enumerated.item = 1;
+		strcpy(uinfo->value.enumerated.name, "HPLOUT/HPROUT");
+	} else {
+		strcpy(uinfo->value.enumerated.name, "HPLOUT/HPLCOM");
+	}
+	return 0;
+}
+
+static int output_type_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.enumerated.item[0] = kcontrol->private_value;
+	return 0;
+}
+
+static int output_type_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = kcontrol->private_data;
+	unsigned int val = (ucontrol->value.enumerated.item[0] != 0);
+	char *differential = "Audio Out Differential";
+	char *stereo = "Audio Out Stereo";
+
+	if (kcontrol->private_value == val)
+		return 0;
+	kcontrol->private_value = val;
+	snd_soc_dapm_disable_pin(codec, val ? differential : stereo);
+	snd_soc_dapm_sync(codec);
+	snd_soc_dapm_enable_pin(codec, val ? stereo : differential);
+	snd_soc_dapm_sync(codec);
+
+	return 1;
+}
+
+static const struct snd_kcontrol_new audio_out_mux = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Master Output Mux",
+	.index = 0,
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = output_type_info,
+	.get = output_type_get,
+	.put = output_type_put,
+	.private_value = 1 /* default to stereo */
+};
+
+/* Logic for a aic3x as connected on the s6105 ip camera ref design */
+static int s6105_aic3x_init(struct snd_soc_codec *codec)
+{
+	/* Add s6105 specific widgets */
+	snd_soc_dapm_new_controls(codec, aic3x_dapm_widgets,
+				  ARRAY_SIZE(aic3x_dapm_widgets));
+
+	/* Set up s6105 specific audio path audio_map */
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	/* not present */
+	snd_soc_dapm_nc_pin(codec, "MONO_LOUT");
+	snd_soc_dapm_nc_pin(codec, "LINE2L");
+	snd_soc_dapm_nc_pin(codec, "LINE2R");
+
+	/* not connected */
+	snd_soc_dapm_nc_pin(codec, "MIC3L"); /* LINE2L on this chip */
+	snd_soc_dapm_nc_pin(codec, "MIC3R"); /* LINE2R on this chip */
+	snd_soc_dapm_nc_pin(codec, "LLOUT");
+	snd_soc_dapm_nc_pin(codec, "RLOUT");
+	snd_soc_dapm_nc_pin(codec, "HPRCOM");
+
+	/* always connected */
+	snd_soc_dapm_enable_pin(codec, "Audio In");
+
+	/* must correspond to audio_out_mux.private_value initializer */
+	snd_soc_dapm_disable_pin(codec, "Audio Out Differential");
+	snd_soc_dapm_sync(codec);
+	snd_soc_dapm_enable_pin(codec, "Audio Out Stereo");
+
+	snd_soc_dapm_sync(codec);
+
+	snd_ctl_add(codec->card, snd_ctl_new1(&audio_out_mux, codec));
+
+	return 0;
+}
+
+/* s6105 digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link s6105_dai = {
+	.name = "TLV320AIC31",
+	.stream_name = "AIC31",
+	.cpu_dai = &s6000_i2s_dai,
+	.codec_dai = &aic3x_dai,
+	.init = s6105_aic3x_init,
+	.ops = &s6105_ops,
+};
+
+/* s6105 audio machine driver */
+static struct snd_soc_card snd_soc_card_s6105 = {
+	.name = "Stretch IP Camera",
+	.platform = &s6000_soc_platform,
+	.dai_link = &s6105_dai,
+	.num_links = 1,
+};
+
+/* s6105 audio private data */
+static struct aic3x_setup_data s6105_aic3x_setup = {
+	.i2c_bus = 0,
+	.i2c_address = 0x18,
+};
+
+/* s6105 audio subsystem */
+static struct snd_soc_device s6105_snd_devdata = {
+	.card = &snd_soc_card_s6105,
+	.codec_dev = &soc_codec_dev_aic3x,
+	.codec_data = &s6105_aic3x_setup,
+};
+
+static struct s6000_snd_platform_data __initdata s6105_snd_data = {
+	.wide		= 0,
+	.channel_in	= 0,
+	.channel_out	= 1,
+	.lines_in	= 1,
+	.lines_out	= 1,
+	.same_rate	= 1,
+};
+
+static struct platform_device *s6105_snd_device;
+
+static int __init s6105_init(void)
+{
+	int ret;
+
+	s6105_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!s6105_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(s6105_snd_device, &s6105_snd_devdata);
+	s6105_snd_devdata.dev = &s6105_snd_device->dev;
+	platform_device_add_data(s6105_snd_device, &s6105_snd_data,
+				 sizeof(s6105_snd_data));
+
+	ret = platform_device_add(s6105_snd_device);
+	if (ret)
+		platform_device_put(s6105_snd_device);
+
+	return ret;
+}
+
+static void __exit s6105_exit(void)
+{
+	platform_device_unregister(s6105_snd_device);
+}
+
+module_init(s6105_init);
+module_exit(s6105_exit);
+
+MODULE_AUTHOR("Daniel Gloeckner");
+MODULE_DESCRIPTION("Stretch s6105 IP camera ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sh/ssi.c b/sound/soc/sh/ssi.c
index 56fa087..b378096 100644
--- a/sound/soc/sh/ssi.c
+++ b/sound/soc/sh/ssi.c
@@ -145,7 +145,7 @@
 	recv = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
 
 	pr_debug("ssi_hw_params() enter\nssicr was    %08lx\n", ssicr);
-	pr_debug("bits: %d channels: %d\n", bits, channels);
+	pr_debug("bits: %u channels: %u\n", bits, channels);
 
 	ssicr &= ~(CR_TRMD | CR_CHNL_MASK | CR_DWL_MASK | CR_PDTA |
 		   CR_SWL_MASK);
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
new file mode 100644
index 0000000..c8ceddc
--- /dev/null
+++ b/sound/soc/soc-cache.c
@@ -0,0 +1,218 @@
+/*
+ * soc-cache.c  --  ASoC register cache helpers
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+static unsigned int snd_soc_7_9_read(struct snd_soc_codec *codec,
+				     unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= codec->reg_cache_size)
+		return -1;
+	return cache[reg];
+}
+
+static int snd_soc_7_9_write(struct snd_soc_codec *codec, unsigned int reg,
+			     unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	u8 data[2];
+	int ret;
+
+	BUG_ON(codec->volatile_register);
+
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	if (reg < codec->reg_cache_size)
+		cache[reg] = value;
+	ret = codec->hw_write(codec->control_data, data, 2);
+	if (ret == 2)
+		return 0;
+	if (ret < 0)
+		return ret;
+	else
+		return -EIO;
+}
+
+#if defined(CONFIG_SPI_MASTER)
+static int snd_soc_7_9_spi_write(void *control_data, const char *data,
+				 int len)
+{
+	struct spi_device *spi = control_data;
+	struct spi_transfer t;
+	struct spi_message m;
+	u8 msg[2];
+
+	if (len <= 0)
+		return 0;
+
+	msg[0] = data[0];
+	msg[1] = data[1];
+
+	spi_message_init(&m);
+	memset(&t, 0, (sizeof t));
+
+	t.tx_buf = &msg[0];
+	t.len = len;
+
+	spi_message_add_tail(&t, &m);
+	spi_sync(spi, &m);
+
+	return len;
+}
+#else
+#define snd_soc_7_9_spi_write NULL
+#endif
+
+static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
+			      unsigned int value)
+{
+	u16 *reg_cache = codec->reg_cache;
+	u8 data[3];
+
+	data[0] = reg;
+	data[1] = (value >> 8) & 0xff;
+	data[2] = value & 0xff;
+
+	if (!snd_soc_codec_volatile_register(codec, reg))
+		reg_cache[reg] = value;
+
+	if (codec->hw_write(codec->control_data, data, 3) == 3)
+		return 0;
+	else
+		return -EIO;
+}
+
+static unsigned int snd_soc_8_16_read(struct snd_soc_codec *codec,
+				      unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+
+	if (reg >= codec->reg_cache_size ||
+	    snd_soc_codec_volatile_register(codec, reg))
+		return codec->hw_read(codec, reg);
+	else
+		return cache[reg];
+}
+
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+static unsigned int snd_soc_8_16_read_i2c(struct snd_soc_codec *codec,
+					  unsigned int r)
+{
+	struct i2c_msg xfer[2];
+	u8 reg = r;
+	u16 data;
+	int ret;
+	struct i2c_client *client = codec->control_data;
+
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 1;
+	xfer[0].buf = &reg;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = 2;
+	xfer[1].buf = (u8 *)&data;
+
+	ret = i2c_transfer(client->adapter, xfer, 2);
+	if (ret != 2) {
+		dev_err(&client->dev, "i2c_transfer() returned %d\n", ret);
+		return 0;
+	}
+
+	return (data >> 8) | ((data & 0xff) << 8);
+}
+#else
+#define snd_soc_8_16_read_i2c NULL
+#endif
+
+static struct {
+	int addr_bits;
+	int data_bits;
+	int (*write)(struct snd_soc_codec *codec, unsigned int, unsigned int);
+	int (*spi_write)(void *, const char *, int);
+	unsigned int (*read)(struct snd_soc_codec *, unsigned int);
+	unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
+} io_types[] = {
+	{ 7, 9, snd_soc_7_9_write, snd_soc_7_9_spi_write, snd_soc_7_9_read },
+	{ 8, 16, snd_soc_8_16_write, NULL, snd_soc_8_16_read,
+	  snd_soc_8_16_read_i2c },
+};
+
+/**
+ * snd_soc_codec_set_cache_io: Set up standard I/O functions.
+ *
+ * @codec: CODEC to configure.
+ * @type: Type of cache.
+ * @addr_bits: Number of bits of register address data.
+ * @data_bits: Number of bits of data per register.
+ * @control: Control bus used.
+ *
+ * Register formats are frequently shared between many I2C and SPI
+ * devices.  In order to promote code reuse the ASoC core provides
+ * some standard implementations of CODEC read and write operations
+ * which can be set up using this function.
+ *
+ * The caller is responsible for allocating and initialising the
+ * actual cache.
+ *
+ * Note that at present this code cannot be used by CODECs with
+ * volatile registers.
+ */
+int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
+			       int addr_bits, int data_bits,
+			       enum snd_soc_control_type control)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(io_types); i++)
+		if (io_types[i].addr_bits == addr_bits &&
+		    io_types[i].data_bits == data_bits)
+			break;
+	if (i == ARRAY_SIZE(io_types)) {
+		printk(KERN_ERR
+		       "No I/O functions for %d bit address %d bit data\n",
+		       addr_bits, data_bits);
+		return -EINVAL;
+	}
+
+	codec->write = io_types[i].write;
+	codec->read = io_types[i].read;
+
+	switch (control) {
+	case SND_SOC_CUSTOM:
+		break;
+
+	case SND_SOC_I2C:
+#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
+		codec->hw_write = (hw_write_t)i2c_master_send;
+#endif
+		if (io_types[i].i2c_read)
+			codec->hw_read = io_types[i].i2c_read;
+		break;
+
+	case SND_SOC_SPI:
+		if (io_types[i].spi_write)
+			codec->hw_write = io_types[i].spi_write;
+		break;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 1cd149b..e984a17c 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -28,6 +28,7 @@
 #include <linux/bitops.h>
 #include <linux/debugfs.h>
 #include <linux/platform_device.h>
+#include <sound/ac97_codec.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -113,6 +114,35 @@
 }
 #endif
 
+static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_soc_card *card = socdev->card;
+	struct snd_soc_dai_link *machine = rtd->dai;
+	struct snd_soc_dai *cpu_dai = machine->cpu_dai;
+	struct snd_soc_dai *codec_dai = machine->codec_dai;
+	int ret;
+
+	if (codec_dai->symmetric_rates || cpu_dai->symmetric_rates ||
+	    machine->symmetric_rates) {
+		dev_dbg(card->dev, "Symmetry forces %dHz rate\n", 
+			machine->rate);
+
+		ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+						   SNDRV_PCM_HW_PARAM_RATE,
+						   machine->rate,
+						   machine->rate);
+		if (ret < 0) {
+			dev_err(card->dev,
+				"Unable to apply rate symmetry constraint: %d\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 /*
  * Called by ALSA when a PCM substream is opened, the runtime->hw record is
  * then initialized and any private data can be allocated. This also calls
@@ -221,6 +251,13 @@
 		goto machine_err;
 	}
 
+	/* Symmetry only applies if we've already got an active stream. */
+	if (cpu_dai->active || codec_dai->active) {
+		ret = soc_pcm_apply_symmetry(substream);
+		if (ret != 0)
+			goto machine_err;
+	}
+
 	pr_debug("asoc: %s <-> %s info:\n", codec_dai->name, cpu_dai->name);
 	pr_debug("asoc: rate mask 0x%x\n", runtime->hw.rates);
 	pr_debug("asoc: min ch %d max ch %d\n", runtime->hw.channels_min,
@@ -263,7 +300,6 @@
 {
 	struct snd_soc_card *card = container_of(work, struct snd_soc_card,
 						 delayed_work.work);
-	struct snd_soc_device *socdev = card->socdev;
 	struct snd_soc_codec *codec = card->codec;
 	struct snd_soc_dai *codec_dai;
 	int i;
@@ -279,27 +315,10 @@
 
 		/* are we waiting on this codec DAI stream */
 		if (codec_dai->pop_wait == 1) {
-
-			/* Reduce power if no longer active */
-			if (codec->active == 0) {
-				pr_debug("pop wq D1 %s %s\n", codec->name,
-					 codec_dai->playback.stream_name);
-				snd_soc_dapm_set_bias_level(socdev,
-					SND_SOC_BIAS_PREPARE);
-			}
-
 			codec_dai->pop_wait = 0;
 			snd_soc_dapm_stream_event(codec,
 				codec_dai->playback.stream_name,
 				SND_SOC_DAPM_STREAM_STOP);
-
-			/* Fall into standby if no longer active */
-			if (codec->active == 0) {
-				pr_debug("pop wq D3 %s %s\n", codec->name,
-					 codec_dai->playback.stream_name);
-				snd_soc_dapm_set_bias_level(socdev,
-					SND_SOC_BIAS_STANDBY);
-			}
 		}
 	}
 	mutex_unlock(&pcm_mutex);
@@ -363,10 +382,6 @@
 		snd_soc_dapm_stream_event(codec,
 			codec_dai->capture.stream_name,
 			SND_SOC_DAPM_STREAM_STOP);
-
-		if (codec->active == 0 && codec_dai->pop_wait == 0)
-			snd_soc_dapm_set_bias_level(socdev,
-						SND_SOC_BIAS_STANDBY);
 	}
 
 	mutex_unlock(&pcm_mutex);
@@ -431,36 +446,16 @@
 		cancel_delayed_work(&card->delayed_work);
 	}
 
-	/* do we need to power up codec */
-	if (codec->bias_level != SND_SOC_BIAS_ON) {
-		snd_soc_dapm_set_bias_level(socdev,
-					    SND_SOC_BIAS_PREPARE);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		snd_soc_dapm_stream_event(codec,
+					  codec_dai->playback.stream_name,
+					  SND_SOC_DAPM_STREAM_START);
+	else
+		snd_soc_dapm_stream_event(codec,
+					  codec_dai->capture.stream_name,
+					  SND_SOC_DAPM_STREAM_START);
 
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			snd_soc_dapm_stream_event(codec,
-					codec_dai->playback.stream_name,
-					SND_SOC_DAPM_STREAM_START);
-		else
-			snd_soc_dapm_stream_event(codec,
-					codec_dai->capture.stream_name,
-					SND_SOC_DAPM_STREAM_START);
-
-		snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON);
-		snd_soc_dai_digital_mute(codec_dai, 0);
-
-	} else {
-		/* codec already powered - power on widgets */
-		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-			snd_soc_dapm_stream_event(codec,
-					codec_dai->playback.stream_name,
-					SND_SOC_DAPM_STREAM_START);
-		else
-			snd_soc_dapm_stream_event(codec,
-					codec_dai->capture.stream_name,
-					SND_SOC_DAPM_STREAM_START);
-
-		snd_soc_dai_digital_mute(codec_dai, 0);
-	}
+	snd_soc_dai_digital_mute(codec_dai, 0);
 
 out:
 	mutex_unlock(&pcm_mutex);
@@ -521,6 +516,8 @@
 		}
 	}
 
+	machine->rate = params_rate(params);
+
 out:
 	mutex_unlock(&pcm_mutex);
 	return ret;
@@ -623,8 +620,9 @@
 
 #ifdef CONFIG_PM
 /* powers down audio subsystem for suspend */
-static int soc_suspend(struct platform_device *pdev, pm_message_t state)
+static int soc_suspend(struct device *dev)
 {
+	struct platform_device *pdev = to_platform_device(dev);
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_card *card = socdev->card;
 	struct snd_soc_platform *platform = card->platform;
@@ -632,6 +630,12 @@
 	struct snd_soc_codec *codec = card->codec;
 	int i;
 
+	/* If the initialization of this soc device failed, there is no codec
+	 * associated with it. Just bail out in this case.
+	 */
+	if (!codec)
+		return 0;
+
 	/* Due to the resume being scheduled into a workqueue we could
 	* suspend before that's finished - wait for it to complete.
 	 */
@@ -654,7 +658,7 @@
 		snd_pcm_suspend_all(card->dai_link[i].pcm);
 
 	if (card->suspend_pre)
-		card->suspend_pre(pdev, state);
+		card->suspend_pre(pdev, PMSG_SUSPEND);
 
 	for (i = 0; i < card->num_links; i++) {
 		struct snd_soc_dai  *cpu_dai = card->dai_link[i].cpu_dai;
@@ -680,7 +684,7 @@
 	}
 
 	if (codec_dev->suspend)
-		codec_dev->suspend(pdev, state);
+		codec_dev->suspend(pdev, PMSG_SUSPEND);
 
 	for (i = 0; i < card->num_links; i++) {
 		struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
@@ -689,7 +693,7 @@
 	}
 
 	if (card->suspend_post)
-		card->suspend_post(pdev, state);
+		card->suspend_post(pdev, PMSG_SUSPEND);
 
 	return 0;
 }
@@ -763,8 +767,9 @@
 }
 
 /* powers up audio subsystem after a suspend */
-static int soc_resume(struct platform_device *pdev)
+static int soc_resume(struct device *dev)
 {
+	struct platform_device *pdev = to_platform_device(dev);
 	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
 	struct snd_soc_card *card = socdev->card;
 	struct snd_soc_dai *cpu_dai = card->dai_link[0].cpu_dai;
@@ -786,6 +791,44 @@
 	return 0;
 }
 
+/**
+ * snd_soc_suspend_device: Notify core of device suspend
+ *
+ * @dev: Device being suspended.
+ *
+ * In order to ensure that the entire audio subsystem is suspended in a
+ * coordinated fashion ASoC devices should suspend themselves when
+ * called by ASoC.  When the standard kernel suspend process asks the
+ * device to suspend it should call this function to initiate a suspend
+ * of the entire ASoC card.
+ *
+ * \note Currently this function is stubbed out.
+ */
+int snd_soc_suspend_device(struct device *dev)
+{
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_suspend_device);
+
+/**
+ * snd_soc_resume_device: Notify core of device resume
+ *
+ * @dev: Device being resumed.
+ *
+ * In order to ensure that the entire audio subsystem is resumed in a
+ * coordinated fashion ASoC devices should resume themselves when called
+ * by ASoC.  When the standard kernel resume process asks the device
+ * to resume it should call this function.  Once all the components of
+ * the card have notified that they are ready to be resumed the card
+ * will be resumed.
+ *
+ * \note Currently this function is stubbed out.
+ */
+int snd_soc_resume_device(struct device *dev)
+{
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_resume_device);
 #else
 #define soc_suspend	NULL
 #define soc_resume	NULL
@@ -979,16 +1022,39 @@
 	return 0;
 }
 
+static int soc_poweroff(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_card *card = socdev->card;
+
+	if (!card->instantiated)
+		return 0;
+
+	/* Flush out pmdown_time work - we actually do want to run it
+	 * now, we're shutting down so no imminent restart. */
+	run_delayed_work(&card->delayed_work);
+
+	snd_soc_dapm_shutdown(socdev);
+
+	return 0;
+}
+
+static struct dev_pm_ops soc_pm_ops = {
+	.suspend = soc_suspend,
+	.resume = soc_resume,
+	.poweroff = soc_poweroff,
+};
+
 /* ASoC platform driver */
 static struct platform_driver soc_driver = {
 	.driver		= {
 		.name		= "soc-audio",
 		.owner		= THIS_MODULE,
+		.pm		= &soc_pm_ops,
 	},
 	.probe		= soc_probe,
 	.remove		= soc_remove,
-	.suspend	= soc_suspend,
-	.resume		= soc_resume,
 };
 
 /* create a new pcm */
@@ -1060,6 +1126,23 @@
 	return ret;
 }
 
+/**
+ * snd_soc_codec_volatile_register: Report if a register is volatile.
+ *
+ * @codec: CODEC to query.
+ * @reg: Register to query.
+ *
+ * Boolean function indiciating if a CODEC register is volatile.
+ */
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg)
+{
+	if (codec->volatile_register)
+		return codec->volatile_register(reg);
+	else
+		return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_volatile_register);
+
 /* codec register dump */
 static ssize_t soc_codec_reg_show(struct snd_soc_codec *codec, char *buf)
 {
@@ -1073,6 +1156,9 @@
 
 	count += sprintf(buf, "%s registers\n", codec->name);
 	for (i = 0; i < codec->reg_cache_size; i += step) {
+		if (codec->readable_register && !codec->readable_register(i))
+			continue;
+
 		count += sprintf(buf + count, "%2x: ", i);
 		if (count >= PAGE_SIZE - 1)
 			break;
@@ -1262,10 +1348,10 @@
  * Returns 1 for change else 0.
  */
 int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
-				unsigned short mask, unsigned short value)
+				unsigned int mask, unsigned int value)
 {
 	int change;
-	unsigned short old, new;
+	unsigned int old, new;
 
 	mutex_lock(&io_mutex);
 	old = snd_soc_read(codec, reg);
@@ -1292,10 +1378,10 @@
  * Returns 1 for change else 0.
  */
 int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned short reg,
-				unsigned short mask, unsigned short value)
+				unsigned int mask, unsigned int value)
 {
 	int change;
-	unsigned short old, new;
+	unsigned int old, new;
 
 	mutex_lock(&io_mutex);
 	old = snd_soc_read(codec, reg);
@@ -1334,6 +1420,7 @@
 		return ret;
 	}
 
+	codec->socdev = socdev;
 	codec->card->dev = socdev->dev;
 	codec->card->private_data = codec;
 	strncpy(codec->card->driver, codec->name, sizeof(codec->card->driver));
@@ -1378,14 +1465,20 @@
 				continue;
 			}
 		}
-		if (card->dai_link[i].codec_dai->ac97_control)
+		if (card->dai_link[i].codec_dai->ac97_control) {
 			ac97 = 1;
+			snd_ac97_dev_add_pdata(codec->ac97,
+				card->dai_link[i].cpu_dai->ac97_pdata);
+		}
 	}
 	snprintf(codec->card->shortname, sizeof(codec->card->shortname),
 		 "%s",  card->name);
 	snprintf(codec->card->longname, sizeof(codec->card->longname),
 		 "%s (%s)", card->name, codec->name);
 
+	/* Make sure all DAPM widgets are instantiated */
+	snd_soc_dapm_new_widgets(codec);
+
 	ret = snd_card_register(codec->card);
 	if (ret < 0) {
 		printk(KERN_ERR "asoc: failed to register soundcard for %s\n",
@@ -1580,7 +1673,7 @@
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned short val, bitmask;
+	unsigned int val, bitmask;
 
 	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
 		;
@@ -1609,8 +1702,8 @@
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned short val;
-	unsigned short mask, bitmask;
+	unsigned int val;
+	unsigned int mask, bitmask;
 
 	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
 		;
@@ -1646,7 +1739,7 @@
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned short reg_val, val, mux;
+	unsigned int reg_val, val, mux;
 
 	reg_val = snd_soc_read(codec, e->reg);
 	val = (reg_val >> e->shift_l) & e->mask;
@@ -1685,8 +1778,8 @@
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned short val;
-	unsigned short mask;
+	unsigned int val;
+	unsigned int mask;
 
 	if (ucontrol->value.enumerated.item[0] > e->max - 1)
 		return -EINVAL;
@@ -1744,7 +1837,7 @@
 {
 	int max = kcontrol->private_value;
 
-	if (max == 1)
+	if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
 		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
 	else
 		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1774,7 +1867,7 @@
 	unsigned int shift = mc->shift;
 	unsigned int rshift = mc->rshift;
 
-	if (max == 1)
+	if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
 		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
 	else
 		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1846,7 +1939,7 @@
 	int max = mc->max;
 	unsigned int mask = (1 << fls(max)) - 1;
 	unsigned int invert = mc->invert;
-	unsigned short val, val2, val_mask;
+	unsigned int val, val2, val_mask;
 
 	val = (ucontrol->value.integer.value[0] & mask);
 	if (invert)
@@ -1881,7 +1974,7 @@
 		(struct soc_mixer_control *)kcontrol->private_value;
 	int max = mc->max;
 
-	if (max == 1)
+	if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
 		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
 	else
 		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
@@ -1912,7 +2005,7 @@
 	unsigned int reg2 = mc->rreg;
 	unsigned int shift = mc->shift;
 	int max = mc->max;
-	unsigned int mask = (1<<fls(max))-1;
+	unsigned int mask = (1 << fls(max)) - 1;
 	unsigned int invert = mc->invert;
 
 	ucontrol->value.integer.value[0] =
@@ -1952,7 +2045,7 @@
 	unsigned int mask = (1 << fls(max)) - 1;
 	unsigned int invert = mc->invert;
 	int err;
-	unsigned short val, val2, val_mask;
+	unsigned int val, val2, val_mask;
 
 	val_mask = mask << shift;
 	val = (ucontrol->value.integer.value[0] & mask);
@@ -2044,7 +2137,7 @@
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
 	unsigned int reg = mc->reg;
 	int min = mc->min;
-	unsigned short val;
+	unsigned int val;
 
 	val = (ucontrol->value.integer.value[0]+min) & 0xff;
 	val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
@@ -2065,7 +2158,7 @@
 int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
 	unsigned int freq, int dir)
 {
-	if (dai->ops->set_sysclk)
+	if (dai->ops && dai->ops->set_sysclk)
 		return dai->ops->set_sysclk(dai, clk_id, freq, dir);
 	else
 		return -EINVAL;
@@ -2085,7 +2178,7 @@
 int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
 	int div_id, int div)
 {
-	if (dai->ops->set_clkdiv)
+	if (dai->ops && dai->ops->set_clkdiv)
 		return dai->ops->set_clkdiv(dai, div_id, div);
 	else
 		return -EINVAL;
@@ -2104,7 +2197,7 @@
 int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
 	int pll_id, unsigned int freq_in, unsigned int freq_out)
 {
-	if (dai->ops->set_pll)
+	if (dai->ops && dai->ops->set_pll)
 		return dai->ops->set_pll(dai, pll_id, freq_in, freq_out);
 	else
 		return -EINVAL;
@@ -2120,7 +2213,7 @@
  */
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 {
-	if (dai->ops->set_fmt)
+	if (dai->ops && dai->ops->set_fmt)
 		return dai->ops->set_fmt(dai, fmt);
 	else
 		return -EINVAL;
@@ -2130,17 +2223,20 @@
 /**
  * snd_soc_dai_set_tdm_slot - configure DAI TDM.
  * @dai: DAI
- * @mask: DAI specific mask representing used slots.
+ * @tx_mask: bitmask representing active TX slots.
+ * @rx_mask: bitmask representing active RX slots.
  * @slots: Number of slots in use.
+ * @slot_width: Width in bits for each slot.
  *
  * Configures a DAI for TDM operation. Both mask and slots are codec and DAI
  * specific.
  */
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
-	unsigned int mask, int slots)
+	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
 {
-	if (dai->ops->set_sysclk)
-		return dai->ops->set_tdm_slot(dai, mask, slots);
+	if (dai->ops && dai->ops->set_tdm_slot)
+		return dai->ops->set_tdm_slot(dai, tx_mask, rx_mask,
+				slots, slot_width);
 	else
 		return -EINVAL;
 }
@@ -2155,7 +2251,7 @@
  */
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate)
 {
-	if (dai->ops->set_sysclk)
+	if (dai->ops && dai->ops->set_tristate)
 		return dai->ops->set_tristate(dai, tristate);
 	else
 		return -EINVAL;
@@ -2171,7 +2267,7 @@
  */
 int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
 {
-	if (dai->ops->digital_mute)
+	if (dai->ops && dai->ops->digital_mute)
 		return dai->ops->digital_mute(dai, mute);
 	else
 		return -EINVAL;
@@ -2352,6 +2448,39 @@
 }
 EXPORT_SYMBOL_GPL(snd_soc_unregister_platform);
 
+static u64 codec_format_map[] = {
+	SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE,
+	SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE,
+	SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE,
+	SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE,
+	SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE,
+	SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE,
+	SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3BE,
+	SNDRV_PCM_FMTBIT_U24_3LE | SNDRV_PCM_FMTBIT_U24_3BE,
+	SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE,
+	SNDRV_PCM_FMTBIT_U20_3LE | SNDRV_PCM_FMTBIT_U20_3BE,
+	SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE,
+	SNDRV_PCM_FMTBIT_U18_3LE | SNDRV_PCM_FMTBIT_U18_3BE,
+	SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE,
+	SNDRV_PCM_FMTBIT_FLOAT64_LE | SNDRV_PCM_FMTBIT_FLOAT64_BE,
+	SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE
+	| SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE,
+};
+
+/* Fix up the DAI formats for endianness: codecs don't actually see
+ * the endianness of the data but we're using the CPU format
+ * definitions which do need to include endianness so we ensure that
+ * codec DAIs always have both big and little endian variants set.
+ */
+static void fixup_codec_formats(struct snd_soc_pcm_stream *stream)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(codec_format_map); i++)
+		if (stream->formats & codec_format_map[i])
+			stream->formats |= codec_format_map[i];
+}
+
 /**
  * snd_soc_register_codec - Register a codec with the ASoC core
  *
@@ -2359,6 +2488,8 @@
  */
 int snd_soc_register_codec(struct snd_soc_codec *codec)
 {
+	int i;
+
 	if (!codec->name)
 		return -EINVAL;
 
@@ -2368,6 +2499,11 @@
 
 	INIT_LIST_HEAD(&codec->list);
 
+	for (i = 0; i < codec->num_dai; i++) {
+		fixup_codec_formats(&codec->dai[i].playback);
+		fixup_codec_formats(&codec->dai[i].capture);
+	}
+
 	mutex_lock(&client_mutex);
 	list_add(&codec->list, &codec_list);
 	snd_soc_instantiate_cards();
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 735903a..c68c204 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -12,7 +12,7 @@
  *  Features:
  *    o Changes power status of internal codec blocks depending on the
  *      dynamic configuration of codec internal audio paths and active
- *      DAC's/ADC's.
+ *      DACs/ADCs.
  *    o Platform power domain - can support external components i.e. amps and
  *      mic/meadphone insertion events.
  *    o Automatic Mic Bias support
@@ -52,23 +52,39 @@
 
 /* dapm power sequences - make this per codec in the future */
 static int dapm_up_seq[] = {
-	snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
-	snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_dac,
-	snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_pga,
-	snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
+	[snd_soc_dapm_pre] = 0,
+	[snd_soc_dapm_supply] = 1,
+	[snd_soc_dapm_micbias] = 2,
+	[snd_soc_dapm_mic] = 3,
+	[snd_soc_dapm_mux] = 4,
+	[snd_soc_dapm_value_mux] = 4,
+	[snd_soc_dapm_dac] = 5,
+	[snd_soc_dapm_mixer] = 6,
+	[snd_soc_dapm_mixer_named_ctl] = 6,
+	[snd_soc_dapm_pga] = 7,
+	[snd_soc_dapm_adc] = 8,
+	[snd_soc_dapm_hp] = 9,
+	[snd_soc_dapm_spk] = 10,
+	[snd_soc_dapm_post] = 11,
 };
 
 static int dapm_down_seq[] = {
-	snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
-	snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer,
-	snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias,
-	snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_post
+	[snd_soc_dapm_pre] = 0,
+	[snd_soc_dapm_adc] = 1,
+	[snd_soc_dapm_hp] = 2,
+	[snd_soc_dapm_spk] = 3,
+	[snd_soc_dapm_pga] = 4,
+	[snd_soc_dapm_mixer_named_ctl] = 5,
+	[snd_soc_dapm_mixer] = 5,
+	[snd_soc_dapm_dac] = 6,
+	[snd_soc_dapm_mic] = 7,
+	[snd_soc_dapm_micbias] = 8,
+	[snd_soc_dapm_mux] = 9,
+	[snd_soc_dapm_value_mux] = 9,
+	[snd_soc_dapm_supply] = 10,
+	[snd_soc_dapm_post] = 11,
 };
 
-static int dapm_status = 1;
-module_param(dapm_status, int, 0);
-MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
-
 static void pop_wait(u32 pop_time)
 {
 	if (pop_time)
@@ -96,6 +112,48 @@
 	return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
 }
 
+/**
+ * snd_soc_dapm_set_bias_level - set the bias level for the system
+ * @socdev: audio device
+ * @level: level to configure
+ *
+ * Configure the bias (power) levels for the SoC audio device.
+ *
+ * Returns 0 for success else error.
+ */
+static int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
+				       enum snd_soc_bias_level level)
+{
+	struct snd_soc_card *card = socdev->card;
+	struct snd_soc_codec *codec = socdev->card->codec;
+	int ret = 0;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		dev_dbg(socdev->dev, "Setting full bias\n");
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		dev_dbg(socdev->dev, "Setting bias prepare\n");
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		dev_dbg(socdev->dev, "Setting standby bias\n");
+		break;
+	case SND_SOC_BIAS_OFF:
+		dev_dbg(socdev->dev, "Setting bias off\n");
+		break;
+	default:
+		dev_err(socdev->dev, "Setting invalid bias %d\n", level);
+		return -EINVAL;
+	}
+
+	if (card->set_bias_level)
+		ret = card->set_bias_level(card, level);
+	if (ret == 0 && codec->set_bias_level)
+		ret = codec->set_bias_level(codec, level);
+
+	return ret;
+}
+
 /* set up initial codec paths */
 static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
 	struct snd_soc_dapm_path *p, int i)
@@ -165,6 +223,7 @@
 	case snd_soc_dapm_dac:
 	case snd_soc_dapm_micbias:
 	case snd_soc_dapm_vmid:
+	case snd_soc_dapm_supply:
 		p->connect = 1;
 	break;
 	/* does effect routing - dynamically connected */
@@ -179,7 +238,7 @@
 	}
 }
 
-/* connect mux widget to it's interconnecting audio paths */
+/* connect mux widget to its interconnecting audio paths */
 static int dapm_connect_mux(struct snd_soc_codec *codec,
 	struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
 	struct snd_soc_dapm_path *path, const char *control_name,
@@ -202,7 +261,7 @@
 	return -ENODEV;
 }
 
-/* connect mixer widget to it's interconnecting audio paths */
+/* connect mixer widget to its interconnecting audio paths */
 static int dapm_connect_mixer(struct snd_soc_codec *codec,
 	struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
 	struct snd_soc_dapm_path *path, const char *control_name)
@@ -227,7 +286,7 @@
 static int dapm_update_bits(struct snd_soc_dapm_widget *widget)
 {
 	int change, power;
-	unsigned short old, new;
+	unsigned int old, new;
 	struct snd_soc_codec *codec = widget->codec;
 
 	/* check for valid widgets */
@@ -357,8 +416,9 @@
 				path->long_name);
 			ret = snd_ctl_add(codec->card, path->kcontrol);
 			if (ret < 0) {
-				printk(KERN_ERR "asoc: failed to add dapm kcontrol %s\n",
-						path->long_name);
+				printk(KERN_ERR "asoc: failed to add dapm kcontrol %s: %d\n",
+				       path->long_name,
+				       ret);
 				kfree(path->long_name);
 				path->long_name = NULL;
 				return ret;
@@ -434,6 +494,9 @@
 	struct snd_soc_dapm_path *path;
 	int con = 0;
 
+	if (widget->id == snd_soc_dapm_supply)
+		return 0;
+
 	if (widget->id == snd_soc_dapm_adc && widget->active)
 		return 1;
 
@@ -470,6 +533,9 @@
 	struct snd_soc_dapm_path *path;
 	int con = 0;
 
+	if (widget->id == snd_soc_dapm_supply)
+		return 0;
+
 	/* active stream ? */
 	if (widget->id == snd_soc_dapm_dac && widget->active)
 		return 1;
@@ -521,84 +587,12 @@
 }
 EXPORT_SYMBOL_GPL(dapm_reg_event);
 
-/*
- * Scan a single DAPM widget for a complete audio path and update the
- * power status appropriately.
+/* Standard power change method, used to apply power changes to most
+ * widgets.
  */
-static int dapm_power_widget(struct snd_soc_codec *codec, int event,
-			     struct snd_soc_dapm_widget *w)
+static int dapm_generic_apply_power(struct snd_soc_dapm_widget *w)
 {
-	int in, out, power_change, power, ret;
-
-	/* vmid - no action */
-	if (w->id == snd_soc_dapm_vmid)
-		return 0;
-
-	/* active ADC */
-	if (w->id == snd_soc_dapm_adc && w->active) {
-		in = is_connected_input_ep(w);
-		dapm_clear_walk(w->codec);
-		w->power = (in != 0) ? 1 : 0;
-		dapm_update_bits(w);
-		return 0;
-	}
-
-	/* active DAC */
-	if (w->id == snd_soc_dapm_dac && w->active) {
-		out = is_connected_output_ep(w);
-		dapm_clear_walk(w->codec);
-		w->power = (out != 0) ? 1 : 0;
-		dapm_update_bits(w);
-		return 0;
-	}
-
-	/* pre and post event widgets */
-	if (w->id == snd_soc_dapm_pre) {
-		if (!w->event)
-			return 0;
-
-		if (event == SND_SOC_DAPM_STREAM_START) {
-			ret = w->event(w,
-				       NULL, SND_SOC_DAPM_PRE_PMU);
-			if (ret < 0)
-				return ret;
-		} else if (event == SND_SOC_DAPM_STREAM_STOP) {
-			ret = w->event(w,
-				       NULL, SND_SOC_DAPM_PRE_PMD);
-			if (ret < 0)
-				return ret;
-		}
-		return 0;
-	}
-	if (w->id == snd_soc_dapm_post) {
-		if (!w->event)
-			return 0;
-
-		if (event == SND_SOC_DAPM_STREAM_START) {
-			ret = w->event(w,
-				       NULL, SND_SOC_DAPM_POST_PMU);
-			if (ret < 0)
-				return ret;
-		} else if (event == SND_SOC_DAPM_STREAM_STOP) {
-			ret = w->event(w,
-				       NULL, SND_SOC_DAPM_POST_PMD);
-			if (ret < 0)
-				return ret;
-		}
-		return 0;
-	}
-
-	/* all other widgets */
-	in = is_connected_input_ep(w);
-	dapm_clear_walk(w->codec);
-	out = is_connected_output_ep(w);
-	dapm_clear_walk(w->codec);
-	power = (out != 0 && in != 0) ? 1 : 0;
-	power_change = (w->power == power) ? 0 : 1;
-	w->power = power;
-
-	if (!power_change)
-		return 0;
+	int ret;
 
 	/* call any power change event handlers */
 	if (w->event)
@@ -607,7 +601,7 @@
 			 w->name, w->event_flags);
 
 	/* power up pre event */
-	if (power && w->event &&
+	if (w->power && w->event &&
 	    (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
 		ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
 		if (ret < 0)
@@ -615,7 +609,7 @@
 	}
 
 	/* power down pre event */
-	if (!power && w->event &&
+	if (!w->power && w->event &&
 	    (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
 		ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
 		if (ret < 0)
@@ -623,17 +617,17 @@
 	}
 
 	/* Lower PGA volume to reduce pops */
-	if (w->id == snd_soc_dapm_pga && !power)
-		dapm_set_pga(w, power);
+	if (w->id == snd_soc_dapm_pga && !w->power)
+		dapm_set_pga(w, w->power);
 
 	dapm_update_bits(w);
 
 	/* Raise PGA volume to reduce pops */
-	if (w->id == snd_soc_dapm_pga && power)
-		dapm_set_pga(w, power);
+	if (w->id == snd_soc_dapm_pga && w->power)
+		dapm_set_pga(w, w->power);
 
 	/* power up post event */
-	if (power && w->event &&
+	if (w->power && w->event &&
 	    (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
 		ret = w->event(w,
 			       NULL, SND_SOC_DAPM_POST_PMU);
@@ -642,7 +636,7 @@
 	}
 
 	/* power down post event */
-	if (!power && w->event &&
+	if (!w->power && w->event &&
 	    (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
 		ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
 		if (ret < 0)
@@ -652,6 +646,274 @@
 	return 0;
 }
 
+/* Generic check to see if a widget should be powered.
+ */
+static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
+{
+	int in, out;
+
+	in = is_connected_input_ep(w);
+	dapm_clear_walk(w->codec);
+	out = is_connected_output_ep(w);
+	dapm_clear_walk(w->codec);
+	return out != 0 && in != 0;
+}
+
+/* Check to see if an ADC has power */
+static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
+{
+	int in;
+
+	if (w->active) {
+		in = is_connected_input_ep(w);
+		dapm_clear_walk(w->codec);
+		return in != 0;
+	} else {
+		return dapm_generic_check_power(w);
+	}
+}
+
+/* Check to see if a DAC has power */
+static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
+{
+	int out;
+
+	if (w->active) {
+		out = is_connected_output_ep(w);
+		dapm_clear_walk(w->codec);
+		return out != 0;
+	} else {
+		return dapm_generic_check_power(w);
+	}
+}
+
+/* Check to see if a power supply is needed */
+static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
+{
+	struct snd_soc_dapm_path *path;
+	int power = 0;
+
+	/* Check if one of our outputs is connected */
+	list_for_each_entry(path, &w->sinks, list_source) {
+		if (path->sink && path->sink->power_check &&
+		    path->sink->power_check(path->sink)) {
+			power = 1;
+			break;
+		}
+	}
+
+	dapm_clear_walk(w->codec);
+
+	return power;
+}
+
+static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
+			    struct snd_soc_dapm_widget *b,
+			    int sort[])
+{
+	if (sort[a->id] != sort[b->id])
+		return sort[a->id] - sort[b->id];
+	if (a->reg != b->reg)
+		return a->reg - b->reg;
+
+	return 0;
+}
+
+/* Insert a widget in order into a DAPM power sequence. */
+static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
+			    struct list_head *list,
+			    int sort[])
+{
+	struct snd_soc_dapm_widget *w;
+
+	list_for_each_entry(w, list, power_list)
+		if (dapm_seq_compare(new_widget, w, sort) < 0) {
+			list_add_tail(&new_widget->power_list, &w->power_list);
+			return;
+		}
+
+	list_add_tail(&new_widget->power_list, list);
+}
+
+/* Apply the coalesced changes from a DAPM sequence */
+static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
+				   struct list_head *pending)
+{
+	struct snd_soc_dapm_widget *w;
+	int reg, power, ret;
+	unsigned int value = 0;
+	unsigned int mask = 0;
+	unsigned int cur_mask;
+
+	reg = list_first_entry(pending, struct snd_soc_dapm_widget,
+			       power_list)->reg;
+
+	list_for_each_entry(w, pending, power_list) {
+		cur_mask = 1 << w->shift;
+		BUG_ON(reg != w->reg);
+
+		if (w->invert)
+			power = !w->power;
+		else
+			power = w->power;
+
+		mask |= cur_mask;
+		if (power)
+			value |= cur_mask;
+
+		pop_dbg(codec->pop_time,
+			"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
+			w->name, reg, value, mask);
+
+		/* power up pre event */
+		if (w->power && w->event &&
+		    (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
+			pop_dbg(codec->pop_time, "pop test : %s PRE_PMU\n",
+				w->name);
+			ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
+			if (ret < 0)
+				pr_err("%s: pre event failed: %d\n",
+				       w->name, ret);
+		}
+
+		/* power down pre event */
+		if (!w->power && w->event &&
+		    (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
+			pop_dbg(codec->pop_time, "pop test : %s PRE_PMD\n",
+				w->name);
+			ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
+			if (ret < 0)
+				pr_err("%s: pre event failed: %d\n",
+				       w->name, ret);
+		}
+
+		/* Lower PGA volume to reduce pops */
+		if (w->id == snd_soc_dapm_pga && !w->power)
+			dapm_set_pga(w, w->power);
+	}
+
+	if (reg >= 0) {
+		pop_dbg(codec->pop_time,
+			"pop test : Applying 0x%x/0x%x to %x in %dms\n",
+			value, mask, reg, codec->pop_time);
+		pop_wait(codec->pop_time);
+		snd_soc_update_bits(codec, reg, mask, value);
+	}
+
+	list_for_each_entry(w, pending, power_list) {
+		/* Raise PGA volume to reduce pops */
+		if (w->id == snd_soc_dapm_pga && w->power)
+			dapm_set_pga(w, w->power);
+
+		/* power up post event */
+		if (w->power && w->event &&
+		    (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
+			pop_dbg(codec->pop_time, "pop test : %s POST_PMU\n",
+				w->name);
+			ret = w->event(w,
+				       NULL, SND_SOC_DAPM_POST_PMU);
+			if (ret < 0)
+				pr_err("%s: post event failed: %d\n",
+				       w->name, ret);
+		}
+
+		/* power down post event */
+		if (!w->power && w->event &&
+		    (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
+			pop_dbg(codec->pop_time, "pop test : %s POST_PMD\n",
+				w->name);
+			ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
+			if (ret < 0)
+				pr_err("%s: post event failed: %d\n",
+				       w->name, ret);
+		}
+	}
+}
+
+/* Apply a DAPM power sequence.
+ *
+ * We walk over a pre-sorted list of widgets to apply power to.  In
+ * order to minimise the number of writes to the device required
+ * multiple widgets will be updated in a single write where possible.
+ * Currently anything that requires more than a single write is not
+ * handled.
+ */
+static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
+			 int event, int sort[])
+{
+	struct snd_soc_dapm_widget *w, *n;
+	LIST_HEAD(pending);
+	int cur_sort = -1;
+	int cur_reg = SND_SOC_NOPM;
+	int ret;
+
+	list_for_each_entry_safe(w, n, list, power_list) {
+		ret = 0;
+
+		/* Do we need to apply any queued changes? */
+		if (sort[w->id] != cur_sort || w->reg != cur_reg) {
+			if (!list_empty(&pending))
+				dapm_seq_run_coalesced(codec, &pending);
+
+			INIT_LIST_HEAD(&pending);
+			cur_sort = -1;
+			cur_reg = SND_SOC_NOPM;
+		}
+
+		switch (w->id) {
+		case snd_soc_dapm_pre:
+			if (!w->event)
+				list_for_each_entry_safe_continue(w, n, list,
+								  power_list);
+
+			if (event == SND_SOC_DAPM_STREAM_START)
+				ret = w->event(w,
+					       NULL, SND_SOC_DAPM_PRE_PMU);
+			else if (event == SND_SOC_DAPM_STREAM_STOP)
+				ret = w->event(w,
+					       NULL, SND_SOC_DAPM_PRE_PMD);
+			break;
+
+		case snd_soc_dapm_post:
+			if (!w->event)
+				list_for_each_entry_safe_continue(w, n, list,
+								  power_list);
+
+			if (event == SND_SOC_DAPM_STREAM_START)
+				ret = w->event(w,
+					       NULL, SND_SOC_DAPM_POST_PMU);
+			else if (event == SND_SOC_DAPM_STREAM_STOP)
+				ret = w->event(w,
+					       NULL, SND_SOC_DAPM_POST_PMD);
+			break;
+
+		case snd_soc_dapm_input:
+		case snd_soc_dapm_output:
+		case snd_soc_dapm_hp:
+		case snd_soc_dapm_mic:
+		case snd_soc_dapm_line:
+		case snd_soc_dapm_spk:
+			/* No register support currently */
+			ret = dapm_generic_apply_power(w);
+			break;
+
+		default:
+			/* Queue it up for application */
+			cur_sort = sort[w->id];
+			cur_reg = w->reg;
+			list_move(&w->power_list, &pending);
+			break;
+		}
+
+		if (ret < 0)
+			pr_err("Failed to apply widget power: %d\n",
+			       ret);
+	}
+
+	if (!list_empty(&pending))
+		dapm_seq_run_coalesced(codec, &pending);
+}
+
 /*
  * Scan each dapm widget for complete audio path.
  * A complete path is a route that has valid endpoints i.e.:-
@@ -663,31 +925,81 @@
  */
 static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
 {
+	struct snd_soc_device *socdev = codec->socdev;
 	struct snd_soc_dapm_widget *w;
-	int i, c = 1, *seq = NULL, ret = 0;
+	LIST_HEAD(up_list);
+	LIST_HEAD(down_list);
+	int ret = 0;
+	int power;
+	int sys_power = 0;
 
-	/* do we have a sequenced stream event */
-	if (event == SND_SOC_DAPM_STREAM_START) {
-		c = ARRAY_SIZE(dapm_up_seq);
-		seq = dapm_up_seq;
-	} else if (event == SND_SOC_DAPM_STREAM_STOP) {
-		c = ARRAY_SIZE(dapm_down_seq);
-		seq = dapm_down_seq;
-	}
+	/* Check which widgets we need to power and store them in
+	 * lists indicating if they should be powered up or down.
+	 */
+	list_for_each_entry(w, &codec->dapm_widgets, list) {
+		switch (w->id) {
+		case snd_soc_dapm_pre:
+			dapm_seq_insert(w, &down_list, dapm_down_seq);
+			break;
+		case snd_soc_dapm_post:
+			dapm_seq_insert(w, &up_list, dapm_up_seq);
+			break;
 
-	for (i = 0; i < c; i++) {
-		list_for_each_entry(w, &codec->dapm_widgets, list) {
-
-			/* is widget in stream order */
-			if (seq && seq[i] && w->id != seq[i])
+		default:
+			if (!w->power_check)
 				continue;
 
-			ret = dapm_power_widget(codec, event, w);
-			if (ret != 0)
-				return ret;
+			power = w->power_check(w);
+			if (power)
+				sys_power = 1;
+
+			if (w->power == power)
+				continue;
+
+			if (power)
+				dapm_seq_insert(w, &up_list, dapm_up_seq);
+			else
+				dapm_seq_insert(w, &down_list, dapm_down_seq);
+
+			w->power = power;
+			break;
 		}
 	}
 
+	/* If we're changing to all on or all off then prepare */
+	if ((sys_power && codec->bias_level == SND_SOC_BIAS_STANDBY) ||
+	    (!sys_power && codec->bias_level == SND_SOC_BIAS_ON)) {
+		ret = snd_soc_dapm_set_bias_level(socdev,
+						  SND_SOC_BIAS_PREPARE);
+		if (ret != 0)
+			pr_err("Failed to prepare bias: %d\n", ret);
+	}
+
+	/* Power down widgets first; try to avoid amplifying pops. */
+	dapm_seq_run(codec, &down_list, event, dapm_down_seq);
+
+	/* Now power up. */
+	dapm_seq_run(codec, &up_list, event, dapm_up_seq);
+
+	/* If we just powered the last thing off drop to standby bias */
+	if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {
+		ret = snd_soc_dapm_set_bias_level(socdev,
+						  SND_SOC_BIAS_STANDBY);
+		if (ret != 0)
+			pr_err("Failed to apply standby bias: %d\n", ret);
+	}
+
+	/* If we just powered up then move to active bias */
+	if (codec->bias_level == SND_SOC_BIAS_PREPARE && sys_power) {
+		ret = snd_soc_dapm_set_bias_level(socdev,
+						  SND_SOC_BIAS_ON);
+		if (ret != 0)
+			pr_err("Failed to apply active bias: %d\n", ret);
+	}
+
+	pop_dbg(codec->pop_time, "DAPM sequencing finished, waiting %dms\n",
+		codec->pop_time);
+
 	return 0;
 }
 
@@ -723,6 +1035,7 @@
 		case snd_soc_dapm_pga:
 		case snd_soc_dapm_mixer:
 		case snd_soc_dapm_mixer_named_ctl:
+		case snd_soc_dapm_supply:
 			if (w->name) {
 				in = is_connected_input_ep(w);
 				dapm_clear_walk(w->codec);
@@ -851,6 +1164,7 @@
 		case snd_soc_dapm_pga:
 		case snd_soc_dapm_mixer:
 		case snd_soc_dapm_mixer_named_ctl:
+		case snd_soc_dapm_supply:
 			if (w->name)
 				count += sprintf(buf + count, "%s: %s\n",
 					w->name, w->power ? "On":"Off");
@@ -883,16 +1197,12 @@
 
 int snd_soc_dapm_sys_add(struct device *dev)
 {
-	if (!dapm_status)
-		return 0;
 	return device_create_file(dev, &dev_attr_dapm_widget);
 }
 
 static void snd_soc_dapm_sys_remove(struct device *dev)
 {
-	if (dapm_status) {
-		device_remove_file(dev, &dev_attr_dapm_widget);
-	}
+	device_remove_file(dev, &dev_attr_dapm_widget);
 }
 
 /* free all dapm widgets and resources */
@@ -983,8 +1293,8 @@
 	if (wsink->id == snd_soc_dapm_input) {
 		if (wsource->id == snd_soc_dapm_micbias ||
 			wsource->id == snd_soc_dapm_mic ||
-			wsink->id == snd_soc_dapm_line ||
-			wsink->id == snd_soc_dapm_output)
+			wsource->id == snd_soc_dapm_line ||
+			wsource->id == snd_soc_dapm_output)
 			wsink->ext = 1;
 	}
 	if (wsource->id == snd_soc_dapm_output) {
@@ -1015,6 +1325,7 @@
 	case snd_soc_dapm_vmid:
 	case snd_soc_dapm_pre:
 	case snd_soc_dapm_post:
+	case snd_soc_dapm_supply:
 		list_add(&path->list, &codec->dapm_paths);
 		list_add(&path->list_sink, &wsink->sources);
 		list_add(&path->list_source, &wsource->sinks);
@@ -1108,15 +1419,22 @@
 		case snd_soc_dapm_switch:
 		case snd_soc_dapm_mixer:
 		case snd_soc_dapm_mixer_named_ctl:
+			w->power_check = dapm_generic_check_power;
 			dapm_new_mixer(codec, w);
 			break;
 		case snd_soc_dapm_mux:
 		case snd_soc_dapm_value_mux:
+			w->power_check = dapm_generic_check_power;
 			dapm_new_mux(codec, w);
 			break;
 		case snd_soc_dapm_adc:
+			w->power_check = dapm_adc_check_power;
+			break;
 		case snd_soc_dapm_dac:
+			w->power_check = dapm_dac_check_power;
+			break;
 		case snd_soc_dapm_pga:
+			w->power_check = dapm_generic_check_power;
 			dapm_new_pga(codec, w);
 			break;
 		case snd_soc_dapm_input:
@@ -1126,6 +1444,10 @@
 		case snd_soc_dapm_hp:
 		case snd_soc_dapm_mic:
 		case snd_soc_dapm_line:
+			w->power_check = dapm_generic_check_power;
+			break;
+		case snd_soc_dapm_supply:
+			w->power_check = dapm_supply_check_power;
 		case snd_soc_dapm_vmid:
 		case snd_soc_dapm_pre:
 		case snd_soc_dapm_post:
@@ -1205,7 +1527,7 @@
 	int max = mc->max;
 	unsigned int mask = (1 << fls(max)) - 1;
 	unsigned int invert = mc->invert;
-	unsigned short val, val2, val_mask;
+	unsigned int val, val2, val_mask;
 	int ret;
 
 	val = (ucontrol->value.integer.value[0] & mask);
@@ -1269,7 +1591,7 @@
 {
 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned short val, bitmask;
+	unsigned int val, bitmask;
 
 	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
 		;
@@ -1297,8 +1619,8 @@
 {
 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned short val, mux;
-	unsigned short mask, bitmask;
+	unsigned int val, mux;
+	unsigned int mask, bitmask;
 	int ret = 0;
 
 	for (bitmask = 1; bitmask < e->max; bitmask <<= 1)
@@ -1356,7 +1678,7 @@
 {
 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned short reg_val, val, mux;
+	unsigned int reg_val, val, mux;
 
 	reg_val = snd_soc_read(widget->codec, e->reg);
 	val = (reg_val >> e->shift_l) & e->mask;
@@ -1396,8 +1718,8 @@
 {
 	struct snd_soc_dapm_widget *widget = snd_kcontrol_chip(kcontrol);
 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-	unsigned short val, mux;
-	unsigned short mask;
+	unsigned int val, mux;
+	unsigned int mask;
 	int ret = 0;
 
 	if (ucontrol->value.enumerated.item[0] > e->max - 1)
@@ -1626,35 +1948,11 @@
 EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event);
 
 /**
- * snd_soc_dapm_set_bias_level - set the bias level for the system
- * @socdev: audio device
- * @level: level to configure
- *
- * Configure the bias (power) levels for the SoC audio device.
- *
- * Returns 0 for success else error.
- */
-int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
-				enum snd_soc_bias_level level)
-{
-	struct snd_soc_card *card = socdev->card;
-	struct snd_soc_codec *codec = socdev->card->codec;
-	int ret = 0;
-
-	if (card->set_bias_level)
-		ret = card->set_bias_level(card, level);
-	if (ret == 0 && codec->set_bias_level)
-		ret = codec->set_bias_level(codec, level);
-
-	return ret;
-}
-
-/**
  * snd_soc_dapm_enable_pin - enable pin.
  * @codec: SoC codec
  * @pin: pin name
  *
- * Enables input/output pin and it's parents or children widgets iff there is
+ * Enables input/output pin and its parents or children widgets iff there is
  * a valid audio route and active audio stream.
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
@@ -1670,7 +1968,7 @@
  * @codec: SoC codec
  * @pin: pin name
  *
- * Disables input/output pin and it's parents or children widgets.
+ * Disables input/output pin and its parents or children widgets.
  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
  * do any widget power switching.
  */
@@ -1737,6 +2035,36 @@
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
 
+/*
+ * snd_soc_dapm_shutdown - callback for system shutdown
+ */
+void snd_soc_dapm_shutdown(struct snd_soc_device *socdev)
+{
+	struct snd_soc_codec *codec = socdev->card->codec;
+	struct snd_soc_dapm_widget *w;
+	LIST_HEAD(down_list);
+	int powerdown = 0;
+
+	list_for_each_entry(w, &codec->dapm_widgets, list) {
+		if (w->power) {
+			dapm_seq_insert(w, &down_list, dapm_down_seq);
+			w->power = 0;
+			powerdown = 1;
+		}
+	}
+
+	/* If there were no widgets to power down we're already in
+	 * standby.
+	 */
+	if (powerdown) {
+		snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_PREPARE);
+		dapm_seq_run(codec, &down_list, 0, dapm_down_seq);
+		snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_STANDBY);
+	}
+
+	snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_OFF);
+}
+
 /* Module information */
 MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk");
 MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC");
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 28346fb..1d455ab7 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -73,14 +73,15 @@
 	oldstatus = jack->status;
 
 	jack->status &= ~mask;
-	jack->status |= status;
+	jack->status |= status & mask;
 
-	/* The DAPM sync is expensive enough to be worth skipping */
-	if (jack->status == oldstatus)
+	/* The DAPM sync is expensive enough to be worth skipping.
+	 * However, empty mask means pin synchronization is desired. */
+	if (mask && (jack->status == oldstatus))
 		goto out;
 
 	list_for_each_entry(pin, &jack->pins, list) {
-		enable = pin->mask & status;
+		enable = pin->mask & jack->status;
 
 		if (pin->invert)
 			enable = !enable;
@@ -220,6 +221,9 @@
 		if (ret)
 			goto err;
 
+		INIT_WORK(&gpios[i].work, gpio_work);
+		gpios[i].jack = jack;
+
 		ret = request_irq(gpio_to_irq(gpios[i].gpio),
 				gpio_handler,
 				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
@@ -228,8 +232,13 @@
 		if (ret)
 			goto err;
 
-		INIT_WORK(&gpios[i].work, gpio_work);
-		gpios[i].jack = jack;
+#ifdef CONFIG_GPIO_SYSFS
+		/* Expose GPIO value over sysfs for diagnostic purposes */
+		gpio_export(gpios[i].gpio, false);
+#endif
+
+		/* Update initial jack status */
+		snd_soc_jack_gpio_detect(&gpios[i]);
 	}
 
 	return 0;
@@ -258,6 +267,9 @@
 	int i;
 
 	for (i = 0; i < count; i++) {
+#ifdef CONFIG_GPIO_SYSFS
+		gpio_unexport(gpios[i].gpio);
+#endif
 		free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]);
 		gpio_free(gpios[i].gpio);
 		gpios[i].jack = NULL;
diff --git a/sound/soc/txx9/Kconfig b/sound/soc/txx9/Kconfig
new file mode 100644
index 0000000..ebc9327
--- /dev/null
+++ b/sound/soc/txx9/Kconfig
@@ -0,0 +1,29 @@
+##
+## TXx9 ACLC
+##
+config SND_SOC_TXX9ACLC
+	tristate "SoC Audio for TXx9"
+	depends on HAS_TXX9_ACLC && TXX9_DMAC
+	help
+	  This option enables support for the AC Link Controllers in TXx9 SoC.
+
+config HAS_TXX9_ACLC
+	bool
+
+config SND_SOC_TXX9ACLC_AC97
+	tristate
+	select AC97_BUS
+	select SND_AC97_CODEC
+	select SND_SOC_AC97_BUS
+
+
+##
+## Boards
+##
+config SND_SOC_TXX9ACLC_GENERIC
+	tristate "Generic TXx9 ACLC sound machine"
+	depends on SND_SOC_TXX9ACLC
+	select SND_SOC_TXX9ACLC_AC97
+	select SND_SOC_AC97_CODEC
+	help
+	  This is a generic AC97 sound machine for use in TXx9 based systems.
diff --git a/sound/soc/txx9/Makefile b/sound/soc/txx9/Makefile
new file mode 100644
index 0000000..551f16c
--- /dev/null
+++ b/sound/soc/txx9/Makefile
@@ -0,0 +1,11 @@
+# Platform
+snd-soc-txx9aclc-objs := txx9aclc.o
+snd-soc-txx9aclc-ac97-objs := txx9aclc-ac97.o
+
+obj-$(CONFIG_SND_SOC_TXX9ACLC) += snd-soc-txx9aclc.o
+obj-$(CONFIG_SND_SOC_TXX9ACLC_AC97) += snd-soc-txx9aclc-ac97.o
+
+# Machine
+snd-soc-txx9aclc-generic-objs := txx9aclc-generic.o
+
+obj-$(CONFIG_SND_SOC_TXX9ACLC_GENERIC) += snd-soc-txx9aclc-generic.o
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
new file mode 100644
index 0000000..0f83bdb
--- /dev/null
+++ b/sound/soc/txx9/txx9aclc-ac97.c
@@ -0,0 +1,255 @@
+/*
+ * TXx9 ACLC AC97 driver
+ *
+ * Copyright (C) 2009 Atsushi Nemoto
+ *
+ * Based on RBTX49xx patch from CELF patch archive.
+ * (C) Copyright TOSHIBA CORPORATION 2004-2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include "txx9aclc.h"
+
+#define AC97_DIR	\
+	(SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE)
+
+#define AC97_RATES	\
+	SNDRV_PCM_RATE_8000_48000
+
+#ifdef __BIG_ENDIAN
+#define AC97_FMTS	SNDRV_PCM_FMTBIT_S16_BE
+#else
+#define AC97_FMTS	SNDRV_PCM_FMTBIT_S16_LE
+#endif
+
+static DECLARE_WAIT_QUEUE_HEAD(ac97_waitq);
+
+/* REVISIT: How to find txx9aclc_soc_device from snd_ac97? */
+static struct txx9aclc_soc_device *txx9aclc_soc_dev;
+
+static int txx9aclc_regready(struct txx9aclc_soc_device *dev)
+{
+	struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+
+	return __raw_readl(drvdata->base + ACINTSTS) & ACINT_REGACCRDY;
+}
+
+/* AC97 controller reads codec register */
+static unsigned short txx9aclc_ac97_read(struct snd_ac97 *ac97,
+					 unsigned short reg)
+{
+	struct txx9aclc_soc_device *dev = txx9aclc_soc_dev;
+	struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+	void __iomem *base = drvdata->base;
+	u32 dat;
+
+	if (!(__raw_readl(base + ACINTSTS) & ACINT_CODECRDY(ac97->num)))
+		return 0xffff;
+	reg |= ac97->num << 7;
+	dat = (reg << ACREGACC_REG_SHIFT) | ACREGACC_READ;
+	__raw_writel(dat, base + ACREGACC);
+	__raw_writel(ACINT_REGACCRDY, base + ACINTEN);
+	if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(dev), HZ)) {
+		__raw_writel(ACINT_REGACCRDY, base + ACINTDIS);
+		dev_err(dev->soc_dev.dev, "ac97 read timeout (reg %#x)\n", reg);
+		dat = 0xffff;
+		goto done;
+	}
+	dat = __raw_readl(base + ACREGACC);
+	if (((dat >> ACREGACC_REG_SHIFT) & 0xff) != reg) {
+		dev_err(dev->soc_dev.dev, "reg mismatch %x with %x\n",
+			dat, reg);
+		dat = 0xffff;
+		goto done;
+	}
+	dat = (dat >> ACREGACC_DAT_SHIFT) & 0xffff;
+done:
+	__raw_writel(ACINT_REGACCRDY, base + ACINTDIS);
+	return dat;
+}
+
+/* AC97 controller writes to codec register */
+static void txx9aclc_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+				unsigned short val)
+{
+	struct txx9aclc_soc_device *dev = txx9aclc_soc_dev;
+	struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+	void __iomem *base = drvdata->base;
+
+	__raw_writel(((reg | (ac97->num << 7)) << ACREGACC_REG_SHIFT) |
+		     (val << ACREGACC_DAT_SHIFT),
+		     base + ACREGACC);
+	__raw_writel(ACINT_REGACCRDY, base + ACINTEN);
+	if (!wait_event_timeout(ac97_waitq, txx9aclc_regready(dev), HZ)) {
+		dev_err(dev->soc_dev.dev,
+			"ac97 write timeout (reg %#x)\n", reg);
+	}
+	__raw_writel(ACINT_REGACCRDY, base + ACINTDIS);
+}
+
+static void txx9aclc_ac97_cold_reset(struct snd_ac97 *ac97)
+{
+	struct txx9aclc_soc_device *dev = txx9aclc_soc_dev;
+	struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+	void __iomem *base = drvdata->base;
+	u32 ready = ACINT_CODECRDY(ac97->num) | ACINT_REGACCRDY;
+
+	__raw_writel(ACCTL_ENLINK, base + ACCTLDIS);
+	mmiowb();
+	udelay(1);
+	__raw_writel(ACCTL_ENLINK, base + ACCTLEN);
+	/* wait for primary codec ready status */
+	__raw_writel(ready, base + ACINTEN);
+	if (!wait_event_timeout(ac97_waitq,
+				(__raw_readl(base + ACINTSTS) & ready) == ready,
+				HZ)) {
+		dev_err(&ac97->dev, "primary codec is not ready "
+			"(status %#x)\n",
+			__raw_readl(base + ACINTSTS));
+	}
+	__raw_writel(ACINT_REGACCRDY, base + ACINTSTS);
+	__raw_writel(ready, base + ACINTDIS);
+}
+
+/* AC97 controller operations */
+struct snd_ac97_bus_ops soc_ac97_ops = {
+	.read		= txx9aclc_ac97_read,
+	.write		= txx9aclc_ac97_write,
+	.reset		= txx9aclc_ac97_cold_reset,
+};
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+static irqreturn_t txx9aclc_ac97_irq(int irq, void *dev_id)
+{
+	struct txx9aclc_plat_drvdata *drvdata = dev_id;
+	void __iomem *base = drvdata->base;
+
+	__raw_writel(__raw_readl(base + ACINTMSTS), base + ACINTDIS);
+	wake_up(&ac97_waitq);
+	return IRQ_HANDLED;
+}
+
+static int txx9aclc_ac97_probe(struct platform_device *pdev,
+			       struct snd_soc_dai *dai)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct txx9aclc_soc_device *dev =
+		container_of(socdev, struct txx9aclc_soc_device, soc_dev);
+
+	dev->aclc_pdev = to_platform_device(dai->dev);
+	txx9aclc_soc_dev = dev;
+	return 0;
+}
+
+static void txx9aclc_ac97_remove(struct platform_device *pdev,
+				 struct snd_soc_dai *dai)
+{
+	struct platform_device *aclc_pdev = to_platform_device(dai->dev);
+	struct txx9aclc_plat_drvdata *drvdata = platform_get_drvdata(aclc_pdev);
+
+	/* disable AC-link */
+	__raw_writel(ACCTL_ENLINK, drvdata->base + ACCTLDIS);
+	txx9aclc_soc_dev = NULL;
+}
+
+struct snd_soc_dai txx9aclc_ac97_dai = {
+	.name			= "txx9aclc_ac97",
+	.ac97_control		= 1,
+	.probe			= txx9aclc_ac97_probe,
+	.remove			= txx9aclc_ac97_remove,
+	.playback = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+	.capture = {
+		.rates		= AC97_RATES,
+		.formats	= AC97_FMTS,
+		.channels_min	= 2,
+		.channels_max	= 2,
+	},
+};
+EXPORT_SYMBOL_GPL(txx9aclc_ac97_dai);
+
+static int __devinit txx9aclc_ac97_dev_probe(struct platform_device *pdev)
+{
+	struct txx9aclc_plat_drvdata *drvdata;
+	struct resource *r;
+	int err;
+	int irq;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!r)
+		return -EBUSY;
+
+	if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r),
+				     dev_name(&pdev->dev)))
+		return -EBUSY;
+
+	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
+	if (!drvdata)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, drvdata);
+	drvdata->physbase = r->start;
+	if (sizeof(drvdata->physbase) > sizeof(r->start) &&
+	    r->start >= TXX9_DIRECTMAP_BASE &&
+	    r->start < TXX9_DIRECTMAP_BASE + 0x400000)
+		drvdata->physbase |= 0xf00000000ull;
+	drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
+	if (!drvdata->base)
+		return -EBUSY;
+	err = devm_request_irq(&pdev->dev, irq, txx9aclc_ac97_irq,
+			       IRQF_DISABLED, dev_name(&pdev->dev), drvdata);
+	if (err < 0)
+		return err;
+
+	txx9aclc_ac97_dai.dev = &pdev->dev;
+	return snd_soc_register_dai(&txx9aclc_ac97_dai);
+}
+
+static int __devexit txx9aclc_ac97_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_dai(&txx9aclc_ac97_dai);
+	return 0;
+}
+
+static struct platform_driver txx9aclc_ac97_driver = {
+	.probe		= txx9aclc_ac97_dev_probe,
+	.remove		= __devexit_p(txx9aclc_ac97_dev_remove),
+	.driver		= {
+		.name	= "txx9aclc-ac97",
+		.owner	= THIS_MODULE,
+	},
+};
+
+static int __init txx9aclc_ac97_init(void)
+{
+	return platform_driver_register(&txx9aclc_ac97_driver);
+}
+
+static void __exit txx9aclc_ac97_exit(void)
+{
+	platform_driver_unregister(&txx9aclc_ac97_driver);
+}
+
+module_init(txx9aclc_ac97_init);
+module_exit(txx9aclc_ac97_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("TXx9 ACLC AC97 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/txx9/txx9aclc-generic.c b/sound/soc/txx9/txx9aclc-generic.c
new file mode 100644
index 0000000..3175de9
--- /dev/null
+++ b/sound/soc/txx9/txx9aclc-generic.c
@@ -0,0 +1,98 @@
+/*
+ * Generic TXx9 ACLC machine driver
+ *
+ * Copyright (C) 2009 Atsushi Nemoto
+ *
+ * Based on RBTX49xx patch from CELF patch archive.
+ * (C) Copyright TOSHIBA CORPORATION 2004-2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This is a very generic AC97 sound machine driver for boards which
+ * have (AC97) audio at ACLC (e.g. RBTX49XX boards).
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include "../codecs/ac97.h"
+#include "txx9aclc.h"
+
+static struct snd_soc_dai_link txx9aclc_generic_dai = {
+	.name = "AC97",
+	.stream_name = "AC97 HiFi",
+	.cpu_dai = &txx9aclc_ac97_dai,
+	.codec_dai = &ac97_dai,
+};
+
+static struct snd_soc_card txx9aclc_generic_card = {
+	.name		= "Generic TXx9 ACLC Audio",
+	.platform	= &txx9aclc_soc_platform,
+	.dai_link	= &txx9aclc_generic_dai,
+	.num_links	= 1,
+};
+
+static struct txx9aclc_soc_device txx9aclc_generic_soc_device = {
+	.soc_dev = {
+		.card		= &txx9aclc_generic_card,
+		.codec_dev	= &soc_codec_dev_ac97,
+	},
+};
+
+static int __init txx9aclc_generic_probe(struct platform_device *pdev)
+{
+	struct txx9aclc_soc_device *dev = &txx9aclc_generic_soc_device;
+	struct platform_device *soc_pdev;
+	int ret;
+
+	soc_pdev = platform_device_alloc("soc-audio", -1);
+	if (!soc_pdev)
+		return -ENOMEM;
+	platform_set_drvdata(soc_pdev, &dev->soc_dev);
+	dev->soc_dev.dev = &soc_pdev->dev;
+	ret = platform_device_add(soc_pdev);
+	if (ret) {
+		platform_device_put(soc_pdev);
+		return ret;
+	}
+	platform_set_drvdata(pdev, soc_pdev);
+	return 0;
+}
+
+static int __exit txx9aclc_generic_remove(struct platform_device *pdev)
+{
+	struct platform_device *soc_pdev = platform_get_drvdata(pdev);
+
+	platform_device_unregister(soc_pdev);
+	return 0;
+}
+
+static struct platform_driver txx9aclc_generic_driver = {
+	.remove = txx9aclc_generic_remove,
+	.driver = {
+		.name = "txx9aclc-generic",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init txx9aclc_generic_init(void)
+{
+	return platform_driver_probe(&txx9aclc_generic_driver,
+				     txx9aclc_generic_probe);
+}
+
+static void __exit txx9aclc_generic_exit(void)
+{
+	platform_driver_unregister(&txx9aclc_generic_driver);
+}
+
+module_init(txx9aclc_generic_init);
+module_exit(txx9aclc_generic_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Generic TXx9 ACLC ALSA SoC audio driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
new file mode 100644
index 0000000..efed64b
--- /dev/null
+++ b/sound/soc/txx9/txx9aclc.c
@@ -0,0 +1,432 @@
+/*
+ * Generic TXx9 ACLC platform driver
+ *
+ * Copyright (C) 2009 Atsushi Nemoto
+ *
+ * Based on RBTX49xx patch from CELF patch archive.
+ * (C) Copyright TOSHIBA CORPORATION 2004-2006
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "txx9aclc.h"
+
+static const struct snd_pcm_hardware txx9aclc_pcm_hardware = {
+	/*
+	 * REVISIT: SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID
+	 * needs more works for noncoherent MIPS.
+	 */
+	.info		  = SNDRV_PCM_INFO_INTERLEAVED |
+			    SNDRV_PCM_INFO_BATCH |
+			    SNDRV_PCM_INFO_PAUSE,
+#ifdef __BIG_ENDIAN
+	.formats	  = SNDRV_PCM_FMTBIT_S16_BE,
+#else
+	.formats	  = SNDRV_PCM_FMTBIT_S16_LE,
+#endif
+	.period_bytes_min = 1024,
+	.period_bytes_max = 8 * 1024,
+	.periods_min	  = 2,
+	.periods_max	  = 4096,
+	.buffer_bytes_max = 32 * 1024,
+};
+
+static int txx9aclc_pcm_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct snd_soc_device *socdev = rtd->socdev;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct txx9aclc_dmadata *dmadata = runtime->private_data;
+	int ret;
+
+	ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(socdev->dev,
+		"runtime->dma_area = %#lx dma_addr = %#lx dma_bytes = %zd "
+		"runtime->min_align %ld\n",
+		(unsigned long)runtime->dma_area,
+		(unsigned long)runtime->dma_addr, runtime->dma_bytes,
+		runtime->min_align);
+	dev_dbg(socdev->dev,
+		"periods %d period_bytes %d stream %d\n",
+		params_periods(params), params_period_bytes(params),
+		substream->stream);
+
+	dmadata->substream = substream;
+	dmadata->pos = 0;
+	return 0;
+}
+
+static int txx9aclc_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+static int txx9aclc_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct txx9aclc_dmadata *dmadata = runtime->private_data;
+
+	dmadata->dma_addr = runtime->dma_addr;
+	dmadata->buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+	dmadata->period_bytes = snd_pcm_lib_period_bytes(substream);
+
+	if (dmadata->buffer_bytes == dmadata->period_bytes) {
+		dmadata->frag_bytes = dmadata->period_bytes >> 1;
+		dmadata->frags = 2;
+	} else {
+		dmadata->frag_bytes = dmadata->period_bytes;
+		dmadata->frags = dmadata->buffer_bytes / dmadata->period_bytes;
+	}
+	dmadata->frag_count = 0;
+	dmadata->pos = 0;
+	return 0;
+}
+
+static void txx9aclc_dma_complete(void *arg)
+{
+	struct txx9aclc_dmadata *dmadata = arg;
+	unsigned long flags;
+
+	/* dma completion handler cannot submit new operations */
+	spin_lock_irqsave(&dmadata->dma_lock, flags);
+	if (dmadata->frag_count >= 0) {
+		dmadata->dmacount--;
+		BUG_ON(dmadata->dmacount < 0);
+		tasklet_schedule(&dmadata->tasklet);
+	}
+	spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+}
+
+static struct dma_async_tx_descriptor *
+txx9aclc_dma_submit(struct txx9aclc_dmadata *dmadata, dma_addr_t buf_dma_addr)
+{
+	struct dma_chan *chan = dmadata->dma_chan;
+	struct dma_async_tx_descriptor *desc;
+	struct scatterlist sg;
+
+	sg_init_table(&sg, 1);
+	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf_dma_addr)),
+		    dmadata->frag_bytes, buf_dma_addr & (PAGE_SIZE - 1));
+	sg_dma_address(&sg) = buf_dma_addr;
+	desc = chan->device->device_prep_slave_sg(chan, &sg, 1,
+		dmadata->substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		DMA_TO_DEVICE : DMA_FROM_DEVICE,
+		DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc) {
+		dev_err(&chan->dev->device, "cannot prepare slave dma\n");
+		return NULL;
+	}
+	desc->callback = txx9aclc_dma_complete;
+	desc->callback_param = dmadata;
+	desc->tx_submit(desc);
+	return desc;
+}
+
+#define NR_DMA_CHAIN		2
+
+static void txx9aclc_dma_tasklet(unsigned long data)
+{
+	struct txx9aclc_dmadata *dmadata = (struct txx9aclc_dmadata *)data;
+	struct dma_chan *chan = dmadata->dma_chan;
+	struct dma_async_tx_descriptor *desc;
+	struct snd_pcm_substream *substream = dmadata->substream;
+	u32 ctlbit = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		ACCTL_AUDODMA : ACCTL_AUDIDMA;
+	int i;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dmadata->dma_lock, flags);
+	if (dmadata->frag_count < 0) {
+		struct txx9aclc_soc_device *dev =
+			container_of(dmadata, struct txx9aclc_soc_device,
+				     dmadata[substream->stream]);
+		struct txx9aclc_plat_drvdata *drvdata =
+			txx9aclc_get_plat_drvdata(dev);
+		void __iomem *base = drvdata->base;
+
+		spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+		chan->device->device_terminate_all(chan);
+		/* first time */
+		for (i = 0; i < NR_DMA_CHAIN; i++) {
+			desc = txx9aclc_dma_submit(dmadata,
+				dmadata->dma_addr + i * dmadata->frag_bytes);
+			if (!desc)
+				return;
+		}
+		dmadata->dmacount = NR_DMA_CHAIN;
+		chan->device->device_issue_pending(chan);
+		spin_lock_irqsave(&dmadata->dma_lock, flags);
+		__raw_writel(ctlbit, base + ACCTLEN);
+		dmadata->frag_count = NR_DMA_CHAIN % dmadata->frags;
+		spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+		return;
+	}
+	BUG_ON(dmadata->dmacount >= NR_DMA_CHAIN);
+	while (dmadata->dmacount < NR_DMA_CHAIN) {
+		dmadata->dmacount++;
+		spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+		desc = txx9aclc_dma_submit(dmadata,
+			dmadata->dma_addr +
+			dmadata->frag_count * dmadata->frag_bytes);
+		if (!desc)
+			return;
+		chan->device->device_issue_pending(chan);
+
+		spin_lock_irqsave(&dmadata->dma_lock, flags);
+		dmadata->frag_count++;
+		dmadata->frag_count %= dmadata->frags;
+		dmadata->pos += dmadata->frag_bytes;
+		dmadata->pos %= dmadata->buffer_bytes;
+		if ((dmadata->frag_count * dmadata->frag_bytes) %
+		    dmadata->period_bytes == 0)
+			snd_pcm_period_elapsed(substream);
+	}
+	spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+}
+
+static int txx9aclc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct txx9aclc_soc_device *dev =
+		container_of(rtd->socdev, struct txx9aclc_soc_device, soc_dev);
+	struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+	void __iomem *base = drvdata->base;
+	unsigned long flags;
+	int ret = 0;
+	u32 ctlbit = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		ACCTL_AUDODMA : ACCTL_AUDIDMA;
+
+	spin_lock_irqsave(&dmadata->dma_lock, flags);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dmadata->frag_count = -1;
+		tasklet_schedule(&dmadata->tasklet);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		__raw_writel(ctlbit, base + ACCTLDIS);
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		__raw_writel(ctlbit, base + ACCTLEN);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	spin_unlock_irqrestore(&dmadata->dma_lock, flags);
+	return ret;
+}
+
+static snd_pcm_uframes_t
+txx9aclc_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
+
+	return bytes_to_frames(substream->runtime, dmadata->pos);
+}
+
+static int txx9aclc_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct txx9aclc_soc_device *dev =
+		container_of(rtd->socdev, struct txx9aclc_soc_device, soc_dev);
+	struct txx9aclc_dmadata *dmadata = &dev->dmadata[substream->stream];
+	int ret;
+
+	ret = snd_soc_set_runtime_hwparams(substream, &txx9aclc_pcm_hardware);
+	if (ret)
+		return ret;
+	/* ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(substream->runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		return ret;
+	substream->runtime->private_data = dmadata;
+	return 0;
+}
+
+static int txx9aclc_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
+	struct dma_chan *chan = dmadata->dma_chan;
+
+	dmadata->frag_count = -1;
+	chan->device->device_terminate_all(chan);
+	return 0;
+}
+
+static struct snd_pcm_ops txx9aclc_pcm_ops = {
+	.open		= txx9aclc_pcm_open,
+	.close		= txx9aclc_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= txx9aclc_pcm_hw_params,
+	.hw_free	= txx9aclc_pcm_hw_free,
+	.prepare	= txx9aclc_pcm_prepare,
+	.trigger	= txx9aclc_pcm_trigger,
+	.pointer	= txx9aclc_pcm_pointer,
+};
+
+static void txx9aclc_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int txx9aclc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+			    struct snd_pcm *pcm)
+{
+	return snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+		card->dev, 64 * 1024, 4 * 1024 * 1024);
+}
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+	struct txx9aclc_dmadata *dmadata = param;
+	char *devname;
+	bool found = false;
+
+	devname = kasprintf(GFP_KERNEL, "%s.%d", dmadata->dma_res->name,
+		(int)dmadata->dma_res->start);
+	if (strcmp(dev_name(chan->device->dev), devname) == 0) {
+		chan->private = &dmadata->dma_slave;
+		found = true;
+	}
+	kfree(devname);
+	return found;
+}
+
+static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev,
+			     struct txx9aclc_dmadata *dmadata)
+{
+	struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+	struct txx9dmac_slave *ds = &dmadata->dma_slave;
+	dma_cap_mask_t mask;
+
+	spin_lock_init(&dmadata->dma_lock);
+
+	ds->reg_width = sizeof(u32);
+	if (dmadata->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ds->tx_reg = drvdata->physbase + ACAUDODAT;
+		ds->rx_reg = 0;
+	} else {
+		ds->tx_reg = 0;
+		ds->rx_reg = drvdata->physbase + ACAUDIDAT;
+	}
+
+	/* Try to grab a DMA channel */
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	dmadata->dma_chan = dma_request_channel(mask, filter, dmadata);
+	if (!dmadata->dma_chan) {
+		dev_err(dev->soc_dev.dev,
+			"DMA channel for %s is not available\n",
+			dmadata->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			"playback" : "capture");
+		return -EBUSY;
+	}
+	tasklet_init(&dmadata->tasklet, txx9aclc_dma_tasklet,
+		     (unsigned long)dmadata);
+	return 0;
+}
+
+static int txx9aclc_pcm_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct txx9aclc_soc_device *dev =
+		container_of(socdev, struct txx9aclc_soc_device, soc_dev);
+	struct resource *r;
+	int i;
+	int ret;
+
+	dev->dmadata[0].stream = SNDRV_PCM_STREAM_PLAYBACK;
+	dev->dmadata[1].stream = SNDRV_PCM_STREAM_CAPTURE;
+	for (i = 0; i < 2; i++) {
+		r = platform_get_resource(dev->aclc_pdev, IORESOURCE_DMA, i);
+		if (!r) {
+			ret = -EBUSY;
+			goto exit;
+		}
+		dev->dmadata[i].dma_res = r;
+		ret = txx9aclc_dma_init(dev, &dev->dmadata[i]);
+		if (ret)
+			goto exit;
+	}
+	return 0;
+
+exit:
+	for (i = 0; i < 2; i++) {
+		if (dev->dmadata[i].dma_chan)
+			dma_release_channel(dev->dmadata[i].dma_chan);
+		dev->dmadata[i].dma_chan = NULL;
+	}
+	return ret;
+}
+
+static int txx9aclc_pcm_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct txx9aclc_soc_device *dev =
+		container_of(socdev, struct txx9aclc_soc_device, soc_dev);
+	struct txx9aclc_plat_drvdata *drvdata = txx9aclc_get_plat_drvdata(dev);
+	void __iomem *base = drvdata->base;
+	int i;
+
+	/* disable all FIFO DMAs */
+	__raw_writel(ACCTL_AUDODMA | ACCTL_AUDIDMA, base + ACCTLDIS);
+	/* dummy R/W to clear pending DMAREQ if any */
+	__raw_writel(__raw_readl(base + ACAUDIDAT), base + ACAUDODAT);
+
+	for (i = 0; i < 2; i++) {
+		struct txx9aclc_dmadata *dmadata = &dev->dmadata[i];
+		struct dma_chan *chan = dmadata->dma_chan;
+		if (chan) {
+			dmadata->frag_count = -1;
+			chan->device->device_terminate_all(chan);
+			dma_release_channel(chan);
+		}
+		dev->dmadata[i].dma_chan = NULL;
+	}
+	return 0;
+}
+
+struct snd_soc_platform txx9aclc_soc_platform = {
+	.name		= "txx9aclc-audio",
+	.probe		= txx9aclc_pcm_probe,
+	.remove		= txx9aclc_pcm_remove,
+	.pcm_ops 	= &txx9aclc_pcm_ops,
+	.pcm_new	= txx9aclc_pcm_new,
+	.pcm_free	= txx9aclc_pcm_free_dma_buffers,
+};
+EXPORT_SYMBOL_GPL(txx9aclc_soc_platform);
+
+static int __init txx9aclc_soc_platform_init(void)
+{
+	return snd_soc_register_platform(&txx9aclc_soc_platform);
+}
+
+static void __exit txx9aclc_soc_platform_exit(void)
+{
+	snd_soc_unregister_platform(&txx9aclc_soc_platform);
+}
+
+module_init(txx9aclc_soc_platform_init);
+module_exit(txx9aclc_soc_platform_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("TXx9 ACLC Audio DMA driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/txx9/txx9aclc.h b/sound/soc/txx9/txx9aclc.h
new file mode 100644
index 0000000..6769aab
--- /dev/null
+++ b/sound/soc/txx9/txx9aclc.h
@@ -0,0 +1,83 @@
+/*
+ * TXx9 SoC AC Link Controller
+ *
+ * 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 __TXX9ACLC_H
+#define __TXX9ACLC_H
+
+#include <linux/interrupt.h>
+#include <asm/txx9/dmac.h>
+
+#define ACCTLEN			0x00	/* control enable */
+#define ACCTLDIS		0x04	/* control disable */
+#define   ACCTL_ENLINK		0x00000001	/* enable/disable AC-link */
+#define   ACCTL_AUDODMA		0x00000100	/* AUDODMA enable/disable */
+#define   ACCTL_AUDIDMA		0x00001000	/* AUDIDMA enable/disable */
+#define   ACCTL_AUDOEHLT	0x00010000	/* AUDO error halt
+						   enable/disable */
+#define   ACCTL_AUDIEHLT	0x00100000	/* AUDI error halt
+						   enable/disable */
+#define ACREGACC		0x08	/* codec register access */
+#define   ACREGACC_DAT_SHIFT	0	/* data field */
+#define   ACREGACC_REG_SHIFT	16	/* address field */
+#define   ACREGACC_CODECID_SHIFT	24	/* CODEC ID field */
+#define   ACREGACC_READ		0x80000000	/* CODEC read */
+#define   ACREGACC_WRITE	0x00000000	/* CODEC write */
+#define ACINTSTS		0x10	/* interrupt status */
+#define ACINTMSTS		0x14	/* interrupt masked status */
+#define ACINTEN			0x18	/* interrupt enable */
+#define ACINTDIS		0x1c	/* interrupt disable */
+#define   ACINT_CODECRDY(n)	(0x00000001 << (n))	/* CODECn ready */
+#define   ACINT_REGACCRDY	0x00000010	/* ACREGACC ready */
+#define   ACINT_AUDOERR		0x00000100	/* AUDO underrun error */
+#define   ACINT_AUDIERR		0x00001000	/* AUDI overrun error */
+#define ACDMASTS		0x80	/* DMA request status */
+#define   ACDMA_AUDO		0x00000001	/* AUDODMA pending */
+#define   ACDMA_AUDI		0x00000010	/* AUDIDMA pending */
+#define ACAUDODAT		0xa0	/* audio out data */
+#define ACAUDIDAT		0xb0	/* audio in data */
+#define ACREVID			0xfc	/* revision ID */
+
+struct txx9aclc_dmadata {
+	struct resource *dma_res;
+	struct txx9dmac_slave dma_slave;
+	struct dma_chan *dma_chan;
+	struct tasklet_struct tasklet;
+	spinlock_t dma_lock;
+	int stream; /* SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE */
+	struct snd_pcm_substream *substream;
+	unsigned long pos;
+	dma_addr_t dma_addr;
+	unsigned long buffer_bytes;
+	unsigned long period_bytes;
+	unsigned long frag_bytes;
+	int frags;
+	int frag_count;
+	int dmacount;
+};
+
+struct txx9aclc_plat_drvdata {
+	void __iomem *base;
+	u64 physbase;
+};
+
+struct txx9aclc_soc_device {
+	struct snd_soc_device soc_dev;
+	struct platform_device *aclc_pdev;	/* for ioresources, drvdata */
+	struct txx9aclc_dmadata dmadata[2];
+};
+
+static inline struct txx9aclc_plat_drvdata *txx9aclc_get_plat_drvdata(
+	struct txx9aclc_soc_device *sdev)
+{
+	return platform_get_drvdata(sdev->aclc_pdev);
+}
+
+extern struct snd_soc_platform txx9aclc_soc_platform;
+extern struct snd_soc_dai txx9aclc_ac97_dai;
+
+#endif /* __TXX9ACLC_H */
diff --git a/sound/sound_core.c b/sound/sound_core.c
index 2b302bb..12522e6 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -27,6 +27,11 @@
 MODULE_AUTHOR("Alan Cox");
 MODULE_LICENSE("GPL");
 
+static char *sound_nodename(struct device *dev)
+{
+	return kasprintf(GFP_KERNEL, "snd/%s", dev_name(dev));
+}
+
 static int __init init_soundcore(void)
 {
 	int rc;
@@ -41,6 +46,8 @@
 		return PTR_ERR(sound_class);
 	}
 
+	sound_class->nodename = sound_nodename;
+
 	return 0;
 }
 
diff --git a/sound/synth/Makefile b/sound/synth/Makefile
index e99fd76..11eb06a 100644
--- a/sound/synth/Makefile
+++ b/sound/synth/Makefile
@@ -5,16 +5,8 @@
 
 snd-util-mem-objs := util_mem.o
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_EMU10K1) += snd-util-mem.o
 obj-$(CONFIG_SND_TRIDENT) += snd-util-mem.o
-obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-util-mem.o
-obj-$(call sequencer,$(CONFIG_SND)) += emux/
+obj-$(CONFIG_SND_SBAWE_SEQ) += snd-util-mem.o
+obj-$(CONFIG_SND_SEQUENCER) += emux/
diff --git a/sound/synth/emux/Makefile b/sound/synth/emux/Makefile
index b690352..328594e 100644
--- a/sound/synth/emux/Makefile
+++ b/sound/synth/emux/Makefile
@@ -7,14 +7,6 @@
 		       emux_effect.o emux_proc.o emux_hwdep.o soundfont.o \
 		       $(if $(CONFIG_SND_SEQUENCER_OSS),emux_oss.o)
 
-#
-# this function returns:
-#   "m" - CONFIG_SND_SEQUENCER is m
-#   <empty string> - CONFIG_SND_SEQUENCER is undefined
-#   otherwise parameter #1 value
-#
-sequencer = $(if $(subst y,,$(CONFIG_SND_SEQUENCER)),$(if $(1),m),$(if $(CONFIG_SND_SEQUENCER),$(1)))
-
 # Toplevel Module Dependencies
-obj-$(call sequencer,$(CONFIG_SND_SBAWE)) += snd-emux-synth.o
-obj-$(call sequencer,$(CONFIG_SND_EMU10K1)) += snd-emux-synth.o
+obj-$(CONFIG_SND_SBAWE_SEQ) += snd-emux-synth.o
+obj-$(CONFIG_SND_EMU10K1_SEQ) += snd-emux-synth.o
diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c
index b13ce76..8f9b60c 100644
--- a/sound/usb/caiaq/audio.c
+++ b/sound/usb/caiaq/audio.c
@@ -42,10 +42,10 @@
 	(stream << 1) | (~(i / (dev->n_streams * BYTES_PER_SAMPLE_USB)) & 1)
 
 static struct snd_pcm_hardware snd_usb_caiaq_pcm_hardware = {
-	.info 		= (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | 
+	.info 		= (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 			   SNDRV_PCM_INFO_BLOCK_TRANSFER),
 	.formats 	= SNDRV_PCM_FMTBIT_S24_3BE,
-	.rates 		= (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | 
+	.rates 		= (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
 			   SNDRV_PCM_RATE_96000),
 	.rate_min	= 44100,
 	.rate_max	= 0, /* will overwrite later */
@@ -68,7 +68,7 @@
 		dev->sub_capture[sub->number] = sub;
 }
 
-static void 
+static void
 deactivate_substream(struct snd_usb_caiaqdev *dev,
 		     struct snd_pcm_substream *sub)
 {
@@ -118,7 +118,7 @@
 			return -EPIPE;
 		}
 	}
-	
+
 	return 0;
 }
 
@@ -129,7 +129,7 @@
 	debug("%s(%p)\n", __func__, dev);
 	if (!dev->streaming)
 		return;
-	
+
 	dev->streaming = 0;
 
 	for (i = 0; i < N_URBS; i++) {
@@ -154,7 +154,7 @@
 	debug("%s(%p)\n", __func__, substream);
 	if (all_substreams_zero(dev->sub_playback) &&
 	    all_substreams_zero(dev->sub_capture)) {
-		/* when the last client has stopped streaming, 
+		/* when the last client has stopped streaming,
 		 * all sample rates are allowed again */
 		stream_stop(dev);
 		dev->pcm_info.rates = dev->samplerates;
@@ -194,30 +194,31 @@
 	struct snd_pcm_runtime *runtime = substream->runtime;
 
 	debug("%s(%p)\n", __func__, substream);
-	
+
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
 		dev->period_out_count[index] = BYTES_PER_SAMPLE + 1;
 		dev->audio_out_buf_pos[index] = BYTES_PER_SAMPLE + 1;
 	} else {
-		dev->period_in_count[index] = BYTES_PER_SAMPLE;
-		dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE;
+		int in_pos = (dev->spec.data_alignment == 2) ? 0 : 2;
+		dev->period_in_count[index] = BYTES_PER_SAMPLE + in_pos;
+		dev->audio_in_buf_pos[index] = BYTES_PER_SAMPLE + in_pos;
 	}
 
 	if (dev->streaming)
 		return 0;
-	
+
 	/* the first client that opens a stream defines the sample rate
 	 * setting for all subsequent calls, until the last client closed. */
 	for (i=0; i < ARRAY_SIZE(rates); i++)
 		if (runtime->rate == rates[i])
 			dev->pcm_info.rates = 1 << i;
-	
+
 	snd_pcm_limit_hw_rates(runtime);
 
 	bytes_per_sample = BYTES_PER_SAMPLE;
 	if (dev->spec.data_alignment == 2)
 		bytes_per_sample++;
-	
+
 	bpp = ((runtime->rate / 8000) + CLOCK_DRIFT_TOLERANCE)
 		* bytes_per_sample * CHANNELS_PER_STREAM * dev->n_streams;
 
@@ -232,7 +233,7 @@
 	ret = stream_start(dev);
 	if (ret)
 		return ret;
-	
+
 	dev->output_running = 0;
 	wait_event_timeout(dev->prepare_wait_queue, dev->output_running, HZ);
 	if (!dev->output_running) {
@@ -273,7 +274,7 @@
 		return SNDRV_PCM_POS_XRUN;
 
 	if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK)
-		return bytes_to_frames(sub->runtime, 
+		return bytes_to_frames(sub->runtime,
 					dev->audio_out_buf_pos[index]);
 	else
 		return bytes_to_frames(sub->runtime,
@@ -291,7 +292,7 @@
 	.trigger =	snd_usb_caiaq_pcm_trigger,
 	.pointer =	snd_usb_caiaq_pcm_pointer
 };
-	
+
 static void check_for_elapsed_periods(struct snd_usb_caiaqdev *dev,
 				      struct snd_pcm_substream **subs)
 {
@@ -333,7 +334,7 @@
 				struct snd_pcm_runtime *rt = sub->runtime;
 				char *audio_buf = rt->dma_area;
 				int sz = frames_to_bytes(rt, rt->buffer_size);
-				audio_buf[dev->audio_in_buf_pos[stream]++] 
+				audio_buf[dev->audio_in_buf_pos[stream]++]
 					= usb_buf[i];
 				dev->period_in_count[stream]++;
 				if (dev->audio_in_buf_pos[stream] == sz)
@@ -354,14 +355,14 @@
 
 	for (i = 0; i < iso->actual_length;) {
 		if (i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 0) {
-			for (stream = 0; 
-			     stream < dev->n_streams; 
+			for (stream = 0;
+			     stream < dev->n_streams;
 			     stream++, i++) {
 				if (dev->first_packet)
 					continue;
 
 				check_byte = MAKE_CHECKBYTE(dev, stream, i);
-				
+
 				if ((usb_buf[i] & 0x3f) != check_byte)
 					dev->input_panic = 1;
 
@@ -410,21 +411,21 @@
 	}
 
 	if ((dev->input_panic || dev->output_panic) && !dev->warned) {
-		debug("streaming error detected %s %s\n", 
+		debug("streaming error detected %s %s\n",
 				dev->input_panic ? "(input)" : "",
 				dev->output_panic ? "(output)" : "");
 		dev->warned = 1;
 	}
 }
 
-static void fill_out_urb(struct snd_usb_caiaqdev *dev, 
-			 struct urb *urb, 
+static void fill_out_urb(struct snd_usb_caiaqdev *dev,
+			 struct urb *urb,
 			 const struct usb_iso_packet_descriptor *iso)
 {
 	unsigned char *usb_buf = urb->transfer_buffer + iso->offset;
 	struct snd_pcm_substream *sub;
 	int stream, i;
-	
+
 	for (i = 0; i < iso->length;) {
 		for (stream = 0; stream < dev->n_streams; stream++, i++) {
 			sub = dev->sub_playback[stream];
@@ -444,7 +445,7 @@
 
 		/* fill in the check bytes */
 		if (dev->spec.data_alignment == 2 &&
-		    i % (dev->n_streams * BYTES_PER_SAMPLE_USB) == 
+		    i % (dev->n_streams * BYTES_PER_SAMPLE_USB) ==
 		    	(dev->n_streams * CHANNELS_PER_STREAM))
 		    for (stream = 0; stream < dev->n_streams; stream++, i++)
 		    	usb_buf[i] = MAKE_CHECKBYTE(dev, stream, i);
@@ -453,7 +454,7 @@
 
 static void read_completed(struct urb *urb)
 {
-	struct snd_usb_caiaq_cb_info *info = urb->context; 
+	struct snd_usb_caiaq_cb_info *info = urb->context;
 	struct snd_usb_caiaqdev *dev;
 	struct urb *out;
 	int frame, len, send_it = 0, outframe = 0;
@@ -478,7 +479,7 @@
 		out->iso_frame_desc[outframe].length = len;
 		out->iso_frame_desc[outframe].actual_length = 0;
 		out->iso_frame_desc[outframe].offset = BYTES_PER_FRAME * frame;
-		
+
 		if (len > 0) {
 			spin_lock(&dev->spinlock);
 			fill_out_urb(dev, out, &out->iso_frame_desc[outframe]);
@@ -497,14 +498,14 @@
 		out->transfer_flags = URB_ISO_ASAP;
 		usb_submit_urb(out, GFP_ATOMIC);
 	}
-	
+
 	/* re-submit inbound urb */
 	for (frame = 0; frame < FRAMES_PER_URB; frame++) {
 		urb->iso_frame_desc[frame].offset = BYTES_PER_FRAME * frame;
 		urb->iso_frame_desc[frame].length = BYTES_PER_FRAME;
 		urb->iso_frame_desc[frame].actual_length = 0;
 	}
-	
+
 	urb->number_of_packets = FRAMES_PER_URB;
 	urb->transfer_flags = URB_ISO_ASAP;
 	usb_submit_urb(urb, GFP_ATOMIC);
@@ -528,7 +529,7 @@
 	struct usb_device *usb_dev = dev->chip.dev;
 	unsigned int pipe;
 
-	pipe = (dir == SNDRV_PCM_STREAM_PLAYBACK) ? 
+	pipe = (dir == SNDRV_PCM_STREAM_PLAYBACK) ?
 		usb_sndisocpipe(usb_dev, ENDPOINT_PLAYBACK) :
 		usb_rcvisocpipe(usb_dev, ENDPOINT_CAPTURE);
 
@@ -547,25 +548,25 @@
 			return urbs;
 		}
 
-		urbs[i]->transfer_buffer = 
+		urbs[i]->transfer_buffer =
 			kmalloc(FRAMES_PER_URB * BYTES_PER_FRAME, GFP_KERNEL);
 		if (!urbs[i]->transfer_buffer) {
 			log("unable to kmalloc() transfer buffer, OOM!?\n");
 			*ret = -ENOMEM;
 			return urbs;
 		}
-		
+
 		for (frame = 0; frame < FRAMES_PER_URB; frame++) {
-			struct usb_iso_packet_descriptor *iso = 
+			struct usb_iso_packet_descriptor *iso =
 				&urbs[i]->iso_frame_desc[frame];
-			
+
 			iso->offset = BYTES_PER_FRAME * frame;
 			iso->length = BYTES_PER_FRAME;
 		}
-		
+
 		urbs[i]->dev = usb_dev;
 		urbs[i]->pipe = pipe;
-		urbs[i]->transfer_buffer_length = FRAMES_PER_URB 
+		urbs[i]->transfer_buffer_length = FRAMES_PER_URB
 						* BYTES_PER_FRAME;
 		urbs[i]->context = &dev->data_cb_info[i];
 		urbs[i]->interval = 1;
@@ -589,7 +590,7 @@
 	for (i = 0; i < N_URBS; i++) {
 		if (!urbs[i])
 			continue;
-		
+
 		usb_kill_urb(urbs[i]);
 		kfree(urbs[i]->transfer_buffer);
 		usb_free_urb(urbs[i]);
@@ -602,11 +603,11 @@
 {
 	int i, ret;
 
-	dev->n_audio_in  = max(dev->spec.num_analog_audio_in, 
-			       dev->spec.num_digital_audio_in) / 
+	dev->n_audio_in  = max(dev->spec.num_analog_audio_in,
+			       dev->spec.num_digital_audio_in) /
 				CHANNELS_PER_STREAM;
 	dev->n_audio_out = max(dev->spec.num_analog_audio_out,
-			       dev->spec.num_digital_audio_out) / 
+			       dev->spec.num_digital_audio_out) /
 				CHANNELS_PER_STREAM;
 	dev->n_streams = max(dev->n_audio_in, dev->n_audio_out);
 
@@ -619,7 +620,7 @@
 		return -EINVAL;
 	}
 
-	ret = snd_pcm_new(dev->chip.card, dev->product_name, 0, 
+	ret = snd_pcm_new(dev->chip.card, dev->product_name, 0,
 			dev->n_audio_out, dev->n_audio_in, &dev->pcm);
 
 	if (ret < 0) {
@@ -632,7 +633,7 @@
 
 	memset(dev->sub_playback, 0, sizeof(dev->sub_playback));
 	memset(dev->sub_capture, 0, sizeof(dev->sub_capture));
-	
+
 	memcpy(&dev->pcm_info, &snd_usb_caiaq_pcm_hardware,
 			sizeof(snd_usb_caiaq_pcm_hardware));
 
@@ -651,9 +652,9 @@
 		break;
 	}
 
-	snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK, 
+	snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_PLAYBACK,
 				&snd_usb_caiaq_ops);
-	snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE, 
+	snd_pcm_set_ops(dev->pcm, SNDRV_PCM_STREAM_CAPTURE,
 				&snd_usb_caiaq_ops);
 
 	snd_pcm_lib_preallocate_pages_for_all(dev->pcm,
@@ -662,7 +663,7 @@
 					MAX_BUFFER_SIZE, MAX_BUFFER_SIZE);
 
 	dev->data_cb_info =
-		kmalloc(sizeof(struct snd_usb_caiaq_cb_info) * N_URBS, 
+		kmalloc(sizeof(struct snd_usb_caiaq_cb_info) * N_URBS,
 					GFP_KERNEL);
 
 	if (!dev->data_cb_info)
@@ -672,14 +673,14 @@
 		dev->data_cb_info[i].dev = dev;
 		dev->data_cb_info[i].index = i;
 	}
-	
+
 	dev->data_urbs_in = alloc_urbs(dev, SNDRV_PCM_STREAM_CAPTURE, &ret);
 	if (ret < 0) {
 		kfree(dev->data_cb_info);
 		free_urbs(dev->data_urbs_in);
 		return ret;
 	}
-	
+
 	dev->data_urbs_out = alloc_urbs(dev, SNDRV_PCM_STREAM_PLAYBACK, &ret);
 	if (ret < 0) {
 		kfree(dev->data_cb_info);
diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c
index 515de1c..0e5db71 100644
--- a/sound/usb/caiaq/device.c
+++ b/sound/usb/caiaq/device.c
@@ -35,7 +35,7 @@
 #include "input.h"
 
 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
-MODULE_DESCRIPTION("caiaq USB audio, version 1.3.14");
+MODULE_DESCRIPTION("caiaq USB audio, version 1.3.17");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
 			 "{Native Instruments, RigKontrol3},"
@@ -79,7 +79,7 @@
 	{
 		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE,
 		.idVendor =	USB_VID_NATIVEINSTRUMENTS,
-		.idProduct =	USB_PID_RIGKONTROL2 
+		.idProduct =	USB_PID_RIGKONTROL2
 	},
 	{
 		.match_flags =	USB_DEVICE_ID_MATCH_DEVICE,
@@ -197,7 +197,7 @@
 
 	if (buffer && len > 0)
 		memcpy(dev->ep1_out_buf+1, buffer, len);
-	
+
 	dev->ep1_out_buf[0] = command;
 	return usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, 1),
 			   dev->ep1_out_buf, len+1, &actual_len, 200);
@@ -208,7 +208,7 @@
 {
 	int ret;
 	char tmp[5];
-	
+
 	switch (rate) {
 	case 44100:	tmp[0] = SAMPLERATE_44100;   break;
 	case 48000:	tmp[0] = SAMPLERATE_48000;   break;
@@ -237,12 +237,12 @@
 
 	if (ret)
 		return ret;
-	
-	if (!wait_event_timeout(dev->ep1_wait_queue, 
+
+	if (!wait_event_timeout(dev->ep1_wait_queue,
 	    dev->audio_parm_answer >= 0, HZ))
 		return -EPIPE;
-		
-	if (dev->audio_parm_answer != 1) 
+
+	if (dev->audio_parm_answer != 1)
 		debug("unable to set the device's audio params\n");
 	else
 		dev->bpp = bpp;
@@ -250,8 +250,8 @@
 	return dev->audio_parm_answer == 1 ? 0 : -EINVAL;
 }
 
-int snd_usb_caiaq_set_auto_msg (struct snd_usb_caiaqdev *dev, 
-				int digital, int analog, int erp)
+int snd_usb_caiaq_set_auto_msg(struct snd_usb_caiaqdev *dev,
+			       int digital, int analog, int erp)
 {
 	char tmp[3] = { digital, analog, erp };
 	return snd_usb_caiaq_send_command(dev, EP1_CMD_AUTO_MSG,
@@ -262,7 +262,7 @@
 {
 	int ret;
 	char val[4];
-	
+
 	/* device-specific startup specials */
 	switch (dev->chip.usb_id) {
 	case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_RIGKONTROL2):
@@ -314,7 +314,7 @@
 			dev->control_state, 1);
 		break;
 	}
-	
+
 	if (dev->spec.num_analog_audio_out +
 	    dev->spec.num_analog_audio_in +
 	    dev->spec.num_digital_audio_out +
@@ -323,7 +323,7 @@
 		if (ret < 0)
 			log("Unable to set up audio system (ret=%d)\n", ret);
 	}
-	
+
 	if (dev->spec.num_midi_in +
 	    dev->spec.num_midi_out > 0) {
 		ret = snd_usb_caiaq_midi_init(dev);
@@ -363,7 +363,7 @@
 	if (devnum >= SNDRV_CARDS)
 		return -ENODEV;
 
-	err = snd_card_create(index[devnum], id[devnum], THIS_MODULE, 
+	err = snd_card_create(index[devnum], id[devnum], THIS_MODULE,
 			      sizeof(struct snd_usb_caiaqdev), &card);
 	if (err < 0)
 		return err;
@@ -382,11 +382,11 @@
 
 static int __devinit init_card(struct snd_usb_caiaqdev *dev)
 {
-	char *c;
+	char *c, usbpath[32];
 	struct usb_device *usb_dev = dev->chip.dev;
 	struct snd_card *card = dev->chip.card;
 	int err, len;
-	
+
 	if (usb_set_interface(usb_dev, 0, 1) != 0) {
 		log("can't set alt interface.\n");
 		return -EIO;
@@ -395,19 +395,19 @@
 	usb_init_urb(&dev->ep1_in_urb);
 	usb_init_urb(&dev->midi_out_urb);
 
-	usb_fill_bulk_urb(&dev->ep1_in_urb, usb_dev, 
+	usb_fill_bulk_urb(&dev->ep1_in_urb, usb_dev,
 			  usb_rcvbulkpipe(usb_dev, 0x1),
-			  dev->ep1_in_buf, EP1_BUFSIZE, 
+			  dev->ep1_in_buf, EP1_BUFSIZE,
 			  usb_ep1_command_reply_dispatch, dev);
 
-	usb_fill_bulk_urb(&dev->midi_out_urb, usb_dev, 
+	usb_fill_bulk_urb(&dev->midi_out_urb, usb_dev,
 			  usb_sndbulkpipe(usb_dev, 0x1),
-			  dev->midi_out_buf, EP1_BUFSIZE, 
+			  dev->midi_out_buf, EP1_BUFSIZE,
 			  snd_usb_caiaq_midi_output_done, dev);
-	
+
 	init_waitqueue_head(&dev->ep1_wait_queue);
 	init_waitqueue_head(&dev->prepare_wait_queue);
-	
+
 	if (usb_submit_urb(&dev->ep1_in_urb, GFP_KERNEL) != 0)
 		return -EIO;
 
@@ -420,47 +420,52 @@
 
 	usb_string(usb_dev, usb_dev->descriptor.iManufacturer,
 		   dev->vendor_name, CAIAQ_USB_STR_LEN);
-	
+
 	usb_string(usb_dev, usb_dev->descriptor.iProduct,
 		   dev->product_name, CAIAQ_USB_STR_LEN);
-	
-	usb_string(usb_dev, usb_dev->descriptor.iSerialNumber,
-		   dev->serial, CAIAQ_USB_STR_LEN);
 
-	/* terminate serial string at first white space occurence */
-	c = strchr(dev->serial, ' ');
-	if (c)
-		*c = '\0';
-	
-	strcpy(card->driver, MODNAME);
-	strcpy(card->shortname, dev->product_name);
+	strlcpy(card->driver, MODNAME, sizeof(card->driver));
+	strlcpy(card->shortname, dev->product_name, sizeof(card->shortname));
+	strlcpy(card->mixername, dev->product_name, sizeof(card->mixername));
 
-	len = snprintf(card->longname, sizeof(card->longname),
-		       "%s %s (serial %s, ",
-		       dev->vendor_name, dev->product_name, dev->serial);
+	/* if the id was not passed as module option, fill it with a shortened
+	 * version of the product string which does not contain any
+	 * whitespaces */
 
-	if (len < sizeof(card->longname) - 2)
-		len += usb_make_path(usb_dev, card->longname + len,
-				     sizeof(card->longname) - len);
+	if (*card->id == '\0') {
+		char id[sizeof(card->id)];
 
-	card->longname[len++] = ')';
-	card->longname[len] = '\0';
+		memset(id, 0, sizeof(id));
+
+		for (c = card->shortname, len = 0;
+			*c && len < sizeof(card->id); c++)
+			if (*c != ' ')
+				id[len++] = *c;
+
+		snd_card_set_id(card, id);
+	}
+
+	usb_make_path(usb_dev, usbpath, sizeof(usbpath));
+	snprintf(card->longname, sizeof(card->longname),
+		       "%s %s (%s)",
+		       dev->vendor_name, dev->product_name, usbpath);
+
 	setup_card(dev);
 	return 0;
 }
 
-static int __devinit snd_probe(struct usb_interface *intf, 
+static int __devinit snd_probe(struct usb_interface *intf,
 		     const struct usb_device_id *id)
 {
 	int ret;
 	struct snd_card *card;
 	struct usb_device *device = interface_to_usbdev(intf);
-	
+
 	ret = create_card(device, &card);
-	
+
 	if (ret < 0)
 		return ret;
-			
+
 	usb_set_intfdata(intf, card);
 	ret = init_card(caiaqdev(card));
 	if (ret < 0) {
@@ -468,7 +473,7 @@
 		snd_card_free(card);
 		return ret;
 	}
-	
+
 	return 0;
 }
 
@@ -489,10 +494,10 @@
 	snd_usb_caiaq_input_free(dev);
 #endif
 	snd_usb_caiaq_audio_free(dev);
-	
+
 	usb_kill_urb(&dev->ep1_in_urb);
 	usb_kill_urb(&dev->midi_out_urb);
-	
+
 	snd_card_free(card);
 	usb_reset_device(interface_to_usbdev(intf));
 }
diff --git a/sound/usb/caiaq/device.h b/sound/usb/caiaq/device.h
index 4cce1ad..ece7351 100644
--- a/sound/usb/caiaq/device.h
+++ b/sound/usb/caiaq/device.h
@@ -81,7 +81,6 @@
 
 	char vendor_name[CAIAQ_USB_STR_LEN];
 	char product_name[CAIAQ_USB_STR_LEN];
-	char serial[CAIAQ_USB_STR_LEN];
 
 	int n_streams, n_audio_in, n_audio_out;
 	int streaming, first_packet, output_running;
diff --git a/sound/usb/caiaq/midi.c b/sound/usb/caiaq/midi.c
index 8fa8cd88..538e8c0 100644
--- a/sound/usb/caiaq/midi.c
+++ b/sound/usb/caiaq/midi.c
@@ -40,7 +40,7 @@
 
 	if (!dev)
 		return;
-	
+
 	dev->midi_receive_substream = up ? substream : NULL;
 }
 
@@ -64,18 +64,18 @@
 				    struct snd_rawmidi_substream *substream)
 {
 	int len, ret;
-	
+
 	dev->midi_out_buf[0] = EP1_CMD_MIDI_WRITE;
 	dev->midi_out_buf[1] = 0; /* port */
 	len = snd_rawmidi_transmit(substream, dev->midi_out_buf + 3,
 				   EP1_BUFSIZE - 3);
-	
+
 	if (len <= 0)
 		return;
-	
+
 	dev->midi_out_buf[2] = len;
 	dev->midi_out_urb.transfer_buffer_length = len+3;
-	
+
 	ret = usb_submit_urb(&dev->midi_out_urb, GFP_ATOMIC);
 	if (ret < 0)
 		log("snd_usb_caiaq_midi_send(%p): usb_submit_urb() failed,"
@@ -88,7 +88,7 @@
 static void snd_usb_caiaq_midi_output_trigger(struct snd_rawmidi_substream *substream, int up)
 {
 	struct snd_usb_caiaqdev *dev = substream->rmidi->private_data;
-	
+
 	if (up) {
 		dev->midi_out_substream = substream;
 		if (!dev->midi_out_active)
@@ -113,12 +113,12 @@
 	.trigger =      snd_usb_caiaq_midi_input_trigger,
 };
 
-void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev, 
+void snd_usb_caiaq_midi_handle_input(struct snd_usb_caiaqdev *dev,
 				     int port, const char *buf, int len)
 {
 	if (!dev->midi_receive_substream)
 		return;
-	
+
 	snd_rawmidi_receive(dev->midi_receive_substream, buf, len);
 }
 
@@ -142,16 +142,16 @@
 
 	if (device->spec.num_midi_out > 0) {
 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
-		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 
+		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
 				    &snd_usb_caiaq_midi_output);
 	}
 
 	if (device->spec.num_midi_in > 0) {
 		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
-		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 
+		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
 				    &snd_usb_caiaq_midi_input);
 	}
-	
+
 	device->rmidi = rmidi;
 
 	return 0;
@@ -160,7 +160,7 @@
 void snd_usb_caiaq_midi_output_done(struct urb* urb)
 {
 	struct snd_usb_caiaqdev *dev = urb->context;
-	
+
 	dev->midi_out_active = 0;
 	if (urb->status != 0)
 		return;
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
index a6b8848..c7b9023 100644
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -627,6 +627,7 @@
 	subs->hwptr_done += offs;
 	if (subs->hwptr_done >= runtime->buffer_size)
 		subs->hwptr_done -= runtime->buffer_size;
+	runtime->delay += offs;
 	spin_unlock_irqrestore(&subs->lock, flags);
 	urb->transfer_buffer_length = offs * stride;
 	if (period_elapsed)
@@ -636,12 +637,22 @@
 
 /*
  * process after playback data complete
- * - nothing to do
+ * - decrease the delay count again
  */
 static int retire_playback_urb(struct snd_usb_substream *subs,
 			       struct snd_pcm_runtime *runtime,
 			       struct urb *urb)
 {
+	unsigned long flags;
+	int stride = runtime->frame_bits >> 3;
+	int processed = urb->transfer_buffer_length / stride;
+
+	spin_lock_irqsave(&subs->lock, flags);
+	if (processed > runtime->delay)
+		runtime->delay = 0;
+	else
+		runtime->delay -= processed;
+	spin_unlock_irqrestore(&subs->lock, flags);
 	return 0;
 }
 
@@ -1520,6 +1531,7 @@
 	subs->hwptr_done = 0;
 	subs->transfer_done = 0;
 	subs->phase = 0;
+	runtime->delay = 0;
 
 	/* clear urbs (to be sure) */
 	deactivate_urbs(subs, 0, 1);
@@ -3279,6 +3291,25 @@
 	return snd_usb_cm106_write_int_reg(dev, 2, 0x8004);
 }
 
+/*
+ * C-Media CM6206 is based on CM106 with two additional
+ * registers that are not documented in the data sheet.
+ * Values here are chosen based on sniffing USB traffic
+ * under Windows.
+ */
+static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
+{
+	int err, reg;
+	int val[] = {0x200c, 0x3000, 0xf800, 0x143f, 0x0000, 0x3000};
+
+	for (reg = 0; reg < ARRAY_SIZE(val); reg++) {
+		err = snd_usb_cm106_write_int_reg(dev, reg, val[reg]);
+		if (err < 0)
+			return err;
+	}
+
+	return err;
+}
 
 /*
  * Setup quirks
@@ -3565,6 +3596,12 @@
 			goto __err_val;
 	}
 
+	/* C-Media CM6206 / CM106-Like Sound Device */
+	if (id == USB_ID(0x0d8c, 0x0102)) {
+		if (snd_usb_cm6206_boot_quirk(dev) < 0)
+			goto __err_val;
+	}
+
 	/*
 	 * found a config.  now register to ALSA
 	 */
diff --git a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
index ecb58e7..4bd3a7a 100644
--- a/sound/usb/usbmixer.c
+++ b/sound/usb/usbmixer.c
@@ -423,7 +423,7 @@
 	value_set = convert_bytes_value(cval, value_set);
 	buf[0] = value_set & 0xff;
 	buf[1] = (value_set >> 8) & 0xff;
-	while (timeout -- > 0)
+	while (timeout-- > 0)
 		if (snd_usb_ctl_msg(cval->mixer->chip->dev,
 				    usb_sndctrlpipe(cval->mixer->chip->dev, 0),
 				    request,
diff --git a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
index 5d955aa..f6f201e 100644
--- a/sound/usb/usbquirks.h
+++ b/sound/usb/usbquirks.h
@@ -1470,6 +1470,41 @@
 	}
 },
 {
+	/* Edirol M-16DX */
+	/* FIXME: This quirk gives a good-working capture stream but the
+	 *        playback seems problematic because of lacking of sync
+	 *        with capture stream.  It needs to sync with the capture
+	 *        clock.  As now, you'll get frequent sound distortions
+	 *        via the playback.
+	 */
+	USB_DEVICE(0x0582, 0x00c4),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_COMPOSITE,
+		.data = (const struct snd_usb_audio_quirk[]) {
+			{
+				.ifnum = 0,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 1,
+				.type = QUIRK_AUDIO_STANDARD_INTERFACE
+			},
+			{
+				.ifnum = 2,
+				.type = QUIRK_MIDI_FIXED_ENDPOINT,
+				.data = & (const struct snd_usb_midi_endpoint_info) {
+					.out_cables = 0x0001,
+					.in_cables  = 0x0001
+				}
+			},
+			{
+				.ifnum = -1
+			}
+		}
+	}
+},
+{
 	/* BOSS GT-10 */
 	USB_DEVICE(0x0582, 0x00da),
 	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
@@ -1951,6 +1986,14 @@
 	}
 },
 {
+	USB_DEVICE(0x0ccd, 0x0028),
+	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+		.vendor_name = "TerraTec",
+		.product_name = "Aureon5.1MkII",
+		.ifnum = QUIRK_NO_INTERFACE
+	}
+},
+{
 	USB_DEVICE(0x0ccd, 0x0035),
 	.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
 		.vendor_name = "Miditech",