ASoC: DMA and SSI drivers for i.MX27

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



As I described in one of my previous mails, I have been trying to port
an old ASoC driver for kernel 2.6.22 to latest stable 2.6.30 kernel.
I am currently facing a big problem which make some repeated samples
appear with a regular pattern:
- frequency of bad samples = 2 * period_size
- bad samples in audio stream are taken from "buffer_size" samples
back in the original file.

You can reproduce this with:
aplay -t raw -f S16_LE -r8000 8k16bitpcm.wav

(http://en.wikipedia.org/wiki/WAV to get this file)

Here I submit two patches:
- One which adds ssi and dma drivers plus a custom machine driver
(imx27asoc_list.patch).
- Another patch which adds wm8974 codec support taken from ASoC
wm8974-upstream branch (wm8974_list.patch).

Note that the machine driver attached uses wm8974 codec, thus second
patch is needed. Please, note also that patches are currently quite
dirty, and in an early development state. The purpose of this mail is
not to even consider to include them in any branch right now, but to
gather some help to solve the problem. As soon as I have it solved I
will clean all the stuff and try to prepare it for mainline.



-- 
Javier Martin
Vista Silicon S.L.
Universidad de Cantabria
CDTUC - FASE C - Oficina S-345
Avda de los Castros s/n
39005- Santander. Cantabria. Spain
+34 942 25 32 60
www.vista-silicon.com
commit 061556c42e6f48911338a6a7996445da968e962a
Author: Javier Martin <javier.martin@xxxxxxxxxxxxxxxxx>
Date:   Mon Jul 20 16:37:44 2009 +0200

    ASOC: i.MX27 DMA and DAI platform drivers support.
    
    They have been ported from visstrim_m10 2.6.22 kernel.
    They still do not work properly, but at least it compiles and
    it is detected by the system properly as a sound card.
---
 sound/soc/Kconfig              |    1 +
 sound/soc/Makefile             |    1 +
 sound/soc/imx/Kconfig          |   58 +++
 sound/soc/imx/Makefile         |   21 +
 sound/soc/imx/imx27-pcm.c      |  596 +++++++++++++++++++++++++
 sound/soc/imx/imx27-pcm.h      |   95 ++++
 sound/soc/imx/imx27-ssi.c      |  934 ++++++++++++++++++++++++++++++++++++++++
 sound/soc/imx/imx27-ssi.h      |  226 ++++++++++
 sound/soc/imx/mx27vis_wm8974.c |  357 +++++++++++++++
 9 files changed, 2289 insertions(+), 0 deletions(-)

diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 3d2bb6f..c9bbc70 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -33,6 +33,7 @@ source "sound/soc/omap/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/s3c24xx/Kconfig"
 source "sound/soc/sh/Kconfig"
+source "sound/soc/imx/Kconfig"
 
 # Supported codecs
 source "sound/soc/codecs/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 0237879..8120b52 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_SND_SOC)	+= omap/
 obj-$(CONFIG_SND_SOC)	+= pxa/
 obj-$(CONFIG_SND_SOC)	+= s3c24xx/
 obj-$(CONFIG_SND_SOC)	+= sh/
+obj-$(CONFIG_SND_SOC)   += imx/
diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig
new file mode 100644
index 0000000..d156dc6
--- /dev/null
+++ b/sound/soc/imx/Kconfig
@@ -0,0 +1,58 @@
+config SND_MX31_SOC
+	tristate "SoC Audio for the Freescale i.MX CPU"
+	depends on ARCH_MX3 && SND
+	select SND_PCM
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the MXC AC97, I2S or SSP interface. You will also need
+	  to select the audio interfaces to support below.
+
+config SND_MX31_AC97
+	tristate
+	select SND_AC97_CODEC
+
+config SND_MX31_SOC_AC97
+	tristate
+	select AC97_BUS
+
+config SND_MX31_SOC_SSI
+	tristate
+
+config SND_MX27_SOC
+	tristate "SoC Audio for the Freecale i.MX27 CPU"
+	depends on MACH_MX27 && SND
+	select SND_PCM
+	help
+	  Say Y or M if you want to add support for codecs attached to
+	  the MXC AC97, I2S or SSP interface. You will also need
+	  to select the audio interfaces to support below.
+
+config SND_MX27_SOC_SSI
+	tristate
+
+config SND_SOC_MX31ADS_WM8753
+	tristate "SoC Audio support for MX31 - WM8753"
+	depends on SND_MX31_SOC && ARCH_MX3
+	select SND_MX31_SOC_SSI
+	select SND_SOC_WM8753
+	help
+	  Say Y if you want to add support for SoC audio on MX31ADS
+	  with the WM8753.
+
+
+config SND_SOC_MX27VIS_WM8974
+	tristate "SoC Audio support for MX27 - WM8974 Visstrim_sm10 board"
+	depends on SND_MX27_SOC && MACH_MX27
+	select SND_MX27_SOC_SSI
+	select SND_SOC_WM8974
+	help
+	  Say Y if you want to add support for SoC audio on Visstrim SM10 
+	  board with WM8974.
+
+config SND_SOC_MX27_SSI1
+	tristate "Select SSI port 1 for audio in MX27VIS"
+	depends on SND_SOC_MX27VIS_WM8974
+	help
+	  Say Y if you want to add support for SSI1 interface, say N if you 
+	  want to add support for SSI2 interface.
+
diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile
new file mode 100644
index 0000000..3fb4174
--- /dev/null
+++ b/sound/soc/imx/Makefile
@@ -0,0 +1,21 @@
+# i.MX Platform Support
+snd-soc-imx21-objs := imx21-pcm.o
+snd-soc-imx27-objs := imx27-pcm.o
+snd-soc-imx31-objs := imx31-pcm.o
+snd-soc-imx-ac97-objs := imx-ac97.o
+snd-soc-imx-ssi-objs := imx-ssi.o
+snd-soc-imx27-ssi-objs := imx27-ssi.o
+
+obj-$(CONFIG_SND_MX31_SOC) += snd-soc-imx31.o
+obj-$(CONFIG_SND_MX31_SOC_AC97) += snd-soc-imx-ac97.o
+obj-$(CONFIG_SND_MX31_SOC_SSI) += snd-soc-imx-ssi.o
+obj-$(CONFIG_SND_MX27_SOC) += snd-soc-imx27.o
+obj-$(CONFIG_SND_MX27_SOC_SSI) += snd-soc-imx27-ssi.o
+
+# i.MX Machine Support
+snd-soc-mx31ads-wm8753-objs := mx31ads_wm8753.o
+obj-$(CONFIG_SND_SOC_MX31ADS_WM8753) += snd-soc-mx31ads-wm8753.o
+snd-soc-mx21ads-wm8731-objs := mx21ads_wm8731.o
+obj-$(CONFIG_SND_SOC_MX21ADS_WM8731) += snd-soc-mx21ads-wm8731.o
+snd-soc-mx27vis-wm8974-objs := mx27vis_wm8974.o
+obj-$(CONFIG_SND_SOC_MX27VIS_WM8974) += snd-soc-mx27vis-wm8974.o
diff --git a/sound/soc/imx/imx27-pcm.c b/sound/soc/imx/imx27-pcm.c
new file mode 100644
index 0000000..0f5ef8b
--- /dev/null
+++ b/sound/soc/imx/imx27-pcm.c
@@ -0,0 +1,596 @@
+/*
+ * linux/sound/arm/mx27-pcm.c -- ALSA SoC interface for the Freescale i.MX27 CPU
+ *
+ * Copyright 2009 Vista Silicon S.L.
+ * Author: Javier Martin
+ *         javier.martin@xxxxxxxxxxxxxxxxx
+ *
+ * Based on mxc-pcm.c by	Liam Girdwood, (C) 2006 Wolfson Microelectronics PLC.
+ * and on mxc-alsa-mc13783 (C) 2006 Freescale.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ *
+ */
+
+// #define DEBUG
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <asm/dma.h>
+#include <mach/hardware.h>
+#include <mach/dma-mx1-mx2.h>
+
+#include "imx27-pcm.h"
+
+/* Just for debugging */
+#include "imx27-ssi.h"
+
+/* Taken from drivers/mxc/ssi/registers.h just provisionally */
+#define    MXC_SSI1STX0		0x00
+#define    MXC_SSI1SRX0		0x08
+#define    MXC_SSI2STX0		0x00
+#define    MXC_SSI2SRX0		0x08
+
+
+static const struct snd_pcm_hardware mx27_pcm_hardware = {
+	.info			= (SNDRV_PCM_INFO_INTERLEAVED |
+				   SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				   SNDRV_PCM_INFO_MMAP |
+				   SNDRV_PCM_INFO_MMAP_VALID),
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.buffer_bytes_max	= 32 * 1024,
+	.period_bytes_min	= 64,
+	.period_bytes_max	= 8 * 1024,
+	.periods_min		= 2,
+	.periods_max		= 255,
+	.fifo_size		= 0,
+};
+
+struct mx27_runtime_data {
+	int dma_ch_tx; //DMA channel number for tx
+	int dma_ch_rx; //DMA channel number for rx
+	int active; //Is the stream active?
+	unsigned int period; //Period number
+	unsigned int periods; //�?
+	int tx_spin; //�?
+	spinlock_t dma_lock; //DMA spinlock
+	struct mx27_pcm_dma_param *dma_params; //FIXME this is SDMA api
+};
+
+/**
+  * This function stops the current dma transfer for playback
+  * and clears the dma pointers.
+  *
+  * @param	substream	pointer to the structure of the current stream.
+  *
+  */
+static int audio_stop_dma(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct mx27_runtime_data *prtd = runtime->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&prtd->dma_lock, flags);
+
+	pr_debug("MX27 : audio_stop_dma active = 0\n");
+
+	prtd->active = 0;
+	prtd->period = 0;
+	prtd->periods = 0;
+
+	/* this stops the dma channel and clears the buffer ptrs */
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		imx_dma_disable(prtd->dma_ch_tx);
+	else
+		imx_dma_disable(prtd->dma_ch_rx);
+
+	spin_unlock_irqrestore(&prtd->dma_lock, flags);
+
+	return 0;
+}
+
+/**
+  * This function is called whenever a new audio block needs to be
+  * transferred to the codec. The function receives the address and the size
+  * of the new block and start a new DMA transfer.
+  *
+  * @param	substream	pointer to the structure of the current stream.
+  *
+  */
+static int dma_new_period(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime =  substream->runtime;
+	struct mx27_runtime_data *prtd = runtime->private_data;
+	unsigned int dma_size;
+	unsigned int offset;
+	int ret = 0;
+// 	mxc_dma_requestbuf_t dma_request;
+	dma_addr_t mem_addr;
+	unsigned int dev_addr;
+	unsigned int num_of_bytes;
+
+	if (prtd->active) {
+// 		memset(&dma_request, 0, sizeof(mxc_dma_requestbuf_t));
+		dma_size = frames_to_bytes(runtime, runtime->period_size);
+
+		pr_debug("dma_new_period: prtd->period (%x) runtime->periods (%d)\n",
+			prtd->period, runtime->periods);
+		pr_debug("dma_new_period: runtime->period_size (%d) dma_size (%d)\n",
+			(unsigned int)runtime->period_size,
+			runtime->dma_bytes);
+
+		offset = dma_size * prtd->period;
+		snd_BUG_ON(dma_size > mx27_pcm_hardware.period_bytes_max);
+
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			pr_debug("\ndma_new_period (playback): runtime->dma_area = %x\noffset = %x\ndma_size = %x\n",
+				(unsigned int)runtime->dma_area, offset,
+				 dma_size);
+			pr_debug("\ndma_new_period : dma_addr is %x\n",
+				 (runtime->dma_addr + offset));
+
+			mem_addr = (dma_addr_t)(runtime->dma_addr + offset);
+#ifdef CONFIG_SND_SOC_MX27_SSI1
+
+			dev_addr = (dma_addr_t) (SSI1_BASE_ADDR + MXC_SSI1STX0);
+
+			pr_debug("mem_addr is %x\n", mem_addr);
+			pr_debug("dev_addr is %x\n", dev_addr);
+#else
+			dev_addr =
+			    (dma_addr_t) (SSI2_BASE_ADDR + MXC_SSI2STX0);
+#endif
+		} else {
+			pr_debug("\ndma_new_period (capture): runtime->dma_area = %x\noffset = %x\ndma_size = %x\n",
+				(unsigned int)runtime->dma_area, offset,
+				 dma_size);
+			pr_debug("\ndma_new_period: dma_addr is %x\n",
+				(runtime->dma_addr + offset));
+
+			mem_addr = (dma_addr_t) (runtime->dma_addr + offset);
+#ifdef CONFIG_SND_SOC_MX27_SSI1
+			dev_addr =
+			    (dma_addr_t) (SSI1_BASE_ADDR + MXC_SSI1SRX0);
+#else
+			dev_addr =
+			    (dma_addr_t) (SSI2_BASE_ADDR + MXC_SSI2SRX0);
+#endif
+		}
+
+		num_of_bytes = dma_size;
+		pr_debug("dma_new_period: Start DMA offset (%d) size (%d)\n",
+			 offset, runtime->dma_bytes);
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			pr_debug("dma_new_period: configuring tx dma\n");
+			ret = imx_dma_setup_single(prtd->dma_ch_tx, mem_addr,
+						num_of_bytes, dev_addr,
+						DMA_MODE_WRITE);
+			if (ret < 0) {
+				pr_debug("dma_new_period: buffer could not be added for playback transfer\n");
+				return ret;
+			}
+
+			pr_debug("dma_new_period: enable  tx dma transfer\n");
+			pr_debug("TXFIFO HAS %d WORDS\n", (( SSI1_SFCSR & (0xf<<8))>>8));
+
+			imx_dma_enable(prtd->dma_ch_tx);
+		} else {
+			ret = imx_dma_setup_single(prtd->dma_ch_rx, mem_addr,
+						num_of_bytes, dev_addr,
+						DMA_MODE_READ);
+			if (ret < 0) {
+				pr_debug("dma_new_period: buffer could not be added for capture transfer\n");
+				return ret;
+			}
+
+			pr_debug("dma_new_period: enable  dma transfer\n");
+			imx_dma_enable(prtd->dma_ch_rx);
+		}
+
+		pr_debug("dma_new_period: transfer enabled\n mem_addr = %x\n dev_addr = %x\n num_of_byes = %d\n", (unsigned int) mem_addr,
+			(unsigned int) dev_addr, num_of_bytes);
+
+		prtd->tx_spin = 1; /* FGA little trick to retrieve DMA pos */
+		prtd->period++;
+		prtd->period %= runtime->periods;
+    }
+	return ret;
+}
+
+
+/**
+  * This is a callback which will be called
+  * when a TX transfer finishes. The call occurs
+  * in interrupt context.
+  *
+  * @param	dat	pointer to the structure of the current stream.
+  *
+  */
+static void audio_dma_irq(int channel, void *data)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_pcm_runtime *runtime;
+	struct mx27_runtime_data *prtd;
+	unsigned int dma_size;
+	unsigned int previous_period;
+	unsigned int offset;
+
+	substream = data;
+	runtime = substream->runtime;
+	prtd = runtime->private_data;
+	previous_period  = prtd->periods;
+	dma_size = frames_to_bytes(runtime, runtime->period_size);
+	offset = dma_size * previous_period;
+
+	prtd->tx_spin = 0;
+	prtd->periods++;
+	prtd->periods %= runtime->periods;
+
+	pr_debug("audio_dma_irq: irq per %d offset %x\n", prtd->periods, offset);
+	pr_debug("(1) TXFIFO HAS %d WORDS\n", (( SSI1_SFCSR & (0xf<<8))>>8));
+
+	/*
+	  * If we are getting a callback for an active stream then we inform
+	  * the PCM middle layer we've finished a period
+	  */
+ 	if (prtd->active)
+		snd_pcm_period_elapsed(substream);
+
+	/*
+	  * Trig next DMA transfer
+	  */
+	dma_new_period(substream);
+}
+
+/**
+  * This function configures the hardware to allow audio
+  * playback operations. It is called by ALSA framework.
+  *
+  * @param	substream	pointer to the structure of the current stream.
+  *
+  * @return              0 on success, -1 otherwise.
+  */
+static int
+snd_mxc_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime =  substream->runtime;
+	struct mx27_runtime_data *prtd = runtime->private_data;
+
+	prtd->period = 0;
+	prtd->periods = 0;
+
+	return 0;
+}
+
+static int mxc_pcm_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int ret;
+
+	if((ret=snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
+	{
+		pr_debug("imx27-pcm: failed to malloc pcm pages\n");
+		return ret;
+	}
+
+	pr_debug("mxc_pcm_hw_params: snd_pcm_lib_malloc pages has return OK\n");
+	pr_debug("IMX27: snd_imx27_audio_hw_params runtime->dma_addr 0x(%x)\n",
+		 (unsigned int)runtime->dma_addr);
+	pr_debug("IMX27: snd_imx27_audio_hw_params runtime->dma_area 0x(%x)\n",
+		 (unsigned int)runtime->dma_area);
+	pr_debug("IMX27: snd_imx27_audio_hw_params runtime->dma_bytes 0x(%x)\n",
+		 (unsigned int)runtime->dma_bytes);
+
+	return ret;
+}
+
+static int mxc_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct mx27_runtime_data *prtd = runtime->private_data;
+	
+
+	if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		imx_dma_free(prtd->dma_ch_tx);
+	else
+		imx_dma_free(prtd->dma_ch_rx);
+
+	snd_pcm_lib_free_pages(substream);
+
+	return 0;
+}
+
+static int mxc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct mx27_runtime_data *prtd = substream->runtime->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		prtd->tx_spin = 0;
+		/* requested stream startup */
+		prtd->active = 1;
+		pr_debug("mxc_pcm_trigger: starting dma_new_period\n");
+        	ret = dma_new_period(substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		/* requested stream shutdown */
+		pr_debug("mxc_pcm_trigger: stopping dma transfer\n");
+		ret = audio_stop_dma(substream);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t mxc_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct mx27_runtime_data *prtd = runtime->private_data;
+	unsigned int offset = 0;
+
+	pr_debug("mxc_pcm_pointer:\n runtime->period_size=%d\n prtd->periods=%d\n runtime->buffer_size=%d\n prtd->tx_spin=%d\n",
+		(int)runtime->period_size, prtd->periods,
+		(int)runtime->buffer_size, prtd->tx_spin);
+	/* tx_spin value is used here to check if a transfer is active */
+	if (prtd->tx_spin){
+		offset = (runtime->period_size * (prtd->periods)) +
+						(runtime->period_size >> 1);
+		if (offset >= runtime->buffer_size)
+			offset = runtime->period_size >> 1;
+	} else {
+		offset = (runtime->period_size * (prtd->periods));
+		if (offset >= runtime->buffer_size)
+			offset = 0;
+	}
+	pr_debug("mxc_pcm_pointer: pointer offset %x\n", offset);
+	
+	return offset;
+}
+
+//Alocation of substream->runtime->private_data
+static int mxc_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct mx27_runtime_data *prtd;
+	int ret;
+
+	snd_soc_set_runtime_hwparams(substream, &mx27_pcm_hardware);
+
+	if ((ret = snd_pcm_hw_constraint_integer(runtime,
+					SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return ret;
+
+	if ((prtd = kzalloc(sizeof(struct mx27_runtime_data), GFP_KERNEL)) == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	runtime->private_data = prtd;
+
+	if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+	{
+		pr_debug("mxc_pcm_open: Requesting MXC_DMA_SSI1_16BIT_TX0 dma channel\n");
+// 		prtd->dma_ch_tx  = mxc_dma_request(MXC_DMA_SSI1_16BIT_TX0, "ALSA TX DMA", NULL);
+		prtd->dma_ch_tx  = imx_dma_request_by_prio("ALSA TX DMA",
+							DMA_PRIO_MEDIUM);
+		if(prtd->dma_ch_tx < 0) { 
+			pr_debug("error requesting a write dma channel\n");
+			return ret;
+		}
+		imx_dma_config_burstlen(prtd->dma_ch_tx, 8);
+#ifdef CONFIG_SND_SOC_MX27_SSI1
+		ret = imx_dma_config_channel(prtd->dma_ch_tx,
+				IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+				IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+				DMA_REQ_SSI1_TX0, 0);
+#else
+		ret = imx_dma_config_channel(prtd->dma_ch_tx,
+				IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+				IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+				DMA_REQ_SSI2_TX0, 0);
+#endif
+		if(ret) {
+			pr_debug("error configuring dma channel %d\n",
+				prtd->dma_ch_tx);
+			return ret;
+		}
+		/* Set dma callback */
+		pr_debug("mxc_pcm_open: Setting tx dma callback function\n");
+// 		ret = mxc_dma_callback_set(prtd->dma_ch_tx, (mxc_dma_callback_t) audio_dma_irq, (void *)substream);
+		ret = imx_dma_setup_handlers(prtd->dma_ch_tx,
+					audio_dma_irq, NULL,
+					(void *)substream);
+		if(ret < 0) {
+			pr_debug("error setting dma callback function\n");
+			return ret;
+		}
+
+		return 0;
+	} else {
+		pr_debug("mxc_pcm_open: Requesting MXC_DMA_SSI1_16BIT_RX0 dma channel\n");
+// 		prtd->dma_ch_rx  = mxc_dma_request(MXC_DMA_SSI1_16BIT_RX0, "ALSA RX DMA", NULL);
+		prtd->dma_ch_rx  = imx_dma_request_by_prio("ALSA RX DMA",
+							DMA_PRIO_MEDIUM);
+		if(prtd->dma_ch_rx < 0) { 
+			pr_debug("error requesting a read dma channel\n");
+			return ret;
+		}
+		imx_dma_config_burstlen(prtd->dma_ch_rx, 8);
+#ifdef CONFIG_SND_SOC_MX27_SSI1
+		ret = imx_dma_config_channel(prtd->dma_ch_rx,
+				IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+				IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+				DMA_REQ_SSI1_RX0, 0);
+#else
+		ret = imx_dma_config_channel(prtd->dma_ch_rx,
+				IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
+				IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
+				DMA_REQ_SSI2_RX0, 0);
+#endif
+		if(ret) {
+			pr_debug("error configuring dma channel %d\n",
+				prtd->dma_ch_rx);
+			return ret;
+		}
+
+		//Set dma callback
+		pr_debug("mxc_pcm_open: Setting rx dma callback function\n");
+		ret = imx_dma_setup_handlers(prtd->dma_ch_rx,
+					audio_dma_irq, NULL,
+					(void *)substream);
+		if(ret < 0) {
+			pr_debug("error setting dma callback function\n");
+			return ret;
+		}
+		return 0;
+	}
+
+ out:
+	return ret;
+}
+
+static int mxc_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct mxc_runtime_data *prtd = runtime->private_data;
+
+	kfree(prtd);
+
+	return 0;
+}
+
+static int
+mxc_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+				     runtime->dma_area,
+				     runtime->dma_addr,
+				     runtime->dma_bytes);
+}
+
+struct snd_pcm_ops mxc_pcm_ops = {
+	.open		= mxc_pcm_open,
+	.close		= mxc_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= mxc_pcm_hw_params,
+	.hw_free	= mxc_pcm_hw_free,
+	.prepare	= snd_mxc_prepare,
+	.trigger	= mxc_pcm_trigger,
+	.pointer	= mxc_pcm_pointer,
+	.mmap		= mxc_pcm_mmap,
+};
+
+static u64 mxc_pcm_dmamask = 0xffffffff;
+
+static int imx31_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 = mx27_pcm_hardware.buffer_bytes_max;
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+
+	//Reserve uncached-buffered memory area for DMA
+	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+					   &buf->addr, GFP_KERNEL);
+
+	pr_debug("imx27_pcm: preallocate_dma_buffer: area=%p, addr=%p, size=%d\n", (void *) buf->area, (void *) buf->addr, size);
+
+	if (!buf->area)
+		return -ENOMEM;
+		
+	buf->bytes = size;
+	return 0;
+}
+
+static void imx31_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+
+		dma_free_writecombine(pcm->card->dev, buf->bytes,
+				      buf->area, buf->addr);
+		buf->area = NULL;
+	}
+}
+
+int mxc_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+	struct snd_pcm *pcm)
+{
+	int ret = 0;
+
+	if (!card->dev->dma_mask)
+		card->dev->dma_mask = &mxc_pcm_dmamask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (dai->playback.channels_min) {
+		ret = imx31_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_PLAYBACK);
+		pr_debug("mxc_pcm_new: preallocate playback buffer\n");
+		if (ret)
+			goto out;
+	}
+
+	if (dai->capture.channels_min) {
+		ret = imx31_pcm_preallocate_dma_buffer(pcm,
+			SNDRV_PCM_STREAM_CAPTURE);
+		pr_debug("mxc_pcm_new: preallocate capture buffer\n");
+		if (ret)
+			goto out;
+	}
+ out:
+	return ret;
+}
+
+struct snd_soc_platform imx27_soc_platform = {
+	.name		= "mxc-audio",
+	.pcm_ops 	= &mxc_pcm_ops,
+	.pcm_new	= mxc_pcm_new,
+	.pcm_free	= imx31_pcm_free_dma_buffers,
+};
+
+EXPORT_SYMBOL_GPL(imx27_soc_platform);
+
+static int __init imx27_soc_platform_init(void)
+{
+	return snd_soc_register_platform(&imx27_soc_platform);
+}
+module_init(imx27_soc_platform_init);
+
+static void __exit imx27_soc_platform_exit(void)
+{
+	snd_soc_unregister_platform(&imx27_soc_platform);
+}
+module_exit(imx27_soc_platform_exit);
+
+MODULE_AUTHOR("Javier Martin");
+MODULE_DESCRIPTION("Freescale i.MX27 PCM DMA module");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx27-pcm.h b/sound/soc/imx/imx27-pcm.h
new file mode 100644
index 0000000..e84a741
--- /dev/null
+++ b/sound/soc/imx/imx27-pcm.h
@@ -0,0 +1,95 @@
+/*
+ * mxc-pcm.h :- ASoC platform header for Freescale i.MX
+ *
+ * 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 _MXC_PCM_H
+#define _MXC_PCM_H
+
+
+/* AUDMUX regs definition - MOVE to asm/arch when stable 
+#define AUDMUX_IO_BASE_ADDR	IO_ADDRESS(AUDMUX_BASE_ADDR)
+
+#define DAM_PTCR1	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x00)))
+#define DAM_PDCR1	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x04)))
+#define DAM_PTCR2	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x08)))
+#define DAM_PDCR2	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x0C)))
+#define DAM_PTCR3	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x10)))
+#define DAM_PDCR3	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x14)))
+#define DAM_PTCR4	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x18)))
+#define DAM_PDCR4	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x1C)))
+#define DAM_PTCR5	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x20)))
+#define DAM_PDCR5	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x24)))
+#define DAM_PTCR6	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x28)))
+#define DAM_PDCR6	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x2C)))
+#define DAM_PTCR7	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x30)))
+#define DAM_PDCR7	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x34)))
+#define DAM_CNMCR	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x38)))
+#define DAM_PTCR(a)	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + a*8)))
+#define DAM_PDCR(a)	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 4 + a*8)))
+
+
+#define AUDMUX_PTCR_TFSDIR		(1 << 31)
+#define AUDMUX_PTCR_TFSSEL(x, y)		((x << 30) | (((y - 1) & 0x7) << 27))
+#define AUDMUX_PTCR_TCLKDIR		(1 << 26)
+#define AUDMUX_PTCR_TCSEL(x, y)		((x << 25) | (((y - 1) & 0x7) << 22))
+#define AUDMUX_PTCR_RFSDIR		(1 << 21)
+#define AUDMUX_PTCR_RFSSEL(x, y)		((x << 20) | (((y - 1) & 0x7) << 17))
+#define AUDMUX_PTCR_RCLKDIR		(1 << 16)
+#define AUDMUX_PTCR_RCSEL(x, y)		((x << 15) | (((y - 1) & 0x7) << 12))
+#define AUDMUX_PTCR_SYN		(1 << 11)
+
+#define AUDMUX_FROM_TXFS	0
+#define AUDMUX_FROM_RXFS	1
+
+#define AUDMUX_PDCR_RXDSEL(x)		(((x - 1) & 0x7) << 13)
+#define AUDMUX_PDCR_TXDXEN		(1 << 12)
+#define AUDMUX_PDCR_MODE(x)		(((x) & 0x3) << 8)
+#define AUDMUX_PDCR_INNMASK(x)		(((x) & 0xff) << 0)
+
+#define AUDMUX_CNMCR_CEN		(1 << 18)
+#define AUDMUX_CNMCR_FSPOL		(1 << 17)
+#define AUDMUX_CNMCR_CLKPOL		(1 << 16)
+#define AUDMUX_CNMCR_CNTHI(x)		(((x) & 0xff) << 8)
+#define AUDMUX_CNMCR_CNTLOW(x)		(((x) & 0xff) << 0)
+*/
+
+/* Previous defines are only valid for imx31 */
+#define AUDMUX_IO_BASE_ADDR	IO_ADDRESS(AUDMUX_BASE_ADDR)
+
+#define DAM_HPCR1	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x00)))
+#define DAM_HPCR2	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x04)))
+#define DAM_HPCR3	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x08)))
+#define DAM_PPCR1	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x10)))
+#define DAM_PPCR2	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x14)))
+#define DAM_PPCR3	(*((volatile u32 *)(AUDMUX_IO_BASE_ADDR + 0x1C)))
+
+#define AUDMUX_HPCR_TFSDIR	(1 << 31)
+#define AUDMUX_HPCR_TCLKDIR	(1 << 30)
+#define AUDMUX_HPCR_TFCSEL(x)	(((x) & 0xff) << 26)
+#define AUDMUX_HPCR_RXDSEL(x)	(((x) & 0x7) << 13)
+#define AUDMUX_HPCR_SYN		(1 << 12)
+
+#define AUDMUX_PPCR_TFSDIR	(1 << 31)
+#define AUDMUX_PPCR_TCLKDIR	(1 << 30)
+#define AUDMUX_PPCR_TFCSEL(x)	(((x) & 0xff) << 26)
+#define AUDMUX_PPCR_RXDSEL(x)	(((x) & 0x7) << 13)
+#define AUDMUX_PPCR_SYN		(1 << 12)
+
+
+
+struct mx27_pcm_dma_params {
+	char *name;			/* stream identifier */
+	//mxc_dma_requestbuf_t buf_params; /*DMA buffer params */
+};
+
+extern struct snd_soc_dai mxc_ssi_dai[3];
+
+/* platform data */
+extern struct snd_soc_platform imx27_soc_platform;
+
+
+#endif
diff --git a/sound/soc/imx/imx27-ssi.c b/sound/soc/imx/imx27-ssi.c
new file mode 100644
index 0000000..e44c6c8
--- /dev/null
+++ b/sound/soc/imx/imx27-ssi.c
@@ -0,0 +1,934 @@
+/*
+ * imx-ssi.c  --  SSI driver for Freescale IMX
+ *
+ * Copyright 2006 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@xxxxxxxxxxxxxxxx or linux@xxxxxxxxxxxxxxxx
+ *
+ *  Based on mxc-alsa-mc13783 (C) 2006 Freescale.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    29th Aug 2006   Initial version.
+ * 
+ * TODO: 
+ *   Need to rework SSI register defs when new defs go into mainline.
+ *   Add support for TDM and FIFO 1.
+ *
+ */
+
+// #define DEBUG
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <mach/dma-mx1-mx2.h>
+#include <asm/mach-types.h>
+
+#include "imx27-ssi.h"
+#include "imx27-pcm.h"
+
+/* debug */
+#define IMX_SSI_DEBUG 0
+#if IMX_SSI_DEBUG
+#define dbg(format, arg...) printk(format, ## arg)
+#else
+#define dbg(format, arg...)
+#endif
+
+#define SSI_DUMP() 
+/*#define SSI_DUMP() \
+	pr_debug("dump @ %s\n", __FUNCTION__); \
+	pr_debug("scr %x\n", SSI1_SCR); \
+	pr_debug("sisr %x\n", SSI1_SISR); \
+	pr_debug("stcr %x\n", SSI1_STCR); \
+	pr_debug("stccr %x\n", SSI1_STCCR); \
+	pr_debug("stfsr %x\n", SSI1_SFCSR); \
+	pr_debug("stmsk %x\n", SSI1_STMSK); \
+	pr_debug("sier %x\n", SSI1_SIER); \
+	pr_debug("sor %x\n", SSI1_SOR); \
+	pr_debug("srcr %x\n", SSI1_SRCR);*/ 
+
+#define SSI1_PORT	0
+#define SSI2_PORT	1
+
+static int ssi_active[2] = {0,0};
+
+//FIXME: This is using SDMA api which is not supported in I.MX27/I.MX21
+// static struct mxc_pcm_dma_params imx_ssi1_pcm_stereo_out0 = {
+// 	.name			= "SSI1 PCM Stereo out 0",
+// 	.params = {
+// 		.bd_number = 1,
+// 		.transfer_type = emi_2_per,
+// 		.watermark_level = SDMA_TXFIFO_WATERMARK,
+// 		.per_address = SSI1_BASE_ADDR,
+// 		.event_id = DMA_REQ_SSI1_TX1,
+// 		.peripheral_type = SSI,
+// 	},
+// };
+// 
+// static struct mxc_pcm_dma_params imx_ssi1_pcm_stereo_out1 = {
+// 	.name			= "SSI1 PCM Stereo out 1",
+// 	.params = {
+// 		.bd_number = 1,
+// 		.transfer_type = emi_2_per,
+// 		.watermark_level = SDMA_TXFIFO_WATERMARK,
+// 		.per_address = SSI1_BASE_ADDR + 0x4,
+// 		.event_id = DMA_REQ_SSI1_TX2,
+// 		.peripheral_type = SSI,
+// 	},
+// };
+// 
+// static struct mxc_pcm_dma_params imx_ssi1_pcm_stereo_in0 = {
+// 	.name			= "SSI1 PCM Stereo in 0",
+// 	.params = {
+// 		.bd_number = 1,
+// 		.transfer_type = per_2_emi,
+// 		.watermark_level = SDMA_RXFIFO_WATERMARK,
+// 		.per_address = SSI1_BASE_ADDR + 0x8,
+// 		.event_id = DMA_REQ_SSI1_RX1,
+// 		.peripheral_type = SSI,
+// 	},
+// };
+// 
+// static struct mxc_pcm_dma_params imx_ssi1_pcm_stereo_in1 = {
+// 	.name			= "SSI1 PCM Stereo in 1",
+// 	.params = {
+// 		.bd_number = 1,
+// 		.transfer_type = per_2_emi,
+// 		.watermark_level = SDMA_RXFIFO_WATERMARK,
+// 		.per_address = SSI1_BASE_ADDR + 0xc,
+// 		.event_id = DMA_REQ_SSI1_RX2,
+// 		.peripheral_type = SSI,
+// 	},
+// };
+// 
+// static struct mxc_pcm_dma_params imx_ssi2_pcm_stereo_out0 = {
+// 	.name			= "SSI2 PCM Stereo out 0",
+// 	.params = {
+// 		.bd_number = 1,
+// 		.transfer_type = per_2_emi,
+// 		.watermark_level = SDMA_TXFIFO_WATERMARK,
+// 		.per_address = SSI2_BASE_ADDR,
+// 		.event_id = DMA_REQ_SSI2_TX1,
+// 		.peripheral_type = SSI,
+// 	},
+// };
+// 
+// static struct mxc_pcm_dma_params imx_ssi2_pcm_stereo_out1 = {
+// 	.name			= "SSI2 PCM Stereo out 1",
+// 	.params = {
+// 		.bd_number = 1,
+// 		.transfer_type = per_2_emi,
+// 		.watermark_level = SDMA_TXFIFO_WATERMARK,
+// 		.per_address = SSI2_BASE_ADDR + 0x4,
+// 		.event_id = DMA_REQ_SSI2_TX2,
+// 		.peripheral_type = SSI,
+// 	},
+// };
+// 
+// static struct mxc_pcm_dma_params imx_ssi2_pcm_stereo_in0 = {
+// 	.name			= "SSI2 PCM Stereo in 0",
+// 	.params = {
+// 		.bd_number = 1,
+// 		.transfer_type = per_2_emi,
+// 		.watermark_level = SDMA_RXFIFO_WATERMARK,
+// 		.per_address = SSI2_BASE_ADDR + 0x8,
+// 		.event_id = DMA_REQ_SSI2_RX1,
+// 		.peripheral_type = SSI,
+// 	},
+// };
+// 
+// static struct mxc_pcm_dma_params imx_ssi2_pcm_stereo_in1 = {
+// 	.name			= "SSI2 PCM Stereo in 1",
+// 	.params = {
+// 		.bd_number = 1,
+// 		.transfer_type = per_2_emi,
+// 		.watermark_level = SDMA_RXFIFO_WATERMARK,
+// 		.per_address = SSI2_BASE_ADDR + 0xc,
+// 		.event_id = DMA_REQ_SSI2_RX2,
+// 		.peripheral_type = SSI,
+// 	},
+// };
+
+static struct clk *ssi_clk0, *ssi_clk1;
+
+int get_ssi_clk(int ssi, struct device *dev)
+{
+	switch (ssi) {
+	case 0:
+		ssi_clk0 = clk_get(dev, "ssi1");
+		if (IS_ERR(ssi_clk0))
+			return PTR_ERR(ssi_clk0);
+		return 0;
+	case 1:
+		ssi_clk1 = clk_get(dev, "ssi2");
+		if (IS_ERR(ssi_clk1))
+			return PTR_ERR(ssi_clk1);
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL(get_ssi_clk);
+
+void put_ssi_clk(int ssi)
+{
+	switch (ssi) {
+	case 0:
+		clk_put(ssi_clk0);
+		ssi_clk0 = NULL;
+		break;
+	case 1:
+		clk_put(ssi_clk1);
+		ssi_clk1 = NULL;
+		break;
+	}
+}
+EXPORT_SYMBOL(put_ssi_clk);
+
+
+
+/*
+ * SSI system clock configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+	int clk_id, unsigned int freq, int dir)
+{
+	u32 scr;
+
+	SSI_DUMP();
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		scr = SSI1_SCR;
+		pr_debug("imx_ssi_set_dat_sysclk: SSI_SCR is %x\n", scr);
+	}
+	else {
+		pr_debug("imx_ssi_set_dai_sysclk: SSI2 scr\n");
+		scr = SSI2_SCR;
+	}
+
+	if (scr & SSI_SCR_SSIEN) {
+		pr_debug("imx_ssi_set_dat_sysclk: ERROR SSI IS ALREADY ENABLED\n");	
+		return 0;	
+	}
+
+	switch (clk_id) {
+	case IMX_SSP_SYS_CLK:
+		if (dir == SND_SOC_CLOCK_OUT) {
+			pr_debug("imx_ssi_set_dai_sysclk: clk is out\n");
+			scr |= SSI_SCR_SYS_CLK_EN;
+		}
+		else {
+			pr_debug("imx_ssi_set_dat_sysclk: setting clk as input\n");
+			scr &= ~SSI_SCR_SYS_CLK_EN;
+		}
+		break;
+	default:
+		pr_debug("imx_ssi_set_dai_sysclk: WRONG ARG\n");
+		return -EINVAL;
+	}
+
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		pr_debug("imx_ssi_set_dai_sysclk: writeback SSI1_SCR\n");
+		SSI1_SCR = scr;
+	}
+	else {
+		pr_debug("imx_ssi_set_dai_sysclk: writeback SSI1_SCR\n");
+		SSI2_SCR = scr;
+	}
+
+	SSI_DUMP();
+	return 0;
+}
+
+/*
+ * SSI Clock dividers
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
+	int div_id, int div)
+{
+	u32 stccr, srccr;
+
+	pr_debug("imx_ssi_set_dai_clkdiv\n");
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		if (SSI1_SCR & SSI_SCR_SSIEN)
+			return 0;
+		
+		srccr = SSI1_STCCR;
+		stccr = SSI1_STCCR;
+	} else {
+		if (SSI2_SCR & SSI_SCR_SSIEN)
+			return 0;
+			
+		srccr = SSI2_STCCR;
+		stccr = SSI2_STCCR;
+	}
+	
+	switch (div_id) {
+	case IMX_SSI_TX_DIV_2:
+		stccr &= ~SSI_STCCR_DIV2;
+		stccr |= div;
+		break;
+	case IMX_SSI_TX_DIV_PSR:
+		stccr &= ~SSI_STCCR_PSR;
+		stccr |= div;
+		break;
+	case IMX_SSI_TX_DIV_PM:
+		stccr &= ~0xff;
+		stccr |= SSI_STCCR_PM(div);
+		break;
+	case IMX_SSI_RX_DIV_2:
+		stccr &= ~SSI_STCCR_DIV2;
+		stccr |= div;
+		break;
+	case IMX_SSI_RX_DIV_PSR:
+		stccr &= ~SSI_STCCR_PSR;
+		stccr |= div;
+		break;
+	case IMX_SSI_RX_DIV_PM:
+		stccr &= ~0xff;
+		stccr |= SSI_STCCR_PM(div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		SSI1_STCCR = stccr;
+		SSI1_SRCCR = srccr;
+	} else {
+		SSI2_STCCR = stccr;
+		SSI2_SRCCR = srccr;
+	}
+	return 0;
+}
+
+/*
+ * SSI Network Mode or TDM slots configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ */
+static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
+	unsigned int mask, int slots)
+{
+	u32 stmsk, srmsk, stccr;
+
+	SSI_DUMP();
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		if (SSI1_SCR & SSI_SCR_SSIEN) {
+			pr_debug("imx_ssi_set_dai_tdm_slot: ERROR SSI ALREADY ACTIVE\n");
+			return 0;
+		}
+		stccr = SSI1_STCCR;
+	} else {
+		if (SSI2_SCR & SSI_SCR_SSIEN)
+			return 0;
+		stccr = SSI2_STCCR;
+	}
+	
+	stmsk = srmsk = mask;
+	stccr &= ~SSI_STCCR_DC_MASK;
+	stccr |= SSI_STCCR_DC(slots - 1);
+
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		SSI1_STMSK = stmsk;
+		SSI1_SRMSK = srmsk;
+		SSI1_SRCCR = SSI1_STCCR = stccr;
+	} else {
+		SSI2_STMSK = stmsk;
+		SSI2_SRMSK = srmsk;
+		SSI2_SRCCR = SSI2_STCCR = stccr;
+	}
+	SSI_DUMP();
+	return 0;
+}
+
+/*
+ * SSI DAI format configuration.
+ * Should only be called when port is inactive (i.e. SSIEN = 0).
+ * Note: We don't use the I2S modes but instead manually configure the
+ * SSI for I2S.
+ */
+static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+		unsigned int fmt)
+{
+	u32 stcr = 0, srcr = 0, scr;
+
+
+	SSI_DUMP();
+
+	/* This is done to avoid this function to modify previous set values in stcr */
+	stcr = SSI1_STCR;
+
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+		scr = SSI1_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
+	else
+		scr = SSI2_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
+		
+	if (scr & SSI_SCR_SSIEN) {
+		pr_debug("imx_ssi_set_dai_fmt: ERROR SSI ALREADY ACTIVE\n");
+		return 0;
+	}
+
+	/* DAI mode */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		/* data on rising edge of bclk, frame low 1clk before data */ 
+		stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
+		srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		/* data on rising edge of bclk, frame high with data */ 
+		stcr |= SSI_STCR_TXBIT0;
+		srcr |= SSI_SRCR_RXBIT0;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		/* data on rising edge of bclk, frame high with data */ 
+		stcr |= SSI_STCR_TFSL;
+		srcr |= SSI_SRCR_RFSL;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		/* data on rising edge of bclk, frame high 1clk before data */ 
+		stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS;
+		srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS;
+		break;
+	}
+
+	/* DAI clock inversion */
+	switch(fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_IB_IF:
+		stcr |= SSI_STCR_TFSI;
+		stcr &= ~SSI_STCR_TSCKP;
+		srcr |= SSI_SRCR_RFSI;
+		srcr &= ~SSI_SRCR_RSCKP;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
+		srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI);
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		pr_debug("SSI Invertion of FRAME CLK\n");
+		stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
+		srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP;
+		break;
+	case SND_SOC_DAIFMT_NB_NF:
+		stcr &= ~SSI_STCR_TFSI;
+		stcr |= SSI_STCR_TSCKP;
+		srcr &= ~SSI_SRCR_RFSI;
+		srcr |= SSI_SRCR_RSCKP;
+		break;
+	}
+
+	/* DAI clock master masks */
+	switch(fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
+		srcr |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		stcr |= SSI_STCR_TFDIR;
+		srcr |= SSI_SRCR_RFDIR;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		stcr |= SSI_STCR_TXDIR;
+		srcr |= SSI_SRCR_RXDIR;
+		break;
+	}
+
+	/* sync */
+	if (!(fmt & SND_SOC_DAIFMT_ASYNC))
+		scr |= SSI_SCR_SYN;
+		
+	/* tdm - only for stereo atm */
+	if (fmt & SND_SOC_DAIFMT_TDM)
+		scr |= SSI_SCR_NET;
+
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		SSI1_STCR = stcr;
+		SSI1_SRCR = srcr;
+		SSI1_SCR = scr;
+	} else {
+		SSI2_STCR = stcr;
+		SSI2_SRCR = srcr;
+		SSI2_SCR = scr;
+	}
+	SSI_DUMP();
+	return 0;
+}
+
+static int imx_ssi_startup(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+
+	SSI_DUMP();
+	/* we cant really change any SSI values after SSI is enabled 
+	 * need to fix in software for max flexibility - lrg */
+	if (cpu_dai->active) {
+		pr_debug("imx_ssi_startup: ERROR SSI ALREADY ACTIVE\n");
+		return 0;
+	}
+
+	/* reset the SSI port - Sect 45.4.4 */
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		
+		if (!ssi_clk0)
+			return -EINVAL;
+
+		if (ssi_active[SSI1_PORT]++) {
+			pr_debug("imx_ssi_startup: exitting before reset\n");
+			return 0;
+		}
+
+		SSI1_SCR = 0; //SSI1 Reset
+// 		clk_enable(ssi_clk0); //FIXME 	mxc_clks_enable(SSI1_BAUD);
+		clk_disable(ssi_clk0);
+		
+		
+		/* BIG FAT WARNING
+		 * SDMA FIFO watermark must == SSI FIFO watermark for 
+		 * best results.
+		 */
+		SSI1_SFCSR = SSI_SFCSR_RFWM1(SDMA_RXFIFO_WATERMARK) |
+			SSI_SFCSR_RFWM0(SDMA_RXFIFO_WATERMARK) |
+			SSI_SFCSR_TFWM1(SDMA_TXFIFO_WATERMARK) |
+			SSI_SFCSR_TFWM0(SDMA_TXFIFO_WATERMARK);
+	} else {
+		
+		if (!ssi_clk1)
+			return -EINVAL;
+
+		if (ssi_active[SSI2_PORT]++)
+			return 0;
+		
+		SSI2_SCR = 0;
+		clk_enable(ssi_clk1); //FIXME mxc_clks_enable(SSI2_BAUD);
+		
+		/* above warning applies here too */
+		SSI2_SFCSR = SSI_SFCSR_RFWM1(SDMA_RXFIFO_WATERMARK) |
+			SSI_SFCSR_RFWM0(SDMA_RXFIFO_WATERMARK) |
+			SSI_SFCSR_TFWM1(SDMA_TXFIFO_WATERMARK) |
+			SSI_SFCSR_TFWM0(SDMA_TXFIFO_WATERMARK);
+	}
+
+	SSI_DUMP();
+	return 0;
+}
+
+static int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	u32 stccr, stcr, sier;
+
+	pr_debug("imx_ssi_hw_tx_params enters here\n");
+	SSI_DUMP();
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		stccr = SSI1_STCCR & ~SSI_STCCR_WL_MASK;
+		stcr = SSI1_STCR;
+		sier = SSI1_SIER;
+	} else {
+		stccr = SSI2_STCCR & ~SSI_STCCR_WL_MASK;
+		stcr = SSI2_STCR;
+		sier = SSI2_SIER;
+	}
+
+	/* DAI data (word) size */
+	switch(params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		stccr |= SSI_STCCR_WL(16);
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		stccr |= SSI_STCCR_WL(20);
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		stccr |= SSI_STCCR_WL(24);
+		break;
+	}
+
+	/* enable interrupts */
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+		stcr |= SSI_STCR_TFEN0;
+	else
+		stcr |= SSI_STCR_TFEN1;
+	sier |= SSI_SIER_TDMAE;
+
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		SSI1_STCR = stcr;
+		SSI1_STCCR = stccr;
+		SSI1_SIER = sier;
+	} else {
+		SSI2_STCR = stcr;
+		SSI2_STCCR = stccr;
+		SSI2_SIER = sier;
+	}
+
+	SSI_DUMP();
+	return 0;
+}
+
+static int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	u32 srccr, srcr, sier;
+
+	pr_debug("imx_ssi_hw_rx_params enters here\n");
+	SSI_DUMP();
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		srccr = SSI1_SRCCR & ~SSI_SRCCR_WL_MASK;
+		srcr = SSI1_SRCR;
+		sier = SSI1_SIER;
+	} else {
+		srccr = SSI2_SRCCR & ~SSI_SRCCR_WL_MASK;
+		srcr = SSI2_SRCR;
+		sier = SSI2_SIER;
+	}
+
+	/* DAI data (word) size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		srccr |= SSI_SRCCR_WL(16);
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		srccr |= SSI_SRCCR_WL(20);
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		srccr |= SSI_SRCCR_WL(24);
+		break;
+	}
+
+	/* enable interrupts */
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+		srcr |= SSI_SRCR_RFEN0;
+	else
+		srcr |= SSI_SRCR_RFEN1;
+	sier |= SSI_SIER_RDMAE;
+	
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+		SSI1_SRCR = srcr;
+		SSI1_SRCCR = srccr;
+		SSI1_SIER = sier;
+	} else {
+		SSI2_SRCR = srcr;
+		SSI2_SRCCR = srccr;
+		SSI2_SIER = sier;
+	}
+	SSI_DUMP();
+	return 0;
+}
+
+/*
+ * Should only be called when port is inactive (i.e. SSIEN = 0),
+ * although can be called multiple times by upper layers.
+ */
+static int imx_ssi_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+	SSI_DUMP();
+	/* Tx/Rx config */
+// 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+// 		/* set up DMA params */
+// 		switch (cpu_dai->id) {
+// 		case IMX_DAI_SSI0:
+// //FIXME this uses sdma api			cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out0;
+// 			break;
+// 		case IMX_DAI_SSI1:
+// //FIXME this uses sdma api			cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out1;
+// 			break;
+// 		case IMX_DAI_SSI2:
+// //FIXME this uses sdma api			cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out0;
+// 			break;
+// 		case IMX_DAI_SSI3:
+// //FIXME this uses sdma api			cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out1;
+// 			break;
+// 		}
+// 		
+		/* cant change any parameters when SSI is running */
+		if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+			if (SSI1_SCR & SSI_SCR_SSIEN) {
+				pr_debug("imx_ssi_hw_params: ERROR SSI IS ALREADY ACTIVE\n");
+				return 0;
+			}
+		} else {
+			if (SSI2_SCR & SSI_SCR_SSIEN)
+				return 0;
+		}
+// 		SSI_DUMP();
+		/*return */imx_ssi_hw_tx_params(substream, params);
+// 	} else {
+// 		/* set up DMA params */
+// 		switch (cpu_dai->id) {
+// 		case IMX_DAI_SSI0:
+// //FIXME this uses sdma api			cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in0;
+// 			break;
+// 		case IMX_DAI_SSI1:
+// //FIXME this uses sdma api			cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in1;
+// 			break;
+// 		case IMX_DAI_SSI2:
+// //FIXME this uses sdma api			cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in0;
+// 			break;
+// 		case IMX_DAI_SSI3:
+// //FIXME this uses sdma api			cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in1;
+// 			break;
+// 		}
+// 		
+// 		/* cant change any parameters when SSI is running */
+// 		if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+// 			if (SSI1_SCR & SSI_SCR_SSIEN)
+// 				return 0;
+// 		} else {
+// 			if (SSI2_SCR & SSI_SCR_SSIEN)
+// 				return 0;
+// 		}
+		return imx_ssi_hw_rx_params(substream, params);
+// 	}
+}
+
+static int imx_ssi_prepare(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+// 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+// 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+// 	u32 scr;
+	
+	SSI_DUMP();
+	/* enable the SSI port, note that no other port config 
+	 * should happen after SSIEN is set */
+// 	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
+// 		scr = SSI1_SCR;
+// 		SSI1_SCR = scr | SSI_SCR_SSIEN;
+// 	} else {
+// 		scr = SSI2_SCR;
+// 		SSI2_SCR = scr | SSI_SCR_SSIEN;
+// 	}
+// 	
+	clk_enable(ssi_clk0); //Fixme enable clk here to follow SSI init sequence
+
+	SSI_DUMP();
+	return 0;
+}
+
+static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
+			struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	u32 scr;
+
+	SSI_DUMP();
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+		scr = SSI1_SCR;
+	else
+		scr = SSI2_SCR;
+	
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			scr |= SSI_SCR_TE | SSI_SCR_SSIEN;
+		else
+			scr |= SSI_SCR_RE | SSI_SCR_SSIEN;
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			scr &= ~SSI_SCR_TE;
+		else
+			scr &= ~SSI_SCR_RE;
+		break;
+	default:
+		return -EINVAL;
+	}
+	
+	if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
+		SSI1_SCR = scr;
+	else
+		SSI2_SCR = scr;
+
+	SSI_DUMP();
+	return 0;
+}
+
+static void imx_ssi_shutdown(struct snd_pcm_substream *substream,
+			struct snd_soc_dai *dai)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+
+// 	SSI_DUMP();
+	/* shutdown SSI if neither Tx or Rx is active */
+	if (!cpu_dai->active) {
+
+		if (cpu_dai->id == IMX_DAI_SSI0 || 
+			cpu_dai->id == IMX_DAI_SSI2) {
+
+			if (--ssi_active[SSI1_PORT] > 1)
+				return;
+
+			SSI1_SCR = 0;
+			clk_disable(ssi_clk0); //FIXME mxc_clks_disable(SSI1_BAUD);
+		} else {
+			if (--ssi_active[SSI2_PORT])
+				return;
+			SSI2_SCR = 0;
+			clk_disable(ssi_clk1);//FIXME mxc_clks_disable(SSI2_BAUD);
+		}
+	}
+}
+
+#ifdef CONFIG_PM
+static int imx_ssi_suspend(struct platform_device *dev,
+	struct snd_soc_dai *dai)
+{
+	if(!dai->active)
+		return 0;
+
+	// do we need to disable any clocks
+
+	return 0;
+}
+
+static int imx_ssi_resume(struct platform_device *pdev,
+	struct snd_soc_dai *dai)
+{
+	if(!dai->active)
+		return 0;
+
+	// do we need to enable any clocks
+	return 0;
+}
+
+#else
+#define imx_ssi_suspend	NULL
+#define imx_ssi_resume	NULL
+#endif
+
+#define IMX_SSI_RATES \
+	(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
+	SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+	SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+	SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+	SNDRV_PCM_RATE_96000)
+
+#define IMX_SSI_BITS \
+	(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+	SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
+	.startup = imx_ssi_startup, /* OK */
+	.shutdown = imx_ssi_shutdown, /* OK */
+	.trigger = imx_ssi_trigger, /* OK */
+	.prepare = imx_ssi_prepare, /* OK */
+	.hw_params = imx_ssi_hw_params, /* BAD */
+	.set_sysclk = imx_ssi_set_dai_sysclk,
+	.set_clkdiv = imx_ssi_set_dai_clkdiv,
+	.set_fmt = imx_ssi_set_dai_fmt,
+	.set_tdm_slot = imx_ssi_set_dai_tdm_slot,
+};
+
+struct snd_soc_dai imx_ssi_pcm_dai[] = {
+{
+	.name = "imx-i2s-1-0",
+	.id = IMX_DAI_SSI0,
+	.suspend = imx_ssi_suspend,
+	.resume = imx_ssi_resume,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.formats = IMX_SSI_BITS,
+		.rates = IMX_SSI_RATES,},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.formats = IMX_SSI_BITS,
+		.rates = IMX_SSI_RATES,},
+	.ops = &imx_ssi_pcm_dai_ops,
+},
+{
+	.name = "imx-i2s-2-0",
+	.id = IMX_DAI_SSI1,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.formats = IMX_SSI_BITS,
+		.rates = IMX_SSI_RATES,},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.formats = IMX_SSI_BITS,
+		.rates = IMX_SSI_RATES,},
+	.ops = &imx_ssi_pcm_dai_ops,
+},
+{
+	.name = "imx-i2s-1-1",
+	.id = IMX_DAI_SSI2,
+	.suspend = imx_ssi_suspend,
+	.resume = imx_ssi_resume,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.formats = IMX_SSI_BITS,
+		.rates = IMX_SSI_RATES,},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.formats = IMX_SSI_BITS,
+		.rates = IMX_SSI_RATES,},
+	.ops = &imx_ssi_pcm_dai_ops,
+},
+{
+	.name = "imx-i2s-2-1",
+	.id = IMX_DAI_SSI3,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.formats = IMX_SSI_BITS,
+		.rates = IMX_SSI_RATES,},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.formats = IMX_SSI_BITS,
+		.rates = IMX_SSI_RATES,},
+	.ops = &imx_ssi_pcm_dai_ops,
+},
+};
+EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
+
+static int __init imx_ssi_init(void)
+{
+	return snd_soc_register_dais(imx_ssi_pcm_dai, ARRAY_SIZE(imx_ssi_pcm_dai));
+}
+
+static void __exit imx_ssi_exit(void)
+{
+	snd_soc_unregister_dais(imx_ssi_pcm_dai, ARRAY_SIZE(imx_ssi_pcm_dai));
+}
+
+module_init(imx_ssi_init);
+module_exit(imx_ssi_exit);
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, liam.girdwood@xxxxxxxxxxxxxxxx, www.wolfsonmicro.com");
+MODULE_DESCRIPTION("i.MX ASoC I2S driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/imx/imx27-ssi.h b/sound/soc/imx/imx27-ssi.h
new file mode 100644
index 0000000..6a6346e
--- /dev/null
+++ b/sound/soc/imx/imx27-ssi.h
@@ -0,0 +1,226 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _IMX_SSI_H
+#define _IMX_SSI_H
+
+#include <mach/hardware.h>
+
+/* SSI regs definition - MOVE to asm/arch when stable */
+#define SSI1_IO_BASE_ADDR	IO_ADDRESS(SSI1_BASE_ADDR)
+#define SSI2_IO_BASE_ADDR	IO_ADDRESS(SSI2_BASE_ADDR)
+
+//FIXME this is only for debugging
+#define IMX27_CSCR	*((volatile u32 *)(IO_ADDRESS(CCM_BASE_ADDR)))
+#define IMX27_PCDR0	*((volatile u32 *)(IO_ADDRESS(0x10027018)))
+#define IMX27_PCDR1	*((volatile u32 *)(IO_ADDRESS(0x1002701C)))
+#define IMX27_PCCR0	*((volatile u32 *)(IO_ADDRESS(0x10027020)))
+#define IMX27_PCCR1	*((volatile u32 *)(IO_ADDRESS(0x10027024)))
+#define IMX27_MPLCTL0	*((volatile u32 *)(IO_ADDRESS(0x10027004)))
+// #define IMX27_UPCTL0	*((volatile u32 *)(IO_ADDRESS(0x10027024)))
+
+
+
+#define SSI1_STX0	*((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x00))
+#define SSI1_STX1   *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x04))
+#define SSI1_SRX0   *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x08))
+#define SSI1_SRX1   *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x0c))
+#define SSI1_SCR    *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x10))
+#define SSI1_SISR   *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x14))
+#define SSI1_SIER   *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x18))
+#define SSI1_STCR   *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x1c))
+#define SSI1_SRCR   *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x20))
+#define SSI1_STCCR  *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x24))
+#define SSI1_SRCCR  *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x28))
+#define SSI1_SFCSR  *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x2c))
+#define SSI1_STR    *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x30))
+#define SSI1_SOR    *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x34))
+#define SSI1_SACNT  *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x38))
+#define SSI1_SACADD *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x3c))
+#define SSI1_SACDAT *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x40))
+#define SSI1_SATAG  *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x44))
+#define SSI1_STMSK  *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x48))
+#define SSI1_SRMSK  *((volatile u32 *)(SSI1_IO_BASE_ADDR + 0x4c))
+
+#define SSI2_STX0	*((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x00))
+#define SSI2_STX1   *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x04))
+#define SSI2_SRX0   *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x08))
+#define SSI2_SRX1   *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x0c))
+#define SSI2_SCR    *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x10))
+#define SSI2_SISR   *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x14))
+#define SSI2_SIER   *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x18))
+#define SSI2_STCR   *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x1c))
+#define SSI2_SRCR   *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x20))
+#define SSI2_STCCR  *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x24))
+#define SSI2_SRCCR  *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x28))
+#define SSI2_SFCSR  *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x2c))
+#define SSI2_STR    *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x30))
+#define SSI2_SOR    *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x34))
+#define SSI2_SACNT  *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x38))
+#define SSI2_SACADD *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x3c))
+#define SSI2_SACDAT *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x40))
+#define SSI2_SATAG  *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x44))
+#define SSI2_STMSK  *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x48))
+#define SSI2_SRMSK  *((volatile u32 *)(SSI2_IO_BASE_ADDR + 0x4c))
+
+#define SSI_SCR_CLK_IST        (1 << 9)
+#define SSI_SCR_TCH_EN         (1 << 8)
+#define SSI_SCR_SYS_CLK_EN     (1 << 7)
+#define SSI_SCR_I2S_MODE_NORM  (0 << 5)
+#define SSI_SCR_I2S_MODE_MSTR  (1 << 5)
+#define SSI_SCR_I2S_MODE_SLAVE (2 << 5)
+#define SSI_SCR_SYN            (1 << 4)
+#define SSI_SCR_NET            (1 << 3)
+#define SSI_SCR_RE             (1 << 2)
+#define SSI_SCR_TE             (1 << 1)
+#define SSI_SCR_SSIEN          (1 << 0)
+
+#define SSI_SISR_CMDAU         (1 << 18)
+#define SSI_SISR_CMDDU         (1 << 17)
+#define SSI_SISR_RXT           (1 << 16)
+#define SSI_SISR_RDR1          (1 << 15)
+#define SSI_SISR_RDR0          (1 << 14)
+#define SSI_SISR_TDE1          (1 << 13)
+#define SSI_SISR_TDE0          (1 << 12)
+#define SSI_SISR_ROE1          (1 << 11)
+#define SSI_SISR_ROE0          (1 << 10)
+#define SSI_SISR_TUE1          (1 << 9)
+#define SSI_SISR_TUE0          (1 << 8)
+#define SSI_SISR_TFS           (1 << 7)
+#define SSI_SISR_RFS           (1 << 6)
+#define SSI_SISR_TLS           (1 << 5)
+#define SSI_SISR_RLS           (1 << 4)
+#define SSI_SISR_RFF1          (1 << 3)
+#define SSI_SISR_RFF0          (1 << 2)
+#define SSI_SISR_TFE1          (1 << 1)
+#define SSI_SISR_TFE0          (1 << 0)
+
+#define SSI_SIER_RDMAE         (1 << 22)
+#define SSI_SIER_RIE           (1 << 21)
+#define SSI_SIER_TDMAE         (1 << 20)
+#define SSI_SIER_TIE           (1 << 19)
+#define SSI_SIER_CMDAU_EN      (1 << 18)
+#define SSI_SIER_CMDDU_EN      (1 << 17)
+#define SSI_SIER_RXT_EN        (1 << 16)
+#define SSI_SIER_RDR1_EN       (1 << 15)
+#define SSI_SIER_RDR0_EN       (1 << 14)
+#define SSI_SIER_TDE1_EN       (1 << 13)
+#define SSI_SIER_TDE0_EN       (1 << 12)
+#define SSI_SIER_ROE1_EN       (1 << 11)
+#define SSI_SIER_ROE0_EN       (1 << 10)
+#define SSI_SIER_TUE1_EN       (1 << 9)
+#define SSI_SIER_TUE0_EN       (1 << 8)
+#define SSI_SIER_TFS_EN        (1 << 7)
+#define SSI_SIER_RFS_EN        (1 << 6)
+#define SSI_SIER_TLS_EN        (1 << 5)
+#define SSI_SIER_RLS_EN        (1 << 4)
+#define SSI_SIER_RFF1_EN       (1 << 3)
+#define SSI_SIER_RFF0_EN       (1 << 2)
+#define SSI_SIER_TFE1_EN       (1 << 1)
+#define SSI_SIER_TFE0_EN       (1 << 0)
+
+#define SSI_STCR_TXBIT0        (1 << 9)
+#define SSI_STCR_TFEN1         (1 << 8)
+#define SSI_STCR_TFEN0         (1 << 7)
+#define SSI_STCR_TFDIR         (1 << 6)
+#define SSI_STCR_TXDIR         (1 << 5)
+#define SSI_STCR_TSHFD         (1 << 4)
+#define SSI_STCR_TSCKP         (1 << 3)
+#define SSI_STCR_TFSI          (1 << 2)
+#define SSI_STCR_TFSL          (1 << 1)
+#define SSI_STCR_TEFS          (1 << 0)
+
+#define SSI_SRCR_RXBIT0        (1 << 9)
+#define SSI_SRCR_RFEN1         (1 << 8)
+#define SSI_SRCR_RFEN0         (1 << 7)
+#define SSI_SRCR_RFDIR         (1 << 6)
+#define SSI_SRCR_RXDIR         (1 << 5)
+#define SSI_SRCR_RSHFD         (1 << 4)
+#define SSI_SRCR_RSCKP         (1 << 3)
+#define SSI_SRCR_RFSI          (1 << 2)
+#define SSI_SRCR_RFSL          (1 << 1)
+#define SSI_SRCR_REFS          (1 << 0)
+
+#define SSI_STCCR_DIV2         (1 << 18)
+#define SSI_STCCR_PSR          (1 << 15)
+#define SSI_STCCR_WL(x)        ((((x) - 2) >> 1) << 13)
+#define SSI_STCCR_DC(x)        (((x) & 0x1f) << 8)
+#define SSI_STCCR_PM(x)        (((x) & 0xff) << 0)
+#define SSI_STCCR_WL_MASK        (0xf << 13)
+#define SSI_STCCR_DC_MASK        (0x1f << 8)
+#define SSI_STCCR_PM_MASK        (0xff << 0)
+
+#define SSI_SRCCR_DIV2         (1 << 18)
+#define SSI_SRCCR_PSR          (1 << 15)
+#define SSI_SRCCR_WL(x)        ((((x) - 2) >> 1) << 13)
+#define SSI_SRCCR_DC(x)        (((x) & 0x1f) << 8)
+#define SSI_SRCCR_PM(x)        (((x) & 0xff) << 0)
+#define SSI_SRCCR_WL_MASK        (0xf << 13)
+#define SSI_SRCCR_DC_MASK        (0x1f << 8)
+#define SSI_SRCCR_PM_MASK        (0xff << 0)
+
+
+#define SSI_SFCSR_RFCNT1(x)   (((x) & 0xf) << 28)
+#define SSI_SFCSR_TFCNT1(x)   (((x) & 0xf) << 24)
+#define SSI_SFCSR_RFWM1(x)    (((x) & 0xf) << 20)
+#define SSI_SFCSR_TFWM1(x)    (((x) & 0xf) << 16)
+#define SSI_SFCSR_RFCNT0(x)   (((x) & 0xf) << 12)
+#define SSI_SFCSR_TFCNT0(x)   (((x) & 0xf) <<  8)
+#define SSI_SFCSR_RFWM0(x)    (((x) & 0xf) <<  4)
+#define SSI_SFCSR_TFWM0(x)    (((x) & 0xf) <<  0)
+
+#define SSI_STR_TEST          (1 << 15)
+#define SSI_STR_RCK2TCK       (1 << 14)
+#define SSI_STR_RFS2TFS       (1 << 13)
+#define SSI_STR_RXSTATE(x)    (((x) & 0xf) << 8)
+#define SSI_STR_TXD2RXD       (1 <<  7)
+#define SSI_STR_TCK2RCK       (1 <<  6)
+#define SSI_STR_TFS2RFS       (1 <<  5)
+#define SSI_STR_TXSTATE(x)    (((x) & 0xf) << 0)
+
+#define SSI_SOR_CLKOFF        (1 << 6)
+#define SSI_SOR_RX_CLR        (1 << 5)
+#define SSI_SOR_TX_CLR        (1 << 4)
+#define SSI_SOR_INIT          (1 << 3)
+#define SSI_SOR_WAIT(x)       (((x) & 0x3) << 1)
+#define SSI_SOR_SYNRST        (1 << 0)
+
+#define SSI_SACNT_FRDIV(x)    (((x) & 0x3f) << 5)
+#define SSI_SACNT_WR          (x << 4)
+#define SSI_SACNT_RD          (x << 3)
+#define SSI_SACNT_TIF         (x << 2)
+#define SSI_SACNT_FV          (x << 1)
+#define SSI_SACNT_AC97EN      (x << 0)
+
+/* SDMA watermarks for FIFO's */
+#define SDMA_TXFIFO_WATERMARK				0x4
+#define SDMA_RXFIFO_WATERMARK				0x4
+
+/* i.MX DAI SSP ID's */
+#define IMX_DAI_SSI0			0 /* SSI1 FIFO 0 */
+#define IMX_DAI_SSI1			1 /* SSI1 FIFO 1 */
+#define IMX_DAI_SSI2			2 /* SSI2 FIFO 0 */
+#define IMX_DAI_SSI3			3 /* SSI2 FIFO 1 */
+
+/* SSI clock sources */
+#define IMX_SSP_SYS_CLK		0
+
+/* SSI audio dividers */
+#define IMX_SSI_TX_DIV_2			0
+#define IMX_SSI_TX_DIV_PSR			1
+#define IMX_SSI_TX_DIV_PM			2
+#define IMX_SSI_RX_DIV_2			3
+#define IMX_SSI_RX_DIV_PSR			4
+#define IMX_SSI_RX_DIV_PM			5
+
+
+/* SSI Div 2 */
+#define IMX_SSI_DIV_2_OFF		~SSI_STCCR_DIV2
+#define IMX_SSI_DIV_2_ON		SSI_STCCR_DIV2
+
+extern struct snd_soc_dai imx_ssi_pcm_dai[4];
+
+#endif
diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c
new file mode 100644
index 0000000..01f42da
--- /dev/null
+++ b/sound/soc/imx/mx27vis_wm8974.c
@@ -0,0 +1,357 @@
+/*
+ * mx27vis_wm8974.c  --  SoC audio for mx27vis
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@xxxxxxxxxxxxxxxx or linux@xxxxxxxxxxxxxxxx
+ *
+ *  mx27vis audio amplifier code taken from arch/arm/mach-pxa/mx27vis.c
+ *  Copyright:	MontaVista Software Inc.
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  Revision history
+ *    30th Oct 2005   Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+// #include <mach/hardware.h>
+
+#include "../codecs/wm8974.h"
+#include "imx27-pcm.h"
+#include "imx27-ssi.h"
+#include <mach/gpio.h>
+#include <mach/iomux.h>
+
+#define IGNORED_ARG 0
+
+static struct snd_soc_card mx27vis;
+
+/**
+  * This function connects SSI1 (HPCR1) as slave to 
+  * SSI1 external signals (PPCR1).
+  * As slave, HPCR1 must set TFSDIR and TCLKDIR as inputs from 
+  * port 4
+  */
+void audmux_connect_1_4(void)
+{
+	pr_debug("AUDMUX: normal operation mode\n");
+
+	/* Reset HPCR1 and PPCR1 */
+
+	DAM_HPCR1 = 0x00000000;
+	DAM_PPCR1 = 0x00000000;
+
+	/* set to synchronous */
+	DAM_HPCR1 |= AUDMUX_HPCR_SYN;
+	DAM_PPCR1 |= AUDMUX_PPCR_SYN;
+
+
+	/* set Rx sources 1 <--> 4 */
+	DAM_HPCR1 |= AUDMUX_HPCR_RXDSEL(3); //port 4
+	DAM_PPCR1 |= AUDMUX_PPCR_RXDSEL(0); //port 1
+
+	
+	/* set Tx frame and Clock direction and source  4 --> 1 output */
+	DAM_HPCR1 |= AUDMUX_HPCR_TFSDIR | AUDMUX_HPCR_TCLKDIR;
+	DAM_HPCR1 |= AUDMUX_HPCR_TFCSEL(3); //TxDS and TxCclk from port 4
+
+	return;
+}
+
+static int mx27vis_hifi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+	unsigned int pll_out = 0, bclk = 0, fmt = 0, mclk = 0;
+	int ret = 0;
+
+	/*
+	 * The WM8974 is better at generating accurate audio clocks than the
+	 * MX31 SSI controller, so we will use it as master when we can.
+	 */
+// 	printk("\n\n\n\nmx27vs_hifi_hw_params -> params_rate(params) = %d \n\n\n\n", params_rate(params));
+	switch (params_rate(params)) {
+	case 8000:
+		fmt = SND_SOC_DAIFMT_CBM_CFM; 
+		mclk = WM8974_MCLKDIV_12;
+		pll_out = 24576000;
+		break;
+	case 16000:
+		fmt = SND_SOC_DAIFMT_CBM_CFM;
+		pll_out = 12288000;
+		break;
+	case 48000:
+		fmt = SND_SOC_DAIFMT_CBM_CFM;
+		bclk = WM8974_BCLKDIV_4;
+		pll_out = 12288000;
+		break; 
+	case 96000:
+		fmt = SND_SOC_DAIFMT_CBM_CFM;
+		bclk = WM8974_BCLKDIV_2;
+		pll_out = 12288000;
+		break;
+	case 11025:
+		fmt = SND_SOC_DAIFMT_CBM_CFM;
+		bclk = WM8974_BCLKDIV_16;
+		pll_out = 11289600;
+		break;
+	case 22050:
+		fmt = SND_SOC_DAIFMT_CBM_CFM;
+		bclk = WM8974_BCLKDIV_8;
+		pll_out = 11289600;
+		break;
+	case 44100:
+		fmt = SND_SOC_DAIFMT_CBM_CFM;
+		bclk = WM8974_BCLKDIV_4;
+		mclk = WM8974_MCLKDIV_2;
+		pll_out = 11289600;
+		break;
+	case 88200:
+		fmt = SND_SOC_DAIFMT_CBM_CFM;
+		bclk = WM8974_BCLKDIV_2;
+		pll_out = 11289600;
+		break;
+	}
+
+	/* set codec DAI configuration */
+	ret = codec_dai->ops->set_fmt(codec_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_SYNC | fmt);
+	if (ret < 0) {
+		printk("\n\nReturning error from codec DAI configuration\n\n");
+		return ret;
+	}
+
+	/* set cpu DAI configuration */
+	ret = cpu_dai->ops->set_fmt(cpu_dai,
+		SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_SYNC | fmt );
+	if (ret < 0) {
+		printk("\n\nReturning error from cpu DAI configuration\n\n");
+		return ret;
+	}
+
+	/* Put DC field of STCCR to 1 (not zero) */
+	ret = cpu_dai->ops->set_tdm_slot(cpu_dai,0,2);
+
+	/* set the SSI system clock as input (unused) */
+// 	printk("mx27vis_wm8974: set SSI system clock as input\n");
+	ret = cpu_dai->ops->set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
+		SND_SOC_CLOCK_IN); 
+	if (ret < 0) {
+		printk("\n\nReturning error when setting system SSI clk\n\n");
+		return ret;
+	}
+
+	/* set codec BCLK division for sample rate */
+	ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_BCLKDIV, bclk);
+	if (ret < 0) {
+		printk("\n\nReturning error when setting BCLK division\n");
+		return ret;
+	}
+
+
+	/* codec PLL input is 25 MHz */
+	ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, 25000000, pll_out);
+	if (ret < 0) {
+		printk("\n\nReturning error when setting PLL input\n");
+		return ret;
+	}
+
+	/*set codec MCLK division for sample rate */
+	ret = codec_dai->ops->set_clkdiv(codec_dai, WM8974_MCLKDIV, mclk);
+	if (ret < 0) {
+		printk("\n\nReturning error when setting MCLK division\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int mx27vis_hifi_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+
+	/* disable the PLL */
+	return codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, 0, 0);
+}
+
+/*
+ * mx27vis WM8974 HiFi DAI opserations.
+ */
+static struct snd_soc_ops mx27vis_hifi_ops = {
+	.hw_params = mx27vis_hifi_hw_params,
+	.hw_free = mx27vis_hifi_hw_free,
+};
+
+
+static int mx27vis_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	return 0;
+}
+
+static int mx27vis_resume(struct platform_device *pdev)
+{
+	return 0;
+}
+
+extern int get_ssi_clk(int ssi, struct device *dev);
+extern void put_ssi_clk(int ssi);
+
+static int mx27vis_probe(struct platform_device *pdev)
+{
+	int ret=0;
+#ifdef CONFIG_SND_SOC_MX27_SSI1
+	ret = get_ssi_clk(0, &pdev->dev);
+#else
+	ret = get_ssi_clk(1, &pdev->dev);
+#endif
+	if (ret < 0) {
+		printk(KERN_ERR "%s: cant get ssi clock\n", __func__);
+		return ret;
+	}
+
+
+	return 0;
+}
+
+static int mx27vis_remove(struct platform_device *pdev)
+{
+#ifdef CONFIG_SND_SOC_MX27_SSI1
+	put_ssi_clk(0);
+#else
+	put_ssi_clk(1);
+#endif
+	return 0;
+}
+
+static struct snd_soc_dai_link mx27vis_dai[] = {
+{ /* Hifi Playback - for similatious use with voice below */
+	.name = "WM8974",
+	.stream_name = "WM8974 HiFi",
+#ifdef CONFIG_SND_SOC_MX27_SSI1
+	.cpu_dai = &imx_ssi_pcm_dai[0], //FIXME change array element to change ssi dai
+#else
+	.cpu_dai = &imx_ssi_pcm_dai[1],
+#endif
+	.codec_dai = &wm8974_dai,
+	.ops = &mx27vis_hifi_ops,
+},
+};
+
+static struct snd_soc_card mx27vis = {
+	.name = "mx27vis",
+	.platform = &imx27_soc_platform,
+	.probe = mx27vis_probe,
+	.remove = mx27vis_remove,
+	.suspend_pre = mx27vis_suspend,
+	.resume_post = mx27vis_resume,
+	.dai_link = mx27vis_dai,
+	.num_links = ARRAY_SIZE(mx27vis_dai),
+};
+/*
+static struct wm8974_setup_data mx27vis_wm8974_setup = {
+	.i2c_address = 0x1a,
+};*/
+
+static struct snd_soc_device mx27vis_snd_devdata = {
+// 	.machine = &mx27vis,
+	.card = &mx27vis,
+// 	.platform = &imx27_soc_platform,
+	.codec_dev = &soc_codec_dev_wm8974,
+// 	.codec_data = &mx27vis_wm8974_setup,
+};
+
+static struct platform_device *mx27vis_snd_device;
+
+/* Temporal definition of board specific behaviour */
+void gpio_ssi_active(int ssi_num)
+{
+	int ret = 0;
+
+	unsigned int ssi1_pins[] = {
+		PC20_PF_SSI1_FS,
+		PC21_PF_SSI1_RXD,
+		PC22_PF_SSI1_TXD,
+		PC23_PF_SSI1_CLK,
+	};
+	unsigned int ssi2_pins[] = {
+		PC24_PF_SSI2_FS,
+		PC25_PF_SSI2_RXD,
+		PC26_PF_SSI2_TXD,
+		PC27_PF_SSI2_CLK,
+	};
+	if(ssi_num == 0) 
+		ret = mxc_gpio_setup_multiple_pins(ssi1_pins,
+				ARRAY_SIZE(ssi1_pins), "USB OTG");
+	else
+		ret = mxc_gpio_setup_multiple_pins(ssi2_pins,
+				ARRAY_SIZE(ssi2_pins), "USB OTG");
+	if(ret)
+		printk(KERN_ERR "Error requesting ssi %x pins\n", ssi_num);
+}
+
+
+static int __init mx27vis_init(void)
+{
+	int ret;
+
+	mx27vis_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!mx27vis_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(mx27vis_snd_device, &mx27vis_snd_devdata);
+	mx27vis_snd_devdata.dev = &mx27vis_snd_device->dev;
+	ret = platform_device_add(mx27vis_snd_device);
+
+	if (ret) {
+		printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+		platform_device_put(mx27vis_snd_device);
+	}
+
+#ifdef CONFIG_SND_SOC_MX27_SSI1
+	printk("ASoC: Activating SSI1\n");
+	gpio_ssi_active(0); //SSI1 active
+
+	/* WM8974 uses SSI1 (HPCR1) via AUDMUX port 4 for audio (PPCR1) */
+	audmux_connect_1_4();
+
+#else
+	printk("ASoC: Activating SSI2\n");
+	gpio_ssi_active(1); //SSI2 active
+	
+#endif
+	return ret;
+}
+
+static void __exit mx27vis_exit(void)
+{
+#ifdef CONFIG_SND_SOC_MX27_SSI1
+// 	gpio_ssi_inactive(0); //SSI1 inactive
+#else
+// 	gpio_ssi_inactive(1); //SSI2 inactive
+#endif 
+	platform_device_unregister(mx27vis_snd_device);
+}
+
+module_init(mx27vis_init);
+module_exit(mx27vis_exit);
+
+/* Module information */
+MODULE_AUTHOR("Javier Martin, javier.martin@xxxxxxxxxxxxxxxxx, www.vista-silicon.com");
+MODULE_DESCRIPTION("ALSA SoC WM8974 mx27vis");
+MODULE_LICENSE("GPL");
commit 69d824099e04cd965920326b49d20bbb03d686c6
Author: Javier Martin <javier.martin@xxxxxxxxxxxxxxxxx>
Date:   Mon Jul 20 16:40:20 2009 +0200

    ASOC: Add wm8974 codec driver support.
    
    This has been taken from Wolfson's wm8974-upstream branch
    which means that it is quite stable and candidate to be
    released in mainline kernel.
---
 sound/soc/codecs/Kconfig  |    4 +
 sound/soc/codecs/Makefile |    2 +
 sound/soc/codecs/wm8974.c |  846 +++++++++++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/wm8974.h |   99 ++++++
 4 files changed, 951 insertions(+), 0 deletions(-)

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index b6c7f7a..ee5a14c 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_WM8900 if I2C
 	select SND_SOC_WM8903 if I2C
 	select SND_SOC_WM8971 if I2C
+	select SND_SOC_WM8974 if I2C
 	select SND_SOC_WM8990 if I2C
 	select SND_SOC_WM9705 if SND_SOC_AC97_BUS
 	select SND_SOC_WM9712 if SND_SOC_AC97_BUS
@@ -141,6 +142,9 @@ config SND_SOC_WM8903
 config SND_SOC_WM8971
 	tristate
 
+config SND_SOC_WM8974
+	tristate
+
 config SND_SOC_WM8990
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f265380..e7d7f1f 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -24,6 +24,7 @@ snd-soc-wm8753-objs := wm8753.o
 snd-soc-wm8900-objs := wm8900.o
 snd-soc-wm8903-objs := wm8903.o
 snd-soc-wm8971-objs := wm8971.o
+snd-soc-wm8974-objs := wm8974.o
 snd-soc-wm8990-objs := wm8990.o
 snd-soc-wm9705-objs := wm9705.o
 snd-soc-wm9712-objs := wm9712.o
@@ -55,6 +56,7 @@ obj-$(CONFIG_SND_SOC_WM8753)	+= snd-soc-wm8753.o
 obj-$(CONFIG_SND_SOC_WM8900)	+= snd-soc-wm8900.o
 obj-$(CONFIG_SND_SOC_WM8903)	+= snd-soc-wm8903.o
 obj-$(CONFIG_SND_SOC_WM8971)	+= snd-soc-wm8971.o
+obj-$(CONFIG_SND_SOC_WM8974)	+= snd-soc-wm8974.o
 obj-$(CONFIG_SND_SOC_WM8990)	+= snd-soc-wm8990.o
 obj-$(CONFIG_SND_SOC_WM9705)	+= snd-soc-wm9705.o
 obj-$(CONFIG_SND_SOC_WM9712)	+= snd-soc-wm9712.o
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
new file mode 100644
index 0000000..f26c8d0
--- /dev/null
+++ b/sound/soc/codecs/wm8974.c
@@ -0,0 +1,846 @@
+/*
+ * wm8974.c  --  WM8974 ALSA Soc Audio driver
+ *
+ * Copyright 2006-2009 Wolfson Microelectronics PLC.
+ *
+ * Author: Liam Girdwood <linux@xxxxxxxxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <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 "wm8974.h"
+
+static const u16 wm8974_reg[WM8974_CACHEREGNUM] = {
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0050, 0x0000, 0x0140, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x00ff,
+	0x0000, 0x0000, 0x0100, 0x00ff,
+	0x0000, 0x0000, 0x012c, 0x002c,
+	0x002c, 0x002c, 0x002c, 0x0000,
+	0x0032, 0x0000, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0038, 0x000b, 0x0032, 0x0000,
+	0x0008, 0x000c, 0x0093, 0x00e9,
+	0x0000, 0x0000, 0x0000, 0x0000,
+	0x0003, 0x0010, 0x0000, 0x0000,
+	0x0000, 0x0002, 0x0000, 0x0000,
+	0x0000, 0x0000, 0x0039, 0x0000,
+	0x0000,
+};
+
+#define WM8974_POWER1_BIASEN  0x08
+#define WM8974_POWER1_BUFIOEN 0x10
+
+struct wm8974_priv {
+	struct snd_soc_codec codec;
+	u16 reg_cache[WM8974_CACHEREGNUM];
+};
+
+static struct snd_soc_codec *wm8974_codec;
+
+/*
+ * read wm8974 register cache
+ */
+static inline unsigned int wm8974_read_reg_cache(struct snd_soc_codec *codec,
+	unsigned int reg)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg == WM8974_RESET)
+		return 0;
+	if (reg >= WM8974_CACHEREGNUM)
+		return -1;
+	return cache[reg];
+}
+
+/*
+ * write wm8974 register cache
+ */
+static inline void wm8974_write_reg_cache(struct snd_soc_codec *codec,
+	u16 reg, unsigned int value)
+{
+	u16 *cache = codec->reg_cache;
+	if (reg >= WM8974_CACHEREGNUM)
+		return;
+	cache[reg] = value;
+}
+
+/*
+ * write to the WM8974 register space
+ */
+static int wm8974_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	u8 data[2];
+
+	/* data is
+	 *   D15..D9 WM8974 register offset
+	 *   D8...D0 register data
+	 */
+	data[0] = (reg << 1) | ((value >> 8) & 0x0001);
+	data[1] = value & 0x00ff;
+
+	wm8974_write_reg_cache(codec, reg, value);
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+#define wm8974_reset(c)	wm8974_write(c, WM8974_RESET, 0)
+
+static const char *wm8974_companding[] = {"Off", "NC", "u-law", "A-law" };
+static const char *wm8974_deemp[] = {"None", "32kHz", "44.1kHz", "48kHz" };
+static const char *wm8974_eqmode[] = {"Capture", "Playback" };
+static const char *wm8974_bw[] = {"Narrow", "Wide" };
+static const char *wm8974_eq1[] = {"80Hz", "105Hz", "135Hz", "175Hz" };
+static const char *wm8974_eq2[] = {"230Hz", "300Hz", "385Hz", "500Hz" };
+static const char *wm8974_eq3[] = {"650Hz", "850Hz", "1.1kHz", "1.4kHz" };
+static const char *wm8974_eq4[] = {"1.8kHz", "2.4kHz", "3.2kHz", "4.1kHz" };
+static const char *wm8974_eq5[] = {"5.3kHz", "6.9kHz", "9kHz", "11.7kHz" };
+static const char *wm8974_alc[] = {"ALC", "Limiter" };
+
+static const struct soc_enum wm8974_enum[] = {
+	SOC_ENUM_SINGLE(WM8974_COMP, 1, 4, wm8974_companding), /* adc */
+	SOC_ENUM_SINGLE(WM8974_COMP, 3, 4, wm8974_companding), /* dac */
+	SOC_ENUM_SINGLE(WM8974_DAC,  4, 4, wm8974_deemp),
+	SOC_ENUM_SINGLE(WM8974_EQ1,  8, 2, wm8974_eqmode),
+
+	SOC_ENUM_SINGLE(WM8974_EQ1,  5, 4, wm8974_eq1),
+	SOC_ENUM_SINGLE(WM8974_EQ2,  8, 2, wm8974_bw),
+	SOC_ENUM_SINGLE(WM8974_EQ2,  5, 4, wm8974_eq2),
+	SOC_ENUM_SINGLE(WM8974_EQ3,  8, 2, wm8974_bw),
+
+	SOC_ENUM_SINGLE(WM8974_EQ3,  5, 4, wm8974_eq3),
+	SOC_ENUM_SINGLE(WM8974_EQ4,  8, 2, wm8974_bw),
+	SOC_ENUM_SINGLE(WM8974_EQ4,  5, 4, wm8974_eq4),
+	SOC_ENUM_SINGLE(WM8974_EQ5,  8, 2, wm8974_bw),
+
+	SOC_ENUM_SINGLE(WM8974_EQ5,  5, 4, wm8974_eq5),
+	SOC_ENUM_SINGLE(WM8974_ALC3,  8, 2, wm8974_alc),
+};
+
+static const char *wm8974_auxmode_text[] = { "Buffer", "Mixer" };
+
+static const struct soc_enum wm8974_auxmode =
+	SOC_ENUM_SINGLE(WM8974_INPUT,  3, 2, wm8974_auxmode_text);
+
+static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1);
+static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
+static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
+
+static const struct snd_kcontrol_new wm8974_snd_controls[] = {
+
+SOC_SINGLE("Digital Loopback Switch", WM8974_COMP, 0, 1, 0),
+
+SOC_ENUM("DAC Companding", wm8974_enum[1]),
+SOC_ENUM("ADC Companding", wm8974_enum[0]),
+
+SOC_ENUM("Playback De-emphasis", wm8974_enum[2]),
+SOC_SINGLE("DAC Inversion Switch", WM8974_DAC, 0, 1, 0),
+
+SOC_SINGLE_TLV("PCM Volume", WM8974_DACVOL, 0, 255, 0, digital_tlv),
+
+SOC_SINGLE("High Pass Filter Switch", WM8974_ADC, 8, 1, 0),
+SOC_SINGLE("High Pass Cut Off", WM8974_ADC, 4, 7, 0),
+SOC_SINGLE("ADC Inversion Switch", WM8974_COMP, 0, 1, 0),
+
+SOC_SINGLE_TLV("Capture Volume", WM8974_ADCVOL,  0, 255, 0, digital_tlv),
+
+SOC_ENUM("Equaliser Function", wm8974_enum[3]),
+SOC_ENUM("EQ1 Cut Off", wm8974_enum[4]),
+SOC_SINGLE_TLV("EQ1 Volume", WM8974_EQ1,  0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ2 Bandwith", wm8974_enum[5]),
+SOC_ENUM("EQ2 Cut Off", wm8974_enum[6]),
+SOC_SINGLE_TLV("EQ2 Volume", WM8974_EQ2,  0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ3 Bandwith", wm8974_enum[7]),
+SOC_ENUM("EQ3 Cut Off", wm8974_enum[8]),
+SOC_SINGLE_TLV("EQ3 Volume", WM8974_EQ3,  0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ4 Bandwith", wm8974_enum[9]),
+SOC_ENUM("EQ4 Cut Off", wm8974_enum[10]),
+SOC_SINGLE_TLV("EQ4 Volume", WM8974_EQ4,  0, 24, 1, eq_tlv),
+
+SOC_ENUM("Equaliser EQ5 Bandwith", wm8974_enum[11]),
+SOC_ENUM("EQ5 Cut Off", wm8974_enum[12]),
+SOC_SINGLE_TLV("EQ5 Volume", WM8974_EQ5,  0, 24, 1, eq_tlv),
+
+SOC_SINGLE("DAC Playback Limiter Switch", WM8974_DACLIM1,  8, 1, 0),
+SOC_SINGLE("DAC Playback Limiter Decay", WM8974_DACLIM1,  4, 15, 0),
+SOC_SINGLE("DAC Playback Limiter Attack", WM8974_DACLIM1,  0, 15, 0),
+
+SOC_SINGLE("DAC Playback Limiter Threshold", WM8974_DACLIM2,  4, 7, 0),
+SOC_SINGLE("DAC Playback Limiter Boost", WM8974_DACLIM2,  0, 15, 0),
+
+SOC_SINGLE("ALC Enable Switch", WM8974_ALC1,  8, 1, 0),
+SOC_SINGLE("ALC Capture Max Gain", WM8974_ALC1,  3, 7, 0),
+SOC_SINGLE("ALC Capture Min Gain", WM8974_ALC1,  0, 7, 0),
+
+SOC_SINGLE("ALC Capture ZC Switch", WM8974_ALC2,  8, 1, 0),
+SOC_SINGLE("ALC Capture Hold", WM8974_ALC2,  4, 7, 0),
+SOC_SINGLE("ALC Capture Target", WM8974_ALC2,  0, 15, 0),
+
+SOC_ENUM("ALC Capture Mode", wm8974_enum[13]),
+SOC_SINGLE("ALC Capture Decay", WM8974_ALC3,  4, 15, 0),
+SOC_SINGLE("ALC Capture Attack", WM8974_ALC3,  0, 15, 0),
+
+SOC_SINGLE("ALC Capture Noise Gate Switch", WM8974_NGATE,  3, 1, 0),
+SOC_SINGLE("ALC Capture Noise Gate Threshold", WM8974_NGATE,  0, 7, 0),
+
+SOC_SINGLE("Capture PGA ZC Switch", WM8974_INPPGA,  7, 1, 0),
+SOC_SINGLE_TLV("Capture PGA Volume", WM8974_INPPGA,  0, 63, 0, inpga_tlv),
+
+SOC_SINGLE("Speaker Playback ZC Switch", WM8974_SPKVOL,  7, 1, 0),
+SOC_SINGLE("Speaker Playback Switch", WM8974_SPKVOL,  6, 1, 1),
+SOC_SINGLE_TLV("Speaker Playback Volume", WM8974_SPKVOL,  0, 63, 0, spk_tlv),
+
+SOC_ENUM("Aux Mode", wm8974_auxmode),
+
+SOC_SINGLE("Capture Boost(+20dB)", WM8974_ADCBOOST,  8, 1, 0),
+SOC_SINGLE("Mono Playback Switch", WM8974_MONOMIX, 6, 1, 1),
+};
+
+/* Speaker Output Mixer */
+static const struct snd_kcontrol_new wm8974_speaker_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_SPKMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_SPKMIX, 5, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_SPKMIX, 0, 1, 1),
+};
+
+/* Mono Output Mixer */
+static const struct snd_kcontrol_new wm8974_mono_mixer_controls[] = {
+SOC_DAPM_SINGLE("Line Bypass Switch", WM8974_MONOMIX, 1, 1, 0),
+SOC_DAPM_SINGLE("Aux Playback Switch", WM8974_MONOMIX, 2, 1, 0),
+SOC_DAPM_SINGLE("PCM Playback Switch", WM8974_MONOMIX, 0, 1, 0),
+};
+
+/* Boost mixer */
+static const struct snd_kcontrol_new wm8974_boost_mixer[] = {
+SOC_DAPM_SINGLE("Aux Switch", WM8974_INPPGA, 6, 1, 0),
+};
+
+/* Input PGA */
+static const struct snd_kcontrol_new wm8974_inpga[] = {
+SOC_DAPM_SINGLE("Aux Switch", WM8974_INPUT, 2, 1, 0),
+SOC_DAPM_SINGLE("MicN Switch", WM8974_INPUT, 1, 1, 0),
+SOC_DAPM_SINGLE("MicP Switch", WM8974_INPUT, 0, 1, 0),
+};
+
+/* AUX Input boost vol */
+static const struct snd_kcontrol_new wm8974_aux_boost_controls =
+SOC_DAPM_SINGLE("Aux Volume", WM8974_ADCBOOST, 0, 7, 0);
+
+/* Mic Input boost vol */
+static const struct snd_kcontrol_new wm8974_mic_boost_controls =
+SOC_DAPM_SINGLE("Mic Volume", WM8974_ADCBOOST, 4, 7, 0);
+
+static const struct snd_soc_dapm_widget wm8974_dapm_widgets[] = {
+SND_SOC_DAPM_MIXER("Speaker Mixer", WM8974_POWER3, 2, 0,
+	&wm8974_speaker_mixer_controls[0],
+	ARRAY_SIZE(wm8974_speaker_mixer_controls)),
+SND_SOC_DAPM_MIXER("Mono Mixer", WM8974_POWER3, 3, 0,
+	&wm8974_mono_mixer_controls[0],
+	ARRAY_SIZE(wm8974_mono_mixer_controls)),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8974_POWER3, 0, 0),
+SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8974_POWER2, 0, 0),
+SND_SOC_DAPM_PGA("Aux Input", WM8974_POWER1, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkN Out", WM8974_POWER3, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SpkP Out", WM8974_POWER3, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Mono Out", WM8974_POWER3, 7, 0, NULL, 0),
+
+SND_SOC_DAPM_MIXER("Input PGA", WM8974_POWER2, 2, 0, wm8974_inpga,
+		   ARRAY_SIZE(wm8974_inpga)),
+SND_SOC_DAPM_MIXER("Boost Mixer", WM8974_POWER2, 4, 0,
+		   wm8974_boost_mixer, ARRAY_SIZE(wm8974_boost_mixer)),
+
+SND_SOC_DAPM_MICBIAS("Mic Bias", WM8974_POWER1, 4, 0),
+
+SND_SOC_DAPM_INPUT("MICN"),
+SND_SOC_DAPM_INPUT("MICP"),
+SND_SOC_DAPM_INPUT("AUX"),
+SND_SOC_DAPM_OUTPUT("MONOOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+};
+
+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"},
+
+	/* Boost Mixer */
+	{"ADC", NULL, "Boost Mixer"},
+	{"Boost Mixer", "Aux Switch", "Aux Input"},
+	{"Boost Mixer", NULL, "Input PGA"},
+	{"Boost Mixer", NULL, "MICP"},
+
+	/* Input PGA */
+	{"Input PGA", "Aux Switch", "Aux Input"},
+	{"Input PGA", "MicN Switch", "MICN"},
+	{"Input PGA", "MicP Switch", "MICP"},
+
+	/* Inputs */
+	{"Aux Input", NULL, "AUX"},
+};
+
+static int wm8974_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, wm8974_dapm_widgets,
+				  ARRAY_SIZE(wm8974_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+struct pll_ {
+	unsigned int pre_div:4; /* prescale - 1 */
+	unsigned int n:4;
+	unsigned int k;
+};
+
+static struct pll_ pll_div;
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 24) * 10)
+
+static void pll_factors(unsigned int target, unsigned int source)
+{
+	unsigned long long Kpart;
+	unsigned int K, Ndiv, Nmod;
+
+	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))
+		printk(KERN_WARNING
+			"WM8974 N value %u outwith recommended range!\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;
+}
+
+static int wm8974_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;
+
+	if (freq_in == 0 || freq_out == 0) {
+		/* Clock CODEC directly from MCLK */
+		reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
+		wm8974_write(codec, WM8974_CLOCK, reg & 0x0ff);
+
+		/* Turn off PLL */
+		reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
+		wm8974_write(codec, WM8974_POWER1, reg & 0x1df);
+		return 0;
+	}
+
+	pll_factors(freq_out*4, freq_in);
+
+	wm8974_write(codec, WM8974_PLLN, (pll_div.pre_div << 4) | pll_div.n);
+	wm8974_write(codec, WM8974_PLLK1, pll_div.k >> 18);
+	wm8974_write(codec, WM8974_PLLK2, (pll_div.k >> 9) & 0x1ff);
+	wm8974_write(codec, WM8974_PLLK3, pll_div.k & 0x1ff);
+	reg = wm8974_read_reg_cache(codec, WM8974_POWER1);
+	wm8974_write(codec, WM8974_POWER1, reg | 0x020);
+
+	/* Run CODEC from PLL instead of MCLK */
+	reg = wm8974_read_reg_cache(codec, WM8974_CLOCK);
+	wm8974_write(codec, WM8974_CLOCK, reg | 0x100);
+
+	return 0;
+}
+
+/*
+ * Configure WM8974 clock dividers.
+ */
+static int wm8974_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 WM8974_OPCLKDIV:
+		reg = wm8974_read_reg_cache(codec, WM8974_GPIO) & 0x1cf;
+		wm8974_write(codec, WM8974_GPIO, reg | div);
+		break;
+	case WM8974_MCLKDIV:
+		reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x11f;
+		wm8974_write(codec, WM8974_CLOCK, reg | div);
+		break;
+	case WM8974_ADCCLK:
+		reg = wm8974_read_reg_cache(codec, WM8974_ADC) & 0x1f7;
+		wm8974_write(codec, WM8974_ADC, reg | div);
+		break;
+	case WM8974_DACCLK:
+		reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0x1f7;
+		wm8974_write(codec, WM8974_DAC, reg | div);
+		break;
+	case WM8974_BCLKDIV:
+		reg = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1e3;
+		wm8974_write(codec, WM8974_CLOCK, reg | div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
+		unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 iface = 0;
+	u16 clk = wm8974_read_reg_cache(codec, WM8974_CLOCK) & 0x1fe;
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		clk |= 0x0001;
+		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 |= 0x0010;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface |= 0x0008;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface |= 0x00018;
+		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 |= 0x0180;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		iface |= 0x0100;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		iface |= 0x0080;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	wm8974_write(codec, WM8974_IFACE, iface);
+	wm8974_write(codec, WM8974_CLOCK, clk);
+	return 0;
+}
+
+static int wm8974_pcm_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;
+	u16 iface = wm8974_read_reg_cache(codec, WM8974_IFACE) & 0x19f;
+	u16 adn = wm8974_read_reg_cache(codec, WM8974_ADD) & 0x1f1;
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		iface |= 0x0020;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		iface |= 0x0040;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		iface |= 0x0060;
+		break;
+	}
+
+	/* filter coefficient */
+	switch (params_rate(params)) {
+	case SNDRV_PCM_RATE_8000:
+		adn |= 0x5 << 1;
+		break;
+	case SNDRV_PCM_RATE_11025:
+		adn |= 0x4 << 1;
+		break;
+	case SNDRV_PCM_RATE_16000:
+		adn |= 0x3 << 1;
+		break;
+	case SNDRV_PCM_RATE_22050:
+		adn |= 0x2 << 1;
+		break;
+	case SNDRV_PCM_RATE_32000:
+		adn |= 0x1 << 1;
+		break;
+	case SNDRV_PCM_RATE_44100:
+	case SNDRV_PCM_RATE_48000:
+		break;
+	}
+
+	wm8974_write(codec, WM8974_IFACE, iface);
+	wm8974_write(codec, WM8974_ADD, adn);
+	return 0;
+}
+
+static int wm8974_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 mute_reg = wm8974_read_reg_cache(codec, WM8974_DAC) & 0xffbf;
+
+	if (mute)
+		wm8974_write(codec, WM8974_DAC, mute_reg | 0x40);
+	else
+		wm8974_write(codec, WM8974_DAC, mute_reg);
+	return 0;
+}
+
+/* liam need to make this lower power with dapm */
+static int wm8974_set_bias_level(struct snd_soc_codec *codec,
+	enum snd_soc_bias_level level)
+{
+	u16 power1 = wm8974_read_reg_cache(codec, WM8974_POWER1) & ~0x3;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+		power1 |= 0x1;  /* VMID 50k */
+		wm8974_write(codec, WM8974_POWER1, power1);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		power1 |= WM8974_POWER1_BIASEN | WM8974_POWER1_BUFIOEN;
+
+		if (codec->bias_level == SND_SOC_BIAS_OFF) {
+			/* Initial cap charge at VMID 5k */
+			wm8974_write(codec, WM8974_POWER1, power1 | 0x3);
+			mdelay(100);
+		}
+
+		power1 |= 0x2;  /* VMID 500k */
+		wm8974_write(codec, WM8974_POWER1, power1);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		wm8974_write(codec, WM8974_POWER1, 0);
+		wm8974_write(codec, WM8974_POWER2, 0);
+		wm8974_write(codec, WM8974_POWER3, 0);
+		break;
+	}
+
+	codec->bias_level = level;
+	return 0;
+}
+
+#define WM8974_RATES (SNDRV_PCM_RATE_8000_48000)
+
+#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+	SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8974_ops = {
+	.hw_params = wm8974_pcm_hw_params,
+	.digital_mute = wm8974_mute,
+	.set_fmt = wm8974_set_dai_fmt,
+	.set_clkdiv = wm8974_set_dai_clkdiv,
+	.set_pll = wm8974_set_dai_pll,
+};
+
+struct snd_soc_dai wm8974_dai = {
+	.name = "WM8974 HiFi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,   /* Only 1 channel of data */
+		.rates = WM8974_RATES,
+		.formats = WM8974_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,   /* Only 1 channel of data */
+		.rates = WM8974_RATES,
+		.formats = WM8974_FORMATS,},
+	.ops = &wm8974_ops,
+};
+EXPORT_SYMBOL_GPL(wm8974_dai);
+
+static int wm8974_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;
+
+	wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8974_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(wm8974_reg); i++) {
+		data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001);
+		data[1] = cache[i] & 0x00ff;
+		codec->hw_write(codec->control_data, data, 2);
+	}
+	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	wm8974_set_bias_level(codec, codec->suspend_bias_level);
+	return 0;
+}
+
+static int wm8974_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (wm8974_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = wm8974_codec;
+	codec = wm8974_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, wm8974_snd_controls,
+			     ARRAY_SIZE(wm8974_snd_controls));
+	wm8974_add_widgets(codec);
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
+		goto card_err;
+	}
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+/* power down chip */
+static int wm8974_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_wm8974 = {
+	.probe = 	wm8974_probe,
+	.remove = 	wm8974_remove,
+	.suspend = 	wm8974_suspend,
+	.resume =	wm8974_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_wm8974);
+
+static __devinit int wm8974_register(struct wm8974_priv *wm8974)
+{
+	int ret;
+	struct snd_soc_codec *codec = &wm8974->codec;
+
+	if (wm8974_codec) {
+		dev_err(codec->dev, "Another WM8974 is registered\n");
+		return -EINVAL;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = wm8974;
+	codec->name = "WM8974";
+	codec->owner = THIS_MODULE;
+	codec->read = wm8974_read_reg_cache;
+	codec->write = wm8974_write;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = wm8974_set_bias_level;
+	codec->dai = &wm8974_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = WM8974_CACHEREGNUM;
+	codec->reg_cache = &wm8974->reg_cache;
+
+	memcpy(codec->reg_cache, wm8974_reg, sizeof(wm8974_reg));
+
+	ret = wm8974_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		return ret;
+	}
+
+	wm8974_dai.dev = codec->dev;
+
+	wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	wm8974_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(&wm8974_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		snd_soc_unregister_codec(codec);
+		return ret;
+	}
+
+	return 0;
+}
+
+static __devexit void wm8974_unregister(struct wm8974_priv *wm8974)
+{
+	wm8974_set_bias_level(&wm8974->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dai(&wm8974_dai);
+	snd_soc_unregister_codec(&wm8974->codec);
+	kfree(wm8974);
+	wm8974_codec = NULL;
+}
+
+static __devinit int wm8974_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8974_priv *wm8974;
+	struct snd_soc_codec *codec;
+
+	wm8974 = kzalloc(sizeof(struct wm8974_priv), GFP_KERNEL);
+	if (wm8974 == NULL)
+		return -ENOMEM;
+
+	codec = &wm8974->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(i2c, wm8974);
+	codec->control_data = i2c;
+
+	codec->dev = &i2c->dev;
+
+	return wm8974_register(wm8974);
+}
+
+static __devexit int wm8974_i2c_remove(struct i2c_client *client)
+{
+	struct wm8974_priv *wm8974 = i2c_get_clientdata(client);
+	wm8974_unregister(wm8974);
+	return 0;
+}
+
+static const struct i2c_device_id wm8974_i2c_id[] = {
+	{ "wm8974", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
+
+static struct i2c_driver wm8974_i2c_driver = {
+	.driver = {
+		.name = "WM8974",
+		.owner = THIS_MODULE,
+	},
+	.probe =    wm8974_i2c_probe,
+	.remove =   __devexit_p(wm8974_i2c_remove),
+	.id_table = wm8974_i2c_id,
+};
+
+static int __init wm8974_modinit(void)
+{
+	return i2c_add_driver(&wm8974_i2c_driver);
+}
+module_init(wm8974_modinit);
+
+static void __exit wm8974_exit(void)
+{
+	i2c_del_driver(&wm8974_i2c_driver);
+}
+module_exit(wm8974_exit);
+
+MODULE_DESCRIPTION("ASoC WM8974 driver");
+MODULE_AUTHOR("Liam Girdwood");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8974.h b/sound/soc/codecs/wm8974.h
new file mode 100644
index 0000000..98de956
--- /dev/null
+++ b/sound/soc/codecs/wm8974.h
@@ -0,0 +1,99 @@
+/*
+ * wm8974.h  --  WM8974 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 _WM8974_H
+#define _WM8974_H
+
+/* WM8974 register space */
+
+#define WM8974_RESET		0x0
+#define WM8974_POWER1		0x1
+#define WM8974_POWER2		0x2
+#define WM8974_POWER3		0x3
+#define WM8974_IFACE		0x4
+#define WM8974_COMP		0x5
+#define WM8974_CLOCK		0x6
+#define WM8974_ADD		0x7
+#define WM8974_GPIO		0x8
+#define WM8974_DAC		0xa
+#define WM8974_DACVOL		0xb
+#define WM8974_ADC		0xe
+#define WM8974_ADCVOL		0xf
+#define WM8974_EQ1		0x12
+#define WM8974_EQ2		0x13
+#define WM8974_EQ3		0x14
+#define WM8974_EQ4		0x15
+#define WM8974_EQ5		0x16
+#define WM8974_DACLIM1		0x18
+#define WM8974_DACLIM2		0x19
+#define WM8974_NOTCH1		0x1b
+#define WM8974_NOTCH2		0x1c
+#define WM8974_NOTCH3		0x1d
+#define WM8974_NOTCH4		0x1e
+#define WM8974_ALC1		0x20
+#define WM8974_ALC2		0x21
+#define WM8974_ALC3		0x22
+#define WM8974_NGATE		0x23
+#define WM8974_PLLN		0x24
+#define WM8974_PLLK1		0x25
+#define WM8974_PLLK2		0x26
+#define WM8974_PLLK3		0x27
+#define WM8974_ATTEN		0x28
+#define WM8974_INPUT		0x2c
+#define WM8974_INPPGA		0x2d
+#define WM8974_ADCBOOST		0x2f
+#define WM8974_OUTPUT		0x31
+#define WM8974_SPKMIX		0x32
+#define WM8974_SPKVOL		0x36
+#define WM8974_MONOMIX		0x38
+
+#define WM8974_CACHEREGNUM 	57
+
+/* Clock divider Id's */
+#define WM8974_OPCLKDIV		0
+#define WM8974_MCLKDIV		1
+#define WM8974_ADCCLK		2
+#define WM8974_DACCLK		3
+#define WM8974_BCLKDIV		4
+
+/* DAC clock dividers */
+#define WM8974_DACCLK_F2	(1 << 3)
+#define WM8974_DACCLK_F4	(0 << 3)
+
+/* ADC clock dividers */
+#define WM8974_ADCCLK_F2	(1 << 3)
+#define WM8974_ADCCLK_F4	(0 << 3)
+
+/* PLL Out dividers */
+#define WM8974_OPCLKDIV_1	(0 << 4)
+#define WM8974_OPCLKDIV_2	(1 << 4)
+#define WM8974_OPCLKDIV_3	(2 << 4)
+#define WM8974_OPCLKDIV_4	(3 << 4)
+
+/* BCLK clock dividers */
+#define WM8974_BCLKDIV_1	(0 << 2)
+#define WM8974_BCLKDIV_2	(1 << 2)
+#define WM8974_BCLKDIV_4	(2 << 2)
+#define WM8974_BCLKDIV_8	(3 << 2)
+#define WM8974_BCLKDIV_16	(4 << 2)
+#define WM8974_BCLKDIV_32	(5 << 2)
+
+/* MCLK clock dividers */
+#define WM8974_MCLKDIV_1	(0 << 5)
+#define WM8974_MCLKDIV_1_5	(1 << 5)
+#define WM8974_MCLKDIV_2	(2 << 5)
+#define WM8974_MCLKDIV_3	(3 << 5)
+#define WM8974_MCLKDIV_4	(4 << 5)
+#define WM8974_MCLKDIV_6	(5 << 5)
+#define WM8974_MCLKDIV_8	(6 << 5)
+#define WM8974_MCLKDIV_12	(7 << 5)
+
+extern struct snd_soc_dai wm8974_dai;
+extern struct snd_soc_codec_device soc_codec_dev_wm8974;
+
+#endif
_______________________________________________
Alsa-devel mailing list
Alsa-devel@xxxxxxxxxxxxxxxx
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Pulse Audio]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux