ARM: LPC315X: LPC315X Analog CODEC driver linux-2.6.33_lpc313x_v1.01
authorBangaragiri G <bangaragiri.g@nxp.com>
Tue, 8 Feb 2011 08:30:31 +0000 (13:30 +0530)
committerBangaragiri G <bangaragiri.g@nxp.com>
Tue, 8 Feb 2011 08:30:31 +0000 (13:30 +0530)
This patch consists of ALSA Audio CODEC driver for
CODEC present in Analog Die in NXP LPC315X MCU.

Signed-off-by: Bangaragiri G <bangaragiri.g@nxp.com>

12 files changed:
arch/arm/configs/ea3152_defconfig
arch/arm/mach-lpc313x/i2c.c
arch/arm/mach-lpc313x/include/mach/lpc315x_ad.h [new file with mode: 0644]
arch/arm/mach-lpc313x/psu.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/lpc315x_codec.c [new file with mode: 0644]
sound/soc/codecs/lpc315x_codec.h [new file with mode: 0644]
sound/soc/lpc313x/Kconfig
sound/soc/lpc313x/Makefile
sound/soc/lpc313x/lpc313x-i2s-clocking.c
sound/soc/lpc313x/lpc315x-codec.c [new file with mode: 0644]

index ed50fde..ee55c8d 100644 (file)
@@ -227,11 +227,11 @@ CONFIG_MACH_EA3152=y
 # CONFIG_MACH_VAL3153 is not set
 # CONFIG_MACH_VAL3154 is not set
 CONFIG_LPC3152_AD=y
-CONFIG_HZ_100=y
-# CONFIG_HZ_250 is not set
+#CONFIG_HZ_100=y
+CONFIG_HZ_250=y
 # CONFIG_HZ_300 is not set
 # CONFIG_HZ_1000 is not set
-CONFIG_HZ=100
+CONFIG_HZ=250
 # CONFIG_SCHED_HRTICK is not set
 
 #
@@ -758,7 +758,7 @@ CONFIG_I2C_PNX=y
 # CONFIG_SENSORS_TSL2550 is not set
 # CONFIG_I2C_DEBUG_CORE is not set
 # CONFIG_I2C_DEBUG_ALGO is not set
-CONFIG_I2C_DEBUG_BUS=y
+# CONFIG_I2C_DEBUG_BUS is not set
 # CONFIG_I2C_DEBUG_CHIP is not set
 CONFIG_SPI=y
 # CONFIG_SPI_DEBUG is not set
@@ -846,7 +846,60 @@ CONFIG_SSB_POSSIBLE=y
 #
 # CONFIG_VGA_CONSOLE is not set
 CONFIG_DUMMY_CONSOLE=y
-# CONFIG_SOUND is not set
+CONFIG_SOUND=y
+CONFIG_SOUND_OSS_CORE=y
+CONFIG_SOUND_OSS_CORE_PRECLAIM=y
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+CONFIG_SND_JACK=y
+CONFIG_SND_SEQUENCER=y
+CONFIG_SND_SEQ_DUMMY=y
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+CONFIG_SND_SEQUENCER_OSS=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+# CONFIG_SND_RAWMIDI_SEQ is not set
+# CONFIG_SND_OPL3_LIB_SEQ is not set
+# CONFIG_SND_OPL4_LIB_SEQ is not set
+# CONFIG_SND_SBAWE_SEQ is not set
+# CONFIG_SND_EMU10K1_SEQ is not set
+CONFIG_SND_DRIVERS=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+CONFIG_SND_ARM=y
+CONFIG_SND_SPI=y
+CONFIG_SND_USB=y
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+CONFIG_SND_SOC=y
+CONFIG_SND_LPC315X_SOC=y
+CONFIG_SND_LPC313X_SOC_I2S=y
+CONFIG_SND_LPC315X_SOC_I2S_LPC315X_CODEC=y
+CONFIG_SND_USE_EA3152=y
+CONFIG_SND_I2C1_CHANNEL_LPC315X_CODEC=y
+# CONFIG_SND_I2S_RX0_MASTER is not set
+CONFIG_SND_I2S_RX1_MASTER=y
+# CONFIG_SND_I2S_RX0_SLAVE is not set
+# CONFIG_SND_I2S_RX1_SLAVE is not set
+# CONFIG_SND_I2S_TX0_MASTER is not set
+CONFIG_SND_I2S_TX1_MASTER=y
+CONFIG_SND_CODEC_FS128=y
+# CONFIG_SND_CODEC_NO_FS256_NEEDED is not set
+CONFIG_SND_USE_DMA_LINKLIST=y
+CONFIG_SND_SOC_I2C_AND_SPI=y
+# CONFIG_SND_SOC_ALL_CODECS is not set
+CONFIG_SND_SOC_LPC315X_CODEC=y
+# CONFIG_SOUND_PRIME is not set
 # CONFIG_HID_SUPPORT is not set
 CONFIG_USB_SUPPORT=y
 CONFIG_USB_ARCH_HAS_HCD=y
index 245157c..a638579 100644 (file)
 
 static int set_clock_run(struct platform_device *pdev)
 {
-       if (pdev->id)
+       if (pdev->id == 0)
                cgu_clk_en_dis( CGU_SB_I2C0_PCLK_ID, 1);
        else
                cgu_clk_en_dis( CGU_SB_I2C1_PCLK_ID, 1);
-       
+
        udelay(2);
        return 0;
 }
 
 static int set_clock_stop(struct platform_device *pdev)
 {
-       if (pdev->id)
+       if (pdev->id == 0)
                cgu_clk_en_dis( CGU_SB_I2C0_PCLK_ID, 0);
        else
                cgu_clk_en_dis( CGU_SB_I2C1_PCLK_ID, 0);
@@ -149,7 +149,7 @@ void __init lpc313x_register_i2c_devices(void)
 #if defined (CONFIG_MACH_VAL3153) || defined (CONFIG_MACH_EA313X)
        /* on EA and VAL boards UDA1380 is connected to I2C1
         * whose slave address is same as LPC313x's default slave
-        * adress causing bus contention errors. So change the 
+        * adress causing bus contention errors. So change the
         * deafult slave address register value of LPC313x here.
         */
        LPC313x_I2C0_SLV_ADDR = 0x06E;
diff --git a/arch/arm/mach-lpc313x/include/mach/lpc315x_ad.h b/arch/arm/mach-lpc313x/include/mach/lpc315x_ad.h
new file mode 100644 (file)
index 0000000..9c5b68a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * arch/arm/mach-lpc313x/include/mach/lpc315x_ad.h
+ *
+ * Author: Kevin Wells <kevin.wells@nxp.com>
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * 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 __MACH_LPC315X_AD_H
+#define __MACH_LPC315X_AD_H
+
+/* Get I2C client structure function */
+extern struct i2c_client *lpc315x_ad_get_i2c_client_struct(void);
+
+#endif
index bd41fb2..45b187e 100644 (file)
@@ -56,6 +56,23 @@ struct psu_data {
 
 static struct psu_data g_pca_data;
 
+/* I2C client structure is required for I2C communications.
+ * The probe function is called only once when driver is added.
+ * Hence the following function be used by the other drivers,
+ * to get I2C client structure
+ */
+struct i2c_client *lpc315x_ad_get_i2c_client_struct(void)
+{
+       /* Check if psu_data structure is initialised */
+       if(!g_pca_data.client) {
+               printk(KERN_ERR "I2C not initialised \r\n");
+               return NULL;
+       }
+
+       return g_pca_data.client;
+}
+EXPORT_SYMBOL(lpc315x_ad_get_i2c_client_struct);
+
 /* following are the sysfs callback functions */
 static ssize_t psu_show(struct device *dev, struct device_attribute *attr,
                            char *buf)
@@ -141,7 +158,7 @@ static struct attribute *psu_attributes[] = {
        &sensor_dev_attr_rtc_set_ena_stat.dev_attr.attr,
        &sensor_dev_attr_rtc_clr_ena_stat.dev_attr.attr,
        &sensor_dev_attr_mod_id.dev_attr.attr,
-       NULL                       
+       NULL
 };
 
 static struct attribute_group psu_defattr_group = {
index 52b005f..57f423f 100644 (file)
@@ -35,6 +35,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_TWL4030 if TWL4030_CORE
        select SND_SOC_UDA134X
        select SND_SOC_UDA1380 if I2C
+       select SND_SOC_LPC315X_CODEC if I2C
        select SND_SOC_WM8350 if MFD_WM8350
        select SND_SOC_WM8400 if MFD_WM8400
        select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
@@ -158,6 +159,9 @@ config SND_SOC_TWL4030
 config SND_SOC_UDA134X
        tristate
 
+config SND_SOC_LPC315X_CODEC
+       tristate
+
 config SND_SOC_UDA1380
         tristate
 
index e97bac8..391d8a7 100644 (file)
@@ -22,6 +22,7 @@ snd-soc-tlv320dac33-objs := tlv320dac33.o
 snd-soc-twl4030-objs := twl4030.o
 snd-soc-uda134x-objs := uda134x.o
 snd-soc-uda1380-objs := uda1380.o
+snd-soc-lpc315x-codec-objs := lpc315x_codec.o
 snd-soc-wm8350-objs := wm8350.o
 snd-soc-wm8400-objs := wm8400.o
 snd-soc-wm8510-objs := wm8510.o
@@ -78,6 +79,7 @@ obj-$(CONFIG_SND_SOC_TLV320DAC33)     += snd-soc-tlv320dac33.o
 obj-$(CONFIG_SND_SOC_TWL4030)  += snd-soc-twl4030.o
 obj-$(CONFIG_SND_SOC_UDA134X)  += snd-soc-uda134x.o
 obj-$(CONFIG_SND_SOC_UDA1380)  += snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_LPC315X_CODEC)    += snd-soc-lpc315x-codec.o
 obj-$(CONFIG_SND_SOC_WM8350)   += snd-soc-wm8350.o
 obj-$(CONFIG_SND_SOC_WM8400)   += snd-soc-wm8400.o
 obj-$(CONFIG_SND_SOC_WM8510)   += snd-soc-wm8510.o
@@ -110,4 +112,5 @@ obj-$(CONFIG_SND_SOC_WM_HUBS)       += snd-soc-wm-hubs.o
 obj-$(CONFIG_SND_SOC_MAX9877)  += snd-soc-max9877.o
 obj-$(CONFIG_SND_SOC_TPA6130A2)        += snd-soc-tpa6130a2.o
 
-CFLAGS_uda1380.o := -DDEBUG
\ No newline at end of file
+CFLAGS_uda1380.o := -DDEBUG
+
diff --git a/sound/soc/codecs/lpc315x_codec.c b/sound/soc/codecs/lpc315x_codec.c
new file mode 100644 (file)
index 0000000..546d649
--- /dev/null
@@ -0,0 +1,1559 @@
+/*
+ * sound/soc/codecs/lpc315x_codec.c
+ *
+ * Author: Kevin Wells <kevin.wells@nxp.com>
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include <mach/lpc315x_ad.h>
+#include "lpc315x_codec.h"
+
+#define ANALOG_DIE_I2C_ADDR    (0x0C)
+
+#define DEC_GAIN               (0x0)
+#define AVC_COA_VOL            (0x7C0)
+#define AVC_FIN_VOL            (0x1)
+
+/*
+ * LPC315X CODEC structure
+ * */
+static struct snd_soc_codec *lpc315x_codec;
+
+/*
+ * LPC315X codec private data
+ * */
+struct lpc315x_codec_priv {
+       struct snd_soc_codec    codec;
+       u32                     lpc315x_reg_cache[LPC315X_CODEC_NUM_REGS];
+       struct work_struct      work;
+};
+static struct lpc315x_codec_priv *lpc315x_codec_prv;
+
+/*
+ * LPC315X CODEC register cache
+ */
+static const u32 lpc315x_codec_regs[LPC315X_CODEC_NUM_REGS] = {
+       0x00000000, /* PGA */
+       0x0002FFFC, /* AIN_1 */
+       0x8000F5FB, /* AOUT */
+       0x00000000, /* DEC */
+       0x00000200, /* INT0 */
+       0x00000000, /* INT1 */
+       0x00010303, /* I2SMUX1 */
+       0x00000000, /* DEC_STA */
+};
+
+/* Register cache dirty flags */
+static unsigned long lpc315x_codec_cache_dirty;
+
+/*
+ * Read LPC315X CODEC register cache
+ */
+static inline u32 lpc315x_codec_read_reg_cache(struct snd_soc_codec *codec,
+               unsigned int reg)
+{
+       u32 *cache = codec->reg_cache;
+
+       /* Check if register offset is valid */
+       if (reg > LPC315X_CODEC_DEC_STA) {
+               return -1;
+       }
+
+       /* Return data */
+       return cache[reg - LPC315X_CODEC_CODEC_START];
+}
+
+/*
+ * Write LPC315X CODEC register cache
+ */
+static inline void lpc315x_codec_write_reg_cache(struct snd_soc_codec
+               *codec, unsigned int reg, u32 value)
+{
+       u32 *cache = codec->reg_cache;
+
+       /* Check if register offset is valid */
+       if (reg > LPC315X_CODEC_DEC_STA)
+               return;
+
+       /* Set dirty flag */
+       if (cache[reg - LPC315X_CODEC_CODEC_START] != value) {
+               set_bit((reg - LPC315X_CODEC_CODEC_START),
+                               &lpc315x_codec_cache_dirty);
+       }
+
+       /* Write to register cache */
+       cache[reg - LPC315X_CODEC_CODEC_START] = value;
+       return;
+}
+
+/*
+ * Write to LPC315X CODEC registers using I2C functions
+ */
+static int lpc315x_codec_register_rw(struct snd_soc_codec *codec,
+               unsigned int reg, u32 *value, int read)
+{
+       int ret = 0;
+       u8 off[2], data[6];
+       u32 val = 0;
+
+       if(read) {
+               /*
+               * Send register offsets:
+               *  data[0] is MSB of register offset (0x00)
+               *  data[1] is LSB of register offset
+               *  Read register data:
+               *  data[0] is Data bits 31:24
+               *  data[1] is Data bits 23:16
+               *  data[2] is Data bits 15:8
+               *  data[3] is Data bits 7:0
+               */
+               /* Register offsets */
+               off[0] = (u8) ((reg >> 8) & 0xFF);
+               off[1] = (u8) (reg & 0xFF);
+
+               /* Read Operation */
+               if (codec->hw_write(codec->control_data, off, 2) == 2) {
+                       if (i2c_master_recv(codec->control_data, data, 4) == 4) {
+                               *value = ((data[0] & 0xFF) << 24);
+                               *value |= ((data[1] & 0xFF) << 16);
+                               *value |= ((data[2] & 0xFF) << 8);
+                               *value |= (data[3] & 0xFF);
+                               pr_debug("lpc315x codec read: hw read %x val %x\n", reg, *value);
+                       }
+                       else {
+                               pr_debug("lpc315x codec read: hw read reg %x failed \n", reg);
+                               ret = -EIO;
+                       }
+               }
+               else {
+                       pr_debug("lpc315x codec read: hw send addres %x failed \n", reg);
+                       ret = -EIO;
+               }
+       }
+       else {
+               /*  Send register offsets, followed by data bytes
+                *  data[0] is MSB of register offset (0x00)
+                *  data[1] is LSB of register offset
+                *  data[2] is Data bits 31:24
+                *  data[3] is Data bits 23:16
+                *  data[4] is Data bits 15:8
+                *  data[5] is Data bits 7:0
+               */
+               val = *value;
+               data[0] = (u8) ((reg >> 8) & 0xFF);
+               data[1] = (u8) (reg & 0xFF);
+               data[2] = (u8) ((val >> 24) & 0xFF);
+               data[3] = (u8) ((val >> 16) & 0xFF);
+               data[4] = (u8) ((val >> 8) & 0xFF);
+               data[5] = (u8) (val & 0xFF);
+
+               /* Write to CODEC register */
+               if (codec->hw_write(codec->control_data, data, 6) != 6) {
+                       pr_debug("lpc315x codec write: hw reg write  %x failed \n", reg);
+                       ret = -EIO;
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * Write to LPC315X CODEC register space
+ */
+static int lpc315x_codec_write(struct snd_soc_codec *codec,
+               unsigned int reg, unsigned int value)
+{
+       int ret = 0;
+
+       /* Write to reg cache */
+       lpc315x_codec_write_reg_cache(codec, reg, value);
+
+       /* Write to Analog Die register */
+       ret = lpc315x_codec_register_rw(codec, reg, &value, 0);
+       if(!ret) {
+               pr_debug("lpc315x codec: hw write %x val %x\n", reg, value);
+
+               /* Clear cache dirty flag */
+               clear_bit((reg - LPC315X_CODEC_CODEC_START),
+                       &lpc315x_codec_cache_dirty);
+       }
+
+       return ret;
+}
+
+/*
+ * LPC315X CODEC work queue function
+ * */
+static void lpc315x_codec_work(struct work_struct *work)
+{
+       u16 bit, reg;
+       u32 data;
+
+       for_each_bit(bit, &lpc315x_codec_cache_dirty,
+                       (LPC315X_CODEC_DEC_STA - LPC315X_CODEC_CODEC_START)) {
+               reg = bit + LPC315X_CODEC_CODEC_START;
+               data = lpc315x_codec_read_reg_cache(lpc315x_codec, reg);
+               lpc315x_codec_write(lpc315x_codec, reg, data);
+               clear_bit(bit, &lpc315x_codec_cache_dirty);
+       }
+}
+
+/*
+ * LPC315X CODEC volatge ramp up & ramp down function
+ * for Playback functionality
+ * This will be called after the DAC widget is powered up
+ * snd after powered down
+ * */
+static int lpc315x_codec_ref_vol(struct snd_soc_dapm_widget *w,
+                                struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       unsigned int cgu_reg = 0;
+       u32 aout_reg, int0_reg, ain_reg, int1_reg, i2srx1_reg;
+
+       switch(event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /* Complete the power up sequence for playback.
+                * This code will be executed after DACs are powered up
+                * */
+               /* By Default, decimator is connected Interpolator.
+                * Connect I2SRX1 output Interpolator.
+                * */
+               i2srx1_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_I2SMUX1);
+               i2srx1_reg &= ~(LPC315X_CODEC_I2SMUX1_DEMUX_MSK);
+               lpc315x_codec_write(codec, LPC315X_CODEC_I2SMUX1, i2srx1_reg);
+
+               /* Connect DAC outputs to HP Amplifiers */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg |= LPC315X_CODEC_AOUT_SWDAC_ON;
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+
+               /* Ramp up Interpolator volatge */
+               int0_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_INT0);
+               int0_reg &= ~(LPC315X_CODEC_INT0_PD_DAC);
+               lpc315x_codec_write(codec, LPC315X_CODEC_INT0, int0_reg);
+               mdelay(30);
+
+               /* Ramp up Reference Volatge */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg |= LPC315X_CODEC_AOUT_VREF_SLOW_UP;
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+               mdelay(500);
+
+               /* Set Interpolator Volume & Unmute */
+               int1_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_INT1);
+               int1_reg &= ~(LPC315X_CODEC_INT1_MAS_MUTE |
+                               LPC315X_CODEC_INT1_MAS_VOL_L_MSK |
+                            LPC315X_CODEC_INT1_MAS_VOL_R_MSK);
+               lpc315x_codec_write(codec, LPC315X_CODEC_INT1, int1_reg);
+
+               /* By default AVC is muted, set AVC Volume & unmute */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg &= ~(LPC315X_CODEC_AOUT_AVC_COA_GAIN_MSK |
+                               LPC315X_CODEC_AOUT_AVC_FIN_GAIN_MSK);
+               aout_reg |=
+                       ((AVC_COA_VOL << LPC315X_CODEC_AOUT_AVC_COA_GAIN_POS)|
+                       (AVC_FIN_VOL << LPC315X_CODEC_AOUT_AVC_FIN_GAIN_POS));
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+
+               break;
+
+       case SND_SOC_DAPM_POST_PMD:
+               /* Complete the power down sequence for playback.
+                * This code will be executed aftet DACs are powered down
+                * */
+
+               /* Power down Central Reference source */
+               ain_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AIN1);
+               ain_reg |= (LPC315X_CODEC_AIN1_PD_VCOM_VREF1);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AIN1, ain_reg);
+
+               /* Power down Reference buffer Voltage */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg |= (LPC315X_CODEC_AOUT_VREF_SLOW);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+
+               /* Power down HP Amplifiers */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg |= (LPC315X_CODEC_AOUT_PD_HP_L |
+                               LPC315X_CODEC_AOUT_PD_HP_C |
+                               LPC315X_CODEC_AOUT_PD_HP_R );
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+
+               /* Switch off NS, DSP, DAC clocks */
+               lpc315x_codec_register_rw(codec, LPC315X_CODEC_CGU,
+                               &cgu_reg, 1);
+               cgu_reg |= (LPC315X_CODEC_CGU_PD_DSP_CLK |
+                               LPC315X_CODEC_CGU_PD_NS_CLK |
+                               LPC315X_CODEC_CGU_PD_I2SRX_BCLK |
+                               LPC315X_CODEC_CGU_PD_I2SRX_SYSCLK |
+                               LPC315X_CODEC_CGU_PD_I2C256FS_CLK |
+                               LPC315X_CODEC_CGU_PD_DAC_CLK);
+               lpc315x_codec_register_rw(codec, LPC315X_CODEC_CGU,
+                               &cgu_reg, 0);
+               break;
+       default:
+               BUG();
+       }
+
+       return 0;
+}
+
+/* Digital Demux control */
+static const char *lpc315x_codec_dmux_sel[] = {
+       "I2SRX1",
+       "Decimator",
+};
+
+/* De-emphasis control */
+static const char *lpc315x_codec_deemp_sel[] = {
+       "None",
+       "32kHz",
+       "44.1kHz",
+       "48kHz",
+       "96kHz",
+};
+
+/* Silence detection time window */
+static const char *lpc315x_codec_sdet_sel[] = {
+       "3200fs samples",
+       "4800fs samples",
+       "9600fs samples",
+       "19200fs samples",
+};
+
+/* Nosie Shaper Enums */
+static const char *lpc315x_codec_ns_sel[] = {
+       "1-bit",
+       "4-bit",
+       "5-bit",
+       "6-bit",
+};
+
+/* Interpolator Input data rate Enums */
+static const char *lpc315x_codec_os_sel[] = {
+       "1fs",
+       "2fs0",
+       "2fs1",
+       "8fs",
+};
+
+/* Interpolator Filter Settings Enums */
+static const char *lpc315x_codec_fil_sel[] = {
+       "2fs-8fs FIR for slow0",
+       "2fs-8fs FIR for slow1",
+       "2fs-8fs FIR for slow2",
+       "2fs-8fs FIR for sharp",
+};
+
+/* Data Weight Enums */
+static const char *lpc315x_codec_dwa_sel[] = {
+       "Uni-Directional",
+       "Bi-Directional",
+};
+
+/* MUX0 enum */
+static const char *lpc315x_mux0_sel[] = {
+       "Tuner In",
+       "Line In",
+};
+
+/* MUX1 enum */
+static const char *lpc315x_mux1_sel[] = {
+       "Lin/Tun In",
+       "Mic In",
+};
+
+/* DAC Switch enum */
+static const char *lpc315x_swdac_sel[] = {
+       "AVC only",
+       "Both AVC & SDAC",
+};
+
+/* Digital Demux selection */
+static const struct soc_enum lpc315x_codec_dmux_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_I2SMUX1, 16, 2, lpc315x_codec_dmux_sel);
+
+/* from -64 to 63 dB in 0.5 dB steps (-128...63) */
+static DECLARE_TLV_DB_SCALE(int_gain_tlv, -6400, 50, 1);
+
+/* De-emphasis settings */
+static const struct soc_enum lpc315x_codec_deemp_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_INT0, 0, 5, lpc315x_codec_deemp_sel);
+
+/* Silence Detector Settings */
+static const struct soc_enum lpc315x_codec_sdet_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_INT0, 4, 4, lpc315x_codec_sdet_sel);
+
+/* Interpolator Input data rate */
+static const struct soc_enum lpc315x_codec_ns_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_INT0, 9, 4, lpc315x_codec_ns_sel);
+
+/* Interpolator Input data rate */
+static const struct soc_enum lpc315x_codec_os_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_INT0, 11, 4, lpc315x_codec_os_sel);
+
+/* Interpolator Filter Settings */
+static const struct soc_enum lpc315x_codec_fil_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_INT0, 13, 4, lpc315x_codec_fil_sel);
+
+/* from -64 to 63 dB in 0.5 dB steps (-128...63) */
+static DECLARE_TLV_DB_SCALE(dec_gain_tlv, -6400, 50, 1);
+
+/* Data weight algorithm */
+static const struct soc_enum lpc315x_codec_dwa_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_AOUT, 2, 2, lpc315x_codec_dwa_sel);
+
+/* from 0 to 24dB in 3dB steps */
+static DECLARE_TLV_DB_SCALE(line_pga_tlv, 0, 300, 0);
+
+/* from 0 to 24dB in 3dB steps */
+static DECLARE_TLV_DB_SCALE(mic_pga_tlv, 0, 300, 0);
+
+/*
+ * LPC315X AVC Coarse Volume control functions
+ * */
+/* from 0 to -60dB in 6dB steps */
+static DECLARE_TLV_DB_SCALE(avc_coa_tlv, -6000, 600, 1);
+
+/*
+ * LPC315X AVC Coarse Volume put function
+ * */
+static int snd_soc_lpc315x_coa_put_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u32 i, val, reg, avc_gain;
+
+       val = (ucontrol->value.integer.value[0] & 0xF);
+       /* conversion to AVC Coarse volume bit pattern
+       * 0000  = Mute  (00000000000) = 0x000
+       * 0001  = -60dB (10000000000) = 0x400
+       * 0010  = -54dB (11000000000) = 0x600
+       * 0011  = -48dB (11100000000) = 0x700
+       * 0100  = -42dB (11110000000) = 0x780
+       * 0101  = -36dB (11111000000) = 0x7C0
+       * 0110  = -30dB (11111100000) = 0x7E0
+       * 0111  = -24dB (11111110000) = 0x7F0
+       * 1000  = -18dB (11111111000) = 0x7F8
+       * 1001  = -12dB (11111111100) = 0x7FC
+       * 1010  = -06dB (11111111110) = 0x7FE
+       * 1011  =  00dB (11111111111) = 0x7FF (Max)
+       */
+       if(val >= 0xB) {
+               avc_gain = 0x7FF; /* 0dB */
+       }
+       else if(!val) {
+               avc_gain = 0; /* Mute */
+       }
+       else {
+               /* Calculate bit pattern */
+               avc_gain = 0x400;
+               for(i = 1; i < val; i++) {
+                       avc_gain |= (avc_gain >> 1);
+               }
+       }
+
+       /* Write to register */
+       reg = lpc315x_codec_read_reg_cache(codec, LPC315X_CODEC_AOUT);
+               reg &= ~(0x7FF << LPC315X_CODEC_AOUT_AVC_COA_GAIN_POS);
+       reg |= (avc_gain << LPC315X_CODEC_AOUT_AVC_COA_GAIN_POS);
+       lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, reg);
+
+       return 0;
+}
+
+/*
+ * LPC315X AVC Coarse Volume get function
+ * */
+static int snd_soc_lpc315x_coa_get_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u32 reg, cnt;
+
+       /* Get value from register */
+       reg = lpc315x_codec_read_reg_cache(codec, LPC315X_CODEC_AOUT);
+
+       /* Calculate count value from register value */
+               reg = ((reg >> LPC315X_CODEC_AOUT_AVC_COA_GAIN_POS) & 0x7FF);
+       if(reg >= 0x7FF) {
+               cnt = 0xB;
+       }
+       else if(!reg) {
+               cnt = 0;
+       }
+       else {
+               for(cnt = 1; reg; cnt++) {
+                       reg &= (reg - 1);
+               }
+       }
+       ucontrol->value.integer.value[0] = cnt;
+       return 0;
+
+}
+
+/* AVC Coarse Volume TLV macro */
+#define SOC_LPC315X_AVCCOA_SINGLE_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_lpc315x_coa_get_volsw,\
+       .put = snd_soc_lpc315x_coa_put_volsw, \
+       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+/*
+ * LPC315X AVC Fine Volume control functions
+ * */
+/* from 0 to -4.5dB in 1.5dB steps */
+static DECLARE_TLV_DB_SCALE(avc_fin_tlv, -450, 150, 1);
+
+/*
+ * LPC315X AVC Fine Volume put function
+ * */
+static int snd_soc_lpc315x_fin_put_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u32 val, reg, avc_gain;
+
+       val = (ucontrol->value.integer.value[0] & 0x7);
+       /* conversion to AVC Fine volume bit pattern
+       * 000  = -4.5dB (000) = 0x0
+       * 001  = -3.0dB (001) = 0x1
+       * 010  = -1.5dB (011) = 0x3
+       * 011  =  0.0dB (111) = 0x7 (Max)
+       */
+       if( val >= 0x3) {
+               val = 0x3;
+       }
+       avc_gain = (1 << val) - 1;
+
+       /* Write into register */
+       reg = lpc315x_codec_read_reg_cache(codec, LPC315X_CODEC_AOUT);
+               reg &= ~(0x7 << LPC315X_CODEC_AOUT_AVC_FIN_GAIN_POS);
+       reg |= (avc_gain << LPC315X_CODEC_AOUT_AVC_FIN_GAIN_POS);
+       lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, reg);
+
+       return 0;
+}
+
+/*
+ * LPC315X AVC Fine Volume get function
+ * */
+static int snd_soc_lpc315x_fin_get_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       u32 reg, cnt;
+
+       /* Get value from register */
+       reg = lpc315x_codec_read_reg_cache(codec, LPC315X_CODEC_AOUT);
+
+       /* Calculate count value from register value */
+               reg = ((reg >> LPC315X_CODEC_AOUT_AVC_FIN_GAIN_POS) & 0x7);
+       cnt = 0;
+       while(reg != ((1 << cnt) - 1)) {
+               cnt++;
+       }
+
+       if(cnt >= 0x3) {
+               cnt = 0x3;
+       }
+       ucontrol->value.integer.value[0] = cnt;
+       return 0;
+
+}
+
+/* AVC Fine Volume TLV macro */
+#define SOC_LPC315X_AVCFIN_SINGLE_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_lpc315x_fin_get_volsw,\
+       .put = snd_soc_lpc315x_fin_put_volsw, \
+       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+/*
+ * Audio Controls
+ * */
+static const struct snd_kcontrol_new lpc315x_codec_snd_ctrls[] = {
+       /* MIC PGA Gain Control */
+       SOC_SINGLE_TLV("Mic Capture Volume", LPC315X_CODEC_PGA, 4, 8, 0,
+                       mic_pga_tlv),
+       /* PGA Gain Control for Left & Right channels */
+       SOC_DOUBLE_TLV("Line Capture Volume", LPC315X_CODEC_PGA, 8, 0, 8, 0,
+                       line_pga_tlv),
+       /* ADC Dither Input for Left & Right Channels */
+       SOC_DOUBLE("ADC Dither Input", LPC315X_CODEC_AIN1, 3, 2, 1, 0),
+       /* Feedback in Loopfilter */
+       SOC_DOUBLE("Loopfilter Feedback", LPC315X_CODEC_AIN1, 1, 0, 1, 0),
+       /* AVC outputs to AB Amplifier */
+       SOC_SINGLE("AVC Output Switch", LPC315X_CODEC_AOUT, 31, 1, 0),
+       /* AVC Coarse Volume Control */
+       SOC_LPC315X_AVCCOA_SINGLE_TLV("AVC Coarse Volume Control",
+                       LPC315X_CODEC_AOUT, 16, 11, 0, avc_coa_tlv),
+       /* AVC Fine Volume Control */
+       SOC_LPC315X_AVCFIN_SINGLE_TLV("AVC Fine Volume Control",
+                       LPC315X_CODEC_AOUT, 27, 3, 0, avc_fin_tlv),
+       /* Data Weight Algorithm */
+       SOC_ENUM("Data Weight Algorithm", lpc315x_codec_dwa_enum),
+       /* AGC Time settings */
+       SOC_SINGLE("AGC Timing", LPC315X_CODEC_DEC, 25, 7, 0),
+       /* AGC Levels */
+       SOC_SINGLE("AGC Target level", LPC315X_CODEC_DEC, 23, 3, 1),
+       /* AGC Switch */
+       SOC_SINGLE("AGC Switch", LPC315X_CODEC_DEC, 22, 1, 0),
+       /* Decimator Mute */
+       SOC_SINGLE("ADC Dec Mute", LPC315X_CODEC_DEC, 21, 1, 0),
+       /* Channel Polarity Inverting Switch */
+       SOC_SINGLE("ADC Polarity Inv Switch", LPC315X_CODEC_DEC,
+                       20, 1, 0),
+       /* Input DC blocking filter (before Decimator) */
+       SOC_SINGLE("Input DC Filter Switch", LPC315X_CODEC_DEC, 19, 1, 0),
+       /* Output DC blocking filter (after Decimator) */
+       SOC_SINGLE("Output DC Filter Switch", LPC315X_CODEC_DEC, 18, 1, 0),
+       /* Enable soft start up after reset switch */
+       SOC_SINGLE("Db Linear After Reset Switch", LPC315X_CODEC_DEC,
+                       17, 1, 0),
+       /* Enable  sy timer after reset switch */
+       SOC_SINGLE("Timer After Reset Switch", LPC315X_CODEC_DEC, 16, 1, 0),
+       /* ADC Capture volume */
+       SOC_DOUBLE_S8_TLV("ADC Capture Volume", LPC315X_CODEC_DEC,
+                       -128, 127, dec_gain_tlv),
+       /* DAC Polarity Inversion Switch */
+       SOC_SINGLE("DAC Polarity inverting Switch", LPC315X_CODEC_INT0,
+                       15, 1, 0),
+       /* Interpolator Filter Settings */
+       SOC_ENUM("INT Filter Settings", lpc315x_codec_fil_enum),
+       /* Input data rate */
+       SOC_ENUM("Oversampling Input", lpc315x_codec_os_enum),
+       /* Nosie Shaper Settings */
+       SOC_ENUM("Noise Shaper", lpc315x_codec_ns_enum),
+       /* Silence Enable Switch */
+       SOC_SINGLE("Overule Silence Dit Switch", LPC315X_CODEC_INT0,
+                       3, 1, 0),
+       /* Silence Detector Switch */
+       SOC_SINGLE("Silence Detector Switch", LPC315X_CODEC_INT0, 6, 1, 0),
+       /* Silence Detector Settings */
+       SOC_ENUM("Silence Detector Setting", lpc315x_codec_sdet_enum),
+       /* De-emphasis settings */
+       SOC_ENUM("PCM Playback De-emphasis", lpc315x_codec_deemp_enum),
+       /* Interpolator Volume Control Settings */
+       SOC_DOUBLE_S8_TLV("ADC Playback Volume", LPC315X_CODEC_INT1,
+                       -128, 127, int_gain_tlv),
+       /* Digital Mux Selection */
+       SOC_ENUM("Digital Mux Switch", lpc315x_codec_dmux_enum),
+};
+
+static const struct soc_enum lpc315x_muxl0_sel_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_AIN1, 17, 2, lpc315x_mux0_sel);
+static const struct soc_enum lpc315x_muxr0_sel_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_AIN1, 15, 2, lpc315x_mux0_sel);
+static const struct soc_enum lpc315x_muxl1_sel_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_AIN1, 18, 2, lpc315x_mux1_sel);
+static const struct soc_enum lpc315x_muxr1_sel_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_AIN1, 16, 2, lpc315x_mux1_sel);
+static const struct soc_enum lpc315x_swdac_sel_enum =
+       SOC_ENUM_SINGLE(LPC315X_CODEC_AOUT, 31, 2, lpc315x_swdac_sel);
+static const struct snd_kcontrol_new lpc315x_muxl0_control =
+       SOC_DAPM_ENUM("Route", lpc315x_muxl0_sel_enum);
+static const struct snd_kcontrol_new lpc315x_muxr0_control =
+       SOC_DAPM_ENUM("Route", lpc315x_muxr0_sel_enum);
+static const struct snd_kcontrol_new lpc315x_muxl1_control =
+       SOC_DAPM_ENUM("Route", lpc315x_muxl1_sel_enum);
+static const struct snd_kcontrol_new lpc315x_muxr1_control =
+       SOC_DAPM_ENUM("Route", lpc315x_muxr1_sel_enum);
+static const struct snd_kcontrol_new lpc315x_output_mux_control =
+       SOC_DAPM_ENUM("Route", lpc315x_swdac_sel_enum);
+
+/*
+ * Audio widgets
+ * */
+static const struct snd_soc_dapm_widget lpc315x_codec_dapm_widgets[] = {
+       SND_SOC_DAPM_MUX("Output Mux", SND_SOC_NOPM, 0, 0,
+               &lpc315x_output_mux_control),
+       SND_SOC_DAPM_MUX("MUX_L0", SND_SOC_NOPM, 0, 0,
+               &lpc315x_muxl0_control),
+       SND_SOC_DAPM_MUX("MUX_R0", SND_SOC_NOPM, 0, 0,
+               &lpc315x_muxr0_control),
+       SND_SOC_DAPM_MUX("MUX_L1", SND_SOC_NOPM, 0, 0,
+               &lpc315x_muxl1_control),
+       SND_SOC_DAPM_MUX("MUX_R1", SND_SOC_NOPM, 0, 0,
+               &lpc315x_muxr1_control),
+       SND_SOC_DAPM_PGA("Mic LNA", LPC315X_CODEC_AIN1, 14, 1, NULL, 0),
+       SND_SOC_DAPM_PGA("Mic PGA", LPC315X_CODEC_AIN1, 12, 1, NULL, 0),
+       SND_SOC_DAPM_PGA("Left PGA", LPC315X_CODEC_AIN1, 13, 1, NULL, 0),
+       SND_SOC_DAPM_PGA("Right PGA", LPC315X_CODEC_AIN1, 11, 1, NULL, 0),
+       SND_SOC_DAPM_PGA("Mic SDC", LPC315X_CODEC_AIN1, 9, 1, NULL, 0),
+       SND_SOC_DAPM_PGA("Left SDC", LPC315X_CODEC_AIN1, 10, 1, NULL, 0),
+       SND_SOC_DAPM_PGA("Right SDC", LPC315X_CODEC_AIN1, 8, 1, NULL, 0),
+       SND_SOC_DAPM_ADC("SADC Left", "Left Capture",
+                       LPC315X_CODEC_AIN1, 7, 1),
+       SND_SOC_DAPM_ADC("SADC Right", "Right Capture",
+                       LPC315X_CODEC_AIN1, 6, 1),
+       SND_SOC_DAPM_INPUT("ADC_MIC"),
+       SND_SOC_DAPM_INPUT("ADC_VINL"),
+       SND_SOC_DAPM_INPUT("ADC_VINR"),
+       SND_SOC_DAPM_INPUT("ADC_TINL"),
+       SND_SOC_DAPM_INPUT("ADC_TINR"),
+       SND_SOC_DAPM_OUTPUT("HP_OUTL"),
+       SND_SOC_DAPM_OUTPUT("HP_OUTC"),
+       SND_SOC_DAPM_OUTPUT("HP_OUTR"),
+       SND_SOC_DAPM_PGA("HP Amp Left", LPC315X_CODEC_AOUT, 14, 1, NULL, 0),
+       SND_SOC_DAPM_PGA("HP Amp Right", LPC315X_CODEC_AOUT, 12, 1, NULL, 0),
+       SND_SOC_DAPM_PGA("AVC Left", LPC315X_CODEC_AOUT, 1, 1, NULL, 0),
+       SND_SOC_DAPM_PGA("AVC Right", LPC315X_CODEC_AOUT, 0, 1, NULL, 0),
+       SND_SOC_DAPM_DAC("SDAC Right", "Right Playback",
+                       LPC315X_CODEC_AOUT, 4, 1),
+       SND_SOC_DAPM_DAC_E("SDAC Left", "Left Playback",
+                       LPC315X_CODEC_AOUT, 5, 1, lpc315x_codec_ref_vol,
+                       SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+};
+
+/*
+ * Audio paths
+ * */
+static const struct snd_soc_dapm_route audio_map[] = {
+       /* Audio output path */
+       {"HP_OUTL", NULL, "HP Amp Left"},
+       {"HP_OUTR", NULL, "HP Amp Right"},
+
+       /* Headphone Amplifier */
+       {"HP Amp Left", NULL, "AVC Left"},
+       {"HP Amp Right", NULL, "AVC Right"},
+       {"HP Amp Right", "Both AVC & SDAC", "SDAC Right"},
+       {"HP Amp Left", "Both AVC & SDAC", "SDAC Left"},
+
+       /* Audio  Input paths */
+       /* ADC <- MUX1 */
+       {"SADC Left", NULL, "MUX_L1"},
+       {"SADC Right", NULL, "MUX_R1"},
+
+       /* MUX1 <- Mic SDC */
+       {"MUX_L1", "Mic In", "Mic SDC"},
+       {"MUX_R1", "Mic In", "Mic SDC"},
+       /* Mic SDC <- Mic PGA */
+       {"Mic SDC", NULL, "Mic PGA"},
+       /* Mic PGA <- Mic LNA */
+       {"Mic PGA", NULL, "Mic LNA"},
+       /* Mic LNA <- MIC Input */
+       {"Mic LNA", NULL, "ADC_MIC"},
+
+       /* MUX1 <- Left & Right SDC */
+       {"MUX_L1", "Lin/Tun In", "Left SDC"},
+       {"MUX_R1", "Lin/Tun In", "Right SDC"},
+       /* Left & Right SDC <- Left & Right PGA */
+       {"Left SDC", NULL, "Left PGA"},
+       {"Right SDC", NULL, "Right PGA"},
+       /* Left & Right PGA <- MUX0 */
+       {"Left PGA", NULL, "MUX_L0"},
+       {"Right PGA", NULL, "MUX_R0"},
+       /* MUX0 <- Line/Tuner Stereo Inputs */
+       {"MUX_L0", "Line In", "ADC_VINL"},
+       {"MUX_R0", "Line In", "ADC_VINR"},
+       {"MUX_L0", "Tuner In", "ADC_TINL"},
+       {"MUX_R0", "Tuner In", "ADC_TINR"},
+
+};
+
+/*
+ * LPC315X CODEC Add Audio widgets function
+ * */
+static int lpc315x_codec_add_widgets(struct snd_soc_codec *codec)
+{
+       /* Add LPC315X CODEC audio widgets */
+       snd_soc_dapm_new_controls(codec, lpc315x_codec_dapm_widgets,
+                                 ARRAY_SIZE(lpc315x_codec_dapm_widgets));
+
+       /* Add LPC315X CODEC audio paths */
+       snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
+       return 0;
+}
+
+/*
+ * LPC315X CODEC set format function for both Playback & Capture
+ * */
+static int lpc315x_codec_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u32 i2s_mux_reg;
+
+       /* Set up DAI based upon fmt */
+       i2s_mux_reg = lpc315x_codec_read_reg_cache(codec,
+                       LPC315X_CODEC_I2SMUX1);
+       i2s_mux_reg &= ~(LPC315X_CODEC_I2SMUX1_TX_FMT_MSK |
+                       LPC315X_CODEC_I2SMUX1_RX_FMT_MSK);
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               i2s_mux_reg |= LPC315X_CODEC_I2SMUX1_TX_FMT_I2S |
+                       LPC315X_CODEC_I2SMUX1_RX_FMT_I2S;
+               break;
+       case SND_SOC_DAIFMT_LSB:
+               i2s_mux_reg |= LPC315X_CODEC_I2SMUX1_TX_FMT_LSB_16 |
+                       LPC315X_CODEC_I2SMUX1_RX_FMT_LSB_16;
+               break;
+       }
+
+       /* DATAI is slave only, so in single-link mode,
+        * this has to be slave */
+       if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+               return -EINVAL;
+
+       lpc315x_codec_write(codec, LPC315X_CODEC_I2SMUX1, i2s_mux_reg);
+
+       return 0;
+}
+
+/*
+ * LPC315X CODEC set format function for Playback
+ * */
+static int lpc315x_codec_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u32 i2s_mux_reg;
+
+       /* Set up DAI based upon fmt */
+       i2s_mux_reg = lpc315x_codec_read_reg_cache(codec,
+                       LPC315X_CODEC_I2SMUX1);
+       i2s_mux_reg &= ~(LPC315X_CODEC_I2SMUX1_RX_FMT_MSK);
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               i2s_mux_reg |= LPC315X_CODEC_I2SMUX1_RX_FMT_I2S;
+               break;
+       case SND_SOC_DAIFMT_LSB:
+               i2s_mux_reg |= LPC315X_CODEC_I2SMUX1_RX_FMT_LSB_16;
+               break;
+       }
+
+       /* DATAI is slave only, so this has to be slave */
+       if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+               return -EINVAL;
+
+       lpc315x_codec_write(codec, LPC315X_CODEC_I2SMUX1, i2s_mux_reg);
+
+       return 0;
+}
+
+/*
+ * LPC315X CODEC set format function for Capture
+ * */
+static int lpc315x_codec_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u32 i2s_mux_reg;
+
+       /* set up DAI based upon fmt */
+       i2s_mux_reg = lpc315x_codec_read_reg_cache(codec,
+                       LPC315X_CODEC_I2SMUX1);
+       i2s_mux_reg &= ~(LPC315X_CODEC_I2SMUX1_TX_FMT_MSK);
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               i2s_mux_reg |= LPC315X_CODEC_I2SMUX1_TX_FMT_I2S;
+               break;
+       case SND_SOC_DAIFMT_LSB:
+               i2s_mux_reg |= LPC315X_CODEC_I2SMUX1_TX_FMT_LSB_16;
+               break;
+       }
+
+       /* DATAI is slave only, so this has to be slave */
+       if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
+               return -EINVAL;
+
+       lpc315x_codec_write(codec, LPC315X_CODEC_I2SMUX1, i2s_mux_reg);
+
+       return 0;
+}
+
+/*
+ * LPC315X CODEC trigger function
+ * */
+static int lpc315x_codec_trigger(struct snd_pcm_substream *substream,
+               int cmd, struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       struct lpc315x_codec_priv *prv = codec->private_data;
+       u32 int0_reg = lpc315x_codec_read_reg_cache(codec,
+                       LPC315X_CODEC_INT0);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               lpc315x_codec_write_reg_cache(codec, LPC315X_CODEC_INT0,
+                       int0_reg & ~LPC315X_CODEC_INT0_SET_SIL);
+               schedule_work(&prv->work);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               lpc315x_codec_write_reg_cache(codec, LPC315X_CODEC_INT0,
+                               int0_reg | LPC315X_CODEC_INT0_SET_SIL);
+               schedule_work(&prv->work);
+               break;
+       }
+
+       return 0;
+}
+
+/*
+ * LPC315X CODEC HW parameters function
+ * */
+static int lpc315x_codec_pcm_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       unsigned int cgu_reg = 0;
+       u32 aout_reg, ain_reg, int0_reg, dec_reg;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* Playback power up sequence */
+               /* Power up DAC Clocks */
+               lpc315x_codec_register_rw(codec, LPC315X_CODEC_CGU,
+                               &cgu_reg, 1);
+               cgu_reg &= ~(LPC315X_CODEC_CGU_PD_DSP_CLK |
+                               LPC315X_CODEC_CGU_PD_NS_CLK |
+                               LPC315X_CODEC_CGU_PD_I2SRX_BCLK |
+                               LPC315X_CODEC_CGU_PD_I2SRX_SYSCLK |
+                               LPC315X_CODEC_CGU_PD_I2C256FS_CLK |
+                               LPC315X_CODEC_CGU_PD_DAC_CLK);
+               lpc315x_codec_register_rw(codec, LPC315X_CODEC_CGU,
+                               &cgu_reg, 0);
+               /* Power up HP */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg &= ~(LPC315X_CODEC_AOUT_PD_HP_L |
+                       LPC315X_CODEC_AOUT_PD_HP_C |
+                       LPC315X_CODEC_AOUT_PD_HP_R);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+
+               /* Power up Reference buffer */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg &= ~(LPC315X_CODEC_AOUT_VREF_SLOW);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+
+               /* Power up Central Reference source */
+               ain_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AIN1);
+               ain_reg &= ~(LPC315X_CODEC_AIN1_PD_VCOM_VREF1);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AIN1, ain_reg);
+
+               /* Power down INT DAC */
+               int0_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_INT0);
+               int0_reg |= (LPC315X_CODEC_INT0_PD_DAC);
+               lpc315x_codec_write(codec, LPC315X_CODEC_INT0, int0_reg);
+       }
+       else {
+               /* Recording power up sequence */
+               /* Switch on ADC, Decimator clocks */
+               lpc315x_codec_register_rw(codec, LPC315X_CODEC_CGU,
+                               &cgu_reg, 1);
+               cgu_reg &= ~(LPC315X_CODEC_CGU_PD_DEC_CLK |
+                               LPC315X_CODEC_CGU_PD_I2STX_BCLK |
+                               LPC315X_CODEC_CGU_PD_I2STX_SYSCLK |
+                               LPC315X_CODEC_CGU_PD_I2C256FS_CLK |
+                               LPC315X_CODEC_CGU_PD_ADCSYS_CLK |
+                               LPC315X_CODEC_CGU_PD_ADC2_CLK |
+                               LPC315X_CODEC_CGU_PD_ADC1_CLK);
+               lpc315x_codec_register_rw(codec, LPC315X_CODEC_CGU,
+                               &cgu_reg, 0);
+               /* Mute */
+               dec_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_DEC);
+               dec_reg |= (LPC315X_CODEC_DEC_MUTE);
+               dec_reg &= ~(LPC315X_CODEC_DEC_AGC_EN);
+               lpc315x_codec_write(codec, LPC315X_CODEC_DEC, dec_reg);
+
+               /* Power up Reference buffer */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg &= ~(LPC315X_CODEC_AOUT_VREF_SLOW);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+
+               /* Power up Central Reference source */
+               ain_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AIN1);
+               ain_reg &= ~(LPC315X_CODEC_AIN1_PD_VCOM_VREF1 |
+                               LPC315X_CODEC_AIN1_PD_BIAS);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AIN1, ain_reg);
+
+               /* Ramp up Reference voltage */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg |= LPC315X_CODEC_AOUT_VREF_SLOW_UP;
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+               mdelay(500);
+
+               /* Enable Decimator & set Volume */
+               dec_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_DEC);
+               dec_reg &= ~(LPC315X_CODEC_DEC_GAIN_R_MASK |
+                               LPC315X_CODEC_DEC_GAIN_L_MASK |
+                               LPC315X_CODEC_DEC_MUTE);
+               dec_reg |= ((DEC_GAIN << LPC315X_CODEC_DEC_GAIN_R_POS) |
+                               (DEC_GAIN << LPC315X_CODEC_DEC_GAIN_L_POS) |
+                               LPC315X_CODEC_DEC_DC_IN_FIL |
+                               LPC315X_CODEC_DEC_DC_OUT_FIL);
+               lpc315x_codec_write(codec, LPC315X_CODEC_DEC, dec_reg);
+       }
+
+       return 0;
+}
+
+/*
+ * LPC315X CODEC PCM shutdown function
+ * */
+static void lpc315x_codec_pcm_shutdown(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_device *socdev = rtd->socdev;
+       struct snd_soc_codec *codec = socdev->card->codec;
+       unsigned int cgu_reg = 0;
+       u32 aout_reg, dec_reg, int0_reg, ain_reg, int1_reg;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               /* Set Interpolator to mute */
+               int1_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_INT1);
+               int1_reg |= (LPC315X_CODEC_INT1_MAS_MUTE);
+               lpc315x_codec_write(codec, LPC315X_CODEC_INT1, int1_reg);
+
+               /* Set AVC to mute  */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg &= ~(LPC315X_CODEC_AOUT_AVC_COA_GAIN_MSK |
+                               LPC315X_CODEC_AOUT_AVC_FIN_GAIN_MSK);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+
+               /* Playback power down sequence */
+               /* Ramp down Reference Volatge */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg &= ~(LPC315X_CODEC_AOUT_VREF_SLOW_UP);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+               mdelay(500);
+
+               /* Ramp down INT DAC volatge */
+               int0_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_INT0);
+               int0_reg |= (LPC315X_CODEC_INT0_PD_DAC);
+               lpc315x_codec_write(codec, LPC315X_CODEC_INT0, int0_reg);
+               mdelay(30);
+
+               /* Disconnect DAC outputs to HP */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg &= ~(LPC315X_CODEC_AOUT_SWDAC_ON);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+
+       }
+       else {
+               /* Mute */
+               dec_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_DEC);
+               dec_reg |= (LPC315X_CODEC_DEC_MUTE);
+               dec_reg &= ~(LPC315X_CODEC_DEC_AGC_EN);
+               lpc315x_codec_write(codec, LPC315X_CODEC_DEC, dec_reg);
+
+               /* Recording power down sequence */
+               /* Ramp down Reference voltage */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg &= ~(LPC315X_CODEC_AOUT_VREF_SLOW_UP);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+               mdelay(500);
+
+               /* Power up Central Reference source */
+               ain_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AIN1);
+               ain_reg |= (LPC315X_CODEC_AIN1_PD_VCOM_VREF1);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AIN1, ain_reg);
+
+               /* Power down Reference buffer */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg |= (LPC315X_CODEC_AOUT_VREF_SLOW);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+
+               /* Power down AVC */
+               aout_reg = lpc315x_codec_read_reg_cache(codec,
+                               LPC315X_CODEC_AOUT);
+               aout_reg |= (LPC315X_CODEC_AOUT_PD_ANVC_L |
+                       LPC315X_CODEC_AOUT_PD_ANVC_R);
+               lpc315x_codec_write(codec, LPC315X_CODEC_AOUT, aout_reg);
+
+               /* Switch off ADC, Decimator clocks */
+               lpc315x_codec_register_rw(codec, LPC315X_CODEC_CGU,
+                               &cgu_reg, 1);
+               cgu_reg &= ~(LPC315X_CODEC_CGU_PD_DEC_CLK |
+                               LPC315X_CODEC_CGU_PD_I2SRX_BCLK |
+                               LPC315X_CODEC_CGU_PD_I2SRX_SYSCLK |
+                               LPC315X_CODEC_CGU_PD_I2C256FS_CLK |
+                               LPC315X_CODEC_CGU_PD_ADCSYS_CLK |
+                               LPC315X_CODEC_CGU_PD_ADC2_CLK |
+                               LPC315X_CODEC_CGU_PD_ADC1_CLK);
+               lpc315x_codec_register_rw(codec, LPC315X_CODEC_CGU,
+                               &cgu_reg, 0);
+       }
+
+       return;
+}
+
+/*
+ * LPC315X CODEC Mute function
+ * */
+static int lpc315x_codec_mute(struct snd_soc_dai *codec_dai,
+               int mute)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u32 int1_reg = lpc315x_codec_read_reg_cache(codec, LPC315X_CODEC_INT1)
+               & ~LPC315X_CODEC_INT1_MAS_MUTE;
+
+       /* Mute */
+       if (mute) {
+               lpc315x_codec_write(codec, LPC315X_CODEC_INT1,
+                               int1_reg | LPC315X_CODEC_INT1_MAS_MUTE);
+       }
+       else {
+               lpc315x_codec_write(codec, LPC315X_CODEC_INT1, int1_reg);
+       }
+
+       return 0;
+}
+
+/*
+ * LPC315X CODEC BIAS control function
+ * */
+static int lpc315x_codec_set_bias_level(struct snd_soc_codec *codec,
+       enum snd_soc_bias_level level)
+{
+       u32 ain1_reg = lpc315x_codec_read_reg_cache(codec,
+                       LPC315X_CODEC_AIN1);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+       case SND_SOC_BIAS_PREPARE:
+       case SND_SOC_BIAS_STANDBY:
+               ain1_reg &= ~LPC315X_CODEC_AIN1_PD_BIAS;
+               lpc315x_codec_write(codec, LPC315X_CODEC_AIN1, ain1_reg);
+               break;
+       case SND_SOC_BIAS_OFF:
+               lpc315x_codec_write(codec, LPC315X_CODEC_AIN1,
+                               LPC315X_CODEC_AIN1_BIAS_OFF);
+               break;
+       }
+
+       codec->bias_level = level;
+       return 0;
+}
+
+/*
+ * LPC315X CODEC supported data rates
+ * */
+#define LPC315X_CODEC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+                      SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
+                      SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+
+/*
+ * DAI operations for both Playback & Capture
+ * */
+static struct snd_soc_dai_ops lpc315x_codec_ops = {
+       .hw_params      = lpc315x_codec_pcm_hw_params,
+       .shutdown       = lpc315x_codec_pcm_shutdown,
+       .trigger        = lpc315x_codec_trigger,
+       .digital_mute = lpc315x_codec_mute,
+       .set_fmt        = lpc315x_codec_set_dai_fmt_both,
+};
+
+/*
+ * DAI operations for Playback
+ * */
+static struct snd_soc_dai_ops lpc315x_codec_ops_playback = {
+       .hw_params      = lpc315x_codec_pcm_hw_params,
+       .shutdown       = lpc315x_codec_pcm_shutdown,
+       .trigger        = lpc315x_codec_trigger,
+       .digital_mute = lpc315x_codec_mute,
+       .set_fmt        = lpc315x_codec_set_dai_fmt_playback,
+};
+
+/*
+ * DAI operations for Capture
+ * */
+static struct snd_soc_dai_ops lpc315x_codec_ops_capture = {
+       .hw_params      = lpc315x_codec_pcm_hw_params,
+       .shutdown       = lpc315x_codec_pcm_shutdown,
+       .trigger        = lpc315x_codec_trigger,
+       .digital_mute = lpc315x_codec_mute,
+       .set_fmt        = lpc315x_codec_set_dai_fmt_capture,
+};
+
+/*
+ * LPC315X CODEC DAI
+ * */
+struct snd_soc_dai lpc315x_codec_dais[] = {
+{
+       .name = "LPC315X_CODEC",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = LPC315X_CODEC_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = LPC315X_CODEC_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .ops = &lpc315x_codec_ops,
+},
+{ /* playback only - dual interface */
+       .name = "LPC315X_CODEC",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = LPC315X_CODEC_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .ops = &lpc315x_codec_ops_playback,
+},
+{ /* capture only - dual interface*/
+       .name = "LPC315X_CODEC",
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = LPC315X_CODEC_RATES,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .ops = &lpc315x_codec_ops_capture,
+},
+};
+EXPORT_SYMBOL_GPL(lpc315x_codec_dais);
+
+/*
+ * LPC315X CODEC suspend function
+ * */
+static int lpc315x_codec_suspend(struct platform_device *pdev,
+               pm_message_t state)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       /* Set to OFF */
+       lpc315x_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+/*
+ * LPC315X CODEC resume function
+ * */
+static int lpc315x_codec_resume(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+       u8 data[6];
+       u32 i, *cache = codec->reg_cache;
+
+       /* Sync reg_cache with the hardware */
+       for (i = 0; i < ARRAY_SIZE(lpc315x_codec_regs); i++) {
+               data[0] = ((i >> 8) & 0xFF);
+               data[1] = (i & 0xFF);
+               data[2] = ((cache[i]  >> 24) & 0xFF);
+               data[3] = ((cache[i] >> 16) & 0xFF);
+               data[4] = ((cache[i] >> 8) & 0xFF);
+               data[5] = (cache[i] & 0xFF);
+               codec->hw_write(codec->control_data, data, 6);
+       }
+
+       /* Set the BIAS levels */
+       lpc315x_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       lpc315x_codec_set_bias_level(codec, codec->suspend_bias_level);
+
+       return 0;
+}
+
+/*
+ * LPC315X CODEC probe function
+ * */
+static int lpc315x_codec_probe(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec;
+       int ret = 0;
+       u32 cgu_reg = 0;
+
+       /* Check if LPC315X CODECX registered */
+       if (lpc315x_codec == NULL) {
+               dev_err(&pdev->dev, "Codec device not registered\n");
+               return -ENODEV;
+       }
+
+       socdev->card->codec = lpc315x_codec;
+       codec = lpc315x_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;
+       }
+
+       /* Select ADC & DAC clocks */
+       lpc315x_codec_register_rw(codec, LPC315X_CODEC_CGU,
+                       &cgu_reg, 1);
+       cgu_reg &= ~(LPC315X_CODEC_CGU_PD_I2C256FS_CLK |
+                       LPC315X_CODEC_CGU_ADCSYS_256FS |
+                       LPC315X_CODEC_CGU_ADC2_128FS |
+                       LPC315X_CODEC_CGU_ADC1_OFF |
+                       LPC315X_CODEC_CGU_CLKDAC_256FS |
+                       LPC315X_CODEC_CGU_CLKINT_256FS);
+       /* DAC Clock not inverted */
+       lpc315x_codec_register_rw(codec, LPC315X_CODEC_CGU, &cgu_reg, 0);
+
+       /* power on device */
+       lpc315x_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Add LPC315X CODEC controls */
+       snd_soc_add_controls(codec, lpc315x_codec_snd_ctrls,
+                               ARRAY_SIZE(lpc315x_codec_snd_ctrls));
+
+       /* Add Widgets & Audio map */
+       lpc315x_codec_add_widgets(codec);
+pcm_err:
+       return ret;
+}
+
+/*
+ * LPC315X CODEC remove function
+ * */
+static int lpc315x_codec_remove(struct platform_device *pdev)
+{
+       struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+       struct snd_soc_codec *codec = socdev->card->codec;
+
+       /* Set BIAS level to off */
+       if (codec->control_data)
+               lpc315x_codec_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       /* Unregister PCMs */
+       snd_soc_free_pcms(socdev);
+
+       /* Remove DAPM widgets */
+       snd_soc_dapm_free(socdev);
+
+       return 0;
+}
+
+/*
+ * CODEC device
+ * */
+struct snd_soc_codec_device soc_codec_lpc315x_dev = {
+       .probe =        lpc315x_codec_probe,
+       .remove =       lpc315x_codec_remove,
+       .suspend =      lpc315x_codec_suspend,
+       .resume =       lpc315x_codec_resume,
+};
+EXPORT_SYMBOL_GPL(soc_codec_lpc315x_dev);
+
+/*
+ * LPC315X CODEC register function
+ * */
+static int lpc315x_codec_register(struct lpc315x_codec_priv *codec_prv)
+{
+       int ret = 0, i;
+       struct snd_soc_codec *codec = &codec_prv->codec;
+
+       /* Check if LPC315X CODEC registered */
+       if (lpc315x_codec) {
+               dev_err(codec->dev, "Another LPC315X CODEC is registered\n");
+               return -EINVAL;
+       }
+
+       /* Initialise CODEC structure */
+       mutex_init(&codec->mutex);
+       INIT_LIST_HEAD(&codec->dapm_widgets);
+       INIT_LIST_HEAD(&codec->dapm_paths);
+       codec->private_data = codec_prv;
+       codec->name = "LPC315X_CODEC";
+       codec->owner = THIS_MODULE;
+       codec->read = lpc315x_codec_read_reg_cache;
+       codec->write = lpc315x_codec_write;
+       codec->bias_level = SND_SOC_BIAS_OFF;
+       codec->set_bias_level = lpc315x_codec_set_bias_level;
+       codec->dai = lpc315x_codec_dais;
+       codec->num_dai = ARRAY_SIZE(lpc315x_codec_dais);
+       codec->reg_cache_size = ARRAY_SIZE(lpc315x_codec_regs);
+       codec->reg_cache = &codec_prv->lpc315x_reg_cache;
+       codec->reg_cache_step = 1;
+
+       /* Copy register contents */
+       memcpy(codec->reg_cache, lpc315x_codec_regs,
+                       sizeof(lpc315x_codec_regs));
+
+       /* Initialise work queue */
+       INIT_WORK(&codec_prv->work, lpc315x_codec_work);
+
+       /* Store devier structure */
+       for (i = 0; i < ARRAY_SIZE(lpc315x_codec_dais); i++)
+               lpc315x_codec_dais[i].dev = codec->dev;
+
+       /* Initialise LPC315X CODEC structure */
+       lpc315x_codec = codec;
+
+       /* Register CODEC */
+       ret = snd_soc_register_codec(codec);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register codec: %d\n", ret);
+               goto err_reset;
+       }
+
+       /* Register CODEC DAIs */
+       ret = snd_soc_register_dais(lpc315x_codec_dais,
+                       ARRAY_SIZE(lpc315x_codec_dais));
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
+               goto err_dai;
+       }
+
+       return 0;
+
+err_dai:
+       snd_soc_unregister_codec(codec);
+err_reset:
+       return ret;
+}
+
+/*
+ * LPC315X CODEC unregister function
+ * */
+static void lpc315x_codec_unregister(struct lpc315x_codec_priv
+                   *codec_prv)
+{
+       /* Unregister CODEC DAIs */
+       snd_soc_unregister_dais(lpc315x_codec_dais,
+                       ARRAY_SIZE(lpc315x_codec_dais));
+
+       /* Unregister CODEC */
+       snd_soc_unregister_codec(&codec_prv->codec);
+
+       /* Free memory */
+       kfree(codec_prv);
+
+       /* Invalidate CODEC structure pointer */
+       lpc315x_codec = NULL;
+}
+
+/*
+ * LPC315X CODEC driver module initialisation function
+ * */
+static int __init lpc315x_codec_init(void)
+{
+       struct i2c_client *i2c;
+       struct snd_soc_codec *codec;
+       int ret = 0;
+
+       printk(KERN_INFO "CODEC I2C Probe called \r\n");
+
+       /* Get I2C client structure structure */
+       i2c = lpc315x_ad_get_i2c_client_struct();
+       if (!i2c) {
+               pr_err("Unable to get I2C client \n");
+               return -ENODEV;
+       }
+
+       /* Allocate memory for LPC315X CODEC private structure */
+       lpc315x_codec_prv = kzalloc(sizeof(struct lpc315x_codec_priv),
+                       GFP_KERNEL);
+       if (lpc315x_codec_prv == NULL) {
+               pr_err("Failed to allocate memory \n");
+               return -ENOMEM;
+       }
+
+       /* Initialise CODEC structure */
+       codec = &lpc315x_codec_prv->codec;
+       codec->hw_write = (hw_write_t) i2c_master_send;
+       codec->control_data = i2c;
+       codec->dev = &i2c->dev;
+
+       /* Register LPC315X CODEC */
+       ret = lpc315x_codec_register(lpc315x_codec_prv);
+       if (ret != 0) {
+               pr_err("Failed to register LPC315X CODEC \n");
+               kfree(lpc315x_codec_prv);
+       }
+
+       return ret;
+}
+
+/*
+ * LPC315X CODEC driver module exit function
+ * */
+static void __exit lpc315x_codec_exit(void)
+{
+       /* Unregister LPC315X CODEC */
+       lpc315x_codec_unregister(lpc315x_codec_prv);
+
+       return;
+}
+
+module_init(lpc315x_codec_init);
+module_exit(lpc315x_codec_exit);
+
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
+MODULE_DESCRIPTION("ASoC Audio CODEC driver for LPC315X codec");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lpc315x_codec.h b/sound/soc/codecs/lpc315x_codec.h
new file mode 100644 (file)
index 0000000..9502432
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * sound/soc/codecs/lpc315x_codec.h
+ *
+ * Author: Kevin Wells <kevin.wells@nxp.com>
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * 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 _LPC315X_CODEC_H
+#define _LPC315X_CODEC_H
+
+/* LPC315X CODEC register addresses */
+#define LPC315X_CODEC_CODEC_START      (0x00000010)
+
+#define LPC315X_CODEC_CGU              (0x00000002)
+#define LPC315X_CODEC_PGA              (0x00000010)
+#define LPC315X_CODEC_AIN1             (0x00000011)
+#define LPC315X_CODEC_AOUT             (0x00000012)
+#define LPC315X_CODEC_DEC              (0x00000013)
+#define LPC315X_CODEC_INT0             (0x00000014)
+#define LPC315X_CODEC_INT1             (0x00000015)
+#define LPC315X_CODEC_I2SMUX1          (0x00000016)
+#define LPC315X_CODEC_DEC_STA          (0x00000017)
+
+#define LPC315X_CODEC_NUM_REGS (0x8)
+
+/* PGA register bit definitions */
+#define LPC315X_CODEC_PGA_RC           (0x0000000F)
+#define LPC315X_CODEC_PGA_MIC          (0x000000F0)
+#define LPC315X_CODEC_PGA_LC           (0x00000F00)
+
+/* AIN1 register bit definitions */
+#define LPC315X_CODEC_AIN1_XFBADR              (0x00000001)
+#define LPC315X_CODEC_AIN1_XFBADL              (0x00000002)
+#define LPC315X_CODEC_AIN1_DITHR               (0x00000004)
+#define LPC315X_CODEC_AIN1_PD_VCOM_VREF1       (0x00000010)
+#define LPC315X_CODEC_AIN1_PD_BIAS             (0x00000020)
+#define LPC315X_CODEC_AIN1_PD_ADCR             (0x00000040)
+#define LPC315X_CODEC_AIN1_PD_ADCL             (0x00000080)
+#define LPC315X_CODEC_AIN1_PD_SDCR             (0x00000100)
+#define LPC315X_CODEC_AIN1_PD_SDCM             (0x00000200)
+#define LPC315X_CODEC_AIN1_PD_SDCL             (0x00000400)
+#define LPC315X_CODEC_AIN1_PD_PGAR             (0x00000800)
+#define LPC315X_CODEC_AIN1_PD_PGAM             (0x00001000)
+#define LPC315X_CODEC_AIN1_PD_PGAL             (0x00002000)
+#define LPC315X_CODEC_AIN1_PD_LNA              (0x00004000)
+
+#define LPC315X_CODEC_AIN1_BIAS_OFF            (0x00007FFF)
+
+/* MUX0 & MUX1 input selection for right channel */
+#define LPC315X_CODEC_AIN1_MUXR_MSK            (0x00018000)
+#define LPC315X_CODEC_AIN1_MUXR_TIN            (0x00000000)
+#define LPC315X_CODEC_AIN1_MUXR_LIN            (0x00008000)
+#define LPC315X_CODEC_AIN1_MUXR_MIN_TBY                (0x00010000)
+#define LPC315X_CODEC_AIN1_MUXR_MIN_LBY                (0x00018000)
+
+/* MUX0 & MUX1 input selection for left channel */
+#define LPC315X_CODEC_AIN1_MUXL_MSK            (0x00060000)
+#define LPC315X_CODEC_AIN1_MUXL_TIN            (0x00000000)
+#define LPC315X_CODEC_AIN1_MUXL_LIN            (0x00020000)
+#define LPC315X_CODEC_AIN1_MUXL_MIN_TBY                (0x00040000)
+#define LPC315X_CODEC_AIN1_MUXL_MIN_LBY                (0x00060000)
+
+/* AOUT register bit definitions */
+#define LPC315X_CODEC_AOUT_PD_ANVC_R           (0x00000001)
+#define LPC315X_CODEC_AOUT_PD_ANVC_L           (0x00000002)
+#define LPC315X_CODEC_AOUT_PD_SET_DWA          (0x00000004)
+#define LPC315X_CODEC_AOUT_PD_SET_I2S_FMT      (0x00000008)
+#define LPC315X_CODEC_AOUT_PD_SDAC_R           (0x00000010)
+#define LPC315X_CODEC_AOUT_PD_SDAC_L           (0x00000020)
+
+#define LPC315X_CODEC_AOUT_CTRL_SET_LMT                (0x000000C0)
+
+#define LPC315X_CODEC_AOUT_PD_HP_R             (0x00001000)
+#define LPC315X_CODEC_AOUT_PD_HP_C             (0x00002000)
+#define LPC315X_CODEC_AOUT_PD_HP_L             (0x00004000)
+#define LPC315X_CODEC_AOUT_VREF_SLOW           (0x00008000)
+#define LPC315X_CODEC_AOUT_VREF_SLOW_UP                (0x40000000)
+#define LPC315X_CODEC_AOUT_SWDAC_ON            (0x80000000)
+
+#define LPC315X_CODEC_AOUT_AVC_COA_GAIN_POS    (16)
+#define LPC315X_CODEC_AOUT_AVC_COA_GAIN_MSK    (0x07FF0000)
+#define LPC315X_CODEC_AOUT_AVC_FIN_GAIN_POS    (27)
+#define LPC315X_CODEC_AOUT_AVC_FIN_GAIN_MSK    (0x38000000)
+
+/* I2S MUX - Format values */
+#define LPC315X_CODEC_I2SMUX1_TX_FMT_MSK       (0x00000007)
+#define LPC315X_CODEC_I2SMUX1_TX_FMT_I2S       (0x00000003)
+#define LPC315X_CODEC_I2SMUX1_TX_FMT_LSB_16    (0x00000004)
+#define LPC315X_CODEC_I2SMUX1_TX_FMT_LSB_18    (0x00000005)
+#define LPC315X_CODEC_I2SMUX1_TX_FMT_LSB_20    (0x00000006)
+#define LPC315X_CODEC_I2SMUX1_TX_FMT_LSB_24    (0x00000007)
+
+#define LPC315X_CODEC_I2SMUX1_RX_FMT_MSK       (0x00000700)
+#define LPC315X_CODEC_I2SMUX1_RX_FMT_DAD       (0x00000000)
+#define LPC315X_CODEC_I2SMUX1_RX_FMT_SPD       (0x00000100)
+#define LPC315X_CODEC_I2SMUX1_RX_FMT_I2S       (0x00000300)
+#define LPC315X_CODEC_I2SMUX1_RX_FMT_LSB_16    (0x00000400)
+#define LPC315X_CODEC_I2SMUX1_RX_FMT_LSB_18    (0x00000500)
+#define LPC315X_CODEC_I2SMUX1_RX_FMT_LSB_20    (0x00000600)
+#define LPC315X_CODEC_I2SMUX1_RX_FMT_LSB_24    (0x00000700)
+
+#define LPC315X_CODEC_I2SMUX1_DEMUX_MSK                (0x00010000)
+
+/* INT0 Register bit definitions */
+#define        LPC315X_CODEC_INT0_PD_DAC               (0x00000080)
+#define        LPC315X_CODEC_INT0_SET_SIL              (0x00000008)
+
+/* Analog CGU register bit definitions */
+#define        LPC315X_CODEC_CGU_CLKDAC_INV            (0x00800000)
+#define        LPC315X_CODEC_CGU_ADCSYS_256FS          (0x00100000)
+#define        LPC315X_CODEC_CGU_ADC1_OFF              (0x00080000)
+#define        LPC315X_CODEC_CGU_ADC2_128FS            (0x00040000)
+#define        LPC315X_CODEC_CGU_CLKDAC_256FS          (0x00020000)
+#define        LPC315X_CODEC_CGU_CLKINT_256FS          (0x00010000)
+
+/* Playback clocks */
+#define        LPC315X_CODEC_CGU_PD_I2C256FS_CLK       (0x00000001)
+#define        LPC315X_CODEC_CGU_PD_DSP_CLK            (0x00000002)
+#define        LPC315X_CODEC_CGU_PD_NS_CLK             (0x00000004)
+#define        LPC315X_CODEC_CGU_PD_I2SRX_BCLK         (0x00000040)
+#define        LPC315X_CODEC_CGU_PD_I2SRX_SYSCLK       (0x00000080)
+#define        LPC315X_CODEC_CGU_PD_DAC_CLK            (0x00000800)
+
+/* Recording clocks */
+#define        LPC315X_CODEC_CGU_PD_DEC_CLK            (0x00000008)
+#define        LPC315X_CODEC_CGU_PD_I2STX_BCLK         (0x00000010)
+#define        LPC315X_CODEC_CGU_PD_I2STX_SYSCLK       (0x00000020)
+#define        LPC315X_CODEC_CGU_PD_ADC2_CLK           (0x00001000)
+#define        LPC315X_CODEC_CGU_PD_ADC1_CLK           (0x00002000)
+#define        LPC315X_CODEC_CGU_PD_ADCSYS_CLK         (0x00004000)
+
+/* INT1 register definitions */
+#define        LPC315X_CODEC_INT1_MAS_MUTE             (0x00010000)
+#define        LPC315X_CODEC_INT1_MAS_VOL_L_POS        (0)
+#define        LPC315X_CODEC_INT1_MAS_VOL_L_MSK        (0x000000FF)
+#define        LPC315X_CODEC_INT1_MAS_VOL_R_POS        (8)
+#define        LPC315X_CODEC_INT1_MAS_VOL_R_MSK        (0x0000FF00)
+
+/* Decimator register bits */
+#define        LPC315X_CODEC_DEC_GAIN_R_MASK           (0x000000FF)
+#define        LPC315X_CODEC_DEC_GAIN_L_MASK           (0x0000FF00)
+#define        LPC315X_CODEC_DEC_GAIN_R_POS            (0)
+#define        LPC315X_CODEC_DEC_GAIN_L_POS            (8)
+#define        LPC315X_CODEC_DEC_DC_IN_FIL             (0x00080000)
+#define        LPC315X_CODEC_DEC_DC_OUT_FIL            (0x00040000)
+#define        LPC315X_CODEC_DEC_MUTE                  (0x00200000)
+#define        LPC315X_CODEC_DEC_AGC_EN                (0x00400000)
+
+extern struct snd_soc_codec_device soc_codec_lpc315x_dev;
+extern struct snd_soc_dai lpc315x_codec_dais[];
+
+#endif
index 6595627..91298dd 100644 (file)
@@ -1,11 +1,19 @@
 config SND_LPC313X_SOC
         tristate "SoC Audio for the NXP LPC313X System-on-a-Chip"
-        depends on ARCH_LPC313X && SND_SOC
+                               depends on ARCH_LPC313X && SND_SOC && MACH_EA313X
         help
           Say Y or M if you want to add audio support for the LPC313X.
           You will also need to to select the audio interfaces to
          support below.
 
+config SND_LPC315X_SOC
+       tristate "SoC Audio for the NXP LPC315X System-on-a-Chip"
+       depends on ARCH_LPC313X && SND_SOC && MACH_EA3152
+       help
+       Say Y or M if you want to add audio support for the LPC313X.
+       You will also need to to select the audio interfaces to
+       support below.
+
 config SND_LPC313X_SOC_I2S
         bool
 
@@ -21,6 +29,18 @@ config SND_LPC313X_SOC_I2S_UDA1380
          and I2C channel connected to the I2C interface of the
          UDA1380 codec.
 
+config SND_LPC315X_SOC_I2S_LPC315X_CODEC
+       bool "SoC Audio for the Analog Die CODEC using I2S/I2C in 315X SoC"
+       depends on SND_LPC315X_SOC
+       select I2C_PNX
+       select SND_LPC313X_SOC_I2S
+       select SND_SOC_LPC315X_CODEC
+       help
+               Say Y here if you want to add support for SoC audio on the
+               LPC315x with the Analog Die CODEC. This requires an I2S channel
+               and I2C channel connected to the I2C interface of the
+               Analog Die CODEC.
+
 config SND_USE_EA3131
         bool "Enable audio configuration for the EA3131 board"
         depends on SND_LPC313X_SOC
@@ -34,6 +54,19 @@ config SND_USE_EA3131
          board used the I2S1 channel with the UDA1380 audio CODEC and
          I2C on channel 1.
 
+config SND_USE_EA3152
+       bool "Enable audio configuration for the EA3152 board"
+       depends on SND_LPC315X_SOC
+       select SND_LPC315X_SOC_I2S_LPC315X_CODEC
+       select SND_I2C1_CHANNEL_LPC315X_CODEC
+       select SND_I2S_RX1_MASTER
+       select SND_I2S_TX1_MASTER
+       select SND_CODEC_FS128
+       help
+               Say Y to configuration audio for the EA3152 board. The EA3152
+               board uses the I2S1 channel with the Analog Die audio CODEC and
+               I2C on channel 1.
+
 config SND_I2C1_CHANNEL_UDA1380
        bool "I2C1 channel connected to the UDA1380"
        depends on SND_LPC313X_SOC_I2S_UDA1380
@@ -41,6 +74,13 @@ config SND_I2C1_CHANNEL_UDA1380
          Say Y here to use the I2C1 channel to communicate with the
          UDA1380 CODEC. Not selecting this option will use I2C0.
 
+config SND_I2C1_CHANNEL_LPC315X_CODEC
+       bool "I2C1 channel connected to the Analog Die Audio CODEC"
+       depends on SND_LPC315X_SOC_I2S_LPC315X_CODEC
+       help
+         Say Y here to use the I2C1 channel to communicate with the
+         Analog Die Audio CODEC. Not selecting this option will use I2C0.
+
 choice
        prompt "I2S RX channel configuration"
        depends on SND_LPC313X_SOC_I2S
@@ -95,7 +135,7 @@ endchoice
 
 choice
        prompt "WS clock divider"
-       depends on SND_LPC313X_SOC_I2S
+       depends on SND_LPC313X_SOC_I2S && SND_LPC313X_SOC
        help
          This divider is used to generate the WS rate from the CODEC
          clock. The CODEC clock may or may not be generated by the LPC313x.
@@ -121,9 +161,25 @@ choice
 
 endchoice
 
+choice
+       prompt "WS clock divider"
+       depends on SND_LPC313X_SOC_I2S && SND_LPC315X_SOC
+       help
+         This divider is used to generate the WS rate from the CODEC
+         clock. The CODEC clock is generated by LPC315x digital Die.
+         It is generated on the CLK_256FS pin. This divider is
+         usually a factor of 128 times the sample frequency.
+         Selected the correct multiplier here to have the audio
+         driver generate the correct clock rates for the audio CODEC.
+
+       config SND_CODEC_FS128
+       bool "CODEC clock is 128FS"
+
+endchoice
+
 config SND_CODEC_NO_FS256_NEEDED
         bool "Disable the CLK_FS256 signals"
-        depends on SND_LPC313X_SOC
+                               depends on SND_LPC313X_SOC || SND_LPC315X_SOC
         help
           For audio CODECs that do not need the FS256 clock, enable this
          option. These CODECs usually generate internal clocking based
@@ -132,7 +188,7 @@ config SND_CODEC_NO_FS256_NEEDED
 config SND_USE_DMA_LINKLIST
         bool "Use a DMA linked list instead of a circular buffer"
        default y
-        depends on SND_LPC313X_SOC
+                               depends on SND_LPC313X_SOC || SND_LPC315X_SOC
         help
           The audio driver supports 2 DMA mode: circular buffer mode and
          DMA linked list mode. This option lets you choose which mode to
index 6df826d..8c42e30 100644 (file)
@@ -3,6 +3,7 @@ snd-soc-lpc313x-objs := lpc313x-pcm.o
 snd-soc-lpc313x-i2s-objs := lpc313x-i2s.o
 
 obj-$(CONFIG_SND_LPC313X_SOC) += snd-soc-lpc313x.o
+obj-$(CONFIG_SND_LPC315X_SOC) += snd-soc-lpc313x.o
 obj-$(CONFIG_SND_LPC313X_SOC_I2S) += snd-soc-lpc313x-i2s.o lpc313x-i2s-clocking.o
 
 # LPC3XXX Machine Support
@@ -10,3 +11,8 @@ snd-soc-lpc313x-uda1380-objs := lpc313x-uda1380.o
 
 obj-$(CONFIG_SND_LPC313X_SOC_I2S_UDA1380) += snd-soc-lpc313x-uda1380.o
 
+# LPC315X Machine Support
+snd-soc-lpc315x-codec-objs := lpc315x-codec.o
+
+obj-$(CONFIG_SND_LPC315X_SOC_I2S_LPC315X_CODEC) += snd-soc-lpc315x-codec.o
+
index cb11a64..7eaa29b 100644 (file)
@@ -106,7 +106,7 @@ static P_CGU_CLOCK_ID_T clkarray[CLK_TX_1 + 1] = {
  *            8K  11.025K 12K 16K 22.05K 24K 32K 44.1K 48K
  *  128FS     48          32  24         16  12        8
  *  256FS     24          16  12         8   6         4
- *  384FS     16              8              4         
+ *  384FS     16              8              4
  *  512FS     12          8              4   3         2
  *  768FS     8               4              2
  *  1024FS    6           4   3          2             1
@@ -122,9 +122,9 @@ static CGU_HPLL_SETUP_T pllsetup_48K_1024 = {
  *  128FS     36          24  18         12            6
  *  256FS     18          12  9          6             3
  *  384FS     12          8   6          4   3         2
- *  512FS     9           6                            
+ *  512FS     9           6
  *  768FS     6           4              2             1
- *  1024FS                                             
+ *  1024FS
 */
 static CGU_HPLL_SETUP_T pllsetup_48K_768 = {
        CGU_FIN_SELECT_FFAST, 514, 21299, 21, 0, 44, 22, 0, (48000 * 768)
@@ -136,9 +136,9 @@ static CGU_HPLL_SETUP_T pllsetup_48K_768 = {
  *            8K  11.025K 12K 16K 22.05K 24K 32K 44.1K 48K
  *  128FS         32              16              8
  *  256FS         16              8               4
- *  384FS                                         
+ *  384FS
  *  512FS         8               4               2
- *  768FS                                         
+ *  768FS
  *  1024FS        4               2               1
 */
 static CGU_HPLL_SETUP_T pllsetup_44_1K_1024 = {
@@ -152,9 +152,9 @@ static CGU_HPLL_SETUP_T pllsetup_44_1K_1024 = {
  *  128FS         24              12              6
  *  256FS         12              6               3
  *  384FS         8               4               2
- *  512FS         6               3                
+ *  512FS         6               3
  *  768FS         4               2               1
- *  1024FS        3                               
+ *  1024FS        3
 */
 static CGU_HPLL_SETUP_T pllsetup_44_1K_768 = {
        CGU_FIN_SELECT_FFAST, 514, 19660, 11, 0, 48, 23, 0, (44100 * 768)
@@ -164,12 +164,12 @@ static CGU_HPLL_SETUP_T pllsetup_44_1K_768 = {
    32768000Hz FS clock, error .01638Hz
    used for the following rates with the following dividers:
  *            8K  11.025K 12K 16K 22.05K 24K 32K 44.1K 48K
- *  128FS     32              16             8    
- *  256FS     16              8              4    
- *  384FS                                         
- *  512FS     8               4              2    
- *  768FS                                         
- *  1024FS                                   1    
+ *  128FS     32              16             8
+ *  256FS     16              8              4
+ *  384FS
+ *  512FS     8               4              2
+ *  768FS
+ *  1024FS                                   1
 */
 static CGU_HPLL_SETUP_T pllsetup_32K_1024 = {
        CGU_FIN_SELECT_FFAST, 770, 32765, 21, 0, 20, 10, 0, (32000 * 1024)
@@ -184,6 +184,15 @@ static CGU_HPLL_SETUP_T *ppll_list[] = {
        NULL
 };
 
+#if defined(CONFIG_SND_LPC315X_SOC)
+/* For LPC315X Analog CODEC 128FS clock has to be used */
+static const u32 fsdiv =
+#if defined (CONFIG_SND_CODEC_FS128)
+       128;
+#endif
+#endif
+
+#if defined(CONFIG_SND_LPC313X_SOC)
 static const u32 fsdiv =
 #if defined (CONFIG_SND_CODEC_FS256)
        256;
@@ -196,6 +205,7 @@ static const u32 fsdiv =
 #elif defined (CONFIG_SND_CODEC_FS1024)
        1024;
 #endif
+#endif
 
 static u32 lpc313x_set_best_rate(u32 freq)
 {
@@ -340,7 +350,7 @@ u32 lpc313x_main_clk_rate(u32 freq)
        /* Compute and set proper divider */
        ret = lpc313x_set_codec_freq(freq);
 #if defined (CONFIG_SND_DEBUG_VERBOSE)
-       pr_info("LPC313x ASOC main clock : %d (%d)\n", 
+       pr_info("LPC313x ASOC main clock : %d (%d)\n",
                i2s_clk_state.target_codec_rate,
                i2s_clk_state.real_fs_codec_rate);
 #endif
diff --git a/sound/soc/lpc313x/lpc315x-codec.c b/sound/soc/lpc313x/lpc315x-codec.c
new file mode 100644 (file)
index 0000000..63f7053
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * sound/soc/lpc313x/lpc315x-codec.c
+ *
+ * Author: Kevin Wells <kevin.wells@nxp.com>
+ *
+ * Copyright (C) 2010 NXP Semiconductors
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/lpc315x_codec.h"
+#include "lpc313x-pcm.h"
+#include "lpc313x-i2s.h"
+#include "lpc313x-i2s-clocking.h"
+
+#include <linux/io.h>
+#include <mach/cgu.h>
+#include <mach/board.h>
+#include <mach/registers.h>
+
+#define SND_MODNAME "lpc315x_machine"
+
+static int ea315x_lpc315x_codec_hw_params(struct snd_pcm_substream
+               *substream, struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
+       struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
+       const unsigned int fmt = (SND_SOC_DAIFMT_I2S |
+                                 SND_SOC_DAIFMT_CBS_CFS);
+       int ret;
+
+       /* Set the CPU I2S rate clock (first) */
+       ret = snd_soc_dai_set_sysclk(cpu_dai, 0, params_rate(params),
+                                           SND_SOC_CLOCK_OUT);
+       if (ret < 0) {
+               pr_warning("%s: "
+                          "Failed to set I2S clock (%d)\n",
+                          SND_MODNAME, ret);
+               return ret;
+       }
+
+       /* Set CPU and CODEC DAI format */
+       ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+       if (ret < 0) {
+               pr_warning("%s: "
+                          "Failed to set CPU DAI format (%d)\n",
+                          SND_MODNAME, ret);
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_fmt(codec_dai, fmt);
+       if (ret < 0) {
+               pr_warning("%s: "
+                          "Failed to set CODEC DAI format (%d)\n",
+                          SND_MODNAME, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops ea315x_lpc315x_codec_ops = {
+       .hw_params = ea315x_lpc315x_codec_hw_params,
+};
+
+static const struct snd_soc_dapm_widget ea315x_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+       SND_SOC_DAPM_LINE("Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       /* Headphone connected to HP_OUTR, HP_OUTL */
+       {"Headphone Jack", NULL, "HP_OUTR"},
+       {"Headphone Jack", NULL, "HP_OUTL"},
+
+       /* Mic connected to ADC_VINM */
+       {"ADC_MIC", NULL, "Mic Jack"},
+
+       /* Line In connected to ADC_VINR, ADC_VINL */
+       {"ADC_VINL", NULL, "Line In"},
+       {"ADC_VINR", NULL, "Line In"},
+
+       /* Line In connected to ADC_TINR, ADC_TINL */
+       {"ADC_TINL", NULL, "Line In"},
+       {"ADC_TINR", NULL, "Line In"},
+};
+
+static int ea315x_lpc315x_codec_init(struct snd_soc_codec *codec)
+{
+       /* Add widgets */
+       snd_soc_dapm_new_controls(codec, ea315x_dapm_widgets,
+                                 ARRAY_SIZE(ea315x_dapm_widgets));
+
+       /* Set up audio path audio_map */
+       snd_soc_dapm_add_routes(codec, intercon, ARRAY_SIZE(intercon));
+
+       /* Always connected pins */
+       snd_soc_dapm_enable_pin(codec, "Headphone Jack");
+       snd_soc_dapm_enable_pin(codec, "Mic Jack");
+       snd_soc_dapm_enable_pin(codec, "Line In");
+
+       snd_soc_dapm_sync(codec);
+
+       return 0;
+}
+
+static struct snd_soc_dai_link ea315x_lpc315x_codec_dai[] = {
+       {
+               .name = "LPC315X_CODEC",
+               .stream_name = "LPC315X_CODEC",
+               .cpu_dai = &lpc313x_i2s_dai,
+               .codec_dai = &lpc315x_codec_dais[0],
+               .init = ea315x_lpc315x_codec_init,
+               .ops = &ea315x_lpc315x_codec_ops,
+       },
+};
+
+static struct snd_soc_card snd_soc_machine_ea315x = {
+       .name = "LPC315X_CODEC",
+       .platform = &lpc313x_soc_platform,
+       .dai_link = &ea315x_lpc315x_codec_dai[0],
+       .num_links = ARRAY_SIZE(ea315x_lpc315x_codec_dai),
+};
+
+static struct snd_soc_device ea315x_lpc315x_codec_snd_dev = {
+       .card = &snd_soc_machine_ea315x,
+       .codec_dev = &soc_codec_lpc315x_dev,
+};
+
+static struct platform_device *ea315x_snd_device;
+
+/*
+ * EA315X Module init function
+ * */
+static int __init ea315x_asoc_init(void)
+{
+       int ret = 0;
+
+       /* Enable CODEC clock first or I2C will fail to the CODEC */
+       lpc313x_main_clk_rate(48000);
+
+       /* Analog Die is added as I2C device in EA3131 Board file.
+        * So no need to add the I2C device again
+        */
+       /* Create and register platform device */
+       ea315x_snd_device = platform_device_alloc("soc-audio", -1);
+       if (ea315x_snd_device == NULL) {
+               printk(KERN_ERR "Unable to register Audio device \r\n");
+               return -ENOMEM;
+       }
+
+       /* Store platform device info */
+       platform_set_drvdata(ea315x_snd_device,
+                       &ea315x_lpc315x_codec_snd_dev);
+       ea315x_lpc315x_codec_snd_dev.dev = &ea315x_snd_device->dev;
+
+       /* Add Audio platform device */
+       ret = platform_device_add(ea315x_snd_device);
+       if (ret) {
+               pr_warning("%s: platform_device_add failed (%d)\n",
+                          SND_MODNAME, ret);
+               goto err_device_add;
+       }
+
+       return 0;
+
+err_device_add:
+       if (ea315x_snd_device != NULL) {
+               platform_device_put(ea315x_snd_device);
+               lpc313x_main_clk_rate(0);
+               ea315x_snd_device = NULL;
+       }
+
+       return ret;
+}
+
+static void __exit ea315x_asoc_exit(void)
+{
+       platform_device_unregister(ea315x_snd_device);
+       lpc313x_main_clk_rate(0);
+       ea315x_snd_device = NULL;
+}
+
+module_init(ea315x_asoc_init);
+module_exit(ea315x_asoc_exit);
+
+MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
+MODULE_DESCRIPTION("ASoC machine driver for LPC315X/Analog Die Audio CODEC");
+MODULE_LICENSE("GPL");
+