Merge branch 'for-2.6.32' into for-2.6.33
diff --git a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
index 07659da..abf2fbc 100644
--- a/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
+++ b/arch/arm/plat-s3c/include/plat/regs-s3c2412-iis.h
@@ -67,6 +67,8 @@
 #define S3C2412_IISMOD_BCLK_MASK	(3 << 1)
 #define S3C2412_IISMOD_8BIT		(1 << 0)
 
+#define S3C64XX_IISMOD_CDCLKCON		(1 << 12)
+
 #define S3C2412_IISPSR_PSREN		(1 << 15)
 
 #define S3C2412_IISFIC_TXFLUSH		(1 << 15)
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 97ca9af..e0c7fa7 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -106,7 +106,7 @@
 	int div_id, int div);
 
 int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-	int pll_id, unsigned int freq_in, unsigned int freq_out);
+	int pll_id, int source, unsigned int freq_in, unsigned int freq_out);
 
 /* Digital Audio interface formatting */
 int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt);
@@ -114,6 +114,10 @@
 int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
 	unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width);
 
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+	unsigned int tx_num, unsigned int *tx_slot,
+	unsigned int rx_num, unsigned int *rx_slot);
+
 int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
 
 /* Digital Audio Interface mute */
@@ -136,8 +140,8 @@
 	 */
 	int (*set_sysclk)(struct snd_soc_dai *dai,
 		int clk_id, unsigned int freq, int dir);
-	int (*set_pll)(struct snd_soc_dai *dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out);
+	int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
+		unsigned int freq_in, unsigned int freq_out);
 	int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
 
 	/*
@@ -148,6 +152,9 @@
 	int (*set_tdm_slot)(struct snd_soc_dai *dai,
 		unsigned int tx_mask, unsigned int rx_mask,
 		int slots, int slot_width);
+	int (*set_channel_map)(struct snd_soc_dai *dai,
+		unsigned int tx_num, unsigned int *tx_slot,
+		unsigned int rx_num, unsigned int *rx_slot);
 	int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
 
 	/*
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index c1410e3..67224db 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -333,6 +333,10 @@
 	const char *sink;
 	const char *control;
 	const char *source;
+
+	/* Note: currently only supported for links where source is a supply */
+	int (*connected)(struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink);
 };
 
 /* dapm audio path between two widgets */
@@ -349,6 +353,9 @@
 	u32 connect:1;	/* source and sink widgets are connected */
 	u32 walked:1;	/* path has been walked */
 
+	int (*connected)(struct snd_soc_dapm_widget *source,
+			 struct snd_soc_dapm_widget *sink);
+
 	struct list_head list_source;
 	struct list_head list_sink;
 	struct list_head list;
diff --git a/sound/soc/atmel/playpaq_wm8510.c b/sound/soc/atmel/playpaq_wm8510.c
index 9eb610c..9df4c68 100644
--- a/sound/soc/atmel/playpaq_wm8510.c
+++ b/sound/soc/atmel/playpaq_wm8510.c
@@ -268,7 +268,7 @@
 #endif /* CONFIG_SND_AT32_SOC_PLAYPAQ_SLAVE */
 
 
-	ret = snd_soc_dai_set_pll(codec_dai, 0,
+	ret = snd_soc_dai_set_pll(codec_dai, 0, 0,
 					 clk_get_rate(CODEC_CLK), pll_out);
 	if (ret < 0) {
 		pr_warning("playpaq_wm8510: Failed to set CODEC DAI PLL (%d)\n",
diff --git a/sound/soc/blackfin/bf5xx-ad1836.c b/sound/soc/blackfin/bf5xx-ad1836.c
index cd361e3..0f45a3f 100644
--- a/sound/soc/blackfin/bf5xx-ad1836.c
+++ b/sound/soc/blackfin/bf5xx-ad1836.c
@@ -52,6 +52,7 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	unsigned int channel_map[] = {0, 4, 1, 5, 2, 6, 3, 7};
 	int ret = 0;
 	/* set cpu DAI configuration */
 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@@ -65,6 +66,12 @@
 	if (ret < 0)
 		return ret;
 
+	/* set cpu DAI channel mapping */
+	ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+		channel_map, ARRAY_SIZE(channel_map), channel_map);
+	if (ret < 0)
+		return ret;
+
 	return 0;
 }
 
diff --git a/sound/soc/blackfin/bf5xx-ad1938.c b/sound/soc/blackfin/bf5xx-ad1938.c
index 08269e9..2ef1e50 100644
--- a/sound/soc/blackfin/bf5xx-ad1938.c
+++ b/sound/soc/blackfin/bf5xx-ad1938.c
@@ -61,6 +61,7 @@
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
 	struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+	unsigned int channel_map[] = {0, 1, 2, 3, 4, 5, 6, 7};
 	int ret = 0;
 	/* set cpu DAI configuration */
 	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
@@ -75,7 +76,13 @@
 		return ret;
 
 	/* set codec DAI slots, 8 channels, all channels are enabled */
-	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 8);
+	ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xFF, 0xFF, 8, 32);
+	if (ret < 0)
+		return ret;
+
+	/* set cpu DAI channel mapping */
+	ret = snd_soc_dai_set_channel_map(cpu_dai, ARRAY_SIZE(channel_map),
+		channel_map, ARRAY_SIZE(channel_map), channel_map);
 	if (ret < 0)
 		return ret;
 
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c
index ccb5e82..a8c73cb 100644
--- a/sound/soc/blackfin/bf5xx-tdm-pcm.c
+++ b/sound/soc/blackfin/bf5xx-tdm-pcm.c
@@ -43,7 +43,7 @@
 #include "bf5xx-tdm.h"
 #include "bf5xx-sport.h"
 
-#define PCM_BUFFER_MAX  0x10000
+#define PCM_BUFFER_MAX  0x8000
 #define FRAGMENT_SIZE_MIN  (4*1024)
 #define FRAGMENTS_MIN  2
 #define FRAGMENTS_MAX  32
@@ -177,6 +177,9 @@
 static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
 	snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count)
 {
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct sport_device *sport = runtime->private_data;
+	struct bf5xx_tdm_port *tdm_port = sport->private_data;
 	unsigned int *src;
 	unsigned int *dst;
 	int i;
@@ -188,7 +191,7 @@
 		dst += pos * 8;
 		while (count--) {
 			for (i = 0; i < substream->runtime->channels; i++)
-				*(dst + i) = *src++;
+				*(dst + tdm_port->tx_map[i]) = *src++;
 			dst += 8;
 		}
 	} else {
@@ -198,7 +201,7 @@
 		src += pos * 8;
 		while (count--) {
 			for (i = 0; i < substream->runtime->channels; i++)
-				*dst++ = *(src+i);
+				*dst++ = *(src + tdm_port->rx_map[i]);
 			src += 8;
 		}
 	}
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c
index 3096bad..600987d 100644
--- a/sound/soc/blackfin/bf5xx-tdm.c
+++ b/sound/soc/blackfin/bf5xx-tdm.c
@@ -46,14 +46,6 @@
 #include "bf5xx-sport.h"
 #include "bf5xx-tdm.h"
 
-struct bf5xx_tdm_port {
-	u16 tcr1;
-	u16 rcr1;
-	u16 tcr2;
-	u16 rcr2;
-	int configured;
-};
-
 static struct bf5xx_tdm_port bf5xx_tdm;
 static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
 
@@ -181,6 +173,40 @@
 		bf5xx_tdm.configured = 0;
 }
 
+static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai,
+		unsigned int tx_num, unsigned int *tx_slot,
+		unsigned int rx_num, unsigned int *rx_slot)
+{
+	int i;
+	unsigned int slot;
+	unsigned int tx_mapped = 0, rx_mapped = 0;
+
+	if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) ||
+			(rx_num > BFIN_TDM_DAI_MAX_SLOTS))
+		return -EINVAL;
+
+	for (i = 0; i < tx_num; i++) {
+		slot = tx_slot[i];
+		if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+				(!(tx_mapped & (1 << slot)))) {
+			bf5xx_tdm.tx_map[i] = slot;
+			tx_mapped |= 1 << slot;
+		} else
+			return -EINVAL;
+	}
+	for (i = 0; i < rx_num; i++) {
+		slot = rx_slot[i];
+		if ((slot < BFIN_TDM_DAI_MAX_SLOTS) &&
+				(!(rx_mapped & (1 << slot)))) {
+			bf5xx_tdm.rx_map[i] = slot;
+			rx_mapped |= 1 << slot;
+		} else
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
 #ifdef CONFIG_PM
 static int bf5xx_tdm_suspend(struct snd_soc_dai *dai)
 {
@@ -235,6 +261,7 @@
 	.hw_params      = bf5xx_tdm_hw_params,
 	.set_fmt        = bf5xx_tdm_set_dai_fmt,
 	.shutdown       = bf5xx_tdm_shutdown,
+	.set_channel_map   = bf5xx_tdm_set_channel_map,
 };
 
 struct snd_soc_dai bf5xx_tdm_dai = {
@@ -300,6 +327,8 @@
 		pr_err("Failed to register DAI: %d\n", ret);
 		goto sport_config_err;
 	}
+
+	sport_handle->private_data = &bf5xx_tdm;
 	return 0;
 
 sport_config_err:
diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h
index 618ec3d..04189a1 100644
--- a/sound/soc/blackfin/bf5xx-tdm.h
+++ b/sound/soc/blackfin/bf5xx-tdm.h
@@ -9,6 +9,17 @@
 #ifndef _BF5XX_TDM_H
 #define _BF5XX_TDM_H
 
+#define BFIN_TDM_DAI_MAX_SLOTS 8
+struct bf5xx_tdm_port {
+	u16 tcr1;
+	u16 rcr1;
+	u16 tcr2;
+	u16 rcr2;
+	unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS];
+	unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS];
+	int configured;
+};
+
 extern struct snd_soc_dai bf5xx_tdm_dai;
 
 #endif
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 0edca93..a2bb659 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -19,6 +19,7 @@
 	select SND_SOC_AK4104 if SPI_MASTER
 	select SND_SOC_AK4535 if I2C
 	select SND_SOC_AK4642 if I2C
+	select SND_SOC_AK4671 if I2C
 	select SND_SOC_CS4270 if I2C
 	select SND_SOC_MAX9877 if I2C
 	select SND_SOC_PCM3008
@@ -96,6 +97,9 @@
 config SND_SOC_AK4642
 	tristate
 
+config SND_SOC_AK4671
+	tristate
+
 # Cirrus Logic CS4270 Codec
 config SND_SOC_CS4270
 	tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index fb4af28..13f7b4f 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -6,6 +6,7 @@
 snd-soc-ak4104-objs := ak4104.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-ak4642-objs := ak4642.o
+snd-soc-ak4671-objs := ak4671.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-l3-objs := l3.o
@@ -56,6 +57,7 @@
 obj-$(CONFIG_SND_SOC_AK4104)	+= snd-soc-ak4104.o
 obj-$(CONFIG_SND_SOC_AK4535)	+= snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_AK4642)	+= snd-soc-ak4642.o
+obj-$(CONFIG_SND_SOC_AK4671)	+= snd-soc-ak4671.o
 obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
new file mode 100644
index 0000000..b61214d
--- /dev/null
+++ b/sound/soc/codecs/ak4671.c
@@ -0,0 +1,825 @@
+/*
+ * ak4671.c  --  audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "ak4671.h"
+
+static struct snd_soc_codec *ak4671_codec;
+
+/* codec private data */
+struct ak4671_priv {
+	struct snd_soc_codec codec;
+	u8 reg_cache[AK4671_CACHEREGNUM];
+};
+
+/* ak4671 register cache & default register settings */
+static const u8 ak4671_reg[AK4671_CACHEREGNUM] = {
+	0x00,	/* AK4671_AD_DA_POWER_MANAGEMENT	(0x00)	*/
+	0xf6,	/* AK4671_PLL_MODE_SELECT0		(0x01)	*/
+	0x00,	/* AK4671_PLL_MODE_SELECT1		(0x02)	*/
+	0x02,	/* AK4671_FORMAT_SELECT			(0x03)	*/
+	0x00,	/* AK4671_MIC_SIGNAL_SELECT		(0x04)	*/
+	0x55,	/* AK4671_MIC_AMP_GAIN			(0x05)	*/
+	0x00,	/* AK4671_MIXING_POWER_MANAGEMENT0	(0x06)	*/
+	0x00,	/* AK4671_MIXING_POWER_MANAGEMENT1	(0x07)	*/
+	0xb5,	/* AK4671_OUTPUT_VOLUME_CONTROL		(0x08)	*/
+	0x00,	/* AK4671_LOUT1_SIGNAL_SELECT		(0x09)	*/
+	0x00,	/* AK4671_ROUT1_SIGNAL_SELECT		(0x0a)	*/
+	0x00,	/* AK4671_LOUT2_SIGNAL_SELECT		(0x0b)	*/
+	0x00,	/* AK4671_ROUT2_SIGNAL_SELECT		(0x0c)	*/
+	0x00,	/* AK4671_LOUT3_SIGNAL_SELECT		(0x0d)	*/
+	0x00,	/* AK4671_ROUT3_SIGNAL_SELECT		(0x0e)	*/
+	0x00,	/* AK4671_LOUT1_POWER_MANAGERMENT	(0x0f)	*/
+	0x00,	/* AK4671_LOUT2_POWER_MANAGERMENT	(0x10)	*/
+	0x80,	/* AK4671_LOUT3_POWER_MANAGERMENT	(0x11)	*/
+	0x91,	/* AK4671_LCH_INPUT_VOLUME_CONTROL	(0x12)	*/
+	0x91,	/* AK4671_RCH_INPUT_VOLUME_CONTROL	(0x13)	*/
+	0xe1,	/* AK4671_ALC_REFERENCE_SELECT		(0x14)	*/
+	0x00,	/* AK4671_DIGITAL_MIXING_CONTROL	(0x15)	*/
+	0x00,	/* AK4671_ALC_TIMER_SELECT		(0x16)	*/
+	0x00,	/* AK4671_ALC_MODE_CONTROL		(0x17)	*/
+	0x02,	/* AK4671_MODE_CONTROL1			(0x18)	*/
+	0x01,	/* AK4671_MODE_CONTROL2			(0x19)	*/
+	0x18,	/* AK4671_LCH_OUTPUT_VOLUME_CONTROL	(0x1a)	*/
+	0x18,	/* AK4671_RCH_OUTPUT_VOLUME_CONTROL	(0x1b)	*/
+	0x00,	/* AK4671_SIDETONE_A_CONTROL		(0x1c)	*/
+	0x02,	/* AK4671_DIGITAL_FILTER_SELECT		(0x1d)	*/
+	0x00,	/* AK4671_FIL3_COEFFICIENT0		(0x1e)	*/
+	0x00,	/* AK4671_FIL3_COEFFICIENT1		(0x1f)	*/
+	0x00,	/* AK4671_FIL3_COEFFICIENT2		(0x20)	*/
+	0x00,	/* AK4671_FIL3_COEFFICIENT3		(0x21)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT0		(0x22)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT1		(0x23)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT2		(0x24)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT3		(0x25)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT4		(0x26)	*/
+	0x00,	/* AK4671_EQ_COEFFICIENT5		(0x27)	*/
+	0xa9,	/* AK4671_FIL1_COEFFICIENT0		(0x28)	*/
+	0x1f,	/* AK4671_FIL1_COEFFICIENT1		(0x29)	*/
+	0xad,	/* AK4671_FIL1_COEFFICIENT2		(0x2a)	*/
+	0x20,	/* AK4671_FIL1_COEFFICIENT3		(0x2b)	*/
+	0x00,	/* AK4671_FIL2_COEFFICIENT0		(0x2c)	*/
+	0x00,	/* AK4671_FIL2_COEFFICIENT1		(0x2d)	*/
+	0x00,	/* AK4671_FIL2_COEFFICIENT2		(0x2e)	*/
+	0x00,	/* AK4671_FIL2_COEFFICIENT3		(0x2f)	*/
+	0x00,	/* AK4671_DIGITAL_FILTER_SELECT2	(0x30)	*/
+	0x00,	/* this register not used			*/
+	0x00,	/* AK4671_E1_COEFFICIENT0		(0x32)	*/
+	0x00,	/* AK4671_E1_COEFFICIENT1		(0x33)	*/
+	0x00,	/* AK4671_E1_COEFFICIENT2		(0x34)	*/
+	0x00,	/* AK4671_E1_COEFFICIENT3		(0x35)	*/
+	0x00,	/* AK4671_E1_COEFFICIENT4		(0x36)	*/
+	0x00,	/* AK4671_E1_COEFFICIENT5		(0x37)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT0		(0x38)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT1		(0x39)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT2		(0x3a)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT3		(0x3b)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT4		(0x3c)	*/
+	0x00,	/* AK4671_E2_COEFFICIENT5		(0x3d)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT0		(0x3e)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT1		(0x3f)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT2		(0x40)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT3		(0x41)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT4		(0x42)	*/
+	0x00,	/* AK4671_E3_COEFFICIENT5		(0x43)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT0		(0x44)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT1		(0x45)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT2		(0x46)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT3		(0x47)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT4		(0x48)	*/
+	0x00,	/* AK4671_E4_COEFFICIENT5		(0x49)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT0		(0x4a)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT1		(0x4b)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT2		(0x4c)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT3		(0x4d)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT4		(0x4e)	*/
+	0x00,	/* AK4671_E5_COEFFICIENT5		(0x4f)	*/
+	0x88,	/* AK4671_EQ_CONTROL_250HZ_100HZ	(0x50)	*/
+	0x88,	/* AK4671_EQ_CONTROL_3500HZ_1KHZ	(0x51)	*/
+	0x08,	/* AK4671_EQ_CONTRO_10KHZ		(0x52)	*/
+	0x00,	/* AK4671_PCM_IF_CONTROL0		(0x53)	*/
+	0x00,	/* AK4671_PCM_IF_CONTROL1		(0x54)	*/
+	0x00,	/* AK4671_PCM_IF_CONTROL2		(0x55)	*/
+	0x18,	/* AK4671_DIGITAL_VOLUME_B_CONTROL	(0x56)	*/
+	0x18,	/* AK4671_DIGITAL_VOLUME_C_CONTROL	(0x57)	*/
+	0x00,	/* AK4671_SIDETONE_VOLUME_CONTROL	(0x58)	*/
+	0x00,	/* AK4671_DIGITAL_MIXING_CONTROL2	(0x59)	*/
+	0x00,	/* AK4671_SAR_ADC_CONTROL		(0x5a)	*/
+};
+
+/*
+ * LOUT1/ROUT1 output volume control:
+ * from -24 to 6 dB in 6 dB steps (mute instead of -30 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out1_tlv, -3000, 600, 1);
+
+/*
+ * LOUT2/ROUT2 output volume control:
+ * from -33 to 6 dB in 3 dB steps (mute instead of -33 dB)
+ */
+static DECLARE_TLV_DB_SCALE(out2_tlv, -3300, 300, 1);
+
+/*
+ * LOUT3/ROUT3 output volume control:
+ * from -6 to 3 dB in 3 dB steps
+ */
+static DECLARE_TLV_DB_SCALE(out3_tlv, -600, 300, 0);
+
+/*
+ * Mic amp gain control:
+ * from -15 to 30 dB in 3 dB steps
+ * REVISIT: The actual min value(0x01) is -12 dB and the reg value 0x00 is not
+ * available
+ */
+static DECLARE_TLV_DB_SCALE(mic_amp_tlv, -1500, 300, 0);
+
+static const struct snd_kcontrol_new ak4671_snd_controls[] = {
+	/* Common playback gain controls */
+	SOC_SINGLE_TLV("Line Output1 Playback Volume",
+			AK4671_OUTPUT_VOLUME_CONTROL, 0, 0x6, 0, out1_tlv),
+	SOC_SINGLE_TLV("Headphone Output2 Playback Volume",
+			AK4671_OUTPUT_VOLUME_CONTROL, 4, 0xd, 0, out2_tlv),
+	SOC_SINGLE_TLV("Line Output3 Playback Volume",
+			AK4671_LOUT3_POWER_MANAGERMENT, 6, 0x3, 0, out3_tlv),
+
+	/* Common capture gain controls */
+	SOC_DOUBLE_TLV("Mic Amp Capture Volume",
+			AK4671_MIC_AMP_GAIN, 0, 4, 0xf, 0, mic_amp_tlv),
+};
+
+/* event handlers */
+static int ak4671_out2_event(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	u8 reg;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+		reg |= AK4671_MUTEN;
+		snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		reg = snd_soc_read(codec, AK4671_LOUT2_POWER_MANAGERMENT);
+		reg &= ~AK4671_MUTEN;
+		snd_soc_write(codec, AK4671_LOUT2_POWER_MANAGERMENT, reg);
+		break;
+	}
+
+	return 0;
+}
+
+/* Output Mixers */
+static const struct snd_kcontrol_new ak4671_lout1_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACL", AK4671_LOUT1_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("LINL1", AK4671_LOUT1_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("LINL2", AK4671_LOUT1_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("LINL3", AK4671_LOUT1_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("LINL4", AK4671_LOUT1_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPL", AK4671_LOUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout1_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACR", AK4671_ROUT1_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("RINR1", AK4671_ROUT1_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("RINR2", AK4671_ROUT1_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("RINR3", AK4671_ROUT1_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("RINR4", AK4671_ROUT1_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPR", AK4671_ROUT1_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout2_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACHL", AK4671_LOUT2_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("LINH1", AK4671_LOUT2_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("LINH2", AK4671_LOUT2_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("LINH3", AK4671_LOUT2_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("LINH4", AK4671_LOUT2_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPHL", AK4671_LOUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout2_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACHR", AK4671_ROUT2_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("RINH1", AK4671_ROUT2_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("RINH2", AK4671_ROUT2_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("RINH3", AK4671_ROUT2_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("RINH4", AK4671_ROUT2_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPHR", AK4671_ROUT2_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_lout3_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACSL", AK4671_LOUT3_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("LINS1", AK4671_LOUT3_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("LINS2", AK4671_LOUT3_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("LINS3", AK4671_LOUT3_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("LINS4", AK4671_LOUT3_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPSL", AK4671_LOUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new ak4671_rout3_mixer_controls[] = {
+	SOC_DAPM_SINGLE("DACSR", AK4671_ROUT3_SIGNAL_SELECT, 0, 1, 0),
+	SOC_DAPM_SINGLE("RINS1", AK4671_ROUT3_SIGNAL_SELECT, 1, 1, 0),
+	SOC_DAPM_SINGLE("RINS2", AK4671_ROUT3_SIGNAL_SELECT, 2, 1, 0),
+	SOC_DAPM_SINGLE("RINS3", AK4671_ROUT3_SIGNAL_SELECT, 3, 1, 0),
+	SOC_DAPM_SINGLE("RINS4", AK4671_ROUT3_SIGNAL_SELECT, 4, 1, 0),
+	SOC_DAPM_SINGLE("LOOPSR", AK4671_ROUT3_SIGNAL_SELECT, 5, 1, 0),
+};
+
+/* Input MUXs */
+static const char *ak4671_lin_mux_texts[] =
+		{"LIN1", "LIN2", "LIN3", "LIN4"};
+static const struct soc_enum ak4671_lin_mux_enum =
+	SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 0,
+			ARRAY_SIZE(ak4671_lin_mux_texts),
+			ak4671_lin_mux_texts);
+static const struct snd_kcontrol_new ak4671_lin_mux_control =
+	SOC_DAPM_ENUM("Route", ak4671_lin_mux_enum);
+
+static const char *ak4671_rin_mux_texts[] =
+		{"RIN1", "RIN2", "RIN3", "RIN4"};
+static const struct soc_enum ak4671_rin_mux_enum =
+	SOC_ENUM_SINGLE(AK4671_MIC_SIGNAL_SELECT, 2,
+			ARRAY_SIZE(ak4671_rin_mux_texts),
+			ak4671_rin_mux_texts);
+static const struct snd_kcontrol_new ak4671_rin_mux_control =
+	SOC_DAPM_ENUM("Route", ak4671_rin_mux_enum);
+
+static const struct snd_soc_dapm_widget ak4671_dapm_widgets[] = {
+	/* Inputs */
+	SND_SOC_DAPM_INPUT("LIN1"),
+	SND_SOC_DAPM_INPUT("RIN1"),
+	SND_SOC_DAPM_INPUT("LIN2"),
+	SND_SOC_DAPM_INPUT("RIN2"),
+	SND_SOC_DAPM_INPUT("LIN3"),
+	SND_SOC_DAPM_INPUT("RIN3"),
+	SND_SOC_DAPM_INPUT("LIN4"),
+	SND_SOC_DAPM_INPUT("RIN4"),
+
+	/* Outputs */
+	SND_SOC_DAPM_OUTPUT("LOUT1"),
+	SND_SOC_DAPM_OUTPUT("ROUT1"),
+	SND_SOC_DAPM_OUTPUT("LOUT2"),
+	SND_SOC_DAPM_OUTPUT("ROUT2"),
+	SND_SOC_DAPM_OUTPUT("LOUT3"),
+	SND_SOC_DAPM_OUTPUT("ROUT3"),
+
+	/* DAC */
+	SND_SOC_DAPM_DAC("DAC Left", "Left HiFi Playback",
+			AK4671_AD_DA_POWER_MANAGEMENT, 6, 0),
+	SND_SOC_DAPM_DAC("DAC Right", "Right HiFi Playback",
+			AK4671_AD_DA_POWER_MANAGEMENT, 7, 0),
+
+	/* ADC */
+	SND_SOC_DAPM_ADC("ADC Left", "Left HiFi Capture",
+			AK4671_AD_DA_POWER_MANAGEMENT, 4, 0),
+	SND_SOC_DAPM_ADC("ADC Right", "Right HiFi Capture",
+			AK4671_AD_DA_POWER_MANAGEMENT, 5, 0),
+
+	/* PGA */
+	SND_SOC_DAPM_PGA("LOUT2 Mix Amp",
+			AK4671_LOUT2_POWER_MANAGERMENT, 5, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("ROUT2 Mix Amp",
+			AK4671_LOUT2_POWER_MANAGERMENT, 6, 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA("LIN1 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("RIN1 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 1, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("LIN2 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 2, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("RIN2 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 3, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("LIN3 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 4, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("RIN3 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 5, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("LIN4 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 6, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("RIN4 Mixing Circuit",
+			AK4671_MIXING_POWER_MANAGEMENT1, 7, 0, NULL, 0),
+
+	/* Output Mixers */
+	SND_SOC_DAPM_MIXER("LOUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 0, 0,
+			&ak4671_lout1_mixer_controls[0],
+			ARRAY_SIZE(ak4671_lout1_mixer_controls)),
+	SND_SOC_DAPM_MIXER("ROUT1 Mixer", AK4671_LOUT1_POWER_MANAGERMENT, 1, 0,
+			&ak4671_rout1_mixer_controls[0],
+			ARRAY_SIZE(ak4671_rout1_mixer_controls)),
+	SND_SOC_DAPM_MIXER_E("LOUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+			0, 0, &ak4671_lout2_mixer_controls[0],
+			ARRAY_SIZE(ak4671_lout2_mixer_controls),
+			ak4671_out2_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_MIXER_E("ROUT2 Mixer", AK4671_LOUT2_POWER_MANAGERMENT,
+			1, 0, &ak4671_rout2_mixer_controls[0],
+			ARRAY_SIZE(ak4671_rout2_mixer_controls),
+			ak4671_out2_event,
+			SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_MIXER("LOUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 0, 0,
+			&ak4671_lout3_mixer_controls[0],
+			ARRAY_SIZE(ak4671_lout3_mixer_controls)),
+	SND_SOC_DAPM_MIXER("ROUT3 Mixer", AK4671_LOUT3_POWER_MANAGERMENT, 1, 0,
+			&ak4671_rout3_mixer_controls[0],
+			ARRAY_SIZE(ak4671_rout3_mixer_controls)),
+
+	/* Input MUXs */
+	SND_SOC_DAPM_MUX("LIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 2, 0,
+			&ak4671_lin_mux_control),
+	SND_SOC_DAPM_MUX("RIN MUX", AK4671_AD_DA_POWER_MANAGEMENT, 3, 0,
+			&ak4671_rin_mux_control),
+
+	/* Mic Power */
+	SND_SOC_DAPM_MICBIAS("Mic Bias", AK4671_AD_DA_POWER_MANAGEMENT, 1, 0),
+
+	/* Supply */
+	SND_SOC_DAPM_SUPPLY("PMPLL", AK4671_PLL_MODE_SELECT1, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	{"DAC Left", "NULL", "PMPLL"},
+	{"DAC Right", "NULL", "PMPLL"},
+	{"ADC Left", "NULL", "PMPLL"},
+	{"ADC Right", "NULL", "PMPLL"},
+
+	/* Outputs */
+	{"LOUT1", "NULL", "LOUT1 Mixer"},
+	{"ROUT1", "NULL", "ROUT1 Mixer"},
+	{"LOUT2", "NULL", "LOUT2 Mix Amp"},
+	{"ROUT2", "NULL", "ROUT2 Mix Amp"},
+	{"LOUT3", "NULL", "LOUT3 Mixer"},
+	{"ROUT3", "NULL", "ROUT3 Mixer"},
+
+	{"LOUT1 Mixer", "DACL", "DAC Left"},
+	{"ROUT1 Mixer", "DACR", "DAC Right"},
+	{"LOUT2 Mixer", "DACHL", "DAC Left"},
+	{"ROUT2 Mixer", "DACHR", "DAC Right"},
+	{"LOUT2 Mix Amp", "NULL", "LOUT2 Mixer"},
+	{"ROUT2 Mix Amp", "NULL", "ROUT2 Mixer"},
+	{"LOUT3 Mixer", "DACSL", "DAC Left"},
+	{"ROUT3 Mixer", "DACSR", "DAC Right"},
+
+	/* Inputs */
+	{"LIN MUX", "LIN1", "LIN1"},
+	{"LIN MUX", "LIN2", "LIN2"},
+	{"LIN MUX", "LIN3", "LIN3"},
+	{"LIN MUX", "LIN4", "LIN4"},
+
+	{"RIN MUX", "RIN1", "RIN1"},
+	{"RIN MUX", "RIN2", "RIN2"},
+	{"RIN MUX", "RIN3", "RIN3"},
+	{"RIN MUX", "RIN4", "RIN4"},
+
+	{"LIN1", NULL, "Mic Bias"},
+	{"RIN1", NULL, "Mic Bias"},
+	{"LIN2", NULL, "Mic Bias"},
+	{"RIN2", NULL, "Mic Bias"},
+
+	{"ADC Left", "NULL", "LIN MUX"},
+	{"ADC Right", "NULL", "RIN MUX"},
+
+	/* Analog Loops */
+	{"LIN1 Mixing Circuit", "NULL", "LIN1"},
+	{"RIN1 Mixing Circuit", "NULL", "RIN1"},
+	{"LIN2 Mixing Circuit", "NULL", "LIN2"},
+	{"RIN2 Mixing Circuit", "NULL", "RIN2"},
+	{"LIN3 Mixing Circuit", "NULL", "LIN3"},
+	{"RIN3 Mixing Circuit", "NULL", "RIN3"},
+	{"LIN4 Mixing Circuit", "NULL", "LIN4"},
+	{"RIN4 Mixing Circuit", "NULL", "RIN4"},
+
+	{"LOUT1 Mixer", "LINL1", "LIN1 Mixing Circuit"},
+	{"ROUT1 Mixer", "RINR1", "RIN1 Mixing Circuit"},
+	{"LOUT2 Mixer", "LINH1", "LIN1 Mixing Circuit"},
+	{"ROUT2 Mixer", "RINH1", "RIN1 Mixing Circuit"},
+	{"LOUT3 Mixer", "LINS1", "LIN1 Mixing Circuit"},
+	{"ROUT3 Mixer", "RINS1", "RIN1 Mixing Circuit"},
+
+	{"LOUT1 Mixer", "LINL2", "LIN2 Mixing Circuit"},
+	{"ROUT1 Mixer", "RINR2", "RIN2 Mixing Circuit"},
+	{"LOUT2 Mixer", "LINH2", "LIN2 Mixing Circuit"},
+	{"ROUT2 Mixer", "RINH2", "RIN2 Mixing Circuit"},
+	{"LOUT3 Mixer", "LINS2", "LIN2 Mixing Circuit"},
+	{"ROUT3 Mixer", "RINS2", "RIN2 Mixing Circuit"},
+
+	{"LOUT1 Mixer", "LINL3", "LIN3 Mixing Circuit"},
+	{"ROUT1 Mixer", "RINR3", "RIN3 Mixing Circuit"},
+	{"LOUT2 Mixer", "LINH3", "LIN3 Mixing Circuit"},
+	{"ROUT2 Mixer", "RINH3", "RIN3 Mixing Circuit"},
+	{"LOUT3 Mixer", "LINS3", "LIN3 Mixing Circuit"},
+	{"ROUT3 Mixer", "RINS3", "RIN3 Mixing Circuit"},
+
+	{"LOUT1 Mixer", "LINL4", "LIN4 Mixing Circuit"},
+	{"ROUT1 Mixer", "RINR4", "RIN4 Mixing Circuit"},
+	{"LOUT2 Mixer", "LINH4", "LIN4 Mixing Circuit"},
+	{"ROUT2 Mixer", "RINH4", "RIN4 Mixing Circuit"},
+	{"LOUT3 Mixer", "LINS4", "LIN4 Mixing Circuit"},
+	{"ROUT3 Mixer", "RINS4", "RIN4 Mixing Circuit"},
+};
+
+static int ak4671_add_widgets(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_new_controls(codec, ak4671_dapm_widgets,
+				  ARRAY_SIZE(ak4671_dapm_widgets));
+
+	snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+	snd_soc_dapm_new_widgets(codec);
+	return 0;
+}
+
+static int ak4671_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;
+	u8 fs;
+
+	fs = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+	fs &= ~AK4671_FS;
+
+	switch (params_rate(params)) {
+	case 8000:
+		fs |= AK4671_FS_8KHZ;
+		break;
+	case 12000:
+		fs |= AK4671_FS_12KHZ;
+		break;
+	case 16000:
+		fs |= AK4671_FS_16KHZ;
+		break;
+	case 24000:
+		fs |= AK4671_FS_24KHZ;
+		break;
+	case 11025:
+		fs |= AK4671_FS_11_025KHZ;
+		break;
+	case 22050:
+		fs |= AK4671_FS_22_05KHZ;
+		break;
+	case 32000:
+		fs |= AK4671_FS_32KHZ;
+		break;
+	case 44100:
+		fs |= AK4671_FS_44_1KHZ;
+		break;
+	case 48000:
+		fs |= AK4671_FS_48KHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, fs);
+
+	return 0;
+}
+
+static int ak4671_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+		unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u8 pll;
+
+	pll = snd_soc_read(codec, AK4671_PLL_MODE_SELECT0);
+	pll &= ~AK4671_PLL;
+
+	switch (freq) {
+	case 11289600:
+		pll |= AK4671_PLL_11_2896MHZ;
+		break;
+	case 12000000:
+		pll |= AK4671_PLL_12MHZ;
+		break;
+	case 12288000:
+		pll |= AK4671_PLL_12_288MHZ;
+		break;
+	case 13000000:
+		pll |= AK4671_PLL_13MHZ;
+		break;
+	case 13500000:
+		pll |= AK4671_PLL_13_5MHZ;
+		break;
+	case 19200000:
+		pll |= AK4671_PLL_19_2MHZ;
+		break;
+	case 24000000:
+		pll |= AK4671_PLL_24MHZ;
+		break;
+	case 26000000:
+		pll |= AK4671_PLL_26MHZ;
+		break;
+	case 27000000:
+		pll |= AK4671_PLL_27MHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, AK4671_PLL_MODE_SELECT0, pll);
+
+	return 0;
+}
+
+static int ak4671_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u8 mode;
+	u8 format;
+
+	/* set master/slave audio interface */
+	mode = snd_soc_read(codec, AK4671_PLL_MODE_SELECT1);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		mode |= AK4671_M_S;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		mode &= ~(AK4671_M_S);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* interface format */
+	format = snd_soc_read(codec, AK4671_FORMAT_SELECT);
+	format &= ~AK4671_DIF;
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		format |= AK4671_DIF_I2S_MODE;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		format |= AK4671_DIF_MSB_MODE;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		format |= AK4671_DIF_DSP_MODE;
+		format |= AK4671_BCKP;
+		format |= AK4671_MSBS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* set mode and format */
+	snd_soc_write(codec, AK4671_PLL_MODE_SELECT1, mode);
+	snd_soc_write(codec, AK4671_FORMAT_SELECT, format);
+
+	return 0;
+}
+
+static int ak4671_set_bias_level(struct snd_soc_codec *codec,
+		enum snd_soc_bias_level level)
+{
+	u8 reg;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+	case SND_SOC_BIAS_STANDBY:
+		reg = snd_soc_read(codec, AK4671_AD_DA_POWER_MANAGEMENT);
+		snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT,
+				reg | AK4671_PMVCM);
+		break;
+	case SND_SOC_BIAS_OFF:
+		snd_soc_write(codec, AK4671_AD_DA_POWER_MANAGEMENT, 0x00);
+		break;
+	}
+	codec->bias_level = level;
+	return 0;
+}
+
+#define AK4671_RATES		(SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+				SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
+				SNDRV_PCM_RATE_48000)
+
+#define AK4671_FORMATS		SNDRV_PCM_FMTBIT_S16_LE
+
+static struct snd_soc_dai_ops ak4671_dai_ops = {
+	.hw_params	= ak4671_hw_params,
+	.set_sysclk	= ak4671_set_dai_sysclk,
+	.set_fmt	= ak4671_set_dai_fmt,
+};
+
+struct snd_soc_dai ak4671_dai = {
+	.name = "AK4671",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = AK4671_RATES,
+		.formats = AK4671_FORMATS,},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = AK4671_RATES,
+		.formats = AK4671_FORMATS,},
+	.ops = &ak4671_dai_ops,
+};
+EXPORT_SYMBOL_GPL(ak4671_dai);
+
+static int ak4671_probe(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+	struct snd_soc_codec *codec;
+	int ret = 0;
+
+	if (ak4671_codec == NULL) {
+		dev_err(&pdev->dev, "Codec device not registered\n");
+		return -ENODEV;
+	}
+
+	socdev->card->codec = ak4671_codec;
+	codec = ak4671_codec;
+
+	/* register pcms */
+	ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to create pcms: %d\n", ret);
+		goto pcm_err;
+	}
+
+	snd_soc_add_controls(codec, ak4671_snd_controls,
+			     ARRAY_SIZE(ak4671_snd_controls));
+	ak4671_add_widgets(codec);
+
+	ret = snd_soc_init_card(socdev);
+	if (ret < 0) {
+		dev_err(codec->dev, "failed to register card: %d\n", ret);
+		goto card_err;
+	}
+
+	ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return ret;
+
+card_err:
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+pcm_err:
+	return ret;
+}
+
+static int ak4671_remove(struct platform_device *pdev)
+{
+	struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+
+	snd_soc_free_pcms(socdev);
+	snd_soc_dapm_free(socdev);
+
+	return 0;
+}
+
+struct snd_soc_codec_device soc_codec_dev_ak4671 = {
+	.probe = ak4671_probe,
+	.remove = ak4671_remove,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_ak4671);
+
+static int ak4671_register(struct ak4671_priv *ak4671,
+		enum snd_soc_control_type control)
+{
+	int ret;
+	struct snd_soc_codec *codec = &ak4671->codec;
+
+	if (ak4671_codec) {
+		dev_err(codec->dev, "Another AK4671 is registered\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mutex_init(&codec->mutex);
+	INIT_LIST_HEAD(&codec->dapm_widgets);
+	INIT_LIST_HEAD(&codec->dapm_paths);
+
+	codec->private_data = ak4671;
+	codec->name = "AK4671";
+	codec->owner = THIS_MODULE;
+	codec->bias_level = SND_SOC_BIAS_OFF;
+	codec->set_bias_level = ak4671_set_bias_level;
+	codec->dai = &ak4671_dai;
+	codec->num_dai = 1;
+	codec->reg_cache_size = AK4671_CACHEREGNUM;
+	codec->reg_cache = &ak4671->reg_cache;
+
+	memcpy(codec->reg_cache, ak4671_reg, sizeof(ak4671_reg));
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, control);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		goto err;
+	}
+
+	ak4671_dai.dev = codec->dev;
+	ak4671_codec = codec;
+
+	ret = snd_soc_register_codec(codec);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+		goto err;
+	}
+
+	ret = snd_soc_register_dai(&ak4671_dai);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
+		goto err_codec;
+	}
+
+	return 0;
+
+err_codec:
+	snd_soc_unregister_codec(codec);
+err:
+	kfree(ak4671);
+	return ret;
+}
+
+static void ak4671_unregister(struct ak4671_priv *ak4671)
+{
+	ak4671_set_bias_level(&ak4671->codec, SND_SOC_BIAS_OFF);
+	snd_soc_unregister_dai(&ak4671_dai);
+	snd_soc_unregister_codec(&ak4671->codec);
+	kfree(ak4671);
+	ak4671_codec = NULL;
+}
+
+static int __devinit ak4671_i2c_probe(struct i2c_client *client,
+		const struct i2c_device_id *id)
+{
+	struct ak4671_priv *ak4671;
+	struct snd_soc_codec *codec;
+
+	ak4671 = kzalloc(sizeof(struct ak4671_priv), GFP_KERNEL);
+	if (ak4671 == NULL)
+		return -ENOMEM;
+
+	codec = &ak4671->codec;
+	codec->hw_write = (hw_write_t)i2c_master_send;
+
+	i2c_set_clientdata(client, ak4671);
+	codec->control_data = client;
+
+	codec->dev = &client->dev;
+
+	return ak4671_register(ak4671, SND_SOC_I2C);
+}
+
+static __devexit int ak4671_i2c_remove(struct i2c_client *client)
+{
+	struct ak4671_priv *ak4671 = i2c_get_clientdata(client);
+
+	ak4671_unregister(ak4671);
+
+	return 0;
+}
+
+static const struct i2c_device_id ak4671_i2c_id[] = {
+	{ "ak4671", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
+
+static struct i2c_driver ak4671_i2c_driver = {
+	.driver = {
+		.name = "ak4671",
+		.owner = THIS_MODULE,
+	},
+	.probe = ak4671_i2c_probe,
+	.remove = __devexit_p(ak4671_i2c_remove),
+	.id_table = ak4671_i2c_id,
+};
+
+static int __init ak4671_modinit(void)
+{
+	return i2c_add_driver(&ak4671_i2c_driver);
+}
+module_init(ak4671_modinit);
+
+static void __exit ak4671_exit(void)
+{
+	i2c_del_driver(&ak4671_i2c_driver);
+}
+module_exit(ak4671_exit);
+
+MODULE_DESCRIPTION("ASoC AK4671 codec driver");
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/ak4671.h b/sound/soc/codecs/ak4671.h
new file mode 100644
index 0000000..e2fad96
--- /dev/null
+++ b/sound/soc/codecs/ak4671.h
@@ -0,0 +1,156 @@
+/*
+ * ak4671.h  --  audio driver for AK4671
+ *
+ * Copyright (C) 2009 Samsung Electronics Co.Ltd
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef _AK4671_H
+#define _AK4671_H
+
+#define AK4671_AD_DA_POWER_MANAGEMENT		0x00
+#define AK4671_PLL_MODE_SELECT0			0x01
+#define AK4671_PLL_MODE_SELECT1			0x02
+#define AK4671_FORMAT_SELECT			0x03
+#define AK4671_MIC_SIGNAL_SELECT		0x04
+#define AK4671_MIC_AMP_GAIN			0x05
+#define AK4671_MIXING_POWER_MANAGEMENT0		0x06
+#define AK4671_MIXING_POWER_MANAGEMENT1		0x07
+#define AK4671_OUTPUT_VOLUME_CONTROL		0x08
+#define AK4671_LOUT1_SIGNAL_SELECT		0x09
+#define AK4671_ROUT1_SIGNAL_SELECT		0x0a
+#define AK4671_LOUT2_SIGNAL_SELECT		0x0b
+#define AK4671_ROUT2_SIGNAL_SELECT		0x0c
+#define AK4671_LOUT3_SIGNAL_SELECT		0x0d
+#define AK4671_ROUT3_SIGNAL_SELECT		0x0e
+#define AK4671_LOUT1_POWER_MANAGERMENT		0x0f
+#define AK4671_LOUT2_POWER_MANAGERMENT		0x10
+#define AK4671_LOUT3_POWER_MANAGERMENT		0x11
+#define AK4671_LCH_INPUT_VOLUME_CONTROL		0x12
+#define AK4671_RCH_INPUT_VOLUME_CONTROL		0x13
+#define AK4671_ALC_REFERENCE_SELECT		0x14
+#define AK4671_DIGITAL_MIXING_CONTROL		0x15
+#define AK4671_ALC_TIMER_SELECT			0x16
+#define AK4671_ALC_MODE_CONTROL			0x17
+#define AK4671_MODE_CONTROL1			0x18
+#define AK4671_MODE_CONTROL2			0x19
+#define AK4671_LCH_OUTPUT_VOLUME_CONTROL	0x1a
+#define AK4671_RCH_OUTPUT_VOLUME_CONTROL	0x1b
+#define AK4671_SIDETONE_A_CONTROL		0x1c
+#define AK4671_DIGITAL_FILTER_SELECT		0x1d
+#define AK4671_FIL3_COEFFICIENT0		0x1e
+#define AK4671_FIL3_COEFFICIENT1		0x1f
+#define AK4671_FIL3_COEFFICIENT2		0x20
+#define AK4671_FIL3_COEFFICIENT3		0x21
+#define AK4671_EQ_COEFFICIENT0			0x22
+#define AK4671_EQ_COEFFICIENT1			0x23
+#define AK4671_EQ_COEFFICIENT2			0x24
+#define AK4671_EQ_COEFFICIENT3			0x25
+#define AK4671_EQ_COEFFICIENT4			0x26
+#define AK4671_EQ_COEFFICIENT5			0x27
+#define AK4671_FIL1_COEFFICIENT0		0x28
+#define AK4671_FIL1_COEFFICIENT1		0x29
+#define AK4671_FIL1_COEFFICIENT2		0x2a
+#define AK4671_FIL1_COEFFICIENT3		0x2b
+#define AK4671_FIL2_COEFFICIENT0		0x2c
+#define AK4671_FIL2_COEFFICIENT1		0x2d
+#define AK4671_FIL2_COEFFICIENT2		0x2e
+#define AK4671_FIL2_COEFFICIENT3		0x2f
+#define AK4671_DIGITAL_FILTER_SELECT2		0x30
+#define AK4671_E1_COEFFICIENT0			0x32
+#define AK4671_E1_COEFFICIENT1			0x33
+#define AK4671_E1_COEFFICIENT2			0x34
+#define AK4671_E1_COEFFICIENT3			0x35
+#define AK4671_E1_COEFFICIENT4			0x36
+#define AK4671_E1_COEFFICIENT5			0x37
+#define AK4671_E2_COEFFICIENT0			0x38
+#define AK4671_E2_COEFFICIENT1			0x39
+#define AK4671_E2_COEFFICIENT2			0x3a
+#define AK4671_E2_COEFFICIENT3			0x3b
+#define AK4671_E2_COEFFICIENT4			0x3c
+#define AK4671_E2_COEFFICIENT5			0x3d
+#define AK4671_E3_COEFFICIENT0			0x3e
+#define AK4671_E3_COEFFICIENT1			0x3f
+#define AK4671_E3_COEFFICIENT2			0x40
+#define AK4671_E3_COEFFICIENT3			0x41
+#define AK4671_E3_COEFFICIENT4			0x42
+#define AK4671_E3_COEFFICIENT5			0x43
+#define AK4671_E4_COEFFICIENT0			0x44
+#define AK4671_E4_COEFFICIENT1			0x45
+#define AK4671_E4_COEFFICIENT2			0x46
+#define AK4671_E4_COEFFICIENT3			0x47
+#define AK4671_E4_COEFFICIENT4			0x48
+#define AK4671_E4_COEFFICIENT5			0x49
+#define AK4671_E5_COEFFICIENT0			0x4a
+#define AK4671_E5_COEFFICIENT1			0x4b
+#define AK4671_E5_COEFFICIENT2			0x4c
+#define AK4671_E5_COEFFICIENT3			0x4d
+#define AK4671_E5_COEFFICIENT4			0x4e
+#define AK4671_E5_COEFFICIENT5			0x4f
+#define AK4671_EQ_CONTROL_250HZ_100HZ		0x50
+#define AK4671_EQ_CONTROL_3500HZ_1KHZ		0x51
+#define AK4671_EQ_CONTRO_10KHZ			0x52
+#define AK4671_PCM_IF_CONTROL0			0x53
+#define AK4671_PCM_IF_CONTROL1			0x54
+#define AK4671_PCM_IF_CONTROL2			0x55
+#define AK4671_DIGITAL_VOLUME_B_CONTROL		0x56
+#define AK4671_DIGITAL_VOLUME_C_CONTROL		0x57
+#define AK4671_SIDETONE_VOLUME_CONTROL		0x58
+#define AK4671_DIGITAL_MIXING_CONTROL2		0x59
+#define AK4671_SAR_ADC_CONTROL			0x5a
+
+#define AK4671_CACHEREGNUM			(AK4671_SAR_ADC_CONTROL + 1)
+
+/* Bitfield Definitions */
+
+/* AK4671_AD_DA_POWER_MANAGEMENT (0x00) Fields */
+#define AK4671_PMVCM				0x01
+
+/* AK4671_PLL_MODE_SELECT0 (0x01) Fields */
+#define AK4671_PLL				0x0f
+#define AK4671_PLL_11_2896MHZ			(4 << 0)
+#define AK4671_PLL_12_288MHZ			(5 << 0)
+#define AK4671_PLL_12MHZ			(6 << 0)
+#define AK4671_PLL_24MHZ			(7 << 0)
+#define AK4671_PLL_19_2MHZ			(8 << 0)
+#define AK4671_PLL_13_5MHZ			(12 << 0)
+#define AK4671_PLL_27MHZ			(13 << 0)
+#define AK4671_PLL_13MHZ			(14 << 0)
+#define AK4671_PLL_26MHZ			(15 << 0)
+#define AK4671_FS				0xf0
+#define AK4671_FS_8KHZ				(0 << 4)
+#define AK4671_FS_12KHZ				(1 << 4)
+#define AK4671_FS_16KHZ				(2 << 4)
+#define AK4671_FS_24KHZ				(3 << 4)
+#define AK4671_FS_11_025KHZ			(5 << 4)
+#define AK4671_FS_22_05KHZ			(7 << 4)
+#define AK4671_FS_32KHZ				(10 << 4)
+#define AK4671_FS_48KHZ				(11 << 4)
+#define AK4671_FS_44_1KHZ			(15 << 4)
+
+/* AK4671_PLL_MODE_SELECT1 (0x02) Fields */
+#define AK4671_PMPLL				0x01
+#define AK4671_M_S				0x02
+
+/* AK4671_FORMAT_SELECT (0x03) Fields */
+#define AK4671_DIF				0x03
+#define AK4671_DIF_DSP_MODE			(0 << 0)
+#define AK4671_DIF_MSB_MODE			(2 << 0)
+#define AK4671_DIF_I2S_MODE			(3 << 0)
+#define AK4671_BCKP				0x04
+#define AK4671_MSBS				0x08
+#define AK4671_SDOD				0x10
+
+/* AK4671_LOUT2_POWER_MANAGEMENT (0x10) Fields */
+#define AK4671_MUTEN				0x04
+
+extern struct snd_soc_dai ak4671_dai;
+extern struct snd_soc_codec_device soc_codec_dev_ak4671;
+
+#endif
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 3ff0373..3f7e8a8 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1101,7 +1101,7 @@
 }
 
 static int wm8350_set_fll(struct snd_soc_dai *codec_dai,
-			  int pll_id, unsigned int freq_in,
+			  int pll_id, int source, unsigned int freq_in,
 			  unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index b9ef4d9..9cb8e50 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -1011,7 +1011,8 @@
 }
 
 static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
-			      unsigned int freq_in, unsigned int freq_out)
+			      int source, unsigned int freq_in,
+			      unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct wm8400_priv *wm8400 = codec->private_data;
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 060d5d0..5702435 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -271,8 +271,8 @@
 	pll_div.k = K;
 }
 
-static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 reg;
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 6bded8c..3be5c0b 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -407,8 +407,8 @@
 	return 0;
 }
 
-static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	int offset;
 	struct snd_soc_codec *codec = codec_dai->codec;
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index d80d414..f60f3a0 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -723,8 +723,8 @@
 	pll_div->k = K;
 }
 
-static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8753_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	u16 reg, enable;
 	int offset;
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 5e9c855..882604e 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -814,8 +814,8 @@
 	return 0;
 }
 
-static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8900_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	return wm8900_set_fll(codec_dai->codec, pll_id, freq_in, freq_out);
 }
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index da97aae..914d788 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -536,8 +536,8 @@
 }
 
 /* Untested at the moment */
-static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8940_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 reg;
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index f59703b..416fb3c 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -540,8 +540,8 @@
 	return 0;
 }
 
-static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 reg;
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 98d663a..93d66e3 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -328,8 +328,8 @@
 	pll_div.k = K;
 }
 
-static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8974_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	u16 reg;
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 2d702db..f657e9a 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -972,8 +972,8 @@
 	pll_div->k = K;
 }
 
-static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm8990_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	u16 reg;
 	struct snd_soc_codec *codec = codec_dai->codec;
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index d998799..6b32a285 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -422,7 +422,7 @@
 	return 0;
 }
 
-static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id,
+static int wm8993_set_fll(struct snd_soc_dai *dai, int fll_id, int source,
 			  unsigned int Fref, unsigned int Fout)
 {
 	struct snd_soc_codec *codec = dai->codec;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index abed37a..ca3d449 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -800,8 +800,8 @@
 	return 0;
 }
 
-static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai,
-		int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int wm9713_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+		int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct snd_soc_codec *codec = codec_dai->codec;
 	return wm9713_set_pll(codec, pll_id, freq_in, freq_out);
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 4dfd4ad..047ee39 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -13,9 +13,9 @@
 	tristate
 
 config SND_DAVINCI_SOC_EVM
-	tristate "SoC Audio support for DaVinci DM6446 or DM355 EVM"
+	tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM"
 	depends on SND_DAVINCI_SOC
-	depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM
+	depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM  || MACH_DAVINCI_DM365_EVM
 	select SND_DAVINCI_SOC_I2S
 	select SND_SOC_TLV320AIC3X
 	help
diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c
index 67414f6..7ccbe66 100644
--- a/sound/soc/davinci/davinci-evm.c
+++ b/sound/soc/davinci/davinci-evm.c
@@ -45,7 +45,8 @@
 	unsigned sysclk;
 
 	/* ASP1 on DM355 EVM is clocked by an external oscillator */
-	if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm())
+	if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() ||
+	    machine_is_davinci_dm365_evm())
 		sysclk = 27000000;
 
 	/* ASP0 in DM6446 EVM is clocked by U55, as configured by
@@ -176,7 +177,7 @@
 	.ops = &evm_ops,
 };
 
-/* davinci-evm audio machine driver */
+/* davinci dm6446, dm355 or dm365 evm audio machine driver */
 static struct snd_soc_card snd_soc_card_evm = {
 	.name = "DaVinci EVM",
 	.platform = &davinci_soc_platform,
@@ -243,7 +244,7 @@
 	int index;
 	int ret;
 
-	if (machine_is_davinci_evm()) {
+	if (machine_is_davinci_evm() || machine_is_davinci_dm365_evm()) {
 		evm_snd_dev_data = &evm_snd_devdata;
 		index = 0;
 	} else if (machine_is_davinci_dm355_evm()) {
diff --git a/sound/soc/imx/mx27vis_wm8974.c b/sound/soc/imx/mx27vis_wm8974.c
index e4dcb53..0267d2d 100644
--- a/sound/soc/imx/mx27vis_wm8974.c
+++ b/sound/soc/imx/mx27vis_wm8974.c
@@ -157,7 +157,7 @@
 
 
 	/* codec PLL input is 25 MHz */
-	ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG,
+	ret = codec_dai->ops->set_pll(codec_dai, IGNORED_ARG, IGNORED_ARG,
 					25000000, pll_out);
 	if (ret < 0) {
 		printk(KERN_ERR "Error when setting PLL input\n");
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
index 9f7c61e..4c8d99a 100644
--- a/sound/soc/pxa/magician.c
+++ b/sound/soc/pxa/magician.c
@@ -213,7 +213,7 @@
 		return ret;
 
 	/* set SSP audio pll clock */
-	ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, acps);
+	ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, acps);
 	if (ret < 0)
 		return ret;
 
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 5b9ed64..57f201c 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -305,8 +305,8 @@
 /*
  * Configure the PLL frequency pxa27x and (afaik - pxa320 only)
  */
-static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai,
-	int pll_id, unsigned int freq_in, unsigned int freq_out)
+static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
+	int source, unsigned int freq_in, unsigned int freq_out)
 {
 	struct ssp_priv *priv = cpu_dai->private_data;
 	struct ssp_device *ssp = priv->dev.ssp;
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
index 9a386b4..dd678ae 100644
--- a/sound/soc/pxa/zylonite.c
+++ b/sound/soc/pxa/zylonite.c
@@ -74,7 +74,8 @@
 static int zylonite_wm9713_init(struct snd_soc_codec *codec)
 {
 	if (clk_pout)
-		snd_soc_dai_set_pll(&codec->dai[0], 0, clk_get_rate(pout), 0);
+		snd_soc_dai_set_pll(&codec->dai[0], 0, 0,
+				    clk_get_rate(pout), 0);
 
 	snd_soc_dapm_new_controls(codec, zylonite_dapm_widgets,
 				  ARRAY_SIZE(zylonite_dapm_widgets));
@@ -128,7 +129,7 @@
 	if (ret < 0)
 		return ret;
 
-	ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, pll_out);
+	ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, pll_out);
 	if (ret < 0)
 		return ret;
 
diff --git a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
index 0c52e36..6ddd1b3 100644
--- a/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_gta02_wm8753.c
@@ -119,7 +119,7 @@
 		return ret;
 
 	/* codec PLL input is PCLK/4 */
-	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
 		iis_clkrate / 4, pll_out);
 	if (ret < 0)
 		return ret;
diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c
index 906709e..16009eb 100644
--- a/sound/soc/s3c24xx/neo1973_wm8753.c
+++ b/sound/soc/s3c24xx/neo1973_wm8753.c
@@ -137,7 +137,7 @@
 		return ret;
 
 	/* codec PLL input is PCLK/4 */
-	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1,
+	ret = snd_soc_dai_set_pll(codec_dai, WM8753_PLL1, 0,
 		iis_clkrate / 4, pll_out);
 	if (ret < 0)
 		return ret;
diff --git a/sound/soc/s3c24xx/s3c-i2s-v2.c b/sound/soc/s3c24xx/s3c-i2s-v2.c
index 9bc4aa3..11c45a3 100644
--- a/sound/soc/s3c24xx/s3c-i2s-v2.c
+++ b/sound/soc/s3c24xx/s3c-i2s-v2.c
@@ -312,12 +312,15 @@
 
 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
 	case SND_SOC_DAIFMT_RIGHT_J:
+		iismod |= S3C2412_IISMOD_LR_RLOW;
 		iismod |= S3C2412_IISMOD_SDF_MSB;
 		break;
 	case SND_SOC_DAIFMT_LEFT_J:
+		iismod |= S3C2412_IISMOD_LR_RLOW;
 		iismod |= S3C2412_IISMOD_SDF_LSB;
 		break;
 	case SND_SOC_DAIFMT_I2S:
+		iismod &= ~S3C2412_IISMOD_LR_RLOW;
 		iismod |= S3C2412_IISMOD_SDF_IIS;
 		break;
 	default:
@@ -467,6 +470,31 @@
 
 	switch (div_id) {
 	case S3C_I2SV2_DIV_BCLK:
+		if (div > 3) {
+			/* convert value to bit field */
+
+			switch (div) {
+			case 16:
+				div = S3C2412_IISMOD_BCLK_16FS;
+				break;
+
+			case 32:
+				div = S3C2412_IISMOD_BCLK_32FS;
+				break;
+
+			case 24:
+				div = S3C2412_IISMOD_BCLK_24FS;
+				break;
+
+			case 48:
+				div = S3C2412_IISMOD_BCLK_48FS;
+				break;
+
+			default:
+				return -EINVAL;
+			}
+		}
+
 		reg = readl(i2s->regs + S3C2412_IISMOD);
 		reg &= ~S3C2412_IISMOD_BCLK_MASK;
 		writel(reg | div, i2s->regs + S3C2412_IISMOD);
@@ -626,7 +654,7 @@
 	}
 
 	i2s->iis_pclk = clk_get(dev, "iis");
-	if (i2s->iis_pclk == NULL) {
+	if (IS_ERR(i2s->iis_pclk)) {
 		dev_err(dev, "failed to get iis_clock\n");
 		iounmap(i2s->regs);
 		return -ENOENT;
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.c b/sound/soc/s3c24xx/s3c64xx-i2s.c
index 3c06c40..43fb253 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.c
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.c
@@ -99,6 +99,19 @@
 		iismod |= S3C64XX_IISMOD_IMS_SYSMUX;
 		break;
 
+	case S3C64XX_CLKSRC_CDCLK:
+		switch (dir) {
+		case SND_SOC_CLOCK_IN:
+			iismod |= S3C64XX_IISMOD_CDCLKCON;
+			break;
+		case SND_SOC_CLOCK_OUT:
+			iismod &= ~S3C64XX_IISMOD_CDCLKCON;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+
 	default:
 		return -EINVAL;
 	}
@@ -111,8 +124,12 @@
 struct clk *s3c64xx_i2s_get_clock(struct snd_soc_dai *dai)
 {
 	struct s3c_i2sv2_info *i2s = to_info(dai);
+	u32 iismod = readl(i2s->regs + S3C2412_IISMOD);
 
-	return i2s->iis_cclk;
+	if (iismod & S3C64XX_IISMOD_IMS_SYSMUX)
+		return i2s->iis_cclk;
+	else
+		return i2s->iis_pclk;
 }
 EXPORT_SYMBOL_GPL(s3c64xx_i2s_get_clock);
 
diff --git a/sound/soc/s3c24xx/s3c64xx-i2s.h b/sound/soc/s3c24xx/s3c64xx-i2s.h
index 02148ce..abe7253 100644
--- a/sound/soc/s3c24xx/s3c64xx-i2s.h
+++ b/sound/soc/s3c24xx/s3c64xx-i2s.h
@@ -25,6 +25,7 @@
 
 #define S3C64XX_CLKSRC_PCLK	(0)
 #define S3C64XX_CLKSRC_MUX	(1)
+#define S3C64XX_CLKSRC_CDCLK    (2)
 
 extern struct snd_soc_dai s3c64xx_i2s_dai[];
 
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index c8ceddc..404231e 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -77,6 +77,35 @@
 #define snd_soc_7_9_spi_write NULL
 #endif
 
+static int snd_soc_8_8_write(struct snd_soc_codec *codec, unsigned int reg,
+			     unsigned int value)
+{
+	u8 *cache = codec->reg_cache;
+	u8 data[2];
+
+	BUG_ON(codec->volatile_register);
+
+	data[0] = reg & 0xff;
+	data[1] = value & 0xff;
+
+	if (reg < codec->reg_cache_size)
+		cache[reg] = value;
+
+	if (codec->hw_write(codec->control_data, data, 2) == 2)
+		return 0;
+	else
+		return -EIO;
+}
+
+static unsigned int snd_soc_8_8_read(struct snd_soc_codec *codec,
+				     unsigned int reg)
+{
+	u8 *cache = codec->reg_cache;
+	if (reg >= codec->reg_cache_size)
+		return -1;
+	return cache[reg];
+}
+
 static int snd_soc_8_16_write(struct snd_soc_codec *codec, unsigned int reg,
 			      unsigned int value)
 {
@@ -151,6 +180,7 @@
 	unsigned int (*i2c_read)(struct snd_soc_codec *, unsigned int);
 } io_types[] = {
 	{ 7, 9, snd_soc_7_9_write, snd_soc_7_9_spi_write, snd_soc_7_9_read },
+	{ 8, 8, snd_soc_8_8_write, NULL, snd_soc_8_8_read, NULL },
 	{ 8, 16, snd_soc_8_16_write, NULL, snd_soc_8_16_read,
 	  snd_soc_8_16_read_i2c },
 };
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 7ff04ad..f5b356f 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -2197,16 +2197,18 @@
  * snd_soc_dai_set_pll - configure DAI PLL.
  * @dai: DAI
  * @pll_id: DAI specific PLL ID
+ * @source: DAI specific source for the PLL
  * @freq_in: PLL input clock frequency in Hz
  * @freq_out: requested PLL output clock frequency in Hz
  *
  * Configures and enables PLL to generate output clock based on input clock.
  */
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai,
-	int pll_id, unsigned int freq_in, unsigned int freq_out)
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+	unsigned int freq_in, unsigned int freq_out)
 {
 	if (dai->ops && dai->ops->set_pll)
-		return dai->ops->set_pll(dai, pll_id, freq_in, freq_out);
+		return dai->ops->set_pll(dai, pll_id, source,
+					 freq_in, freq_out);
 	else
 		return -EINVAL;
 }
@@ -2251,6 +2253,30 @@
 EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot);
 
 /**
+ * snd_soc_dai_set_channel_map - configure DAI audio channel map
+ * @dai: DAI
+ * @tx_num: how many TX channels
+ * @tx_slot: pointer to an array which imply the TX slot number channel
+ *           0~num-1 uses
+ * @rx_num: how many RX channels
+ * @rx_slot: pointer to an array which imply the RX slot number channel
+ *           0~num-1 uses
+ *
+ * configure the relationship between channel number and TDM slot number.
+ */
+int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
+	unsigned int tx_num, unsigned int *tx_slot,
+	unsigned int rx_num, unsigned int *rx_slot)
+{
+	if (dai->ops && dai->ops->set_channel_map)
+		return dai->ops->set_channel_map(dai, tx_num, tx_slot,
+			rx_num, rx_slot);
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_channel_map);
+
+/**
  * snd_soc_dai_set_tristate - configure DAI system or master clock.
  * @dai: DAI
  * @tristate: tristate enable
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index f79711b..9babda5 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -718,6 +718,10 @@
 
 	/* Check if one of our outputs is connected */
 	list_for_each_entry(path, &w->sinks, list_source) {
+		if (path->connected &&
+		    !path->connected(path->source, path->sink))
+			continue;
+
 		if (path->sink && path->sink->power_check &&
 		    path->sink->power_check(path->sink)) {
 			power = 1;
@@ -1137,6 +1141,9 @@
 				w->active ? "active" : "inactive");
 
 	list_for_each_entry(p, &w->sources, list_sink) {
+		if (p->connected && !p->connected(w, p->sink))
+			continue;
+
 		if (p->connect)
 			ret += snprintf(buf + ret, PAGE_SIZE - ret,
 					" in  %s %s\n",
@@ -1144,6 +1151,9 @@
 					p->source->name);
 	}
 	list_for_each_entry(p, &w->sinks, list_source) {
+		if (p->connected && !p->connected(w, p->sink))
+			continue;
+
 		if (p->connect)
 			ret += snprintf(buf + ret, PAGE_SIZE - ret,
 					" out %s %s\n",
@@ -1386,10 +1396,13 @@
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
 static int snd_soc_dapm_add_route(struct snd_soc_codec *codec,
-	const char *sink, const char *control, const char *source)
+				  const struct snd_soc_dapm_route *route)
 {
 	struct snd_soc_dapm_path *path;
 	struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
+	const char *sink = route->sink;
+	const char *control = route->control;
+	const char *source = route->source;
 	int ret = 0;
 
 	/* find src and dest widgets */
@@ -1413,6 +1426,7 @@
 
 	path->source = wsource;
 	path->sink = wsink;
+	path->connected = route->connected;
 	INIT_LIST_HEAD(&path->list);
 	INIT_LIST_HEAD(&path->list_source);
 	INIT_LIST_HEAD(&path->list_sink);
@@ -1513,8 +1527,7 @@
 	int i, ret;
 
 	for (i = 0; i < num; i++) {
-		ret = snd_soc_dapm_add_route(codec, route->sink,
-					     route->control, route->source);
+		ret = snd_soc_dapm_add_route(codec, route);
 		if (ret < 0) {
 			printk(KERN_ERR "Failed to add route %s->%s\n",
 			       route->source,