Merge branch 'for-2.6.38' into for-2.6.39
diff --git a/arch/arm/mach-shmobile/board-ag5evm.c b/arch/arm/mach-shmobile/board-ag5evm.c
index c18a740..343362d 100644
--- a/arch/arm/mach-shmobile/board-ag5evm.c
+++ b/arch/arm/mach-shmobile/board-ag5evm.c
@@ -118,13 +118,6 @@
 };
 
 /* FSI A */
-static struct sh_fsi_platform_info fsi_info = {
-	.porta_flags = SH_FSI_OUT_SLAVE_MODE	|
-		       SH_FSI_IN_SLAVE_MODE	|
-		       SH_FSI_OFMT(I2S)		|
-		       SH_FSI_IFMT(I2S),
-};
-
 static struct resource fsi_resources[] = {
 	[0] = {
 		.name	= "FSI",
@@ -143,9 +136,6 @@
 	.id		= -1,
 	.num_resources	= ARRAY_SIZE(fsi_resources),
 	.resource	= fsi_resources,
-	.dev	= {
-		.platform_data	= &fsi_info,
-	},
 };
 
 static struct resource sh_mmcif_resources[] = {
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 3cf0951..17f528a 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -673,16 +673,12 @@
 }
 
 static struct sh_fsi_platform_info fsi_info = {
-	.porta_flags = SH_FSI_BRS_INV |
-		       SH_FSI_OUT_SLAVE_MODE |
-		       SH_FSI_IN_SLAVE_MODE |
-		       SH_FSI_OFMT(PCM) |
-		       SH_FSI_IFMT(PCM),
+	.porta_flags = SH_FSI_BRS_INV,
 
 	.portb_flags = SH_FSI_BRS_INV |
 		       SH_FSI_BRM_INV |
 		       SH_FSI_LRS_INV |
-		       SH_FSI_OFMT(SPDIF),
+		       SH_FSI_FMT_SPDIF,
 	.set_rate = fsi_set_rate,
 };
 
@@ -783,6 +779,10 @@
 	},
 };
 
+static struct platform_device fsi_hdmi_device = {
+	.name		= "sh_fsi2_b_hdmi",
+};
+
 static long ap4evb_clk_optimize(unsigned long target, unsigned long *best_freq,
 				unsigned long *parent_freq)
 {
@@ -936,6 +936,7 @@
 	&usb1_host_device,
 	&fsi_device,
 	&fsi_ak4643_device,
+	&fsi_hdmi_device,
 	&sh_mmcif_device,
 	&lcdc1_device,
 	&lcdc_device,
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index 7b15d21..73b8c90 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -400,6 +400,10 @@
 	},
 };
 
+static struct platform_device fsi_hdmi_device = {
+	.name		= "sh_fsi2_b_hdmi",
+};
+
 static int __init hdmi_init_pm_clock(void)
 {
 	struct clk *hdmi_ick = clk_get(&hdmi_device.dev, "ick");
@@ -610,16 +614,12 @@
 }
 
 static struct sh_fsi_platform_info fsi_info = {
-	.porta_flags =	SH_FSI_BRS_INV		|
-			SH_FSI_OUT_SLAVE_MODE	|
-			SH_FSI_IN_SLAVE_MODE	|
-			SH_FSI_OFMT(PCM)	|
-			SH_FSI_IFMT(PCM),
+	.porta_flags =	SH_FSI_BRS_INV,
 
 	.portb_flags =	SH_FSI_BRS_INV	|
 			SH_FSI_BRM_INV	|
 			SH_FSI_LRS_INV	|
-			SH_FSI_OFMT(SPDIF),
+			SH_FSI_FMT_SPDIF,
 
 	.set_rate = fsi_set_rate,
 };
@@ -922,6 +922,7 @@
 	&leds_device,
 	&fsi_device,
 	&fsi_ak4643_device,
+	&fsi_hdmi_device,
 	&sdhi0_device,
 #if !defined(CONFIG_MMC_SH_MMCIF)
 	&sdhi1_device,
diff --git a/arch/arm/mach-tegra/include/mach/harmony_audio.h b/arch/arm/mach-tegra/include/mach/harmony_audio.h
new file mode 100644
index 0000000..5c46391
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/harmony_audio.h
@@ -0,0 +1,19 @@
+/*
+ * arch/arm/mach-tegra/include/mach/harmony_audio.h
+ *
+ * Copyright 2011 NVIDIA, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+struct harmony_audio_platform_data {
+	int gpio_spkr_en;
+};
diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c
index 33b6629..b96b79b 100644
--- a/arch/sh/boards/mach-ecovec24/setup.c
+++ b/arch/sh/boards/mach-ecovec24/setup.c
@@ -723,11 +723,7 @@
 
 /* FSI */
 static struct sh_fsi_platform_info fsi_info = {
-	.portb_flags = SH_FSI_BRS_INV |
-		       SH_FSI_OUT_SLAVE_MODE |
-		       SH_FSI_IN_SLAVE_MODE |
-		       SH_FSI_OFMT(I2S) |
-		       SH_FSI_IFMT(I2S),
+	.portb_flags = SH_FSI_BRS_INV,
 };
 
 static struct resource fsi_resources[] = {
diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c
index 5276793..c8bcf6a 100644
--- a/arch/sh/boards/mach-se/7724/setup.c
+++ b/arch/sh/boards/mach-se/7724/setup.c
@@ -286,11 +286,7 @@
 /* FSI */
 /* change J20, J21, J22 pin to 1-2 connection to use slave mode */
 static struct sh_fsi_platform_info fsi_info = {
-	.porta_flags = SH_FSI_BRS_INV |
-		       SH_FSI_OUT_SLAVE_MODE |
-		       SH_FSI_IN_SLAVE_MODE |
-		       SH_FSI_OFMT(PCM) |
-		       SH_FSI_IFMT(PCM),
+	.porta_flags = SH_FSI_BRS_INV,
 };
 
 static struct resource fsi_resources[] = {
diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h
new file mode 100644
index 0000000..16f8d32
--- /dev/null
+++ b/include/sound/cs4271.h
@@ -0,0 +1,25 @@
+/*
+ * Definitions for CS4271 ASoC codec driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CS4271_H
+#define __CS4271_H
+
+struct cs4271_platform_data {
+	int gpio_nreset;	/* GPIO driving Reset pin, if any */
+	int gpio_disable;	/* GPIO that disable serial bus, if any */
+};
+
+#endif /* __CS4271_H */
diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h
index d798941..9a155f9 100644
--- a/include/sound/sh_fsi.h
+++ b/include/sound/sh_fsi.h
@@ -15,67 +15,29 @@
 #define FSI_PORT_A	0
 #define FSI_PORT_B	1
 
-/* flags format
-
- * 0xABCDEEFF
- *
- * A:  channel size for TDM (input)
- * B:  channel size for TDM (ooutput)
- * C:  inversion
- * D:  mode
- * E:  input format
- * F:  output format
- */
-
 #include <linux/clk.h>
 #include <sound/soc.h>
 
-/* TDM channel */
-#define SH_FSI_SET_CH_I(x)	((x & 0xF) << 28)
-#define SH_FSI_SET_CH_O(x)	((x & 0xF) << 24)
+/*
+ * flags format
+ *
+ * 0x000000BA
+ *
+ * A:  inversion
+ * B:  format mode
+ */
 
-#define SH_FSI_CH_IMASK		0xF0000000
-#define SH_FSI_CH_OMASK		0x0F000000
-#define SH_FSI_GET_CH_I(x)	((x & SH_FSI_CH_IMASK) >> 28)
-#define SH_FSI_GET_CH_O(x)	((x & SH_FSI_CH_OMASK) >> 24)
+/* A: clock inversion */
+#define SH_FSI_INVERSION_MASK	0x0000000F
+#define SH_FSI_LRM_INV		(1 << 0)
+#define SH_FSI_BRM_INV		(1 << 1)
+#define SH_FSI_LRS_INV		(1 << 2)
+#define SH_FSI_BRS_INV		(1 << 3)
 
-/* clock inversion */
-#define SH_FSI_INVERSION_MASK	0x00F00000
-#define SH_FSI_LRM_INV		(1 << 20)
-#define SH_FSI_BRM_INV		(1 << 21)
-#define SH_FSI_LRS_INV		(1 << 22)
-#define SH_FSI_BRS_INV		(1 << 23)
-
-/* mode */
-#define SH_FSI_MODE_MASK	0x000F0000
-#define SH_FSI_IN_SLAVE_MODE	(1 << 16)  /* default master mode */
-#define SH_FSI_OUT_SLAVE_MODE	(1 << 17)  /* default master mode */
-
-/* DI format */
-#define SH_FSI_FMT_MASK		0x000000FF
-#define SH_FSI_IFMT(x)		(((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8)
-#define SH_FSI_OFMT(x)		(((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0)
-#define SH_FSI_GET_IFMT(x)	((x >> 8) & SH_FSI_FMT_MASK)
-#define SH_FSI_GET_OFMT(x)	((x >> 0) & SH_FSI_FMT_MASK)
-
-#define SH_FSI_FMT_MONO		0
-#define SH_FSI_FMT_MONO_DELAY	1
-#define SH_FSI_FMT_PCM		2
-#define SH_FSI_FMT_I2S		3
-#define SH_FSI_FMT_TDM		4
-#define SH_FSI_FMT_TDM_DELAY	5
-#define SH_FSI_FMT_SPDIF	6
-
-
-#define SH_FSI_IFMT_TDM_CH(x) \
-	(SH_FSI_IFMT(TDM)	| SH_FSI_SET_CH_I(x))
-#define SH_FSI_IFMT_TDM_DELAY_CH(x) \
-	(SH_FSI_IFMT(TDM_DELAY)	| SH_FSI_SET_CH_I(x))
-
-#define SH_FSI_OFMT_TDM_CH(x) \
-	(SH_FSI_OFMT(TDM)	| SH_FSI_SET_CH_O(x))
-#define SH_FSI_OFMT_TDM_DELAY_CH(x) \
-	(SH_FSI_OFMT(TDM_DELAY)	| SH_FSI_SET_CH_O(x))
+/* B: format mode */
+#define SH_FSI_FMT_MASK		0x000000F0
+#define SH_FSI_FMT_DAI		(0 << 4)
+#define SH_FSI_FMT_SPDIF	(1 << 4)
 
 
 /*
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 8031769..979ed84 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -157,6 +157,18 @@
 	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
 	.event = wevent, .event_flags = wflags}
 
+/* additional sequencing control within an event type */
+#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
+	wevent, wflags) \
+{	.id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+	.invert = winvert, .event = wevent, .event_flags = wflags, \
+	.subseq = wsubseq}
+#define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \
+	wflags)	\
+{	.id = snd_soc_dapm_supply, .name = wname, .reg = wreg,	\
+	.shift = wshift, .invert = winvert, .event = wevent, \
+	.event_flags = wflags, .subseq = wsubseq}
+
 /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
 #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
 	wevent, wflags) \
@@ -450,6 +462,7 @@
 	unsigned char ext:1;			/* has external widgets */
 	unsigned char force:1;			/* force state */
 	unsigned char ignore_suspend:1;         /* kept enabled over suspend */
+	int subseq;				/* sort within widget type */
 
 	int (*power_check)(struct snd_soc_dapm_widget *w);
 
@@ -487,6 +500,9 @@
 
 	struct snd_soc_dapm_update *update;
 
+	void (*seq_notifier)(struct snd_soc_dapm_context *,
+			     enum snd_soc_dapm_type, int);
+
 	struct device *dev; /* from parent - for debug */
 	struct snd_soc_codec *codec; /* parent codec */
 	struct snd_soc_card *card; /* parent card */
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 74921f2..4b6c0a8 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -258,6 +258,11 @@
 	SND_SOC_RBTREE_COMPRESSION
 };
 
+int snd_soc_register_card(struct snd_soc_card *card);
+int snd_soc_unregister_card(struct snd_soc_card *card);
+int snd_soc_suspend(struct device *dev);
+int snd_soc_resume(struct device *dev);
+int snd_soc_poweroff(struct device *dev);
 int snd_soc_register_platform(struct device *dev,
 		struct snd_soc_platform_driver *platform_drv);
 void snd_soc_unregister_platform(struct device *dev);
@@ -265,7 +270,8 @@
 		const struct snd_soc_codec_driver *codec_drv,
 		struct snd_soc_dai_driver *dai_drv, int num_dai);
 void snd_soc_unregister_codec(struct device *dev);
-int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
+				    unsigned int reg);
 int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
 			       int addr_bits, int data_bits,
 			       enum snd_soc_control_type control);
@@ -276,6 +282,10 @@
 			unsigned int reg, unsigned int value);
 int snd_soc_cache_read(struct snd_soc_codec *codec,
 		       unsigned int reg, unsigned int *value);
+int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
+				      unsigned int reg);
+int snd_soc_default_readable_register(struct snd_soc_codec *codec,
+				      unsigned int reg);
 
 /* Utility functions to get clock rates from various things */
 int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
@@ -367,6 +377,22 @@
 	struct snd_ctl_elem_value *ucontrol);
 
 /**
+ * struct snd_soc_reg_access - Describes whether a given register is
+ * readable, writable or volatile.
+ *
+ * @reg: the register number
+ * @read: whether this register is readable
+ * @write: whether this register is writable
+ * @vol: whether this register is volatile
+ */
+struct snd_soc_reg_access {
+	u16 reg;
+	u16 read;
+	u16 write;
+	u16 vol;
+};
+
+/**
  * struct snd_soc_jack_pin - Describes a pin to update based on jack detection
  *
  * @pin:    name of the pin to update
@@ -459,18 +485,22 @@
 	struct list_head card_list;
 	int num_dai;
 	enum snd_soc_compress_type compress_type;
+	size_t reg_size;	/* reg_cache_size * reg_word_size */
+	int (*volatile_register)(struct snd_soc_codec *, unsigned int);
+	int (*readable_register)(struct snd_soc_codec *, unsigned int);
 
 	/* runtime */
 	struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */
 	unsigned int active;
-	unsigned int cache_only:1;  /* Suppress writes to hardware */
-	unsigned int cache_sync:1; /* Cache needs to be synced to hardware */
+	unsigned int cache_bypass:1; /* Suppress access to the cache */
 	unsigned int suspended:1; /* Codec is in suspend PM state */
 	unsigned int probed:1; /* Codec has been probed */
 	unsigned int ac97_registered:1; /* Codec has been AC97 registered */
 	unsigned int ac97_created:1; /* Codec has been created by SoC */
 	unsigned int sysfs_registered:1; /* codec has been sysfs registered */
 	unsigned int cache_init:1; /* codec cache has been initialized */
+	u32 cache_only;  /* Suppress writes to hardware */
+	u32 cache_sync; /* Cache needs to be synced to hardware */
 
 	/* codec IO */
 	void *control_data; /* codec control (i2c/3wire) data */
@@ -508,17 +538,22 @@
 	int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
 	int (*display_register)(struct snd_soc_codec *, char *,
 				size_t, unsigned int);
-	int (*volatile_register)(unsigned int);
-	int (*readable_register)(unsigned int);
+	int (*volatile_register)(struct snd_soc_codec *, unsigned int);
+	int (*readable_register)(struct snd_soc_codec *, unsigned int);
 	short reg_cache_size;
 	short reg_cache_step;
 	short reg_word_size;
 	const void *reg_cache_default;
+	short reg_access_size;
+	const struct snd_soc_reg_access *reg_access_default;
 	enum snd_soc_compress_type compress_type;
 
 	/* codec bias level */
 	int (*set_bias_level)(struct snd_soc_codec *,
 			      enum snd_soc_bias_level level);
+
+	void (*seq_notifier)(struct snd_soc_dapm_context *,
+			     enum snd_soc_dapm_type, int);
 };
 
 /* SoC platform interface */
@@ -617,15 +652,15 @@
 
 	bool instantiated;
 
-	int (*probe)(struct platform_device *pdev);
-	int (*remove)(struct platform_device *pdev);
+	int (*probe)(struct snd_soc_card *card);
+	int (*remove)(struct snd_soc_card *card);
 
 	/* the pre and post PM functions are used to do any PM work before and
 	 * after the codec and DAI's do any PM work. */
-	int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
-	int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
-	int (*resume_pre)(struct platform_device *pdev);
-	int (*resume_post)(struct platform_device *pdev);
+	int (*suspend_pre)(struct snd_soc_card *card);
+	int (*suspend_post)(struct snd_soc_card *card);
+	int (*resume_pre)(struct snd_soc_card *card);
+	int (*resume_post)(struct snd_soc_card *card);
 
 	/* callbacks */
 	int (*set_bias_level)(struct snd_soc_card *,
@@ -670,6 +705,8 @@
 	struct dentry *debugfs_pop_time;
 #endif
 	u32 pop_time;
+
+	void *drvdata;
 };
 
 /* SoC machine DAI configuration, glues a codec and cpu DAI together */
@@ -721,6 +758,17 @@
 
 /* device driver data */
 
+static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
+		void *data)
+{
+	card->drvdata = data;
+}
+
+static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card)
+{
+	return card->drvdata;
+}
+
 static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec,
 		void *data)
 {
@@ -754,6 +802,22 @@
 	return dev_get_drvdata(&rtd->dev);
 }
 
+static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
+{
+	INIT_LIST_HEAD(&card->dai_dev_list);
+	INIT_LIST_HEAD(&card->codec_dev_list);
+	INIT_LIST_HEAD(&card->platform_dev_list);
+	INIT_LIST_HEAD(&card->widgets);
+	INIT_LIST_HEAD(&card->paths);
+	INIT_LIST_HEAD(&card->dapm_list);
+}
+
 #include <sound/soc-dai.h>
 
+#ifdef CONFIG_DEBUG_FS
+extern struct dentry *snd_soc_debugfs_root;
+#endif
+
+extern const struct dev_pm_ops snd_soc_pm_ops;
+
 #endif
diff --git a/include/sound/wm8903.h b/include/sound/wm8903.h
index b4a0db23..86172cf 100644
--- a/include/sound/wm8903.h
+++ b/include/sound/wm8903.h
@@ -37,6 +37,21 @@
 #define WM8903_MICBIAS_ENA_WIDTH                     1  /* MICBIAS_ENA */
 
 /*
+ * WM8903_GPn_FN values
+ *
+ * See datasheets for list of valid values per pin
+ */
+#define WM8903_GPn_FN_GPIO_OUTPUT                    0
+#define WM8903_GPn_FN_BCLK                           1
+#define WM8903_GPn_FN_IRQ_OUTPT                      2
+#define WM8903_GPn_FN_GPIO_INPUT                     3
+#define WM8903_GPn_FN_MICBIAS_CURRENT_DETECT         4
+#define WM8903_GPn_FN_MICBIAS_SHORT_DETECT           5
+#define WM8903_GPn_FN_DMIC_LR_CLK_OUTPUT             6
+#define WM8903_GPn_FN_FLL_LOCK_OUTPUT                8
+#define WM8903_GPn_FN_FLL_CLOCK_OUTPUT               9
+
+/*
  * R116 (0x74) - GPIO Control 1
  */
 #define WM8903_GP1_FN_MASK                      0x1F00  /* GP1_FN - [12:8] */
@@ -231,6 +246,8 @@
 #define WM8903_GP5_DB_SHIFT                          0  /* GP5_DB */
 #define WM8903_GP5_DB_WIDTH                          1  /* GP5_DB */
 
+#define WM8903_NUM_GPIO 5
+
 struct wm8903_platform_data {
 	bool irq_active_low;   /* Set if IRQ active low, default high */
 
@@ -243,7 +260,8 @@
 
 	int micdet_delay;      /* Delay after microphone detection (ms) */
 
-	u32 gpio_cfg[5];       /* Default register values for GPIO pin mux */
+	int gpio_base;
+	u32 gpio_cfg[WM8903_NUM_GPIO]; /* Default register values for GPIO pin mux */
 };
 
 #endif
diff --git a/include/trace/events/asoc.h b/include/trace/events/asoc.h
index 186e84d..ae973d2 100644
--- a/include/trace/events/asoc.h
+++ b/include/trace/events/asoc.h
@@ -229,6 +229,31 @@
 	TP_printk("jack=%s %x", __get_str(name), (int)__entry->val)
 );
 
+TRACE_EVENT(snd_soc_cache_sync,
+
+	TP_PROTO(struct snd_soc_codec *codec, const char *type,
+		 const char *status),
+
+	TP_ARGS(codec, type, status),
+
+	TP_STRUCT__entry(
+		__string(	name,		codec->name	)
+		__string(	status,		status		)
+		__string(	type,		type		)
+		__field(	int,		id		)
+	),
+
+	TP_fast_assign(
+		__assign_str(name, codec->name);
+		__assign_str(status, status);
+		__assign_str(type, type);
+		__entry->id = codec->id;
+	),
+
+	TP_printk("codec=%s.%d type=%s status=%s", __get_str(name),
+		  (int)__entry->id, __get_str(type), __get_str(status))
+);
+
 #endif /* _TRACE_ASOC_H */
 
 /* This part must be outside protection */
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index a3efc52..8224db5 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -50,10 +50,12 @@
 source "sound/soc/nuc900/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/kirkwood/Kconfig"
+source "sound/soc/mid-x86/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/samsung/Kconfig"
 source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
+source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
 
 # Supported codecs
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index ce913bf..1ed61c5 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -10,6 +10,7 @@
 obj-$(CONFIG_SND_SOC)	+= fsl/
 obj-$(CONFIG_SND_SOC)   += imx/
 obj-$(CONFIG_SND_SOC)	+= jz4740/
+obj-$(CONFIG_SND_SOC)	+= mid-x86/
 obj-$(CONFIG_SND_SOC)	+= nuc900/
 obj-$(CONFIG_SND_SOC)	+= omap/
 obj-$(CONFIG_SND_SOC)	+= kirkwood/
@@ -17,4 +18,5 @@
 obj-$(CONFIG_SND_SOC)	+= samsung/
 obj-$(CONFIG_SND_SOC)	+= s6000/
 obj-$(CONFIG_SND_SOC)	+= sh/
+obj-$(CONFIG_SND_SOC)	+= tegra/
 obj-$(CONFIG_SND_SOC)	+= txx9/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index c48b23c..e239345a 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -26,12 +26,14 @@
 	select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
 	select SND_SOC_CS42L51 if I2C
 	select SND_SOC_CS4270 if I2C
+	select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_CX20442
 	select SND_SOC_DA7210 if I2C
 	select SND_SOC_JZ4740_CODEC if SOC_JZ4740
 	select SND_SOC_MAX98088 if I2C
 	select SND_SOC_MAX9877 if I2C
 	select SND_SOC_PCM3008
+	select SND_SOC_SN95031 if INTEL_SCU_IPC
 	select SND_SOC_SPDIF
 	select SND_SOC_SSM2602 if I2C
 	select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
@@ -76,6 +78,7 @@
 	select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8990 if I2C
+	select SND_SOC_WM8991 if I2C
 	select SND_SOC_WM8993 if I2C
 	select SND_SOC_WM8994 if MFD_WM8994
 	select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
@@ -155,6 +158,9 @@
 	bool
 	depends on SND_SOC_CS4270
 
+config SND_SOC_CS4271
+	tristate
+
 config SND_SOC_CX20442
 	tristate
 
@@ -176,6 +182,9 @@
 config SND_SOC_PCM3008
        tristate
 
+config SND_SOC_SN95031
+	tristate
+
 config SND_SOC_SPDIF
 	tristate
 
@@ -304,6 +313,9 @@
 config SND_SOC_WM8990
 	tristate
 
+config SND_SOC_WM8991
+	tristate
+
 config SND_SOC_WM8993
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 579af9c..83b7acc 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -12,6 +12,7 @@
 snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs4270-objs := cs4270.o
+snd-soc-cs4271-objs := cs4271.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-dmic-objs := dmic.o
@@ -19,6 +20,7 @@
 snd-soc-max98088-objs := max98088.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-alc5623-objs := alc5623.o
+snd-soc-sn95031-objs := sn95031.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
 snd-soc-stac9766-objs := stac9766.o
@@ -61,6 +63,7 @@
 snd-soc-wm8985-objs := wm8985.o
 snd-soc-wm8988-objs := wm8988.o
 snd-soc-wm8990-objs := wm8990.o
+snd-soc-wm8991-objs := wm8991.o
 snd-soc-wm8993-objs := wm8993.o
 snd-soc-wm8994-objs := wm8994.o wm8994-tables.o
 snd-soc-wm8995-objs := wm8995.o
@@ -91,6 +94,7 @@
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CS42L51)	+= snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_CS4271)	+= snd-soc-cs4271.o
 obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
@@ -99,6 +103,7 @@
 obj-$(CONFIG_SND_SOC_MAX98088)	+= snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_PCM3008)	+= snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_ALC5623)    += snd-soc-alc5623.o
+obj-$(CONFIG_SND_SOC_SN95031)	+=snd-soc-sn95031.o
 obj-$(CONFIG_SND_SOC_SPDIF)	+= snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)	+= snd-soc-ssm2602.o
 obj-$(CONFIG_SND_SOC_STAC9766)	+= snd-soc-stac9766.o
@@ -141,6 +146,7 @@
 obj-$(CONFIG_SND_SOC_WM8985)	+= snd-soc-wm8985.o
 obj-$(CONFIG_SND_SOC_WM8988)	+= snd-soc-wm8988.o
 obj-$(CONFIG_SND_SOC_WM8990)	+= snd-soc-wm8990.o
+obj-$(CONFIG_SND_SOC_WM8991)	+= snd-soc-wm8991.o
 obj-$(CONFIG_SND_SOC_WM8993)	+= snd-soc-wm8993.o
 obj-$(CONFIG_SND_SOC_WM8994)	+= snd-soc-wm8994.o
 obj-$(CONFIG_SND_SOC_WM8995)	+= snd-soc-wm8995.o
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index f00eba3..4be0570 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -116,6 +116,12 @@
 #define BCKO_MASK	(1 << 3)
 #define BCKO_64		BCKO_MASK
 
+#define DIF_MASK	(3 << 0)
+#define DSP		(0 << 0)
+#define RIGHT_J		(1 << 0)
+#define LEFT_J		(2 << 0)
+#define I2S		(3 << 0)
+
 /* MD_CTL2 */
 #define FS0		(1 << 0)
 #define FS1		(1 << 1)
@@ -354,6 +360,24 @@
 	snd_soc_update_bits(codec, PW_MGMT2, MS, data);
 	snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko);
 
+	/* format type */
+	data = 0;
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_LEFT_J:
+		data = LEFT_J;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		data = I2S;
+		break;
+	/* FIXME
+	 * Please add RIGHT_J / DSP support here
+	 */
+	default:
+		return -EINVAL;
+		break;
+	}
+	snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data);
+
 	return 0;
 }
 
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 8b51245..c0fccad 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -193,12 +193,12 @@
 /* The number of MCLK/LRCK ratios supported by the CS4270 */
 #define NUM_MCLK_RATIOS		ARRAY_SIZE(cs4270_mode_ratios)
 
-static int cs4270_reg_is_readable(unsigned int reg)
+static int cs4270_reg_is_readable(struct snd_soc_codec *codec, unsigned int reg)
 {
 	return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG);
 }
 
-static int cs4270_reg_is_volatile(unsigned int reg)
+static int cs4270_reg_is_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
 	/* Unreadable registers are considered volatile */
 	if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
new file mode 100644
index 0000000..9c5b7db
--- /dev/null
+++ b/sound/soc/codecs/cs4271.c
@@ -0,0 +1,659 @@
+/*
+ * CS4271 ASoC codec driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver support CS4271 codec being master or slave, working
+ * in control port mode, connected either via SPI or I2C.
+ * The data format accepted is I2S or left-justified.
+ * DAPM support not implemented.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <sound/cs4271.h>
+
+#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+			    SNDRV_PCM_FMTBIT_S24_LE | \
+			    SNDRV_PCM_FMTBIT_S32_LE)
+
+/*
+ * CS4271 registers
+ * High byte represents SPI chip address (0x10) + write command (0)
+ * Low byte - codec register address
+ */
+#define CS4271_MODE1	0x2001	/* Mode Control 1 */
+#define CS4271_DACCTL	0x2002	/* DAC Control */
+#define CS4271_DACVOL	0x2003	/* DAC Volume & Mixing Control */
+#define CS4271_VOLA	0x2004	/* DAC Channel A Volume Control */
+#define CS4271_VOLB	0x2005	/* DAC Channel B Volume Control */
+#define CS4271_ADCCTL	0x2006	/* ADC Control */
+#define CS4271_MODE2	0x2007	/* Mode Control 2 */
+#define CS4271_CHIPID	0x2008	/* Chip ID */
+
+#define CS4271_FIRSTREG	CS4271_MODE1
+#define CS4271_LASTREG	CS4271_MODE2
+#define CS4271_NR_REGS	((CS4271_LASTREG & 0xFF) + 1)
+
+/* Bit masks for the CS4271 registers */
+#define CS4271_MODE1_MODE_MASK	0xC0
+#define CS4271_MODE1_MODE_1X	0x00
+#define CS4271_MODE1_MODE_2X	0x80
+#define CS4271_MODE1_MODE_4X	0xC0
+
+#define CS4271_MODE1_DIV_MASK	0x30
+#define CS4271_MODE1_DIV_1	0x00
+#define CS4271_MODE1_DIV_15	0x10
+#define CS4271_MODE1_DIV_2	0x20
+#define CS4271_MODE1_DIV_3	0x30
+
+#define CS4271_MODE1_MASTER	0x08
+
+#define CS4271_MODE1_DAC_DIF_MASK	0x07
+#define CS4271_MODE1_DAC_DIF_LJ		0x00
+#define CS4271_MODE1_DAC_DIF_I2S	0x01
+#define CS4271_MODE1_DAC_DIF_RJ16	0x02
+#define CS4271_MODE1_DAC_DIF_RJ24	0x03
+#define CS4271_MODE1_DAC_DIF_RJ20	0x04
+#define CS4271_MODE1_DAC_DIF_RJ18	0x05
+
+#define CS4271_DACCTL_AMUTE	0x80
+#define CS4271_DACCTL_IF_SLOW	0x40
+
+#define CS4271_DACCTL_DEM_MASK	0x30
+#define CS4271_DACCTL_DEM_DIS	0x00
+#define CS4271_DACCTL_DEM_441	0x10
+#define CS4271_DACCTL_DEM_48	0x20
+#define CS4271_DACCTL_DEM_32	0x30
+
+#define CS4271_DACCTL_SVRU	0x08
+#define CS4271_DACCTL_SRD	0x04
+#define CS4271_DACCTL_INVA	0x02
+#define CS4271_DACCTL_INVB	0x01
+
+#define CS4271_DACVOL_BEQUA	0x40
+#define CS4271_DACVOL_SOFT	0x20
+#define CS4271_DACVOL_ZEROC	0x10
+
+#define CS4271_DACVOL_ATAPI_MASK	0x0F
+#define CS4271_DACVOL_ATAPI_M_M		0x00
+#define CS4271_DACVOL_ATAPI_M_BR	0x01
+#define CS4271_DACVOL_ATAPI_M_BL	0x02
+#define CS4271_DACVOL_ATAPI_M_BLR2	0x03
+#define CS4271_DACVOL_ATAPI_AR_M	0x04
+#define CS4271_DACVOL_ATAPI_AR_BR	0x05
+#define CS4271_DACVOL_ATAPI_AR_BL	0x06
+#define CS4271_DACVOL_ATAPI_AR_BLR2	0x07
+#define CS4271_DACVOL_ATAPI_AL_M	0x08
+#define CS4271_DACVOL_ATAPI_AL_BR	0x09
+#define CS4271_DACVOL_ATAPI_AL_BL	0x0A
+#define CS4271_DACVOL_ATAPI_AL_BLR2	0x0B
+#define CS4271_DACVOL_ATAPI_ALR2_M	0x0C
+#define CS4271_DACVOL_ATAPI_ALR2_BR	0x0D
+#define CS4271_DACVOL_ATAPI_ALR2_BL	0x0E
+#define CS4271_DACVOL_ATAPI_ALR2_BLR2	0x0F
+
+#define CS4271_VOLA_MUTE	0x80
+#define CS4271_VOLA_VOL_MASK	0x7F
+#define CS4271_VOLB_MUTE	0x80
+#define CS4271_VOLB_VOL_MASK	0x7F
+
+#define CS4271_ADCCTL_DITHER16	0x20
+
+#define CS4271_ADCCTL_ADC_DIF_MASK	0x10
+#define CS4271_ADCCTL_ADC_DIF_LJ	0x00
+#define CS4271_ADCCTL_ADC_DIF_I2S	0x10
+
+#define CS4271_ADCCTL_MUTEA	0x08
+#define CS4271_ADCCTL_MUTEB	0x04
+#define CS4271_ADCCTL_HPFDA	0x02
+#define CS4271_ADCCTL_HPFDB	0x01
+
+#define CS4271_MODE2_LOOP	0x10
+#define CS4271_MODE2_MUTECAEQUB	0x08
+#define CS4271_MODE2_FREEZE	0x04
+#define CS4271_MODE2_CPEN	0x02
+#define CS4271_MODE2_PDN	0x01
+
+#define CS4271_CHIPID_PART_MASK	0xF0
+#define CS4271_CHIPID_REV_MASK	0x0F
+
+/*
+ * Default CS4271 power-up configuration
+ * Array contains non-existing in hw register at address 0
+ * Array do not include Chip ID, as codec driver does not use
+ * registers read operations at all
+ */
+static const u8 cs4271_dflt_reg[CS4271_NR_REGS] = {
+	0,
+	0,
+	CS4271_DACCTL_AMUTE,
+	CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR,
+	0,
+	0,
+	0,
+	0,
+};
+
+struct cs4271_private {
+	/* SND_SOC_I2C or SND_SOC_SPI */
+	enum snd_soc_control_type	bus_type;
+	void				*control_data;
+	unsigned int			mclk;
+	bool				master;
+	bool				deemph;
+	/* Current sample rate for de-emphasis control */
+	int				rate;
+	/* GPIO driving Reset pin, if any */
+	int				gpio_nreset;
+	/* GPIO that disable serial bus, if any */
+	int				gpio_disable;
+};
+
+struct cs4271_clk_cfg {
+	unsigned int	ratio;		/* MCLK / sample rate */
+	u8		speed_mode;	/* codec speed mode: 1x, 2x, 4x */
+	u8		mclk_master;	/* ratio bit mask for Master mode */
+	u8		mclk_slave;	/* ratio bit mask for Slave mode */
+};
+
+static struct cs4271_clk_cfg cs4271_clk_tab[] = {
+	{64,   CS4271_MODE1_MODE_4X, CS4271_MODE1_DIV_1,  CS4271_MODE1_DIV_1},
+	{96,   CS4271_MODE1_MODE_4X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1},
+	{128,  CS4271_MODE1_MODE_2X, CS4271_MODE1_DIV_1,  CS4271_MODE1_DIV_1},
+	{192,  CS4271_MODE1_MODE_2X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1},
+	{256,  CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_1,  CS4271_MODE1_DIV_1},
+	{384,  CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_15, CS4271_MODE1_DIV_1},
+	{512,  CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_2,  CS4271_MODE1_DIV_1},
+	{768,  CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_3,  CS4271_MODE1_DIV_3},
+	{1024, CS4271_MODE1_MODE_1X, CS4271_MODE1_DIV_3,  CS4271_MODE1_DIV_3}
+};
+
+#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
+
+/*
+ * @freq is the desired MCLK rate
+ * MCLK rate should (c) be the sample rate, multiplied by one of the
+ * ratios listed in cs4271_mclk_fs_ratios table
+ */
+static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+				 int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+	cs4271->mclk = freq;
+	return 0;
+}
+
+static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			      unsigned int format)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+	unsigned int val = 0;
+	int ret;
+
+	switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		cs4271->master = 0;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		cs4271->master = 1;
+		val |= CS4271_MODE1_MASTER;
+		break;
+	default:
+		dev_err(codec->dev, "Invalid DAI format\n");
+		return -EINVAL;
+	}
+
+	switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_LEFT_J:
+		val |= CS4271_MODE1_DAC_DIF_LJ;
+		ret = snd_soc_update_bits(codec, CS4271_ADCCTL,
+			CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ);
+		if (ret < 0)
+			return ret;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		val |= CS4271_MODE1_DAC_DIF_I2S;
+		ret = snd_soc_update_bits(codec, CS4271_ADCCTL,
+			CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S);
+		if (ret < 0)
+			return ret;
+		break;
+	default:
+		dev_err(codec->dev, "Invalid DAI format\n");
+		return -EINVAL;
+	}
+
+	ret = snd_soc_update_bits(codec, CS4271_MODE1,
+		CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int cs4271_deemph[] = {0, 44100, 48000, 32000};
+
+static int cs4271_set_deemph(struct snd_soc_codec *codec)
+{
+	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+	int i, ret;
+	int val = CS4271_DACCTL_DEM_DIS;
+
+	if (cs4271->deemph) {
+		/* Find closest de-emphasis freq */
+		val = 1;
+		for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++)
+			if (abs(cs4271_deemph[i] - cs4271->rate) <
+			    abs(cs4271_deemph[val] - cs4271->rate))
+				val = i;
+		val <<= 4;
+	}
+
+	ret = snd_soc_update_bits(codec, CS4271_DACCTL,
+		CS4271_DACCTL_DEM_MASK, val);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.enumerated.item[0] = cs4271->deemph;
+	return 0;
+}
+
+static int cs4271_put_deemph(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+	cs4271->deemph = ucontrol->value.enumerated.item[0];
+	return cs4271_set_deemph(codec);
+}
+
+static int cs4271_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_codec *codec = rtd->codec;
+	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+	int i, ret;
+	unsigned int ratio, val;
+
+	cs4271->rate = params_rate(params);
+	ratio = cs4271->mclk / cs4271->rate;
+	for (i = 0; i < CS4171_NR_RATIOS; i++)
+		if (cs4271_clk_tab[i].ratio == ratio)
+			break;
+
+	if ((i == CS4171_NR_RATIOS) || ((ratio == 1024) && cs4271->master)) {
+		dev_err(codec->dev, "Invalid sample rate\n");
+		return -EINVAL;
+	}
+
+	/* Configure DAC */
+	val = cs4271_clk_tab[i].speed_mode;
+
+	if (cs4271->master)
+		val |= cs4271_clk_tab[i].mclk_master;
+	else
+		val |= cs4271_clk_tab[i].mclk_slave;
+
+	ret = snd_soc_update_bits(codec, CS4271_MODE1,
+		CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val);
+	if (ret < 0)
+		return ret;
+
+	return cs4271_set_deemph(codec);
+}
+
+static int cs4271_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	int ret;
+	int val_a = 0;
+	int val_b = 0;
+
+	if (mute) {
+		val_a = CS4271_VOLA_MUTE;
+		val_b = CS4271_VOLB_MUTE;
+	}
+
+	ret = snd_soc_update_bits(codec, CS4271_VOLA, CS4271_VOLA_MUTE, val_a);
+	if (ret < 0)
+		return ret;
+	ret = snd_soc_update_bits(codec, CS4271_VOLB, CS4271_VOLB_MUTE, val_b);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+/* CS4271 controls */
+static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0);
+
+static const struct snd_kcontrol_new cs4271_snd_controls[] = {
+	SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB,
+		0, 0x7F, 1, cs4271_dac_tlv),
+	SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0),
+	SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0),
+	SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0),
+	SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+		cs4271_get_deemph, cs4271_put_deemph),
+	SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0),
+	SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0),
+	SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0),
+	SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0),
+	SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0),
+	SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0),
+	SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1),
+	SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0),
+	SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1),
+	SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB,
+		7, 1, 1),
+};
+
+static struct snd_soc_dai_ops cs4271_dai_ops = {
+	.hw_params	= cs4271_hw_params,
+	.set_sysclk	= cs4271_set_dai_sysclk,
+	.set_fmt	= cs4271_set_dai_fmt,
+	.digital_mute	= cs4271_digital_mute,
+};
+
+static struct snd_soc_dai_driver cs4271_dai = {
+	.name = "cs4271-hifi",
+	.playback = {
+		.stream_name	= "Playback",
+		.channels_min	= 2,
+		.channels_max	= 2,
+		.rates		= SNDRV_PCM_RATE_8000_96000,
+		.formats	= CS4271_PCM_FORMATS,
+	},
+	.capture = {
+		.stream_name	= "Capture",
+		.channels_min	= 2,
+		.channels_max	= 2,
+		.rates		= SNDRV_PCM_RATE_8000_96000,
+		.formats	= CS4271_PCM_FORMATS,
+	},
+	.ops = &cs4271_dai_ops,
+	.symmetric_rates = 1,
+};
+
+#ifdef CONFIG_PM
+static int cs4271_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg)
+{
+	int ret;
+	/* Set power-down bit */
+	ret = snd_soc_update_bits(codec, CS4271_MODE2, 0, CS4271_MODE2_PDN);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int cs4271_soc_resume(struct snd_soc_codec *codec)
+{
+	int ret;
+	/* Restore codec state */
+	ret = snd_soc_cache_sync(codec);
+	if (ret < 0)
+		return ret;
+	/* then disable the power-down bit */
+	ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0);
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+#else
+#define cs4271_soc_suspend	NULL
+#define cs4271_soc_resume	NULL
+#endif /* CONFIG_PM */
+
+static int cs4271_probe(struct snd_soc_codec *codec)
+{
+	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+	struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
+	int ret;
+	int gpio_nreset = -EINVAL;
+	int gpio_disable = -EINVAL;
+
+	codec->control_data = cs4271->control_data;
+
+	if (cs4271plat) {
+		if (gpio_is_valid(cs4271plat->gpio_nreset))
+			gpio_nreset = cs4271plat->gpio_nreset;
+		if (gpio_is_valid(cs4271plat->gpio_disable))
+			gpio_disable = cs4271plat->gpio_disable;
+	}
+
+	if (gpio_disable >= 0)
+		if (gpio_request(gpio_disable, "CS4271 Disable"))
+			gpio_disable = -EINVAL;
+	if (gpio_disable >= 0)
+		gpio_direction_output(gpio_disable, 0);
+
+	if (gpio_nreset >= 0)
+		if (gpio_request(gpio_nreset, "CS4271 Reset"))
+			gpio_nreset = -EINVAL;
+	if (gpio_nreset >= 0) {
+		/* Reset codec */
+		gpio_direction_output(gpio_nreset, 0);
+		udelay(1);
+		gpio_set_value(gpio_nreset, 1);
+		/* Give the codec time to wake up */
+		udelay(1);
+	}
+
+	cs4271->gpio_nreset = gpio_nreset;
+	cs4271->gpio_disable = gpio_disable;
+
+	/*
+	 * In case of I2C, chip address specified in board data.
+	 * So cache IO operations use 8 bit codec register address.
+	 * In case of SPI, chip address and register address
+	 * passed together as 16 bit value.
+	 * Anyway, register address is masked with 0xFF inside
+	 * soc-cache code.
+	 */
+	if (cs4271->bus_type == SND_SOC_SPI)
+		ret = snd_soc_codec_set_cache_io(codec, 16, 8,
+			cs4271->bus_type);
+	else
+		ret = snd_soc_codec_set_cache_io(codec, 8, 8,
+			cs4271->bus_type);
+	if (ret) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	ret = snd_soc_update_bits(codec, CS4271_MODE2, 0,
+		CS4271_MODE2_PDN | CS4271_MODE2_CPEN);
+	if (ret < 0)
+		return ret;
+	ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0);
+	if (ret < 0)
+		return ret;
+	/* Power-up sequence requires 85 uS */
+	udelay(85);
+
+	return snd_soc_add_controls(codec, cs4271_snd_controls,
+		ARRAY_SIZE(cs4271_snd_controls));
+}
+
+static int cs4271_remove(struct snd_soc_codec *codec)
+{
+	struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+	int gpio_nreset, gpio_disable;
+
+	gpio_nreset = cs4271->gpio_nreset;
+	gpio_disable = cs4271->gpio_disable;
+
+	if (gpio_is_valid(gpio_nreset)) {
+		/* Set codec to the reset state */
+		gpio_set_value(gpio_nreset, 0);
+		gpio_free(gpio_nreset);
+	}
+
+	if (gpio_is_valid(gpio_disable))
+		gpio_free(gpio_disable);
+
+	return 0;
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
+	.probe			= cs4271_probe,
+	.remove			= cs4271_remove,
+	.suspend		= cs4271_soc_suspend,
+	.resume			= cs4271_soc_resume,
+	.reg_cache_default	= cs4271_dflt_reg,
+	.reg_cache_size		= ARRAY_SIZE(cs4271_dflt_reg),
+	.reg_word_size		= sizeof(cs4271_dflt_reg[0]),
+	.compress_type		= SND_SOC_FLAT_COMPRESSION,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit cs4271_spi_probe(struct spi_device *spi)
+{
+	struct cs4271_private *cs4271;
+
+	cs4271 = devm_kzalloc(&spi->dev, sizeof(*cs4271), GFP_KERNEL);
+	if (!cs4271)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, cs4271);
+	cs4271->control_data = spi;
+	cs4271->bus_type = SND_SOC_SPI;
+
+	return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271,
+		&cs4271_dai, 1);
+}
+
+static int __devexit cs4271_spi_remove(struct spi_device *spi)
+{
+	snd_soc_unregister_codec(&spi->dev);
+	return 0;
+}
+
+static struct spi_driver cs4271_spi_driver = {
+	.driver = {
+		.name	= "cs4271",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= cs4271_spi_probe,
+	.remove		= __devexit_p(cs4271_spi_remove),
+};
+#endif /* defined(CONFIG_SPI_MASTER) */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static struct i2c_device_id cs4271_i2c_id[] = {
+	{"cs4271", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+
+static int __devinit cs4271_i2c_probe(struct i2c_client *client,
+				      const struct i2c_device_id *id)
+{
+	struct cs4271_private *cs4271;
+
+	cs4271 = devm_kzalloc(&client->dev, sizeof(*cs4271), GFP_KERNEL);
+	if (!cs4271)
+		return -ENOMEM;
+
+	i2c_set_clientdata(client, cs4271);
+	cs4271->control_data = client;
+	cs4271->bus_type = SND_SOC_I2C;
+
+	return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271,
+		&cs4271_dai, 1);
+}
+
+static int __devexit cs4271_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static struct i2c_driver cs4271_i2c_driver = {
+	.driver = {
+		.name	= "cs4271",
+		.owner	= THIS_MODULE,
+	},
+	.id_table	= cs4271_i2c_id,
+	.probe		= cs4271_i2c_probe,
+	.remove		= __devexit_p(cs4271_i2c_remove),
+};
+#endif /* defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) */
+
+/*
+ * We only register our serial bus driver here without
+ * assignment to particular chip. So if any of the below
+ * fails, there is some problem with I2C or SPI subsystem.
+ * In most cases this module will be compiled with support
+ * of only one serial bus.
+ */
+static int __init cs4271_modinit(void)
+{
+	int ret;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	ret = i2c_add_driver(&cs4271_i2c_driver);
+	if (ret) {
+		pr_err("Failed to register CS4271 I2C driver: %d\n", ret);
+		return ret;
+	}
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+	ret = spi_register_driver(&cs4271_spi_driver);
+	if (ret) {
+		pr_err("Failed to register CS4271 SPI driver: %d\n", ret);
+		return ret;
+	}
+#endif
+
+	return 0;
+}
+module_init(cs4271_modinit);
+
+static void __exit cs4271_modexit(void)
+{
+#if defined(CONFIG_SPI_MASTER)
+	spi_unregister_driver(&cs4271_spi_driver);
+#endif
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+	i2c_del_driver(&cs4271_i2c_driver);
+#endif
+}
+module_exit(cs4271_modexit);
+
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 89498f9..bd0517c 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -608,7 +608,7 @@
        { 0xFF, 0x00, 1 }, /* FF */
 };
 
-static int max98088_volatile_register(unsigned int reg)
+static int max98088_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        return max98088_access[reg].vol;
 }
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
new file mode 100644
index 0000000..593632c
--- /dev/null
+++ b/sound/soc/codecs/sn95031.c
@@ -0,0 +1,495 @@
+/*
+ *  sn95031.c -  TI sn95031 Codec driver
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/intel_scu_ipc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include "sn95031.h"
+
+#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100)
+#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+/*
+ * todo:
+ * capture paths
+ * jack detection
+ * PM functions
+ */
+
+static inline unsigned int sn95031_read(struct snd_soc_codec *codec,
+			unsigned int reg)
+{
+	u8 value = 0;
+	int ret;
+
+	ret = intel_scu_ipc_ioread8(reg, &value);
+	if (ret)
+		pr_err("read of %x failed, err %d\n", reg, ret);
+	return value;
+
+}
+
+static inline int sn95031_write(struct snd_soc_codec *codec,
+			unsigned int reg, unsigned int value)
+{
+	int ret;
+
+	ret = intel_scu_ipc_iowrite8(reg, value);
+	if (ret)
+		pr_err("write of %x failed, err %d\n", reg, ret);
+	return ret;
+}
+
+static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
+		enum snd_soc_bias_level level)
+{
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+			pr_debug("vaud_bias powering up pll\n");
+			/* power up the pll */
+			snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5));
+			/* enable pcm 2 */
+			snd_soc_update_bits(codec, SN95031_PCM2C2,
+					BIT(0), BIT(0));
+		}
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+			pr_debug("vaud_bias power up rail\n");
+			/* power up the rail */
+			snd_soc_write(codec, SN95031_VAUD,
+					BIT(2)|BIT(1)|BIT(0));
+			msleep(1);
+		} else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+			/* turn off pcm */
+			pr_debug("vaud_bias power dn pcm\n");
+			snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0);
+			snd_soc_write(codec, SN95031_AUDPLLCTRL, 0);
+		}
+		break;
+
+
+	case SND_SOC_BIAS_OFF:
+		pr_debug("vaud_bias _OFF doing rail shutdown\n");
+		snd_soc_write(codec, SN95031_VAUD, BIT(3));
+		break;
+	}
+
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static int sn95031_vhs_event(struct snd_soc_dapm_widget *w,
+		    struct snd_kcontrol *kcontrol, int event)
+{
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		pr_debug("VHS SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
+		/* power up the rail */
+		snd_soc_write(w->codec, SN95031_VHSP, 0x3D);
+		snd_soc_write(w->codec, SN95031_VHSN, 0x3F);
+		msleep(1);
+	} else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		pr_debug("VHS SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
+		snd_soc_write(w->codec, SN95031_VHSP, 0xC4);
+		snd_soc_write(w->codec, SN95031_VHSN, 0x04);
+	}
+	return 0;
+}
+
+static int sn95031_vihf_event(struct snd_soc_dapm_widget *w,
+		    struct snd_kcontrol *kcontrol, int event)
+{
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		pr_debug("VIHF SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
+		/* power up the rail */
+		snd_soc_write(w->codec, SN95031_VIHF, 0x27);
+		msleep(1);
+	} else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		pr_debug("VIHF SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
+		snd_soc_write(w->codec, SN95031_VIHF, 0x24);
+	}
+	return 0;
+}
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = {
+
+	/* all end points mic, hs etc */
+	SND_SOC_DAPM_OUTPUT("HPOUTL"),
+	SND_SOC_DAPM_OUTPUT("HPOUTR"),
+	SND_SOC_DAPM_OUTPUT("EPOUT"),
+	SND_SOC_DAPM_OUTPUT("IHFOUTL"),
+	SND_SOC_DAPM_OUTPUT("IHFOUTR"),
+	SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+	SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+	SND_SOC_DAPM_OUTPUT("VIB1OUT"),
+	SND_SOC_DAPM_OUTPUT("VIB2OUT"),
+
+	SND_SOC_DAPM_SUPPLY("Headset Rail", SND_SOC_NOPM, 0, 0,
+			sn95031_vhs_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("Speaker Rail", SND_SOC_NOPM, 0, 0,
+			sn95031_vihf_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* playback path driver enables */
+	SND_SOC_DAPM_PGA("Headset Left Playback",
+			SN95031_DRIVEREN, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Headset Right Playback",
+			SN95031_DRIVEREN, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Speaker Left Playback",
+			SN95031_DRIVEREN, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Speaker Right Playback",
+			SN95031_DRIVEREN, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Vibra1 Playback",
+			SN95031_DRIVEREN, 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Vibra2 Playback",
+			SN95031_DRIVEREN, 5, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Earpiece Playback",
+			SN95031_DRIVEREN, 6, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Lineout Left Playback",
+			SN95031_LOCTL, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Lineout Right Playback",
+			SN95031_LOCTL, 4, 0, NULL, 0),
+
+	/* playback path filter enable */
+	SND_SOC_DAPM_PGA("Headset Left Filter",
+			SN95031_HSEPRXCTRL, 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Headset Right Filter",
+			SN95031_HSEPRXCTRL, 5, 0,  NULL, 0),
+	SND_SOC_DAPM_PGA("Speaker Left Filter",
+			SN95031_IHFRXCTRL, 0, 0,  NULL, 0),
+	SND_SOC_DAPM_PGA("Speaker Right Filter",
+			SN95031_IHFRXCTRL, 1, 0,  NULL, 0),
+
+	/* DACs */
+	SND_SOC_DAPM_DAC("HSDAC Left", "Headset",
+			SN95031_DACCONFIG, 0, 0),
+	SND_SOC_DAPM_DAC("HSDAC Right", "Headset",
+			SN95031_DACCONFIG, 1, 0),
+	SND_SOC_DAPM_DAC("IHFDAC Left", "Speaker",
+			SN95031_DACCONFIG, 2, 0),
+	SND_SOC_DAPM_DAC("IHFDAC Right", "Speaker",
+			SN95031_DACCONFIG, 3, 0),
+	SND_SOC_DAPM_DAC("Vibra1 DAC", "Vibra1",
+			SN95031_VIB1C5, 1, 0),
+	SND_SOC_DAPM_DAC("Vibra2 DAC", "Vibra2",
+			SN95031_VIB2C5, 1, 0),
+};
+
+static const struct snd_soc_dapm_route sn95031_audio_map[] = {
+	/* headset and earpiece map */
+	{ "HPOUTL", NULL, "Headset Left Playback" },
+	{ "HPOUTR", NULL, "Headset Right Playback" },
+	{ "EPOUT", NULL, "Earpiece Playback" },
+	{ "Headset Left Playback", NULL, "Headset Left Filter"},
+	{ "Headset Right Playback", NULL, "Headset Right Filter"},
+	{ "Earpiece Playback", NULL, "Headset Left Filter"},
+	{ "Headset Left Filter", NULL, "HSDAC Left"},
+	{ "Headset Right Filter", NULL, "HSDAC Right"},
+	{ "HSDAC Left", NULL, "Headset Rail"},
+	{ "HSDAC Right", NULL, "Headset Rail"},
+
+	/* speaker map */
+	{ "IHFOUTL", "NULL", "Speaker Left Playback"},
+	{ "IHFOUTR", "NULL", "Speaker Right Playback"},
+	{ "Speaker Left Playback", NULL, "Speaker Left Filter"},
+	{ "Speaker Right Playback", NULL, "Speaker Right Filter"},
+	{ "Speaker Left Filter", NULL, "IHFDAC Left"},
+	{ "Speaker Right Filter", NULL, "IHFDAC Right"},
+	{ "IHFDAC Left", NULL, "Speaker Rail"},
+	{ "IHFDAC Right", NULL, "Speaker Rail"},
+
+	/* vibra map */
+	{ "VIB1OUT", NULL, "Vibra1 Playback"},
+	{ "Vibra1 Playback", NULL, "Vibra1 DAC"},
+
+	{ "VIB2OUT", NULL, "Vibra2 Playback"},
+	{ "Vibra2 Playback", NULL, "Vibra2 DAC"},
+
+	/* lineout */
+	{ "LINEOUTL", NULL, "Lineout Left Playback"},
+	{ "LINEOUTR", NULL, "Lineout Right Playback"},
+	{ "Lineout Left Playback", NULL, "Headset Left Filter"},
+	{ "Lineout Left Playback", NULL, "Speaker Left Filter"},
+	{ "Lineout Left Playback", NULL, "Vibra1 DAC"},
+	{ "Lineout Right Playback", NULL, "Headset Right Filter"},
+	{ "Lineout Right Playback", NULL, "Speaker Right Filter"},
+	{ "Lineout Right Playback", NULL, "Vibra2 DAC"},
+};
+
+/* speaker and headset mutes, for audio pops and clicks */
+static int sn95031_pcm_hs_mute(struct snd_soc_dai *dai, int mute)
+{
+	snd_soc_update_bits(dai->codec,
+			SN95031_HSLVOLCTRL, BIT(7), (!mute << 7));
+	snd_soc_update_bits(dai->codec,
+			SN95031_HSRVOLCTRL, BIT(7), (!mute << 7));
+	return 0;
+}
+
+static int sn95031_pcm_spkr_mute(struct snd_soc_dai *dai, int mute)
+{
+	snd_soc_update_bits(dai->codec,
+			SN95031_IHFLVOLCTRL, BIT(7), (!mute << 7));
+	snd_soc_update_bits(dai->codec,
+			SN95031_IHFRVOLCTRL, BIT(7), (!mute << 7));
+	return 0;
+}
+
+int sn95031_pcm_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	unsigned int format, rate;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		format = BIT(4)|BIT(5);
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		format = 0;
+		break;
+	default:
+		return -EINVAL;
+	}
+	snd_soc_update_bits(dai->codec, SN95031_PCM2C2,
+			BIT(4)|BIT(5), format);
+
+	switch (params_rate(params)) {
+	case 48000:
+		pr_debug("RATE_48000\n");
+		rate = 0;
+		break;
+
+	case 44100:
+		pr_debug("RATE_44100\n");
+		rate = BIT(7);
+		break;
+
+	default:
+		pr_err("ERR rate %d\n", params_rate(params));
+		return -EINVAL;
+	}
+	snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate);
+
+	return 0;
+}
+
+/* Codec DAI section */
+static struct snd_soc_dai_ops sn95031_headset_dai_ops = {
+	.digital_mute	= sn95031_pcm_hs_mute,
+	.hw_params	= sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_speaker_dai_ops = {
+	.digital_mute	= sn95031_pcm_spkr_mute,
+	.hw_params	= sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_vib1_dai_ops = {
+	.hw_params	= sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_vib2_dai_ops = {
+	.hw_params	= sn95031_pcm_hw_params,
+};
+
+struct snd_soc_dai_driver sn95031_dais[] = {
+{
+	.name = "SN95031 Headset",
+	.playback = {
+		.stream_name = "Headset",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SN95031_RATES,
+		.formats = SN95031_FORMATS,
+	},
+	.ops = &sn95031_headset_dai_ops,
+},
+{	.name = "SN95031 Speaker",
+	.playback = {
+		.stream_name = "Speaker",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = SN95031_RATES,
+		.formats = SN95031_FORMATS,
+	},
+	.ops = &sn95031_speaker_dai_ops,
+},
+{	.name = "SN95031 Vibra1",
+	.playback = {
+		.stream_name = "Vibra1",
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SN95031_RATES,
+		.formats = SN95031_FORMATS,
+	},
+	.ops = &sn95031_vib1_dai_ops,
+},
+{	.name = "SN95031 Vibra2",
+	.playback = {
+		.stream_name = "Vibra2",
+		.channels_min = 1,
+		.channels_max = 1,
+		.rates = SN95031_RATES,
+		.formats = SN95031_FORMATS,
+	},
+	.ops = &sn95031_vib2_dai_ops,
+},
+};
+
+/* codec registration */
+static int sn95031_codec_probe(struct snd_soc_codec *codec)
+{
+	int ret;
+
+	pr_debug("codec_probe called\n");
+
+	codec->dapm.bias_level = SND_SOC_BIAS_OFF;
+	codec->dapm.idle_bias_off = 1;
+
+	/* PCM interface config
+	 * This sets the pcm rx slot conguration to max 6 slots
+	 * for max 4 dais (2 stereo and 2 mono)
+	 */
+	snd_soc_write(codec, SN95031_PCM2RXSLOT01, 0x10);
+	snd_soc_write(codec, SN95031_PCM2RXSLOT23, 0x32);
+	snd_soc_write(codec, SN95031_PCM2RXSLOT45, 0x54);
+	/* pcm port setting
+	 * This sets the pcm port to slave and clock at 19.2Mhz which
+	 * can support 6slots, sampling rate set per stream in hw-params
+	 */
+	snd_soc_write(codec, SN95031_PCM1C1, 0x00);
+	snd_soc_write(codec, SN95031_PCM2C1, 0x01);
+	snd_soc_write(codec, SN95031_PCM2C2, 0x0A);
+	snd_soc_write(codec, SN95031_HSMIXER, BIT(0)|BIT(4));
+	/* vendor vibra workround, the vibras are muted by
+	 * custom register so unmute them
+	 */
+	snd_soc_write(codec, SN95031_SSR5, 0x80);
+	snd_soc_write(codec, SN95031_SSR6, 0x80);
+	snd_soc_write(codec, SN95031_VIB1C5, 0x00);
+	snd_soc_write(codec, SN95031_VIB2C5, 0x00);
+	/* configure vibras for pcm port */
+	snd_soc_write(codec, SN95031_VIB1C3, 0x00);
+	snd_soc_write(codec, SN95031_VIB2C3, 0x00);
+
+	/* soft mute ramp time */
+	snd_soc_write(codec, SN95031_SOFTMUTE, 0x3);
+	/* fix the initial volume at 1dB,
+	 * default in +9dB,
+	 * 1dB give optimal swing on DAC, amps
+	 */
+	snd_soc_write(codec, SN95031_HSLVOLCTRL, 0x08);
+	snd_soc_write(codec, SN95031_HSRVOLCTRL, 0x08);
+	snd_soc_write(codec, SN95031_IHFLVOLCTRL, 0x08);
+	snd_soc_write(codec, SN95031_IHFRVOLCTRL, 0x08);
+	/* dac mode and lineout workaround */
+	snd_soc_write(codec, SN95031_SSR2, 0x10);
+	snd_soc_write(codec, SN95031_SSR3, 0x40);
+
+	ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets,
+				ARRAY_SIZE(sn95031_dapm_widgets));
+	if (ret)
+		pr_err("soc_dapm_new_control failed %d", ret);
+	ret = snd_soc_dapm_add_routes(&codec->dapm, sn95031_audio_map,
+				ARRAY_SIZE(sn95031_audio_map));
+	if (ret)
+		pr_err("soc_dapm_add_routes failed %d", ret);
+
+	return ret;
+}
+
+static int sn95031_codec_remove(struct snd_soc_codec *codec)
+{
+	pr_debug("codec_remove called\n");
+	sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+struct snd_soc_codec_driver sn95031_codec = {
+	.probe		= sn95031_codec_probe,
+	.remove		= sn95031_codec_remove,
+	.read		= sn95031_read,
+	.write		= sn95031_write,
+	.set_bias_level	= sn95031_set_vaud_bias,
+};
+
+static int __devinit sn95031_device_probe(struct platform_device *pdev)
+{
+	pr_debug("codec device probe called for %s\n", dev_name(&pdev->dev));
+	return snd_soc_register_codec(&pdev->dev, &sn95031_codec,
+			sn95031_dais, ARRAY_SIZE(sn95031_dais));
+}
+
+static int __devexit sn95031_device_remove(struct platform_device *pdev)
+{
+	pr_debug("codec device remove called\n");
+	snd_soc_unregister_codec(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver sn95031_codec_driver = {
+	.driver		= {
+		.name		= "sn95031",
+		.owner		= THIS_MODULE,
+	},
+	.probe		= sn95031_device_probe,
+	.remove		= sn95031_device_remove,
+};
+
+static int __init sn95031_init(void)
+{
+	pr_debug("driver init called\n");
+	return platform_driver_register(&sn95031_codec_driver);
+}
+module_init(sn95031_init);
+
+static void __exit sn95031_exit(void)
+{
+	pr_debug("driver exit called\n");
+	platform_driver_unregister(&sn95031_codec_driver);
+}
+module_exit(sn95031_exit);
+
+MODULE_DESCRIPTION("ASoC TI SN95031 codec driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sn95031");
diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h
new file mode 100644
index 0000000..e2b17d9
--- /dev/null
+++ b/sound/soc/codecs/sn95031.h
@@ -0,0 +1,99 @@
+/*
+ *  sn95031.h - TI sn95031 Codec driver
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#ifndef _SN95031_H
+#define _SN95031_H
+
+/*register map*/
+#define SN95031_VAUD			0xDB
+#define SN95031_VHSP			0xDC
+#define SN95031_VHSN			0xDD
+#define SN95031_VIHF			0xC9
+
+#define SN95031_AUDPLLCTRL		0x240
+#define SN95031_DMICBUF0123		0x241
+#define SN95031_DMICBUF45		0x242
+#define SN95031_DMICGPO			0x244
+#define SN95031_DMICMUX			0x245
+#define SN95031_DMICLK			0x246
+#define SN95031_MICBIAS			0x247
+#define SN95031_ADCCONFIG		0x248
+#define SN95031_MICAMP1			0x249
+#define SN95031_MICAMP2			0x24A
+#define SN95031_NOISEMUX		0x24B
+#define SN95031_AUDIOMUX12		0x24C
+#define SN95031_AUDIOMUX34		0x24D
+#define SN95031_AUDIOSINC		0x24E
+#define SN95031_AUDIOTXEN		0x24F
+#define SN95031_HSEPRXCTRL		0x250
+#define SN95031_IHFRXCTRL		0x251
+#define SN95031_HSMIXER			0x256
+#define SN95031_DACCONFIG		0x257
+#define SN95031_SOFTMUTE		0x258
+#define SN95031_HSLVOLCTRL		0x259
+#define SN95031_HSRVOLCTRL		0x25A
+#define SN95031_IHFLVOLCTRL		0x25B
+#define SN95031_IHFRVOLCTRL		0x25C
+#define SN95031_DRIVEREN		0x25D
+#define SN95031_LOCTL			0x25E
+#define SN95031_VIB1C1			0x25F
+#define SN95031_VIB1C2			0x260
+#define SN95031_VIB1C3			0x261
+#define SN95031_VIB1SPIPCM1		0x262
+#define SN95031_VIB1SPIPCM2		0x263
+#define SN95031_VIB1C5			0x264
+#define SN95031_VIB2C1			0x265
+#define SN95031_VIB2C2			0x266
+#define SN95031_VIB2C3			0x267
+#define SN95031_VIB2SPIPCM1		0x268
+#define SN95031_VIB2SPIPCM2		0x269
+#define SN95031_VIB2C5			0x26A
+#define SN95031_BTNCTRL1		0x26B
+#define SN95031_BTNCTRL2		0x26C
+#define SN95031_PCM1TXSLOT01		0x26D
+#define SN95031_PCM1TXSLOT23		0x26E
+#define SN95031_PCM1TXSLOT45		0x26F
+#define SN95031_PCM1RXSLOT0_3		0x270
+#define SN95031_PCM1RXSLOT45		0x271
+#define SN95031_PCM2TXSLOT01		0x272
+#define SN95031_PCM2TXSLOT23		0x273
+#define SN95031_PCM2TXSLOT45		0x274
+#define SN95031_PCM2RXSLOT01		0x275
+#define SN95031_PCM2RXSLOT23		0x276
+#define SN95031_PCM2RXSLOT45		0x277
+#define SN95031_PCM1C1			0x278
+#define SN95031_PCM1C2			0x279
+#define SN95031_PCM1C3			0x27A
+#define SN95031_PCM2C1			0x27B
+#define SN95031_PCM2C2			0x27C
+/*end codec register defn*/
+
+/*vendor defn these are not part of avp*/
+#define SN95031_SSR2			0x381
+#define SN95031_SSR3			0x382
+#define SN95031_SSR5			0x384
+#define SN95031_SSR6			0x385
+
+#endif
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index 5eb2f50..4fd4d8d 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -58,7 +58,7 @@
 	0x0000,     /* R8 - ZERO_DETECT */
 };
 
-static int wm8523_volatile_register(unsigned int reg)
+static int wm8523_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM8523_DEVICE_ID:
@@ -414,7 +414,6 @@
 static int wm8523_probe(struct snd_soc_codec *codec)
 {
 	struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
-	u16 *reg_cache = codec->reg_cache;
 	int ret, i;
 
 	codec->hw_write = (hw_write_t)i2c_master_send;
@@ -471,8 +470,9 @@
 	}
 
 	/* Change some default settings - latch VU and enable ZC */
-	reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU;
-	reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC;
+	snd_soc_update_bits(codec, WM8523_DAC_GAINR,
+			    WM8523_DACR_VU, WM8523_DACR_VU);
+	snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC);
 
 	wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 494f2d3..25af901 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -421,7 +421,6 @@
 static int wm8741_probe(struct snd_soc_codec *codec)
 {
 	struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
-	u16 *reg_cache = codec->reg_cache;
 	int ret = 0;
 
 	ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8741->control_type);
@@ -437,10 +436,14 @@
 	}
 
 	/* Change some default settings - latch VU */
-	reg_cache[WM8741_DACLLSB_ATTENUATION] |= WM8741_UPDATELL;
-	reg_cache[WM8741_DACLMSB_ATTENUATION] |= WM8741_UPDATELM;
-	reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERL;
-	reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERM;
+	snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
+			    WM8741_UPDATELL, WM8741_UPDATELL);
+	snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
+			    WM8741_UPDATELM, WM8741_UPDATELM);
+	snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+			    WM8741_UPDATERL, WM8741_UPDATERL);
+	snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+			    WM8741_UPDATERM, WM8741_UPDATERM);
 
 	snd_soc_add_controls(codec, wm8741_snd_controls,
 			     ARRAY_SIZE(wm8741_snd_controls));
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index 6dae1b4..6785688 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -175,7 +175,7 @@
 	return 0;
 }
 
-static int wm8804_volatile(unsigned int reg)
+static int wm8804_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM8804_RST_DEVID1:
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index cd09599..449ea09 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -180,7 +180,7 @@
 	/* Remaining registers all zero */
 };
 
-static int wm8900_volatile_register(unsigned int reg)
+static int wm8900_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM8900_REG_ID:
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 987476a..9c4f2c4 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -2,6 +2,7 @@
  * wm8903.c  --  WM8903 ALSA SoC Audio driver
  *
  * Copyright 2008 Wolfson Microelectronics
+ * Copyright 2011 NVIDIA, Inc.
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
@@ -19,6 +20,7 @@
 #include <linux/init.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
@@ -213,6 +215,7 @@
 };
 
 struct wm8903_priv {
+	struct snd_soc_codec *codec;
 
 	int sysclk;
 	int irq;
@@ -230,9 +233,13 @@
 	int mic_short;
 	int mic_last_report;
 	int mic_delay;
+
+#ifdef CONFIG_GPIOLIB
+	struct gpio_chip gpio_chip;
+#endif
 };
 
-static int wm8903_volatile_register(unsigned int reg)
+static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM8903_SW_RESET_AND_ID:
@@ -1635,6 +1642,119 @@
 	return 0;
 }
 
+#ifdef CONFIG_GPIOLIB
+static inline struct wm8903_priv *gpio_to_wm8903(struct gpio_chip *chip)
+{
+	return container_of(chip, struct wm8903_priv, gpio_chip);
+}
+
+static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset >= WM8903_NUM_GPIO)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+	struct snd_soc_codec *codec = wm8903->codec;
+	unsigned int mask, val;
+
+	mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK;
+	val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) |
+		WM8903_GP1_DIR;
+
+	return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
+				   mask, val);
+}
+
+static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+	struct snd_soc_codec *codec = wm8903->codec;
+	int reg;
+
+	reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset);
+
+	return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT;
+}
+
+static int wm8903_gpio_direction_out(struct gpio_chip *chip,
+				     unsigned offset, int value)
+{
+	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+	struct snd_soc_codec *codec = wm8903->codec;
+	unsigned int mask, val;
+
+	mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK;
+	val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) |
+		(value << WM8903_GP2_LVL_SHIFT);
+
+	return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
+				   mask, val);
+}
+
+static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+	struct snd_soc_codec *codec = wm8903->codec;
+
+	snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
+			    WM8903_GP1_LVL_MASK, value << WM8903_GP1_LVL_SHIFT);
+}
+
+static struct gpio_chip wm8903_template_chip = {
+	.label			= "wm8903",
+	.owner			= THIS_MODULE,
+	.request		= wm8903_gpio_request,
+	.direction_input	= wm8903_gpio_direction_in,
+	.get			= wm8903_gpio_get,
+	.direction_output	= wm8903_gpio_direction_out,
+	.set			= wm8903_gpio_set,
+	.can_sleep		= 1,
+};
+
+static void wm8903_init_gpio(struct snd_soc_codec *codec)
+{
+	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+	struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev);
+	int ret;
+
+	wm8903->gpio_chip = wm8903_template_chip;
+	wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO;
+	wm8903->gpio_chip.dev = codec->dev;
+
+	if (pdata && pdata->gpio_base)
+		wm8903->gpio_chip.base = pdata->gpio_base;
+	else
+		wm8903->gpio_chip.base = -1;
+
+	ret = gpiochip_add(&wm8903->gpio_chip);
+	if (ret != 0)
+		dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void wm8903_free_gpio(struct snd_soc_codec *codec)
+{
+	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	ret = gpiochip_remove(&wm8903->gpio_chip);
+	if (ret != 0)
+		dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
+}
+#else
+static void wm8903_init_gpio(struct snd_soc_codec *codec)
+{
+}
+
+static void wm8903_free_gpio(struct snd_soc_codec *codec)
+{
+}
+#endif
+
 static int wm8903_probe(struct snd_soc_codec *codec)
 {
 	struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev);
@@ -1643,6 +1763,7 @@
 	int trigger, irq_pol;
 	u16 val;
 
+	wm8903->codec = codec;
 	init_completion(&wm8903->wseq);
 
 	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
@@ -1667,7 +1788,7 @@
 	/* Set up GPIOs and microphone detection */
 	if (pdata) {
 		for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) {
-			if (!pdata->gpio_cfg[i])
+			if (pdata->gpio_cfg[i] == WM8903_GPIO_NO_CONFIG)
 				continue;
 
 			snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i,
@@ -1749,12 +1870,15 @@
 				ARRAY_SIZE(wm8903_snd_controls));
 	wm8903_add_widgets(codec);
 
+	wm8903_init_gpio(codec);
+
 	return ret;
 }
 
 /* power down chip */
 static int wm8903_remove(struct snd_soc_codec *codec)
 {
+	wm8903_free_gpio(codec);
 	wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 9de44a4..443ae58 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -596,7 +596,7 @@
 	{ 0x003F, 0x003F, 0 }, /* R248 - FLL NCO Test 1 */
 };
 
-static int wm8904_volatile_register(unsigned int reg)
+static int wm8904_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	return wm8904_access[reg].vol;
 }
@@ -2436,19 +2436,28 @@
 	}
 
 	/* Change some default settings - latch VU and enable ZC */
-	reg_cache[WM8904_ADC_DIGITAL_VOLUME_LEFT] |= WM8904_ADC_VU;
-	reg_cache[WM8904_ADC_DIGITAL_VOLUME_RIGHT] |= WM8904_ADC_VU;
-	reg_cache[WM8904_DAC_DIGITAL_VOLUME_LEFT] |= WM8904_DAC_VU;
-	reg_cache[WM8904_DAC_DIGITAL_VOLUME_RIGHT] |= WM8904_DAC_VU;
-	reg_cache[WM8904_ANALOGUE_OUT1_LEFT] |= WM8904_HPOUT_VU |
-		WM8904_HPOUTLZC;
-	reg_cache[WM8904_ANALOGUE_OUT1_RIGHT] |= WM8904_HPOUT_VU |
-		WM8904_HPOUTRZC;
-	reg_cache[WM8904_ANALOGUE_OUT2_LEFT] |= WM8904_LINEOUT_VU |
-		WM8904_LINEOUTLZC;
-	reg_cache[WM8904_ANALOGUE_OUT2_RIGHT] |= WM8904_LINEOUT_VU |
-		WM8904_LINEOUTRZC;
-	reg_cache[WM8904_CLOCK_RATES_0] &= ~WM8904_SR_MODE;
+	snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_LEFT,
+			    WM8904_ADC_VU, WM8904_ADC_VU);
+	snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_RIGHT,
+			    WM8904_ADC_VU, WM8904_ADC_VU);
+	snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_LEFT,
+			    WM8904_DAC_VU, WM8904_DAC_VU);
+	snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_RIGHT,
+			    WM8904_DAC_VU, WM8904_DAC_VU);
+	snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_LEFT,
+			    WM8904_HPOUT_VU | WM8904_HPOUTLZC,
+			    WM8904_HPOUT_VU | WM8904_HPOUTLZC);
+	snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_RIGHT,
+			    WM8904_HPOUT_VU | WM8904_HPOUTRZC,
+			    WM8904_HPOUT_VU | WM8904_HPOUTRZC);
+	snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_LEFT,
+			    WM8904_LINEOUT_VU | WM8904_LINEOUTLZC,
+			    WM8904_LINEOUT_VU | WM8904_LINEOUTLZC);
+	snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_RIGHT,
+			    WM8904_LINEOUT_VU | WM8904_LINEOUTRZC,
+			    WM8904_LINEOUT_VU | WM8904_LINEOUTRZC);
+	snd_soc_update_bits(codec, WM8904_CLOCK_RATES_0,
+			    WM8904_SR_MODE, 0);
 
 	/* Apply configuration from the platform data. */
 	if (wm8904->pdata) {
@@ -2469,10 +2478,12 @@
 	/* Set Class W by default - this will be managed by the Class
 	 * G widget at runtime where bypass paths are available.
 	 */
-	reg_cache[WM8904_CLASS_W_0] |= WM8904_CP_DYN_PWR;
+	snd_soc_update_bits(codec, WM8904_CLASS_W_0,
+			    WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR);
 
 	/* Use normal bias source */
-	reg_cache[WM8904_BIAS_CONTROL_0] &= ~WM8904_POBCTRL;
+	snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+			    WM8904_POBCTRL, 0);
 
 	wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 7167dfc..5e0214d 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -934,16 +934,27 @@
 	}
 
 	/* Change some default settings - latch VU and enable ZC */
-	reg_cache[WM8955_LEFT_DAC_VOLUME] |= WM8955_LDVU;
-	reg_cache[WM8955_RIGHT_DAC_VOLUME] |= WM8955_RDVU;
-	reg_cache[WM8955_LOUT1_VOLUME] |= WM8955_LO1VU | WM8955_LO1ZC;
-	reg_cache[WM8955_ROUT1_VOLUME] |= WM8955_RO1VU | WM8955_RO1ZC;
-	reg_cache[WM8955_LOUT2_VOLUME] |= WM8955_LO2VU | WM8955_LO2ZC;
-	reg_cache[WM8955_ROUT2_VOLUME] |= WM8955_RO2VU | WM8955_RO2ZC;
-	reg_cache[WM8955_MONOOUT_VOLUME] |= WM8955_MOZC;
+	snd_soc_update_bits(codec, WM8955_LEFT_DAC_VOLUME,
+			    WM8955_LDVU, WM8955_LDVU);
+	snd_soc_update_bits(codec, WM8955_RIGHT_DAC_VOLUME,
+			    WM8955_RDVU, WM8955_RDVU);
+	snd_soc_update_bits(codec, WM8955_LOUT1_VOLUME,
+			    WM8955_LO1VU | WM8955_LO1ZC,
+			    WM8955_LO1VU | WM8955_LO1ZC);
+	snd_soc_update_bits(codec, WM8955_ROUT1_VOLUME,
+			    WM8955_RO1VU | WM8955_RO1ZC,
+			    WM8955_RO1VU | WM8955_RO1ZC);
+	snd_soc_update_bits(codec, WM8955_LOUT2_VOLUME,
+			    WM8955_LO2VU | WM8955_LO2ZC,
+			    WM8955_LO2VU | WM8955_LO2ZC);
+	snd_soc_update_bits(codec, WM8955_ROUT2_VOLUME,
+			    WM8955_RO2VU | WM8955_RO2ZC,
+			    WM8955_RO2VU | WM8955_RO2ZC);
+	snd_soc_update_bits(codec, WM8955_MONOOUT_VOLUME,
+			    WM8955_MOZC, WM8955_MOZC);
 
 	/* Also enable adaptive bass boost by default */
-	reg_cache[WM8955_BASS_CONTROL] |= WM8955_BB;
+	snd_soc_update_bits(codec, WM8955_BASS_CONTROL, WM8955_BB, WM8955_BB);
 
 	/* Set platform data values */
 	if (pdata) {
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index 55252e7..cdee810 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -291,7 +291,7 @@
 	int sysclk;
 };
 
-static int wm8961_volatile_register(unsigned int reg)
+static int wm8961_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM8961_SOFTWARE_RESET:
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index b9cb1fc..5c7b730 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -1938,7 +1938,7 @@
 	[21139] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21139 - VSS_XTS32_0 */
 };
 
-static int wm8962_volatile_register(unsigned int reg)
+static int wm8962_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	if (wm8962_reg_access[reg].vol)
 		return 1;
@@ -1946,7 +1946,7 @@
 		return 0;
 }
 
-static int wm8962_readable_register(unsigned int reg)
+static int wm8962_readable_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	if (wm8962_reg_access[reg].read)
 		return 1;
@@ -3822,16 +3822,26 @@
 	}
 
 	/* Latch volume update bits */
-	reg_cache[WM8962_LEFT_INPUT_VOLUME] |= WM8962_IN_VU;
-	reg_cache[WM8962_RIGHT_INPUT_VOLUME] |= WM8962_IN_VU;
-	reg_cache[WM8962_LEFT_ADC_VOLUME] |= WM8962_ADC_VU;
-	reg_cache[WM8962_RIGHT_ADC_VOLUME] |= WM8962_ADC_VU;
-	reg_cache[WM8962_LEFT_DAC_VOLUME] |= WM8962_DAC_VU;
-	reg_cache[WM8962_RIGHT_DAC_VOLUME] |= WM8962_DAC_VU;
-	reg_cache[WM8962_SPKOUTL_VOLUME] |= WM8962_SPKOUT_VU;
-	reg_cache[WM8962_SPKOUTR_VOLUME] |= WM8962_SPKOUT_VU;
-	reg_cache[WM8962_HPOUTL_VOLUME] |= WM8962_HPOUT_VU;
-	reg_cache[WM8962_HPOUTR_VOLUME] |= WM8962_HPOUT_VU;
+	snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME,
+			    WM8962_IN_VU, WM8962_IN_VU);
+	snd_soc_update_bits(codec, WM8962_RIGHT_INPUT_VOLUME,
+			    WM8962_IN_VU, WM8962_IN_VU);
+	snd_soc_update_bits(codec, WM8962_LEFT_ADC_VOLUME,
+			    WM8962_ADC_VU, WM8962_ADC_VU);
+	snd_soc_update_bits(codec, WM8962_RIGHT_ADC_VOLUME,
+			    WM8962_ADC_VU, WM8962_ADC_VU);
+	snd_soc_update_bits(codec, WM8962_LEFT_DAC_VOLUME,
+			    WM8962_DAC_VU, WM8962_DAC_VU);
+	snd_soc_update_bits(codec, WM8962_RIGHT_DAC_VOLUME,
+			    WM8962_DAC_VU, WM8962_DAC_VU);
+	snd_soc_update_bits(codec, WM8962_SPKOUTL_VOLUME,
+			    WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
+	snd_soc_update_bits(codec, WM8962_SPKOUTR_VOLUME,
+			    WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
+	snd_soc_update_bits(codec, WM8962_HPOUTL_VOLUME,
+			    WM8962_HPOUT_VU, WM8962_HPOUT_VU);
+	snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME,
+			    WM8962_HPOUT_VU, WM8962_HPOUT_VU);
 
 	wm8962_add_widgets(codec);
 
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 4bbc344..30fb48e 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -965,7 +965,7 @@
 	 * written.
 	 */
 	for (i = 0; i < ARRAY_SIZE(update_reg); i++)
-		((u16 *)codec->reg_cache)[update_reg[i]] |= 0x100;
+		snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100);
 
 	/* Reset the codec */
 	ret = snd_soc_write(codec, WM8978_RESET, 0);
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
new file mode 100644
index 0000000..28fdfd6
--- /dev/null
+++ b/sound/soc/codecs/wm8991.c
@@ -0,0 +1,1427 @@
+/*
+ * wm8991.c  --  WM8991 ALSA Soc Audio driver
+ *
+ * Copyright 2007-2010 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#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 <linux/slab.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 <asm/div64.h>
+
+#include "wm8991.h"
+
+struct wm8991_priv {
+	enum snd_soc_control_type control_type;
+	unsigned int pcmclk;
+};
+
+static const u16 wm8991_reg_defs[] = {
+	0x8991,     /* R0  - Reset */
+	0x0000,     /* R1  - Power Management (1) */
+	0x6000,     /* R2  - Power Management (2) */
+	0x0000,     /* R3  - Power Management (3) */
+	0x4050,     /* R4  - Audio Interface (1) */
+	0x4000,     /* R5  - Audio Interface (2) */
+	0x01C8,     /* R6  - Clocking (1) */
+	0x0000,     /* R7  - Clocking (2) */
+	0x0040,     /* R8  - Audio Interface (3) */
+	0x0040,     /* R9  - Audio Interface (4) */
+	0x0004,     /* R10 - DAC CTRL */
+	0x00C0,     /* R11 - Left DAC Digital Volume */
+	0x00C0,     /* R12 - Right DAC Digital Volume */
+	0x0000,     /* R13 - Digital Side Tone */
+	0x0100,     /* R14 - ADC CTRL */
+	0x00C0,     /* R15 - Left ADC Digital Volume */
+	0x00C0,     /* R16 - Right ADC Digital Volume */
+	0x0000,     /* R17 */
+	0x0000,     /* R18 - GPIO CTRL 1 */
+	0x1000,     /* R19 - GPIO1 & GPIO2 */
+	0x1010,     /* R20 - GPIO3 & GPIO4 */
+	0x1010,     /* R21 - GPIO5 & GPIO6 */
+	0x8000,     /* R22 - GPIOCTRL 2 */
+	0x0800,     /* R23 - GPIO_POL */
+	0x008B,     /* R24 - Left Line Input 1&2 Volume */
+	0x008B,     /* R25 - Left Line Input 3&4 Volume */
+	0x008B,     /* R26 - Right Line Input 1&2 Volume */
+	0x008B,     /* R27 - Right Line Input 3&4 Volume */
+	0x0000,     /* R28 - Left Output Volume */
+	0x0000,     /* R29 - Right Output Volume */
+	0x0066,     /* R30 - Line Outputs Volume */
+	0x0022,     /* R31 - Out3/4 Volume */
+	0x0079,     /* R32 - Left OPGA Volume */
+	0x0079,     /* R33 - Right OPGA Volume */
+	0x0003,     /* R34 - Speaker Volume */
+	0x0003,     /* R35 - ClassD1 */
+	0x0000,     /* R36 */
+	0x0100,     /* R37 - ClassD3 */
+	0x0000,     /* R38 */
+	0x0000,     /* R39 - Input Mixer1 */
+	0x0000,     /* R40 - Input Mixer2 */
+	0x0000,     /* R41 - Input Mixer3 */
+	0x0000,     /* R42 - Input Mixer4 */
+	0x0000,     /* R43 - Input Mixer5 */
+	0x0000,     /* R44 - Input Mixer6 */
+	0x0000,     /* R45 - Output Mixer1 */
+	0x0000,     /* R46 - Output Mixer2 */
+	0x0000,     /* R47 - Output Mixer3 */
+	0x0000,     /* R48 - Output Mixer4 */
+	0x0000,     /* R49 - Output Mixer5 */
+	0x0000,     /* R50 - Output Mixer6 */
+	0x0180,     /* R51 - Out3/4 Mixer */
+	0x0000,     /* R52 - Line Mixer1 */
+	0x0000,     /* R53 - Line Mixer2 */
+	0x0000,     /* R54 - Speaker Mixer */
+	0x0000,     /* R55 - Additional Control */
+	0x0000,     /* R56 - AntiPOP1 */
+	0x0000,     /* R57 - AntiPOP2 */
+	0x0000,     /* R58 - MICBIAS */
+	0x0000,     /* R59 */
+	0x0008,     /* R60 - PLL1 */
+	0x0031,     /* R61 - PLL2 */
+	0x0026,     /* R62 - PLL3 */
+};
+
+#define wm8991_reset(c) snd_soc_write(c, WM8991_RESET, 0)
+
+static const unsigned int rec_mix_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 7, TLV_DB_LINEAR_ITEM(-1500, 600),
+};
+
+static const unsigned int in_pga_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 0x1F, TLV_DB_LINEAR_ITEM(-1650, 3000),
+};
+
+static const unsigned int out_mix_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 7, TLV_DB_LINEAR_ITEM(0, -2100),
+};
+
+static const unsigned int out_pga_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 127, TLV_DB_LINEAR_ITEM(-7300, 600),
+};
+
+static const unsigned int out_omix_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 7, TLV_DB_LINEAR_ITEM(-600, 0),
+};
+
+static const unsigned int out_dac_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 255, TLV_DB_LINEAR_ITEM(-7163, 0),
+};
+
+static const unsigned int in_adc_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 255, TLV_DB_LINEAR_ITEM(-7163, 1763),
+};
+
+static const unsigned int out_sidetone_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 31, TLV_DB_LINEAR_ITEM(-3600, 0),
+};
+
+static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int ret;
+	u16 val;
+
+	ret = snd_soc_put_volsw(kcontrol, ucontrol);
+	if (ret < 0)
+		return ret;
+
+	/* now hit the volume update bits (always bit 8) */
+	val = snd_soc_read(codec, reg);
+	return snd_soc_write(codec, reg, val | 0x0100);
+}
+
+static const char *wm8991_digital_sidetone[] =
+{"None", "Left ADC", "Right ADC", "Reserved"};
+
+static const struct soc_enum wm8991_left_digital_sidetone_enum =
+	SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
+			WM8991_ADC_TO_DACL_SHIFT,
+			WM8991_ADC_TO_DACL_MASK,
+			wm8991_digital_sidetone);
+
+static const struct soc_enum wm8991_right_digital_sidetone_enum =
+	SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
+			WM8991_ADC_TO_DACR_SHIFT,
+			WM8991_ADC_TO_DACR_MASK,
+			wm8991_digital_sidetone);
+
+static const char *wm8991_adcmode[] =
+{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static const struct soc_enum wm8991_right_adcmode_enum =
+	SOC_ENUM_SINGLE(WM8991_ADC_CTRL,
+			WM8991_ADC_HPF_CUT_SHIFT,
+			WM8991_ADC_HPF_CUT_MASK,
+			wm8991_adcmode);
+
+static const struct snd_kcontrol_new wm8991_snd_controls[] = {
+	/* INMIXL */
+	SOC_SINGLE("LIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L12MNBST_BIT, 1, 0),
+	SOC_SINGLE("LIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L34MNBST_BIT, 1, 0),
+	/* INMIXR */
+	SOC_SINGLE("RIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R12MNBST_BIT, 1, 0),
+	SOC_SINGLE("RIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R34MNBST_BIT, 1, 0),
+
+	/* LOMIX */
+	SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER3,
+		WM8991_LLI3LOVOL_SHIFT, WM8991_LLI3LOVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
+		WM8991_LR12LOVOL_SHIFT, WM8991_LR12LOVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
+		WM8991_LL12LOVOL_SHIFT, WM8991_LL12LOVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER5,
+		WM8991_LRI3LOVOL_SHIFT, WM8991_LRI3LOVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
+		WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
+		WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),
+
+	/* ROMIX */
+	SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER4,
+		WM8991_RRI3ROVOL_SHIFT, WM8991_RRI3ROVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
+		WM8991_RL12ROVOL_SHIFT, WM8991_RL12ROVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
+		WM8991_RR12ROVOL_SHIFT, WM8991_RR12ROVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER6,
+		WM8991_RLI3ROVOL_SHIFT, WM8991_RLI3ROVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
+		WM8991_RLBROVOL_SHIFT, WM8991_RLBROVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
+		WM8991_RRBROVOL_SHIFT, WM8991_RRBROVOL_MASK, 1, out_mix_tlv),
+
+	/* LOUT */
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8991_LEFT_OUTPUT_VOLUME,
+		WM8991_LOUTVOL_SHIFT, WM8991_LOUTVOL_MASK, 0, out_pga_tlv),
+	SOC_SINGLE("LOUT ZC", WM8991_LEFT_OUTPUT_VOLUME, WM8991_LOZC_BIT, 1, 0),
+
+	/* ROUT */
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8991_RIGHT_OUTPUT_VOLUME,
+		WM8991_ROUTVOL_SHIFT, WM8991_ROUTVOL_MASK, 0, out_pga_tlv),
+	SOC_SINGLE("ROUT ZC", WM8991_RIGHT_OUTPUT_VOLUME, WM8991_ROZC_BIT, 1, 0),
+
+	/* LOPGA */
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8991_LEFT_OPGA_VOLUME,
+		WM8991_LOPGAVOL_SHIFT, WM8991_LOPGAVOL_MASK, 0, out_pga_tlv),
+	SOC_SINGLE("LOPGA ZC Switch", WM8991_LEFT_OPGA_VOLUME,
+		WM8991_LOPGAZC_BIT, 1, 0),
+
+	/* ROPGA */
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8991_RIGHT_OPGA_VOLUME,
+		WM8991_ROPGAVOL_SHIFT, WM8991_ROPGAVOL_MASK, 0, out_pga_tlv),
+	SOC_SINGLE("ROPGA ZC Switch", WM8991_RIGHT_OPGA_VOLUME,
+		WM8991_ROPGAZC_BIT, 1, 0),
+
+	SOC_SINGLE("LON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_LONMUTE_BIT, 1, 0),
+	SOC_SINGLE("LOP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_LOPMUTE_BIT, 1, 0),
+	SOC_SINGLE("LOP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_LOATTN_BIT, 1, 0),
+	SOC_SINGLE("RON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_RONMUTE_BIT, 1, 0),
+	SOC_SINGLE("ROP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_ROPMUTE_BIT, 1, 0),
+	SOC_SINGLE("ROP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_ROATTN_BIT, 1, 0),
+
+	SOC_SINGLE("OUT3 Mute Switch", WM8991_OUT3_4_VOLUME,
+		WM8991_OUT3MUTE_BIT, 1, 0),
+	SOC_SINGLE("OUT3 Attenuation Switch", WM8991_OUT3_4_VOLUME,
+		WM8991_OUT3ATTN_BIT, 1, 0),
+
+	SOC_SINGLE("OUT4 Mute Switch", WM8991_OUT3_4_VOLUME,
+		WM8991_OUT4MUTE_BIT, 1, 0),
+	SOC_SINGLE("OUT4 Attenuation Switch", WM8991_OUT3_4_VOLUME,
+		WM8991_OUT4ATTN_BIT, 1, 0),
+
+	SOC_SINGLE("Speaker Mode Switch", WM8991_CLASSD1,
+		WM8991_CDMODE_BIT, 1, 0),
+
+	SOC_SINGLE("Speaker Output Attenuation Volume", WM8991_SPEAKER_VOLUME,
+		WM8991_SPKVOL_SHIFT, WM8991_SPKVOL_MASK, 0),
+	SOC_SINGLE("Speaker DC Boost Volume", WM8991_CLASSD3,
+		WM8991_DCGAIN_SHIFT, WM8991_DCGAIN_MASK, 0),
+	SOC_SINGLE("Speaker AC Boost Volume", WM8991_CLASSD3,
+		WM8991_ACGAIN_SHIFT, WM8991_ACGAIN_MASK, 0),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+		WM8991_LEFT_DAC_DIGITAL_VOLUME,
+		WM8991_DACL_VOL_SHIFT,
+		WM8991_DACL_VOL_MASK,
+		0,
+		out_dac_tlv),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+		WM8991_RIGHT_DAC_DIGITAL_VOLUME,
+		WM8991_DACR_VOL_SHIFT,
+		WM8991_DACR_VOL_MASK,
+		0,
+		out_dac_tlv),
+
+	SOC_ENUM("Left Digital Sidetone", wm8991_left_digital_sidetone_enum),
+	SOC_ENUM("Right Digital Sidetone", wm8991_right_digital_sidetone_enum),
+
+	SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
+		WM8991_ADCL_DAC_SVOL_SHIFT, WM8991_ADCL_DAC_SVOL_MASK, 0,
+		out_sidetone_tlv),
+	SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
+		WM8991_ADCR_DAC_SVOL_SHIFT, WM8991_ADCR_DAC_SVOL_MASK, 0,
+		out_sidetone_tlv),
+
+	SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8991_ADC_CTRL,
+		WM8991_ADC_HPF_ENA_BIT, 1, 0),
+
+	SOC_ENUM("ADC HPF Mode", wm8991_right_adcmode_enum),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+		WM8991_LEFT_ADC_DIGITAL_VOLUME,
+		WM8991_ADCL_VOL_SHIFT,
+		WM8991_ADCL_VOL_MASK,
+		0,
+		in_adc_tlv),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+		WM8991_RIGHT_ADC_DIGITAL_VOLUME,
+		WM8991_ADCR_VOL_SHIFT,
+		WM8991_ADCR_VOL_MASK,
+		0,
+		in_adc_tlv),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+		WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+		WM8991_LIN12VOL_SHIFT,
+		WM8991_LIN12VOL_MASK,
+		0,
+		in_pga_tlv),
+
+	SOC_SINGLE("LIN12 ZC Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+		WM8991_LI12ZC_BIT, 1, 0),
+
+	SOC_SINGLE("LIN12 Mute Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+		WM8991_LI12MUTE_BIT, 1, 0),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+		WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+		WM8991_LIN34VOL_SHIFT,
+		WM8991_LIN34VOL_MASK,
+		0,
+		in_pga_tlv),
+
+	SOC_SINGLE("LIN34 ZC Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+		WM8991_LI34ZC_BIT, 1, 0),
+
+	SOC_SINGLE("LIN34 Mute Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+		WM8991_LI34MUTE_BIT, 1, 0),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+		WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+		WM8991_RIN12VOL_SHIFT,
+		WM8991_RIN12VOL_MASK,
+		0,
+		in_pga_tlv),
+
+	SOC_SINGLE("RIN12 ZC Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+		WM8991_RI12ZC_BIT, 1, 0),
+
+	SOC_SINGLE("RIN12 Mute Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+		WM8991_RI12MUTE_BIT, 1, 0),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+		WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+		WM8991_RIN34VOL_SHIFT,
+		WM8991_RIN34VOL_MASK,
+		0,
+		in_pga_tlv),
+
+	SOC_SINGLE("RIN34 ZC Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+		WM8991_RI34ZC_BIT, 1, 0),
+
+	SOC_SINGLE("RIN34 Mute Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+		WM8991_RI34MUTE_BIT, 1, 0),
+};
+
+/*
+ * _DAPM_ Controls
+ */
+static int inmixer_event(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol, int event)
+{
+	u16 reg, fakepower;
+
+	reg = snd_soc_read(w->codec, WM8991_POWER_MANAGEMENT_2);
+	fakepower = snd_soc_read(w->codec, WM8991_INTDRIVBITS);
+
+	if (fakepower & ((1 << WM8991_INMIXL_PWR_BIT) |
+			 (1 << WM8991_AINLMUX_PWR_BIT)))
+		reg |= WM8991_AINL_ENA;
+	else
+		reg &= ~WM8991_AINL_ENA;
+
+	if (fakepower & ((1 << WM8991_INMIXR_PWR_BIT) |
+			 (1 << WM8991_AINRMUX_PWR_BIT)))
+		reg |= WM8991_AINR_ENA;
+	else
+		reg &= ~WM8991_AINL_ENA;
+
+	snd_soc_write(w->codec, WM8991_POWER_MANAGEMENT_2, reg);
+	return 0;
+}
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *kcontrol, int event)
+{
+	u32 reg_shift = kcontrol->private_value & 0xfff;
+	int ret = 0;
+	u16 reg;
+
+	switch (reg_shift) {
+	case WM8991_SPEAKER_MIXER | (WM8991_LDSPK_BIT << 8):
+		reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER1);
+		if (reg & WM8991_LDLO) {
+			printk(KERN_WARNING
+			       "Cannot set as Output Mixer 1 LDLO Set\n");
+			ret = -1;
+		}
+		break;
+
+	case WM8991_SPEAKER_MIXER | (WM8991_RDSPK_BIT << 8):
+		reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER2);
+		if (reg & WM8991_RDRO) {
+			printk(KERN_WARNING
+			       "Cannot set as Output Mixer 2 RDRO Set\n");
+			ret = -1;
+		}
+		break;
+
+	case WM8991_OUTPUT_MIXER1 | (WM8991_LDLO_BIT << 8):
+		reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER);
+		if (reg & WM8991_LDSPK) {
+			printk(KERN_WARNING
+			       "Cannot set as Speaker Mixer LDSPK Set\n");
+			ret = -1;
+		}
+		break;
+
+	case WM8991_OUTPUT_MIXER2 | (WM8991_RDRO_BIT << 8):
+		reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER);
+		if (reg & WM8991_RDSPK) {
+			printk(KERN_WARNING
+			       "Cannot set as Speaker Mixer RDSPK Set\n");
+			ret = -1;
+		}
+		break;
+	}
+
+	return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = {
+	SOC_DAPM_SINGLE("LIN1 Switch", WM8991_INPUT_MIXER2, WM8991_LMN1_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LIN2 Switch", WM8991_INPUT_MIXER2, WM8991_LMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8991_dapm_lin34_pga_controls[] = {
+	SOC_DAPM_SINGLE("LIN3 Switch", WM8991_INPUT_MIXER2, WM8991_LMN3_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LIN4 Switch", WM8991_INPUT_MIXER2, WM8991_LMP4_BIT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8991_dapm_rin12_pga_controls[] = {
+	SOC_DAPM_SINGLE("RIN1 Switch", WM8991_INPUT_MIXER2, WM8991_RMN1_BIT, 1, 0),
+	SOC_DAPM_SINGLE("RIN2 Switch", WM8991_INPUT_MIXER2, WM8991_RMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8991_dapm_rin34_pga_controls[] = {
+	SOC_DAPM_SINGLE("RIN3 Switch", WM8991_INPUT_MIXER2, WM8991_RMN3_BIT, 1, 0),
+	SOC_DAPM_SINGLE("RIN4 Switch", WM8991_INPUT_MIXER2, WM8991_RMP4_BIT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8991_dapm_inmixl_controls[] = {
+	SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8991_INPUT_MIXER3,
+		WM8991_LDBVOL_SHIFT, WM8991_LDBVOL_MASK, 0, in_mix_tlv),
+	SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8991_INPUT_MIXER5, WM8991_LI2BVOL_SHIFT,
+		7, 0, in_mix_tlv),
+	SOC_DAPM_SINGLE("LINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
+		1, 0),
+	SOC_DAPM_SINGLE("LINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
+		1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8991_dapm_inmixr_controls[] = {
+	SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8991_INPUT_MIXER4,
+		WM8991_RDBVOL_SHIFT, WM8991_RDBVOL_MASK, 0, in_mix_tlv),
+	SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8991_INPUT_MIXER6, WM8991_RI2BVOL_SHIFT,
+		7, 0, in_mix_tlv),
+	SOC_DAPM_SINGLE("RINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
+		1, 0),
+	SOC_DAPM_SINGLE("RINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
+		1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8991_ainlmux[] =
+{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static const struct soc_enum wm8991_ainlmux_enum =
+	SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT,
+			ARRAY_SIZE(wm8991_ainlmux), wm8991_ainlmux);
+
+static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls =
+	SOC_DAPM_ENUM("Route", wm8991_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8991_ainrmux[] =
+{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static const struct soc_enum wm8991_ainrmux_enum =
+	SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT,
+			ARRAY_SIZE(wm8991_ainrmux), wm8991_ainrmux);
+
+static const struct snd_kcontrol_new wm8991_dapm_ainrmux_controls =
+	SOC_DAPM_ENUM("Route", wm8991_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8991_dapm_rxvoice_controls[] = {
+	SOC_DAPM_SINGLE_TLV("LIN4RXN", WM8991_INPUT_MIXER5, WM8991_LR4BVOL_SHIFT,
+		WM8991_LR4BVOL_MASK, 0, in_mix_tlv),
+	SOC_DAPM_SINGLE_TLV("RIN4RXP", WM8991_INPUT_MIXER6, WM8991_RL4BVOL_SHIFT,
+		WM8991_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lomix_controls[] = {
+	SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LRBLO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LLBLO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LRI3LO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LLI3LO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LR12LO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LL12LO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LDLO_BIT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8991_dapm_romix_controls[] = {
+	SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RLBRO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RRBRO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RLI3RO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RRI3RO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RL12RO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RR12RO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RDRO_BIT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lonmix_controls[] = {
+	SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
+		WM8991_LLOPGALON_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER1,
+		WM8991_LROPGALON_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8991_LINE_MIXER1,
+		WM8991_LOPLON_BIT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lopmix_controls[] = {
+	SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER1,
+		WM8991_LR12LOP_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER1,
+		WM8991_LL12LOP_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
+		WM8991_LLOPGALOP_BIT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8991_dapm_ronmix_controls[] = {
+	SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
+		WM8991_RROPGARON_BIT, 1, 0),
+	SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER2,
+		WM8991_RLOPGARON_BIT, 1, 0),
+	SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8991_LINE_MIXER2,
+		WM8991_ROPRON_BIT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8991_dapm_ropmix_controls[] = {
+	SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER2,
+		WM8991_RL12ROP_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER2,
+		WM8991_RR12ROP_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
+		WM8991_RROPGAROP_BIT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8991_dapm_out3mix_controls[] = {
+	SOC_DAPM_SINGLE("OUT3MIX LIN4RXN Bypass Switch", WM8991_OUT3_4_MIXER,
+		WM8991_LI4O3_BIT, 1, 0),
+	SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8991_OUT3_4_MIXER,
+		WM8991_LPGAO3_BIT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8991_dapm_out4mix_controls[] = {
+	SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8991_OUT3_4_MIXER,
+		WM8991_RPGAO4_BIT, 1, 0),
+	SOC_DAPM_SINGLE("OUT4MIX RIN4RXP Bypass Switch", WM8991_OUT3_4_MIXER,
+		WM8991_RI4O4_BIT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8991_dapm_spkmix_controls[] = {
+	SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
+		WM8991_LI2SPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8991_SPEAKER_MIXER,
+		WM8991_LB2SPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8991_SPEAKER_MIXER,
+		WM8991_LOPGASPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8991_SPEAKER_MIXER,
+		WM8991_LDSPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8991_SPEAKER_MIXER,
+		WM8991_RDSPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8991_SPEAKER_MIXER,
+		WM8991_ROPGASPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8991_SPEAKER_MIXER,
+		WM8991_RL12ROP_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
+		WM8991_RI2SPK_BIT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = {
+	/* Input Side */
+	/* Input Lines */
+	SND_SOC_DAPM_INPUT("LIN1"),
+	SND_SOC_DAPM_INPUT("LIN2"),
+	SND_SOC_DAPM_INPUT("LIN3"),
+	SND_SOC_DAPM_INPUT("LIN4RXN"),
+	SND_SOC_DAPM_INPUT("RIN3"),
+	SND_SOC_DAPM_INPUT("RIN4RXP"),
+	SND_SOC_DAPM_INPUT("RIN1"),
+	SND_SOC_DAPM_INPUT("RIN2"),
+	SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+	/* DACs */
+	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8991_POWER_MANAGEMENT_2,
+		WM8991_ADCL_ENA_BIT, 0),
+	SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8991_POWER_MANAGEMENT_2,
+		WM8991_ADCR_ENA_BIT, 0),
+
+	/* Input PGAs */
+	SND_SOC_DAPM_MIXER("LIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN12_ENA_BIT,
+		0, &wm8991_dapm_lin12_pga_controls[0],
+		ARRAY_SIZE(wm8991_dapm_lin12_pga_controls)),
+	SND_SOC_DAPM_MIXER("LIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN34_ENA_BIT,
+		0, &wm8991_dapm_lin34_pga_controls[0],
+		ARRAY_SIZE(wm8991_dapm_lin34_pga_controls)),
+	SND_SOC_DAPM_MIXER("RIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN12_ENA_BIT,
+		0, &wm8991_dapm_rin12_pga_controls[0],
+		ARRAY_SIZE(wm8991_dapm_rin12_pga_controls)),
+	SND_SOC_DAPM_MIXER("RIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN34_ENA_BIT,
+		0, &wm8991_dapm_rin34_pga_controls[0],
+		ARRAY_SIZE(wm8991_dapm_rin34_pga_controls)),
+
+	/* INMIXL */
+	SND_SOC_DAPM_MIXER_E("INMIXL", WM8991_INTDRIVBITS, WM8991_INMIXL_PWR_BIT, 0,
+		&wm8991_dapm_inmixl_controls[0],
+		ARRAY_SIZE(wm8991_dapm_inmixl_controls),
+		inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* AINLMUX */
+	SND_SOC_DAPM_MUX_E("AINLMUX", WM8991_INTDRIVBITS, WM8991_AINLMUX_PWR_BIT, 0,
+		&wm8991_dapm_ainlmux_controls, inmixer_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* INMIXR */
+	SND_SOC_DAPM_MIXER_E("INMIXR", WM8991_INTDRIVBITS, WM8991_INMIXR_PWR_BIT, 0,
+		&wm8991_dapm_inmixr_controls[0],
+		ARRAY_SIZE(wm8991_dapm_inmixr_controls),
+		inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* AINRMUX */
+	SND_SOC_DAPM_MUX_E("AINRMUX", WM8991_INTDRIVBITS, WM8991_AINRMUX_PWR_BIT, 0,
+		&wm8991_dapm_ainrmux_controls, inmixer_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* Output Side */
+	/* DACs */
+	SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8991_POWER_MANAGEMENT_3,
+		WM8991_DACL_ENA_BIT, 0),
+	SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8991_POWER_MANAGEMENT_3,
+		WM8991_DACR_ENA_BIT, 0),
+
+	/* LOMIX */
+	SND_SOC_DAPM_MIXER_E("LOMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOMIX_ENA_BIT,
+		0, &wm8991_dapm_lomix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_lomix_controls),
+		outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+	/* LONMIX */
+	SND_SOC_DAPM_MIXER("LONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LON_ENA_BIT, 0,
+		&wm8991_dapm_lonmix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_lonmix_controls)),
+
+	/* LOPMIX */
+	SND_SOC_DAPM_MIXER("LOPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOP_ENA_BIT, 0,
+		&wm8991_dapm_lopmix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_lopmix_controls)),
+
+	/* OUT3MIX */
+	SND_SOC_DAPM_MIXER("OUT3MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT3_ENA_BIT, 0,
+		&wm8991_dapm_out3mix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_out3mix_controls)),
+
+	/* SPKMIX */
+	SND_SOC_DAPM_MIXER_E("SPKMIX", WM8991_POWER_MANAGEMENT_1, WM8991_SPK_ENA_BIT, 0,
+		&wm8991_dapm_spkmix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_spkmix_controls), outmixer_event,
+		SND_SOC_DAPM_PRE_REG),
+
+	/* OUT4MIX */
+	SND_SOC_DAPM_MIXER("OUT4MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT4_ENA_BIT, 0,
+		&wm8991_dapm_out4mix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_out4mix_controls)),
+
+	/* ROPMIX */
+	SND_SOC_DAPM_MIXER("ROPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROP_ENA_BIT, 0,
+		&wm8991_dapm_ropmix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_ropmix_controls)),
+
+	/* RONMIX */
+	SND_SOC_DAPM_MIXER("RONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_RON_ENA_BIT, 0,
+		&wm8991_dapm_ronmix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_ronmix_controls)),
+
+	/* ROMIX */
+	SND_SOC_DAPM_MIXER_E("ROMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROMIX_ENA_BIT,
+		0, &wm8991_dapm_romix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_romix_controls),
+		outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+	/* LOUT PGA */
+	SND_SOC_DAPM_PGA("LOUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_LOUT_ENA_BIT, 0,
+		NULL, 0),
+
+	/* ROUT PGA */
+	SND_SOC_DAPM_PGA("ROUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_ROUT_ENA_BIT, 0,
+		NULL, 0),
+
+	/* LOPGA */
+	SND_SOC_DAPM_PGA("LOPGA", WM8991_POWER_MANAGEMENT_3, WM8991_LOPGA_ENA_BIT, 0,
+		NULL, 0),
+
+	/* ROPGA */
+	SND_SOC_DAPM_PGA("ROPGA", WM8991_POWER_MANAGEMENT_3, WM8991_ROPGA_ENA_BIT, 0,
+		NULL, 0),
+
+	/* MICBIAS */
+	SND_SOC_DAPM_MICBIAS("MICBIAS", WM8991_POWER_MANAGEMENT_1,
+		WM8991_MICBIAS_ENA_BIT, 0),
+
+	SND_SOC_DAPM_OUTPUT("LON"),
+	SND_SOC_DAPM_OUTPUT("LOP"),
+	SND_SOC_DAPM_OUTPUT("OUT3"),
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+	SND_SOC_DAPM_OUTPUT("SPKN"),
+	SND_SOC_DAPM_OUTPUT("SPKP"),
+	SND_SOC_DAPM_OUTPUT("ROUT"),
+	SND_SOC_DAPM_OUTPUT("OUT4"),
+	SND_SOC_DAPM_OUTPUT("ROP"),
+	SND_SOC_DAPM_OUTPUT("RON"),
+	SND_SOC_DAPM_OUTPUT("OUT"),
+
+	SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Make DACs turn on when playing even if not mixed into any outputs */
+	{"Internal DAC Sink", NULL, "Left DAC"},
+	{"Internal DAC Sink", NULL, "Right DAC"},
+
+	/* Make ADCs turn on when recording even if not mixed from any inputs */
+	{"Left ADC", NULL, "Internal ADC Source"},
+	{"Right ADC", NULL, "Internal ADC Source"},
+
+	/* Input Side */
+	/* LIN12 PGA */
+	{"LIN12 PGA", "LIN1 Switch", "LIN1"},
+	{"LIN12 PGA", "LIN2 Switch", "LIN2"},
+	/* LIN34 PGA */
+	{"LIN34 PGA", "LIN3 Switch", "LIN3"},
+	{"LIN34 PGA", "LIN4 Switch", "LIN4RXN"},
+	/* INMIXL */
+	{"INMIXL", "Record Left Volume", "LOMIX"},
+	{"INMIXL", "LIN2 Volume", "LIN2"},
+	{"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+	{"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+	/* AINLMUX */
+	{"AINLMUX", "INMIXL Mix", "INMIXL"},
+	{"AINLMUX", "DIFFINL Mix", "LIN12 PGA"},
+	{"AINLMUX", "DIFFINL Mix", "LIN34 PGA"},
+	{"AINLMUX", "RXVOICE Mix", "LIN4RXN"},
+	{"AINLMUX", "RXVOICE Mix", "RIN4RXP"},
+	/* ADC */
+	{"Left ADC", NULL, "AINLMUX"},
+
+	/* RIN12 PGA */
+	{"RIN12 PGA", "RIN1 Switch", "RIN1"},
+	{"RIN12 PGA", "RIN2 Switch", "RIN2"},
+	/* RIN34 PGA */
+	{"RIN34 PGA", "RIN3 Switch", "RIN3"},
+	{"RIN34 PGA", "RIN4 Switch", "RIN4RXP"},
+	/* INMIXL */
+	{"INMIXR", "Record Right Volume", "ROMIX"},
+	{"INMIXR", "RIN2 Volume", "RIN2"},
+	{"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+	{"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+	/* AINRMUX */
+	{"AINRMUX", "INMIXR Mix", "INMIXR"},
+	{"AINRMUX", "DIFFINR Mix", "RIN12 PGA"},
+	{"AINRMUX", "DIFFINR Mix", "RIN34 PGA"},
+	{"AINRMUX", "RXVOICE Mix", "LIN4RXN"},
+	{"AINRMUX", "RXVOICE Mix", "RIN4RXP"},
+	/* ADC */
+	{"Right ADC", NULL, "AINRMUX"},
+
+	/* LOMIX */
+	{"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+	{"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+	{"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+	{"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+	{"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"},
+	{"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"},
+	{"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+	/* ROMIX */
+	{"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+	{"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+	{"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+	{"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+	{"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"},
+	{"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"},
+	{"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+	/* SPKMIX */
+	{"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+	{"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+	{"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"},
+	{"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"},
+	{"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+	{"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+	{"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+	{"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"},
+
+	/* LONMIX */
+	{"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+	{"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+	{"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+	/* LOPMIX */
+	{"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+	{"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+	{"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+	/* OUT3MIX */
+	{"OUT3MIX", "OUT3MIX LIN4RXN Bypass Switch", "LIN4RXN"},
+	{"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+	/* OUT4MIX */
+	{"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+	{"OUT4MIX", "OUT4MIX RIN4RXP Bypass Switch", "RIN4RXP"},
+
+	/* RONMIX */
+	{"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+	{"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+	{"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+	/* ROPMIX */
+	{"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+	{"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+	{"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+	/* Out Mixer PGAs */
+	{"LOPGA", NULL, "LOMIX"},
+	{"ROPGA", NULL, "ROMIX"},
+
+	{"LOUT PGA", NULL, "LOMIX"},
+	{"ROUT PGA", NULL, "ROMIX"},
+
+	/* Output Pins */
+	{"LON", NULL, "LONMIX"},
+	{"LOP", NULL, "LOPMIX"},
+	{"OUT", NULL, "OUT3MIX"},
+	{"LOUT", NULL, "LOUT PGA"},
+	{"SPKN", NULL, "SPKMIX"},
+	{"ROUT", NULL, "ROUT PGA"},
+	{"OUT4", NULL, "OUT4MIX"},
+	{"ROP", NULL, "ROPMIX"},
+	{"RON", NULL, "RONMIX"},
+};
+
+/* PLL divisors */
+struct _pll_div {
+	u32 div2;
+	u32 n;
+	u32 k;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 16) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
+			unsigned int source)
+{
+	u64 Kpart;
+	unsigned int K, Ndiv, Nmod;
+
+
+	Ndiv = target / source;
+	if (Ndiv < 6) {
+		source >>= 1;
+		pll_div->div2 = 1;
+		Ndiv = target / source;
+	} else
+		pll_div->div2 = 0;
+
+	if ((Ndiv < 6) || (Ndiv > 12))
+		printk(KERN_WARNING
+		       "WM8991 N value outwith recommended range! N = %d\n", Ndiv);
+
+	pll_div->n = Ndiv;
+	Nmod = target % source;
+	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, source);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	/* Check if we need to round */
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	K /= 10;
+
+	pll_div->k = K;
+}
+
+static int wm8991_set_dai_pll(struct snd_soc_dai *codec_dai,
+			      int pll_id, int src, unsigned int freq_in, unsigned int freq_out)
+{
+	u16 reg;
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct _pll_div pll_div;
+
+	if (freq_in && freq_out) {
+		pll_factors(&pll_div, freq_out * 4, freq_in);
+
+		/* Turn on PLL */
+		reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+		reg |= WM8991_PLL_ENA;
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+
+		/* sysclk comes from PLL */
+		reg = snd_soc_read(codec, WM8991_CLOCKING_2);
+		snd_soc_write(codec, WM8991_CLOCKING_2, reg | WM8991_SYSCLK_SRC);
+
+		/* set up N , fractional mode and pre-divisor if neccessary */
+		snd_soc_write(codec, WM8991_PLL1, pll_div.n | WM8991_SDM |
+			      (pll_div.div2 ? WM8991_PRESCALE : 0));
+		snd_soc_write(codec, WM8991_PLL2, (u8)(pll_div.k>>8));
+		snd_soc_write(codec, WM8991_PLL3, (u8)(pll_div.k & 0xFF));
+	} else {
+		/* Turn on PLL */
+		reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+		reg &= ~WM8991_PLL_ENA;
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+	}
+	return 0;
+}
+
+/*
+ * Set's ADC and Voice DAC format.
+ */
+static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			      unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 audio1, audio3;
+
+	audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1);
+	audio3 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_3);
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		audio3 &= ~WM8991_AIF_MSTR1;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		audio3 |= WM8991_AIF_MSTR1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	audio1 &= ~WM8991_AIF_FMT_MASK;
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		audio1 |= WM8991_AIF_TMF_I2S;
+		audio1 &= ~WM8991_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		audio1 |= WM8991_AIF_TMF_RIGHTJ;
+		audio1 &= ~WM8991_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		audio1 |= WM8991_AIF_TMF_LEFTJ;
+		audio1 &= ~WM8991_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		audio1 |= WM8991_AIF_TMF_DSP;
+		audio1 &= ~WM8991_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		audio1 |= WM8991_AIF_TMF_DSP | WM8991_AIF_LRCLK_INV;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
+	snd_soc_write(codec, WM8991_AUDIO_INTERFACE_3, audio3);
+	return 0;
+}
+
+static int wm8991_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 WM8991_MCLK_DIV:
+		reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+		      ~WM8991_MCLK_DIV_MASK;
+		snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+		break;
+	case WM8991_DACCLK_DIV:
+		reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+		      ~WM8991_DAC_CLKDIV_MASK;
+		snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+		break;
+	case WM8991_ADCCLK_DIV:
+		reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+		      ~WM8991_ADC_CLKDIV_MASK;
+		snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+		break;
+	case WM8991_BCLK_DIV:
+		reg = snd_soc_read(codec, WM8991_CLOCKING_1) &
+		      ~WM8991_BCLK_DIV_MASK;
+		snd_soc_write(codec, WM8991_CLOCKING_1, reg | div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8991_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 audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1);
+
+	audio1 &= ~WM8991_AIF_WL_MASK;
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		audio1 |= WM8991_AIF_WL_20BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		audio1 |= WM8991_AIF_WL_24BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		audio1 |= WM8991_AIF_WL_32BITS;
+		break;
+	}
+
+	snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
+	return 0;
+}
+
+static int wm8991_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 val;
+
+	val  = snd_soc_read(codec, WM8991_DAC_CTRL) & ~WM8991_DAC_MUTE;
+	if (mute)
+		snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
+	else
+		snd_soc_write(codec, WM8991_DAC_CTRL, val);
+	return 0;
+}
+
+static int wm8991_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	u16 val;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		/* VMID=2*50k */
+		val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+		      ~WM8991_VMID_MODE_MASK;
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x2);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+			snd_soc_cache_sync(codec);
+			/* Enable all output discharge bits */
+			snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+				      WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
+				      WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
+				      WM8991_DIS_ROUT);
+
+			/* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+			snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+				      WM8991_BUFDCOPEN | WM8991_POBCTRL |
+				      WM8991_VMIDTOG);
+
+			/* Delay to allow output caps to discharge */
+			msleep(300);
+
+			/* Disable VMIDTOG */
+			snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+				      WM8991_BUFDCOPEN | WM8991_POBCTRL);
+
+			/* disable all output discharge bits */
+			snd_soc_write(codec, WM8991_ANTIPOP1, 0);
+
+			/* Enable outputs */
+			snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1b00);
+
+			msleep(50);
+
+			/* Enable VMID at 2x50k */
+			snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f02);
+
+			msleep(100);
+
+			/* Enable VREF */
+			snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+
+			msleep(600);
+
+			/* Enable BUFIOEN */
+			snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+				      WM8991_BUFDCOPEN | WM8991_POBCTRL |
+				      WM8991_BUFIOEN);
+
+			/* Disable outputs */
+			snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x3);
+
+			/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+			snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_BUFIOEN);
+		}
+
+		/* VMID=2*250k */
+		val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+		      ~WM8991_VMID_MODE_MASK;
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x4);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		/* Enable POBCTRL and SOFT_ST */
+		snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+			      WM8991_POBCTRL | WM8991_BUFIOEN);
+
+		/* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+		snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+			      WM8991_BUFDCOPEN | WM8991_POBCTRL |
+			      WM8991_BUFIOEN);
+
+		/* mute DAC */
+		val = snd_soc_read(codec, WM8991_DAC_CTRL);
+		snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
+
+		/* Enable any disabled outputs */
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+
+		/* Disable VMID */
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f01);
+
+		msleep(300);
+
+		/* Enable all output discharge bits */
+		snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+			      WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
+			      WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
+			      WM8991_DIS_ROUT);
+
+		/* Disable VREF */
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x0);
+
+		/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+		snd_soc_write(codec, WM8991_ANTIPOP2, 0x0);
+		codec->cache_sync = 1;
+		break;
+	}
+
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static int wm8991_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+	wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8991_resume(struct snd_soc_codec *codec)
+{
+	wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	return 0;
+}
+
+/* power down chip */
+static int wm8991_remove(struct snd_soc_codec *codec)
+{
+	wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8991_probe(struct snd_soc_codec *codec)
+{
+	struct wm8991_priv *wm8991;
+	int ret;
+	unsigned int reg;
+
+	wm8991 = snd_soc_codec_get_drvdata(codec);
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8991->control_type);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret);
+		return ret;
+	}
+
+	ret = wm8991_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		return ret;
+	}
+
+	wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	reg = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_4);
+	snd_soc_write(codec, WM8991_AUDIO_INTERFACE_4, reg | WM8991_ALRCGPIO1);
+
+	reg = snd_soc_read(codec, WM8991_GPIO1_GPIO2) &
+	      ~WM8991_GPIO1_SEL_MASK;
+	snd_soc_write(codec, WM8991_GPIO1_GPIO2, reg | 1);
+
+	reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1);
+	snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, reg | WM8991_VREF_ENA|
+		      WM8991_VMID_MODE_MASK);
+
+	reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+	snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg | WM8991_OPCLK_ENA);
+
+	snd_soc_write(codec, WM8991_DAC_CTRL, 0);
+	snd_soc_write(codec, WM8991_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+	snd_soc_write(codec, WM8991_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+
+	snd_soc_add_controls(codec, wm8991_snd_controls,
+			     ARRAY_SIZE(wm8991_snd_controls));
+
+	snd_soc_dapm_new_controls(&codec->dapm, wm8991_dapm_widgets,
+				  ARRAY_SIZE(wm8991_dapm_widgets));
+	snd_soc_dapm_add_routes(&codec->dapm, audio_map,
+				ARRAY_SIZE(audio_map));
+	return 0;
+}
+
+#define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8991_ops = {
+	.hw_params = wm8991_hw_params,
+	.digital_mute = wm8991_mute,
+	.set_fmt = wm8991_set_dai_fmt,
+	.set_clkdiv = wm8991_set_dai_clkdiv,
+	.set_pll = wm8991_set_dai_pll
+};
+
+/*
+ * The WM8991 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+static struct snd_soc_dai_driver wm8991_dai = {
+	/* ADC/DAC on primary */
+	.name = "wm8991",
+	.id = 1,
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = WM8991_FORMATS
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = WM8991_FORMATS
+	},
+	.ops = &wm8991_ops
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8991 = {
+	.probe = wm8991_probe,
+	.remove = wm8991_remove,
+	.suspend = wm8991_suspend,
+	.resume = wm8991_resume,
+	.set_bias_level = wm8991_set_bias_level,
+	.reg_cache_size = WM8991_MAX_REGISTER + 1,
+	.reg_word_size = sizeof(u16),
+	.reg_cache_default = wm8991_reg_defs
+};
+
+static __devinit int wm8991_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8991_priv *wm8991;
+	int ret;
+
+	wm8991 = kzalloc(sizeof *wm8991, GFP_KERNEL);
+	if (!wm8991)
+		return -ENOMEM;
+
+	wm8991->control_type = SND_SOC_I2C;
+	i2c_set_clientdata(i2c, wm8991);
+
+	ret = snd_soc_register_codec(&i2c->dev,
+				     &soc_codec_dev_wm8991, &wm8991_dai, 1);
+	if (ret < 0)
+		kfree(wm8991);
+	return ret;
+}
+
+static __devexit int wm8991_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	kfree(i2c_get_clientdata(client));
+	return 0;
+}
+
+static const struct i2c_device_id wm8991_i2c_id[] = {
+	{ "wm8991", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id);
+
+static struct i2c_driver wm8991_i2c_driver = {
+	.driver = {
+		.name = "wm8991",
+		.owner = THIS_MODULE,
+	},
+	.probe = wm8991_i2c_probe,
+	.remove = __devexit_p(wm8991_i2c_remove),
+	.id_table = wm8991_i2c_id,
+};
+
+static int __init wm8991_modinit(void)
+{
+	int ret;
+	ret = i2c_add_driver(&wm8991_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8991 I2C driver: %d\n",
+		       ret);
+	}
+	return 0;
+}
+module_init(wm8991_modinit);
+
+static void __exit wm8991_exit(void)
+{
+	i2c_del_driver(&wm8991_i2c_driver);
+}
+module_exit(wm8991_exit);
+
+MODULE_DESCRIPTION("ASoC WM8991 driver");
+MODULE_AUTHOR("Graeme Gregory");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h
new file mode 100644
index 0000000..8a942ef
--- /dev/null
+++ b/sound/soc/codecs/wm8991.h
@@ -0,0 +1,833 @@
+/*
+ * wm8991.h  --  audio driver for WM8991
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#ifndef _WM8991_H
+#define _WM8991_H
+
+/*
+ * Register values.
+ */
+#define WM8991_RESET                            0x00
+#define WM8991_POWER_MANAGEMENT_1               0x01
+#define WM8991_POWER_MANAGEMENT_2               0x02
+#define WM8991_POWER_MANAGEMENT_3               0x03
+#define WM8991_AUDIO_INTERFACE_1                0x04
+#define WM8991_AUDIO_INTERFACE_2                0x05
+#define WM8991_CLOCKING_1                       0x06
+#define WM8991_CLOCKING_2                       0x07
+#define WM8991_AUDIO_INTERFACE_3                0x08
+#define WM8991_AUDIO_INTERFACE_4                0x09
+#define WM8991_DAC_CTRL                         0x0A
+#define WM8991_LEFT_DAC_DIGITAL_VOLUME          0x0B
+#define WM8991_RIGHT_DAC_DIGITAL_VOLUME         0x0C
+#define WM8991_DIGITAL_SIDE_TONE                0x0D
+#define WM8991_ADC_CTRL                         0x0E
+#define WM8991_LEFT_ADC_DIGITAL_VOLUME          0x0F
+#define WM8991_RIGHT_ADC_DIGITAL_VOLUME         0x10
+#define WM8991_GPIO_CTRL_1                      0x12
+#define WM8991_GPIO1_GPIO2                      0x13
+#define WM8991_GPIO3_GPIO4                      0x14
+#define WM8991_GPIO5_GPIO6                      0x15
+#define WM8991_GPIOCTRL_2                       0x16
+#define WM8991_GPIO_POL                         0x17
+#define WM8991_LEFT_LINE_INPUT_1_2_VOLUME       0x18
+#define WM8991_LEFT_LINE_INPUT_3_4_VOLUME       0x19
+#define WM8991_RIGHT_LINE_INPUT_1_2_VOLUME      0x1A
+#define WM8991_RIGHT_LINE_INPUT_3_4_VOLUME      0x1B
+#define WM8991_LEFT_OUTPUT_VOLUME               0x1C
+#define WM8991_RIGHT_OUTPUT_VOLUME              0x1D
+#define WM8991_LINE_OUTPUTS_VOLUME              0x1E
+#define WM8991_OUT3_4_VOLUME                    0x1F
+#define WM8991_LEFT_OPGA_VOLUME                 0x20
+#define WM8991_RIGHT_OPGA_VOLUME                0x21
+#define WM8991_SPEAKER_VOLUME                   0x22
+#define WM8991_CLASSD1                          0x23
+#define WM8991_CLASSD3                          0x25
+#define WM8991_INPUT_MIXER1                     0x27
+#define WM8991_INPUT_MIXER2                     0x28
+#define WM8991_INPUT_MIXER3                     0x29
+#define WM8991_INPUT_MIXER4                     0x2A
+#define WM8991_INPUT_MIXER5                     0x2B
+#define WM8991_INPUT_MIXER6                     0x2C
+#define WM8991_OUTPUT_MIXER1                    0x2D
+#define WM8991_OUTPUT_MIXER2                    0x2E
+#define WM8991_OUTPUT_MIXER3                    0x2F
+#define WM8991_OUTPUT_MIXER4                    0x30
+#define WM8991_OUTPUT_MIXER5                    0x31
+#define WM8991_OUTPUT_MIXER6                    0x32
+#define WM8991_OUT3_4_MIXER                     0x33
+#define WM8991_LINE_MIXER1                      0x34
+#define WM8991_LINE_MIXER2                      0x35
+#define WM8991_SPEAKER_MIXER                    0x36
+#define WM8991_ADDITIONAL_CONTROL               0x37
+#define WM8991_ANTIPOP1                         0x38
+#define WM8991_ANTIPOP2                         0x39
+#define WM8991_MICBIAS                          0x3A
+#define WM8991_PLL1                             0x3C
+#define WM8991_PLL2                             0x3D
+#define WM8991_PLL3                             0x3E
+#define WM8991_INTDRIVBITS			0x3F
+
+#define WM8991_REGISTER_COUNT                   60
+#define WM8991_MAX_REGISTER                     0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset
+ */
+#define WM8991_SW_RESET_CHIP_ID_MASK            0xFFFF  /* SW_RESET_CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8991_SPK_ENA                          0x1000  /* SPK_ENA */
+#define WM8991_SPK_ENA_BIT			12
+#define WM8991_OUT3_ENA                         0x0800  /* OUT3_ENA */
+#define WM8991_OUT3_ENA_BIT			11
+#define WM8991_OUT4_ENA                         0x0400  /* OUT4_ENA */
+#define WM8991_OUT4_ENA_BIT			10
+#define WM8991_LOUT_ENA                         0x0200  /* LOUT_ENA */
+#define WM8991_LOUT_ENA_BIT			9
+#define WM8991_ROUT_ENA                         0x0100  /* ROUT_ENA */
+#define WM8991_ROUT_ENA_BIT			8
+#define WM8991_MICBIAS_ENA                      0x0010  /* MICBIAS_ENA */
+#define WM8991_MICBIAS_ENA_BIT			4
+#define WM8991_VMID_MODE_MASK                   0x0006  /* VMID_MODE - [2:1] */
+#define WM8991_VREF_ENA                         0x0001  /* VREF_ENA */
+#define WM8991_VREF_ENA_BIT			0
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8991_PLL_ENA                          0x8000  /* PLL_ENA */
+#define WM8991_PLL_ENA_BIT			15
+#define WM8991_TSHUT_ENA                        0x4000  /* TSHUT_ENA */
+#define WM8991_TSHUT_ENA_BIT			14
+#define WM8991_TSHUT_OPDIS                      0x2000  /* TSHUT_OPDIS */
+#define WM8991_TSHUT_OPDIS_BIT			13
+#define WM8991_OPCLK_ENA                        0x0800  /* OPCLK_ENA */
+#define WM8991_OPCLK_ENA_BIT			11
+#define WM8991_AINL_ENA                         0x0200  /* AINL_ENA */
+#define WM8991_AINL_ENA_BIT			9
+#define WM8991_AINR_ENA                         0x0100  /* AINR_ENA */
+#define WM8991_AINR_ENA_BIT			8
+#define WM8991_LIN34_ENA                        0x0080  /* LIN34_ENA */
+#define WM8991_LIN34_ENA_BIT			7
+#define WM8991_LIN12_ENA                        0x0040  /* LIN12_ENA */
+#define WM8991_LIN12_ENA_BIT			6
+#define WM8991_RIN34_ENA                        0x0020  /* RIN34_ENA */
+#define WM8991_RIN34_ENA_BIT			5
+#define WM8991_RIN12_ENA                        0x0010  /* RIN12_ENA */
+#define WM8991_RIN12_ENA_BIT			4
+#define WM8991_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8991_ADCL_ENA_BIT			1
+#define WM8991_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8991_ADCR_ENA_BIT			0
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8991_LON_ENA                          0x2000  /* LON_ENA */
+#define WM8991_LON_ENA_BIT			13
+#define WM8991_LOP_ENA                          0x1000  /* LOP_ENA */
+#define WM8991_LOP_ENA_BIT			12
+#define WM8991_RON_ENA                          0x0800  /* RON_ENA */
+#define WM8991_RON_ENA_BIT			11
+#define WM8991_ROP_ENA                          0x0400  /* ROP_ENA */
+#define WM8991_ROP_ENA_BIT			10
+#define WM8991_LOPGA_ENA                        0x0080  /* LOPGA_ENA */
+#define WM8991_LOPGA_ENA_BIT			7
+#define WM8991_ROPGA_ENA                        0x0040  /* ROPGA_ENA */
+#define WM8991_ROPGA_ENA_BIT			6
+#define WM8991_LOMIX_ENA                        0x0020  /* LOMIX_ENA */
+#define WM8991_LOMIX_ENA_BIT			5
+#define WM8991_ROMIX_ENA                        0x0010  /* ROMIX_ENA */
+#define WM8991_ROMIX_ENA_BIT			4
+#define WM8991_DACL_ENA                         0x0002  /* DACL_ENA */
+#define WM8991_DACL_ENA_BIT			1
+#define WM8991_DACR_ENA                         0x0001  /* DACR_ENA */
+#define WM8991_DACR_ENA_BIT			0
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8991_AIFADCL_SRC                      0x8000  /* AIFADCL_SRC */
+#define WM8991_AIFADCR_SRC                      0x4000  /* AIFADCR_SRC */
+#define WM8991_AIFADC_TDM                       0x2000  /* AIFADC_TDM */
+#define WM8991_AIFADC_TDM_CHAN                  0x1000  /* AIFADC_TDM_CHAN */
+#define WM8991_AIF_BCLK_INV                     0x0100  /* AIF_BCLK_INV */
+#define WM8991_AIF_LRCLK_INV                    0x0080  /* AIF_LRCLK_INV */
+#define WM8991_AIF_WL_MASK                      0x0060  /* AIF_WL - [6:5] */
+#define WM8991_AIF_WL_16BITS			(0 << 5)
+#define WM8991_AIF_WL_20BITS			(1 << 5)
+#define WM8991_AIF_WL_24BITS			(2 << 5)
+#define WM8991_AIF_WL_32BITS			(3 << 5)
+#define WM8991_AIF_FMT_MASK                     0x0018  /* AIF_FMT - [4:3] */
+#define WM8991_AIF_TMF_RIGHTJ			(0 << 3)
+#define WM8991_AIF_TMF_LEFTJ			(1 << 3)
+#define WM8991_AIF_TMF_I2S			(2 << 3)
+#define WM8991_AIF_TMF_DSP			(3 << 3)
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8991_DACL_SRC                         0x8000  /* DACL_SRC */
+#define WM8991_DACR_SRC                         0x4000  /* DACR_SRC */
+#define WM8991_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8991_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8991_DAC_BOOST_MASK                   0x0C00  /* DAC_BOOST - [11:10] */
+#define WM8991_DAC_COMP                         0x0010  /* DAC_COMP */
+#define WM8991_DAC_COMPMODE                     0x0008  /* DAC_COMPMODE */
+#define WM8991_ADC_COMP                         0x0004  /* ADC_COMP */
+#define WM8991_ADC_COMPMODE                     0x0002  /* ADC_COMPMODE */
+#define WM8991_LOOPBACK                         0x0001  /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking (1)
+ */
+#define WM8991_TOCLK_RATE                       0x8000  /* TOCLK_RATE */
+#define WM8991_TOCLK_ENA                        0x4000  /* TOCLK_ENA */
+#define WM8991_OPCLKDIV_MASK                    0x1E00  /* OPCLKDIV - [12:9] */
+#define WM8991_DCLKDIV_MASK                     0x01C0  /* DCLKDIV - [8:6] */
+#define WM8991_BCLK_DIV_MASK                    0x001E  /* BCLK_DIV - [4:1] */
+#define WM8991_BCLK_DIV_1			(0x0 << 1)
+#define WM8991_BCLK_DIV_1_5			(0x1 << 1)
+#define WM8991_BCLK_DIV_2			(0x2 << 1)
+#define WM8991_BCLK_DIV_3			(0x3 << 1)
+#define WM8991_BCLK_DIV_4			(0x4 << 1)
+#define WM8991_BCLK_DIV_5_5			(0x5 << 1)
+#define WM8991_BCLK_DIV_6			(0x6 << 1)
+#define WM8991_BCLK_DIV_8			(0x7 << 1)
+#define WM8991_BCLK_DIV_11			(0x8 << 1)
+#define WM8991_BCLK_DIV_12			(0x9 << 1)
+#define WM8991_BCLK_DIV_16			(0xA << 1)
+#define WM8991_BCLK_DIV_22			(0xB << 1)
+#define WM8991_BCLK_DIV_24			(0xC << 1)
+#define WM8991_BCLK_DIV_32			(0xD << 1)
+#define WM8991_BCLK_DIV_44			(0xE << 1)
+#define WM8991_BCLK_DIV_48			(0xF << 1)
+
+/*
+ * R7 (0x07) - Clocking (2)
+ */
+#define WM8991_MCLK_SRC                         0x8000  /* MCLK_SRC */
+#define WM8991_SYSCLK_SRC                       0x4000  /* SYSCLK_SRC */
+#define WM8991_CLK_FORCE                        0x2000  /* CLK_FORCE */
+#define WM8991_MCLK_DIV_MASK                    0x1800  /* MCLK_DIV - [12:11] */
+#define WM8991_MCLK_DIV_1			(0 << 11)
+#define WM8991_MCLK_DIV_2			( 2 << 11)
+#define WM8991_MCLK_INV                         0x0400  /* MCLK_INV */
+#define WM8991_ADC_CLKDIV_MASK                  0x00E0  /* ADC_CLKDIV - [7:5] */
+#define WM8991_ADC_CLKDIV_1			(0 << 5)
+#define WM8991_ADC_CLKDIV_1_5			(1 << 5)
+#define WM8991_ADC_CLKDIV_2			(2 << 5)
+#define WM8991_ADC_CLKDIV_3			(3 << 5)
+#define WM8991_ADC_CLKDIV_4			(4 << 5)
+#define WM8991_ADC_CLKDIV_5_5			(5 << 5)
+#define WM8991_ADC_CLKDIV_6			(6 << 5)
+#define WM8991_DAC_CLKDIV_MASK                  0x001C  /* DAC_CLKDIV - [4:2] */
+#define WM8991_DAC_CLKDIV_1			(0 << 2)
+#define WM8991_DAC_CLKDIV_1_5			(1 << 2)
+#define WM8991_DAC_CLKDIV_2			(2 << 2)
+#define WM8991_DAC_CLKDIV_3			(3 << 2)
+#define WM8991_DAC_CLKDIV_4			(4 << 2)
+#define WM8991_DAC_CLKDIV_5_5			(5 << 2)
+#define WM8991_DAC_CLKDIV_6			(6 << 2)
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8991_AIF_MSTR1                        0x8000  /* AIF_MSTR1 */
+#define WM8991_AIF_MSTR2                        0x4000  /* AIF_MSTR2 */
+#define WM8991_AIF_SEL                          0x2000  /* AIF_SEL */
+#define WM8991_ADCLRC_DIR                       0x0800  /* ADCLRC_DIR */
+#define WM8991_ADCLRC_RATE_MASK                 0x07FF  /* ADCLRC_RATE - [10:0] */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8991_ALRCGPIO1                        0x8000  /* ALRCGPIO1 */
+#define WM8991_ALRCBGPIO6                       0x4000  /* ALRCBGPIO6 */
+#define WM8991_AIF_TRIS                         0x2000  /* AIF_TRIS */
+#define WM8991_DACLRC_DIR                       0x0800  /* DACLRC_DIR */
+#define WM8991_DACLRC_RATE_MASK                 0x07FF  /* DACLRC_RATE - [10:0] */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8991_AIF_LRCLKRATE                    0x0400  /* AIF_LRCLKRATE */
+#define WM8991_DAC_MONO                         0x0200  /* DAC_MONO */
+#define WM8991_DAC_SB_FILT                      0x0100  /* DAC_SB_FILT */
+#define WM8991_DAC_MUTERATE                     0x0080  /* DAC_MUTERATE */
+#define WM8991_DAC_MUTEMODE                     0x0040  /* DAC_MUTEMODE */
+#define WM8991_DEEMP_MASK                       0x0030  /* DEEMP - [5:4] */
+#define WM8991_DAC_MUTE                         0x0004  /* DAC_MUTE */
+#define WM8991_DACL_DATINV                      0x0002  /* DACL_DATINV */
+#define WM8991_DACR_DATINV                      0x0001  /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8991_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8991_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8991_DACL_VOL_SHIFT			0
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8991_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8991_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8991_DACR_VOL_SHIFT			0
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8991_ADCL_DAC_SVOL_MASK               0x0F  /* ADCL_DAC_SVOL - [12:9] */
+#define WM8991_ADCL_DAC_SVOL_SHIFT		9
+#define WM8991_ADCR_DAC_SVOL_MASK               0x0F  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8991_ADCR_DAC_SVOL_SHIFT		5
+#define WM8991_ADC_TO_DACL_MASK                 0x03  /* ADC_TO_DACL - [3:2] */
+#define WM8991_ADC_TO_DACL_SHIFT		2
+#define WM8991_ADC_TO_DACR_MASK                 0x03  /* ADC_TO_DACR - [1:0] */
+#define WM8991_ADC_TO_DACR_SHIFT		0
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8991_ADC_HPF_ENA                      0x0100  /* ADC_HPF_ENA */
+#define WM8991_ADC_HPF_ENA_BIT			8
+#define WM8991_ADC_HPF_CUT_MASK                 0x03  /* ADC_HPF_CUT - [6:5] */
+#define WM8991_ADC_HPF_CUT_SHIFT		5
+#define WM8991_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8991_ADCL_DATINV_BIT			1
+#define WM8991_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8991_ADCR_DATINV_BIT			0
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8991_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8991_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8991_ADCL_VOL_SHIFT			0
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8991_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8991_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8991_ADCR_VOL_SHIFT			0
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8991_IRQ                              0x1000  /* IRQ */
+#define WM8991_TEMPOK                           0x0800  /* TEMPOK */
+#define WM8991_MICSHRT                          0x0400  /* MICSHRT */
+#define WM8991_MICDET                           0x0200  /* MICDET */
+#define WM8991_PLL_LCK                          0x0100  /* PLL_LCK */
+#define WM8991_GPI8_STATUS                      0x0080  /* GPI8_STATUS */
+#define WM8991_GPI7_STATUS                      0x0040  /* GPI7_STATUS */
+#define WM8991_GPIO6_STATUS                     0x0020  /* GPIO6_STATUS */
+#define WM8991_GPIO5_STATUS                     0x0010  /* GPIO5_STATUS */
+#define WM8991_GPIO4_STATUS                     0x0008  /* GPIO4_STATUS */
+#define WM8991_GPIO3_STATUS                     0x0004  /* GPIO3_STATUS */
+#define WM8991_GPIO2_STATUS                     0x0002  /* GPIO2_STATUS */
+#define WM8991_GPIO1_STATUS                     0x0001  /* GPIO1_STATUS */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8991_GPIO2_DEB_ENA                    0x8000  /* GPIO2_DEB_ENA */
+#define WM8991_GPIO2_IRQ_ENA                    0x4000  /* GPIO2_IRQ_ENA */
+#define WM8991_GPIO2_PU                         0x2000  /* GPIO2_PU */
+#define WM8991_GPIO2_PD                         0x1000  /* GPIO2_PD */
+#define WM8991_GPIO2_SEL_MASK                   0x0F00  /* GPIO2_SEL - [11:8] */
+#define WM8991_GPIO1_DEB_ENA                    0x0080  /* GPIO1_DEB_ENA */
+#define WM8991_GPIO1_IRQ_ENA                    0x0040  /* GPIO1_IRQ_ENA */
+#define WM8991_GPIO1_PU                         0x0020  /* GPIO1_PU */
+#define WM8991_GPIO1_PD                         0x0010  /* GPIO1_PD */
+#define WM8991_GPIO1_SEL_MASK                   0x000F  /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8991_GPIO4_DEB_ENA                    0x8000  /* GPIO4_DEB_ENA */
+#define WM8991_GPIO4_IRQ_ENA                    0x4000  /* GPIO4_IRQ_ENA */
+#define WM8991_GPIO4_PU                         0x2000  /* GPIO4_PU */
+#define WM8991_GPIO4_PD                         0x1000  /* GPIO4_PD */
+#define WM8991_GPIO4_SEL_MASK                   0x0F00  /* GPIO4_SEL - [11:8] */
+#define WM8991_GPIO3_DEB_ENA                    0x0080  /* GPIO3_DEB_ENA */
+#define WM8991_GPIO3_IRQ_ENA                    0x0040  /* GPIO3_IRQ_ENA */
+#define WM8991_GPIO3_PU                         0x0020  /* GPIO3_PU */
+#define WM8991_GPIO3_PD                         0x0010  /* GPIO3_PD */
+#define WM8991_GPIO3_SEL_MASK                   0x000F  /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8991_GPIO6_DEB_ENA                    0x8000  /* GPIO6_DEB_ENA */
+#define WM8991_GPIO6_IRQ_ENA                    0x4000  /* GPIO6_IRQ_ENA */
+#define WM8991_GPIO6_PU                         0x2000  /* GPIO6_PU */
+#define WM8991_GPIO6_PD                         0x1000  /* GPIO6_PD */
+#define WM8991_GPIO6_SEL_MASK                   0x0F00  /* GPIO6_SEL - [11:8] */
+#define WM8991_GPIO5_DEB_ENA                    0x0080  /* GPIO5_DEB_ENA */
+#define WM8991_GPIO5_IRQ_ENA                    0x0040  /* GPIO5_IRQ_ENA */
+#define WM8991_GPIO5_PU                         0x0020  /* GPIO5_PU */
+#define WM8991_GPIO5_PD                         0x0010  /* GPIO5_PD */
+#define WM8991_GPIO5_SEL_MASK                   0x000F  /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8991_RD_3W_ENA                        0x8000  /* RD_3W_ENA */
+#define WM8991_MODE_3W4W                        0x4000  /* MODE_3W4W */
+#define WM8991_TEMPOK_IRQ_ENA                   0x0800  /* TEMPOK_IRQ_ENA */
+#define WM8991_MICSHRT_IRQ_ENA                  0x0400  /* MICSHRT_IRQ_ENA */
+#define WM8991_MICDET_IRQ_ENA                   0x0200  /* MICDET_IRQ_ENA */
+#define WM8991_PLL_LCK_IRQ_ENA                  0x0100  /* PLL_LCK_IRQ_ENA */
+#define WM8991_GPI8_DEB_ENA                     0x0080  /* GPI8_DEB_ENA */
+#define WM8991_GPI8_IRQ_ENA                     0x0040  /* GPI8_IRQ_ENA */
+#define WM8991_GPI8_ENA                         0x0010  /* GPI8_ENA */
+#define WM8991_GPI7_DEB_ENA                     0x0008  /* GPI7_DEB_ENA */
+#define WM8991_GPI7_IRQ_ENA                     0x0004  /* GPI7_IRQ_ENA */
+#define WM8991_GPI7_ENA                         0x0001  /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8991_IRQ_INV                          0x1000  /* IRQ_INV */
+#define WM8991_TEMPOK_POL                       0x0800  /* TEMPOK_POL */
+#define WM8991_MICSHRT_POL                      0x0400  /* MICSHRT_POL */
+#define WM8991_MICDET_POL                       0x0200  /* MICDET_POL */
+#define WM8991_PLL_LCK_POL                      0x0100  /* PLL_LCK_POL */
+#define WM8991_GPI8_POL                         0x0080  /* GPI8_POL */
+#define WM8991_GPI7_POL                         0x0040  /* GPI7_POL */
+#define WM8991_GPIO6_POL                        0x0020  /* GPIO6_POL */
+#define WM8991_GPIO5_POL                        0x0010  /* GPIO5_POL */
+#define WM8991_GPIO4_POL                        0x0008  /* GPIO4_POL */
+#define WM8991_GPIO3_POL                        0x0004  /* GPIO3_POL */
+#define WM8991_GPIO2_POL                        0x0002  /* GPIO2_POL */
+#define WM8991_GPIO1_POL                        0x0001  /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_LI12MUTE                         0x0080  /* LI12MUTE */
+#define WM8991_LI12MUTE_BIT			7
+#define WM8991_LI12ZC                           0x0040  /* LI12ZC */
+#define WM8991_LI12ZC_BIT			6
+#define WM8991_LIN12VOL_MASK                    0x001F  /* LIN12VOL - [4:0] */
+#define WM8991_LIN12VOL_SHIFT			0
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_LI34MUTE                         0x0080  /* LI34MUTE */
+#define WM8991_LI34MUTE_BIT			7
+#define WM8991_LI34ZC                           0x0040  /* LI34ZC */
+#define WM8991_LI34ZC_BIT			6
+#define WM8991_LIN34VOL_MASK                    0x001F  /* LIN34VOL - [4:0] */
+#define WM8991_LIN34VOL_SHIFT			0
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_RI12MUTE                         0x0080  /* RI12MUTE */
+#define WM8991_RI12MUTE_BIT			7
+#define WM8991_RI12ZC                           0x0040  /* RI12ZC */
+#define WM8991_RI12ZC_BIT			6
+#define WM8991_RIN12VOL_MASK                    0x001F  /* RIN12VOL - [4:0] */
+#define WM8991_RIN12VOL_SHIFT			0
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_RI34MUTE                         0x0080  /* RI34MUTE */
+#define WM8991_RI34MUTE_BIT			7
+#define WM8991_RI34ZC                           0x0040  /* RI34ZC */
+#define WM8991_RI34ZC_BIT			6
+#define WM8991_RIN34VOL_MASK                    0x001F  /* RIN34VOL - [4:0] */
+#define WM8991_RIN34VOL_SHIFT			0
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_LOZC                             0x0080  /* LOZC */
+#define WM8991_LOZC_BIT				7
+#define WM8991_LOUTVOL_MASK                     0x007F  /* LOUTVOL - [6:0] */
+#define WM8991_LOUTVOL_SHIFT			0
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_ROZC                             0x0080  /* ROZC */
+#define WM8991_ROZC_BIT				7
+#define WM8991_ROUTVOL_MASK                     0x007F  /* ROUTVOL - [6:0] */
+#define WM8991_ROUTVOL_SHIFT			0
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8991_LONMUTE                          0x0040  /* LONMUTE */
+#define WM8991_LONMUTE_BIT			6
+#define WM8991_LOPMUTE                          0x0020  /* LOPMUTE */
+#define WM8991_LOPMUTE_BIT			5
+#define WM8991_LOATTN                           0x0010  /* LOATTN */
+#define WM8991_LOATTN_BIT			4
+#define WM8991_RONMUTE                          0x0004  /* RONMUTE */
+#define WM8991_RONMUTE_BIT			2
+#define WM8991_ROPMUTE                          0x0002  /* ROPMUTE */
+#define WM8991_ROPMUTE_BIT			1
+#define WM8991_ROATTN                           0x0001  /* ROATTN */
+#define WM8991_ROATTN_BIT			0
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8991_OUT3MUTE                         0x0020  /* OUT3MUTE */
+#define WM8991_OUT3MUTE_BIT			5
+#define WM8991_OUT3ATTN                         0x0010  /* OUT3ATTN */
+#define WM8991_OUT3ATTN_BIT			4
+#define WM8991_OUT4MUTE                         0x0002  /* OUT4MUTE */
+#define WM8991_OUT4MUTE_BIT			1
+#define WM8991_OUT4ATTN                         0x0001  /* OUT4ATTN */
+#define WM8991_OUT4ATTN_BIT			0
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_LOPGAZC                          0x0080  /* LOPGAZC */
+#define WM8991_LOPGAZC_BIT			7
+#define WM8991_LOPGAVOL_MASK                    0x007F  /* LOPGAVOL - [6:0] */
+#define WM8991_LOPGAVOL_SHIFT			0
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_ROPGAZC                          0x0080  /* ROPGAZC */
+#define WM8991_ROPGAZC_BIT			7
+#define WM8991_ROPGAVOL_MASK                    0x007F  /* ROPGAVOL - [6:0] */
+#define WM8991_ROPGAVOL_SHIFT			0
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8991_SPKVOL_MASK                      0x0003  /* SPKVOL - [1:0] */
+#define WM8991_SPKVOL_SHIFT			0
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8991_CDMODE                           0x0100  /* CDMODE */
+#define WM8991_CDMODE_BIT			8
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8991_DCGAIN_MASK                      0x0007  /* DCGAIN - [5:3] */
+#define WM8991_DCGAIN_SHIFT			3
+#define WM8991_ACGAIN_MASK                      0x0007  /* ACGAIN - [2:0] */
+#define WM8991_ACGAIN_SHIFT			0
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8991_AINLMODE_MASK                    0x000C  /* AINLMODE - [3:2] */
+#define WM8991_AINLMODE_SHIFT			2
+#define WM8991_AINRMODE_MASK                    0x0003  /* AINRMODE - [1:0] */
+#define WM8991_AINRMODE_SHIFT			0
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8991_LMP4								0x0080	/* LMP4 */
+#define WM8991_LMP4_BIT                         7		/* LMP4 */
+#define WM8991_LMN3                             0x0040  /* LMN3 */
+#define WM8991_LMN3_BIT                         6       /* LMN3 */
+#define WM8991_LMP2                             0x0020  /* LMP2 */
+#define WM8991_LMP2_BIT                         5       /* LMP2 */
+#define WM8991_LMN1                             0x0010  /* LMN1 */
+#define WM8991_LMN1_BIT                         4       /* LMN1 */
+#define WM8991_RMP4                             0x0008  /* RMP4 */
+#define WM8991_RMP4_BIT                         3       /* RMP4 */
+#define WM8991_RMN3                             0x0004  /* RMN3 */
+#define WM8991_RMN3_BIT                         2       /* RMN3 */
+#define WM8991_RMP2                             0x0002  /* RMP2 */
+#define WM8991_RMP2_BIT                         1       /* RMP2 */
+#define WM8991_RMN1                             0x0001  /* RMN1 */
+#define WM8991_RMN1_BIT                         0       /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8991_L34MNB                           0x0100  /* L34MNB */
+#define WM8991_L34MNB_BIT			8
+#define WM8991_L34MNBST                         0x0080  /* L34MNBST */
+#define WM8991_L34MNBST_BIT			7
+#define WM8991_L12MNB                           0x0020  /* L12MNB */
+#define WM8991_L12MNB_BIT			5
+#define WM8991_L12MNBST                         0x0010  /* L12MNBST */
+#define WM8991_L12MNBST_BIT			4
+#define WM8991_LDBVOL_MASK                      0x0007  /* LDBVOL - [2:0] */
+#define WM8991_LDBVOL_SHIFT			0
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8991_R34MNB                           0x0100  /* R34MNB */
+#define WM8991_R34MNB_BIT			8
+#define WM8991_R34MNBST                         0x0080  /* R34MNBST */
+#define WM8991_R34MNBST_BIT			7
+#define WM8991_R12MNB                           0x0020  /* R12MNB */
+#define WM8991_R12MNB_BIT			5
+#define WM8991_R12MNBST                         0x0010  /* R12MNBST */
+#define WM8991_R12MNBST_BIT			4
+#define WM8991_RDBVOL_MASK                      0x0007  /* RDBVOL - [2:0] */
+#define WM8991_RDBVOL_SHIFT			0
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8991_LI2BVOL_MASK                     0x07  /* LI2BVOL - [8:6] */
+#define WM8991_LI2BVOL_SHIFT			6
+#define WM8991_LR4BVOL_MASK                     0x07  /* LR4BVOL - [5:3] */
+#define WM8991_LR4BVOL_SHIFT			3
+#define WM8991_LL4BVOL_MASK                     0x07  /* LL4BVOL - [2:0] */
+#define WM8991_LL4BVOL_SHIFT			0
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8991_RI2BVOL_MASK                     0x07  /* RI2BVOL - [8:6] */
+#define WM8991_RI2BVOL_SHIFT			6
+#define WM8991_RL4BVOL_MASK                     0x07  /* RL4BVOL - [5:3] */
+#define WM8991_RL4BVOL_SHIFT			3
+#define WM8991_RR4BVOL_MASK                     0x07  /* RR4BVOL - [2:0] */
+#define WM8991_RR4BVOL_SHIFT			0
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8991_LRBLO                            0x0080  /* LRBLO */
+#define WM8991_LRBLO_BIT			7
+#define WM8991_LLBLO                            0x0040  /* LLBLO */
+#define WM8991_LLBLO_BIT			6
+#define WM8991_LRI3LO                           0x0020  /* LRI3LO */
+#define WM8991_LRI3LO_BIT			5
+#define WM8991_LLI3LO                           0x0010  /* LLI3LO */
+#define WM8991_LLI3LO_BIT			4
+#define WM8991_LR12LO                           0x0008  /* LR12LO */
+#define WM8991_LR12LO_BIT			3
+#define WM8991_LL12LO                           0x0004  /* LL12LO */
+#define WM8991_LL12LO_BIT			2
+#define WM8991_LDLO                             0x0001  /* LDLO */
+#define WM8991_LDLO_BIT				0
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8991_RLBRO                            0x0080  /* RLBRO */
+#define WM8991_RLBRO_BIT			7
+#define WM8991_RRBRO                            0x0040  /* RRBRO */
+#define WM8991_RRBRO_BIT			6
+#define WM8991_RLI3RO                           0x0020  /* RLI3RO */
+#define WM8991_RLI3RO_BIT			5
+#define WM8991_RRI3RO                           0x0010  /* RRI3RO */
+#define WM8991_RRI3RO_BIT			4
+#define WM8991_RL12RO                           0x0008  /* RL12RO */
+#define WM8991_RL12RO_BIT			3
+#define WM8991_RR12RO                           0x0004  /* RR12RO */
+#define WM8991_RR12RO_BIT			2
+#define WM8991_RDRO                             0x0001  /* RDRO */
+#define WM8991_RDRO_BIT				0
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8991_LLI3LOVOL_MASK                   0x07  /* LLI3LOVOL - [8:6] */
+#define WM8991_LLI3LOVOL_SHIFT			6
+#define WM8991_LR12LOVOL_MASK                   0x07  /* LR12LOVOL - [5:3] */
+#define WM8991_LR12LOVOL_SHIFT			3
+#define WM8991_LL12LOVOL_MASK                   0x07  /* LL12LOVOL - [2:0] */
+#define WM8991_LL12LOVOL_SHIFT			0
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8991_RRI3ROVOL_MASK                   0x07  /* RRI3ROVOL - [8:6] */
+#define WM8991_RRI3ROVOL_SHIFT			6
+#define WM8991_RL12ROVOL_MASK                   0x07  /* RL12ROVOL - [5:3] */
+#define WM8991_RL12ROVOL_SHIFT			3
+#define WM8991_RR12ROVOL_MASK                   0x07  /* RR12ROVOL - [2:0] */
+#define WM8991_RR12ROVOL_SHIFT			0
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8991_LRI3LOVOL_MASK                   0x07  /* LRI3LOVOL - [8:6] */
+#define WM8991_LRI3LOVOL_SHIFT			6
+#define WM8991_LRBLOVOL_MASK                    0x07  /* LRBLOVOL - [5:3] */
+#define WM8991_LRBLOVOL_SHIFT			3
+#define WM8991_LLBLOVOL_MASK                    0x07  /* LLBLOVOL - [2:0] */
+#define WM8991_LLBLOVOL_SHIFT			0
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8991_RLI3ROVOL_MASK                   0x07  /* RLI3ROVOL - [8:6] */
+#define WM8991_RLI3ROVOL_SHIFT			6
+#define WM8991_RLBROVOL_MASK                    0x07  /* RLBROVOL - [5:3] */
+#define WM8991_RLBROVOL_SHIFT			3
+#define WM8991_RRBROVOL_MASK                    0x07  /* RRBROVOL - [2:0] */
+#define WM8991_RRBROVOL_SHIFT			0
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8991_VSEL_MASK                        0x0180  /* VSEL - [8:7] */
+#define WM8991_LI4O3                            0x0020  /* LI4O3 */
+#define WM8991_LI4O3_BIT			5
+#define WM8991_LPGAO3                           0x0010  /* LPGAO3 */
+#define WM8991_LPGAO3_BIT			4
+#define WM8991_RI4O4                            0x0002  /* RI4O4 */
+#define WM8991_RI4O4_BIT			1
+#define WM8991_RPGAO4                           0x0001  /* RPGAO4 */
+#define WM8991_RPGAO4_BIT			0
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8991_LLOPGALON                        0x0040  /* LLOPGALON */
+#define WM8991_LLOPGALON_BIT			6
+#define WM8991_LROPGALON                        0x0020  /* LROPGALON */
+#define WM8991_LROPGALON_BIT			5
+#define WM8991_LOPLON                           0x0010  /* LOPLON */
+#define WM8991_LOPLON_BIT			4
+#define WM8991_LR12LOP                          0x0004  /* LR12LOP */
+#define WM8991_LR12LOP_BIT			2
+#define WM8991_LL12LOP                          0x0002  /* LL12LOP */
+#define WM8991_LL12LOP_BIT			1
+#define WM8991_LLOPGALOP                        0x0001  /* LLOPGALOP */
+#define WM8991_LLOPGALOP_BIT			0
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8991_RROPGARON                        0x0040  /* RROPGARON */
+#define WM8991_RROPGARON_BIT			6
+#define WM8991_RLOPGARON                        0x0020  /* RLOPGARON */
+#define WM8991_RLOPGARON_BIT			5
+#define WM8991_ROPRON                           0x0010  /* ROPRON */
+#define WM8991_ROPRON_BIT			4
+#define WM8991_RL12ROP                          0x0004  /* RL12ROP */
+#define WM8991_RL12ROP_BIT			2
+#define WM8991_RR12ROP                          0x0002  /* RR12ROP */
+#define WM8991_RR12ROP_BIT			1
+#define WM8991_RROPGAROP                        0x0001  /* RROPGAROP */
+#define WM8991_RROPGAROP_BIT			0
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8991_LB2SPK                           0x0080  /* LB2SPK */
+#define WM8991_LB2SPK_BIT			7
+#define WM8991_RB2SPK                           0x0040  /* RB2SPK */
+#define WM8991_RB2SPK_BIT			6
+#define WM8991_LI2SPK                           0x0020  /* LI2SPK */
+#define WM8991_LI2SPK_BIT			5
+#define WM8991_RI2SPK                           0x0010  /* RI2SPK */
+#define WM8991_RI2SPK_BIT			4
+#define WM8991_LOPGASPK                         0x0008  /* LOPGASPK */
+#define WM8991_LOPGASPK_BIT			3
+#define WM8991_ROPGASPK                         0x0004  /* ROPGASPK */
+#define WM8991_ROPGASPK_BIT			2
+#define WM8991_LDSPK                            0x0002  /* LDSPK */
+#define WM8991_LDSPK_BIT			1
+#define WM8991_RDSPK                            0x0001  /* RDSPK */
+#define WM8991_RDSPK_BIT			0
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8991_VROI                             0x0001  /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8991_DIS_LLINE                        0x0020  /* DIS_LLINE */
+#define WM8991_DIS_RLINE                        0x0010  /* DIS_RLINE */
+#define WM8991_DIS_OUT3                         0x0008  /* DIS_OUT3 */
+#define WM8991_DIS_OUT4                         0x0004  /* DIS_OUT4 */
+#define WM8991_DIS_LOUT                         0x0002  /* DIS_LOUT */
+#define WM8991_DIS_ROUT                         0x0001  /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8991_SOFTST                           0x0040  /* SOFTST */
+#define WM8991_BUFIOEN                          0x0008  /* BUFIOEN */
+#define WM8991_BUFDCOPEN                        0x0004  /* BUFDCOPEN */
+#define WM8991_POBCTRL                          0x0002  /* POBCTRL */
+#define WM8991_VMIDTOG                          0x0001  /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8991_MCDSCTH_MASK                     0x00C0  /* MCDSCTH - [7:6] */
+#define WM8991_MCDTHR_MASK                      0x0038  /* MCDTHR - [5:3] */
+#define WM8991_MCD                              0x0004  /* MCD */
+#define WM8991_MBSEL                            0x0001  /* MBSEL */
+
+/*
+ * R60 (0x3C) - PLL1
+ */
+#define WM8991_SDM                              0x0080  /* SDM */
+#define WM8991_PRESCALE                         0x0040  /* PRESCALE */
+#define WM8991_PLLN_MASK                        0x000F  /* PLLN - [3:0] */
+
+/*
+ * R61 (0x3D) - PLL2
+ */
+#define WM8991_PLLK1_MASK                       0x00FF  /* PLLK1 - [7:0] */
+
+/*
+ * R62 (0x3E) - PLL3
+ */
+#define WM8991_PLLK2_MASK                       0x00FF  /* PLLK2 - [7:0] */
+
+/*
+ * R63 (0x3F) - Internal Driver Bits
+ */
+#define WM8991_INMIXL_PWR_BIT			0
+#define WM8991_AINLMUX_PWR_BIT			1
+#define WM8991_INMIXR_PWR_BIT			2
+#define WM8991_AINRMUX_PWR_BIT			3
+
+#define WM8991_MCLK_DIV 0
+#define WM8991_DACCLK_DIV 1
+#define WM8991_ADCCLK_DIV 2
+#define WM8991_BCLK_DIV 3
+
+#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
+					 tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
+	.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+#endif /* _WM8991_H */
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 18c0d9c..379fa22 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -242,7 +242,7 @@
 	int fll_src;
 };
 
-static int wm8993_volatile(unsigned int reg)
+static int wm8993_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM8993_SOFTWARE_RESET:
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 3351f77..0ca81d3 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -109,7 +109,7 @@
 	struct wm8994_pdata *pdata;
 };
 
-static int wm8994_readable(unsigned int reg)
+static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM8994_GPIO_1:
@@ -136,7 +136,7 @@
 	return wm8994_access_masks[reg].readable != 0;
 }
 
-static int wm8994_volatile(unsigned int reg)
+static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
 	if (reg >= WM8994_CACHE_SIZE)
 		return 1;
@@ -164,7 +164,7 @@
 
 	BUG_ON(reg > WM8994_MAX_REGISTER);
 
-	if (!wm8994_volatile(reg)) {
+	if (!wm8994_volatile(codec, reg)) {
 		ret = snd_soc_cache_write(codec, reg, value);
 		if (ret != 0)
 			dev_err(codec->dev, "Cache write to %x failed: %d\n",
@@ -182,7 +182,7 @@
 
 	BUG_ON(reg > WM8994_MAX_REGISTER);
 
-	if (!wm8994_volatile(reg) && wm8994_readable(reg) &&
+	if (!wm8994_volatile(codec, reg) && wm8994_readable(codec, reg) &&
 	    reg < codec->driver->reg_cache_size) {
 		ret = snd_soc_cache_read(codec, reg, &val);
 		if (ret >= 0)
@@ -2943,7 +2943,7 @@
 	/* Read our current status back from the chip - we don't want to
 	 * reset as this may interfere with the GPIO or LDO operation. */
 	for (i = 0; i < WM8994_CACHE_SIZE; i++) {
-		if (!wm8994_readable(i) || wm8994_volatile(i))
+		if (!wm8994_readable(codec, i) || wm8994_volatile(codec, i))
 			continue;
 
 		ret = wm8994_reg_read(codec->control_data, i);
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 608c84c..67eaaec 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -19,6 +19,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -30,6 +31,18 @@
 
 #include "wm8995.h"
 
+#define WM8995_NUM_SUPPLIES 8
+static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = {
+	"DCVDD",
+	"DBVDD1",
+	"DBVDD2",
+	"DBVDD3",
+	"AVDD1",
+	"AVDD2",
+	"CPVDD",
+	"MICVDD"
+};
+
 static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = {
 	[0]     = 0x8995, [5]     = 0x0100, [16]    = 0x000b, [17]    = 0x000b,
 	[24]    = 0x02c0, [25]    = 0x02c0, [26]    = 0x02c0, [27]    = 0x02c0,
@@ -126,8 +139,37 @@
 	int mclk[2];
 	int aifclk[2];
 	struct fll_config fll[2], fll_suspend[2];
+	struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES];
+	struct notifier_block disable_nb[WM8995_NUM_SUPPLIES];
+	struct snd_soc_codec *codec;
 };
 
+/*
+ * We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8995_REGULATOR_EVENT(n) \
+static int wm8995_regulator_event_##n(struct notifier_block *nb, \
+				      unsigned long event, void *data)    \
+{ \
+	struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \
+				     disable_nb[n]); \
+	if (event & REGULATOR_EVENT_DISABLE) { \
+		wm8995->codec->cache_sync = 1; \
+	} \
+	return 0; \
+}
+
+WM8995_REGULATOR_EVENT(0)
+WM8995_REGULATOR_EVENT(1)
+WM8995_REGULATOR_EVENT(2)
+WM8995_REGULATOR_EVENT(3)
+WM8995_REGULATOR_EVENT(4)
+WM8995_REGULATOR_EVENT(5)
+WM8995_REGULATOR_EVENT(6)
+WM8995_REGULATOR_EVENT(7)
+
 static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
 static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0);
 static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0);
@@ -909,7 +951,7 @@
 	{ "SPK2R", NULL, "SPK2R Driver" }
 };
 
-static int wm8995_volatile(unsigned int reg)
+static int wm8995_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
 	/* out of bounds registers are generally considered
 	 * volatile to support register banks that are partially
@@ -1483,6 +1525,11 @@
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+			ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
+						    wm8995->supplies);
+			if (ret)
+				return ret;
+
 			ret = snd_soc_cache_sync(codec);
 			if (ret) {
 				dev_err(codec->dev,
@@ -1492,12 +1539,13 @@
 
 			snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
 					    WM8995_BG_ENA_MASK, WM8995_BG_ENA);
-
 		}
 		break;
 	case SND_SOC_BIAS_OFF:
 		snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
 				    WM8995_BG_ENA_MASK, 0);
+		regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies),
+				       wm8995->supplies);
 		break;
 	}
 
@@ -1536,10 +1584,12 @@
 static int wm8995_probe(struct snd_soc_codec *codec)
 {
 	struct wm8995_priv *wm8995;
+	int i;
 	int ret;
 
 	codec->dapm.idle_bias_off = 1;
 	wm8995 = snd_soc_codec_get_drvdata(codec);
+	wm8995->codec = codec;
 
 	ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type);
 	if (ret < 0) {
@@ -1547,21 +1597,58 @@
 		return ret;
 	}
 
+	for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++)
+		wm8995->supplies[i].supply = wm8995_supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies),
+				 wm8995->supplies);
+	if (ret) {
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+		return ret;
+	}
+
+	wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0;
+	wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1;
+	wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2;
+	wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3;
+	wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4;
+	wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5;
+	wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6;
+	wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7;
+
+	/* This should really be moved into the regulator core */
+	for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) {
+		ret = regulator_register_notifier(wm8995->supplies[i].consumer,
+						  &wm8995->disable_nb[i]);
+		if (ret) {
+			dev_err(codec->dev,
+				"Failed to register regulator notifier: %d\n",
+				ret);
+		}
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
+				    wm8995->supplies);
+	if (ret) {
+		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+		goto err_reg_get;
+	}
+
 	ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to read device ID: %d\n", ret);
-		return ret;
+		goto err_reg_enable;
 	}
 
 	if (ret != 0x8995) {
 		dev_err(codec->dev, "Invalid device ID: %#x\n", ret);
-		return -EINVAL;
+		goto err_reg_enable;
 	}
 
 	ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
-		return ret;
+		goto err_reg_enable;
 	}
 
 	wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1596,6 +1683,12 @@
 				ARRAY_SIZE(wm8995_intercon));
 
 	return 0;
+
+err_reg_enable:
+	regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
+err_reg_get:
+	regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
+	return ret;
 }
 
 #define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 43825b2..5c224dd 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -169,7 +169,7 @@
 	struct wm9081_retune_mobile_config *retune;
 };
 
-static int wm9081_volatile_register(unsigned int reg)
+static int wm9081_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM9081_SOFTWARE_RESET:
diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c
index a788c42..4de1220 100644
--- a/sound/soc/codecs/wm9090.c
+++ b/sound/soc/codecs/wm9090.c
@@ -144,7 +144,7 @@
 	void *control_data;
 };
 
-static int wm9090_volatile(unsigned int reg)
+static int wm9090_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM9090_SOFTWARE_RESET:
@@ -518,7 +518,7 @@
 			for (i = 1; i < codec->driver->reg_cache_size; i++) {
 				if (reg_cache[i] == wm9090_reg_defaults[i])
 					continue;
-				if (wm9090_volatile(i))
+				if (wm9090_volatile(codec, i))
 					continue;
 
 				ret = snd_soc_write(codec, i, reg_cache[i]);
@@ -551,7 +551,6 @@
 static int wm9090_probe(struct snd_soc_codec *codec)
 {
 	struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
-	u16 *reg_cache = codec->reg_cache;
 	int ret;
 
 	codec->control_data = wm9090->control_data;
@@ -576,22 +575,30 @@
 	/* Configure some defaults; they will be written out when we
 	 * bring the bias up.
 	 */
-	reg_cache[WM9090_IN1_LINE_INPUT_A_VOLUME] |= WM9090_IN1_VU
-		| WM9090_IN1A_ZC;
-	reg_cache[WM9090_IN1_LINE_INPUT_B_VOLUME] |= WM9090_IN1_VU
-		| WM9090_IN1B_ZC;
-	reg_cache[WM9090_IN2_LINE_INPUT_A_VOLUME] |= WM9090_IN2_VU
-		| WM9090_IN2A_ZC;
-	reg_cache[WM9090_IN2_LINE_INPUT_B_VOLUME] |= WM9090_IN2_VU
-		| WM9090_IN2B_ZC;
-	reg_cache[WM9090_SPEAKER_VOLUME_LEFT] |=
-		WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC;
-	reg_cache[WM9090_LEFT_OUTPUT_VOLUME] |=
-		WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC;
-	reg_cache[WM9090_RIGHT_OUTPUT_VOLUME] |=
-		WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC;
+	snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_A_VOLUME,
+			    WM9090_IN1_VU | WM9090_IN1A_ZC,
+			    WM9090_IN1_VU | WM9090_IN1A_ZC);
+	snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_B_VOLUME,
+			    WM9090_IN1_VU | WM9090_IN1B_ZC,
+			    WM9090_IN1_VU | WM9090_IN1B_ZC);
+	snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_A_VOLUME,
+			    WM9090_IN2_VU | WM9090_IN2A_ZC,
+			    WM9090_IN2_VU | WM9090_IN2A_ZC);
+	snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_B_VOLUME,
+			    WM9090_IN2_VU | WM9090_IN2B_ZC,
+			    WM9090_IN2_VU | WM9090_IN2B_ZC);
+	snd_soc_update_bits(codec, WM9090_SPEAKER_VOLUME_LEFT,
+			    WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC,
+			    WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC);
+	snd_soc_update_bits(codec, WM9090_LEFT_OUTPUT_VOLUME,
+			    WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC,
+			    WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC);
+	snd_soc_update_bits(codec, WM9090_RIGHT_OUTPUT_VOLUME,
+			    WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC,
+			    WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC);
 
-	reg_cache[WM9090_CLOCKING_1] |= WM9090_TOCLK_ENA;
+	snd_soc_update_bits(codec, WM9090_CLOCKING_1,
+			    WM9090_TOCLK_ENA, WM9090_TOCLK_ENA);
 
 	wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
diff --git a/sound/soc/ep93xx/Kconfig b/sound/soc/ep93xx/Kconfig
index 5742904..91a28de 100644
--- a/sound/soc/ep93xx/Kconfig
+++ b/sound/soc/ep93xx/Kconfig
@@ -30,3 +30,12 @@
 	help
 	  Say Y or M here if you want to add support for AC97 audio on the
 	  Simplemachines Sim.One board.
+
+config SND_EP93XX_SOC_EDB93XX
+	tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
+	depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
+	select SND_EP93XX_SOC_I2S
+	select SND_SOC_CS4271
+	help
+	  Say Y or M here if you want to add support for I2S audio on the
+	  Cirrus Logic EDB93xx boards.
diff --git a/sound/soc/ep93xx/Makefile b/sound/soc/ep93xx/Makefile
index 8e7977f..5514146 100644
--- a/sound/soc/ep93xx/Makefile
+++ b/sound/soc/ep93xx/Makefile
@@ -10,6 +10,8 @@
 # EP93XX Machine Support
 snd-soc-snappercl15-objs			:= snappercl15.o
 snd-soc-simone-objs				:= simone.o
+snd-soc-edb93xx-objs				:= edb93xx.o
 
 obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15)	+= snd-soc-snappercl15.o
 obj-$(CONFIG_SND_EP93XX_SOC_SIMONE)		+= snd-soc-simone.o
+obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX)		+= snd-soc-edb93xx.o
diff --git a/sound/soc/ep93xx/edb93xx.c b/sound/soc/ep93xx/edb93xx.c
new file mode 100644
index 0000000..b270085
--- /dev/null
+++ b/sound/soc/ep93xx/edb93xx.c
@@ -0,0 +1,142 @@
+/*
+ * SoC audio for EDB93xx
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver support CS4271 codec being master or slave, working
+ * in control port mode, connected either via SPI or I2C.
+ * The data format accepted is I2S or left-justified.
+ * DAPM support not implemented.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include "ep93xx-pcm.h"
+
+#define edb93xx_has_audio() (machine_is_edb9301() ||	\
+			     machine_is_edb9302() ||	\
+			     machine_is_edb9302a() ||	\
+			     machine_is_edb9307a() ||	\
+			     machine_is_edb9315a())
+
+static int edb93xx_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->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int err;
+	unsigned int rate = params_rate(params);
+	/*
+	 * We set LRCLK equal to `rate' and SCLK = LRCLK * 64,
+	 * because our sample size is 32 bit * 2 channels.
+	 * I2S standard permits us to transmit more bits than
+	 * the codec uses.
+	 * MCLK = SCLK * 4 is the best recommended value,
+	 * but we have to fall back to ratio 2 for higher
+	 * sample rates.
+	 */
+	unsigned int mclk_rate = rate * 64 * ((rate <= 48000) ? 4 : 2);
+
+	err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_IF |
+				  SND_SOC_DAIFMT_CBS_CFS);
+	if (err)
+		return err;
+
+	err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_IF |
+				  SND_SOC_DAIFMT_CBS_CFS);
+	if (err)
+		return err;
+
+	err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate,
+				     SND_SOC_CLOCK_IN);
+	if (err)
+		return err;
+
+	return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate,
+				      SND_SOC_CLOCK_OUT);
+}
+
+static struct snd_soc_ops edb93xx_ops = {
+	.hw_params	= edb93xx_hw_params,
+};
+
+static struct snd_soc_dai_link edb93xx_dai = {
+	.name		= "CS4271",
+	.stream_name	= "CS4271 HiFi",
+	.platform_name	= "ep93xx-pcm-audio",
+	.cpu_dai_name	= "ep93xx-i2s",
+	.codec_name	= "spi0.0",
+	.codec_dai_name	= "cs4271-hifi",
+	.ops		= &edb93xx_ops,
+};
+
+static struct snd_soc_card snd_soc_edb93xx = {
+	.name		= "EDB93XX",
+	.dai_link	= &edb93xx_dai,
+	.num_links	= 1,
+};
+
+static struct platform_device *edb93xx_snd_device;
+
+static int __init edb93xx_init(void)
+{
+	int ret;
+
+	if (!edb93xx_has_audio())
+		return -ENODEV;
+
+	ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
+				 EP93XX_SYSCON_I2SCLKDIV_ORIDE |
+				 EP93XX_SYSCON_I2SCLKDIV_SPOL);
+	if (ret)
+		return ret;
+
+	edb93xx_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!edb93xx_snd_device) {
+		ret = -ENOMEM;
+		goto free_i2s;
+	}
+
+	platform_set_drvdata(edb93xx_snd_device, &snd_soc_edb93xx);
+	ret = platform_device_add(edb93xx_snd_device);
+	if (ret)
+		goto device_put;
+
+	return 0;
+
+device_put:
+	platform_device_put(edb93xx_snd_device);
+free_i2s:
+	ep93xx_i2s_release();
+	return ret;
+}
+module_init(edb93xx_init);
+
+static void __exit edb93xx_exit(void)
+{
+	platform_device_unregister(edb93xx_snd_device);
+	ep93xx_i2s_release();
+}
+module_exit(edb93xx_exit);
+
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_DESCRIPTION("ALSA SoC EDB93xx");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index 7d7847a..c16c6b2 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -53,9 +53,8 @@
  *
  * Here we program the DMACR and PMUXCR registers.
  */
-static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
+static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card)
 {
-	struct snd_soc_card *card = platform_get_drvdata(sound_device);
 	struct mpc8610_hpcd_data *machine_data =
 		container_of(card, struct mpc8610_hpcd_data, card);
 	struct ccsr_guts_86xx __iomem *guts;
@@ -138,9 +137,8 @@
  * This function is called to remove the sound device for one SSI.  We
  * de-program the DMACR and PMUXCR register.
  */
-static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
+static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card)
 {
-	struct snd_soc_card *card = platform_get_drvdata(sound_device);
 	struct mpc8610_hpcd_data *machine_data =
 		container_of(card, struct mpc8610_hpcd_data, card);
 	struct ccsr_guts_86xx __iomem *guts;
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c
index 026b756..66e0b68 100644
--- a/sound/soc/fsl/p1022_ds.c
+++ b/sound/soc/fsl/p1022_ds.c
@@ -85,9 +85,8 @@
  *
  * Here we program the DMACR and PMUXCR registers.
  */
-static int p1022_ds_machine_probe(struct platform_device *sound_device)
+static int p1022_ds_machine_probe(struct snd_soc_card *card)
 {
-	struct snd_soc_card *card = platform_get_drvdata(sound_device);
 	struct machine_data *mdata =
 		container_of(card, struct machine_data, card);
 	struct ccsr_guts_85xx __iomem *guts;
@@ -160,9 +159,8 @@
  * This function is called to remove the sound device for one SSI.  We
  * de-program the DMACR and PMUXCR register.
  */
-static int p1022_ds_machine_remove(struct platform_device *sound_device)
+static int p1022_ds_machine_remove(struct snd_soc_card *card)
 {
-	struct snd_soc_card *card = platform_get_drvdata(sound_device);
 	struct machine_data *mdata =
 		container_of(card, struct machine_data, card);
 	struct ccsr_guts_85xx __iomem *guts;
diff --git a/sound/soc/mid-x86/Kconfig b/sound/soc/mid-x86/Kconfig
new file mode 100644
index 0000000..1ad7538
--- /dev/null
+++ b/sound/soc/mid-x86/Kconfig
@@ -0,0 +1,14 @@
+config SND_MFLD_MACHINE
+	tristate "SOC Machine Audio driver for Intel Medfield MID platform"
+	depends on INTEL_SCU_IPC
+	select SND_SOC_SN95031
+	select SND_SST_PLATFORM
+	help
+          This adds support for ASoC machine driver for Intel(R) MID Medfield platform
+          used as alsa device in audio substem in Intel(R) MID devices
+          Say Y if you have such a device
+          If unsure select "N".
+
+config SND_SST_PLATFORM
+	tristate
+	depends on SND_INTEL_SST
diff --git a/sound/soc/mid-x86/Makefile b/sound/soc/mid-x86/Makefile
new file mode 100644
index 0000000..6398833
--- /dev/null
+++ b/sound/soc/mid-x86/Makefile
@@ -0,0 +1,5 @@
+snd-soc-sst-platform-objs := sst_platform.o
+snd-soc-mfld-machine-objs := mfld_machine.o
+
+obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o
+obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o
diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c
new file mode 100644
index 0000000..1a330be
--- /dev/null
+++ b/sound/soc/mid-x86/mfld_machine.c
@@ -0,0 +1,296 @@
+/*
+ *  mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/sn95031.h"
+
+#define MID_MONO 1
+#define MID_STEREO 2
+#define MID_MAX_CAP 5
+
+static unsigned int	hs_switch;
+static unsigned int	lo_dac;
+
+/* sound card controls */
+static const char *headset_switch_text[] = {"Earpiece", "Headset"};
+
+static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
+
+static const struct soc_enum headset_enum =
+	SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
+
+static const struct soc_enum lo_enum =
+	SOC_ENUM_SINGLE_EXT(4, lo_text);
+
+static int headset_get_switch(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = hs_switch;
+	return 0;
+}
+
+static int headset_set_switch(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+	if (ucontrol->value.integer.value[0] == hs_switch)
+		return 0;
+
+	if (ucontrol->value.integer.value[0]) {
+		pr_debug("hs_set HS path\n");
+		snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTL");
+		snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTR");
+		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+	} else {
+		pr_debug("hs_set EP path\n");
+		snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL");
+		snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR");
+		snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+	}
+	snd_soc_dapm_sync(&codec->dapm);
+	hs_switch = ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static void lo_enable_out_pins(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");
+	snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");
+	snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");
+	snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");
+	snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");
+	snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");
+	if (hs_switch) {
+		snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTL");
+		snd_soc_dapm_enable_pin(&codec->dapm, "HPOUTR");
+		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+	} else {
+		snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL");
+		snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR");
+		snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+	}
+}
+
+static int lo_get_switch(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = lo_dac;
+	return 0;
+}
+
+static int lo_set_switch(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+	if (ucontrol->value.integer.value[0] == lo_dac)
+		return 0;
+
+	/* we dont want to work with last state of lineout so just enable all
+	 * pins and then disable pins not required
+	 */
+	lo_enable_out_pins(codec);
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		pr_debug("set vibra path\n");
+		snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT");
+		snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT");
+		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0);
+		break;
+
+	case 1:
+		pr_debug("set hs  path\n");
+		snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTL");
+		snd_soc_dapm_disable_pin(&codec->dapm, "HPOUTR");
+		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22);
+		break;
+
+	case 2:
+		pr_debug("set spkr path\n");
+		snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL");
+		snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR");
+		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44);
+		break;
+
+	case 3:
+		pr_debug("set null path\n");
+		snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL");
+		snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR");
+		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66);
+		break;
+	}
+	snd_soc_dapm_sync(&codec->dapm);
+	lo_dac = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static const struct snd_kcontrol_new mfld_snd_controls[] = {
+	SOC_ENUM_EXT("Playback Switch", headset_enum,
+			headset_get_switch, headset_set_switch),
+	SOC_ENUM_EXT("Lineout Mux", lo_enum,
+			lo_get_switch, lo_set_switch),
+};
+
+static int mfld_init(struct snd_soc_pcm_runtime *runtime)
+{
+	struct snd_soc_codec *codec = runtime->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	int ret_val;
+
+	ret_val = snd_soc_add_controls(codec, mfld_snd_controls,
+				ARRAY_SIZE(mfld_snd_controls));
+	if (ret_val) {
+		pr_err("soc_add_controls failed %d", ret_val);
+		return ret_val;
+	}
+	/* default is earpiece pin, userspace sets it explcitly */
+	snd_soc_dapm_disable_pin(dapm, "HPOUTL");
+	snd_soc_dapm_disable_pin(dapm, "HPOUTR");
+	/* default is lineout NC, userspace sets it explcitly */
+	snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
+	snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
+	lo_dac = 3;
+	hs_switch = 0;
+	return snd_soc_dapm_sync(dapm);
+}
+
+struct snd_soc_dai_link mfld_msic_dailink[] = {
+	{
+		.name = "Medfield Headset",
+		.stream_name = "Headset",
+		.cpu_dai_name = "Headset-cpu-dai",
+		.codec_dai_name = "SN95031 Headset",
+		.codec_name = "sn95031",
+		.platform_name = "sst-platform",
+		.init = mfld_init,
+	},
+	{
+		.name = "Medfield Speaker",
+		.stream_name = "Speaker",
+		.cpu_dai_name = "Speaker-cpu-dai",
+		.codec_dai_name = "SN95031 Speaker",
+		.codec_name = "sn95031",
+		.platform_name = "sst-platform",
+		.init = NULL,
+	},
+	{
+		.name = "Medfield Vibra",
+		.stream_name = "Vibra1",
+		.cpu_dai_name = "Vibra1-cpu-dai",
+		.codec_dai_name = "SN95031 Vibra1",
+		.codec_name = "sn95031",
+		.platform_name = "sst-platform",
+		.init = NULL,
+	},
+	{
+		.name = "Medfield Haptics",
+		.stream_name = "Vibra2",
+		.cpu_dai_name = "Vibra2-cpu-dai",
+		.codec_dai_name = "SN95031 Vibra2",
+		.codec_name = "sn95031",
+		.platform_name = "sst-platform",
+		.init = NULL,
+	},
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_mfld = {
+	.name = "medfield_audio",
+	.dai_link = mfld_msic_dailink,
+	.num_links = ARRAY_SIZE(mfld_msic_dailink),
+};
+
+static int __devinit snd_mfld_mc_probe(struct platform_device *pdev)
+{
+	struct platform_device *socdev;
+	int ret_val = 0;
+
+	pr_debug("snd_mfld_mc_probe called\n");
+
+	socdev =  platform_device_alloc("soc-audio", -1);
+	if (!socdev) {
+		pr_err("soc-audio device allocation failed\n");
+		return -ENOMEM;
+	}
+	platform_set_drvdata(socdev, &snd_soc_card_mfld);
+	ret_val = platform_device_add(socdev);
+	if (ret_val) {
+		pr_err("Unable to add soc-audio device, err %d\n", ret_val);
+		platform_device_put(socdev);
+	}
+
+	platform_set_drvdata(pdev, socdev);
+
+	pr_debug("successfully exited probe\n");
+	return ret_val;
+}
+
+static int __devexit snd_mfld_mc_remove(struct platform_device *pdev)
+{
+	struct platform_device *socdev =  platform_get_drvdata(pdev);
+	pr_debug("snd_mfld_mc_remove called\n");
+
+	platform_device_unregister(socdev);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver snd_mfld_mc_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "msic_audio",
+	},
+	.probe = snd_mfld_mc_probe,
+	.remove = __devexit_p(snd_mfld_mc_remove),
+};
+
+static int __init snd_mfld_driver_init(void)
+{
+	pr_debug("snd_mfld_driver_init called\n");
+	return platform_driver_register(&snd_mfld_mc_driver);
+}
+module_init(snd_mfld_driver_init);
+
+static void __exit snd_mfld_driver_exit(void)
+{
+	pr_debug("snd_mfld_driver_exit called\n");
+	platform_driver_unregister(&snd_mfld_mc_driver);
+}
+module_exit(snd_mfld_driver_exit);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:msic-audio");
diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c
new file mode 100644
index 0000000..1d1f544
--- /dev/null
+++ b/sound/soc/mid-x86/sst_platform.c
@@ -0,0 +1,459 @@
+/*
+ *  sst_platform.c - Intel MID Platform driver
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../../../drivers/staging/intel_sst/intel_sst_ioctl.h"
+#include "../../../drivers/staging/intel_sst/intel_sst.h"
+#include "sst_platform.h"
+
+static struct snd_pcm_hardware sst_platform_pcm_hw = {
+	.info =	(SNDRV_PCM_INFO_INTERLEAVED |
+			SNDRV_PCM_INFO_DOUBLE |
+			SNDRV_PCM_INFO_PAUSE |
+			SNDRV_PCM_INFO_RESUME |
+			SNDRV_PCM_INFO_MMAP|
+			SNDRV_PCM_INFO_MMAP_VALID |
+			SNDRV_PCM_INFO_BLOCK_TRANSFER |
+			SNDRV_PCM_INFO_SYNC_START),
+	.formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |
+			SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 |
+			SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32),
+	.rates = (SNDRV_PCM_RATE_8000|
+			SNDRV_PCM_RATE_44100 |
+			SNDRV_PCM_RATE_48000),
+	.rate_min = SST_MIN_RATE,
+	.rate_max = SST_MAX_RATE,
+	.channels_min =	SST_MIN_CHANNEL,
+	.channels_max =	SST_MAX_CHANNEL,
+	.buffer_bytes_max = SST_MAX_BUFFER,
+	.period_bytes_min = SST_MIN_PERIOD_BYTES,
+	.period_bytes_max = SST_MAX_PERIOD_BYTES,
+	.periods_min = SST_MIN_PERIODS,
+	.periods_max = SST_MAX_PERIODS,
+	.fifo_size = SST_FIFO_SIZE,
+};
+
+/* MFLD - MSIC */
+struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+	.name = "Headset-cpu-dai",
+	.id = 0,
+	.playback = {
+		.channels_min = SST_STEREO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	},
+},
+{
+	.name = "Speaker-cpu-dai",
+	.id = 1,
+	.playback = {
+		.channels_min = SST_MONO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	},
+},
+{
+	.name = "Vibra1-cpu-dai",
+	.id = 2,
+	.playback = {
+		.channels_min = SST_MONO,
+		.channels_max = SST_MONO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	},
+},
+{
+	.name = "Vibra2-cpu-dai",
+	.id = 3,
+	.playback = {
+		.channels_min = SST_MONO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	},
+},
+};
+
+/* helper functions */
+static inline void sst_set_stream_status(struct sst_runtime_stream *stream,
+					int state)
+{
+	spin_lock(&stream->status_lock);
+	stream->stream_status = state;
+	spin_unlock(&stream->status_lock);
+}
+
+static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
+{
+	int state;
+
+	spin_lock(&stream->status_lock);
+	state = stream->stream_status;
+	spin_unlock(&stream->status_lock);
+	return state;
+}
+
+static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
+				struct snd_sst_stream_params *param)
+{
+
+	param->uc.pcm_params.codec = SST_CODEC_TYPE_PCM;
+	param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;
+	param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
+	param->uc.pcm_params.reserved = 0;
+	param->uc.pcm_params.sfreq = substream->runtime->rate;
+	param->uc.pcm_params.ring_buffer_size =
+					snd_pcm_lib_buffer_bytes(substream);
+	param->uc.pcm_params.period_count = substream->runtime->period_size;
+	param->uc.pcm_params.ring_buffer_addr =
+				virt_to_phys(substream->dma_buffer.area);
+	pr_debug("period_cnt = %d\n", param->uc.pcm_params.period_count);
+	pr_debug("sfreq= %d, wd_sz = %d\n",
+		 param->uc.pcm_params.sfreq, param->uc.pcm_params.pcm_wd_sz);
+}
+
+static int sst_platform_alloc_stream(struct snd_pcm_substream *substream)
+{
+	struct sst_runtime_stream *stream =
+			substream->runtime->private_data;
+	struct snd_sst_stream_params param = {{{0,},},};
+	struct snd_sst_params str_params = {0};
+	int ret_val;
+
+	/* set codec params and inform SST driver the same */
+	sst_fill_pcm_params(substream, &param);
+	substream->runtime->dma_area = substream->dma_buffer.area;
+	str_params.sparams = param;
+	str_params.codec =  param.uc.pcm_params.codec;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		str_params.ops = STREAM_OPS_PLAYBACK;
+		str_params.device_type = substream->pcm->device + 1;
+		pr_debug("Playbck stream,Device %d\n",
+					substream->pcm->device);
+	} else {
+		str_params.ops = STREAM_OPS_CAPTURE;
+		str_params.device_type = SND_SST_DEVICE_CAPTURE;
+		pr_debug("Capture stream,Device %d\n",
+					substream->pcm->device);
+	}
+	ret_val = stream->sstdrv_ops->pcm_control->open(&str_params);
+	pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val);
+	if (ret_val < 0)
+		return ret_val;
+
+	stream->stream_info.str_id = ret_val;
+	pr_debug("str id :  %d\n", stream->stream_info.str_id);
+	return ret_val;
+}
+
+static void sst_period_elapsed(void *mad_substream)
+{
+	struct snd_pcm_substream *substream = mad_substream;
+	struct sst_runtime_stream *stream;
+	int status;
+
+	if (!substream || !substream->runtime)
+		return;
+	stream = substream->runtime->private_data;
+	if (!stream)
+		return;
+	status = sst_get_stream_status(stream);
+	if (status != SST_PLATFORM_RUNNING)
+		return;
+	snd_pcm_period_elapsed(substream);
+}
+
+static int sst_platform_init_stream(struct snd_pcm_substream *substream)
+{
+	struct sst_runtime_stream *stream =
+			substream->runtime->private_data;
+	int ret_val;
+
+	pr_debug("setting buffer ptr param\n");
+	sst_set_stream_status(stream, SST_PLATFORM_INIT);
+	stream->stream_info.period_elapsed = sst_period_elapsed;
+	stream->stream_info.mad_substream = substream;
+	stream->stream_info.buffer_ptr = 0;
+	stream->stream_info.sfreq = substream->runtime->rate;
+	ret_val = stream->sstdrv_ops->pcm_control->device_control(
+			SST_SND_STREAM_INIT, &stream->stream_info);
+	if (ret_val)
+		pr_err("control_set ret error %d\n", ret_val);
+	return ret_val;
+
+}
+/* end -- helper functions */
+
+static int sst_platform_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime;
+	struct sst_runtime_stream *stream;
+	int ret_val = 0;
+
+	pr_debug("sst_platform_open called\n");
+	runtime = substream->runtime;
+	runtime->hw = sst_platform_pcm_hw;
+	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+	if (!stream)
+		return -ENOMEM;
+	spin_lock_init(&stream->status_lock);
+	stream->stream_info.str_id = 0;
+	sst_set_stream_status(stream, SST_PLATFORM_INIT);
+	stream->stream_info.mad_substream = substream;
+	/* allocate memory for SST API set */
+	stream->sstdrv_ops = kzalloc(sizeof(*stream->sstdrv_ops),
+							GFP_KERNEL);
+	if (!stream->sstdrv_ops) {
+		pr_err("sst: mem allocation for ops fail\n");
+		kfree(stream);
+		return -ENOMEM;
+	}
+	stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID;
+	/* registering with SST driver to get access to SST APIs to use */
+	ret_val = register_sst_card(stream->sstdrv_ops);
+	if (ret_val) {
+		pr_err("sst: sst card registration failed\n");
+		return ret_val;
+	}
+	runtime->private_data = stream;
+	return snd_pcm_hw_constraint_integer(runtime,
+			 SNDRV_PCM_HW_PARAM_PERIODS);
+}
+
+static int sst_platform_close(struct snd_pcm_substream *substream)
+{
+	struct sst_runtime_stream *stream;
+	int ret_val = 0, str_id;
+
+	pr_debug("sst_platform_close called\n");
+	stream = substream->runtime->private_data;
+	str_id = stream->stream_info.str_id;
+	if (str_id)
+		ret_val = stream->sstdrv_ops->pcm_control->close(str_id);
+	kfree(stream->sstdrv_ops);
+	kfree(stream);
+	return ret_val;
+}
+
+static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct sst_runtime_stream *stream;
+	int ret_val = 0, str_id;
+
+	pr_debug("sst_platform_pcm_prepare called\n");
+	stream = substream->runtime->private_data;
+	str_id = stream->stream_info.str_id;
+	if (stream->stream_info.str_id) {
+		ret_val = stream->sstdrv_ops->pcm_control->device_control(
+					SST_SND_DROP, &str_id);
+		return ret_val;
+	}
+
+	ret_val = sst_platform_alloc_stream(substream);
+	if (ret_val < 0)
+		return ret_val;
+	snprintf(substream->pcm->id, sizeof(substream->pcm->id),
+			"%d", stream->stream_info.str_id);
+
+	ret_val = sst_platform_init_stream(substream);
+	if (ret_val)
+		return ret_val;
+	substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;
+	return ret_val;
+}
+
+static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
+					int cmd)
+{
+	int ret_val = 0, str_id;
+	struct sst_runtime_stream *stream;
+	int str_cmd, status;
+
+	pr_debug("sst_platform_pcm_trigger called\n");
+	stream = substream->runtime->private_data;
+	str_id = stream->stream_info.str_id;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		pr_debug("sst: Trigger Start\n");
+		str_cmd = SST_SND_START;
+		status = SST_PLATFORM_RUNNING;
+		stream->stream_info.mad_substream = substream;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		pr_debug("sst: in stop\n");
+		str_cmd = SST_SND_DROP;
+		status = SST_PLATFORM_DROPPED;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		pr_debug("sst: in pause\n");
+		str_cmd = SST_SND_PAUSE;
+		status = SST_PLATFORM_PAUSED;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		pr_debug("sst: in pause release\n");
+		str_cmd = SST_SND_RESUME;
+		status = SST_PLATFORM_RUNNING;
+		break;
+	default:
+		return -EINVAL;
+	}
+	ret_val = stream->sstdrv_ops->pcm_control->device_control(str_cmd,
+								&str_id);
+	if (!ret_val)
+		sst_set_stream_status(stream, status);
+
+	return ret_val;
+}
+
+
+static snd_pcm_uframes_t sst_platform_pcm_pointer
+			(struct snd_pcm_substream *substream)
+{
+	struct sst_runtime_stream *stream;
+	int ret_val, status;
+	struct pcm_stream_info *str_info;
+
+	stream = substream->runtime->private_data;
+	status = sst_get_stream_status(stream);
+	if (status == SST_PLATFORM_INIT)
+		return 0;
+	str_info = &stream->stream_info;
+	ret_val = stream->sstdrv_ops->pcm_control->device_control(
+				SST_SND_BUFFER_POINTER, str_info);
+	if (ret_val) {
+		pr_err("sst: error code = %d\n", ret_val);
+		return ret_val;
+	}
+	return stream->stream_info.buffer_ptr;
+}
+
+
+static struct snd_pcm_ops sst_platform_ops = {
+	.open = sst_platform_open,
+	.close = sst_platform_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.prepare = sst_platform_pcm_prepare,
+	.trigger = sst_platform_pcm_trigger,
+	.pointer = sst_platform_pcm_pointer,
+};
+
+static void sst_pcm_free(struct snd_pcm *pcm)
+{
+	pr_debug("sst_pcm_free called\n");
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int sst_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+			struct snd_pcm *pcm)
+{
+	int retval = 0;
+
+	pr_debug("sst_pcm_new called\n");
+	if (dai->driver->playback.channels_min ||
+			dai->driver->capture.channels_min) {
+		retval =  snd_pcm_lib_preallocate_pages_for_all(pcm,
+			SNDRV_DMA_TYPE_CONTINUOUS,
+			snd_dma_continuous_data(GFP_KERNEL),
+			SST_MIN_BUFFER, SST_MAX_BUFFER);
+		if (retval) {
+			pr_err("dma buffer allocationf fail\n");
+			return retval;
+		}
+	}
+	return retval;
+}
+struct snd_soc_platform_driver sst_soc_platform_drv = {
+	.ops		= &sst_platform_ops,
+	.pcm_new	= sst_pcm_new,
+	.pcm_free	= sst_pcm_free,
+};
+
+static int sst_platform_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	pr_debug("sst_platform_probe called\n");
+	ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
+	if (ret) {
+		pr_err("registering soc platform failed\n");
+		return ret;
+	}
+
+	ret = snd_soc_register_dais(&pdev->dev,
+				sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
+	if (ret) {
+		pr_err("registering cpu dais failed\n");
+		snd_soc_unregister_platform(&pdev->dev);
+	}
+	return ret;
+}
+
+static int sst_platform_remove(struct platform_device *pdev)
+{
+
+	snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sst_platform_dai));
+	snd_soc_unregister_platform(&pdev->dev);
+	pr_debug("sst_platform_remove sucess\n");
+	return 0;
+}
+
+static struct platform_driver sst_platform_driver = {
+	.driver		= {
+		.name		= "sst-platform",
+		.owner		= THIS_MODULE,
+	},
+	.probe		= sst_platform_probe,
+	.remove		= sst_platform_remove,
+};
+
+static int __init sst_soc_platform_init(void)
+{
+	pr_debug("sst_soc_platform_init called\n");
+	return  platform_driver_register(&sst_platform_driver);
+}
+module_init(sst_soc_platform_init);
+
+static void __exit sst_soc_platform_exit(void)
+{
+	platform_driver_unregister(&sst_platform_driver);
+	pr_debug("sst_soc_platform_exit sucess\n");
+}
+module_exit(sst_soc_platform_exit);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sst-platform");
diff --git a/sound/soc/mid-x86/sst_platform.h b/sound/soc/mid-x86/sst_platform.h
new file mode 100644
index 0000000..df37028
--- /dev/null
+++ b/sound/soc/mid-x86/sst_platform.h
@@ -0,0 +1,63 @@
+/*
+ *  sst_platform.h - Intel MID Platform driver header file
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+
+#ifndef __SST_PLATFORMDRV_H__
+#define __SST_PLATFORMDRV_H__
+
+#define SST_MONO		1
+#define SST_STEREO		2
+#define SST_MAX_CAP		5
+
+#define SST_MIN_RATE		8000
+#define SST_MAX_RATE		48000
+#define SST_MIN_CHANNEL		1
+#define SST_MAX_CHANNEL		5
+#define SST_MAX_BUFFER		(800*1024)
+#define SST_MIN_BUFFER		(800*1024)
+#define SST_MIN_PERIOD_BYTES	32
+#define SST_MAX_PERIOD_BYTES	SST_MAX_BUFFER
+#define SST_MIN_PERIODS		2
+#define SST_MAX_PERIODS		(1024*2)
+#define SST_FIFO_SIZE		0
+#define SST_CARD_NAMES		"intel_mid_card"
+#define MSIC_VENDOR_ID		3
+
+struct sst_runtime_stream {
+	int     stream_status;
+	struct pcm_stream_info stream_info;
+	struct intel_sst_card_ops *sstdrv_ops;
+	spinlock_t	status_lock;
+};
+
+enum sst_drv_status {
+	SST_PLATFORM_INIT = 1,
+	SST_PLATFORM_STARTED,
+	SST_PLATFORM_RUNNING,
+	SST_PLATFORM_PAUSED,
+	SST_PLATFORM_DROPPED,
+};
+
+#endif
diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c
index 0fd60f4..db1dd56 100644
--- a/sound/soc/pxa/raumfeld.c
+++ b/sound/soc/pxa/raumfeld.c
@@ -151,13 +151,13 @@
 	.hw_params = raumfeld_cs4270_hw_params,
 };
 
-static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state)
+static int raumfeld_line_suspend(struct snd_soc_card *card)
 {
 	raumfeld_enable_audio(false);
 	return 0;
 }
 
-static int raumfeld_line_resume(struct platform_device *pdev)
+static int raumfeld_line_resume(struct snd_soc_card *card)
 {
 	raumfeld_enable_audio(true);
 	return 0;
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index f75804e..489139a 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -237,7 +237,7 @@
 },
 };
 
-static int tosa_probe(struct platform_device *dev)
+static int tosa_probe(struct snd_soc_card *card)
 {
 	int ret;
 
@@ -251,7 +251,7 @@
 	return ret;
 }
 
-static int tosa_remove(struct platform_device *dev)
+static int tosa_remove(struct snd_soc_card *card)
 {
 	gpio_free(TOSA_GPIO_L_MUTE);
 	return 0;
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
index b222a7d..c585829 100644
--- a/sound/soc/pxa/zylonite.c
+++ b/sound/soc/pxa/zylonite.c
@@ -189,7 +189,7 @@
 },
 };
 
-static int zylonite_probe(struct platform_device *pdev)
+static int zylonite_probe(struct snd_soc_card *card)
 {
 	int ret;
 
@@ -216,7 +216,7 @@
 	return 0;
 }
 
-static int zylonite_remove(struct platform_device *pdev)
+static int zylonite_remove(struct snd_soc_card *card)
 {
 	if (clk_pout) {
 		clk_disable(pout);
@@ -226,8 +226,7 @@
 	return 0;
 }
 
-static int zylonite_suspend_post(struct platform_device *pdev,
-				 pm_message_t state)
+static int zylonite_suspend_post(struct snd_soc_card *card)
 {
 	if (clk_pout)
 		clk_disable(pout);
@@ -235,7 +234,7 @@
 	return 0;
 }
 
-static int zylonite_resume_pre(struct platform_device *pdev)
+static int zylonite_resume_pre(struct snd_soc_card *card)
 {
 	int ret = 0;
 
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index 4770a95..f97110e 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -12,24 +12,24 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
 #include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
 
 #include <sound/soc.h>
 
-#include <plat/regs-ac97.h>
 #include <mach/dma.h>
+#include <plat/regs-ac97.h>
 #include <plat/audio.h>
 
 #include "dma.h"
-#include "ac97.h"
 
 #define AC_CMD_ADDR(x) (x << 16)
 #define AC_CMD_DATA(x) (x & 0xffff)
 
+#define S3C_AC97_DAI_PCM 0
+#define S3C_AC97_DAI_MIC 1
+
 struct s3c_ac97_info {
 	struct clk         *ac97_clk;
 	void __iomem	   *regs;
diff --git a/sound/soc/samsung/ac97.h b/sound/soc/samsung/ac97.h
deleted file mode 100644
index 0d0e1b5..0000000
--- a/sound/soc/samsung/ac97.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* sound/soc/samsung/ac97.h
- *
- * ALSA SoC Audio Layer - S3C AC97 Controller driver
- *	Evolved from s3c2443-ac97.h
- *
- * Copyright (c) 2010 Samsung Electronics Co. Ltd
- *	Author: Jaswinder Singh <jassi.brar@samsung.com>
- *	Credits: Graeme Gregory, Sean Choi
- *
- * 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 __S3C_AC97_H_
-#define __S3C_AC97_H_
-
-#define S3C_AC97_DAI_PCM 0
-#define S3C_AC97_DAI_MIC 1
-
-#endif /* __S3C_AC97_H_ */
diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c
index 2124019..9bce1df 100644
--- a/sound/soc/samsung/dma.c
+++ b/sound/soc/samsung/dma.c
@@ -14,17 +14,11 @@
  *  option) any later version.
  */
 
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/io.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 <sound/pcm_params.h>
 
 #include <asm/dma.h>
 #include <mach/hardware.h>
@@ -32,6 +26,9 @@
 
 #include "dma.h"
 
+#define ST_RUNNING		(1<<0)
+#define ST_OPENED		(1<<1)
+
 static const struct snd_pcm_hardware dma_hardware = {
 	.info			= SNDRV_PCM_INFO_INTERLEAVED |
 				    SNDRV_PCM_INFO_BLOCK_TRANSFER |
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
index f8cd2b4..c506592 100644
--- a/sound/soc/samsung/dma.h
+++ b/sound/soc/samsung/dma.h
@@ -12,9 +12,6 @@
 #ifndef _S3C_AUDIO_H
 #define _S3C_AUDIO_H
 
-#define ST_RUNNING		(1<<0)
-#define ST_OPENED		(1<<1)
-
 struct s3c_dma_params {
 	struct s3c2410_dma_client *client;	/* stream identifier */
 	int channel;				/* Channel ID */
@@ -22,9 +19,4 @@
 	int dma_size;			/* Size of the DMA transfer */
 };
 
-#define S3C24XX_DAI_I2S			0
-
-/* platform data */
-extern struct snd_ac97_bus_ops s3c24xx_ac97_ops;
-
 #endif
diff --git a/sound/soc/samsung/goni_wm8994.c b/sound/soc/samsung/goni_wm8994.c
index 34dd9ef..f6b3a3c 100644
--- a/sound/soc/samsung/goni_wm8994.c
+++ b/sound/soc/samsung/goni_wm8994.c
@@ -11,21 +11,13 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
+
 #include <asm/mach-types.h>
 #include <mach/gpio.h>
-#include <mach/regs-clock.h>
 
-#include <linux/mfd/wm8994/core.h>
-#include <linux/mfd/wm8994/registers.h>
 #include "../codecs/wm8994.h"
-#include "dma.h"
-#include "i2s.h"
 
 #define MACHINE_NAME	0
 #define CPU_VOICE_DAI	1
diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c
index c45f7ce..241f55d 100644
--- a/sound/soc/samsung/h1940_uda1380.c
+++ b/sound/soc/samsung/h1940_uda1380.c
@@ -13,25 +13,16 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
 #include <linux/gpio.h>
 
 #include <sound/soc.h>
-#include <sound/uda1380.h>
 #include <sound/jack.h>
 
 #include <plat/regs-iis.h>
-
 #include <mach/h1940-latch.h>
-
 #include <asm/mach-types.h>
 
-#include "dma.h"
 #include "s3c24xx-i2s.h"
-#include "../codecs/uda1380.h"
 
 static unsigned int rates[] = {
 	11025,
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index d00ac3a..ffa09b3 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -15,9 +15,8 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <plat/audio.h>
 
diff --git a/sound/soc/samsung/jive_wm8750.c b/sound/soc/samsung/jive_wm8750.c
index 0880252..3b53ad5 100644
--- a/sound/soc/samsung/jive_wm8750.c
+++ b/sound/soc/samsung/jive_wm8750.c
@@ -11,22 +11,11 @@
  * published by the Free Software Foundation.
 */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
 #include <asm/mach-types.h>
 
-#include "dma.h"
 #include "s3c2412-i2s.h"
-
 #include "../codecs/wm8750.h"
 
 static const struct snd_soc_dapm_route audio_map[] = {
diff --git a/sound/soc/samsung/ln2440sbc_alc650.c b/sound/soc/samsung/ln2440sbc_alc650.c
index a2bb34d..bd91c19 100644
--- a/sound/soc/samsung/ln2440sbc_alc650.c
+++ b/sound/soc/samsung/ln2440sbc_alc650.c
@@ -16,15 +16,8 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
-#include "dma.h"
-#include "ac97.h"
-
 static struct snd_soc_card ln2440sbc;
 
 static struct snd_soc_dai_link ln2440sbc_dai[] = {
diff --git a/sound/soc/samsung/neo1973_gta02_wm8753.c b/sound/soc/samsung/neo1973_gta02_wm8753.c
index 0d0ae2b..95ebf81 100644
--- a/sound/soc/samsung/neo1973_gta02_wm8753.c
+++ b/sound/soc/samsung/neo1973_gta02_wm8753.c
@@ -13,25 +13,15 @@
  *  option) any later version.
  */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
 #include <linux/gpio.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
+
 #include <sound/soc.h>
 
 #include <asm/mach-types.h>
-
 #include <plat/regs-iis.h>
-
-#include <mach/regs-clock.h>
-#include <asm/io.h>
 #include <mach/gta02.h>
+
 #include "../codecs/wm8753.h"
-#include "dma.h"
 #include "s3c24xx-i2s.h"
 
 static struct snd_soc_card neo1973_gta02;
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index 48d0b75..38aac7d 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -11,20 +11,11 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/delay.h>
 #include <linux/clk.h>
-#include <linux/kernel.h>
-#include <linux/gpio.h>
 #include <linux/io.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <plat/audio.h>
 #include <plat/dma.h>
@@ -32,6 +23,113 @@
 #include "dma.h"
 #include "pcm.h"
 
+/*Register Offsets */
+#define S3C_PCM_CTL		0x00
+#define S3C_PCM_CLKCTL		0x04
+#define S3C_PCM_TXFIFO		0x08
+#define S3C_PCM_RXFIFO		0x0C
+#define S3C_PCM_IRQCTL		0x10
+#define S3C_PCM_IRQSTAT		0x14
+#define S3C_PCM_FIFOSTAT	0x18
+#define S3C_PCM_CLRINT		0x20
+
+/* PCM_CTL Bit-Fields */
+#define S3C_PCM_CTL_TXDIPSTICK_MASK	0x3f
+#define S3C_PCM_CTL_TXDIPSTICK_SHIFT	13
+#define S3C_PCM_CTL_RXDIPSTICK_MASK	0x3f
+#define S3C_PCM_CTL_RXDIPSTICK_SHIFT	7
+#define S3C_PCM_CTL_TXDMA_EN		(0x1 << 6)
+#define S3C_PCM_CTL_RXDMA_EN		(0x1 << 5)
+#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC	(0x1 << 4)
+#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC	(0x1 << 3)
+#define S3C_PCM_CTL_TXFIFO_EN		(0x1 << 2)
+#define S3C_PCM_CTL_RXFIFO_EN		(0x1 << 1)
+#define S3C_PCM_CTL_ENABLE		(0x1 << 0)
+
+/* PCM_CLKCTL Bit-Fields */
+#define S3C_PCM_CLKCTL_SERCLK_EN	(0x1 << 19)
+#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK	(0x1 << 18)
+#define S3C_PCM_CLKCTL_SCLKDIV_MASK	0x1ff
+#define S3C_PCM_CLKCTL_SYNCDIV_MASK	0x1ff
+#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT	9
+#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT	0
+
+/* PCM_TXFIFO Bit-Fields */
+#define S3C_PCM_TXFIFO_DVALID	(0x1 << 16)
+#define S3C_PCM_TXFIFO_DATA_MSK	(0xffff << 0)
+
+/* PCM_RXFIFO Bit-Fields */
+#define S3C_PCM_RXFIFO_DVALID	(0x1 << 16)
+#define S3C_PCM_RXFIFO_DATA_MSK	(0xffff << 0)
+
+/* PCM_IRQCTL Bit-Fields */
+#define S3C_PCM_IRQCTL_IRQEN		(0x1 << 14)
+#define S3C_PCM_IRQCTL_WRDEN		(0x1 << 12)
+#define S3C_PCM_IRQCTL_TXEMPTYEN	(0x1 << 11)
+#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN	(0x1 << 10)
+#define S3C_PCM_IRQCTL_TXFULLEN		(0x1 << 9)
+#define S3C_PCM_IRQCTL_TXALMSTFULLEN	(0x1 << 8)
+#define S3C_PCM_IRQCTL_TXSTARVEN	(0x1 << 7)
+#define S3C_PCM_IRQCTL_TXERROVRFLEN	(0x1 << 6)
+#define S3C_PCM_IRQCTL_RXEMPTEN		(0x1 << 5)
+#define S3C_PCM_IRQCTL_RXALMSTEMPTEN	(0x1 << 4)
+#define S3C_PCM_IRQCTL_RXFULLEN		(0x1 << 3)
+#define S3C_PCM_IRQCTL_RXALMSTFULLEN	(0x1 << 2)
+#define S3C_PCM_IRQCTL_RXSTARVEN	(0x1 << 1)
+#define S3C_PCM_IRQCTL_RXERROVRFLEN	(0x1 << 0)
+
+/* PCM_IRQSTAT Bit-Fields */
+#define S3C_PCM_IRQSTAT_IRQPND		(0x1 << 13)
+#define S3C_PCM_IRQSTAT_WRD_XFER	(0x1 << 12)
+#define S3C_PCM_IRQSTAT_TXEMPTY		(0x1 << 11)
+#define S3C_PCM_IRQSTAT_TXALMSTEMPTY	(0x1 << 10)
+#define S3C_PCM_IRQSTAT_TXFULL		(0x1 << 9)
+#define S3C_PCM_IRQSTAT_TXALMSTFULL	(0x1 << 8)
+#define S3C_PCM_IRQSTAT_TXSTARV		(0x1 << 7)
+#define S3C_PCM_IRQSTAT_TXERROVRFL	(0x1 << 6)
+#define S3C_PCM_IRQSTAT_RXEMPT		(0x1 << 5)
+#define S3C_PCM_IRQSTAT_RXALMSTEMPT	(0x1 << 4)
+#define S3C_PCM_IRQSTAT_RXFULL		(0x1 << 3)
+#define S3C_PCM_IRQSTAT_RXALMSTFULL	(0x1 << 2)
+#define S3C_PCM_IRQSTAT_RXSTARV		(0x1 << 1)
+#define S3C_PCM_IRQSTAT_RXERROVRFL	(0x1 << 0)
+
+/* PCM_FIFOSTAT Bit-Fields */
+#define S3C_PCM_FIFOSTAT_TXCNT_MSK		(0x3f << 14)
+#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY		(0x1 << 13)
+#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY	(0x1 << 12)
+#define S3C_PCM_FIFOSTAT_TXFIFOFULL		(0x1 << 11)
+#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL	(0x1 << 10)
+#define S3C_PCM_FIFOSTAT_RXCNT_MSK		(0x3f << 4)
+#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY		(0x1 << 3)
+#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY	(0x1 << 2)
+#define S3C_PCM_FIFOSTAT_RXFIFOFULL		(0x1 << 1)
+#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL	(0x1 << 0)
+
+/**
+ * struct s3c_pcm_info - S3C PCM Controller information
+ * @dev: The parent device passed to use from the probe.
+ * @regs: The pointer to the device register block.
+ * @dma_playback: DMA information for playback channel.
+ * @dma_capture: DMA information for capture channel.
+ */
+struct s3c_pcm_info {
+	spinlock_t lock;
+	struct device	*dev;
+	void __iomem	*regs;
+
+	unsigned int sclk_per_fs;
+
+	/* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
+	unsigned int idleclk;
+
+	struct clk	*pclk;
+	struct clk	*cclk;
+
+	struct s3c_dma_params	*dma_playback;
+	struct s3c_dma_params	*dma_capture;
+};
+
 static struct s3c2410_dma_client s3c_pcm_dma_client_out = {
 	.name		= "PCM Stereo out"
 };
diff --git a/sound/soc/samsung/pcm.h b/sound/soc/samsung/pcm.h
index 03393dc..726baf8 100644
--- a/sound/soc/samsung/pcm.h
+++ b/sound/soc/samsung/pcm.h
@@ -9,116 +9,9 @@
 #ifndef __S3C_PCM_H
 #define __S3C_PCM_H __FILE__
 
-/*Register Offsets */
-#define S3C_PCM_CTL	(0x00)
-#define S3C_PCM_CLKCTL	(0x04)
-#define S3C_PCM_TXFIFO	(0x08)
-#define S3C_PCM_RXFIFO	(0x0C)
-#define S3C_PCM_IRQCTL	(0x10)
-#define S3C_PCM_IRQSTAT	(0x14)
-#define S3C_PCM_FIFOSTAT	(0x18)
-#define S3C_PCM_CLRINT	(0x20)
-
-/* PCM_CTL Bit-Fields */
-#define S3C_PCM_CTL_TXDIPSTICK_MASK		(0x3f)
-#define S3C_PCM_CTL_TXDIPSTICK_SHIFT	(13)
-#define S3C_PCM_CTL_RXDIPSTICK_MASK		(0x3f)
-#define S3C_PCM_CTL_RXDIPSTICK_SHIFT	(7)
-#define S3C_PCM_CTL_TXDMA_EN		(0x1<<6)
-#define S3C_PCM_CTL_RXDMA_EN		(0x1<<5)
-#define S3C_PCM_CTL_TXMSB_AFTER_FSYNC	(0x1<<4)
-#define S3C_PCM_CTL_RXMSB_AFTER_FSYNC	(0x1<<3)
-#define S3C_PCM_CTL_TXFIFO_EN		(0x1<<2)
-#define S3C_PCM_CTL_RXFIFO_EN		(0x1<<1)
-#define S3C_PCM_CTL_ENABLE			(0x1<<0)
-
-/* PCM_CLKCTL Bit-Fields */
-#define S3C_PCM_CLKCTL_SERCLK_EN		(0x1<<19)
-#define S3C_PCM_CLKCTL_SERCLKSEL_PCLK	(0x1<<18)
-#define S3C_PCM_CLKCTL_SCLKDIV_MASK		(0x1ff)
-#define S3C_PCM_CLKCTL_SYNCDIV_MASK		(0x1ff)
-#define S3C_PCM_CLKCTL_SCLKDIV_SHIFT	(9)
-#define S3C_PCM_CLKCTL_SYNCDIV_SHIFT	(0)
-
-/* PCM_TXFIFO Bit-Fields */
-#define S3C_PCM_TXFIFO_DVALID	(0x1<<16)
-#define S3C_PCM_TXFIFO_DATA_MSK	(0xffff<<0)
-
-/* PCM_RXFIFO Bit-Fields */
-#define S3C_PCM_RXFIFO_DVALID	(0x1<<16)
-#define S3C_PCM_RXFIFO_DATA_MSK	(0xffff<<0)
-
-/* PCM_IRQCTL Bit-Fields */
-#define S3C_PCM_IRQCTL_IRQEN		(0x1<<14)
-#define S3C_PCM_IRQCTL_WRDEN		(0x1<<12)
-#define S3C_PCM_IRQCTL_TXEMPTYEN		(0x1<<11)
-#define S3C_PCM_IRQCTL_TXALMSTEMPTYEN	(0x1<<10)
-#define S3C_PCM_IRQCTL_TXFULLEN		(0x1<<9)
-#define S3C_PCM_IRQCTL_TXALMSTFULLEN	(0x1<<8)
-#define S3C_PCM_IRQCTL_TXSTARVEN		(0x1<<7)
-#define S3C_PCM_IRQCTL_TXERROVRFLEN		(0x1<<6)
-#define S3C_PCM_IRQCTL_RXEMPTEN		(0x1<<5)
-#define S3C_PCM_IRQCTL_RXALMSTEMPTEN	(0x1<<4)
-#define S3C_PCM_IRQCTL_RXFULLEN		(0x1<<3)
-#define S3C_PCM_IRQCTL_RXALMSTFULLEN	(0x1<<2)
-#define S3C_PCM_IRQCTL_RXSTARVEN		(0x1<<1)
-#define S3C_PCM_IRQCTL_RXERROVRFLEN		(0x1<<0)
-
-/* PCM_IRQSTAT Bit-Fields */
-#define S3C_PCM_IRQSTAT_IRQPND		(0x1<<13)
-#define S3C_PCM_IRQSTAT_WRD_XFER		(0x1<<12)
-#define S3C_PCM_IRQSTAT_TXEMPTY		(0x1<<11)
-#define S3C_PCM_IRQSTAT_TXALMSTEMPTY	(0x1<<10)
-#define S3C_PCM_IRQSTAT_TXFULL		(0x1<<9)
-#define S3C_PCM_IRQSTAT_TXALMSTFULL		(0x1<<8)
-#define S3C_PCM_IRQSTAT_TXSTARV		(0x1<<7)
-#define S3C_PCM_IRQSTAT_TXERROVRFL		(0x1<<6)
-#define S3C_PCM_IRQSTAT_RXEMPT		(0x1<<5)
-#define S3C_PCM_IRQSTAT_RXALMSTEMPT		(0x1<<4)
-#define S3C_PCM_IRQSTAT_RXFULL		(0x1<<3)
-#define S3C_PCM_IRQSTAT_RXALMSTFULL		(0x1<<2)
-#define S3C_PCM_IRQSTAT_RXSTARV		(0x1<<1)
-#define S3C_PCM_IRQSTAT_RXERROVRFL		(0x1<<0)
-
-/* PCM_FIFOSTAT Bit-Fields */
-#define S3C_PCM_FIFOSTAT_TXCNT_MSK		(0x3f<<14)
-#define S3C_PCM_FIFOSTAT_TXFIFOEMPTY	(0x1<<13)
-#define S3C_PCM_FIFOSTAT_TXFIFOALMSTEMPTY	(0x1<<12)
-#define S3C_PCM_FIFOSTAT_TXFIFOFULL		(0x1<<11)
-#define S3C_PCM_FIFOSTAT_TXFIFOALMSTFULL	(0x1<<10)
-#define S3C_PCM_FIFOSTAT_RXCNT_MSK		(0x3f<<4)
-#define S3C_PCM_FIFOSTAT_RXFIFOEMPTY	(0x1<<3)
-#define S3C_PCM_FIFOSTAT_RXFIFOALMSTEMPTY	(0x1<<2)
-#define S3C_PCM_FIFOSTAT_RXFIFOFULL		(0x1<<1)
-#define S3C_PCM_FIFOSTAT_RXFIFOALMSTFULL	(0x1<<0)
-
 #define S3C_PCM_CLKSRC_PCLK	0
 #define S3C_PCM_CLKSRC_MUX	1
 
 #define S3C_PCM_SCLK_PER_FS	0
 
-/**
- * struct s3c_pcm_info - S3C PCM Controller information
- * @dev: The parent device passed to use from the probe.
- * @regs: The pointer to the device register block.
- * @dma_playback: DMA information for playback channel.
- * @dma_capture: DMA information for capture channel.
- */
-struct s3c_pcm_info {
-	spinlock_t lock;
-	struct device	*dev;
-	void __iomem	*regs;
-
-	unsigned int sclk_per_fs;
-
-	/* Whether to keep PCMSCLK enabled even when idle(no active xfer) */
-	unsigned int idleclk;
-
-	struct clk	*pclk;
-	struct clk	*cclk;
-
-	struct s3c_dma_params	*dma_playback;
-	struct s3c_dma_params	*dma_capture;
-};
-
 #endif /* __S3C_PCM_H */
diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c
index f400274..1e574a5 100644
--- a/sound/soc/samsung/rx1950_uda1380.c
+++ b/sound/soc/samsung/rx1950_uda1380.c
@@ -17,26 +17,15 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
 #include <linux/gpio.h>
-#include <linux/clk.h>
 
 #include <sound/soc.h>
-#include <sound/uda1380.h>
 #include <sound/jack.h>
 
 #include <plat/regs-iis.h>
-
-#include <mach/regs-clock.h>
-
 #include <asm/mach-types.h>
 
-#include "dma.h"
 #include "s3c24xx-i2s.h"
-#include "../codecs/uda1380.h"
 
 static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd);
 static int rx1950_startup(struct snd_pcm_substream *substream);
diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c
index 094f36e..52074a2b 100644
--- a/sound/soc/samsung/s3c-i2s-v2.c
+++ b/sound/soc/samsung/s3c-i2s-v2.c
@@ -20,9 +20,8 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <mach/dma.h>
 
diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c
index 7ea8378..841ab14 100644
--- a/sound/soc/samsung/s3c2412-i2s.c
+++ b/sound/soc/samsung/s3c2412-i2s.c
@@ -16,21 +16,13 @@
  * option) any later version.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
 #include <linux/clk.h>
-#include <linux/kernel.h>
 #include <linux/io.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
-#include <mach/hardware.h>
+#include <sound/pcm_params.h>
 
 #include <mach/regs-gpio.h>
 #include <mach/dma.h>
@@ -39,8 +31,6 @@
 #include "regs-i2s-v2.h"
 #include "s3c2412-i2s.h"
 
-#define S3C2412_I2S_DEBUG 0
-
 static struct s3c2410_dma_client s3c2412_dma_client_out = {
 	.name		= "I2S PCM Stereo out"
 };
diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c
index 13e41ed..63d8849 100644
--- a/sound/soc/samsung/s3c24xx-i2s.c
+++ b/sound/soc/samsung/s3c24xx-i2s.c
@@ -14,28 +14,16 @@
  *  option) any later version.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
-#include <linux/jiffies.h>
 #include <linux/io.h>
 #include <linux/gpio.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
-#include <mach/hardware.h>
 #include <mach/regs-gpio.h>
-#include <mach/regs-clock.h>
-
-#include <asm/dma.h>
 #include <mach/dma.h>
-
 #include <plat/regs-iis.h>
 
 #include "dma.h"
diff --git a/sound/soc/samsung/s3c24xx_simtec.c b/sound/soc/samsung/s3c24xx_simtec.c
index a434032d..349566f 100644
--- a/sound/soc/samsung/s3c24xx_simtec.c
+++ b/sound/soc/samsung/s3c24xx_simtec.c
@@ -7,20 +7,13 @@
  * published by the Free Software Foundation.
 */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
 #include <linux/gpio.h>
 #include <linux/clk.h>
-#include <linux/i2c.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
 #include <plat/audio-simtec.h>
 
-#include "dma.h"
 #include "s3c24xx-i2s.h"
 #include "s3c24xx_simtec.h"
 
diff --git a/sound/soc/samsung/s3c24xx_simtec_hermes.c b/sound/soc/samsung/s3c24xx_simtec_hermes.c
index 08fcaaa..ce6aef6 100644
--- a/sound/soc/samsung/s3c24xx_simtec_hermes.c
+++ b/sound/soc/samsung/s3c24xx_simtec_hermes.c
@@ -7,18 +7,8 @@
  * published by the Free Software Foundation.
 */
 
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
-#include <plat/audio-simtec.h>
-
-#include "dma.h"
-#include "s3c24xx-i2s.h"
 #include "s3c24xx_simtec.h"
 
 static const struct snd_soc_dapm_widget dapm_widgets[] = {
diff --git a/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
index 116e3e6..a7ef7db 100644
--- a/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
+++ b/sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
@@ -7,22 +7,10 @@
  * published by the Free Software Foundation.
 */
 
-#include <linux/module.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
-#include <plat/audio-simtec.h>
-
-#include "dma.h"
-#include "s3c24xx-i2s.h"
 #include "s3c24xx_simtec.h"
 
-#include "../codecs/tlv320aic23.h"
-
 /* supported machines:
  *
  * Machine	Connections		AMP
diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c
index 2c09e93..3cb7007 100644
--- a/sound/soc/samsung/s3c24xx_uda134x.c
+++ b/sound/soc/samsung/s3c24xx_uda134x.c
@@ -11,22 +11,15 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/module.h>
 #include <linux/clk.h>
-#include <linux/mutex.h>
 #include <linux/gpio.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
+
 #include <sound/soc.h>
 #include <sound/s3c24xx_uda134x.h>
-#include <sound/uda134x.h>
 
 #include <plat/regs-iis.h>
 
-#include "dma.h"
 #include "s3c24xx-i2s.h"
-#include "../codecs/uda134x.h"
-
 
 /* #define ENFORCE_RATES 1 */
 /*
diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c
index 61e2b52..0a2c4f2 100644
--- a/sound/soc/samsung/smartq_wm8987.c
+++ b/sound/soc/samsung/smartq_wm8987.c
@@ -13,20 +13,14 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/platform_device.h>
 #include <linux/gpio.h>
 
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
 
 #include <asm/mach-types.h>
 
-#include "dma.h"
 #include "i2s.h"
-
 #include "../codecs/wm8750.h"
 
 /*
diff --git a/sound/soc/samsung/smdk2443_wm9710.c b/sound/soc/samsung/smdk2443_wm9710.c
index 3be7e7e..3a0dbfc 100644
--- a/sound/soc/samsung/smdk2443_wm9710.c
+++ b/sound/soc/samsung/smdk2443_wm9710.c
@@ -12,15 +12,8 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
-#include "dma.h"
-#include "ac97.h"
-
 static struct snd_soc_card smdk2443;
 
 static struct snd_soc_dai_link smdk2443_dai[] = {
diff --git a/sound/soc/samsung/smdk_spdif.c b/sound/soc/samsung/smdk_spdif.c
index b5c3fad..e8ac961 100644
--- a/sound/soc/samsung/smdk_spdif.c
+++ b/sound/soc/samsung/smdk_spdif.c
@@ -10,15 +10,10 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/device.h>
 #include <linux/clk.h>
 
-#include <plat/devs.h>
-
 #include <sound/soc.h>
 
-#include "dma.h"
 #include "spdif.h"
 
 /* Audio clock settings are belonged to board specific part. Every
diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c
index b2cff1a..8aacf23 100644
--- a/sound/soc/samsung/smdk_wm8580.c
+++ b/sound/soc/samsung/smdk_wm8580.c
@@ -10,17 +10,12 @@
  *  option) any later version.
  */
 
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <asm/mach-types.h>
 
 #include "../codecs/wm8580.h"
-#include "dma.h"
 #include "i2s.h"
 
 /*
diff --git a/sound/soc/samsung/smdk_wm9713.c b/sound/soc/samsung/smdk_wm9713.c
index ae5fed6..fffe3c1 100644
--- a/sound/soc/samsung/smdk_wm9713.c
+++ b/sound/soc/samsung/smdk_wm9713.c
@@ -11,13 +11,8 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/device.h>
 #include <sound/soc.h>
 
-#include "dma.h"
-#include "ac97.h"
-
 static struct snd_soc_card smdk;
 
 /*
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
index f0816404..28c491d 100644
--- a/sound/soc/samsung/spdif.c
+++ b/sound/soc/samsung/spdif.c
@@ -13,9 +13,8 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <plat/audio.h>
 #include <mach/dma.h>
diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c
index a14820a..d6f4703 100644
--- a/sound/soc/sh/fsi-ak4642.c
+++ b/sound/soc/sh/fsi-ak4642.c
@@ -18,18 +18,26 @@
 	const char *cpu_dai;
 	const char *codec;
 	const char *platform;
+	int id;
 };
 
 static int fsi_ak4642_dai_init(struct snd_soc_pcm_runtime *rtd)
 {
-	struct snd_soc_dai *dai = rtd->codec_dai;
+	struct snd_soc_dai *codec = rtd->codec_dai;
+	struct snd_soc_dai *cpu = rtd->cpu_dai;
 	int ret;
 
-	ret = snd_soc_dai_set_fmt(dai, SND_SOC_DAIFMT_CBM_CFM);
+	ret = snd_soc_dai_set_fmt(codec, SND_SOC_DAIFMT_LEFT_J |
+					 SND_SOC_DAIFMT_CBM_CFM);
 	if (ret < 0)
 		return ret;
 
-	ret = snd_soc_dai_set_sysclk(dai, 0, 11289600, 0);
+	ret = snd_soc_dai_set_sysclk(codec, 0, 11289600, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_LEFT_J |
+				       SND_SOC_DAIFMT_CBS_CFS);
 
 	return ret;
 }
@@ -60,7 +68,7 @@
 
 	pdata = (struct fsi_ak4642_data *)id_entry->driver_data;
 
-	fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_A);
+	fsi_snd_device = platform_device_alloc("soc-audio", pdata->id);
 	if (!fsi_snd_device)
 		goto out;
 
@@ -93,6 +101,7 @@
 	.cpu_dai	= "fsia-dai",
 	.codec		= "ak4642-codec.0-0012",
 	.platform	= "sh_fsi.0",
+	.id		= FSI_PORT_A,
 };
 
 static struct fsi_ak4642_data fsi_b_ak4642 = {
@@ -101,6 +110,7 @@
 	.cpu_dai	= "fsib-dai",
 	.codec		= "ak4642-codec.0-0012",
 	.platform	= "sh_fsi.0",
+	.id		= FSI_PORT_B,
 };
 
 static struct fsi_ak4642_data fsi_a_ak4643 = {
@@ -109,6 +119,7 @@
 	.cpu_dai	= "fsia-dai",
 	.codec		= "ak4642-codec.0-0013",
 	.platform	= "sh_fsi.0",
+	.id		= FSI_PORT_A,
 };
 
 static struct fsi_ak4642_data fsi_b_ak4643 = {
@@ -117,6 +128,7 @@
 	.cpu_dai	= "fsib-dai",
 	.codec		= "ak4642-codec.0-0013",
 	.platform	= "sh_fsi.0",
+	.id		= FSI_PORT_B,
 };
 
 static struct fsi_ak4642_data fsi2_a_ak4642 = {
@@ -125,6 +137,7 @@
 	.cpu_dai	= "fsia-dai",
 	.codec		= "ak4642-codec.0-0012",
 	.platform	= "sh_fsi2",
+	.id		= FSI_PORT_A,
 };
 
 static struct fsi_ak4642_data fsi2_b_ak4642 = {
@@ -133,6 +146,7 @@
 	.cpu_dai	= "fsib-dai",
 	.codec		= "ak4642-codec.0-0012",
 	.platform	= "sh_fsi2",
+	.id		= FSI_PORT_B,
 };
 
 static struct fsi_ak4642_data fsi2_a_ak4643 = {
@@ -141,6 +155,7 @@
 	.cpu_dai	= "fsia-dai",
 	.codec		= "ak4642-codec.0-0013",
 	.platform	= "sh_fsi2",
+	.id		= FSI_PORT_A,
 };
 
 static struct fsi_ak4642_data fsi2_b_ak4643 = {
@@ -149,6 +164,7 @@
 	.cpu_dai	= "fsib-dai",
 	.codec		= "ak4642-codec.0-0013",
 	.platform	= "sh_fsi2",
+	.id		= FSI_PORT_B,
 };
 
 static struct platform_device_id fsi_id_table[] = {
diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c
index e8df9da..dbafd7a 100644
--- a/sound/soc/sh/fsi-da7210.c
+++ b/sound/soc/sh/fsi-da7210.c
@@ -15,11 +15,20 @@
 
 static int fsi_da7210_init(struct snd_soc_pcm_runtime *rtd)
 {
-	struct snd_soc_dai *dai = rtd->codec_dai;
+	struct snd_soc_dai *codec = rtd->codec_dai;
+	struct snd_soc_dai *cpu = rtd->cpu_dai;
+	int ret;
 
-	return snd_soc_dai_set_fmt(dai,
+	ret = snd_soc_dai_set_fmt(codec,
 				   SND_SOC_DAIFMT_I2S |
 				   SND_SOC_DAIFMT_CBM_CFM);
+	if (ret < 0)
+		return ret;
+
+	ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_I2S |
+				       SND_SOC_DAIFMT_CBS_CFS);
+
+	return ret;
 }
 
 static struct snd_soc_dai_link fsi_da7210_dai = {
diff --git a/sound/soc/sh/fsi-hdmi.c b/sound/soc/sh/fsi-hdmi.c
index a52dd8e..9719985 100644
--- a/sound/soc/sh/fsi-hdmi.c
+++ b/sound/soc/sh/fsi-hdmi.c
@@ -12,31 +12,59 @@
 #include <linux/platform_device.h>
 #include <sound/sh_fsi.h>
 
+struct fsi_hdmi_data {
+	const char *cpu_dai;
+	const char *card;
+	int id;
+};
+
+static int fsi_hdmi_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *cpu = rtd->cpu_dai;
+	int ret;
+
+	ret = snd_soc_dai_set_fmt(cpu, SND_SOC_DAIFMT_CBM_CFM);
+
+	return ret;
+}
+
 static struct snd_soc_dai_link fsi_dai_link = {
 	.name		= "HDMI",
 	.stream_name	= "HDMI",
-	.cpu_dai_name	= "fsib-dai", /* fsi B */
 	.codec_dai_name	= "sh_mobile_hdmi-hifi",
 	.platform_name	= "sh_fsi2",
 	.codec_name	= "sh-mobile-hdmi",
+	.init		= fsi_hdmi_dai_init,
 };
 
 static struct snd_soc_card fsi_soc_card  = {
-	.name		= "FSI (SH MOBILE HDMI)",
 	.dai_link	= &fsi_dai_link,
 	.num_links	= 1,
 };
 
 static struct platform_device *fsi_snd_device;
 
-static int __init fsi_hdmi_init(void)
+static int fsi_hdmi_probe(struct platform_device *pdev)
 {
 	int ret = -ENOMEM;
+	const struct platform_device_id	*id_entry;
+	struct fsi_hdmi_data *pdata;
 
-	fsi_snd_device = platform_device_alloc("soc-audio", FSI_PORT_B);
+	id_entry = pdev->id_entry;
+	if (!id_entry) {
+		dev_err(&pdev->dev, "unknown fsi hdmi\n");
+		return -ENODEV;
+	}
+
+	pdata = (struct fsi_hdmi_data *)id_entry->driver_data;
+
+	fsi_snd_device = platform_device_alloc("soc-audio", pdata->id);
 	if (!fsi_snd_device)
 		goto out;
 
+	fsi_dai_link.cpu_dai_name	= pdata->cpu_dai;
+	fsi_soc_card.name		= pdata->card;
+
 	platform_set_drvdata(fsi_snd_device, &fsi_soc_card);
 	ret = platform_device_add(fsi_snd_device);
 
@@ -47,9 +75,48 @@
 	return ret;
 }
 
-static void __exit fsi_hdmi_exit(void)
+static int fsi_hdmi_remove(struct platform_device *pdev)
 {
 	platform_device_unregister(fsi_snd_device);
+	return 0;
+}
+
+static struct fsi_hdmi_data fsi2_a_hdmi = {
+	.cpu_dai	= "fsia-dai",
+	.card		= "FSI2A (SH MOBILE HDMI)",
+	.id		= FSI_PORT_A,
+};
+
+static struct fsi_hdmi_data fsi2_b_hdmi = {
+	.cpu_dai	= "fsib-dai",
+	.card		= "FSI2B (SH MOBILE HDMI)",
+	.id		= FSI_PORT_B,
+};
+
+static struct platform_device_id fsi_id_table[] = {
+	/* FSI 2 */
+	{ "sh_fsi2_a_hdmi",	(kernel_ulong_t)&fsi2_a_hdmi },
+	{ "sh_fsi2_b_hdmi",	(kernel_ulong_t)&fsi2_b_hdmi },
+	{},
+};
+
+static struct platform_driver fsi_hdmi = {
+	.driver = {
+		.name	= "fsi-hdmi-audio",
+	},
+	.probe		= fsi_hdmi_probe,
+	.remove		= fsi_hdmi_remove,
+	.id_table	= fsi_id_table,
+};
+
+static int __init fsi_hdmi_init(void)
+{
+	return platform_driver_register(&fsi_hdmi);
+}
+
+static void __exit fsi_hdmi_exit(void)
+{
+	platform_driver_unregister(&fsi_hdmi);
 }
 
 module_init(fsi_hdmi_init);
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 2b06402..0c9997e 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -78,6 +78,8 @@
 /* CKG1 */
 #define ACKMD_MASK	0x00007000
 #define BPFMD_MASK	0x00000700
+#define DIMD		(1 << 4)
+#define DOMD		(1 << 0)
 
 /* A/B MST_CTLR */
 #define BP	(1 << 4)	/* Fix the signal of Biphase output */
@@ -111,6 +113,8 @@
 
 #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 
+typedef int (*set_rate_func)(struct device *dev, int is_porta, int rate, int enable);
+
 /*
  * FSI driver use below type name for variable
  *
@@ -128,7 +132,6 @@
 	struct snd_pcm_substream *substream;
 
 	int fifo_max_num;
-	int chan_num;
 
 	int buff_offset;
 	int buff_len;
@@ -143,6 +146,7 @@
 	void __iomem *base;
 	struct fsi_master *master;
 
+	int chan_num;
 	struct fsi_stream playback;
 	struct fsi_stream capture;
 
@@ -252,9 +256,8 @@
 	return  rtd->cpu_dai;
 }
 
-static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
+static struct fsi_priv *fsi_get_priv_frm_dai(struct snd_soc_dai *dai)
 {
-	struct snd_soc_dai *dai = fsi_get_dai(substream);
 	struct fsi_master *master = snd_soc_dai_get_drvdata(dai);
 
 	if (dai->id == 0)
@@ -263,11 +266,27 @@
 		return &master->fsib;
 }
 
+static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream)
+{
+	return fsi_get_priv_frm_dai(fsi_get_dai(substream));
+}
+
+static set_rate_func fsi_get_info_set_rate(struct fsi_master *master)
+{
+	if (!master->info)
+		return NULL;
+
+	return master->info->set_rate;
+}
+
 static u32 fsi_get_info_flags(struct fsi_priv *fsi)
 {
 	int is_porta = fsi_is_port_a(fsi);
 	struct fsi_master *master = fsi_get_master(fsi);
 
+	if (!master->info)
+		return 0;
+
 	return is_porta ? master->info->porta_flags :
 		master->info->portb_flags;
 }
@@ -288,21 +307,6 @@
 	return is_play ? &fsi->playback : &fsi->capture;
 }
 
-static int fsi_is_master_mode(struct fsi_priv *fsi, int is_play)
-{
-	u32 mode;
-	u32 flags = fsi_get_info_flags(fsi);
-
-	mode = is_play ? SH_FSI_OUT_SLAVE_MODE : SH_FSI_IN_SLAVE_MODE;
-
-	/* return
-	 * 1 : master mode
-	 * 0 : slave mode
-	 */
-
-	return (mode & flags) != mode;
-}
-
 static u32 fsi_get_port_shift(struct fsi_priv *fsi, int is_play)
 {
 	int is_porta = fsi_is_port_a(fsi);
@@ -357,7 +361,6 @@
 static int fsi_get_fifo_data_num(struct fsi_priv *fsi, int is_play)
 {
 	u32 status;
-	struct fsi_stream *io = fsi_get_stream(fsi, is_play);
 	int data_num;
 
 	status = is_play ?
@@ -365,7 +368,7 @@
 		fsi_reg_read(fsi, DIFF_ST);
 
 	data_num = 0x1ff & (status >> 8);
-	data_num *= io->chan_num;
+	data_num *= fsi->chan_num;
 
 	return data_num;
 }
@@ -387,7 +390,7 @@
 	struct snd_pcm_substream *substream = io->substream;
 	struct snd_pcm_runtime *runtime = substream->runtime;
 
-	return frames_to_bytes(runtime, 1) / io->chan_num;
+	return frames_to_bytes(runtime, 1) / fsi->chan_num;
 }
 
 static void fsi_count_fifo_err(struct fsi_priv *fsi)
@@ -580,10 +583,10 @@
 	 * 7 channels:  32 ( 32 x 7 = 224)
 	 * 8 channels:  32 ( 32 x 8 = 256)
 	 */
-	for (i = 1; i < io->chan_num; i <<= 1)
+	for (i = 1; i < fsi->chan_num; i <<= 1)
 		io->fifo_max_num >>= 1;
 	dev_dbg(dai->dev, "%d channel %d store\n",
-		io->chan_num, io->fifo_max_num);
+		fsi->chan_num, io->fifo_max_num);
 
 	/*
 	 * set interrupt generation factor
@@ -659,7 +662,7 @@
 		 * data_num_max	: number of FSI fifo free space
 		 * data_num	: number of ALSA residue data
 		 */
-		data_num_max  = io->fifo_max_num * io->chan_num;
+		data_num_max  = io->fifo_max_num * fsi->chan_num;
 		data_num_max -= fsi_get_fifo_data_num(fsi, is_play);
 
 		data_num = data_residue_num;
@@ -754,25 +757,12 @@
 			   struct snd_soc_dai *dai)
 {
 	struct fsi_priv *fsi = fsi_get_priv(substream);
-	struct fsi_master *master = fsi_get_master(fsi);
-	struct fsi_stream *io;
 	u32 flags = fsi_get_info_flags(fsi);
-	u32 fmt;
 	u32 data;
 	int is_play = fsi_is_play(substream);
-	int is_master;
-
-	io = fsi_get_stream(fsi, is_play);
 
 	pm_runtime_get_sync(dai->dev);
 
-	/* CKG1 */
-	data = is_play ? (1 << 0) : (1 << 4);
-	is_master = fsi_is_master_mode(fsi, is_play);
-	if (is_master)
-		fsi_reg_mask_set(fsi, CKG1, data, data);
-	else
-		fsi_reg_mask_set(fsi, CKG1, data, 0);
 
 	/* clock inversion (CKG2) */
 	data = 0;
@@ -787,54 +777,6 @@
 
 	fsi_reg_write(fsi, CKG2, data);
 
-	/* do fmt, di fmt */
-	data = 0;
-	fmt = is_play ? SH_FSI_GET_OFMT(flags) : SH_FSI_GET_IFMT(flags);
-	switch (fmt) {
-	case SH_FSI_FMT_MONO:
-		data = CR_MONO;
-		io->chan_num = 1;
-		break;
-	case SH_FSI_FMT_MONO_DELAY:
-		data = CR_MONO_D;
-		io->chan_num = 1;
-		break;
-	case SH_FSI_FMT_PCM:
-		data = CR_PCM;
-		io->chan_num = 2;
-		break;
-	case SH_FSI_FMT_I2S:
-		data = CR_I2S;
-		io->chan_num = 2;
-		break;
-	case SH_FSI_FMT_TDM:
-		io->chan_num = is_play ?
-			SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
-		data = CR_TDM | (io->chan_num - 1);
-		break;
-	case SH_FSI_FMT_TDM_DELAY:
-		io->chan_num = is_play ?
-			SH_FSI_GET_CH_O(flags) : SH_FSI_GET_CH_I(flags);
-		data = CR_TDM_D | (io->chan_num - 1);
-		break;
-	case SH_FSI_FMT_SPDIF:
-		if (master->core->ver < 2) {
-			dev_err(dai->dev, "This FSI can not use SPDIF\n");
-			return -EINVAL;
-		}
-		data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM;
-		io->chan_num = 2;
-		fsi_spdif_clk_ctrl(fsi, 1);
-		fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
-		break;
-	default:
-		dev_err(dai->dev, "unknown format.\n");
-		return -EINVAL;
-	}
-	is_play ?
-		fsi_reg_write(fsi, DO_FMT, data) :
-		fsi_reg_write(fsi, DI_FMT, data);
-
 	/* irq clear */
 	fsi_irq_disable(fsi, is_play);
 	fsi_irq_clear_status(fsi);
@@ -851,12 +793,12 @@
 	struct fsi_priv *fsi = fsi_get_priv(substream);
 	int is_play = fsi_is_play(substream);
 	struct fsi_master *master = fsi_get_master(fsi);
-	int (*set_rate)(struct device *dev, int is_porta, int rate, int enable);
+	set_rate_func set_rate;
 
 	fsi_irq_disable(fsi, is_play);
 	fsi_clk_ctrl(fsi, 0);
 
-	set_rate = master->info->set_rate;
+	set_rate = fsi_get_info_set_rate(master);
 	if (set_rate && fsi->rate)
 		set_rate(dai->dev, fsi_is_port_a(fsi), fsi->rate, 0);
 	fsi->rate = 0;
@@ -889,18 +831,100 @@
 	return ret;
 }
 
+static int fsi_set_fmt_dai(struct fsi_priv *fsi, unsigned int fmt)
+{
+	u32 data = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		data = CR_I2S;
+		fsi->chan_num = 2;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		data = CR_PCM;
+		fsi->chan_num = 2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	fsi_reg_write(fsi, DO_FMT, data);
+	fsi_reg_write(fsi, DI_FMT, data);
+
+	return 0;
+}
+
+static int fsi_set_fmt_spdif(struct fsi_priv *fsi)
+{
+	struct fsi_master *master = fsi_get_master(fsi);
+	u32 data = 0;
+
+	if (master->core->ver < 2)
+		return -EINVAL;
+
+	data = CR_BWS_16 | CR_DTMD_SPDIF_PCM | CR_PCM;
+	fsi->chan_num = 2;
+	fsi_spdif_clk_ctrl(fsi, 1);
+	fsi_reg_mask_set(fsi, OUT_SEL, DMMD, DMMD);
+
+	fsi_reg_write(fsi, DO_FMT, data);
+	fsi_reg_write(fsi, DI_FMT, data);
+
+	return 0;
+}
+
+static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai);
+	u32 flags = fsi_get_info_flags(fsi);
+	u32 data = 0;
+	int ret;
+
+	pm_runtime_get_sync(dai->dev);
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		data = DIMD | DOMD;
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	default:
+		ret = -EINVAL;
+		goto set_fmt_exit;
+	}
+	fsi_reg_mask_set(fsi, CKG1, (DIMD | DOMD), data);
+
+	/* set format */
+	switch (flags & SH_FSI_FMT_MASK) {
+	case SH_FSI_FMT_DAI:
+		ret = fsi_set_fmt_dai(fsi, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+		break;
+	case SH_FSI_FMT_SPDIF:
+		ret = fsi_set_fmt_spdif(fsi);
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+set_fmt_exit:
+	pm_runtime_put_sync(dai->dev);
+
+	return ret;
+}
+
 static int fsi_dai_hw_params(struct snd_pcm_substream *substream,
 			     struct snd_pcm_hw_params *params,
 			     struct snd_soc_dai *dai)
 {
 	struct fsi_priv *fsi = fsi_get_priv(substream);
 	struct fsi_master *master = fsi_get_master(fsi);
-	int (*set_rate)(struct device *dev, int is_porta, int rate, int enable);
+	set_rate_func set_rate;
 	int fsi_ver = master->core->ver;
 	long rate = params_rate(params);
 	int ret;
 
-	set_rate = master->info->set_rate;
+	set_rate = fsi_get_info_set_rate(master);
 	if (!set_rate)
 		return 0;
 
@@ -975,6 +999,7 @@
 	.startup	= fsi_dai_startup,
 	.shutdown	= fsi_dai_shutdown,
 	.trigger	= fsi_dai_trigger,
+	.set_fmt	= fsi_dai_set_fmt,
 	.hw_params	= fsi_dai_hw_params,
 };
 
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index 8c2a21a..db66dc4 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -18,6 +18,8 @@
 #include <linux/bitmap.h>
 #include <linux/rbtree.h>
 
+#include <trace/events/asoc.h>
+
 static unsigned int snd_soc_4_12_read(struct snd_soc_codec *codec,
 				     unsigned int reg)
 {
@@ -25,7 +27,8 @@
 	unsigned int val;
 
 	if (reg >= codec->driver->reg_cache_size ||
-		snd_soc_codec_volatile_register(codec, reg)) {
+		snd_soc_codec_volatile_register(codec, reg) ||
+		codec->cache_bypass) {
 			if (codec->cache_only)
 				return -1;
 
@@ -49,7 +52,8 @@
 	data[1] = value & 0x00ff;
 
 	if (!snd_soc_codec_volatile_register(codec, reg) &&
-		reg < codec->driver->reg_cache_size) {
+		reg < codec->driver->reg_cache_size &&
+		!codec->cache_bypass) {
 		ret = snd_soc_cache_write(codec, reg, value);
 		if (ret < 0)
 			return -1;
@@ -106,7 +110,8 @@
 	unsigned int val;
 
 	if (reg >= codec->driver->reg_cache_size ||
-		snd_soc_codec_volatile_register(codec, reg)) {
+		snd_soc_codec_volatile_register(codec, reg) ||
+		codec->cache_bypass) {
 			if (codec->cache_only)
 				return -1;
 
@@ -130,7 +135,8 @@
 	data[1] = value & 0x00ff;
 
 	if (!snd_soc_codec_volatile_register(codec, reg) &&
-		reg < codec->driver->reg_cache_size) {
+		reg < codec->driver->reg_cache_size &&
+		!codec->cache_bypass) {
 		ret = snd_soc_cache_write(codec, reg, value);
 		if (ret < 0)
 			return -1;
@@ -191,7 +197,8 @@
 	data[1] = value & 0xff;
 
 	if (!snd_soc_codec_volatile_register(codec, reg) &&
-		reg < codec->driver->reg_cache_size) {
+		reg < codec->driver->reg_cache_size &&
+		!codec->cache_bypass) {
 		ret = snd_soc_cache_write(codec, reg, value);
 		if (ret < 0)
 			return -1;
@@ -216,7 +223,8 @@
 
 	reg &= 0xff;
 	if (reg >= codec->driver->reg_cache_size ||
-		snd_soc_codec_volatile_register(codec, reg)) {
+		snd_soc_codec_volatile_register(codec, reg) ||
+		codec->cache_bypass) {
 			if (codec->cache_only)
 				return -1;
 
@@ -271,7 +279,8 @@
 	data[2] = value & 0xff;
 
 	if (!snd_soc_codec_volatile_register(codec, reg) &&
-		reg < codec->driver->reg_cache_size) {
+		reg < codec->driver->reg_cache_size &&
+		!codec->cache_bypass) {
 		ret = snd_soc_cache_write(codec, reg, value);
 		if (ret < 0)
 			return -1;
@@ -295,7 +304,8 @@
 	unsigned int val;
 
 	if (reg >= codec->driver->reg_cache_size ||
-	    snd_soc_codec_volatile_register(codec, reg)) {
+	    snd_soc_codec_volatile_register(codec, reg) ||
+	    codec->cache_bypass) {
 		if (codec->cache_only)
 			return -1;
 
@@ -450,7 +460,8 @@
 
 	reg &= 0xff;
 	if (reg >= codec->driver->reg_cache_size ||
-		snd_soc_codec_volatile_register(codec, reg)) {
+		snd_soc_codec_volatile_register(codec, reg) ||
+		codec->cache_bypass) {
 			if (codec->cache_only)
 				return -1;
 
@@ -476,7 +487,8 @@
 
 	reg &= 0xff;
 	if (!snd_soc_codec_volatile_register(codec, reg) &&
-		reg < codec->driver->reg_cache_size) {
+		reg < codec->driver->reg_cache_size &&
+		!codec->cache_bypass) {
 		ret = snd_soc_cache_write(codec, reg, value);
 		if (ret < 0)
 			return -1;
@@ -568,7 +580,8 @@
 	unsigned int val;
 
 	if (reg >= codec->driver->reg_cache_size ||
-	    snd_soc_codec_volatile_register(codec, reg)) {
+	    snd_soc_codec_volatile_register(codec, reg) ||
+	    codec->cache_bypass) {
 		if (codec->cache_only)
 			return -1;
 
@@ -595,7 +608,8 @@
 	data[3] = value & 0xff;
 
 	if (!snd_soc_codec_volatile_register(codec, reg) &&
-		reg < codec->driver->reg_cache_size) {
+		reg < codec->driver->reg_cache_size &&
+		!codec->cache_bypass) {
 		ret = snd_soc_cache_write(codec, reg, value);
 		if (ret < 0)
 			return -1;
@@ -761,6 +775,49 @@
 }
 EXPORT_SYMBOL_GPL(snd_soc_codec_set_cache_io);
 
+static bool snd_soc_set_cache_val(void *base, unsigned int idx,
+				  unsigned int val, unsigned int word_size)
+{
+	switch (word_size) {
+	case 1: {
+		u8 *cache = base;
+		if (cache[idx] == val)
+			return true;
+		cache[idx] = val;
+		break;
+	}
+	case 2: {
+		u16 *cache = base;
+		if (cache[idx] == val)
+			return true;
+		cache[idx] = val;
+		break;
+	}
+	default:
+		BUG();
+	}
+	return false;
+}
+
+static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
+		unsigned int word_size)
+{
+	switch (word_size) {
+	case 1: {
+		const u8 *cache = base;
+		return cache[idx];
+	}
+	case 2: {
+		const u16 *cache = base;
+		return cache[idx];
+	}
+	default:
+		BUG();
+	}
+	/* unreachable */
+	return -1;
+}
+
 struct snd_soc_rbtree_node {
 	struct rb_node node;
 	unsigned int reg;
@@ -835,7 +892,9 @@
 		ret = snd_soc_cache_read(codec, rbnode->reg, &val);
 		if (ret)
 			return ret;
+		codec->cache_bypass = 1;
 		ret = snd_soc_write(codec, rbnode->reg, val);
+		codec->cache_bypass = 0;
 		if (ret)
 			return ret;
 		dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
@@ -924,7 +983,12 @@
 
 static int snd_soc_rbtree_cache_init(struct snd_soc_codec *codec)
 {
+	struct snd_soc_rbtree_node *rbtree_node;
 	struct snd_soc_rbtree_ctx *rbtree_ctx;
+	unsigned int val;
+	unsigned int word_size;
+	int i;
+	int ret;
 
 	codec->reg_cache = kmalloc(sizeof *rbtree_ctx, GFP_KERNEL);
 	if (!codec->reg_cache)
@@ -936,53 +1000,25 @@
 	if (!codec->reg_def_copy)
 		return 0;
 
-/*
- * populate the rbtree with the initialized registers.  All other
- * registers will be inserted into the tree when they are first written.
- *
- * The reasoning behind this, is that we need to step through and
- * dereference the cache in u8/u16 increments without sacrificing
- * portability.  This could also be done using memcpy() but that would
- * be slightly more cryptic.
- */
-#define snd_soc_rbtree_populate(cache)					\
-({									\
-	int ret, i;							\
-	struct snd_soc_rbtree_node *rbtree_node;			\
-									\
-	ret = 0;							\
-	cache = codec->reg_def_copy;					\
-	for (i = 0; i < codec->driver->reg_cache_size; ++i) {		\
-		if (!cache[i])						\
-			continue;					\
-		rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL);	\
-		if (!rbtree_node) {					\
-			ret = -ENOMEM;					\
-			snd_soc_cache_exit(codec);			\
-			break;						\
-		}							\
-		rbtree_node->reg = i;					\
-		rbtree_node->value = cache[i];				\
-		rbtree_node->defval = cache[i];				\
-		snd_soc_rbtree_insert(&rbtree_ctx->root,		\
-				      rbtree_node);			\
-	}								\
-	ret;								\
-})
-
-	switch (codec->driver->reg_word_size) {
-	case 1: {
-		const u8 *cache;
-
-		return snd_soc_rbtree_populate(cache);
-	}
-	case 2: {
-		const u16 *cache;
-
-		return snd_soc_rbtree_populate(cache);
-	}
-	default:
-		BUG();
+	/*
+	 * populate the rbtree with the initialized registers.  All other
+	 * registers will be inserted when they are first modified.
+	 */
+	word_size = codec->driver->reg_word_size;
+	for (i = 0; i < codec->driver->reg_cache_size; ++i) {
+		val = snd_soc_get_cache_val(codec->reg_def_copy, i, word_size);
+		if (!val)
+			continue;
+		rbtree_node = kzalloc(sizeof *rbtree_node, GFP_KERNEL);
+		if (!rbtree_node) {
+			ret = -ENOMEM;
+			snd_soc_cache_exit(codec);
+			break;
+		}
+		rbtree_node->reg = i;
+		rbtree_node->value = val;
+		rbtree_node->defval = val;
+		snd_soc_rbtree_insert(&rbtree_ctx->root, rbtree_node);
 	}
 
 	return 0;
@@ -1080,34 +1116,28 @@
 		unsigned int reg)
 {
 	const struct snd_soc_codec_driver *codec_drv;
-	size_t reg_size;
 
 	codec_drv = codec->driver;
-	reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
 	return (reg * codec_drv->reg_word_size) /
-	       DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count());
+	       DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count());
 }
 
 static inline int snd_soc_lzo_get_blkpos(struct snd_soc_codec *codec,
 		unsigned int reg)
 {
 	const struct snd_soc_codec_driver *codec_drv;
-	size_t reg_size;
 
 	codec_drv = codec->driver;
-	reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
-	return reg % (DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count()) /
+	return reg % (DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count()) /
 		      codec_drv->reg_word_size);
 }
 
 static inline int snd_soc_lzo_get_blksize(struct snd_soc_codec *codec)
 {
 	const struct snd_soc_codec_driver *codec_drv;
-	size_t reg_size;
 
 	codec_drv = codec->driver;
-	reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
-	return DIV_ROUND_UP(reg_size, snd_soc_lzo_block_count());
+	return DIV_ROUND_UP(codec->reg_size, snd_soc_lzo_block_count());
 }
 
 static int snd_soc_lzo_cache_sync(struct snd_soc_codec *codec)
@@ -1122,7 +1152,9 @@
 		ret = snd_soc_cache_read(codec, i, &val);
 		if (ret)
 			return ret;
+		codec->cache_bypass = 1;
 		ret = snd_soc_write(codec, i, val);
+		codec->cache_bypass = 0;
 		if (ret)
 			return ret;
 		dev_dbg(codec->dev, "Synced register %#x, value = %#x\n",
@@ -1165,29 +1197,10 @@
 	}
 
 	/* write the new value to the cache */
-	switch (codec->driver->reg_word_size) {
-	case 1: {
-		u8 *cache;
-		cache = lzo_block->dst;
-		if (cache[blkpos] == value) {
-			kfree(lzo_block->dst);
-			goto out;
-		}
-		cache[blkpos] = value;
-	}
-	break;
-	case 2: {
-		u16 *cache;
-		cache = lzo_block->dst;
-		if (cache[blkpos] == value) {
-			kfree(lzo_block->dst);
-			goto out;
-		}
-		cache[blkpos] = value;
-	}
-	break;
-	default:
-		BUG();
+	if (snd_soc_set_cache_val(lzo_block->dst, blkpos, value,
+				  codec->driver->reg_word_size)) {
+		kfree(lzo_block->dst);
+		goto out;
 	}
 
 	/* prepare the source to be the decompressed block */
@@ -1241,25 +1254,10 @@
 
 	/* decompress the block */
 	ret = snd_soc_lzo_decompress_cache_block(codec, lzo_block);
-	if (ret >= 0) {
+	if (ret >= 0)
 		/* fetch the value from the cache */
-		switch (codec->driver->reg_word_size) {
-		case 1: {
-			u8 *cache;
-			cache = lzo_block->dst;
-			*value = cache[blkpos];
-		}
-		break;
-		case 2: {
-			u16 *cache;
-			cache = lzo_block->dst;
-			*value = cache[blkpos];
-		}
-		break;
-		default:
-			BUG();
-		}
-	}
+		*value = snd_soc_get_cache_val(lzo_block->dst, blkpos,
+					       codec->driver->reg_word_size);
 
 	kfree(lzo_block->dst);
 	/* restore the pointer and length of the compressed block */
@@ -1301,7 +1299,7 @@
 static int snd_soc_lzo_cache_init(struct snd_soc_codec *codec)
 {
 	struct snd_soc_lzo_ctx **lzo_blocks;
-	size_t reg_size, bmp_size;
+	size_t bmp_size;
 	const struct snd_soc_codec_driver *codec_drv;
 	int ret, tofree, i, blksize, blkcount;
 	const char *p, *end;
@@ -1309,7 +1307,6 @@
 
 	ret = 0;
 	codec_drv = codec->driver;
-	reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
 
 	/*
 	 * If we have not been given a default register cache
@@ -1321,8 +1318,7 @@
 		tofree = 1;
 
 	if (!codec->reg_def_copy) {
-		codec->reg_def_copy = kzalloc(reg_size,
-						       GFP_KERNEL);
+		codec->reg_def_copy = kzalloc(codec->reg_size, GFP_KERNEL);
 		if (!codec->reg_def_copy)
 			return -ENOMEM;
 	}
@@ -1370,7 +1366,7 @@
 
 	blksize = snd_soc_lzo_get_blksize(codec);
 	p = codec->reg_def_copy;
-	end = codec->reg_def_copy + reg_size;
+	end = codec->reg_def_copy + codec->reg_size;
 	/* compress the register map and fill the lzo blocks */
 	for (i = 0; i < blkcount; ++i, p += blksize) {
 		lzo_blocks[i]->src = p;
@@ -1414,28 +1410,10 @@
 		ret = snd_soc_cache_read(codec, i, &val);
 		if (ret)
 			return ret;
-		if (codec_drv->reg_cache_default) {
-			switch (codec_drv->reg_word_size) {
-			case 1: {
-				const u8 *cache;
-
-				cache = codec_drv->reg_cache_default;
-				if (cache[i] == val)
-					continue;
-			}
-			break;
-			case 2: {
-				const u16 *cache;
-
-				cache = codec_drv->reg_cache_default;
-				if (cache[i] == val)
-					continue;
-			}
-			break;
-			default:
-				BUG();
-			}
-		}
+		if (codec->reg_def_copy)
+			if (snd_soc_get_cache_val(codec->reg_def_copy,
+						  i, codec_drv->reg_word_size) == val)
+				continue;
 		ret = snd_soc_write(codec, i, val);
 		if (ret)
 			return ret;
@@ -1448,50 +1426,16 @@
 static int snd_soc_flat_cache_write(struct snd_soc_codec *codec,
 				    unsigned int reg, unsigned int value)
 {
-	switch (codec->driver->reg_word_size) {
-	case 1: {
-		u8 *cache;
-
-		cache = codec->reg_cache;
-		cache[reg] = value;
-	}
-	break;
-	case 2: {
-		u16 *cache;
-
-		cache = codec->reg_cache;
-		cache[reg] = value;
-	}
-	break;
-	default:
-		BUG();
-	}
-
+	snd_soc_set_cache_val(codec->reg_cache, reg, value,
+			      codec->driver->reg_word_size);
 	return 0;
 }
 
 static int snd_soc_flat_cache_read(struct snd_soc_codec *codec,
 				   unsigned int reg, unsigned int *value)
 {
-	switch (codec->driver->reg_word_size) {
-	case 1: {
-		u8 *cache;
-
-		cache = codec->reg_cache;
-		*value = cache[reg];
-	}
-	break;
-	case 2: {
-		u16 *cache;
-
-		cache = codec->reg_cache;
-		*value = cache[reg];
-	}
-	break;
-	default:
-		BUG();
-	}
-
+	*value = snd_soc_get_cache_val(codec->reg_cache, reg,
+				       codec->driver->reg_word_size);
 	return 0;
 }
 
@@ -1507,24 +1451,14 @@
 static int snd_soc_flat_cache_init(struct snd_soc_codec *codec)
 {
 	const struct snd_soc_codec_driver *codec_drv;
-	size_t reg_size;
 
 	codec_drv = codec->driver;
-	reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
 
-	/*
-	 * for flat compression, we don't need to keep a copy of the
-	 * original defaults register cache as it will definitely not
-	 * be marked as __devinitconst
-	 */
-	kfree(codec->reg_def_copy);
-	codec->reg_def_copy = NULL;
-
-	if (codec_drv->reg_cache_default)
-		codec->reg_cache = kmemdup(codec_drv->reg_cache_default,
-					   reg_size, GFP_KERNEL);
+	if (codec->reg_def_copy)
+		codec->reg_cache = kmemdup(codec->reg_def_copy,
+					   codec->reg_size, GFP_KERNEL);
 	else
-		codec->reg_cache = kzalloc(reg_size, GFP_KERNEL);
+		codec->reg_cache = kzalloc(codec->reg_size, GFP_KERNEL);
 	if (!codec->reg_cache)
 		return -ENOMEM;
 
@@ -1669,21 +1603,78 @@
 int snd_soc_cache_sync(struct snd_soc_codec *codec)
 {
 	int ret;
+	const char *name;
 
 	if (!codec->cache_sync) {
 		return 0;
 	}
 
+	if (codec->cache_ops->name)
+		name = codec->cache_ops->name;
+	else
+		name = "unknown";
+
 	if (codec->cache_ops && codec->cache_ops->sync) {
 		if (codec->cache_ops->name)
 			dev_dbg(codec->dev, "Syncing %s cache for %s codec\n",
 				codec->cache_ops->name, codec->name);
+		trace_snd_soc_cache_sync(codec, name, "start");
 		ret = codec->cache_ops->sync(codec);
 		if (!ret)
 			codec->cache_sync = 0;
+		trace_snd_soc_cache_sync(codec, name, "end");
 		return ret;
 	}
 
 	return -EINVAL;
 }
 EXPORT_SYMBOL_GPL(snd_soc_cache_sync);
+
+static int snd_soc_get_reg_access_index(struct snd_soc_codec *codec,
+					unsigned int reg)
+{
+	const struct snd_soc_codec_driver *codec_drv;
+	unsigned int min, max, index;
+
+	codec_drv = codec->driver;
+	min = 0;
+	max = codec_drv->reg_access_size - 1;
+	do {
+		index = (min + max) / 2;
+		if (codec_drv->reg_access_default[index].reg == reg)
+			return index;
+		if (codec_drv->reg_access_default[index].reg < reg)
+			min = index + 1;
+		else
+			max = index;
+	} while (min <= max);
+	return -1;
+}
+
+int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
+				      unsigned int reg)
+{
+	int index;
+
+	if (reg >= codec->driver->reg_cache_size)
+		return 1;
+	index = snd_soc_get_reg_access_index(codec, reg);
+	if (index < 0)
+		return 0;
+	return codec->driver->reg_access_default[index].vol;
+}
+EXPORT_SYMBOL_GPL(snd_soc_default_volatile_register);
+
+int snd_soc_default_readable_register(struct snd_soc_codec *codec,
+				      unsigned int reg)
+{
+	int index;
+
+	if (reg >= codec->driver->reg_cache_size)
+		return 1;
+	index = snd_soc_get_reg_access_index(codec, reg);
+	if (index < 0)
+		return 0;
+	return codec->driver->reg_access_default[index].read;
+}
+EXPORT_SYMBOL_GPL(snd_soc_default_readable_register);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index c4b6061..9dfbb8f 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -48,7 +48,8 @@
 static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
 
 #ifdef CONFIG_DEBUG_FS
-static struct dentry *debugfs_root;
+struct dentry *snd_soc_debugfs_root;
+EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
 #endif
 
 static DEFINE_MUTEX(client_mutex);
@@ -57,8 +58,6 @@
 static LIST_HEAD(platform_list);
 static LIST_HEAD(codec_list);
 
-static int snd_soc_register_card(struct snd_soc_card *card);
-static int snd_soc_unregister_card(struct snd_soc_card *card);
 static int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
 
 /*
@@ -83,7 +82,7 @@
 
 	count += sprintf(buf, "%s registers\n", codec->name);
 	for (i = 0; i < codec->driver->reg_cache_size; i += step) {
-		if (codec->driver->readable_register && !codec->driver->readable_register(i))
+		if (codec->readable_register && !codec->readable_register(codec, i))
 			continue;
 
 		count += sprintf(buf + count, "%2x: ", i);
@@ -209,6 +208,10 @@
 		start++;
 	if (strict_strtoul(start, 16, &value))
 		return -EINVAL;
+
+	/* Userspace has been fiddling around behind the kernel's back */
+	add_taint(TAINT_USER);
+
 	snd_soc_write(codec, reg, value);
 	return buf_size;
 }
@@ -232,6 +235,11 @@
 		return;
 	}
 
+	debugfs_create_bool("cache_sync", 0444, codec->debugfs_codec_root,
+			    &codec->cache_sync);
+	debugfs_create_bool("cache_only", 0444, codec->debugfs_codec_root,
+			    &codec->cache_only);
+
 	codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
 						 codec->debugfs_codec_root,
 						 codec, &codec_reg_fops);
@@ -356,7 +364,7 @@
 static void soc_init_card_debugfs(struct snd_soc_card *card)
 {
 	card->debugfs_card_root = debugfs_create_dir(card->name,
-						     debugfs_root);
+						     snd_soc_debugfs_root);
 	if (!card->debugfs_card_root) {
 		dev_warn(card->dev,
 			 "ASoC: Failed to create codec debugfs directory\n");
@@ -962,12 +970,11 @@
 	.pointer	= soc_pcm_pointer,
 };
 
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
 /* powers down audio subsystem for suspend */
-static int soc_suspend(struct device *dev)
+int snd_soc_suspend(struct device *dev)
 {
-	struct platform_device *pdev = to_platform_device(dev);
-	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct snd_soc_card *card = dev_get_drvdata(dev);
 	struct snd_soc_codec *codec;
 	int i;
 
@@ -1008,7 +1015,7 @@
 	}
 
 	if (card->suspend_pre)
-		card->suspend_pre(pdev, PMSG_SUSPEND);
+		card->suspend_pre(card);
 
 	for (i = 0; i < card->num_rtd; i++) {
 		struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
@@ -1075,10 +1082,11 @@
 	}
 
 	if (card->suspend_post)
-		card->suspend_post(pdev, PMSG_SUSPEND);
+		card->suspend_post(card);
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_suspend);
 
 /* deferred resume work, so resume can complete before we finished
  * setting our codec back up, which can be very slow on I2C
@@ -1087,7 +1095,6 @@
 {
 	struct snd_soc_card *card =
 			container_of(work, struct snd_soc_card, deferred_resume_work);
-	struct platform_device *pdev = to_platform_device(card->dev);
 	struct snd_soc_codec *codec;
 	int i;
 
@@ -1101,7 +1108,7 @@
 	snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D2);
 
 	if (card->resume_pre)
-		card->resume_pre(pdev);
+		card->resume_pre(card);
 
 	/* resume AC97 DAIs */
 	for (i = 0; i < card->num_rtd; i++) {
@@ -1176,7 +1183,7 @@
 	}
 
 	if (card->resume_post)
-		card->resume_post(pdev);
+		card->resume_post(card);
 
 	dev_dbg(card->dev, "resume work completed\n");
 
@@ -1185,10 +1192,9 @@
 }
 
 /* powers up audio subsystem after a suspend */
-static int soc_resume(struct device *dev)
+int snd_soc_resume(struct device *dev)
 {
-	struct platform_device *pdev = to_platform_device(dev);
-	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct snd_soc_card *card = dev_get_drvdata(dev);
 	int i;
 
 	/* AC97 devices might have other drivers hanging off them so
@@ -1210,9 +1216,10 @@
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_resume);
 #else
-#define soc_suspend	NULL
-#define soc_resume	NULL
+#define snd_soc_suspend NULL
+#define snd_soc_resume NULL
 #endif
 
 static struct snd_soc_dai_ops null_dai_ops = {
@@ -1405,26 +1412,31 @@
 	codec->dapm.card = card;
 	soc_set_name_prefix(card, codec);
 
+	if (!try_module_get(codec->dev->driver->owner))
+		return -ENODEV;
+
 	if (codec->driver->probe) {
 		ret = codec->driver->probe(codec);
 		if (ret < 0) {
 			dev_err(codec->dev,
 				"asoc: failed to probe CODEC %s: %d\n",
 				codec->name, ret);
-			return ret;
+			goto err_probe;
 		}
 	}
 
 	soc_init_codec_debugfs(codec);
 
 	/* mark codec as probed and add to card codec list */
-	if (!try_module_get(codec->dev->driver->owner))
-		return -ENODEV;
-
 	codec->probed = 1;
 	list_add(&codec->card_list, &card->codec_dev_list);
 	list_add(&codec->dapm.list, &card->dapm_list);
 
+	return 0;
+
+err_probe:
+	module_put(codec->dev->driver->owner);
+
 	return ret;
 }
 
@@ -1467,7 +1479,6 @@
 
 	/* Make sure all DAPM widgets are instantiated */
 	snd_soc_dapm_new_widgets(&codec->dapm);
-	snd_soc_dapm_sync(&codec->dapm);
 
 	/* register the rtd device */
 	rtd->codec = codec;
@@ -1543,19 +1554,19 @@
 
 	/* probe the platform */
 	if (!platform->probed) {
+		if (!try_module_get(platform->dev->driver->owner))
+			return -ENODEV;
+
 		if (platform->driver->probe) {
 			ret = platform->driver->probe(platform);
 			if (ret < 0) {
 				printk(KERN_ERR "asoc: failed to probe platform %s\n",
 						platform->name);
+				module_put(platform->dev->driver->owner);
 				return ret;
 			}
 		}
 		/* mark platform as probed and add to card platform list */
-
-		if (!try_module_get(platform->dev->driver->owner))
-			return -ENODEV;
-
 		platform->probed = 1;
 		list_add(&platform->card_list, &card->platform_dev_list);
 	}
@@ -1713,7 +1724,6 @@
 
 static void snd_soc_instantiate_card(struct snd_soc_card *card)
 {
-	struct platform_device *pdev = to_platform_device(card->dev);
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec_conf *codec_conf;
 	enum snd_soc_compress_type compress_type;
@@ -1740,6 +1750,8 @@
 	list_for_each_entry(codec, &codec_list, list) {
 		if (codec->cache_init)
 			continue;
+		/* by default we don't override the compress_type */
+		compress_type = 0;
 		/* check to see if we need to override the compress_type */
 		for (i = 0; i < card->num_configs; ++i) {
 			codec_conf = &card->codec_conf[i];
@@ -1750,18 +1762,6 @@
 					break;
 			}
 		}
-		if (i == card->num_configs) {
-			/* no need to override the compress_type so
-			 * go ahead and do the standard thing */
-			ret = snd_soc_init_codec_cache(codec, 0);
-			if (ret < 0) {
-				mutex_unlock(&card->mutex);
-				return;
-			}
-			continue;
-		}
-		/* override the compress_type with the one supplied in
-		 * the machine driver */
 		ret = snd_soc_init_codec_cache(codec, compress_type);
 		if (ret < 0) {
 			mutex_unlock(&card->mutex);
@@ -1787,7 +1787,7 @@
 
 	/* initialise the sound card only once */
 	if (card->probe) {
-		ret = card->probe(pdev);
+		ret = card->probe(card);
 		if (ret < 0)
 			goto card_probe_error;
 	}
@@ -1848,7 +1848,7 @@
 
 card_probe_error:
 	if (card->remove)
-		card->remove(pdev);
+		card->remove(card);
 
 	snd_card_free(card->snd_card);
 
@@ -1872,16 +1872,16 @@
 	struct snd_soc_card *card = platform_get_drvdata(pdev);
 	int ret = 0;
 
+	/*
+	 * no card, so machine driver should be registering card
+	 * we should not be here in that case so ret error
+	 */
+	if (!card)
+		return -EINVAL;
+
 	/* Bodge while we unpick instantiation */
 	card->dev = &pdev->dev;
-	INIT_LIST_HEAD(&card->dai_dev_list);
-	INIT_LIST_HEAD(&card->codec_dev_list);
-	INIT_LIST_HEAD(&card->platform_dev_list);
-	INIT_LIST_HEAD(&card->widgets);
-	INIT_LIST_HEAD(&card->paths);
-	INIT_LIST_HEAD(&card->dapm_list);
-
-	soc_init_card_debugfs(card);
+	snd_soc_initialize_card_lists(card);
 
 	ret = snd_soc_register_card(card);
 	if (ret != 0) {
@@ -1892,45 +1892,48 @@
 	return 0;
 }
 
+static int soc_cleanup_card_resources(struct snd_soc_card *card)
+{
+	int i;
+
+	/* make sure any delayed work runs */
+	for (i = 0; i < card->num_rtd; i++) {
+		struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+		flush_delayed_work_sync(&rtd->delayed_work);
+	}
+
+	/* remove auxiliary devices */
+	for (i = 0; i < card->num_aux_devs; i++)
+		soc_remove_aux_dev(card, i);
+
+	/* remove and free each DAI */
+	for (i = 0; i < card->num_rtd; i++)
+		soc_remove_dai_link(card, i);
+
+	soc_cleanup_card_debugfs(card);
+
+	/* remove the card */
+	if (card->remove)
+		card->remove(card);
+
+	kfree(card->rtd);
+	snd_card_free(card->snd_card);
+	return 0;
+
+}
+
 /* removes a socdev */
 static int soc_remove(struct platform_device *pdev)
 {
 	struct snd_soc_card *card = platform_get_drvdata(pdev);
-	int i;
 
-		if (card->instantiated) {
-
-		/* make sure any delayed work runs */
-		for (i = 0; i < card->num_rtd; i++) {
-			struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
-			flush_delayed_work_sync(&rtd->delayed_work);
-		}
-
-		/* remove auxiliary devices */
-		for (i = 0; i < card->num_aux_devs; i++)
-			soc_remove_aux_dev(card, i);
-
-		/* remove and free each DAI */
-		for (i = 0; i < card->num_rtd; i++)
-			soc_remove_dai_link(card, i);
-
-		soc_cleanup_card_debugfs(card);
-
-		/* remove the card */
-		if (card->remove)
-			card->remove(pdev);
-
-		kfree(card->rtd);
-		snd_card_free(card->snd_card);
-	}
 	snd_soc_unregister_card(card);
 	return 0;
 }
 
-static int soc_poweroff(struct device *dev)
+int snd_soc_poweroff(struct device *dev)
 {
-	struct platform_device *pdev = to_platform_device(dev);
-	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct snd_soc_card *card = dev_get_drvdata(dev);
 	int i;
 
 	if (!card->instantiated)
@@ -1947,11 +1950,12 @@
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_poweroff);
 
-static const struct dev_pm_ops soc_pm_ops = {
-	.suspend = soc_suspend,
-	.resume = soc_resume,
-	.poweroff = soc_poweroff,
+const struct dev_pm_ops snd_soc_pm_ops = {
+	.suspend = snd_soc_suspend,
+	.resume = snd_soc_resume,
+	.poweroff = snd_soc_poweroff,
 };
 
 /* ASoC platform driver */
@@ -1959,7 +1963,7 @@
 	.driver		= {
 		.name		= "soc-audio",
 		.owner		= THIS_MODULE,
-		.pm		= &soc_pm_ops,
+		.pm		= &snd_soc_pm_ops,
 	},
 	.probe		= soc_probe,
 	.remove		= soc_remove,
@@ -2029,10 +2033,11 @@
  *
  * Boolean function indiciating if a CODEC register is volatile.
  */
-int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg)
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
+				    unsigned int reg)
 {
-	if (codec->driver->volatile_register)
-		return codec->driver->volatile_register(reg);
+	if (codec->volatile_register)
+		return codec->volatile_register(codec, reg);
 	else
 		return 0;
 }
@@ -2129,19 +2134,27 @@
  *
  * Writes new register value.
  *
- * Returns 1 for change else 0.
+ * Returns 1 for change, 0 for no change, or negative error code.
  */
 int snd_soc_update_bits(struct snd_soc_codec *codec, unsigned short reg,
 				unsigned int mask, unsigned int value)
 {
 	int change;
 	unsigned int old, new;
+	int ret;
 
-	old = snd_soc_read(codec, reg);
+	ret = snd_soc_read(codec, reg);
+	if (ret < 0)
+		return ret;
+
+	old = ret;
 	new = (old & ~mask) | value;
 	change = old != new;
-	if (change)
-		snd_soc_write(codec, reg, new);
+	if (change) {
+		ret = snd_soc_write(codec, reg, new);
+		if (ret < 0)
+			return ret;
+	}
 
 	return change;
 }
@@ -3101,17 +3114,16 @@
  *
  * @card: Card to register
  *
- * Note that currently this is an internal only function: it will be
- * exposed to machine drivers after further backporting of ASoC v2
- * registration APIs.
  */
-static int snd_soc_register_card(struct snd_soc_card *card)
+int snd_soc_register_card(struct snd_soc_card *card)
 {
 	int i;
 
 	if (!card->name || !card->dev)
 		return -EINVAL;
 
+	soc_init_card_debugfs(card);
+
 	card->rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime) *
 			    (card->num_links + card->num_aux_devs),
 			    GFP_KERNEL);
@@ -3135,18 +3147,18 @@
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_register_card);
 
 /**
  * snd_soc_unregister_card - Unregister a card with the ASoC core
  *
  * @card: Card to unregister
  *
- * Note that currently this is an internal only function: it will be
- * exposed to machine drivers after further backporting of ASoC v2
- * registration APIs.
  */
-static int snd_soc_unregister_card(struct snd_soc_card *card)
+int snd_soc_unregister_card(struct snd_soc_card *card)
 {
+	if (card->instantiated)
+		soc_cleanup_card_resources(card);
 	mutex_lock(&client_mutex);
 	list_del(&card->list);
 	mutex_unlock(&client_mutex);
@@ -3154,6 +3166,7 @@
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(snd_soc_unregister_card);
 
 /*
  * Simplify DAI link configuration by removing ".-1" from device names
@@ -3483,9 +3496,12 @@
 
 	codec->write = codec_drv->write;
 	codec->read = codec_drv->read;
+	codec->volatile_register = codec_drv->volatile_register;
+	codec->readable_register = codec_drv->readable_register;
 	codec->dapm.bias_level = SND_SOC_BIAS_OFF;
 	codec->dapm.dev = dev;
 	codec->dapm.codec = codec;
+	codec->dapm.seq_notifier = codec_drv->seq_notifier;
 	codec->dev = dev;
 	codec->driver = codec_drv;
 	codec->num_dai = num_dai;
@@ -3494,20 +3510,30 @@
 	/* allocate CODEC register cache */
 	if (codec_drv->reg_cache_size && codec_drv->reg_word_size) {
 		reg_size = codec_drv->reg_cache_size * codec_drv->reg_word_size;
+		codec->reg_size = reg_size;
 		/* it is necessary to make a copy of the default register cache
 		 * because in the case of using a compression type that requires
 		 * the default register cache to be marked as __devinitconst the
 		 * kernel might have freed the array by the time we initialize
 		 * the cache.
 		 */
-		codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default,
-					      reg_size, GFP_KERNEL);
-		if (!codec->reg_def_copy) {
-			ret = -ENOMEM;
-			goto fail;
+		if (codec_drv->reg_cache_default) {
+			codec->reg_def_copy = kmemdup(codec_drv->reg_cache_default,
+						      reg_size, GFP_KERNEL);
+			if (!codec->reg_def_copy) {
+				ret = -ENOMEM;
+				goto fail;
+			}
 		}
 	}
 
+	if (codec_drv->reg_access_size && codec_drv->reg_access_default) {
+		if (!codec->volatile_register)
+			codec->volatile_register = snd_soc_default_volatile_register;
+		if (!codec->readable_register)
+			codec->readable_register = snd_soc_default_readable_register;
+	}
+
 	for (i = 0; i < num_dai; i++) {
 		fixup_codec_formats(&dai_drv[i].playback);
 		fixup_codec_formats(&dai_drv[i].capture);
@@ -3574,22 +3600,22 @@
 static int __init snd_soc_init(void)
 {
 #ifdef CONFIG_DEBUG_FS
-	debugfs_root = debugfs_create_dir("asoc", NULL);
-	if (IS_ERR(debugfs_root) || !debugfs_root) {
+	snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL);
+	if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) {
 		printk(KERN_WARNING
 		       "ASoC: Failed to create debugfs directory\n");
-		debugfs_root = NULL;
+		snd_soc_debugfs_root = NULL;
 	}
 
-	if (!debugfs_create_file("codecs", 0444, debugfs_root, NULL,
+	if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL,
 				 &codec_list_fops))
 		pr_warn("ASoC: Failed to create CODEC list debugfs file\n");
 
-	if (!debugfs_create_file("dais", 0444, debugfs_root, NULL,
+	if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL,
 				 &dai_list_fops))
 		pr_warn("ASoC: Failed to create DAI list debugfs file\n");
 
-	if (!debugfs_create_file("platforms", 0444, debugfs_root, NULL,
+	if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL,
 				 &platform_list_fops))
 		pr_warn("ASoC: Failed to create platform list debugfs file\n");
 #endif
@@ -3601,7 +3627,7 @@
 static void __exit snd_soc_exit(void)
 {
 #ifdef CONFIG_DEBUG_FS
-	debugfs_remove_recursive(debugfs_root);
+	debugfs_remove_recursive(snd_soc_debugfs_root);
 #endif
 	platform_driver_unregister(&soc_driver);
 }
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 8194f15..d0342aa 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -726,10 +726,23 @@
 
 static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
 			    struct snd_soc_dapm_widget *b,
-			    int sort[])
+			    bool power_up)
 {
+	int *sort;
+
+	if (power_up)
+		sort = dapm_up_seq;
+	else
+		sort = dapm_down_seq;
+
 	if (sort[a->id] != sort[b->id])
 		return sort[a->id] - sort[b->id];
+	if (a->subseq != b->subseq) {
+		if (power_up)
+			return a->subseq - b->subseq;
+		else
+			return b->subseq - a->subseq;
+	}
 	if (a->reg != b->reg)
 		return a->reg - b->reg;
 	if (a->dapm != b->dapm)
@@ -741,12 +754,12 @@
 /* Insert a widget in order into a DAPM power sequence. */
 static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
 			    struct list_head *list,
-			    int sort[])
+			    bool power_up)
 {
 	struct snd_soc_dapm_widget *w;
 
 	list_for_each_entry(w, list, power_list)
-		if (dapm_seq_compare(new_widget, w, sort) < 0) {
+		if (dapm_seq_compare(new_widget, w, power_up) < 0) {
 			list_add_tail(&new_widget->power_list, &w->power_list);
 			return;
 		}
@@ -857,26 +870,42 @@
  * handled.
  */
 static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
-			 struct list_head *list, int event, int sort[])
+			 struct list_head *list, int event, bool power_up)
 {
 	struct snd_soc_dapm_widget *w, *n;
 	LIST_HEAD(pending);
 	int cur_sort = -1;
+	int cur_subseq = -1;
 	int cur_reg = SND_SOC_NOPM;
 	struct snd_soc_dapm_context *cur_dapm = NULL;
-	int ret;
+	int ret, i;
+	int *sort;
+
+	if (power_up)
+		sort = dapm_up_seq;
+	else
+		sort = dapm_down_seq;
 
 	list_for_each_entry_safe(w, n, list, power_list) {
 		ret = 0;
 
 		/* Do we need to apply any queued changes? */
 		if (sort[w->id] != cur_sort || w->reg != cur_reg ||
-		    w->dapm != cur_dapm) {
+		    w->dapm != cur_dapm || w->subseq != cur_subseq) {
 			if (!list_empty(&pending))
 				dapm_seq_run_coalesced(cur_dapm, &pending);
 
+			if (cur_dapm && cur_dapm->seq_notifier) {
+				for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
+					if (sort[i] == cur_sort)
+						cur_dapm->seq_notifier(cur_dapm,
+								       i,
+								       cur_subseq);
+			}
+
 			INIT_LIST_HEAD(&pending);
 			cur_sort = -1;
+			cur_subseq = -1;
 			cur_reg = SND_SOC_NOPM;
 			cur_dapm = NULL;
 		}
@@ -921,6 +950,7 @@
 		default:
 			/* Queue it up for application */
 			cur_sort = sort[w->id];
+			cur_subseq = w->subseq;
 			cur_reg = w->reg;
 			cur_dapm = w->dapm;
 			list_move(&w->power_list, &pending);
@@ -934,6 +964,13 @@
 
 	if (!list_empty(&pending))
 		dapm_seq_run_coalesced(dapm, &pending);
+
+	if (cur_dapm && cur_dapm->seq_notifier) {
+		for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
+			if (sort[i] == cur_sort)
+				cur_dapm->seq_notifier(cur_dapm,
+						       i, cur_subseq);
+	}
 }
 
 static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
@@ -1002,10 +1039,10 @@
 	list_for_each_entry(w, &card->widgets, list) {
 		switch (w->id) {
 		case snd_soc_dapm_pre:
-			dapm_seq_insert(w, &down_list, dapm_down_seq);
+			dapm_seq_insert(w, &down_list, false);
 			break;
 		case snd_soc_dapm_post:
-			dapm_seq_insert(w, &up_list, dapm_up_seq);
+			dapm_seq_insert(w, &up_list, true);
 			break;
 
 		default:
@@ -1025,9 +1062,9 @@
 			trace_snd_soc_dapm_widget_power(w, power);
 
 			if (power)
-				dapm_seq_insert(w, &up_list, dapm_up_seq);
+				dapm_seq_insert(w, &up_list, true);
 			else
-				dapm_seq_insert(w, &down_list, dapm_down_seq);
+				dapm_seq_insert(w, &down_list, false);
 
 			w->power = power;
 			break;
@@ -1086,12 +1123,12 @@
 	}
 
 	/* Power down widgets first; try to avoid amplifying pops. */
-	dapm_seq_run(dapm, &down_list, event, dapm_down_seq);
+	dapm_seq_run(dapm, &down_list, event, false);
 
 	dapm_widget_update(dapm);
 
 	/* Now power up. */
-	dapm_seq_run(dapm, &up_list, event, dapm_up_seq);
+	dapm_seq_run(dapm, &up_list, event, true);
 
 	list_for_each_entry(d, &dapm->card->dapm_list, list) {
 		/* If we just powered the last thing off drop to standby bias */
@@ -2372,7 +2409,7 @@
 		if (w->dapm != dapm)
 			continue;
 		if (w->power) {
-			dapm_seq_insert(w, &down_list, dapm_down_seq);
+			dapm_seq_insert(w, &down_list, false);
 			w->power = 0;
 			powerdown = 1;
 		}
@@ -2383,7 +2420,7 @@
 	 */
 	if (powerdown) {
 		snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_PREPARE);
-		dapm_seq_run(dapm, &down_list, 0, dapm_down_seq);
+		dapm_seq_run(dapm, &down_list, 0, false);
 		snd_soc_dapm_set_bias_level(NULL, dapm, SND_SOC_BIAS_STANDBY);
 	}
 }
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
new file mode 100644
index 0000000..66b504f
--- /dev/null
+++ b/sound/soc/tegra/Kconfig
@@ -0,0 +1,26 @@
+config SND_TEGRA_SOC
+	tristate "SoC Audio for the Tegra System-on-Chip"
+	depends on ARCH_TEGRA && TEGRA_SYSTEM_DMA
+	default m
+	help
+	  Say Y or M here if you want support for SoC audio on Tegra.
+
+config SND_TEGRA_SOC_I2S
+	tristate
+	depends on SND_TEGRA_SOC
+	default m
+	help
+	  Say Y or M if you want to add support for codecs attached to the
+	  Tegra I2S interface. You will also need to select the individual
+	  machine drivers to support below.
+
+config SND_TEGRA_SOC_HARMONY
+	tristate "SoC Audio support for Tegra Harmony reference board"
+	depends on SND_TEGRA_SOC && MACH_HARMONY && I2C
+	default m
+	select SND_TEGRA_SOC_I2S
+	select SND_SOC_WM8903
+	help
+	  Say Y or M here if you want to add support for SoC audio on the
+	  Tegra Harmony reference board.
+
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
new file mode 100644
index 0000000..dfd2ab9
--- /dev/null
+++ b/sound/soc/tegra/Makefile
@@ -0,0 +1,14 @@
+# Tegra platform Support
+snd-soc-tegra-das-objs := tegra_das.o
+snd-soc-tegra-pcm-objs := tegra_pcm.o
+snd-soc-tegra-i2s-objs := tegra_i2s.o
+
+obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-das.o
+obj-$(CONFIG_SND_TEGRA_SOC) += snd-soc-tegra-pcm.o
+obj-$(CONFIG_SND_TEGRA_SOC_I2S) += snd-soc-tegra-i2s.o
+
+# Tegra machine Support
+snd-soc-tegra-harmony-objs := harmony.o
+snd-soc-tegra-harmony-objs += tegra_asoc_utils.o
+
+obj-$(CONFIG_SND_TEGRA_SOC_HARMONY) += snd-soc-tegra-harmony.o
diff --git a/sound/soc/tegra/harmony.c b/sound/soc/tegra/harmony.c
new file mode 100644
index 0000000..b160b71
--- /dev/null
+++ b/sound/soc/tegra/harmony.c
@@ -0,0 +1,210 @@
+/*
+ * harmony.c - Harmony machine ASoC driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <asm/mach-types.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+#include "tegra_pcm.h"
+#include "tegra_asoc_utils.h"
+
+#define PREFIX "ASoC Harmony: "
+
+static struct platform_device *harmony_snd_device;
+
+static int harmony_asoc_hw_params(struct snd_pcm_substream *substream,
+					struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int srate, mclk, mclk_change;
+	int err;
+
+	srate = params_rate(params);
+	switch (srate) {
+	case 64000:
+	case 88200:
+	case 96000:
+		mclk = 128 * srate;
+		break;
+	default:
+		mclk = 256 * srate;
+		break;
+	}
+	/* FIXME: Codec only requires >= 3MHz if OSR==0 */
+	while (mclk < 6000000)
+		mclk *= 2;
+
+	err = tegra_asoc_utils_set_rate(srate, mclk, &mclk_change);
+	if (err < 0) {
+		pr_err(PREFIX "Can't configure clocks\n");
+		return err;
+	}
+
+	err = snd_soc_dai_set_fmt(codec_dai,
+					SND_SOC_DAIFMT_I2S |
+					SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBS_CFS);
+	if (err < 0) {
+		pr_err(PREFIX "codec_dai fmt not set\n");
+		return err;
+	}
+
+	err = snd_soc_dai_set_fmt(cpu_dai,
+					SND_SOC_DAIFMT_I2S |
+					SND_SOC_DAIFMT_NB_NF |
+					SND_SOC_DAIFMT_CBS_CFS);
+	if (err < 0) {
+		pr_err(PREFIX "cpu_dai fmt not set\n");
+		return err;
+	}
+
+	if (mclk_change) {
+	    err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk, SND_SOC_CLOCK_IN);
+	    if (err < 0) {
+		    pr_err(PREFIX "codec_dai clock not set\n");
+		    return err;
+	    }
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops harmony_asoc_ops = {
+	.hw_params = harmony_asoc_hw_params,
+};
+
+static const struct snd_soc_dapm_widget harmony_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headphone Jack", NULL),
+	SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route harmony_audio_map[] = {
+	{"Headphone Jack", NULL, "HPOUTR"},
+	{"Headphone Jack", NULL, "HPOUTL"},
+	{"Mic Bias", NULL, "Mic Jack"},
+	{"IN1L", NULL, "Mic Bias"},
+};
+
+static int harmony_asoc_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+	snd_soc_dapm_new_controls(dapm, harmony_dapm_widgets,
+					ARRAY_SIZE(harmony_dapm_widgets));
+
+	snd_soc_dapm_add_routes(dapm, harmony_audio_map,
+				ARRAY_SIZE(harmony_audio_map));
+
+	snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
+	snd_soc_dapm_enable_pin(dapm, "Mic Jack");
+	snd_soc_dapm_sync(dapm);
+
+	return 0;
+}
+
+static struct snd_soc_dai_link harmony_wm8903_dai = {
+	.name = "WM8903",
+	.stream_name = "WM8903 PCM",
+	.codec_name = "wm8903-codec.0-001a",
+	.platform_name = "tegra-pcm-audio",
+	.cpu_dai_name = "tegra-i2s.0",
+	.codec_dai_name = "wm8903-hifi",
+	.init = harmony_asoc_init,
+	.ops = &harmony_asoc_ops,
+};
+
+static struct snd_soc_card snd_soc_harmony = {
+	.name = "tegra-harmony",
+	.dai_link = &harmony_wm8903_dai,
+	.num_links = 1,
+};
+
+static int __init harmony_soc_modinit(void)
+{
+	int ret;
+
+	if (!machine_is_harmony()) {
+		pr_err(PREFIX "Not running on Tegra Harmony!\n");
+		return -ENODEV;
+	}
+
+	ret = tegra_asoc_utils_init();
+	if (ret) {
+		return ret;
+	}
+
+	/*
+	 * Create and register platform device
+	 */
+	harmony_snd_device = platform_device_alloc("soc-audio", -1);
+	if (harmony_snd_device == NULL) {
+		pr_err(PREFIX "platform_device_alloc failed\n");
+		ret = -ENOMEM;
+		goto err_clock_utils;
+	}
+
+	platform_set_drvdata(harmony_snd_device, &snd_soc_harmony);
+
+	ret = platform_device_add(harmony_snd_device);
+	if (ret) {
+		pr_err(PREFIX "platform_device_add failed (%d)\n",
+			ret);
+		goto err_device_put;
+	}
+
+	return 0;
+
+err_device_put:
+	platform_device_put(harmony_snd_device);
+err_clock_utils:
+	tegra_asoc_utils_fini();
+	return ret;
+}
+module_init(harmony_soc_modinit);
+
+static void __exit harmony_soc_modexit(void)
+{
+	platform_device_unregister(harmony_snd_device);
+
+	tegra_asoc_utils_fini();
+}
+module_exit(harmony_soc_modexit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Harmony machine ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
new file mode 100644
index 0000000..cfe2ea8
--- /dev/null
+++ b/sound/soc/tegra/tegra_asoc_utils.c
@@ -0,0 +1,152 @@
+/*
+ * tegra_asoc_utils.c - Harmony machine ASoC driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+
+#include "tegra_asoc_utils.h"
+
+#define PREFIX "ASoC Tegra: "
+
+static struct clk *clk_pll_a;
+static struct clk *clk_pll_a_out0;
+static struct clk *clk_cdev1;
+
+static int set_baseclock, set_mclk;
+
+int tegra_asoc_utils_set_rate(int srate, int mclk, int *mclk_change)
+{
+	int new_baseclock;
+	int err;
+
+	switch (srate) {
+	case 11025:
+	case 22050:
+	case 44100:
+	case 88200:
+		new_baseclock = 56448000;
+		break;
+	case 8000:
+	case 16000:
+	case 32000:
+	case 48000:
+	case 64000:
+	case 96000:
+		new_baseclock = 73728000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*mclk_change = ((new_baseclock != set_baseclock) ||
+			(mclk != set_mclk));
+	if (!*mclk_change)
+	    return 0;
+
+	set_baseclock = 0;
+	set_mclk = 0;
+
+	clk_disable(clk_cdev1);
+	clk_disable(clk_pll_a_out0);
+	clk_disable(clk_pll_a);
+
+	err = clk_set_rate(clk_pll_a, new_baseclock);
+	if (err) {
+		pr_err(PREFIX "Can't set pll_a rate: %d\n", err);
+		return err;
+	}
+
+	err = clk_set_rate(clk_pll_a_out0, mclk);
+	if (err) {
+		pr_err(PREFIX "Can't set pll_a_out0 rate: %d\n", err);
+		return err;
+	}
+
+	/* Don't set cdev1 rate; its locked to pll_a_out0 */
+
+	err = clk_enable(clk_pll_a);
+	if (err) {
+		pr_err(PREFIX "Can't enable pll_a: %d\n", err);
+		return err;
+	}
+
+	err = clk_enable(clk_pll_a_out0);
+	if (err) {
+		pr_err(PREFIX "Can't enable pll_a_out0: %d\n", err);
+		return err;
+	}
+
+	err = clk_enable(clk_cdev1);
+	if (err) {
+		pr_err(PREFIX "Can't enable cdev1: %d\n", err);
+		return err;
+	}
+
+	set_baseclock = new_baseclock;
+	set_mclk = mclk;
+
+	return 0;
+}
+
+int tegra_asoc_utils_init(void)
+{
+	int ret;
+
+	clk_pll_a = clk_get_sys(NULL, "pll_a");
+	if (IS_ERR(clk_pll_a)) {
+		pr_err(PREFIX "Can't retrieve clk pll_a\n");
+		ret = PTR_ERR(clk_pll_a);
+		goto err;
+	}
+
+	clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
+	if (IS_ERR(clk_pll_a_out0)) {
+		pr_err(PREFIX "Can't retrieve clk pll_a_out0\n");
+		ret = PTR_ERR(clk_pll_a_out0);
+		goto err_put_pll_a;
+	}
+
+	clk_cdev1 = clk_get_sys(NULL, "cdev1");
+	if (IS_ERR(clk_cdev1)) {
+		pr_err(PREFIX "Can't retrieve clk cdev1\n");
+		ret = PTR_ERR(clk_cdev1);
+		goto err_put_pll_a_out0;
+	}
+
+	return 0;
+
+err_put_pll_a_out0:
+	clk_put(clk_pll_a_out0);
+err_put_pll_a:
+	clk_put(clk_pll_a);
+err:
+	return ret;
+}
+
+void tegra_asoc_utils_fini(void)
+{
+	clk_put(clk_cdev1);
+	clk_put(clk_pll_a_out0);
+	clk_put(clk_pll_a);
+}
+
diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h
new file mode 100644
index 0000000..855f8f6
--- /dev/null
+++ b/sound/soc/tegra/tegra_asoc_utils.h
@@ -0,0 +1,31 @@
+/*
+ * tegra_asoc_utils.h - Definitions for Tegra DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA_ASOC_UTILS_H__
+#define __TEGRA_ASOC_UTILS_H_
+
+int tegra_asoc_utils_set_rate(int srate, int mclk_rate, int *mclk_change);
+int tegra_asoc_utils_init(void);
+void tegra_asoc_utils_fini(void);
+
+#endif
+
diff --git a/sound/soc/tegra/tegra_das.c b/sound/soc/tegra/tegra_das.c
new file mode 100644
index 0000000..01eb9c9
--- /dev/null
+++ b/sound/soc/tegra/tegra_das.c
@@ -0,0 +1,264 @@
+/*
+ * tegra_das.c - Tegra DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/iomap.h>
+#include <sound/soc.h>
+#include "tegra_das.h"
+
+#define DRV_NAME "tegra-das"
+
+static struct tegra_das *das;
+
+static inline void tegra_das_write(u32 reg, u32 val)
+{
+	__raw_writel(val, das->regs + reg);
+}
+
+static inline u32 tegra_das_read(u32 reg)
+{
+	return __raw_readl(das->regs + reg);
+}
+
+int tegra_das_connect_dap_to_dac(int dap, int dac)
+{
+	u32 addr;
+	u32 reg;
+
+	if (!das)
+		return -ENODEV;
+
+	addr = TEGRA_DAS_DAP_CTRL_SEL +
+		(dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
+	reg = dac << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P;
+
+	tegra_das_write(addr, reg);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac);
+
+int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master,
+					int sdata1rx, int sdata2rx)
+{
+	u32 addr;
+	u32 reg;
+
+	if (!das)
+		return -ENODEV;
+
+	addr = TEGRA_DAS_DAP_CTRL_SEL +
+		(dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
+	reg = otherdap << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P |
+		!!sdata2rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P |
+		!!sdata1rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P |
+		!!master << TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P;
+
+	tegra_das_write(addr, reg);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap);
+
+int tegra_das_connect_dac_to_dap(int dac, int dap)
+{
+	u32 addr;
+	u32 reg;
+
+	if (!das)
+		return -ENODEV;
+
+	addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
+		(dac * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
+	reg = dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P |
+		dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P |
+		dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P;
+
+	tegra_das_write(addr, reg);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap);
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_das_show(struct seq_file *s, void *unused)
+{
+	int i;
+	u32 addr;
+	u32 reg;
+
+	for (i = 0; i < TEGRA_DAS_DAP_CTRL_SEL_COUNT; i++) {
+		addr = TEGRA_DAS_DAP_CTRL_SEL +
+			(i * TEGRA_DAS_DAP_CTRL_SEL_STRIDE);
+		reg = tegra_das_read(addr);
+		seq_printf(s, "TEGRA_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg);
+	}
+
+	for (i = 0; i < TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) {
+		addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL +
+			(i * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE);
+		reg = tegra_das_read(addr);
+		seq_printf(s, "TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n",
+				 i, reg);
+	}
+
+	return 0;
+}
+
+static int tegra_das_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tegra_das_show, inode->i_private);
+}
+
+static const struct file_operations tegra_das_debug_fops = {
+	.open    = tegra_das_debug_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = single_release,
+};
+
+static void tegra_das_debug_add(struct tegra_das *das)
+{
+	das->debug = debugfs_create_file(DRV_NAME, S_IRUGO,
+					 snd_soc_debugfs_root, das,
+					 &tegra_das_debug_fops);
+}
+
+static void tegra_das_debug_remove(struct tegra_das *das)
+{
+	if (das->debug)
+		debugfs_remove(das->debug);
+}
+#else
+static inline void tegra_das_debug_add(struct tegra_das *das)
+{
+}
+
+static inline void tegra_das_debug_remove(struct tegra_das *das)
+{
+}
+#endif
+
+static int __devinit tegra_das_probe(struct platform_device *pdev)
+{
+	struct resource *res, *region;
+	int ret = 0;
+
+	if (das)
+		return -ENODEV;
+
+	das = kzalloc(sizeof(struct tegra_das), GFP_KERNEL);
+	if (!das) {
+		dev_err(&pdev->dev, "Can't allocate tegra_das\n");
+		ret = -ENOMEM;
+		goto exit;
+	}
+	das->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No memory resource\n");
+		ret = -ENODEV;
+		goto err_free;
+	}
+
+	region = request_mem_region(res->start, resource_size(res),
+					pdev->name);
+	if (!region) {
+		dev_err(&pdev->dev, "Memory region already claimed\n");
+		ret = -EBUSY;
+		goto err_free;
+	}
+
+	das->regs = ioremap(res->start, resource_size(res));
+	if (!das->regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_release;
+	}
+
+	tegra_das_debug_add(das);
+
+	platform_set_drvdata(pdev, das);
+
+	return 0;
+
+err_release:
+	release_mem_region(res->start, resource_size(res));
+err_free:
+	kfree(das);
+	das = 0;
+exit:
+	return ret;
+}
+
+static int __devexit tegra_das_remove(struct platform_device *pdev)
+{
+	struct resource *res;
+
+	if (!das)
+		return -ENODEV;
+
+	platform_set_drvdata(pdev, NULL);
+
+	tegra_das_debug_remove(das);
+
+	iounmap(das->regs);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	kfree(das);
+	das = 0;
+
+	return 0;
+}
+
+static struct platform_driver tegra_das_driver = {
+	.probe = tegra_das_probe,
+	.remove = __devexit_p(tegra_das_remove),
+	.driver = {
+		.name = DRV_NAME,
+	},
+};
+
+static int __init tegra_das_modinit(void)
+{
+	return platform_driver_register(&tegra_das_driver);
+}
+module_init(tegra_das_modinit);
+
+static void __exit tegra_das_modexit(void)
+{
+	platform_driver_unregister(&tegra_das_driver);
+}
+module_exit(tegra_das_modexit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra DAS driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_das.h b/sound/soc/tegra/tegra_das.h
new file mode 100644
index 0000000..2c96c7b
--- /dev/null
+++ b/sound/soc/tegra/tegra_das.h
@@ -0,0 +1,135 @@
+/*
+ * tegra_das.h - Definitions for Tegra DAS driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA_DAS_H__
+#define __TEGRA_DAS_H__
+
+/* Register TEGRA_DAS_DAP_CTRL_SEL */
+#define TEGRA_DAS_DAP_CTRL_SEL				0x00
+#define TEGRA_DAS_DAP_CTRL_SEL_COUNT			5
+#define TEGRA_DAS_DAP_CTRL_SEL_STRIDE			4
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P		31
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S		1
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P	30
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S	1
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P	29
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S	1
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P		0
+#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S		5
+
+/* Values for field TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */
+#define TEGRA_DAS_DAP_SEL_DAC1	0
+#define TEGRA_DAS_DAP_SEL_DAC2	1
+#define TEGRA_DAS_DAP_SEL_DAC3	2
+#define TEGRA_DAS_DAP_SEL_DAP1	16
+#define TEGRA_DAS_DAP_SEL_DAP2	17
+#define TEGRA_DAS_DAP_SEL_DAP3	18
+#define TEGRA_DAS_DAP_SEL_DAP4	19
+#define TEGRA_DAS_DAP_SEL_DAP5	20
+
+/* Register TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL */
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL			0x40
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT			3
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE			4
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P	28
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S	4
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P	24
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S	4
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P		0
+#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S		4
+
+/*
+ * Values for:
+ * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL
+ * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL
+ * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL
+ */
+#define TEGRA_DAS_DAC_SEL_DAP1	0
+#define TEGRA_DAS_DAC_SEL_DAP2	1
+#define TEGRA_DAS_DAC_SEL_DAP3	2
+#define TEGRA_DAS_DAC_SEL_DAP4	3
+#define TEGRA_DAS_DAC_SEL_DAP5	4
+
+/*
+ * Names/IDs of the DACs/DAPs.
+ */
+
+#define TEGRA_DAS_DAP_ID_1 0
+#define TEGRA_DAS_DAP_ID_2 1
+#define TEGRA_DAS_DAP_ID_3 2
+#define TEGRA_DAS_DAP_ID_4 3
+#define TEGRA_DAS_DAP_ID_5 4
+
+#define TEGRA_DAS_DAC_ID_1 0
+#define TEGRA_DAS_DAC_ID_2 1
+#define TEGRA_DAS_DAC_ID_3 2
+
+struct tegra_das {
+	struct device *dev;
+	void __iomem *regs;
+	struct dentry *debug;
+};
+
+/*
+ * Terminology:
+ * DAS: Digital audio switch (HW module controlled by this driver)
+ * DAP: Digital audio port (port/pins on Tegra device)
+ * DAC: Digital audio controller (e.g. I2S or AC97 controller elsewhere)
+ * 
+ * The Tegra DAS is a mux/cross-bar which can connect each DAP to a specific
+ * DAC, or another DAP. When DAPs are connected, one must be the master and
+ * one the slave. Each DAC allows selection of a specific DAP for input, to
+ * cater for the case where N DAPs are connected to 1 DAC for broadcast
+ * output.
+ *
+ * This driver is dumb; no attempt is made to ensure that a valid routing
+ * configuration is programmed.
+ */
+
+/*
+ * Connect a DAP to to a DAC
+ * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
+ * dac_sel: DAC to connect to: TEGRA_DAS_DAP_SEL_DAC*
+ */
+extern int tegra_das_connect_dap_to_dac(int dap_id, int dac_sel);
+
+/*
+ * Connect a DAP to to another DAP
+ * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_*
+ * other_dap_sel: DAP to connect to: TEGRA_DAS_DAP_SEL_DAP*
+ * master: Is this DAP the master (1) or slave (0)
+ * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0)
+ * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0)
+ */
+extern int tegra_das_connect_dap_to_dap(int dap_id, int other_dap_sel,
+					int master, int sdata1rx,
+					int sdata2rx);
+
+/*
+ * Connect a DAC's input to a DAP
+ * (DAC outputs are selected by the DAP)
+ * dac_id: DAC ID to connect: TEGRA_DAS_DAC_ID_*
+ * dap_sel: DAP to receive input from: TEGRA_DAS_DAC_SEL_DAP*
+ */
+extern int tegra_das_connect_dac_to_dap(int dac_id, int dap_sel);
+
+#endif
diff --git a/sound/soc/tegra/tegra_i2s.c b/sound/soc/tegra/tegra_i2s.c
new file mode 100644
index 0000000..6d66878
--- /dev/null
+++ b/sound/soc/tegra/tegra_i2s.c
@@ -0,0 +1,502 @@
+/*
+ * tegra_i2s.c - Tegra I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <mach/iomap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_das.h"
+#include "tegra_i2s.h"
+
+#define DRV_NAME "tegra-i2s"
+
+static inline void tegra_i2s_write(struct tegra_i2s *i2s, u32 reg, u32 val)
+{
+	__raw_writel(val, i2s->regs + reg);
+}
+
+static inline u32 tegra_i2s_read(struct tegra_i2s *i2s, u32 reg)
+{
+	return __raw_readl(i2s->regs + reg);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int tegra_i2s_show(struct seq_file *s, void *unused)
+{
+#define REG(r) { r, #r }
+	static const struct {
+		int offset;
+		const char *name;
+	} regs[] = {
+		REG(TEGRA_I2S_CTRL),
+		REG(TEGRA_I2S_STATUS),
+		REG(TEGRA_I2S_TIMING),
+		REG(TEGRA_I2S_FIFO_SCR),
+		REG(TEGRA_I2S_PCM_CTRL),
+		REG(TEGRA_I2S_NW_CTRL),
+		REG(TEGRA_I2S_TDM_CTRL),
+		REG(TEGRA_I2S_TDM_TX_RX_CTRL),
+	};
+#undef REG
+
+	struct tegra_i2s *i2s = s->private;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(regs); i++) {
+		u32 val = tegra_i2s_read(i2s, regs[i].offset);
+		seq_printf(s, "%s = %08x\n", regs[i].name, val);
+	}
+
+	return 0;
+}
+
+static int tegra_i2s_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, tegra_i2s_show, inode->i_private);
+}
+
+static const struct file_operations tegra_i2s_debug_fops = {
+	.open    = tegra_i2s_debug_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = single_release,
+};
+
+static void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id)
+{
+	char name[] = DRV_NAME ".0";
+
+	snprintf(name, sizeof(name), DRV_NAME".%1d", id);
+	i2s->debug = debugfs_create_file(name, S_IRUGO, snd_soc_debugfs_root,
+						i2s, &tegra_i2s_debug_fops);
+}
+
+static void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
+{
+	if (i2s->debug)
+		debugfs_remove(i2s->debug);
+}
+#else
+static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s)
+{
+}
+
+static inline void tegra_i2s_debug_remove(struct tegra_i2s *i2s)
+{
+}
+#endif
+
+static int tegra_i2s_set_fmt(struct snd_soc_dai *dai,
+				unsigned int fmt)
+{
+	struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_MASTER_ENABLE;
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_MASTER_ENABLE;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK | 
+				TEGRA_I2S_CTRL_LRCK_MASK);
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP;
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_R_LOW;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_I2S;
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_RJM;
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_LJM;
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tegra_i2s_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+        struct device *dev = substream->pcm->card->dev;
+	struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+	u32 reg;
+	int ret, sample_size, srate, i2sclock, bitcnt;
+
+	i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_BIT_SIZE_MASK;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_16;
+		sample_size = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_24;
+		sample_size = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_32;
+		sample_size = 32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	srate = params_rate(params);
+
+	/* Final "* 2" required by Tegra hardware */
+	i2sclock = srate * params_channels(params) * sample_size * 2;
+
+	ret = clk_set_rate(i2s->clk_i2s, i2sclock);
+	if (ret) {
+		dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+		return ret;
+	}
+
+	bitcnt = (i2sclock / (2 * srate)) - 1;
+	if (bitcnt < 0 || bitcnt > TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)
+		return -EINVAL;
+	reg = bitcnt << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+
+	if (i2sclock % (2 * srate))
+		reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE;
+
+	tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg);
+
+	tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR,
+		TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS |
+		TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS);
+
+	return 0;
+}
+
+static void tegra_i2s_start_playback(struct tegra_i2s *i2s)
+{
+	i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO1_ENABLE;
+	tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra_i2s_stop_playback(struct tegra_i2s *i2s)
+{
+	i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO1_ENABLE;
+	tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra_i2s_start_capture(struct tegra_i2s *i2s)
+{
+	i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO2_ENABLE;
+	tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static void tegra_i2s_stop_capture(struct tegra_i2s *i2s)
+{
+	i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO2_ENABLE;
+	tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl);
+}
+
+static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+				struct snd_soc_dai *dai)
+{
+	struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		if (!i2s->clk_refs)
+			clk_enable(i2s->clk_i2s);
+		i2s->clk_refs++;
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			tegra_i2s_start_playback(i2s);
+		else
+			tegra_i2s_start_capture(i2s);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			tegra_i2s_stop_playback(i2s);
+		else
+			tegra_i2s_stop_capture(i2s);
+		i2s->clk_refs--;
+		if (!i2s->clk_refs)
+			clk_disable(i2s->clk_i2s);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tegra_i2s_probe(struct snd_soc_dai *dai)
+{
+	struct tegra_i2s * i2s = snd_soc_dai_get_drvdata(dai);
+
+	dai->capture_dma_data = &i2s->capture_dma_data;
+	dai->playback_dma_data = &i2s->playback_dma_data;
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops tegra_i2s_dai_ops = {
+	.set_fmt	= tegra_i2s_set_fmt,
+	.hw_params	= tegra_i2s_hw_params,
+	.trigger	= tegra_i2s_trigger,
+};
+
+struct snd_soc_dai_driver tegra_i2s_dai[] = {
+	{
+		.name = DRV_NAME ".0",
+		.probe = tegra_i2s_probe,
+		.playback = {
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_96000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.capture = {
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_96000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &tegra_i2s_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = DRV_NAME ".1",
+		.probe = tegra_i2s_probe,
+		.playback = {
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_96000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.capture = {
+			.channels_min = 2,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_96000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &tegra_i2s_dai_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev)
+{
+	struct tegra_i2s * i2s;
+	char clk_name[12]; /* tegra-i2s.0 */
+	struct resource *mem, *memregion, *dmareq;
+	int ret;
+
+	if ((pdev->id < 0) ||
+		(pdev->id >= ARRAY_SIZE(tegra_i2s_dai))) {
+		dev_err(&pdev->dev, "ID %d out of range\n", pdev->id);
+		return -EINVAL;
+	}
+
+	/*
+	 * FIXME: Until a codec driver exists for the tegra DAS, hard-code a
+	 * 1:1 mapping between audio controllers and audio ports.
+	 */
+	ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1 + pdev->id,
+					TEGRA_DAS_DAP_SEL_DAC1 + pdev->id);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't set up DAP connection\n");
+		return ret;
+	}
+	ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1 + pdev->id,
+					TEGRA_DAS_DAC_SEL_DAP1 + pdev->id);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't set up DAC connection\n");
+		return ret;
+	}
+
+	i2s = kzalloc(sizeof(struct tegra_i2s), GFP_KERNEL);
+	if (!i2s) {
+		dev_err(&pdev->dev, "Can't allocate tegra_i2s\n");
+		ret = -ENOMEM;
+		goto exit;
+	}
+	dev_set_drvdata(&pdev->dev, i2s);
+
+	snprintf(clk_name, sizeof(clk_name), DRV_NAME ".%d", pdev->id);
+	i2s->clk_i2s = clk_get_sys(clk_name, NULL);
+	if (IS_ERR(i2s->clk_i2s)) {
+		pr_err("Can't retrieve i2s clock\n");
+		ret = PTR_ERR(i2s->clk_i2s);
+		goto err_free;
+	}
+
+	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!mem) {
+		dev_err(&pdev->dev, "No memory resource\n");
+		ret = -ENODEV;
+		goto err_clk_put;
+	}
+
+	dmareq = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!dmareq) {
+		dev_err(&pdev->dev, "No DMA resource\n");
+		ret = -ENODEV;
+		goto err_clk_put;
+	}
+
+	memregion = request_mem_region(mem->start, resource_size(mem),
+					DRV_NAME);
+	if (!memregion) {
+		dev_err(&pdev->dev, "Memory region already claimed\n");
+		ret = -EBUSY;
+		goto err_clk_put;
+	}
+
+	i2s->regs = ioremap(mem->start, resource_size(mem));
+	if (!i2s->regs) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_release;
+	}
+
+	i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2;
+	i2s->capture_dma_data.wrap = 4;
+	i2s->capture_dma_data.width = 32;
+	i2s->capture_dma_data.req_sel = dmareq->start;
+
+	i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1;
+	i2s->playback_dma_data.wrap = 4;
+	i2s->playback_dma_data.width = 32;
+	i2s->playback_dma_data.req_sel = dmareq->start;
+
+	i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED;
+
+	ret = snd_soc_register_dai(&pdev->dev, &tegra_i2s_dai[pdev->id]);
+	if (ret) {
+		dev_err(&pdev->dev, "Could not register DAI: %d\n", ret);
+		ret = -ENOMEM;
+		goto err_unmap;
+	}
+
+	tegra_i2s_debug_add(i2s, pdev->id);
+
+	return 0;
+
+err_unmap:
+	iounmap(i2s->regs);
+err_release:
+	release_mem_region(mem->start, resource_size(mem));
+err_clk_put:
+	clk_put(i2s->clk_i2s);
+err_free:
+	kfree(i2s);
+exit:
+	return ret;
+}
+
+static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev)
+{
+	struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev);
+	struct resource *res;
+
+	snd_soc_unregister_dai(&pdev->dev);
+
+	tegra_i2s_debug_remove(i2s);
+
+	iounmap(i2s->regs);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(res->start, resource_size(res));
+
+	clk_put(i2s->clk_i2s);
+
+	kfree(i2s);
+
+	return 0;
+}
+
+static struct platform_driver tegra_i2s_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = tegra_i2s_platform_probe,
+	.remove = __devexit_p(tegra_i2s_platform_remove),
+};
+
+static int __init snd_tegra_i2s_init(void)
+{
+	return platform_driver_register(&tegra_i2s_driver);
+}
+module_init(snd_tegra_i2s_init);
+
+static void __exit snd_tegra_i2s_exit(void)
+{
+	platform_driver_unregister(&tegra_i2s_driver);
+}
+module_exit(snd_tegra_i2s_exit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra I2S ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_i2s.h b/sound/soc/tegra/tegra_i2s.h
new file mode 100644
index 0000000..2b38a09
--- /dev/null
+++ b/sound/soc/tegra/tegra_i2s.h
@@ -0,0 +1,165 @@
+/*
+ * tegra_i2s.h - Definitions for Tegra I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA_I2S_H__
+#define __TEGRA_I2S_H__
+
+#include "tegra_pcm.h"
+
+/* Register offsets from TEGRA_I2S1_BASE and TEGRA_I2S2_BASE */
+
+#define TEGRA_I2S_CTRL					0x00
+#define TEGRA_I2S_STATUS				0x04
+#define TEGRA_I2S_TIMING				0x08
+#define TEGRA_I2S_FIFO_SCR				0x0c
+#define TEGRA_I2S_PCM_CTRL				0x10
+#define TEGRA_I2S_NW_CTRL				0x14
+#define TEGRA_I2S_TDM_CTRL				0x20
+#define TEGRA_I2S_TDM_TX_RX_CTRL			0x24
+#define TEGRA_I2S_FIFO1					0x40
+#define TEGRA_I2S_FIFO2					0x80
+
+/* Fields in TEGRA_I2S_CTRL */
+
+#define TEGRA_I2S_CTRL_FIFO2_TX_ENABLE			(1 << 30)
+#define TEGRA_I2S_CTRL_FIFO1_ENABLE			(1 << 29)
+#define TEGRA_I2S_CTRL_FIFO2_ENABLE			(1 << 28)
+#define TEGRA_I2S_CTRL_FIFO1_RX_ENABLE			(1 << 27)
+#define TEGRA_I2S_CTRL_FIFO_LPBK_ENABLE			(1 << 26)
+#define TEGRA_I2S_CTRL_MASTER_ENABLE			(1 << 25)
+
+#define TEGRA_I2S_LRCK_LEFT_LOW				0
+#define TEGRA_I2S_LRCK_RIGHT_LOW			1
+
+#define TEGRA_I2S_CTRL_LRCK_SHIFT			24
+#define TEGRA_I2S_CTRL_LRCK_MASK			(1                        << TEGRA_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA_I2S_CTRL_LRCK_L_LOW			(TEGRA_I2S_LRCK_LEFT_LOW  << TEGRA_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA_I2S_CTRL_LRCK_R_LOW			(TEGRA_I2S_LRCK_RIGHT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT)
+
+#define TEGRA_I2S_BIT_FORMAT_I2S			0
+#define TEGRA_I2S_BIT_FORMAT_RJM			1
+#define TEGRA_I2S_BIT_FORMAT_LJM			2
+#define TEGRA_I2S_BIT_FORMAT_DSP			3
+
+#define TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT			10
+#define TEGRA_I2S_CTRL_BIT_FORMAT_MASK			(3                        << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_I2S			(TEGRA_I2S_BIT_FORMAT_I2S << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_RJM			(TEGRA_I2S_BIT_FORMAT_RJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_LJM			(TEGRA_I2S_BIT_FORMAT_LJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_FORMAT_DSP			(TEGRA_I2S_BIT_FORMAT_DSP << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT)
+
+#define TEGRA_I2S_BIT_SIZE_16				0
+#define TEGRA_I2S_BIT_SIZE_20				1
+#define TEGRA_I2S_BIT_SIZE_24				2
+#define TEGRA_I2S_BIT_SIZE_32				3
+
+#define TEGRA_I2S_CTRL_BIT_SIZE_SHIFT			8
+#define TEGRA_I2S_CTRL_BIT_SIZE_MASK			(3                     << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_16			(TEGRA_I2S_BIT_SIZE_16 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_20			(TEGRA_I2S_BIT_SIZE_20 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_24			(TEGRA_I2S_BIT_SIZE_24 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA_I2S_CTRL_BIT_SIZE_32			(TEGRA_I2S_BIT_SIZE_32 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT)
+
+#define TEGRA_I2S_FIFO_16_LSB				0
+#define TEGRA_I2S_FIFO_20_LSB				1
+#define TEGRA_I2S_FIFO_24_LSB				2
+#define TEGRA_I2S_FIFO_32				3
+#define TEGRA_I2S_FIFO_PACKED				7
+
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT		4
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_MASK			(7                     << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_16_LSB		(TEGRA_I2S_FIFO_16_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_20_LSB		(TEGRA_I2S_FIFO_20_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_24_LSB		(TEGRA_I2S_FIFO_24_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_32			(TEGRA_I2S_FIFO_32     << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+#define TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED		(TEGRA_I2S_FIFO_PACKED << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT)
+
+#define TEGRA_I2S_CTRL_IE_FIFO1_ERR			(1 << 3)
+#define TEGRA_I2S_CTRL_IE_FIFO2_ERR			(1 << 2)
+#define TEGRA_I2S_CTRL_QE_FIFO1				(1 << 1)
+#define TEGRA_I2S_CTRL_QE_FIFO2				(1 << 0)
+
+/* Fields in TEGRA_I2S_STATUS */
+
+#define TEGRA_I2S_STATUS_FIFO1_RDY			(1 << 31)
+#define TEGRA_I2S_STATUS_FIFO2_RDY			(1 << 30)
+#define TEGRA_I2S_STATUS_FIFO1_BSY			(1 << 29)
+#define TEGRA_I2S_STATUS_FIFO2_BSY			(1 << 28)
+#define TEGRA_I2S_STATUS_FIFO1_ERR			(1 << 3)
+#define TEGRA_I2S_STATUS_FIFO2_ERR			(1 << 2)
+#define TEGRA_I2S_STATUS_QS_FIFO1			(1 << 1)
+#define TEGRA_I2S_STATUS_QS_FIFO2			(1 << 0)
+
+/* Fields in TEGRA_I2S_TIMING */
+
+#define TEGRA_I2S_TIMING_NON_SYM_ENABLE			(1 << 12)
+#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT	0
+#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US	0x7fff
+#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK		(TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
+
+/* Fields in TEGRA_I2S_FIFO_SCR */
+
+#define TEGRA_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT	24
+#define TEGRA_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT	16
+#define TEGRA_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK	0x3f
+
+#define TEGRA_I2S_FIFO_SCR_FIFO2_CLR			(1 << 12)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_CLR			(1 << 8)
+
+#define TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT			0
+#define TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS		1
+#define TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS		2
+#define TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS		3
+
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT		4
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK		(3 << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT	(TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT     << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS   << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS  << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT)
+
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT		0
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK		(3 << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT	(TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT     << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS   << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS  << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS	(TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT)
+
+struct tegra_i2s {
+	struct clk *clk_i2s;
+	int clk_refs;
+	struct tegra_pcm_dma_params capture_dma_data;
+	struct tegra_pcm_dma_params playback_dma_data;
+	void __iomem *regs;
+	struct dentry *debug;
+	u32 reg_ctrl;
+};
+
+#endif
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
new file mode 100644
index 0000000..663ea9f
--- /dev/null
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -0,0 +1,401 @@
+/*
+ * tegra_pcm.c - Tegra PCM driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ * Vijay Mali <vmali@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra_pcm.h"
+
+static const struct snd_pcm_hardware tegra_pcm_hardware = {
+	.info			= SNDRV_PCM_INFO_MMAP |
+				  SNDRV_PCM_INFO_MMAP_VALID |
+				  SNDRV_PCM_INFO_PAUSE |
+				  SNDRV_PCM_INFO_RESUME |
+				  SNDRV_PCM_INFO_INTERLEAVED,
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.channels_min		= 2,
+	.channels_max		= 2,
+	.period_bytes_min	= 1024,
+	.period_bytes_max	= PAGE_SIZE,
+	.periods_min		= 2,
+	.periods_max		= 8,
+	.buffer_bytes_max	= PAGE_SIZE * 8,
+	.fifo_size		= 4,
+};
+
+static void tegra_pcm_queue_dma(struct tegra_runtime_data *prtd)
+{
+	struct snd_pcm_substream *substream = prtd->substream;
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	struct tegra_dma_req *dma_req;
+	unsigned long addr;
+
+	dma_req = &prtd->dma_req[prtd->dma_req_idx];
+	prtd->dma_req_idx = 1 - prtd->dma_req_idx;
+
+	addr = buf->addr + prtd->dma_pos;
+	prtd->dma_pos += dma_req->size;
+	if (prtd->dma_pos >= prtd->dma_pos_end)
+		prtd->dma_pos = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dma_req->source_addr = addr;
+	else
+		dma_req->dest_addr = addr;
+
+	tegra_dma_enqueue_req(prtd->dma_chan, dma_req);
+}
+
+static void dma_complete_callback(struct tegra_dma_req *req)
+{
+	struct tegra_runtime_data *prtd = (struct tegra_runtime_data *)req->dev;
+	struct snd_pcm_substream *substream = prtd->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	spin_lock(&prtd->lock);
+
+	if (!prtd->running) {
+		spin_unlock(&prtd->lock);
+		return;
+	}
+
+	if (++prtd->period_index >= runtime->periods)
+		prtd->period_index = 0;
+
+	tegra_pcm_queue_dma(prtd);
+
+	spin_unlock(&prtd->lock);
+
+	snd_pcm_period_elapsed(substream);
+}
+
+static void setup_dma_tx_request(struct tegra_dma_req *req,
+					struct tegra_pcm_dma_params * dmap)
+{
+	req->complete = dma_complete_callback;
+	req->to_memory = false;
+	req->dest_addr = dmap->addr;
+	req->dest_wrap = dmap->wrap;
+	req->source_bus_width = 32;
+	req->source_wrap = 0;
+	req->dest_bus_width = dmap->width;
+	req->req_sel = dmap->req_sel;
+}
+
+static void setup_dma_rx_request(struct tegra_dma_req *req,
+					struct tegra_pcm_dma_params * dmap)
+{
+	req->complete = dma_complete_callback;
+	req->to_memory = true;
+	req->source_addr = dmap->addr;
+	req->dest_wrap = 0;
+	req->source_bus_width = dmap->width;
+	req->source_wrap = dmap->wrap;
+	req->dest_bus_width = 32;
+	req->req_sel = dmap->req_sel;
+}
+
+static int tegra_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct tegra_runtime_data *prtd;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct tegra_pcm_dma_params * dmap;
+	int ret = 0;
+
+	prtd = kzalloc(sizeof(struct tegra_runtime_data), GFP_KERNEL);
+	if (prtd == NULL)
+		return -ENOMEM;
+
+	runtime->private_data = prtd;
+	prtd->substream = substream;
+
+	spin_lock_init(&prtd->lock);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+		setup_dma_tx_request(&prtd->dma_req[0], dmap);
+		setup_dma_tx_request(&prtd->dma_req[1], dmap);
+	} else {
+		dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+		setup_dma_rx_request(&prtd->dma_req[0], dmap);
+		setup_dma_rx_request(&prtd->dma_req[1], dmap);
+	}
+
+	prtd->dma_req[0].dev = prtd;
+	prtd->dma_req[1].dev = prtd;
+
+	prtd->dma_chan = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
+	if (IS_ERR(prtd->dma_chan)) {
+		ret = PTR_ERR(prtd->dma_chan);
+		goto err;
+	}
+
+	/* Set HW params now that initialization is complete */
+	snd_soc_set_runtime_hwparams(substream, &tegra_pcm_hardware);
+
+	/* Ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(runtime,
+						SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		goto err;
+
+	return 0;
+
+err:
+	if (prtd->dma_chan) {
+		tegra_dma_free_channel(prtd->dma_chan);
+	}
+
+	kfree(prtd);
+
+	return ret;
+}
+
+static int tegra_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct tegra_runtime_data *prtd = runtime->private_data;
+
+	tegra_dma_free_channel(prtd->dma_chan);
+
+	kfree(prtd);
+
+	return 0;
+}
+
+static int tegra_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct tegra_runtime_data *prtd = runtime->private_data;
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	prtd->dma_req[0].size = params_period_bytes(params);
+	prtd->dma_req[1].size = prtd->dma_req[0].size;
+
+	return 0;
+}
+
+static int tegra_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+	snd_pcm_set_runtime_buffer(substream, NULL);
+
+	return 0;
+}
+
+static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct tegra_runtime_data *prtd = runtime->private_data;
+	unsigned long flags;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		prtd->dma_pos = 0;
+		prtd->dma_pos_end = frames_to_bytes(runtime, runtime->periods * runtime->period_size);
+		prtd->period_index = 0;
+		prtd->dma_req_idx = 0;
+		/* Fall-through */
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		spin_lock_irqsave(&prtd->lock, flags);
+		prtd->running = 1;
+		spin_unlock_irqrestore(&prtd->lock, flags);
+		tegra_pcm_queue_dma(prtd);
+		tegra_pcm_queue_dma(prtd);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		spin_lock_irqsave(&prtd->lock, flags);
+		prtd->running = 0;
+		spin_unlock_irqrestore(&prtd->lock, flags);
+		tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[0]);
+		tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[1]);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t tegra_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct tegra_runtime_data *prtd = runtime->private_data;
+
+	return prtd->period_index * runtime->period_size;
+}
+
+
+static int tegra_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);
+}
+
+static struct snd_pcm_ops tegra_pcm_ops = {
+	.open		= tegra_pcm_open,
+	.close		= tegra_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= tegra_pcm_hw_params,
+	.hw_free	= tegra_pcm_hw_free,
+	.trigger	= tegra_pcm_trigger,
+	.pointer	= tegra_pcm_pointer,
+	.mmap		= tegra_pcm_mmap,
+};
+
+static int tegra_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 = tegra_pcm_hardware.buffer_bytes_max;
+
+	buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+						&buf->addr, GFP_KERNEL);
+	if (!buf->area)
+		return -ENOMEM;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = pcm->card->dev;
+	buf->private_data = NULL;
+	buf->bytes = size;
+
+	return 0;
+}
+
+static void tegra_pcm_deallocate_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;
+
+	if (!buf->area)
+		return;
+
+	dma_free_writecombine(pcm->card->dev, buf->bytes,
+				buf->area, buf->addr);
+	buf->area = NULL;
+}
+
+static u64 tegra_dma_mask = DMA_BIT_MASK(32);
+
+static int tegra_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 = &tegra_dma_mask;
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = 0xffffffff;
+
+	if (dai->driver->playback.channels_min) {
+		ret = tegra_pcm_preallocate_dma_buffer(pcm,
+						SNDRV_PCM_STREAM_PLAYBACK);
+		if (ret)
+			goto err;
+	}
+
+	if (dai->driver->capture.channels_min) {
+		ret = tegra_pcm_preallocate_dma_buffer(pcm,
+						SNDRV_PCM_STREAM_CAPTURE);
+		if (ret)
+			goto err_free_play;
+	}
+
+	return 0;
+
+err_free_play:
+	tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+err:
+	return ret;
+}
+
+static void tegra_pcm_free(struct snd_pcm *pcm)
+{
+	tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
+	tegra_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+struct snd_soc_platform_driver tegra_pcm_platform = {
+	.ops		= &tegra_pcm_ops,
+	.pcm_new	= tegra_pcm_new,
+	.pcm_free	= tegra_pcm_free,
+};
+
+static int __devinit tegra_pcm_platform_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_platform(&pdev->dev, &tegra_pcm_platform);
+}
+
+static int __devexit tegra_pcm_platform_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver tegra_pcm_driver = {
+	.driver = {
+		.name = "tegra-pcm-audio",
+		.owner = THIS_MODULE,
+	},
+	.probe = tegra_pcm_platform_probe,
+	.remove = __devexit_p(tegra_pcm_platform_remove),
+};
+
+static int __init snd_tegra_pcm_init(void)
+{
+	return platform_driver_register(&tegra_pcm_driver);
+}
+module_init(snd_tegra_pcm_init);
+
+static void __exit snd_tegra_pcm_exit(void)
+{
+	platform_driver_unregister(&tegra_pcm_driver);
+}
+module_exit(snd_tegra_pcm_exit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra PCM ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
new file mode 100644
index 0000000..dbb9033
--- /dev/null
+++ b/sound/soc/tegra/tegra_pcm.h
@@ -0,0 +1,55 @@
+/*
+ * tegra_pcm.h - Definitions for Tegra PCM driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (C) 2010 - NVIDIA, Inc.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA_PCM_H__
+#define __TEGRA_PCM_H__
+
+#include <mach/dma.h>
+
+struct tegra_pcm_dma_params {
+	unsigned long addr;
+	unsigned long wrap;
+	unsigned long width;
+	unsigned long req_sel;
+};
+
+struct tegra_runtime_data {
+	struct snd_pcm_substream *substream;
+	spinlock_t lock;
+	int running;
+	int dma_pos;
+	int dma_pos_end;
+	int period_index;
+	int dma_req_idx;
+	struct tegra_dma_req dma_req[2];
+	struct tegra_dma_channel *dma_chan;
+};
+
+#endif