Merge branch 'topic/asoc' into for-linus
Takashi Iwai [Wed, 16 Mar 2011 16:38:41 +0000 (17:38 +0100)]
138 files changed:
arch/arm/mach-shmobile/board-ag5evm.c
arch/arm/mach-shmobile/board-ap4evb.c
arch/arm/mach-shmobile/board-mackerel.c
arch/arm/mach-tegra/include/mach/harmony_audio.h [new file with mode: 0644]
arch/sh/boards/mach-ecovec24/setup.c
arch/sh/boards/mach-se/7724/setup.c
include/linux/mfd/wm8994/pdata.h
include/linux/mfd/wm8994/registers.h
include/sound/control.h
include/sound/cs4271.h [new file with mode: 0644]
include/sound/sh_fsi.h
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/tlv320aic32x4.h [new file with mode: 0644]
include/sound/wm8903.h
include/sound/wm9081.h
include/trace/events/asoc.h
sound/core/control.c
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ak4104.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/cs4271.c [new file with mode: 0644]
sound/soc/codecs/dfbmcs320.c [new file with mode: 0644]
sound/soc/codecs/lm4857.c [new file with mode: 0644]
sound/soc/codecs/max98088.c
sound/soc/codecs/max9850.c [new file with mode: 0644]
sound/soc/codecs/max9850.h [new file with mode: 0644]
sound/soc/codecs/sgtl5000.c [new file with mode: 0644]
sound/soc/codecs/sgtl5000.h [new file with mode: 0644]
sound/soc/codecs/sn95031.c [new file with mode: 0644]
sound/soc/codecs/sn95031.h [new file with mode: 0644]
sound/soc/codecs/tlv320aic32x4.c [new file with mode: 0644]
sound/soc/codecs/tlv320aic32x4.h [new file with mode: 0644]
sound/soc/codecs/tlv320dac33.c
sound/soc/codecs/twl6040.c
sound/soc/codecs/wm2000.c
sound/soc/codecs/wm8523.c
sound/soc/codecs/wm8741.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8804.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8903.h
sound/soc/codecs/wm8904.c
sound/soc/codecs/wm8955.c
sound/soc/codecs/wm8961.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8978.c
sound/soc/codecs/wm8991.c [new file with mode: 0644]
sound/soc/codecs/wm8991.h [new file with mode: 0644]
sound/soc/codecs/wm8993.c
sound/soc/codecs/wm8994-tables.c
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8994.h
sound/soc/codecs/wm8995.c
sound/soc/codecs/wm9081.c
sound/soc/codecs/wm9090.c
sound/soc/codecs/wm_hubs.c
sound/soc/davinci/davinci-i2s.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/ep93xx/Kconfig
sound/soc/ep93xx/Makefile
sound/soc/ep93xx/edb93xx.c [new file with mode: 0644]
sound/soc/ep93xx/ep93xx-ac97.c
sound/soc/ep93xx/ep93xx-i2s.c
sound/soc/ep93xx/ep93xx-pcm.c
sound/soc/fsl/mpc8610_hpcd.c
sound/soc/fsl/p1022_ds.c
sound/soc/imx/Kconfig
sound/soc/imx/Makefile
sound/soc/imx/eukrea-tlv320.c
sound/soc/imx/imx-ssi.c
sound/soc/imx/mx27vis-aic32x4.c [new file with mode: 0644]
sound/soc/mid-x86/Kconfig [new file with mode: 0644]
sound/soc/mid-x86/Makefile [new file with mode: 0644]
sound/soc/mid-x86/mfld_machine.c [new file with mode: 0644]
sound/soc/mid-x86/sst_platform.c [new file with mode: 0644]
sound/soc/mid-x86/sst_platform.h [new file with mode: 0644]
sound/soc/omap/Kconfig
sound/soc/omap/rx51.c
sound/soc/pxa/raumfeld.c
sound/soc/pxa/tosa.c
sound/soc/pxa/z2.c
sound/soc/pxa/zylonite.c
sound/soc/samsung/Kconfig
sound/soc/samsung/Makefile
sound/soc/samsung/ac97.c
sound/soc/samsung/ac97.h [deleted file]
sound/soc/samsung/dma.c
sound/soc/samsung/dma.h
sound/soc/samsung/goni_wm8994.c
sound/soc/samsung/h1940_uda1380.c
sound/soc/samsung/i2s.c
sound/soc/samsung/jive_wm8750.c
sound/soc/samsung/lm4857.h [deleted file]
sound/soc/samsung/ln2440sbc_alc650.c
sound/soc/samsung/neo1973_gta02_wm8753.c [deleted file]
sound/soc/samsung/neo1973_wm8753.c
sound/soc/samsung/pcm.c
sound/soc/samsung/pcm.h
sound/soc/samsung/rx1950_uda1380.c
sound/soc/samsung/s3c-i2s-v2.c
sound/soc/samsung/s3c2412-i2s.c
sound/soc/samsung/s3c24xx-i2s.c
sound/soc/samsung/s3c24xx_simtec.c
sound/soc/samsung/s3c24xx_simtec_hermes.c
sound/soc/samsung/s3c24xx_simtec_tlv320aic23.c
sound/soc/samsung/s3c24xx_uda134x.c
sound/soc/samsung/smartq_wm8987.c
sound/soc/samsung/smdk2443_wm9710.c
sound/soc/samsung/smdk_spdif.c
sound/soc/samsung/smdk_wm8580.c
sound/soc/samsung/smdk_wm9713.c
sound/soc/samsung/spdif.c
sound/soc/sh/fsi-ak4642.c
sound/soc/sh/fsi-da7210.c
sound/soc/sh/fsi-hdmi.c
sound/soc/sh/fsi.c
sound/soc/soc-cache.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-jack.c
sound/soc/soc-utils.c
sound/soc/tegra/Kconfig [new file with mode: 0644]
sound/soc/tegra/Makefile [new file with mode: 0644]
sound/soc/tegra/harmony.c [new file with mode: 0644]
sound/soc/tegra/tegra_asoc_utils.c [new file with mode: 0644]
sound/soc/tegra/tegra_asoc_utils.h [new file with mode: 0644]
sound/soc/tegra/tegra_das.c [new file with mode: 0644]
sound/soc/tegra/tegra_das.h [new file with mode: 0644]
sound/soc/tegra/tegra_i2s.c [new file with mode: 0644]
sound/soc/tegra/tegra_i2s.h [new file with mode: 0644]
sound/soc/tegra/tegra_pcm.c [new file with mode: 0644]
sound/soc/tegra/tegra_pcm.h [new file with mode: 0644]

index 4303a86..3e6f0aa 100644 (file)
@@ -119,13 +119,6 @@ static struct platform_device keysc_device = {
 };
 
 /* FSI A */
-static struct sh_fsi_platform_info fsi_info = {
-       .porta_flags = SH_FSI_OUT_SLAVE_MODE    |
-                      SH_FSI_IN_SLAVE_MODE     |
-                      SH_FSI_OFMT(I2S)         |
-                      SH_FSI_IFMT(I2S),
-};
-
 static struct resource fsi_resources[] = {
        [0] = {
                .name   = "FSI",
@@ -144,9 +137,6 @@ static struct platform_device fsi_device = {
        .id             = -1,
        .num_resources  = ARRAY_SIZE(fsi_resources),
        .resource       = fsi_resources,
-       .dev    = {
-               .platform_data  = &fsi_info,
-       },
 };
 
 static struct resource sh_mmcif_resources[] = {
index 81d6536..1a8118c 100644 (file)
@@ -673,16 +673,12 @@ static int fsi_set_rate(struct device *dev, int is_porta, int rate, int enable)
 }
 
 static struct sh_fsi_platform_info fsi_info = {
-       .porta_flags = SH_FSI_BRS_INV |
-                      SH_FSI_OUT_SLAVE_MODE |
-                      SH_FSI_IN_SLAVE_MODE |
-                      SH_FSI_OFMT(PCM) |
-                      SH_FSI_IFMT(PCM),
+       .porta_flags = SH_FSI_BRS_INV,
 
        .portb_flags = SH_FSI_BRS_INV |
                       SH_FSI_BRM_INV |
                       SH_FSI_LRS_INV |
-                      SH_FSI_OFMT(SPDIF),
+                      SH_FSI_FMT_SPDIF,
        .set_rate = fsi_set_rate,
 };
 
@@ -783,6 +779,10 @@ static struct platform_device hdmi_device = {
        },
 };
 
+static struct platform_device fsi_hdmi_device = {
+       .name           = "sh_fsi2_b_hdmi",
+};
+
 static long ap4evb_clk_optimize(unsigned long target, unsigned long *best_freq,
                                unsigned long *parent_freq)
 {
@@ -936,6 +936,7 @@ static struct platform_device *ap4evb_devices[] __initdata = {
        &usb1_host_device,
        &fsi_device,
        &fsi_ak4643_device,
+       &fsi_hdmi_device,
        &sh_mmcif_device,
        &lcdc1_device,
        &lcdc_device,
index 1657eac..1a63c21 100644 (file)
@@ -399,6 +399,10 @@ static struct platform_device hdmi_device = {
        },
 };
 
+static struct platform_device fsi_hdmi_device = {
+       .name           = "sh_fsi2_b_hdmi",
+};
+
 static int __init hdmi_init_pm_clock(void)
 {
        struct clk *hdmi_ick = clk_get(&hdmi_device.dev, "ick");
@@ -609,16 +613,12 @@ fsi_set_rate_end:
 }
 
 static struct sh_fsi_platform_info fsi_info = {
-       .porta_flags =  SH_FSI_BRS_INV          |
-                       SH_FSI_OUT_SLAVE_MODE   |
-                       SH_FSI_IN_SLAVE_MODE    |
-                       SH_FSI_OFMT(PCM)        |
-                       SH_FSI_IFMT(PCM),
+       .porta_flags =  SH_FSI_BRS_INV,
 
        .portb_flags =  SH_FSI_BRS_INV  |
                        SH_FSI_BRM_INV  |
                        SH_FSI_LRS_INV  |
-                       SH_FSI_OFMT(SPDIF),
+                       SH_FSI_FMT_SPDIF,
 
        .set_rate = fsi_set_rate,
 };
@@ -921,6 +921,7 @@ static struct platform_device *mackerel_devices[] __initdata = {
        &leds_device,
        &fsi_device,
        &fsi_ak4643_device,
+       &fsi_hdmi_device,
        &sdhi0_device,
 #if !defined(CONFIG_MMC_SH_MMCIF)
        &sdhi1_device,
diff --git a/arch/arm/mach-tegra/include/mach/harmony_audio.h b/arch/arm/mach-tegra/include/mach/harmony_audio.h
new file mode 100644 (file)
index 0000000..af08650
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * arch/arm/mach-tegra/include/mach/harmony_audio.h
+ *
+ * Copyright 2011 NVIDIA, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+struct harmony_audio_platform_data {
+       int gpio_spkr_en;
+       int gpio_hp_det;
+       int gpio_int_mic_en;
+       int gpio_ext_mic_en;
+};
index 701667a..7bdb572 100644 (file)
@@ -723,11 +723,7 @@ static struct platform_device camera_devices[] = {
 
 /* FSI */
 static struct sh_fsi_platform_info fsi_info = {
-       .portb_flags = SH_FSI_BRS_INV |
-                      SH_FSI_OUT_SLAVE_MODE |
-                      SH_FSI_IN_SLAVE_MODE |
-                      SH_FSI_OFMT(I2S) |
-                      SH_FSI_IFMT(I2S),
+       .portb_flags = SH_FSI_BRS_INV,
 };
 
 static struct resource fsi_resources[] = {
index 5276793..c8bcf6a 100644 (file)
@@ -286,11 +286,7 @@ static struct platform_device ceu1_device = {
 /* FSI */
 /* change J20, J21, J22 pin to 1-2 connection to use slave mode */
 static struct sh_fsi_platform_info fsi_info = {
-       .porta_flags = SH_FSI_BRS_INV |
-                      SH_FSI_OUT_SLAVE_MODE |
-                      SH_FSI_IN_SLAVE_MODE |
-                      SH_FSI_OFMT(PCM) |
-                      SH_FSI_IFMT(PCM),
+       .porta_flags = SH_FSI_BRS_INV,
 };
 
 static struct resource fsi_resources[] = {
index 9eab263..466b1c7 100644 (file)
@@ -103,13 +103,21 @@ struct wm8994_pdata {
         unsigned int lineout1fb:1;
         unsigned int lineout2fb:1;
 
-        /* Microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
+       /* IRQ for microphone detection if brought out directly as a
+        * signal.
+        */
+       int micdet_irq;
+
+        /* WM8994 microphone biases: 0=0.9*AVDD1 1=0.65*AVVD1 */
         unsigned int micbias1_lvl:1;
         unsigned int micbias2_lvl:1;
 
-        /* Jack detect threashold levels, see datasheet for values */
+        /* WM8994 jack detect threashold levels, see datasheet for values */
         unsigned int jd_scthr:2;
         unsigned int jd_thr:2;
+
+       /* WM8958 microphone bias configuration */
+       int micbias[2];
 };
 
 #endif
index be072fa..f3ee842 100644 (file)
@@ -63,6 +63,8 @@
 #define WM8994_MICBIAS                          0x3A
 #define WM8994_LDO_1                            0x3B
 #define WM8994_LDO_2                            0x3C
+#define WM8958_MICBIAS1                                0x3D
+#define WM8958_MICBIAS2                                0x3E
 #define WM8994_CHARGE_PUMP_1                    0x4C
 #define WM8958_CHARGE_PUMP_2                    0x4D
 #define WM8994_CLASS_W_1                        0x51
index 7715e6f..e67db28 100644 (file)
@@ -115,6 +115,8 @@ int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol);
 int snd_ctl_remove(struct snd_card * card, struct snd_kcontrol * kcontrol);
 int snd_ctl_remove_id(struct snd_card * card, struct snd_ctl_elem_id *id);
 int snd_ctl_rename_id(struct snd_card * card, struct snd_ctl_elem_id *src_id, struct snd_ctl_elem_id *dst_id);
+int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
+                       int active);
 struct snd_kcontrol *snd_ctl_find_numid(struct snd_card * card, unsigned int numid);
 struct snd_kcontrol *snd_ctl_find_id(struct snd_card * card, struct snd_ctl_elem_id *id);
 
diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h
new file mode 100644 (file)
index 0000000..50a059e
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Definitions for CS4271 ASoC codec driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CS4271_H
+#define __CS4271_H
+
+struct cs4271_platform_data {
+       int gpio_nreset;        /* GPIO driving Reset pin, if any */
+};
+
+#endif /* __CS4271_H */
index d798941..9a155f9 100644 (file)
 #define FSI_PORT_A     0
 #define FSI_PORT_B     1
 
-/* flags format
-
- * 0xABCDEEFF
- *
- * A:  channel size for TDM (input)
- * B:  channel size for TDM (ooutput)
- * C:  inversion
- * D:  mode
- * E:  input format
- * F:  output format
- */
-
 #include <linux/clk.h>
 #include <sound/soc.h>
 
-/* TDM channel */
-#define SH_FSI_SET_CH_I(x)     ((x & 0xF) << 28)
-#define SH_FSI_SET_CH_O(x)     ((x & 0xF) << 24)
-
-#define SH_FSI_CH_IMASK                0xF0000000
-#define SH_FSI_CH_OMASK                0x0F000000
-#define SH_FSI_GET_CH_I(x)     ((x & SH_FSI_CH_IMASK) >> 28)
-#define SH_FSI_GET_CH_O(x)     ((x & SH_FSI_CH_OMASK) >> 24)
-
-/* clock inversion */
-#define SH_FSI_INVERSION_MASK  0x00F00000
-#define SH_FSI_LRM_INV         (1 << 20)
-#define SH_FSI_BRM_INV         (1 << 21)
-#define SH_FSI_LRS_INV         (1 << 22)
-#define SH_FSI_BRS_INV         (1 << 23)
-
-/* mode */
-#define SH_FSI_MODE_MASK       0x000F0000
-#define SH_FSI_IN_SLAVE_MODE   (1 << 16)  /* default master mode */
-#define SH_FSI_OUT_SLAVE_MODE  (1 << 17)  /* default master mode */
-
-/* DI format */
-#define SH_FSI_FMT_MASK                0x000000FF
-#define SH_FSI_IFMT(x)         (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 8)
-#define SH_FSI_OFMT(x)         (((SH_FSI_FMT_ ## x) & SH_FSI_FMT_MASK) << 0)
-#define SH_FSI_GET_IFMT(x)     ((x >> 8) & SH_FSI_FMT_MASK)
-#define SH_FSI_GET_OFMT(x)     ((x >> 0) & SH_FSI_FMT_MASK)
-
-#define SH_FSI_FMT_MONO                0
-#define SH_FSI_FMT_MONO_DELAY  1
-#define SH_FSI_FMT_PCM         2
-#define SH_FSI_FMT_I2S         3
-#define SH_FSI_FMT_TDM         4
-#define SH_FSI_FMT_TDM_DELAY   5
-#define SH_FSI_FMT_SPDIF       6
-
-
-#define SH_FSI_IFMT_TDM_CH(x) \
-       (SH_FSI_IFMT(TDM)       | SH_FSI_SET_CH_I(x))
-#define SH_FSI_IFMT_TDM_DELAY_CH(x) \
-       (SH_FSI_IFMT(TDM_DELAY) | SH_FSI_SET_CH_I(x))
+/*
+ * flags format
+ *
+ * 0x000000BA
+ *
+ * A:  inversion
+ * B:  format mode
+ */
 
-#define SH_FSI_OFMT_TDM_CH(x) \
-       (SH_FSI_OFMT(TDM)       | SH_FSI_SET_CH_O(x))
-#define SH_FSI_OFMT_TDM_DELAY_CH(x) \
-       (SH_FSI_OFMT(TDM_DELAY) | SH_FSI_SET_CH_O(x))
+/* A: clock inversion */
+#define SH_FSI_INVERSION_MASK  0x0000000F
+#define SH_FSI_LRM_INV         (1 << 0)
+#define SH_FSI_BRM_INV         (1 << 1)
+#define SH_FSI_LRS_INV         (1 << 2)
+#define SH_FSI_BRS_INV         (1 << 3)
+
+/* B: format mode */
+#define SH_FSI_FMT_MASK                0x000000F0
+#define SH_FSI_FMT_DAI         (0 << 4)
+#define SH_FSI_FMT_SPDIF       (1 << 4)
 
 
 /*
index 8031769..979ed84 100644 (file)
        .invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
        .event = wevent, .event_flags = wflags}
 
+/* additional sequencing control within an event type */
+#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
+       wevent, wflags) \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+       .invert = winvert, .event = wevent, .event_flags = wflags, \
+       .subseq = wsubseq}
+#define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \
+       wflags) \
+{      .id = snd_soc_dapm_supply, .name = wname, .reg = wreg,  \
+       .shift = wshift, .invert = winvert, .event = wevent, \
+       .event_flags = wflags, .subseq = wsubseq}
+
 /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
 #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
        wevent, wflags) \
@@ -450,6 +462,7 @@ struct snd_soc_dapm_widget {
        unsigned char ext:1;                    /* has external widgets */
        unsigned char force:1;                  /* force state */
        unsigned char ignore_suspend:1;         /* kept enabled over suspend */
+       int subseq;                             /* sort within widget type */
 
        int (*power_check)(struct snd_soc_dapm_widget *w);
 
@@ -487,6 +500,9 @@ struct snd_soc_dapm_context {
 
        struct snd_soc_dapm_update *update;
 
+       void (*seq_notifier)(struct snd_soc_dapm_context *,
+                            enum snd_soc_dapm_type, int);
+
        struct device *dev; /* from parent - for debug */
        struct snd_soc_codec *codec; /* parent codec */
        struct snd_soc_card *card; /* parent card */
index 74921f2..bfa4836 100644 (file)
@@ -234,6 +234,7 @@ struct snd_soc_codec;
 struct snd_soc_codec_driver;
 struct soc_enum;
 struct snd_soc_jack;
+struct snd_soc_jack_zone;
 struct snd_soc_jack_pin;
 struct snd_soc_cache_ops;
 #include <sound/soc-dapm.h>
@@ -258,6 +259,16 @@ enum snd_soc_compress_type {
        SND_SOC_RBTREE_COMPRESSION
 };
 
+int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+                            unsigned int freq, int dir);
+int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+                         unsigned int freq_in, unsigned int freq_out);
+
+int snd_soc_register_card(struct snd_soc_card *card);
+int snd_soc_unregister_card(struct snd_soc_card *card);
+int snd_soc_suspend(struct device *dev);
+int snd_soc_resume(struct device *dev);
+int snd_soc_poweroff(struct device *dev);
 int snd_soc_register_platform(struct device *dev,
                struct snd_soc_platform_driver *platform_drv);
 void snd_soc_unregister_platform(struct device *dev);
@@ -265,7 +276,8 @@ int snd_soc_register_codec(struct device *dev,
                const struct snd_soc_codec_driver *codec_drv,
                struct snd_soc_dai_driver *dai_drv, int num_dai);
 void snd_soc_unregister_codec(struct device *dev);
-int snd_soc_codec_volatile_register(struct snd_soc_codec *codec, int reg);
+int snd_soc_codec_volatile_register(struct snd_soc_codec *codec,
+                                   unsigned int reg);
 int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
                               int addr_bits, int data_bits,
                               enum snd_soc_control_type control);
@@ -276,6 +288,10 @@ int snd_soc_cache_write(struct snd_soc_codec *codec,
                        unsigned int reg, unsigned int value);
 int snd_soc_cache_read(struct snd_soc_codec *codec,
                       unsigned int reg, unsigned int *value);
+int snd_soc_default_volatile_register(struct snd_soc_codec *codec,
+                                     unsigned int reg);
+int snd_soc_default_readable_register(struct snd_soc_codec *codec,
+                                     unsigned int reg);
 
 /* Utility functions to get clock rates from various things */
 int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots);
@@ -297,6 +313,9 @@ void snd_soc_jack_notifier_register(struct snd_soc_jack *jack,
                                    struct notifier_block *nb);
 void snd_soc_jack_notifier_unregister(struct snd_soc_jack *jack,
                                      struct notifier_block *nb);
+int snd_soc_jack_add_zones(struct snd_soc_jack *jack, int count,
+                         struct snd_soc_jack_zone *zones);
+int snd_soc_jack_get_type(struct snd_soc_jack *jack, int micbias_voltage);
 #ifdef CONFIG_GPIOLIB
 int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
                        struct snd_soc_jack_gpio *gpios);
@@ -321,7 +340,8 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
  *Controls
  */
 struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
-       void *data, char *long_name);
+                                 void *data, char *long_name,
+                                 const char *prefix);
 int snd_soc_add_controls(struct snd_soc_codec *codec,
        const struct snd_kcontrol_new *controls, int num_controls);
 int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
@@ -367,6 +387,22 @@ int snd_soc_put_volsw_2r_sx(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 
 /**
+ * struct snd_soc_reg_access - Describes whether a given register is
+ * readable, writable or volatile.
+ *
+ * @reg: the register number
+ * @read: whether this register is readable
+ * @write: whether this register is writable
+ * @vol: whether this register is volatile
+ */
+struct snd_soc_reg_access {
+       u16 reg;
+       u16 read;
+       u16 write;
+       u16 vol;
+};
+
+/**
  * struct snd_soc_jack_pin - Describes a pin to update based on jack detection
  *
  * @pin:    name of the pin to update
@@ -381,6 +417,24 @@ struct snd_soc_jack_pin {
 };
 
 /**
+ * struct snd_soc_jack_zone - Describes voltage zones of jack detection
+ *
+ * @min_mv: start voltage in mv
+ * @max_mv: end voltage in mv
+ * @jack_type: type of jack that is expected for this voltage
+ * @debounce_time: debounce_time for jack, codec driver should wait for this
+ *             duration before reading the adc for voltages
+ * @:list: list container
+ */
+struct snd_soc_jack_zone {
+       unsigned int min_mv;
+       unsigned int max_mv;
+       unsigned int jack_type;
+       unsigned int debounce_time;
+       struct list_head list;
+};
+
+/**
  * struct snd_soc_jack_gpio - Describes a gpio pin for jack detection
  *
  * @gpio:         gpio number
@@ -388,6 +442,10 @@ struct snd_soc_jack_pin {
  * @report:       value to report when jack detected
  * @invert:       report presence in low state
  * @debouce_time: debouce time in ms
+ * @wake:        enable as wake source
+ * @jack_status_check: callback function which overrides the detection
+ *                    to provide more complex checks (eg, reading an
+ *                    ADC).
  */
 #ifdef CONFIG_GPIOLIB
 struct snd_soc_jack_gpio {
@@ -396,6 +454,8 @@ struct snd_soc_jack_gpio {
        int report;
        int invert;
        int debounce_time;
+       bool wake;
+
        struct snd_soc_jack *jack;
        struct delayed_work work;
 
@@ -409,6 +469,7 @@ struct snd_soc_jack {
        struct list_head pins;
        int status;
        struct blocking_notifier_head notifier;
+       struct list_head jack_zones;
 };
 
 /* SoC PCM stream information */
@@ -459,18 +520,22 @@ struct snd_soc_codec {
        struct list_head card_list;
        int num_dai;
        enum snd_soc_compress_type compress_type;
+       size_t reg_size;        /* reg_cache_size * reg_word_size */
+       int (*volatile_register)(struct snd_soc_codec *, unsigned int);
+       int (*readable_register)(struct snd_soc_codec *, unsigned int);
 
        /* runtime */
        struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */
        unsigned int active;
-       unsigned int cache_only:1;  /* Suppress writes to hardware */
-       unsigned int cache_sync:1; /* Cache needs to be synced to hardware */
+       unsigned int cache_bypass:1; /* Suppress access to the cache */
        unsigned int suspended:1; /* Codec is in suspend PM state */
        unsigned int probed:1; /* Codec has been probed */
        unsigned int ac97_registered:1; /* Codec has been AC97 registered */
        unsigned int ac97_created:1; /* Codec has been created by SoC */
        unsigned int sysfs_registered:1; /* codec has been sysfs registered */
        unsigned int cache_init:1; /* codec cache has been initialized */
+       u32 cache_only;  /* Suppress writes to hardware */
+       u32 cache_sync; /* Cache needs to be synced to hardware */
 
        /* codec IO */
        void *control_data; /* codec control (i2c/3wire) data */
@@ -503,22 +568,39 @@ struct snd_soc_codec_driver {
                        pm_message_t state);
        int (*resume)(struct snd_soc_codec *);
 
+       /* Default DAPM setup, added after probe() is run */
+       const struct snd_soc_dapm_widget *dapm_widgets;
+       int num_dapm_widgets;
+       const struct snd_soc_dapm_route *dapm_routes;
+       int num_dapm_routes;
+
+       /* codec wide operations */
+       int (*set_sysclk)(struct snd_soc_codec *codec,
+                         int clk_id, unsigned int freq, int dir);
+       int (*set_pll)(struct snd_soc_codec *codec, int pll_id, int source,
+               unsigned int freq_in, unsigned int freq_out);
+
        /* codec IO */
        unsigned int (*read)(struct snd_soc_codec *, unsigned int);
        int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
        int (*display_register)(struct snd_soc_codec *, char *,
                                size_t, unsigned int);
-       int (*volatile_register)(unsigned int);
-       int (*readable_register)(unsigned int);
+       int (*volatile_register)(struct snd_soc_codec *, unsigned int);
+       int (*readable_register)(struct snd_soc_codec *, unsigned int);
        short reg_cache_size;
        short reg_cache_step;
        short reg_word_size;
        const void *reg_cache_default;
+       short reg_access_size;
+       const struct snd_soc_reg_access *reg_access_default;
        enum snd_soc_compress_type compress_type;
 
        /* codec bias level */
        int (*set_bias_level)(struct snd_soc_codec *,
                              enum snd_soc_bias_level level);
+
+       void (*seq_notifier)(struct snd_soc_dapm_context *,
+                            enum snd_soc_dapm_type, int);
 };
 
 /* SoC platform interface */
@@ -617,15 +699,16 @@ struct snd_soc_card {
 
        bool instantiated;
 
-       int (*probe)(struct platform_device *pdev);
-       int (*remove)(struct platform_device *pdev);
+       int (*probe)(struct snd_soc_card *card);
+       int (*late_probe)(struct snd_soc_card *card);
+       int (*remove)(struct snd_soc_card *card);
 
        /* the pre and post PM functions are used to do any PM work before and
         * after the codec and DAI's do any PM work. */
-       int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
-       int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
-       int (*resume_pre)(struct platform_device *pdev);
-       int (*resume_post)(struct platform_device *pdev);
+       int (*suspend_pre)(struct snd_soc_card *card);
+       int (*suspend_post)(struct snd_soc_card *card);
+       int (*resume_pre)(struct snd_soc_card *card);
+       int (*resume_post)(struct snd_soc_card *card);
 
        /* callbacks */
        int (*set_bias_level)(struct snd_soc_card *,
@@ -654,6 +737,14 @@ struct snd_soc_card {
        struct snd_soc_pcm_runtime *rtd_aux;
        int num_aux_rtd;
 
+       /*
+        * Card-specific routes and widgets.
+        */
+       struct snd_soc_dapm_widget *dapm_widgets;
+       int num_dapm_widgets;
+       struct snd_soc_dapm_route *dapm_routes;
+       int num_dapm_routes;
+
        struct work_struct deferred_resume_work;
 
        /* lists of probed devices belonging to this card */
@@ -665,11 +756,16 @@ struct snd_soc_card {
        struct list_head paths;
        struct list_head dapm_list;
 
+       /* Generic DAPM context for the card */
+       struct snd_soc_dapm_context dapm;
+
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_card_root;
        struct dentry *debugfs_pop_time;
 #endif
        u32 pop_time;
+
+       void *drvdata;
 };
 
 /* SoC machine DAI configuration, glues a codec and cpu DAI together */
@@ -721,6 +817,17 @@ unsigned int snd_soc_write(struct snd_soc_codec *codec,
 
 /* device driver data */
 
+static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
+               void *data)
+{
+       card->drvdata = data;
+}
+
+static inline void *snd_soc_card_get_drvdata(struct snd_soc_card *card)
+{
+       return card->drvdata;
+}
+
 static inline void snd_soc_codec_set_drvdata(struct snd_soc_codec *codec,
                void *data)
 {
@@ -754,6 +861,22 @@ static inline void *snd_soc_pcm_get_drvdata(struct snd_soc_pcm_runtime *rtd)
        return dev_get_drvdata(&rtd->dev);
 }
 
+static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card)
+{
+       INIT_LIST_HEAD(&card->dai_dev_list);
+       INIT_LIST_HEAD(&card->codec_dev_list);
+       INIT_LIST_HEAD(&card->platform_dev_list);
+       INIT_LIST_HEAD(&card->widgets);
+       INIT_LIST_HEAD(&card->paths);
+       INIT_LIST_HEAD(&card->dapm_list);
+}
+
 #include <sound/soc-dai.h>
 
+#ifdef CONFIG_DEBUG_FS
+extern struct dentry *snd_soc_debugfs_root;
+#endif
+
+extern const struct dev_pm_ops snd_soc_pm_ops;
+
 #endif
diff --git a/include/sound/tlv320aic32x4.h b/include/sound/tlv320aic32x4.h
new file mode 100644 (file)
index 0000000..c009f70
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * tlv320aic32x4.h  --  TLV320AIC32X4 Soc Audio driver platform data
+ *
+ * Copyright 2011 Vista Silicon S.L.
+ *
+ * Author: Javier Martin <javier.martin@vista-silicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _AIC32X4_PDATA_H
+#define _AIC32X4_PDATA_H
+
+#define AIC32X4_PWR_MICBIAS_2075_LDOIN         0x00000001
+#define AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE     0x00000002
+#define AIC32X4_PWR_AIC32X4_LDO_ENABLE         0x00000004
+#define AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36   0x00000008
+#define AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED    0x00000010
+
+#define AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K     0x00000001
+#define AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K     0x00000002
+
+struct aic32x4_pdata {
+       u32 power_cfg;
+       u32 micpga_routing;
+       bool swapdacs;
+};
+
+#endif
index 1eeebd5..cf7ccb7 100644 (file)
 #define WM8903_MICBIAS_ENA_WIDTH                     1  /* MICBIAS_ENA */
 
 /*
+ * WM8903_GPn_FN values
+ *
+ * See datasheets for list of valid values per pin
+ */
+#define WM8903_GPn_FN_GPIO_OUTPUT                    0
+#define WM8903_GPn_FN_BCLK                           1
+#define WM8903_GPn_FN_IRQ_OUTPT                      2
+#define WM8903_GPn_FN_GPIO_INPUT                     3
+#define WM8903_GPn_FN_MICBIAS_CURRENT_DETECT         4
+#define WM8903_GPn_FN_MICBIAS_SHORT_DETECT           5
+#define WM8903_GPn_FN_DMIC_LR_CLK_OUTPUT             6
+#define WM8903_GPn_FN_FLL_LOCK_OUTPUT                8
+#define WM8903_GPn_FN_FLL_CLOCK_OUTPUT               9
+
+/*
  * R116 (0x74) - GPIO Control 1
  */
 #define WM8903_GP1_FN_MASK                      0x1F00  /* GP1_FN - [12:8] */
 #define WM8903_GP5_DB_SHIFT                          0  /* GP5_DB */
 #define WM8903_GP5_DB_WIDTH                          1  /* GP5_DB */
 
+#define WM8903_NUM_GPIO 5
+
 struct wm8903_platform_data {
        bool irq_active_low;   /* Set if IRQ active low, default high */
 
@@ -239,7 +256,8 @@ struct wm8903_platform_data {
 
        int micdet_delay;      /* Delay after microphone detection (ms) */
 
-       u32 gpio_cfg[5];       /* Default register values for GPIO pin mux */
+       int gpio_base;
+       u32 gpio_cfg[WM8903_NUM_GPIO]; /* Default register values for GPIO pin mux */
 };
 
 #endif
index e173ddb..f34b0b1 100644 (file)
@@ -17,9 +17,12 @@ struct wm9081_retune_mobile_setting {
        u16 config[20];
 };
 
-struct wm9081_retune_mobile_config {
-       struct wm9081_retune_mobile_setting *configs;
-       int num_configs;
+struct wm9081_pdata {
+       bool irq_high;   /* IRQ is active high */
+       bool irq_cmos;   /* IRQ is in CMOS mode */
+
+       struct wm9081_retune_mobile_setting *retune_configs;
+       int num_retune_configs;
 };
 
 #endif
index 186e84d..ae973d2 100644 (file)
@@ -229,6 +229,31 @@ TRACE_EVENT(snd_soc_jack_notify,
        TP_printk("jack=%s %x", __get_str(name), (int)__entry->val)
 );
 
+TRACE_EVENT(snd_soc_cache_sync,
+
+       TP_PROTO(struct snd_soc_codec *codec, const char *type,
+                const char *status),
+
+       TP_ARGS(codec, type, status),
+
+       TP_STRUCT__entry(
+               __string(       name,           codec->name     )
+               __string(       status,         status          )
+               __string(       type,           type            )
+               __field(        int,            id              )
+       ),
+
+       TP_fast_assign(
+               __assign_str(name, codec->name);
+               __assign_str(status, status);
+               __assign_str(type, type);
+               __entry->id = codec->id;
+       ),
+
+       TP_printk("codec=%s.%d type=%s status=%s", __get_str(name),
+                 (int)__entry->id, __get_str(type), __get_str(status))
+);
+
 #endif /* _TRACE_ASOC_H */
 
 /* This part must be outside protection */
index 9ce00ed..db51e4e 100644 (file)
@@ -466,6 +466,52 @@ error:
 }
 
 /**
+ * snd_ctl_activate_id - activate/inactivate the control of the given id
+ * @card: the card instance
+ * @id: the control id to activate/inactivate
+ * @active: non-zero to activate
+ *
+ * Finds the control instance with the given id, and activate or
+ * inactivate the control together with notification, if changed.
+ *
+ * Returns 0 if unchanged, 1 if changed, or a negative error code on failure.
+ */
+int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id,
+                       int active)
+{
+       struct snd_kcontrol *kctl;
+       struct snd_kcontrol_volatile *vd;
+       unsigned int index_offset;
+       int ret;
+
+       down_write(&card->controls_rwsem);
+       kctl = snd_ctl_find_id(card, id);
+       if (kctl == NULL) {
+               ret = -ENOENT;
+               goto unlock;
+       }
+       index_offset = snd_ctl_get_ioff(kctl, &kctl->id);
+       vd = &kctl->vd[index_offset];
+       ret = 0;
+       if (active) {
+               if (!(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
+                       goto unlock;
+               vd->access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+       } else {
+               if (vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)
+                       goto unlock;
+               vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+       }
+       ret = 1;
+ unlock:
+       up_write(&card->controls_rwsem);
+       if (ret > 0)
+               snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, id);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_ctl_activate_id);
+
+/**
  * snd_ctl_rename_id - replace the id of a control on the card
  * @card: the card instance
  * @src_id: the old id
index a3efc52..8224db5 100644 (file)
@@ -50,10 +50,12 @@ source "sound/soc/jz4740/Kconfig"
 source "sound/soc/nuc900/Kconfig"
 source "sound/soc/omap/Kconfig"
 source "sound/soc/kirkwood/Kconfig"
+source "sound/soc/mid-x86/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/samsung/Kconfig"
 source "sound/soc/s6000/Kconfig"
 source "sound/soc/sh/Kconfig"
+source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
 
 # Supported codecs
index ce913bf..1ed61c5 100644 (file)
@@ -10,6 +10,7 @@ obj-$(CONFIG_SND_SOC) += ep93xx/
 obj-$(CONFIG_SND_SOC)  += fsl/
 obj-$(CONFIG_SND_SOC)   += imx/
 obj-$(CONFIG_SND_SOC)  += jz4740/
+obj-$(CONFIG_SND_SOC)  += mid-x86/
 obj-$(CONFIG_SND_SOC)  += nuc900/
 obj-$(CONFIG_SND_SOC)  += omap/
 obj-$(CONFIG_SND_SOC)  += kirkwood/
@@ -17,4 +18,5 @@ obj-$(CONFIG_SND_SOC) += pxa/
 obj-$(CONFIG_SND_SOC)  += samsung/
 obj-$(CONFIG_SND_SOC)  += s6000/
 obj-$(CONFIG_SND_SOC)  += sh/
+obj-$(CONFIG_SND_SOC)  += tegra/
 obj-$(CONFIG_SND_SOC)  += txx9/
index c48b23c..d63c175 100644 (file)
@@ -26,17 +26,24 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
        select SND_SOC_CS42L51 if I2C
        select SND_SOC_CS4270 if I2C
+       select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
        select SND_SOC_CX20442
        select SND_SOC_DA7210 if I2C
+       select SND_SOC_DFBMCS320
        select SND_SOC_JZ4740_CODEC if SOC_JZ4740
+       select SND_SOC_LM4857 if I2C
        select SND_SOC_MAX98088 if I2C
+       select SND_SOC_MAX9850 if I2C
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_PCM3008
+       select SND_SOC_SGTL5000 if I2C
+       select SND_SOC_SN95031 if INTEL_SCU_IPC
        select SND_SOC_SPDIF
        select SND_SOC_SSM2602 if I2C
        select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
        select SND_SOC_TLV320AIC23 if I2C
        select SND_SOC_TLV320AIC26 if SPI_MASTER
+       select SND_SOC_TVL320AIC32X4 if I2C
        select SND_SOC_TLV320AIC3X if I2C
        select SND_SOC_TPA6130A2 if I2C
        select SND_SOC_TLV320DAC33 if I2C
@@ -76,6 +83,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8985 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8988 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8990 if I2C
+       select SND_SOC_WM8991 if I2C
        select SND_SOC_WM8993 if I2C
        select SND_SOC_WM8994 if MFD_WM8994
        select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
@@ -155,6 +163,9 @@ config SND_SOC_CS4270_VD33_ERRATA
        bool
        depends on SND_SOC_CS4270
 
+config SND_SOC_CS4271
+       tristate
+
 config SND_SOC_CX20442
        tristate
 
@@ -167,15 +178,28 @@ config SND_SOC_L3
 config SND_SOC_DA7210
         tristate
 
+config SND_SOC_DFBMCS320
+       tristate
+
 config SND_SOC_DMIC
        tristate
 
 config SND_SOC_MAX98088
        tristate
 
+config SND_SOC_MAX9850
+       tristate
+
 config SND_SOC_PCM3008
        tristate
 
+#Freescale sgtl5000 codec
+config SND_SOC_SGTL5000
+       tristate
+
+config SND_SOC_SN95031
+       tristate
+
 config SND_SOC_SPDIF
        tristate
 
@@ -192,6 +216,9 @@ config SND_SOC_TLV320AIC26
        tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE
        depends on SPI
 
+config SND_SOC_TVL320AIC32X4
+       tristate
+
 config SND_SOC_TLV320AIC3X
        tristate
 
@@ -304,6 +331,9 @@ config SND_SOC_WM8988
 config SND_SOC_WM8990
        tristate
 
+config SND_SOC_WM8991
+       tristate
+
 config SND_SOC_WM8993
        tristate
 
@@ -326,6 +356,9 @@ config SND_SOC_WM9713
        tristate
 
 # Amp
+config SND_SOC_LM4857
+       tristate
+
 config SND_SOC_MAX9877
        tristate
 
@@ -337,4 +370,3 @@ config SND_SOC_WM2000
 
 config SND_SOC_WM9090
        tristate
-
index 579af9c..379bc55 100644 (file)
@@ -12,19 +12,25 @@ snd-soc-ak4671-objs := ak4671.o
 snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs4270-objs := cs4270.o
+snd-soc-cs4271-objs := cs4271.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
+snd-soc-dfbmcs320-objs := dfbmcs320.o
 snd-soc-dmic-objs := dmic.o
 snd-soc-l3-objs := l3.o
 snd-soc-max98088-objs := max98088.o
+snd-soc-max9850-objs := max9850.o
 snd-soc-pcm3008-objs := pcm3008.o
+snd-soc-sgtl5000-objs := sgtl5000.o
 snd-soc-alc5623-objs := alc5623.o
+snd-soc-sn95031-objs := sn95031.o
 snd-soc-spdif-objs := spdif_transciever.o
 snd-soc-ssm2602-objs := ssm2602.o
 snd-soc-stac9766-objs := stac9766.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
+snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
 snd-soc-tlv320dac33-objs := tlv320dac33.o
 snd-soc-twl4030-objs := twl4030.o
 snd-soc-twl6040-objs := twl6040.o
@@ -61,6 +67,7 @@ snd-soc-wm8978-objs := wm8978.o
 snd-soc-wm8985-objs := wm8985.o
 snd-soc-wm8988-objs := wm8988.o
 snd-soc-wm8990-objs := wm8990.o
+snd-soc-wm8991-objs := wm8991.o
 snd-soc-wm8993-objs := wm8993.o
 snd-soc-wm8994-objs := wm8994.o wm8994-tables.o
 snd-soc-wm8995-objs := wm8995.o
@@ -72,6 +79,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
 snd-soc-jz4740-codec-objs := jz4740.o
 
 # Amp
+snd-soc-lm4857-objs := lm4857.o
 snd-soc-max9877-objs := max9877.o
 snd-soc-tpa6130a2-objs := tpa6130a2.o
 snd-soc-wm2000-objs := wm2000.o
@@ -88,23 +96,29 @@ 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_ALC5623)    += snd-soc-alc5623.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CS42L51)  += snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
+obj-$(CONFIG_SND_SOC_CS4271)   += snd-soc-cs4271.o
 obj-$(CONFIG_SND_SOC_CX20442)  += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
+obj-$(CONFIG_SND_SOC_DFBMCS320)        += snd-soc-dfbmcs320.o
 obj-$(CONFIG_SND_SOC_DMIC)     += snd-soc-dmic.o
 obj-$(CONFIG_SND_SOC_L3)       += snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)     += snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
+obj-$(CONFIG_SND_SOC_MAX9850)  += snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
-obj-$(CONFIG_SND_SOC_ALC5623)    += snd-soc-alc5623.o
+obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
+obj-$(CONFIG_SND_SOC_SN95031)  +=snd-soc-sn95031.o
 obj-$(CONFIG_SND_SOC_SPDIF)    += snd-soc-spdif.o
 obj-$(CONFIG_SND_SOC_SSM2602)  += snd-soc-ssm2602.o
 obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)      += snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)      += snd-soc-tlv320aic26.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)      += snd-soc-tlv320aic3x.o
+obj-$(CONFIG_SND_SOC_TVL320AIC32X4)     += snd-soc-tlv320aic32x4.o
 obj-$(CONFIG_SND_SOC_TLV320DAC33)      += snd-soc-tlv320dac33.o
 obj-$(CONFIG_SND_SOC_TWL4030)  += snd-soc-twl4030.o
 obj-$(CONFIG_SND_SOC_TWL6040)  += snd-soc-twl6040.o
@@ -141,6 +155,7 @@ obj-$(CONFIG_SND_SOC_WM8978)        += snd-soc-wm8978.o
 obj-$(CONFIG_SND_SOC_WM8985)   += snd-soc-wm8985.o
 obj-$(CONFIG_SND_SOC_WM8988)   += snd-soc-wm8988.o
 obj-$(CONFIG_SND_SOC_WM8990)   += snd-soc-wm8990.o
+obj-$(CONFIG_SND_SOC_WM8991)   += snd-soc-wm8991.o
 obj-$(CONFIG_SND_SOC_WM8993)   += snd-soc-wm8993.o
 obj-$(CONFIG_SND_SOC_WM8994)   += snd-soc-wm8994.o
 obj-$(CONFIG_SND_SOC_WM8995)   += snd-soc-wm8995.o
@@ -151,6 +166,7 @@ obj-$(CONFIG_SND_SOC_WM9713)        += snd-soc-wm9713.o
 obj-$(CONFIG_SND_SOC_WM_HUBS)  += snd-soc-wm-hubs.o
 
 # Amp
+obj-$(CONFIG_SND_SOC_LM4857)   += snd-soc-lm4857.o
 obj-$(CONFIG_SND_SOC_MAX9877)  += snd-soc-max9877.o
 obj-$(CONFIG_SND_SOC_TPA6130A2)        += snd-soc-tpa6130a2.o
 obj-$(CONFIG_SND_SOC_WM2000)   += snd-soc-wm2000.o
index c27f8f5..cbf0b6d 100644 (file)
@@ -294,7 +294,6 @@ static struct spi_driver ak4104_spi_driver = {
 
 static int __init ak4104_init(void)
 {
-       pr_info("Asahi Kasei AK4104 ALSA SoC Codec Driver\n");
        return spi_register_driver(&ak4104_spi_driver);
 }
 module_init(ak4104_init);
index f00eba3..4be0570 100644 (file)
 #define BCKO_MASK      (1 << 3)
 #define BCKO_64                BCKO_MASK
 
+#define DIF_MASK       (3 << 0)
+#define DSP            (0 << 0)
+#define RIGHT_J                (1 << 0)
+#define LEFT_J         (2 << 0)
+#define I2S            (3 << 0)
+
 /* MD_CTL2 */
 #define FS0            (1 << 0)
 #define FS1            (1 << 1)
@@ -354,6 +360,24 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        snd_soc_update_bits(codec, PW_MGMT2, MS, data);
        snd_soc_update_bits(codec, MD_CTL1, BCKO_MASK, bcko);
 
+       /* format type */
+       data = 0;
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_LEFT_J:
+               data = LEFT_J;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               data = I2S;
+               break;
+       /* FIXME
+        * Please add RIGHT_J / DSP support here
+        */
+       default:
+               return -EINVAL;
+               break;
+       }
+       snd_soc_update_bits(codec, MD_CTL1, DIF_MASK, data);
+
        return 0;
 }
 
index 8b51245..0206a17 100644 (file)
@@ -193,12 +193,12 @@ static struct cs4270_mode_ratios cs4270_mode_ratios[] = {
 /* The number of MCLK/LRCK ratios supported by the CS4270 */
 #define NUM_MCLK_RATIOS                ARRAY_SIZE(cs4270_mode_ratios)
 
-static int cs4270_reg_is_readable(unsigned int reg)
+static int cs4270_reg_is_readable(struct snd_soc_codec *codec, unsigned int reg)
 {
        return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG);
 }
 
-static int cs4270_reg_is_volatile(unsigned int reg)
+static int cs4270_reg_is_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
        /* Unreadable registers are considered volatile */
        if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG))
@@ -719,7 +719,7 @@ static int cs4270_i2c_remove(struct i2c_client *i2c_client)
 /*
  * cs4270_id - I2C device IDs supported by this driver
  */
-static struct i2c_device_id cs4270_id[] = {
+static const struct i2c_device_id cs4270_id[] = {
        {"cs4270", 0},
        {}
 };
@@ -743,8 +743,6 @@ static struct i2c_driver cs4270_i2c_driver = {
 
 static int __init cs4270_init(void)
 {
-       pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n");
-
        return i2c_add_driver(&cs4270_i2c_driver);
 }
 module_init(cs4270_init);
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
new file mode 100644 (file)
index 0000000..083aab9
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ * CS4271 ASoC codec driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver support CS4271 codec being master or slave, working
+ * in control port mode, connected either via SPI or I2C.
+ * The data format accepted is I2S or left-justified.
+ * DAPM support not implemented.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/spi/spi.h>
+#include <sound/cs4271.h>
+
+#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+                           SNDRV_PCM_FMTBIT_S24_LE | \
+                           SNDRV_PCM_FMTBIT_S32_LE)
+#define CS4271_PCM_RATES SNDRV_PCM_RATE_8000_192000
+
+/*
+ * CS4271 registers
+ * High byte represents SPI chip address (0x10) + write command (0)
+ * Low byte - codec register address
+ */
+#define CS4271_MODE1   0x2001  /* Mode Control 1 */
+#define CS4271_DACCTL  0x2002  /* DAC Control */
+#define CS4271_DACVOL  0x2003  /* DAC Volume & Mixing Control */
+#define CS4271_VOLA    0x2004  /* DAC Channel A Volume Control */
+#define CS4271_VOLB    0x2005  /* DAC Channel B Volume Control */
+#define CS4271_ADCCTL  0x2006  /* ADC Control */
+#define CS4271_MODE2   0x2007  /* Mode Control 2 */
+#define CS4271_CHIPID  0x2008  /* Chip ID */
+
+#define CS4271_FIRSTREG        CS4271_MODE1
+#define CS4271_LASTREG CS4271_MODE2
+#define CS4271_NR_REGS ((CS4271_LASTREG & 0xFF) + 1)
+
+/* Bit masks for the CS4271 registers */
+#define CS4271_MODE1_MODE_MASK 0xC0
+#define CS4271_MODE1_MODE_1X   0x00
+#define CS4271_MODE1_MODE_2X   0x80
+#define CS4271_MODE1_MODE_4X   0xC0
+
+#define CS4271_MODE1_DIV_MASK  0x30
+#define CS4271_MODE1_DIV_1     0x00
+#define CS4271_MODE1_DIV_15    0x10
+#define CS4271_MODE1_DIV_2     0x20
+#define CS4271_MODE1_DIV_3     0x30
+
+#define CS4271_MODE1_MASTER    0x08
+
+#define CS4271_MODE1_DAC_DIF_MASK      0x07
+#define CS4271_MODE1_DAC_DIF_LJ                0x00
+#define CS4271_MODE1_DAC_DIF_I2S       0x01
+#define CS4271_MODE1_DAC_DIF_RJ16      0x02
+#define CS4271_MODE1_DAC_DIF_RJ24      0x03
+#define CS4271_MODE1_DAC_DIF_RJ20      0x04
+#define CS4271_MODE1_DAC_DIF_RJ18      0x05
+
+#define CS4271_DACCTL_AMUTE    0x80
+#define CS4271_DACCTL_IF_SLOW  0x40
+
+#define CS4271_DACCTL_DEM_MASK 0x30
+#define CS4271_DACCTL_DEM_DIS  0x00
+#define CS4271_DACCTL_DEM_441  0x10
+#define CS4271_DACCTL_DEM_48   0x20
+#define CS4271_DACCTL_DEM_32   0x30
+
+#define CS4271_DACCTL_SVRU     0x08
+#define CS4271_DACCTL_SRD      0x04
+#define CS4271_DACCTL_INVA     0x02
+#define CS4271_DACCTL_INVB     0x01
+
+#define CS4271_DACVOL_BEQUA    0x40
+#define CS4271_DACVOL_SOFT     0x20
+#define CS4271_DACVOL_ZEROC    0x10
+
+#define CS4271_DACVOL_ATAPI_MASK       0x0F
+#define CS4271_DACVOL_ATAPI_M_M                0x00
+#define CS4271_DACVOL_ATAPI_M_BR       0x01
+#define CS4271_DACVOL_ATAPI_M_BL       0x02
+#define CS4271_DACVOL_ATAPI_M_BLR2     0x03
+#define CS4271_DACVOL_ATAPI_AR_M       0x04
+#define CS4271_DACVOL_ATAPI_AR_BR      0x05
+#define CS4271_DACVOL_ATAPI_AR_BL      0x06
+#define CS4271_DACVOL_ATAPI_AR_BLR2    0x07
+#define CS4271_DACVOL_ATAPI_AL_M       0x08
+#define CS4271_DACVOL_ATAPI_AL_BR      0x09
+#define CS4271_DACVOL_ATAPI_AL_BL      0x0A
+#define CS4271_DACVOL_ATAPI_AL_BLR2    0x0B
+#define CS4271_DACVOL_ATAPI_ALR2_M     0x0C
+#define CS4271_DACVOL_ATAPI_ALR2_BR    0x0D
+#define CS4271_DACVOL_ATAPI_ALR2_BL    0x0E
+#define CS4271_DACVOL_ATAPI_ALR2_BLR2  0x0F
+
+#define CS4271_VOLA_MUTE       0x80
+#define CS4271_VOLA_VOL_MASK   0x7F
+#define CS4271_VOLB_MUTE       0x80
+#define CS4271_VOLB_VOL_MASK   0x7F
+
+#define CS4271_ADCCTL_DITHER16 0x20
+
+#define CS4271_ADCCTL_ADC_DIF_MASK     0x10
+#define CS4271_ADCCTL_ADC_DIF_LJ       0x00
+#define CS4271_ADCCTL_ADC_DIF_I2S      0x10
+
+#define CS4271_ADCCTL_MUTEA    0x08
+#define CS4271_ADCCTL_MUTEB    0x04
+#define CS4271_ADCCTL_HPFDA    0x02
+#define CS4271_ADCCTL_HPFDB    0x01
+
+#define CS4271_MODE2_LOOP      0x10
+#define CS4271_MODE2_MUTECAEQUB        0x08
+#define CS4271_MODE2_FREEZE    0x04
+#define CS4271_MODE2_CPEN      0x02
+#define CS4271_MODE2_PDN       0x01
+
+#define CS4271_CHIPID_PART_MASK        0xF0
+#define CS4271_CHIPID_REV_MASK 0x0F
+
+/*
+ * Default CS4271 power-up configuration
+ * Array contains non-existing in hw register at address 0
+ * Array do not include Chip ID, as codec driver does not use
+ * registers read operations at all
+ */
+static const u8 cs4271_dflt_reg[CS4271_NR_REGS] = {
+       0,
+       0,
+       CS4271_DACCTL_AMUTE,
+       CS4271_DACVOL_SOFT | CS4271_DACVOL_ATAPI_AL_BR,
+       0,
+       0,
+       0,
+       0,
+};
+
+struct cs4271_private {
+       /* SND_SOC_I2C or SND_SOC_SPI */
+       enum snd_soc_control_type       bus_type;
+       void                            *control_data;
+       unsigned int                    mclk;
+       bool                            master;
+       bool                            deemph;
+       /* Current sample rate for de-emphasis control */
+       int                             rate;
+       /* GPIO driving Reset pin, if any */
+       int                             gpio_nreset;
+       /* GPIO that disable serial bus, if any */
+       int                             gpio_disable;
+};
+
+/*
+ * @freq is the desired MCLK rate
+ * MCLK rate should (c) be the sample rate, multiplied by one of the
+ * ratios listed in cs4271_mclk_fs_ratios table
+ */
+static int cs4271_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+       cs4271->mclk = freq;
+       return 0;
+}
+
+static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                             unsigned int format)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = 0;
+       int ret;
+
+       switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               cs4271->master = 0;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               cs4271->master = 1;
+               val |= CS4271_MODE1_MASTER;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid DAI format\n");
+               return -EINVAL;
+       }
+
+       switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_LEFT_J:
+               val |= CS4271_MODE1_DAC_DIF_LJ;
+               ret = snd_soc_update_bits(codec, CS4271_ADCCTL,
+                       CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_LJ);
+               if (ret < 0)
+                       return ret;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               val |= CS4271_MODE1_DAC_DIF_I2S;
+               ret = snd_soc_update_bits(codec, CS4271_ADCCTL,
+                       CS4271_ADCCTL_ADC_DIF_MASK, CS4271_ADCCTL_ADC_DIF_I2S);
+               if (ret < 0)
+                       return ret;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid DAI format\n");
+               return -EINVAL;
+       }
+
+       ret = snd_soc_update_bits(codec, CS4271_MODE1,
+               CS4271_MODE1_DAC_DIF_MASK | CS4271_MODE1_MASTER, val);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int cs4271_deemph[] = {0, 44100, 48000, 32000};
+
+static int cs4271_set_deemph(struct snd_soc_codec *codec)
+{
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+       int i, ret;
+       int val = CS4271_DACCTL_DEM_DIS;
+
+       if (cs4271->deemph) {
+               /* Find closest de-emphasis freq */
+               val = 1;
+               for (i = 2; i < ARRAY_SIZE(cs4271_deemph); i++)
+                       if (abs(cs4271_deemph[i] - cs4271->rate) <
+                           abs(cs4271_deemph[val] - cs4271->rate))
+                               val = i;
+               val <<= 4;
+       }
+
+       ret = snd_soc_update_bits(codec, CS4271_DACCTL,
+               CS4271_DACCTL_DEM_MASK, val);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int cs4271_get_deemph(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.enumerated.item[0] = cs4271->deemph;
+       return 0;
+}
+
+static int cs4271_put_deemph(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+
+       cs4271->deemph = ucontrol->value.enumerated.item[0];
+       return cs4271_set_deemph(codec);
+}
+
+struct cs4271_clk_cfg {
+       bool            master;         /* codec mode */
+       u8              speed_mode;     /* codec speed mode: 1x, 2x, 4x */
+       unsigned short  ratio;          /* MCLK / sample rate */
+       u8              ratio_mask;     /* ratio bit mask for Master mode */
+};
+
+static struct cs4271_clk_cfg cs4271_clk_tab[] = {
+       {1, CS4271_MODE1_MODE_1X, 256,  CS4271_MODE1_DIV_1},
+       {1, CS4271_MODE1_MODE_1X, 384,  CS4271_MODE1_DIV_15},
+       {1, CS4271_MODE1_MODE_1X, 512,  CS4271_MODE1_DIV_2},
+       {1, CS4271_MODE1_MODE_1X, 768,  CS4271_MODE1_DIV_3},
+       {1, CS4271_MODE1_MODE_2X, 128,  CS4271_MODE1_DIV_1},
+       {1, CS4271_MODE1_MODE_2X, 192,  CS4271_MODE1_DIV_15},
+       {1, CS4271_MODE1_MODE_2X, 256,  CS4271_MODE1_DIV_2},
+       {1, CS4271_MODE1_MODE_2X, 384,  CS4271_MODE1_DIV_3},
+       {1, CS4271_MODE1_MODE_4X, 64,   CS4271_MODE1_DIV_1},
+       {1, CS4271_MODE1_MODE_4X, 96,   CS4271_MODE1_DIV_15},
+       {1, CS4271_MODE1_MODE_4X, 128,  CS4271_MODE1_DIV_2},
+       {1, CS4271_MODE1_MODE_4X, 192,  CS4271_MODE1_DIV_3},
+       {0, CS4271_MODE1_MODE_1X, 256,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_1X, 384,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_1X, 512,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_1X, 768,  CS4271_MODE1_DIV_2},
+       {0, CS4271_MODE1_MODE_1X, 1024, CS4271_MODE1_DIV_2},
+       {0, CS4271_MODE1_MODE_2X, 128,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_2X, 192,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_2X, 256,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_2X, 384,  CS4271_MODE1_DIV_2},
+       {0, CS4271_MODE1_MODE_2X, 512,  CS4271_MODE1_DIV_2},
+       {0, CS4271_MODE1_MODE_4X, 64,   CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_4X, 96,   CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_4X, 128,  CS4271_MODE1_DIV_1},
+       {0, CS4271_MODE1_MODE_4X, 192,  CS4271_MODE1_DIV_2},
+       {0, CS4271_MODE1_MODE_4X, 256,  CS4271_MODE1_DIV_2},
+};
+
+#define CS4171_NR_RATIOS ARRAY_SIZE(cs4271_clk_tab)
+
+static int cs4271_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_codec *codec = rtd->codec;
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+       int i, ret;
+       unsigned int ratio, val;
+
+       cs4271->rate = params_rate(params);
+
+       /* Configure DAC */
+       if (cs4271->rate < 50000)
+               val = CS4271_MODE1_MODE_1X;
+       else if (cs4271->rate < 100000)
+               val = CS4271_MODE1_MODE_2X;
+       else
+               val = CS4271_MODE1_MODE_4X;
+
+       ratio = cs4271->mclk / cs4271->rate;
+       for (i = 0; i < CS4171_NR_RATIOS; i++)
+               if ((cs4271_clk_tab[i].master == cs4271->master) &&
+                   (cs4271_clk_tab[i].speed_mode == val) &&
+                   (cs4271_clk_tab[i].ratio == ratio))
+                       break;
+
+       if (i == CS4171_NR_RATIOS) {
+               dev_err(codec->dev, "Invalid sample rate\n");
+               return -EINVAL;
+       }
+
+       val |= cs4271_clk_tab[i].ratio_mask;
+
+       ret = snd_soc_update_bits(codec, CS4271_MODE1,
+               CS4271_MODE1_MODE_MASK | CS4271_MODE1_DIV_MASK, val);
+       if (ret < 0)
+               return ret;
+
+       return cs4271_set_deemph(codec);
+}
+
+static int cs4271_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       int ret;
+       int val_a = 0;
+       int val_b = 0;
+
+       if (mute) {
+               val_a = CS4271_VOLA_MUTE;
+               val_b = CS4271_VOLB_MUTE;
+       }
+
+       ret = snd_soc_update_bits(codec, CS4271_VOLA, CS4271_VOLA_MUTE, val_a);
+       if (ret < 0)
+               return ret;
+       ret = snd_soc_update_bits(codec, CS4271_VOLB, CS4271_VOLB_MUTE, val_b);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+/* CS4271 controls */
+static DECLARE_TLV_DB_SCALE(cs4271_dac_tlv, -12700, 100, 0);
+
+static const struct snd_kcontrol_new cs4271_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("Master Playback Volume", CS4271_VOLA, CS4271_VOLB,
+               0, 0x7F, 1, cs4271_dac_tlv),
+       SOC_SINGLE("Digital Loopback Switch", CS4271_MODE2, 4, 1, 0),
+       SOC_SINGLE("Soft Ramp Switch", CS4271_DACVOL, 5, 1, 0),
+       SOC_SINGLE("Zero Cross Switch", CS4271_DACVOL, 4, 1, 0),
+       SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+               cs4271_get_deemph, cs4271_put_deemph),
+       SOC_SINGLE("Auto-Mute Switch", CS4271_DACCTL, 7, 1, 0),
+       SOC_SINGLE("Slow Roll Off Filter Switch", CS4271_DACCTL, 6, 1, 0),
+       SOC_SINGLE("Soft Volume Ramp-Up Switch", CS4271_DACCTL, 3, 1, 0),
+       SOC_SINGLE("Soft Ramp-Down Switch", CS4271_DACCTL, 2, 1, 0),
+       SOC_SINGLE("Left Channel Inversion Switch", CS4271_DACCTL, 1, 1, 0),
+       SOC_SINGLE("Right Channel Inversion Switch", CS4271_DACCTL, 0, 1, 0),
+       SOC_DOUBLE("Master Capture Switch", CS4271_ADCCTL, 3, 2, 1, 1),
+       SOC_SINGLE("Dither 16-Bit Data Switch", CS4271_ADCCTL, 5, 1, 0),
+       SOC_DOUBLE("High Pass Filter Switch", CS4271_ADCCTL, 1, 0, 1, 1),
+       SOC_DOUBLE_R("Master Playback Switch", CS4271_VOLA, CS4271_VOLB,
+               7, 1, 1),
+};
+
+static struct snd_soc_dai_ops cs4271_dai_ops = {
+       .hw_params      = cs4271_hw_params,
+       .set_sysclk     = cs4271_set_dai_sysclk,
+       .set_fmt        = cs4271_set_dai_fmt,
+       .digital_mute   = cs4271_digital_mute,
+};
+
+static struct snd_soc_dai_driver cs4271_dai = {
+       .name = "cs4271-hifi",
+       .playback = {
+               .stream_name    = "Playback",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = CS4271_PCM_RATES,
+               .formats        = CS4271_PCM_FORMATS,
+       },
+       .capture = {
+               .stream_name    = "Capture",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = CS4271_PCM_RATES,
+               .formats        = CS4271_PCM_FORMATS,
+       },
+       .ops = &cs4271_dai_ops,
+       .symmetric_rates = 1,
+};
+
+#ifdef CONFIG_PM
+static int cs4271_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg)
+{
+       int ret;
+       /* Set power-down bit */
+       ret = snd_soc_update_bits(codec, CS4271_MODE2, 0, CS4271_MODE2_PDN);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static int cs4271_soc_resume(struct snd_soc_codec *codec)
+{
+       int ret;
+       /* Restore codec state */
+       ret = snd_soc_cache_sync(codec);
+       if (ret < 0)
+               return ret;
+       /* then disable the power-down bit */
+       ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+#else
+#define cs4271_soc_suspend     NULL
+#define cs4271_soc_resume      NULL
+#endif /* CONFIG_PM */
+
+static int cs4271_probe(struct snd_soc_codec *codec)
+{
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+       struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
+       int ret;
+       int gpio_nreset = -EINVAL;
+
+       codec->control_data = cs4271->control_data;
+
+       if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset))
+               gpio_nreset = cs4271plat->gpio_nreset;
+
+       if (gpio_nreset >= 0)
+               if (gpio_request(gpio_nreset, "CS4271 Reset"))
+                       gpio_nreset = -EINVAL;
+       if (gpio_nreset >= 0) {
+               /* Reset codec */
+               gpio_direction_output(gpio_nreset, 0);
+               udelay(1);
+               gpio_set_value(gpio_nreset, 1);
+               /* Give the codec time to wake up */
+               udelay(1);
+       }
+
+       cs4271->gpio_nreset = gpio_nreset;
+
+       /*
+        * In case of I2C, chip address specified in board data.
+        * So cache IO operations use 8 bit codec register address.
+        * In case of SPI, chip address and register address
+        * passed together as 16 bit value.
+        * Anyway, register address is masked with 0xFF inside
+        * soc-cache code.
+        */
+       if (cs4271->bus_type == SND_SOC_SPI)
+               ret = snd_soc_codec_set_cache_io(codec, 16, 8,
+                       cs4271->bus_type);
+       else
+               ret = snd_soc_codec_set_cache_io(codec, 8, 8,
+                       cs4271->bus_type);
+       if (ret) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_update_bits(codec, CS4271_MODE2, 0,
+               CS4271_MODE2_PDN | CS4271_MODE2_CPEN);
+       if (ret < 0)
+               return ret;
+       ret = snd_soc_update_bits(codec, CS4271_MODE2, CS4271_MODE2_PDN, 0);
+       if (ret < 0)
+               return ret;
+       /* Power-up sequence requires 85 uS */
+       udelay(85);
+
+       return snd_soc_add_controls(codec, cs4271_snd_controls,
+               ARRAY_SIZE(cs4271_snd_controls));
+}
+
+static int cs4271_remove(struct snd_soc_codec *codec)
+{
+       struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
+       int gpio_nreset;
+
+       gpio_nreset = cs4271->gpio_nreset;
+
+       if (gpio_is_valid(gpio_nreset)) {
+               /* Set codec to the reset state */
+               gpio_set_value(gpio_nreset, 0);
+               gpio_free(gpio_nreset);
+       }
+
+       return 0;
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
+       .probe                  = cs4271_probe,
+       .remove                 = cs4271_remove,
+       .suspend                = cs4271_soc_suspend,
+       .resume                 = cs4271_soc_resume,
+       .reg_cache_default      = cs4271_dflt_reg,
+       .reg_cache_size         = ARRAY_SIZE(cs4271_dflt_reg),
+       .reg_word_size          = sizeof(cs4271_dflt_reg[0]),
+       .compress_type          = SND_SOC_FLAT_COMPRESSION,
+};
+
+#if defined(CONFIG_SPI_MASTER)
+static int __devinit cs4271_spi_probe(struct spi_device *spi)
+{
+       struct cs4271_private *cs4271;
+
+       cs4271 = devm_kzalloc(&spi->dev, sizeof(*cs4271), GFP_KERNEL);
+       if (!cs4271)
+               return -ENOMEM;
+
+       spi_set_drvdata(spi, cs4271);
+       cs4271->control_data = spi;
+       cs4271->bus_type = SND_SOC_SPI;
+
+       return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271,
+               &cs4271_dai, 1);
+}
+
+static int __devexit cs4271_spi_remove(struct spi_device *spi)
+{
+       snd_soc_unregister_codec(&spi->dev);
+       return 0;
+}
+
+static struct spi_driver cs4271_spi_driver = {
+       .driver = {
+               .name   = "cs4271",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = cs4271_spi_probe,
+       .remove         = __devexit_p(cs4271_spi_remove),
+};
+#endif /* defined(CONFIG_SPI_MASTER) */
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static const struct i2c_device_id cs4271_i2c_id[] = {
+       {"cs4271", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+
+static int __devinit cs4271_i2c_probe(struct i2c_client *client,
+                                     const struct i2c_device_id *id)
+{
+       struct cs4271_private *cs4271;
+
+       cs4271 = devm_kzalloc(&client->dev, sizeof(*cs4271), GFP_KERNEL);
+       if (!cs4271)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, cs4271);
+       cs4271->control_data = client;
+       cs4271->bus_type = SND_SOC_I2C;
+
+       return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271,
+               &cs4271_dai, 1);
+}
+
+static int __devexit cs4271_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static struct i2c_driver cs4271_i2c_driver = {
+       .driver = {
+               .name   = "cs4271",
+               .owner  = THIS_MODULE,
+       },
+       .id_table       = cs4271_i2c_id,
+       .probe          = cs4271_i2c_probe,
+       .remove         = __devexit_p(cs4271_i2c_remove),
+};
+#endif /* defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) */
+
+/*
+ * We only register our serial bus driver here without
+ * assignment to particular chip. So if any of the below
+ * fails, there is some problem with I2C or SPI subsystem.
+ * In most cases this module will be compiled with support
+ * of only one serial bus.
+ */
+static int __init cs4271_modinit(void)
+{
+       int ret;
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       ret = i2c_add_driver(&cs4271_i2c_driver);
+       if (ret) {
+               pr_err("Failed to register CS4271 I2C driver: %d\n", ret);
+               return ret;
+       }
+#endif
+
+#if defined(CONFIG_SPI_MASTER)
+       ret = spi_register_driver(&cs4271_spi_driver);
+       if (ret) {
+               pr_err("Failed to register CS4271 SPI driver: %d\n", ret);
+               return ret;
+       }
+#endif
+
+       return 0;
+}
+module_init(cs4271_modinit);
+
+static void __exit cs4271_modexit(void)
+{
+#if defined(CONFIG_SPI_MASTER)
+       spi_unregister_driver(&cs4271_spi_driver);
+#endif
+
+#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+       i2c_del_driver(&cs4271_i2c_driver);
+#endif
+}
+module_exit(cs4271_modexit);
+
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/dfbmcs320.c b/sound/soc/codecs/dfbmcs320.c
new file mode 100644 (file)
index 0000000..704bbde
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Driver for the DFBM-CS320 bluetooth module
+ * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static struct snd_soc_dai_driver dfbmcs320_dai = {
+       .name = "dfbmcs320-pcm",
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 1,
+               .rates = SNDRV_PCM_RATE_8000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .channels_min = 1,
+               .channels_max = 1,
+               .rates = SNDRV_PCM_RATE_8000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_dfbmcs320;
+
+static int __devinit dfbmcs320_probe(struct platform_device *pdev)
+{
+       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_dfbmcs320,
+                       &dfbmcs320_dai, 1);
+}
+
+static int __devexit dfbmcs320_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+
+       return 0;
+}
+
+static struct platform_driver dfmcs320_driver = {
+       .driver = {
+               .name = "dfbmcs320",
+               .owner = THIS_MODULE,
+       },
+       .probe = dfbmcs320_probe,
+       .remove = __devexit_p(dfbmcs320_remove),
+};
+
+static int __init dfbmcs320_init(void)
+{
+       return platform_driver_register(&dfmcs320_driver);
+}
+module_init(dfbmcs320_init);
+
+static void __exit dfbmcs320_exit(void)
+{
+       platform_driver_unregister(&dfmcs320_driver);
+}
+module_exit(dfbmcs320_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ASoC DFBM-CS320 bluethooth module driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
new file mode 100644 (file)
index 0000000..72de47e
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * LM4857 AMP driver
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+struct lm4857 {
+       struct i2c_client *i2c;
+       uint8_t mode;
+};
+
+static const uint8_t lm4857_default_regs[] = {
+       0x00, 0x00, 0x00, 0x00,
+};
+
+/* The register offsets in the cache array */
+#define LM4857_MVOL 0
+#define LM4857_LVOL 1
+#define LM4857_RVOL 2
+#define LM4857_CTRL 3
+
+/* the shifts required to set these bits */
+#define LM4857_3D 5
+#define LM4857_WAKEUP 5
+#define LM4857_EPGAIN 4
+
+static int lm4857_write(struct snd_soc_codec *codec, unsigned int reg,
+               unsigned int value)
+{
+       uint8_t data;
+       int ret;
+
+       ret = snd_soc_cache_write(codec, reg, value);
+       if (ret < 0)
+               return ret;
+
+       data = (reg << 6) | value;
+       ret = i2c_master_send(codec->control_data, &data, 1);
+       if (ret != 1) {
+               dev_err(codec->dev, "Failed to write register: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static unsigned int lm4857_read(struct snd_soc_codec *codec,
+               unsigned int reg)
+{
+       unsigned int val;
+       int ret;
+
+       ret = snd_soc_cache_read(codec, reg, &val);
+       if (ret)
+               return -1;
+
+       return val;
+}
+
+static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = lm4857->mode;
+
+       return 0;
+}
+
+static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
+       uint8_t value = ucontrol->value.integer.value[0];
+
+       lm4857->mode = value;
+
+       if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
+               snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, value + 6);
+
+       return 1;
+}
+
+static int lm4857_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, lm4857->mode + 6);
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, 0);
+               break;
+       default:
+               break;
+       }
+
+       codec->dapm.bias_level = level;
+
+       return 0;
+}
+
+static const char *lm4857_mode[] = {
+       "Earpiece",
+       "Loudspeaker",
+       "Loudspeaker + Headphone",
+       "Headphone",
+};
+
+static const struct soc_enum lm4857_mode_enum =
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lm4857_mode), lm4857_mode);
+
+static const struct snd_soc_dapm_widget lm4857_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("IN"),
+
+       SND_SOC_DAPM_OUTPUT("LS"),
+       SND_SOC_DAPM_OUTPUT("HP"),
+       SND_SOC_DAPM_OUTPUT("EP"),
+};
+
+static const DECLARE_TLV_DB_SCALE(stereo_tlv, -4050, 150, 0);
+static const DECLARE_TLV_DB_SCALE(mono_tlv, -3450, 150, 0);
+
+static const struct snd_kcontrol_new lm4857_controls[] = {
+       SOC_SINGLE_TLV("Left Playback Volume", LM4857_LVOL, 0, 31, 0,
+               stereo_tlv),
+       SOC_SINGLE_TLV("Right Playback Volume", LM4857_RVOL, 0, 31, 0,
+               stereo_tlv),
+       SOC_SINGLE_TLV("Mono Playback Volume", LM4857_MVOL, 0, 31, 0,
+               mono_tlv),
+       SOC_SINGLE("Spk 3D Playback Switch", LM4857_LVOL, LM4857_3D, 1, 0),
+       SOC_SINGLE("HP 3D Playback Switch", LM4857_RVOL, LM4857_3D, 1, 0),
+       SOC_SINGLE("Fast Wakeup Playback Switch", LM4857_CTRL,
+               LM4857_WAKEUP, 1, 0),
+       SOC_SINGLE("Earpiece 6dB Playback Switch", LM4857_CTRL,
+               LM4857_EPGAIN, 1, 0),
+
+       SOC_ENUM_EXT("Mode", lm4857_mode_enum,
+               lm4857_get_mode, lm4857_set_mode),
+};
+
+/* There is a demux inbetween the the input signal and the output signals.
+ * Currently there is no easy way to model it in ASoC and since it does not make
+ * much of a difference in practice simply connect the input direclty to the
+ * outputs. */
+static const struct snd_soc_dapm_route lm4857_routes[] = {
+       {"LS", NULL, "IN"},
+       {"HP", NULL, "IN"},
+       {"EP", NULL, "IN"},
+};
+
+static int lm4857_probe(struct snd_soc_codec *codec)
+{
+       struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       int ret;
+
+       codec->control_data = lm4857->i2c;
+
+       ret = snd_soc_add_controls(codec, lm4857_controls,
+                       ARRAY_SIZE(lm4857_controls));
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dapm_new_controls(dapm, lm4857_dapm_widgets,
+                       ARRAY_SIZE(lm4857_dapm_widgets));
+       if (ret)
+               return ret;
+
+       ret = snd_soc_dapm_add_routes(dapm, lm4857_routes,
+                       ARRAY_SIZE(lm4857_routes));
+       if (ret)
+               return ret;
+
+       snd_soc_dapm_new_widgets(dapm);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
+       .write = lm4857_write,
+       .read = lm4857_read,
+       .probe = lm4857_probe,
+       .reg_cache_size = ARRAY_SIZE(lm4857_default_regs),
+       .reg_word_size = sizeof(uint8_t),
+       .reg_cache_default = lm4857_default_regs,
+       .set_bias_level = lm4857_set_bias_level,
+};
+
+static int __devinit lm4857_i2c_probe(struct i2c_client *i2c,
+       const struct i2c_device_id *id)
+{
+       struct lm4857 *lm4857;
+       int ret;
+
+       lm4857 = kzalloc(sizeof(*lm4857), GFP_KERNEL);
+       if (!lm4857)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, lm4857);
+
+       lm4857->i2c = i2c;
+
+       ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
+
+       if (ret) {
+               kfree(lm4857);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int __devexit lm4857_i2c_remove(struct i2c_client *i2c)
+{
+       struct lm4857 *lm4857 = i2c_get_clientdata(i2c);
+
+       snd_soc_unregister_codec(&i2c->dev);
+       kfree(lm4857);
+
+       return 0;
+}
+
+static const struct i2c_device_id lm4857_i2c_id[] = {
+       { "lm4857", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
+
+static struct i2c_driver lm4857_i2c_driver = {
+       .driver = {
+               .name = "lm4857",
+               .owner = THIS_MODULE,
+       },
+       .probe = lm4857_i2c_probe,
+       .remove = __devexit_p(lm4857_i2c_remove),
+       .id_table = lm4857_i2c_id,
+};
+
+static int __init lm4857_init(void)
+{
+       return i2c_add_driver(&lm4857_i2c_driver);
+}
+module_init(lm4857_init);
+
+static void __exit lm4857_exit(void)
+{
+       i2c_del_driver(&lm4857_i2c_driver);
+}
+module_exit(lm4857_exit);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("LM4857 amplifier driver");
+MODULE_LICENSE("GPL");
index 89498f9..bd0517c 100644 (file)
@@ -608,7 +608,7 @@ static struct {
        { 0xFF, 0x00, 1 }, /* FF */
 };
 
-static int max98088_volatile_register(unsigned int reg)
+static int max98088_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        return max98088_access[reg].vol;
 }
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
new file mode 100644 (file)
index 0000000..208d2ee
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * max9850.c  --  codec driver for max9850
+ *
+ * Copyright (C) 2011 taskit GmbH
+ *
+ * Author: Christian Glindkamp <christian.glindkamp@taskit.de>
+ *
+ * Initial development of this code was funded by
+ * MICRONIC Computer Systeme GmbH, http://www.mcsberlin.de/
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "max9850.h"
+
+struct max9850_priv {
+       unsigned int sysclk;
+};
+
+/* max9850 register cache */
+static const u8 max9850_reg[MAX9850_CACHEREGNUM] = {
+       0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* these registers are not used at the moment but provided for the sake of
+ * completeness */
+static int max9850_volatile_register(struct snd_soc_codec *codec,
+               unsigned int reg)
+{
+       switch (reg) {
+       case MAX9850_STATUSA:
+       case MAX9850_STATUSB:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static const unsigned int max9850_tlv[] = {
+       TLV_DB_RANGE_HEAD(4),
+       0x18, 0x1f, TLV_DB_SCALE_ITEM(-7450, 400, 0),
+       0x20, 0x33, TLV_DB_SCALE_ITEM(-4150, 200, 0),
+       0x34, 0x37, TLV_DB_SCALE_ITEM(-150, 100, 0),
+       0x38, 0x3f, TLV_DB_SCALE_ITEM(250, 50, 0),
+};
+
+static const struct snd_kcontrol_new max9850_controls[] = {
+SOC_SINGLE_TLV("Headphone Volume", MAX9850_VOLUME, 0, 0x3f, 1, max9850_tlv),
+SOC_SINGLE("Headphone Switch", MAX9850_VOLUME, 7, 1, 1),
+SOC_SINGLE("Mono Switch", MAX9850_GENERAL_PURPOSE, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new max9850_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Line In Switch", MAX9850_ENABLE, 1, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget max9850_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("Charge Pump 1", MAX9850_ENABLE, 4, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("Charge Pump 2", MAX9850_ENABLE, 5, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MCLK", MAX9850_ENABLE, 6, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("SHDN", MAX9850_ENABLE, 7, 0, NULL, 0),
+SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", MAX9850_ENABLE, 2, 0,
+               &max9850_mixer_controls[0],
+               ARRAY_SIZE(max9850_mixer_controls)),
+SND_SOC_DAPM_PGA("Headphone Output", MAX9850_ENABLE, 3, 0, NULL, 0),
+SND_SOC_DAPM_DAC("DAC", "HiFi Playback", MAX9850_ENABLE, 0, 0),
+SND_SOC_DAPM_OUTPUT("OUTL"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("OUTR"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+SND_SOC_DAPM_MIXER("Line Input", SND_SOC_NOPM, 0, 0, NULL, 0),
+SND_SOC_DAPM_INPUT("INL"),
+SND_SOC_DAPM_INPUT("INR"),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       /* output mixer */
+       {"Output Mixer", NULL, "DAC"},
+       {"Output Mixer", "Line In Switch", "Line Input"},
+
+       /* outputs */
+       {"Headphone Output", NULL, "Output Mixer"},
+       {"HPL", NULL, "Headphone Output"},
+       {"HPR", NULL, "Headphone Output"},
+       {"OUTL", NULL, "Output Mixer"},
+       {"OUTR", NULL, "Output Mixer"},
+
+       /* inputs */
+       {"Line Input", NULL, "INL"},
+       {"Line Input", NULL, "INR"},
+
+       /* supplies */
+       {"Output Mixer", NULL, "Charge Pump 1"},
+       {"Output Mixer", NULL, "Charge Pump 2"},
+       {"Output Mixer", NULL, "SHDN"},
+       {"DAC", NULL, "MCLK"},
+};
+
+static int max9850_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+       u64 lrclk_div;
+       u8 sf, da;
+
+       if (!max9850->sysclk)
+               return -EINVAL;
+
+       /* lrclk_div = 2^22 * rate / iclk with iclk = mclk / sf */
+       sf = (snd_soc_read(codec, MAX9850_CLOCK) >> 2) + 1;
+       lrclk_div = (1 << 22);
+       lrclk_div *= params_rate(params);
+       lrclk_div *= sf;
+       do_div(lrclk_div, max9850->sysclk);
+
+       snd_soc_write(codec, MAX9850_LRCLK_MSB, (lrclk_div >> 8) & 0x7f);
+       snd_soc_write(codec, MAX9850_LRCLK_LSB, lrclk_div & 0xff);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               da = 0;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               da = 0x2;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               da = 0x3;
+               break;
+       default:
+               return -EINVAL;
+       }
+       snd_soc_update_bits(codec, MAX9850_DIGITAL_AUDIO, 0x3, da);
+
+       return 0;
+}
+
+static int max9850_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max9850_priv *max9850 = snd_soc_codec_get_drvdata(codec);
+
+       /* calculate mclk -> iclk divider */
+       if (freq <= 13000000)
+               snd_soc_write(codec, MAX9850_CLOCK, 0x0);
+       else if (freq <= 26000000)
+               snd_soc_write(codec, MAX9850_CLOCK, 0x4);
+       else if (freq <= 40000000)
+               snd_soc_write(codec, MAX9850_CLOCK, 0x8);
+       else
+               return -EINVAL;
+
+       max9850->sysclk = freq;
+       return 0;
+}
+
+static int max9850_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u8 da = 0;
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               da |= MAX9850_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* interface format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               da |= MAX9850_DLY;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               da |= MAX9850_RTJ;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               da |= MAX9850_BCINV | MAX9850_INV;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               da |= MAX9850_BCINV;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               da |= MAX9850_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set da */
+       snd_soc_write(codec, MAX9850_DIGITAL_AUDIO, da);
+
+       return 0;
+}
+
+static int max9850_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       int ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       ret = snd_soc_cache_sync(codec);
+                       if (ret) {
+                               dev_err(codec->dev,
+                                       "Failed to sync cache: %d\n", ret);
+                               return ret;
+                       }
+               }
+               break;
+       case SND_SOC_BIAS_OFF:
+               break;
+       }
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+#define MAX9850_RATES SNDRV_PCM_RATE_8000_48000
+
+#define MAX9850_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+       SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops max9850_dai_ops = {
+       .hw_params      = max9850_hw_params,
+       .set_sysclk     = max9850_set_dai_sysclk,
+       .set_fmt        = max9850_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver max9850_dai = {
+       .name = "max9850-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX9850_RATES,
+               .formats = MAX9850_FORMATS
+       },
+       .ops = &max9850_dai_ops,
+};
+
+#ifdef CONFIG_PM
+static int max9850_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       max9850_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+static int max9850_resume(struct snd_soc_codec *codec)
+{
+       max9850_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define max9850_suspend NULL
+#define max9850_resume NULL
+#endif
+
+static int max9850_probe(struct snd_soc_codec *codec)
+{
+       struct snd_soc_dapm_context *dapm = &codec->dapm;
+       int ret;
+
+       ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+
+       /* enable zero-detect */
+       snd_soc_update_bits(codec, MAX9850_GENERAL_PURPOSE, 1, 1);
+       /* enable slew-rate control */
+       snd_soc_update_bits(codec, MAX9850_VOLUME, 0x40, 0x40);
+       /* set slew-rate 125ms */
+       snd_soc_update_bits(codec, MAX9850_CHARGE_PUMP, 0xff, 0xc0);
+
+       snd_soc_dapm_new_controls(dapm, max9850_dapm_widgets,
+                                 ARRAY_SIZE(max9850_dapm_widgets));
+       snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
+
+       snd_soc_add_controls(codec, max9850_controls,
+                       ARRAY_SIZE(max9850_controls));
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max9850 = {
+       .probe =        max9850_probe,
+       .suspend =      max9850_suspend,
+       .resume =       max9850_resume,
+       .set_bias_level = max9850_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(max9850_reg),
+       .reg_word_size = sizeof(u8),
+       .reg_cache_default = max9850_reg,
+       .volatile_register = max9850_volatile_register,
+};
+
+static int __devinit max9850_i2c_probe(struct i2c_client *i2c,
+               const struct i2c_device_id *id)
+{
+       struct max9850_priv *max9850;
+       int ret;
+
+       max9850 = kzalloc(sizeof(struct max9850_priv), GFP_KERNEL);
+       if (max9850 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, max9850);
+
+       ret = snd_soc_register_codec(&i2c->dev,
+                       &soc_codec_dev_max9850, &max9850_dai, 1);
+       if (ret < 0)
+               kfree(max9850);
+       return ret;
+}
+
+static __devexit int max9850_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       kfree(i2c_get_clientdata(client));
+       return 0;
+}
+
+static const struct i2c_device_id max9850_i2c_id[] = {
+       { "max9850", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max9850_i2c_id);
+
+static struct i2c_driver max9850_i2c_driver = {
+       .driver = {
+               .name = "max9850",
+               .owner = THIS_MODULE,
+       },
+       .probe = max9850_i2c_probe,
+       .remove = __devexit_p(max9850_i2c_remove),
+       .id_table = max9850_i2c_id,
+};
+
+static int __init max9850_init(void)
+{
+       return i2c_add_driver(&max9850_i2c_driver);
+}
+module_init(max9850_init);
+
+static void __exit max9850_exit(void)
+{
+       i2c_del_driver(&max9850_i2c_driver);
+}
+module_exit(max9850_exit);
+
+MODULE_AUTHOR("Christian Glindkamp <christian.glindkamp@taskit.de>");
+MODULE_DESCRIPTION("ASoC MAX9850 codec driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9850.h b/sound/soc/codecs/max9850.h
new file mode 100644 (file)
index 0000000..72b1ddb
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * max9850.h  --  codec driver for max9850
+ *
+ * Copyright (C) 2011 taskit GmbH
+ * Author: Christian Glindkamp <christian.glindkamp@taskit.de>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef _MAX9850_H
+#define _MAX9850_H
+
+#define MAX9850_STATUSA                        0x00
+#define MAX9850_STATUSB                        0x01
+#define MAX9850_VOLUME                 0x02
+#define MAX9850_GENERAL_PURPOSE                0x03
+#define MAX9850_INTERRUPT              0x04
+#define MAX9850_ENABLE                 0x05
+#define MAX9850_CLOCK                  0x06
+#define MAX9850_CHARGE_PUMP            0x07
+#define MAX9850_LRCLK_MSB              0x08
+#define MAX9850_LRCLK_LSB              0x09
+#define MAX9850_DIGITAL_AUDIO          0x0a
+
+#define MAX9850_CACHEREGNUM 11
+
+/* MAX9850_DIGITAL_AUDIO */
+#define MAX9850_MASTER                 (1<<7)
+#define MAX9850_INV                    (1<<6)
+#define MAX9850_BCINV                  (1<<5)
+#define MAX9850_DLY                    (1<<3)
+#define MAX9850_RTJ                    (1<<2)
+
+#endif
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
new file mode 100644 (file)
index 0000000..1f7217f
--- /dev/null
@@ -0,0 +1,1513 @@
+/*
+ * sgtl5000.c  --  SGTL5000 ALSA SoC Audio driver
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/tlv.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+
+#include "sgtl5000.h"
+
+#define SGTL5000_DAP_REG_OFFSET        0x0100
+#define SGTL5000_MAX_REG_OFFSET        0x013A
+
+/* default value of sgtl5000 registers except DAP */
+static const u16 sgtl5000_regs[SGTL5000_MAX_REG_OFFSET >> 1] =  {
+       0xa011, /* 0x0000, CHIP_ID. 11 stand for revison 17 */
+       0x0000, /* 0x0002, CHIP_DIG_POWER. */
+       0x0008, /* 0x0004, CHIP_CKL_CTRL */
+       0x0010, /* 0x0006, CHIP_I2S_CTRL */
+       0x0000, /* 0x0008, reserved */
+       0x0008, /* 0x000A, CHIP_SSS_CTRL */
+       0x0000, /* 0x000C, reserved */
+       0x020c, /* 0x000E, CHIP_ADCDAC_CTRL */
+       0x3c3c, /* 0x0010, CHIP_DAC_VOL */
+       0x0000, /* 0x0012, reserved */
+       0x015f, /* 0x0014, CHIP_PAD_STRENGTH */
+       0x0000, /* 0x0016, reserved */
+       0x0000, /* 0x0018, reserved */
+       0x0000, /* 0x001A, reserved */
+       0x0000, /* 0x001E, reserved */
+       0x0000, /* 0x0020, CHIP_ANA_ADC_CTRL */
+       0x1818, /* 0x0022, CHIP_ANA_HP_CTRL */
+       0x0111, /* 0x0024, CHIP_ANN_CTRL */
+       0x0000, /* 0x0026, CHIP_LINREG_CTRL */
+       0x0000, /* 0x0028, CHIP_REF_CTRL */
+       0x0000, /* 0x002A, CHIP_MIC_CTRL */
+       0x0000, /* 0x002C, CHIP_LINE_OUT_CTRL */
+       0x0404, /* 0x002E, CHIP_LINE_OUT_VOL */
+       0x7060, /* 0x0030, CHIP_ANA_POWER */
+       0x5000, /* 0x0032, CHIP_PLL_CTRL */
+       0x0000, /* 0x0034, CHIP_CLK_TOP_CTRL */
+       0x0000, /* 0x0036, CHIP_ANA_STATUS */
+       0x0000, /* 0x0038, reserved */
+       0x0000, /* 0x003A, CHIP_ANA_TEST2 */
+       0x0000, /* 0x003C, CHIP_SHORT_CTRL */
+       0x0000, /* reserved */
+};
+
+/* default value of dap registers */
+static const u16 sgtl5000_dap_regs[] = {
+       0x0000, /* 0x0100, DAP_CONTROL */
+       0x0000, /* 0x0102, DAP_PEQ */
+       0x0040, /* 0x0104, DAP_BASS_ENHANCE */
+       0x051f, /* 0x0106, DAP_BASS_ENHANCE_CTRL */
+       0x0000, /* 0x0108, DAP_AUDIO_EQ */
+       0x0040, /* 0x010A, DAP_SGTL_SURROUND */
+       0x0000, /* 0x010C, DAP_FILTER_COEF_ACCESS */
+       0x0000, /* 0x010E, DAP_COEF_WR_B0_MSB */
+       0x0000, /* 0x0110, DAP_COEF_WR_B0_LSB */
+       0x0000, /* 0x0112, reserved */
+       0x0000, /* 0x0114, reserved */
+       0x002f, /* 0x0116, DAP_AUDIO_EQ_BASS_BAND0 */
+       0x002f, /* 0x0118, DAP_AUDIO_EQ_BAND0 */
+       0x002f, /* 0x011A, DAP_AUDIO_EQ_BAND2 */
+       0x002f, /* 0x011C, DAP_AUDIO_EQ_BAND3 */
+       0x002f, /* 0x011E, DAP_AUDIO_EQ_TREBLE_BAND4 */
+       0x8000, /* 0x0120, DAP_MAIN_CHAN */
+       0x0000, /* 0x0122, DAP_MIX_CHAN */
+       0x0510, /* 0x0124, DAP_AVC_CTRL */
+       0x1473, /* 0x0126, DAP_AVC_THRESHOLD */
+       0x0028, /* 0x0128, DAP_AVC_ATTACK */
+       0x0050, /* 0x012A, DAP_AVC_DECAY */
+       0x0000, /* 0x012C, DAP_COEF_WR_B1_MSB */
+       0x0000, /* 0x012E, DAP_COEF_WR_B1_LSB */
+       0x0000, /* 0x0130, DAP_COEF_WR_B2_MSB */
+       0x0000, /* 0x0132, DAP_COEF_WR_B2_LSB */
+       0x0000, /* 0x0134, DAP_COEF_WR_A1_MSB */
+       0x0000, /* 0x0136, DAP_COEF_WR_A1_LSB */
+       0x0000, /* 0x0138, DAP_COEF_WR_A2_MSB */
+       0x0000, /* 0x013A, DAP_COEF_WR_A2_LSB */
+};
+
+/* regulator supplies for sgtl5000, VDDD is an optional external supply */
+enum sgtl5000_regulator_supplies {
+       VDDA,
+       VDDIO,
+       VDDD,
+       SGTL5000_SUPPLY_NUM
+};
+
+/* vddd is optional supply */
+static const char *supply_names[SGTL5000_SUPPLY_NUM] = {
+       "VDDA",
+       "VDDIO",
+       "VDDD"
+};
+
+#define LDO_CONSUMER_NAME      "VDDD_LDO"
+#define LDO_VOLTAGE            1200000
+
+static struct regulator_consumer_supply ldo_consumer[] = {
+       REGULATOR_SUPPLY(LDO_CONSUMER_NAME, NULL),
+};
+
+static struct regulator_init_data ldo_init_data = {
+       .constraints = {
+               .min_uV                 = 850000,
+               .max_uV                 = 1600000,
+               .valid_modes_mask       = REGULATOR_MODE_NORMAL,
+               .valid_ops_mask         = REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies = 1,
+       .consumer_supplies = &ldo_consumer[0],
+};
+
+/*
+ * sgtl5000 internal ldo regulator,
+ * enabled when VDDD not provided
+ */
+struct ldo_regulator {
+       struct regulator_desc desc;
+       struct regulator_dev *dev;
+       int voltage;
+       void *codec_data;
+       bool enabled;
+};
+
+/* sgtl5000 private structure in codec */
+struct sgtl5000_priv {
+       int sysclk;     /* sysclk rate */
+       int master;     /* i2s master or not */
+       int fmt;        /* i2s data format */
+       struct regulator_bulk_data supplies[SGTL5000_SUPPLY_NUM];
+       struct ldo_regulator *ldo;
+};
+
+/*
+ * mic_bias power on/off share the same register bits with
+ * output impedance of mic bias, when power on mic bias, we
+ * need reclaim it to impedance value.
+ * 0x0 = Powered off
+ * 0x1 = 2Kohm
+ * 0x2 = 4Kohm
+ * 0x3 = 8Kohm
+ */
+static int mic_bias_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /* change mic bias resistor to 4Kohm */
+               snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL,
+                               SGTL5000_BIAS_R_4k, SGTL5000_BIAS_R_4k);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               /*
+                * SGTL5000_BIAS_R_8k as mask to clean the two bits
+                * of mic bias and output impedance
+                */
+               snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL,
+                               SGTL5000_BIAS_R_8k, 0);
+               break;
+       }
+       return 0;
+}
+
+/*
+ * using codec assist to small pop, hp_powerup or lineout_powerup
+ * should stay setting until vag_powerup is fully ramped down,
+ * vag fully ramped down require 400ms.
+ */
+static int small_pop_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
+                       SGTL5000_VAG_POWERUP, SGTL5000_VAG_POWERUP);
+               break;
+
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(w->codec, SGTL5000_CHIP_ANA_POWER,
+                       SGTL5000_VAG_POWERUP, 0);
+               msleep(400);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+/* input sources for ADC */
+static const char *adc_mux_text[] = {
+       "MIC_IN", "LINE_IN"
+};
+
+static const struct soc_enum adc_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 2, 2, adc_mux_text);
+
+static const struct snd_kcontrol_new adc_mux =
+SOC_DAPM_ENUM("Capture Mux", adc_enum);
+
+/* input sources for DAC */
+static const char *dac_mux_text[] = {
+       "DAC", "LINE_IN"
+};
+
+static const struct soc_enum dac_enum =
+SOC_ENUM_SINGLE(SGTL5000_CHIP_ANA_CTRL, 6, 2, dac_mux_text);
+
+static const struct snd_kcontrol_new dac_mux =
+SOC_DAPM_ENUM("Headphone Mux", dac_enum);
+
+static const struct snd_soc_dapm_widget sgtl5000_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("LINE_IN"),
+       SND_SOC_DAPM_INPUT("MIC_IN"),
+
+       SND_SOC_DAPM_OUTPUT("HP_OUT"),
+       SND_SOC_DAPM_OUTPUT("LINE_OUT"),
+
+       SND_SOC_DAPM_MICBIAS_E("Mic Bias", SGTL5000_CHIP_MIC_CTRL, 8, 0,
+                               mic_bias_event,
+                               SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_PGA_E("HP", SGTL5000_CHIP_ANA_POWER, 4, 0, NULL, 0,
+                       small_pop_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+       SND_SOC_DAPM_PGA_E("LO", SGTL5000_CHIP_ANA_POWER, 0, 0, NULL, 0,
+                       small_pop_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+       SND_SOC_DAPM_MUX("Capture Mux", SND_SOC_NOPM, 0, 0, &adc_mux),
+       SND_SOC_DAPM_MUX("Headphone Mux", SND_SOC_NOPM, 0, 0, &dac_mux),
+
+       /* aif for i2s input */
+       SND_SOC_DAPM_AIF_IN("AIFIN", "Playback",
+                               0, SGTL5000_CHIP_DIG_POWER,
+                               0, 0),
+
+       /* aif for i2s output */
+       SND_SOC_DAPM_AIF_OUT("AIFOUT", "Capture",
+                               0, SGTL5000_CHIP_DIG_POWER,
+                               1, 0),
+
+       SND_SOC_DAPM_ADC("ADC", "Capture", SGTL5000_CHIP_ANA_POWER, 1, 0),
+
+       SND_SOC_DAPM_DAC("DAC", "Playback", SGTL5000_CHIP_ANA_POWER, 3, 0),
+};
+
+/* routes for sgtl5000 */
+static const struct snd_soc_dapm_route audio_map[] = {
+       {"Capture Mux", "LINE_IN", "LINE_IN"},  /* line_in --> adc_mux */
+       {"Capture Mux", "MIC_IN", "MIC_IN"},    /* mic_in --> adc_mux */
+
+       {"ADC", NULL, "Capture Mux"},           /* adc_mux --> adc */
+       {"AIFOUT", NULL, "ADC"},                /* adc --> i2s_out */
+
+       {"DAC", NULL, "AIFIN"},                 /* i2s-->dac,skip audio mux */
+       {"Headphone Mux", "DAC", "DAC"},        /* dac --> hp_mux */
+       {"LO", NULL, "DAC"},                    /* dac --> line_out */
+
+       {"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */
+       {"HP", NULL, "Headphone Mux"},          /* hp_mux --> hp */
+
+       {"LINE_OUT", NULL, "LO"},
+       {"HP_OUT", NULL, "HP"},
+};
+
+/* custom function to fetch info of PCM playback volume */
+static int dac_info_volsw(struct snd_kcontrol *kcontrol,
+                         struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 2;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 0xfc - 0x3c;
+       return 0;
+}
+
+/*
+ * custom function to get of PCM playback volume
+ *
+ * dac volume register
+ * 15-------------8-7--------------0
+ * | R channel vol | L channel vol |
+ *  -------------------------------
+ *
+ * PCM volume with 0.5017 dB steps from 0 to -90 dB
+ *
+ * register values map to dB
+ * 0x3B and less = Reserved
+ * 0x3C = 0 dB
+ * 0x3D = -0.5 dB
+ * 0xF0 = -90 dB
+ * 0xFC and greater = Muted
+ *
+ * register value map to userspace value
+ *
+ * register value      0x3c(0dB)         0xf0(-90dB)0xfc
+ *                     ------------------------------
+ * userspace value     0xc0                         0
+ */
+static int dac_get_volsw(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       int reg;
+       int l;
+       int r;
+
+       reg = snd_soc_read(codec, SGTL5000_CHIP_DAC_VOL);
+
+       /* get left channel volume */
+       l = (reg & SGTL5000_DAC_VOL_LEFT_MASK) >> SGTL5000_DAC_VOL_LEFT_SHIFT;
+
+       /* get right channel volume */
+       r = (reg & SGTL5000_DAC_VOL_RIGHT_MASK) >> SGTL5000_DAC_VOL_RIGHT_SHIFT;
+
+       /* make sure value fall in (0x3c,0xfc) */
+       l = clamp(l, 0x3c, 0xfc);
+       r = clamp(r, 0x3c, 0xfc);
+
+       /* invert it and map to userspace value */
+       l = 0xfc - l;
+       r = 0xfc - r;
+
+       ucontrol->value.integer.value[0] = l;
+       ucontrol->value.integer.value[1] = r;
+
+       return 0;
+}
+
+/*
+ * custom function to put of PCM playback volume
+ *
+ * dac volume register
+ * 15-------------8-7--------------0
+ * | R channel vol | L channel vol |
+ *  -------------------------------
+ *
+ * PCM volume with 0.5017 dB steps from 0 to -90 dB
+ *
+ * register values map to dB
+ * 0x3B and less = Reserved
+ * 0x3C = 0 dB
+ * 0x3D = -0.5 dB
+ * 0xF0 = -90 dB
+ * 0xFC and greater = Muted
+ *
+ * userspace value map to register value
+ *
+ * userspace value     0xc0                         0
+ *                     ------------------------------
+ * register value      0x3c(0dB)       0xf0(-90dB)0xfc
+ */
+static int dac_put_volsw(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       int reg;
+       int l;
+       int r;
+
+       l = ucontrol->value.integer.value[0];
+       r = ucontrol->value.integer.value[1];
+
+       /* make sure userspace volume fall in (0, 0xfc-0x3c) */
+       l = clamp(l, 0, 0xfc - 0x3c);
+       r = clamp(r, 0, 0xfc - 0x3c);
+
+       /* invert it, get the value can be set to register */
+       l = 0xfc - l;
+       r = 0xfc - r;
+
+       /* shift to get the register value */
+       reg = l << SGTL5000_DAC_VOL_LEFT_SHIFT |
+               r << SGTL5000_DAC_VOL_RIGHT_SHIFT;
+
+       snd_soc_write(codec, SGTL5000_CHIP_DAC_VOL, reg);
+
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(capture_6db_attenuate, -600, 600, 0);
+
+/* tlv for mic gain, 0db 20db 30db 40db */
+static const unsigned int mic_gain_tlv[] = {
+       TLV_DB_RANGE_HEAD(4),
+       0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+       1, 3, TLV_DB_SCALE_ITEM(2000, 1000, 0),
+};
+
+/* tlv for hp volume, -51.5db to 12.0db, step .5db */
+static const DECLARE_TLV_DB_SCALE(headphone_volume, -5150, 50, 0);
+
+static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
+       /* SOC_DOUBLE_S8_TLV with invert */
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "PCM Playback Volume",
+               .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+                       SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info = dac_info_volsw,
+               .get = dac_get_volsw,
+               .put = dac_put_volsw,
+       },
+
+       SOC_DOUBLE("Capture Volume", SGTL5000_CHIP_ANA_ADC_CTRL, 0, 4, 0xf, 0),
+       SOC_SINGLE_TLV("Capture Attenuate Switch (-6dB)",
+                       SGTL5000_CHIP_ANA_ADC_CTRL,
+                       8, 2, 0, capture_6db_attenuate),
+       SOC_SINGLE("Capture ZC Switch", SGTL5000_CHIP_ANA_CTRL, 1, 1, 0),
+
+       SOC_DOUBLE_TLV("Headphone Playback Volume",
+                       SGTL5000_CHIP_ANA_HP_CTRL,
+                       0, 8,
+                       0x7f, 1,
+                       headphone_volume),
+       SOC_SINGLE("Headphone Playback ZC Switch", SGTL5000_CHIP_ANA_CTRL,
+                       5, 1, 0),
+
+       SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL,
+                       0, 4, 0, mic_gain_tlv),
+};
+
+/* mute the codec used by alsa core */
+static int sgtl5000_digital_mute(struct snd_soc_dai *codec_dai, int mute)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u16 adcdac_ctrl = SGTL5000_DAC_MUTE_LEFT | SGTL5000_DAC_MUTE_RIGHT;
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ADCDAC_CTRL,
+                       adcdac_ctrl, mute ? adcdac_ctrl : 0);
+
+       return 0;
+}
+
+/* set codec format */
+static int sgtl5000_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+       u16 i2sctl = 0;
+
+       sgtl5000->master = 0;
+       /*
+        * i2s clock and frame master setting.
+        * ONLY support:
+        *  - clock and frame slave,
+        *  - clock and frame master
+        */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       case SND_SOC_DAIFMT_CBM_CFM:
+               i2sctl |= SGTL5000_I2S_MASTER;
+               sgtl5000->master = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* setting i2s data format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_DSP_A:
+               i2sctl |= SGTL5000_I2S_MODE_PCM;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               i2sctl |= SGTL5000_I2S_MODE_PCM;
+               i2sctl |= SGTL5000_I2S_LRALIGN;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               i2sctl |= SGTL5000_I2S_MODE_I2S_LJ;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               i2sctl |= SGTL5000_I2S_MODE_RJ;
+               i2sctl |= SGTL5000_I2S_LRPOL;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               i2sctl |= SGTL5000_I2S_MODE_I2S_LJ;
+               i2sctl |= SGTL5000_I2S_LRALIGN;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       sgtl5000->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
+       /* Clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               i2sctl |= SGTL5000_I2S_SCLK_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_write(codec, SGTL5000_CHIP_I2S_CTRL, i2sctl);
+
+       return 0;
+}
+
+/* set codec sysclk */
+static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                  int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       switch (clk_id) {
+       case SGTL5000_SYSCLK:
+               sgtl5000->sysclk = freq;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * set clock according to i2s frame clock,
+ * sgtl5000 provide 2 clock sources.
+ * 1. sys_mclk. sample freq can only configure to
+ *     1/256, 1/384, 1/512 of sys_mclk.
+ * 2. pll. can derive any audio clocks.
+ *
+ * clock setting rules:
+ * 1. in slave mode, only sys_mclk can use.
+ * 2. as constraint by sys_mclk, sample freq should
+ *     set to 32k, 44.1k and above.
+ * 3. using sys_mclk prefer to pll to save power.
+ */
+static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
+{
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+       int clk_ctl = 0;
+       int sys_fs;     /* sample freq */
+
+       /*
+        * sample freq should be divided by frame clock,
+        * if frame clock lower than 44.1khz, sample feq should set to
+        * 32khz or 44.1khz.
+        */
+       switch (frame_rate) {
+       case 8000:
+       case 16000:
+               sys_fs = 32000;
+               break;
+       case 11025:
+       case 22050:
+               sys_fs = 44100;
+               break;
+       default:
+               sys_fs = frame_rate;
+               break;
+       }
+
+       /* set divided factor of frame clock */
+       switch (sys_fs / frame_rate) {
+       case 4:
+               clk_ctl |= SGTL5000_RATE_MODE_DIV_4 << SGTL5000_RATE_MODE_SHIFT;
+               break;
+       case 2:
+               clk_ctl |= SGTL5000_RATE_MODE_DIV_2 << SGTL5000_RATE_MODE_SHIFT;
+               break;
+       case 1:
+               clk_ctl |= SGTL5000_RATE_MODE_DIV_1 << SGTL5000_RATE_MODE_SHIFT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set the sys_fs according to frame rate */
+       switch (sys_fs) {
+       case 32000:
+               clk_ctl |= SGTL5000_SYS_FS_32k << SGTL5000_SYS_FS_SHIFT;
+               break;
+       case 44100:
+               clk_ctl |= SGTL5000_SYS_FS_44_1k << SGTL5000_SYS_FS_SHIFT;
+               break;
+       case 48000:
+               clk_ctl |= SGTL5000_SYS_FS_48k << SGTL5000_SYS_FS_SHIFT;
+               break;
+       case 96000:
+               clk_ctl |= SGTL5000_SYS_FS_96k << SGTL5000_SYS_FS_SHIFT;
+               break;
+       default:
+               dev_err(codec->dev, "frame rate %d not supported\n",
+                       frame_rate);
+               return -EINVAL;
+       }
+
+       /*
+        * calculate the divider of mclk/sample_freq,
+        * factor of freq =96k can only be 256, since mclk in range (12m,27m)
+        */
+       switch (sgtl5000->sysclk / sys_fs) {
+       case 256:
+               clk_ctl |= SGTL5000_MCLK_FREQ_256FS <<
+                       SGTL5000_MCLK_FREQ_SHIFT;
+               break;
+       case 384:
+               clk_ctl |= SGTL5000_MCLK_FREQ_384FS <<
+                       SGTL5000_MCLK_FREQ_SHIFT;
+               break;
+       case 512:
+               clk_ctl |= SGTL5000_MCLK_FREQ_512FS <<
+                       SGTL5000_MCLK_FREQ_SHIFT;
+               break;
+       default:
+               /* if mclk not satisify the divider, use pll */
+               if (sgtl5000->master) {
+                       clk_ctl |= SGTL5000_MCLK_FREQ_PLL <<
+                               SGTL5000_MCLK_FREQ_SHIFT;
+               } else {
+                       dev_err(codec->dev,
+                               "PLL not supported in slave mode\n");
+                       return -EINVAL;
+               }
+       }
+
+       /* if using pll, please check manual 6.4.2 for detail */
+       if ((clk_ctl & SGTL5000_MCLK_FREQ_MASK) == SGTL5000_MCLK_FREQ_PLL) {
+               u64 out, t;
+               int div2;
+               int pll_ctl;
+               unsigned int in, int_div, frac_div;
+
+               if (sgtl5000->sysclk > 17000000) {
+                       div2 = 1;
+                       in = sgtl5000->sysclk / 2;
+               } else {
+                       div2 = 0;
+                       in = sgtl5000->sysclk;
+               }
+               if (sys_fs == 44100)
+                       out = 180633600;
+               else
+                       out = 196608000;
+               t = do_div(out, in);
+               int_div = out;
+               t *= 2048;
+               do_div(t, in);
+               frac_div = t;
+               pll_ctl = int_div << SGTL5000_PLL_INT_DIV_SHIFT |
+                   frac_div << SGTL5000_PLL_FRAC_DIV_SHIFT;
+
+               snd_soc_write(codec, SGTL5000_CHIP_PLL_CTRL, pll_ctl);
+               if (div2)
+                       snd_soc_update_bits(codec,
+                               SGTL5000_CHIP_CLK_TOP_CTRL,
+                               SGTL5000_INPUT_FREQ_DIV2,
+                               SGTL5000_INPUT_FREQ_DIV2);
+               else
+                       snd_soc_update_bits(codec,
+                               SGTL5000_CHIP_CLK_TOP_CTRL,
+                               SGTL5000_INPUT_FREQ_DIV2,
+                               0);
+
+               /* power up pll */
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                       SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
+                       SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP);
+       } else {
+               /* power down pll */
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                       SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
+                       0);
+       }
+
+       /* if using pll, clk_ctrl must be set after pll power up */
+       snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+
+       return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ * input: params_rate, params_fmt
+ */
+static int sgtl5000_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_codec *codec = rtd->codec;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+       int channels = params_channels(params);
+       int i2s_ctl = 0;
+       int stereo;
+       int ret;
+
+       /* sysclk should already set */
+       if (!sgtl5000->sysclk) {
+               dev_err(codec->dev, "%s: set sysclk first!\n", __func__);
+               return -EFAULT;
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               stereo = SGTL5000_DAC_STEREO;
+       else
+               stereo = SGTL5000_ADC_STEREO;
+
+       /* set mono to save power */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER, stereo,
+                       channels == 1 ? 0 : stereo);
+
+       /* set codec clock base on lrclk */
+       ret = sgtl5000_set_clock(codec, params_rate(params));
+       if (ret)
+               return ret;
+
+       /* set i2s data format */
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
+                       return -EINVAL;
+               i2s_ctl |= SGTL5000_I2S_DLEN_16 << SGTL5000_I2S_DLEN_SHIFT;
+               i2s_ctl |= SGTL5000_I2S_SCLKFREQ_32FS <<
+                   SGTL5000_I2S_SCLKFREQ_SHIFT;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               i2s_ctl |= SGTL5000_I2S_DLEN_20 << SGTL5000_I2S_DLEN_SHIFT;
+               i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+                   SGTL5000_I2S_SCLKFREQ_SHIFT;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               i2s_ctl |= SGTL5000_I2S_DLEN_24 << SGTL5000_I2S_DLEN_SHIFT;
+               i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+                   SGTL5000_I2S_SCLKFREQ_SHIFT;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               if (sgtl5000->fmt == SND_SOC_DAIFMT_RIGHT_J)
+                       return -EINVAL;
+               i2s_ctl |= SGTL5000_I2S_DLEN_32 << SGTL5000_I2S_DLEN_SHIFT;
+               i2s_ctl |= SGTL5000_I2S_SCLKFREQ_64FS <<
+                   SGTL5000_I2S_SCLKFREQ_SHIFT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_I2S_CTRL, i2s_ctl, i2s_ctl);
+
+       return 0;
+}
+
+static int ldo_regulator_is_enabled(struct regulator_dev *dev)
+{
+       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
+
+       return ldo->enabled;
+}
+
+static int ldo_regulator_enable(struct regulator_dev *dev)
+{
+       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
+       struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
+       int reg;
+
+       if (ldo_regulator_is_enabled(dev))
+               return 0;
+
+       /* set regulator value firstly */
+       reg = (1600 - ldo->voltage / 1000) / 50;
+       reg = clamp(reg, 0x0, 0xf);
+
+       /* amend the voltage value, unit: uV */
+       ldo->voltage = (1600 - reg * 50) * 1000;
+
+       /* set voltage to register */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
+                               (0x1 << 4) - 1, reg);
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINEREG_D_POWERUP,
+                               SGTL5000_LINEREG_D_POWERUP);
+
+       /* when internal ldo enabled, simple digital power can be disabled */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINREG_SIMPLE_POWERUP,
+                               0);
+
+       ldo->enabled = 1;
+       return 0;
+}
+
+static int ldo_regulator_disable(struct regulator_dev *dev)
+{
+       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
+       struct snd_soc_codec *codec = (struct snd_soc_codec *)ldo->codec_data;
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINEREG_D_POWERUP,
+                               0);
+
+       /* clear voltage info */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
+                               (0x1 << 4) - 1, 0);
+
+       ldo->enabled = 0;
+
+       return 0;
+}
+
+static int ldo_regulator_get_voltage(struct regulator_dev *dev)
+{
+       struct ldo_regulator *ldo = rdev_get_drvdata(dev);
+
+       return ldo->voltage;
+}
+
+static struct regulator_ops ldo_regulator_ops = {
+       .is_enabled = ldo_regulator_is_enabled,
+       .enable = ldo_regulator_enable,
+       .disable = ldo_regulator_disable,
+       .get_voltage = ldo_regulator_get_voltage,
+};
+
+static int ldo_regulator_register(struct snd_soc_codec *codec,
+                               struct regulator_init_data *init_data,
+                               int voltage)
+{
+       struct ldo_regulator *ldo;
+
+       ldo = kzalloc(sizeof(struct ldo_regulator), GFP_KERNEL);
+
+       if (!ldo) {
+               dev_err(codec->dev, "failed to allocate ldo_regulator\n");
+               return -ENOMEM;
+       }
+
+       ldo->desc.name = kstrdup(dev_name(codec->dev), GFP_KERNEL);
+       if (!ldo->desc.name) {
+               kfree(ldo);
+               dev_err(codec->dev, "failed to allocate decs name memory\n");
+               return -ENOMEM;
+       }
+
+       ldo->desc.type  = REGULATOR_VOLTAGE;
+       ldo->desc.owner = THIS_MODULE;
+       ldo->desc.ops   = &ldo_regulator_ops;
+       ldo->desc.n_voltages = 1;
+
+       ldo->codec_data = codec;
+       ldo->voltage = voltage;
+
+       ldo->dev = regulator_register(&ldo->desc, codec->dev,
+                                         init_data, ldo);
+       if (IS_ERR(ldo->dev)) {
+               int ret = PTR_ERR(ldo->dev);
+
+               dev_err(codec->dev, "failed to register regulator\n");
+               kfree(ldo->desc.name);
+               kfree(ldo);
+
+               return ret;
+       }
+
+       return 0;
+}
+
+static int ldo_regulator_remove(struct snd_soc_codec *codec)
+{
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+       struct ldo_regulator *ldo = sgtl5000->ldo;
+
+       if (!ldo)
+               return 0;
+
+       regulator_unregister(ldo->dev);
+       kfree(ldo->desc.name);
+       kfree(ldo);
+
+       return 0;
+}
+
+/*
+ * set dac bias
+ * common state changes:
+ * startup:
+ * off --> standby --> prepare --> on
+ * standby --> prepare --> on
+ *
+ * stop:
+ * on --> prepare --> standby
+ */
+static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
+                                  enum snd_soc_bias_level level)
+{
+       int ret;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       ret = regulator_bulk_enable(
+                                               ARRAY_SIZE(sgtl5000->supplies),
+                                               sgtl5000->supplies);
+                       if (ret)
+                               return ret;
+                       udelay(10);
+               }
+
+               break;
+       case SND_SOC_BIAS_OFF:
+               regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+                                       sgtl5000->supplies);
+               break;
+       }
+
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+#define SGTL5000_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+                       SNDRV_PCM_FMTBIT_S20_3LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE |\
+                       SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops sgtl5000_ops = {
+       .hw_params = sgtl5000_pcm_hw_params,
+       .digital_mute = sgtl5000_digital_mute,
+       .set_fmt = sgtl5000_set_dai_fmt,
+       .set_sysclk = sgtl5000_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver sgtl5000_dai = {
+       .name = "sgtl5000",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               /*
+                * only support 8~48K + 96K,
+                * TODO modify hw_param to support more
+                */
+               .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000,
+               .formats = SGTL5000_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_96000,
+               .formats = SGTL5000_FORMATS,
+       },
+       .ops = &sgtl5000_ops,
+       .symmetric_rates = 1,
+};
+
+static int sgtl5000_volatile_register(struct snd_soc_codec *codec,
+                                       unsigned int reg)
+{
+       switch (reg) {
+       case SGTL5000_CHIP_ID:
+       case SGTL5000_CHIP_ADCDAC_CTRL:
+       case SGTL5000_CHIP_ANA_STATUS:
+               return 1;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_SUSPEND
+static int sgtl5000_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+/*
+ * restore all sgtl5000 registers,
+ * since a big hole between dap and regular registers,
+ * we will restore them respectively.
+ */
+static int sgtl5000_restore_regs(struct snd_soc_codec *codec)
+{
+       u16 *cache = codec->reg_cache;
+       int i;
+       int regular_regs = SGTL5000_CHIP_SHORT_CTRL >> 1;
+
+       /* restore regular registers */
+       for (i = 0; i < regular_regs; i++) {
+               int reg = i << 1;
+
+               /* this regs depends on the others */
+               if (reg == SGTL5000_CHIP_ANA_POWER ||
+                       reg == SGTL5000_CHIP_CLK_CTRL ||
+                       reg == SGTL5000_CHIP_LINREG_CTRL ||
+                       reg == SGTL5000_CHIP_LINE_OUT_CTRL ||
+                       reg == SGTL5000_CHIP_CLK_CTRL)
+                       continue;
+
+               snd_soc_write(codec, reg, cache[i]);
+       }
+
+       /* restore dap registers */
+       for (i = SGTL5000_DAP_REG_OFFSET >> 1;
+                       i < SGTL5000_MAX_REG_OFFSET >> 1; i++) {
+               int reg = i << 1;
+
+               snd_soc_write(codec, reg, cache[i]);
+       }
+
+       /*
+        * restore power and other regs according
+        * to set_power() and set_clock()
+        */
+       snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL,
+                       cache[SGTL5000_CHIP_LINREG_CTRL >> 1]);
+
+       snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER,
+                       cache[SGTL5000_CHIP_ANA_POWER >> 1]);
+
+       snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL,
+                       cache[SGTL5000_CHIP_CLK_CTRL >> 1]);
+
+       snd_soc_write(codec, SGTL5000_CHIP_REF_CTRL,
+                       cache[SGTL5000_CHIP_REF_CTRL >> 1]);
+
+       snd_soc_write(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
+                       cache[SGTL5000_CHIP_LINE_OUT_CTRL >> 1]);
+       return 0;
+}
+
+static int sgtl5000_resume(struct snd_soc_codec *codec)
+{
+       /* Bring the codec back up to standby to enable regulators */
+       sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       /* Restore registers by cached in memory */
+       sgtl5000_restore_regs(codec);
+       return 0;
+}
+#else
+#define sgtl5000_suspend NULL
+#define sgtl5000_resume  NULL
+#endif /* CONFIG_SUSPEND */
+
+/*
+ * sgtl5000 has 3 internal power supplies:
+ * 1. VAG, normally set to vdda/2
+ * 2. chargepump, set to different value
+ *     according to voltage of vdda and vddio
+ * 3. line out VAG, normally set to vddio/2
+ *
+ * and should be set according to:
+ * 1. vddd provided by external or not
+ * 2. vdda and vddio voltage value. > 3.1v or not
+ * 3. chip revision >=0x11 or not. If >=0x11, not use external vddd.
+ */
+static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
+{
+       int vddd;
+       int vdda;
+       int vddio;
+       u16 ana_pwr;
+       u16 lreg_ctrl;
+       int vag;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       vdda  = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer);
+       vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer);
+       vddd  = regulator_get_voltage(sgtl5000->supplies[VDDD].consumer);
+
+       vdda  = vdda / 1000;
+       vddio = vddio / 1000;
+       vddd  = vddd / 1000;
+
+       if (vdda <= 0 || vddio <= 0 || vddd < 0) {
+               dev_err(codec->dev, "regulator voltage not set correctly\n");
+
+               return -EINVAL;
+       }
+
+       /* according to datasheet, maximum voltage of supplies */
+       if (vdda > 3600 || vddio > 3600 || vddd > 1980) {
+               dev_err(codec->dev,
+                       "exceed max voltage vdda %dmv vddio %dma vddd %dma\n",
+                       vdda, vddio, vddd);
+
+               return -EINVAL;
+       }
+
+       /* reset value */
+       ana_pwr = snd_soc_read(codec, SGTL5000_CHIP_ANA_POWER);
+       ana_pwr |= SGTL5000_DAC_STEREO |
+                       SGTL5000_ADC_STEREO |
+                       SGTL5000_REFTOP_POWERUP;
+       lreg_ctrl = snd_soc_read(codec, SGTL5000_CHIP_LINREG_CTRL);
+
+       if (vddio < 3100 && vdda < 3100) {
+               /* enable internal oscillator used for charge pump */
+               snd_soc_update_bits(codec, SGTL5000_CHIP_CLK_TOP_CTRL,
+                                       SGTL5000_INT_OSC_EN,
+                                       SGTL5000_INT_OSC_EN);
+               /* Enable VDDC charge pump */
+               ana_pwr |= SGTL5000_VDDC_CHRGPMP_POWERUP;
+       } else if (vddio >= 3100 && vdda >= 3100) {
+               /*
+                * if vddio and vddd > 3.1v,
+                * charge pump should be clean before set ana_pwr
+                */
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_VDDC_CHRGPMP_POWERUP, 0);
+
+               /* VDDC use VDDIO rail */
+               lreg_ctrl |= SGTL5000_VDDC_ASSN_OVRD;
+               lreg_ctrl |= SGTL5000_VDDC_MAN_ASSN_VDDIO <<
+                           SGTL5000_VDDC_MAN_ASSN_SHIFT;
+       }
+
+       snd_soc_write(codec, SGTL5000_CHIP_LINREG_CTRL, lreg_ctrl);
+
+       snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
+
+       /* set voltage to register */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
+                               (0x1 << 4) - 1, 0x8);
+
+       /*
+        * if vddd linear reg has been enabled,
+        * simple digital supply should be clear to get
+        * proper VDDD voltage.
+        */
+       if (ana_pwr & SGTL5000_LINEREG_D_POWERUP)
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINREG_SIMPLE_POWERUP,
+                               0);
+       else
+               snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+                               SGTL5000_LINREG_SIMPLE_POWERUP |
+                               SGTL5000_STARTUP_POWERUP,
+                               0);
+
+       /*
+        * set ADC/DAC VAG to vdda / 2,
+        * should stay in range (0.8v, 1.575v)
+        */
+       vag = vdda / 2;
+       if (vag <= SGTL5000_ANA_GND_BASE)
+               vag = 0;
+       else if (vag >= SGTL5000_ANA_GND_BASE + SGTL5000_ANA_GND_STP *
+                (SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT))
+               vag = SGTL5000_ANA_GND_MASK >> SGTL5000_ANA_GND_SHIFT;
+       else
+               vag = (vag - SGTL5000_ANA_GND_BASE) / SGTL5000_ANA_GND_STP;
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
+                       vag << SGTL5000_ANA_GND_SHIFT,
+                       vag << SGTL5000_ANA_GND_SHIFT);
+
+       /* set line out VAG to vddio / 2, in range (0.8v, 1.675v) */
+       vag = vddio / 2;
+       if (vag <= SGTL5000_LINE_OUT_GND_BASE)
+               vag = 0;
+       else if (vag >= SGTL5000_LINE_OUT_GND_BASE +
+               SGTL5000_LINE_OUT_GND_STP * SGTL5000_LINE_OUT_GND_MAX)
+               vag = SGTL5000_LINE_OUT_GND_MAX;
+       else
+               vag = (vag - SGTL5000_LINE_OUT_GND_BASE) /
+                   SGTL5000_LINE_OUT_GND_STP;
+
+       snd_soc_update_bits(codec, SGTL5000_CHIP_LINE_OUT_CTRL,
+                       vag << SGTL5000_LINE_OUT_GND_SHIFT |
+                       SGTL5000_LINE_OUT_CURRENT_360u <<
+                               SGTL5000_LINE_OUT_CURRENT_SHIFT,
+                       vag << SGTL5000_LINE_OUT_GND_SHIFT |
+                       SGTL5000_LINE_OUT_CURRENT_360u <<
+                               SGTL5000_LINE_OUT_CURRENT_SHIFT);
+
+       return 0;
+}
+
+static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
+{
+       u16 reg;
+       int ret;
+       int rev;
+       int i;
+       int external_vddd = 0;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       for (i = 0; i < ARRAY_SIZE(sgtl5000->supplies); i++)
+               sgtl5000->supplies[i].supply = supply_names[i];
+
+       ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+       if (!ret)
+               external_vddd = 1;
+       else {
+               /* set internal ldo to 1.2v */
+               int voltage = LDO_VOLTAGE;
+
+               ret = ldo_regulator_register(codec, &ldo_init_data, voltage);
+               if (ret) {
+                       dev_err(codec->dev,
+                       "Failed to register vddd internal supplies: %d\n",
+                               ret);
+                       return ret;
+               }
+
+               sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
+
+               ret = regulator_bulk_get(codec->dev,
+                               ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+
+               if (ret) {
+                       ldo_regulator_remove(codec);
+                       dev_err(codec->dev,
+                               "Failed to request supplies: %d\n", ret);
+
+                       return ret;
+               }
+       }
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
+                                       sgtl5000->supplies);
+       if (ret)
+               goto err_regulator_free;
+
+       /* wait for all power rails bring up */
+       udelay(10);
+
+       /* read chip information */
+       reg = snd_soc_read(codec, SGTL5000_CHIP_ID);
+       if (((reg & SGTL5000_PARTID_MASK) >> SGTL5000_PARTID_SHIFT) !=
+           SGTL5000_PARTID_PART_ID) {
+               dev_err(codec->dev,
+                       "Device with ID register %x is not a sgtl5000\n", reg);
+               ret = -ENODEV;
+               goto err_regulator_disable;
+       }
+
+       rev = (reg & SGTL5000_REVID_MASK) >> SGTL5000_REVID_SHIFT;
+       dev_info(codec->dev, "sgtl5000 revision %d\n", rev);
+
+       /*
+        * workaround for revision 0x11 and later,
+        * roll back to use internal LDO
+        */
+       if (external_vddd && rev >= 0x11) {
+               int voltage = LDO_VOLTAGE;
+               /* disable all regulator first */
+               regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+                                       sgtl5000->supplies);
+               /* free VDDD regulator */
+               regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+                                       sgtl5000->supplies);
+
+               ret = ldo_regulator_register(codec, &ldo_init_data, voltage);
+               if (ret)
+                       return ret;
+
+               sgtl5000->supplies[VDDD].supply = LDO_CONSUMER_NAME;
+
+               ret = regulator_bulk_get(codec->dev,
+                               ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+               if (ret) {
+                       ldo_regulator_remove(codec);
+                       dev_err(codec->dev,
+                               "Failed to request supplies: %d\n", ret);
+
+                       return ret;
+               }
+
+               ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
+                                               sgtl5000->supplies);
+               if (ret)
+                       goto err_regulator_free;
+
+               /* wait for all power rails bring up */
+               udelay(10);
+       }
+
+       return 0;
+
+err_regulator_disable:
+       regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+err_regulator_free:
+       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+       if (external_vddd)
+               ldo_regulator_remove(codec);
+       return ret;
+
+}
+
+static int sgtl5000_probe(struct snd_soc_codec *codec)
+{
+       int ret;
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       /* setup i2c data ops */
+       ret = snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_I2C);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+               return ret;
+       }
+
+       ret = sgtl5000_enable_regulators(codec);
+       if (ret)
+               return ret;
+
+       /* power up sgtl5000 */
+       ret = sgtl5000_set_power_regs(codec);
+       if (ret)
+               goto err;
+
+       /* enable small pop, introduce 400ms delay in turning off */
+       snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
+                               SGTL5000_SMALL_POP,
+                               SGTL5000_SMALL_POP);
+
+       /* disable short cut detector */
+       snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0);
+
+       /*
+        * set i2s as default input of sound switch
+        * TODO: add sound switch to control and dapm widge.
+        */
+       snd_soc_write(codec, SGTL5000_CHIP_SSS_CTRL,
+                       SGTL5000_DAC_SEL_I2S_IN << SGTL5000_DAC_SEL_SHIFT);
+       snd_soc_write(codec, SGTL5000_CHIP_DIG_POWER,
+                       SGTL5000_ADC_EN | SGTL5000_DAC_EN);
+
+       /* enable dac volume ramp by default */
+       snd_soc_write(codec, SGTL5000_CHIP_ADCDAC_CTRL,
+                       SGTL5000_DAC_VOL_RAMP_EN |
+                       SGTL5000_DAC_MUTE_RIGHT |
+                       SGTL5000_DAC_MUTE_LEFT);
+
+       snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f);
+
+       snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL,
+                       SGTL5000_HP_ZCD_EN |
+                       SGTL5000_ADC_ZCD_EN);
+
+       snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 0);
+
+       /*
+        * disable DAP
+        * TODO:
+        * Enable DAP in kcontrol and dapm.
+        */
+       snd_soc_write(codec, SGTL5000_DAP_CTRL, 0);
+
+       /* leading to standby state */
+       ret = sgtl5000_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       if (ret)
+               goto err;
+
+       snd_soc_add_controls(codec, sgtl5000_snd_controls,
+                            ARRAY_SIZE(sgtl5000_snd_controls));
+
+       snd_soc_dapm_new_controls(&codec->dapm, sgtl5000_dapm_widgets,
+                                 ARRAY_SIZE(sgtl5000_dapm_widgets));
+
+       snd_soc_dapm_add_routes(&codec->dapm, audio_map,
+                               ARRAY_SIZE(audio_map));
+
+       snd_soc_dapm_new_widgets(&codec->dapm);
+
+       return 0;
+
+err:
+       regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+                                               sgtl5000->supplies);
+       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+       ldo_regulator_remove(codec);
+
+       return ret;
+}
+
+static int sgtl5000_remove(struct snd_soc_codec *codec)
+{
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
+
+       sgtl5000_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
+                                               sgtl5000->supplies);
+       regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+                               sgtl5000->supplies);
+       ldo_regulator_remove(codec);
+
+       return 0;
+}
+
+static struct snd_soc_codec_driver sgtl5000_driver = {
+       .probe = sgtl5000_probe,
+       .remove = sgtl5000_remove,
+       .suspend = sgtl5000_suspend,
+       .resume = sgtl5000_resume,
+       .set_bias_level = sgtl5000_set_bias_level,
+       .reg_cache_size = ARRAY_SIZE(sgtl5000_regs),
+       .reg_word_size = sizeof(u16),
+       .reg_cache_step = 2,
+       .reg_cache_default = sgtl5000_regs,
+       .volatile_register = sgtl5000_volatile_register,
+};
+
+static __devinit int sgtl5000_i2c_probe(struct i2c_client *client,
+                                       const struct i2c_device_id *id)
+{
+       struct sgtl5000_priv *sgtl5000;
+       int ret;
+
+       sgtl5000 = kzalloc(sizeof(struct sgtl5000_priv), GFP_KERNEL);
+       if (!sgtl5000)
+               return -ENOMEM;
+
+       /*
+        * copy DAP default values to default value array.
+        * sgtl5000 register space has a big hole, merge it
+        * at init phase makes life easy.
+        * FIXME: should we drop 'const' of sgtl5000_regs?
+        */
+       memcpy((void *)(&sgtl5000_regs[0] + (SGTL5000_DAP_REG_OFFSET >> 1)),
+                       sgtl5000_dap_regs,
+                       SGTL5000_MAX_REG_OFFSET - SGTL5000_DAP_REG_OFFSET);
+
+       i2c_set_clientdata(client, sgtl5000);
+
+       ret = snd_soc_register_codec(&client->dev,
+                       &sgtl5000_driver, &sgtl5000_dai, 1);
+       if (ret) {
+               dev_err(&client->dev, "Failed to register codec: %d\n", ret);
+               kfree(sgtl5000);
+               return ret;
+       }
+
+       return 0;
+}
+
+static __devexit int sgtl5000_i2c_remove(struct i2c_client *client)
+{
+       struct sgtl5000_priv *sgtl5000 = i2c_get_clientdata(client);
+
+       snd_soc_unregister_codec(&client->dev);
+
+       kfree(sgtl5000);
+       return 0;
+}
+
+static const struct i2c_device_id sgtl5000_id[] = {
+       {"sgtl5000", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, sgtl5000_id);
+
+static struct i2c_driver sgtl5000_i2c_driver = {
+       .driver = {
+                  .name = "sgtl5000",
+                  .owner = THIS_MODULE,
+                  },
+       .probe = sgtl5000_i2c_probe,
+       .remove = __devexit_p(sgtl5000_i2c_remove),
+       .id_table = sgtl5000_id,
+};
+
+static int __init sgtl5000_modinit(void)
+{
+       return i2c_add_driver(&sgtl5000_i2c_driver);
+}
+module_init(sgtl5000_modinit);
+
+static void __exit sgtl5000_exit(void)
+{
+       i2c_del_driver(&sgtl5000_i2c_driver);
+}
+module_exit(sgtl5000_exit);
+
+MODULE_DESCRIPTION("Freescale SGTL5000 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Zeng Zhaoming <zhaoming.zeng@freescale.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
new file mode 100644 (file)
index 0000000..eec3ab3
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * sgtl5000.h - SGTL5000 audio codec interface
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _SGTL5000_H
+#define _SGTL5000_H
+
+/*
+ * Register values.
+ */
+#define SGTL5000_CHIP_ID                       0x0000
+#define SGTL5000_CHIP_DIG_POWER                        0x0002
+#define SGTL5000_CHIP_CLK_CTRL                 0x0004
+#define SGTL5000_CHIP_I2S_CTRL                 0x0006
+#define SGTL5000_CHIP_SSS_CTRL                 0x000a
+#define SGTL5000_CHIP_ADCDAC_CTRL              0x000e
+#define SGTL5000_CHIP_DAC_VOL                  0x0010
+#define SGTL5000_CHIP_PAD_STRENGTH             0x0014
+#define SGTL5000_CHIP_ANA_ADC_CTRL             0x0020
+#define SGTL5000_CHIP_ANA_HP_CTRL              0x0022
+#define SGTL5000_CHIP_ANA_CTRL                 0x0024
+#define SGTL5000_CHIP_LINREG_CTRL              0x0026
+#define SGTL5000_CHIP_REF_CTRL                 0x0028
+#define SGTL5000_CHIP_MIC_CTRL                 0x002a
+#define SGTL5000_CHIP_LINE_OUT_CTRL            0x002c
+#define SGTL5000_CHIP_LINE_OUT_VOL             0x002e
+#define SGTL5000_CHIP_ANA_POWER                        0x0030
+#define SGTL5000_CHIP_PLL_CTRL                 0x0032
+#define SGTL5000_CHIP_CLK_TOP_CTRL             0x0034
+#define SGTL5000_CHIP_ANA_STATUS               0x0036
+#define SGTL5000_CHIP_SHORT_CTRL               0x003c
+#define SGTL5000_CHIP_ANA_TEST2                        0x003a
+#define SGTL5000_DAP_CTRL                      0x0100
+#define SGTL5000_DAP_PEQ                       0x0102
+#define SGTL5000_DAP_BASS_ENHANCE              0x0104
+#define SGTL5000_DAP_BASS_ENHANCE_CTRL         0x0106
+#define SGTL5000_DAP_AUDIO_EQ                  0x0108
+#define SGTL5000_DAP_SURROUND                  0x010a
+#define SGTL5000_DAP_FLT_COEF_ACCESS           0x010c
+#define SGTL5000_DAP_COEF_WR_B0_MSB            0x010e
+#define SGTL5000_DAP_COEF_WR_B0_LSB            0x0110
+#define SGTL5000_DAP_EQ_BASS_BAND0             0x0116
+#define SGTL5000_DAP_EQ_BASS_BAND1             0x0118
+#define SGTL5000_DAP_EQ_BASS_BAND2             0x011a
+#define SGTL5000_DAP_EQ_BASS_BAND3             0x011c
+#define SGTL5000_DAP_EQ_BASS_BAND4             0x011e
+#define SGTL5000_DAP_MAIN_CHAN                 0x0120
+#define SGTL5000_DAP_MIX_CHAN                  0x0122
+#define SGTL5000_DAP_AVC_CTRL                  0x0124
+#define SGTL5000_DAP_AVC_THRESHOLD             0x0126
+#define SGTL5000_DAP_AVC_ATTACK                        0x0128
+#define SGTL5000_DAP_AVC_DECAY                 0x012a
+#define SGTL5000_DAP_COEF_WR_B1_MSB            0x012c
+#define SGTL5000_DAP_COEF_WR_B1_LSB            0x012e
+#define SGTL5000_DAP_COEF_WR_B2_MSB            0x0130
+#define SGTL5000_DAP_COEF_WR_B2_LSB            0x0132
+#define SGTL5000_DAP_COEF_WR_A1_MSB            0x0134
+#define SGTL5000_DAP_COEF_WR_A1_LSB            0x0136
+#define SGTL5000_DAP_COEF_WR_A2_MSB            0x0138
+#define SGTL5000_DAP_COEF_WR_A2_LSB            0x013a
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * SGTL5000_CHIP_ID
+ */
+#define SGTL5000_PARTID_MASK                   0xff00
+#define SGTL5000_PARTID_SHIFT                  8
+#define SGTL5000_PARTID_WIDTH                  8
+#define SGTL5000_PARTID_PART_ID                        0xa0
+#define SGTL5000_REVID_MASK                    0x00ff
+#define SGTL5000_REVID_SHIFT                   0
+#define SGTL5000_REVID_WIDTH                   8
+
+/*
+ * SGTL5000_CHIP_DIG_POWER
+ */
+#define SGTL5000_ADC_EN                                0x0040
+#define SGTL5000_DAC_EN                                0x0020
+#define SGTL5000_DAP_POWERUP                   0x0010
+#define SGTL5000_I2S_OUT_POWERUP               0x0002
+#define SGTL5000_I2S_IN_POWERUP                        0x0001
+
+/*
+ * SGTL5000_CHIP_CLK_CTRL
+ */
+#define SGTL5000_RATE_MODE_MASK                        0x0030
+#define SGTL5000_RATE_MODE_SHIFT               4
+#define SGTL5000_RATE_MODE_WIDTH               2
+#define SGTL5000_RATE_MODE_DIV_1               0
+#define SGTL5000_RATE_MODE_DIV_2               1
+#define SGTL5000_RATE_MODE_DIV_4               2
+#define SGTL5000_RATE_MODE_DIV_6               3
+#define SGTL5000_SYS_FS_MASK                   0x000c
+#define SGTL5000_SYS_FS_SHIFT                  2
+#define SGTL5000_SYS_FS_WIDTH                  2
+#define SGTL5000_SYS_FS_32k                    0x0
+#define SGTL5000_SYS_FS_44_1k                  0x1
+#define SGTL5000_SYS_FS_48k                    0x2
+#define SGTL5000_SYS_FS_96k                    0x3
+#define SGTL5000_MCLK_FREQ_MASK                        0x0003
+#define SGTL5000_MCLK_FREQ_SHIFT               0
+#define SGTL5000_MCLK_FREQ_WIDTH               2
+#define SGTL5000_MCLK_FREQ_256FS               0x0
+#define SGTL5000_MCLK_FREQ_384FS               0x1
+#define SGTL5000_MCLK_FREQ_512FS               0x2
+#define SGTL5000_MCLK_FREQ_PLL                 0x3
+
+/*
+ * SGTL5000_CHIP_I2S_CTRL
+ */
+#define SGTL5000_I2S_SCLKFREQ_MASK             0x0100
+#define SGTL5000_I2S_SCLKFREQ_SHIFT            8
+#define SGTL5000_I2S_SCLKFREQ_WIDTH            1
+#define SGTL5000_I2S_SCLKFREQ_64FS             0x0
+#define SGTL5000_I2S_SCLKFREQ_32FS             0x1     /* Not for RJ mode */
+#define SGTL5000_I2S_MASTER                    0x0080
+#define SGTL5000_I2S_SCLK_INV                  0x0040
+#define SGTL5000_I2S_DLEN_MASK                 0x0030
+#define SGTL5000_I2S_DLEN_SHIFT                        4
+#define SGTL5000_I2S_DLEN_WIDTH                        2
+#define SGTL5000_I2S_DLEN_32                   0x0
+#define SGTL5000_I2S_DLEN_24                   0x1
+#define SGTL5000_I2S_DLEN_20                   0x2
+#define SGTL5000_I2S_DLEN_16                   0x3
+#define SGTL5000_I2S_MODE_MASK                 0x000c
+#define SGTL5000_I2S_MODE_SHIFT                        2
+#define SGTL5000_I2S_MODE_WIDTH                        2
+#define SGTL5000_I2S_MODE_I2S_LJ               0x0
+#define SGTL5000_I2S_MODE_RJ                   0x1
+#define SGTL5000_I2S_MODE_PCM                  0x2
+#define SGTL5000_I2S_LRALIGN                   0x0002
+#define SGTL5000_I2S_LRPOL                     0x0001  /* set for which mode */
+
+/*
+ * SGTL5000_CHIP_SSS_CTRL
+ */
+#define SGTL5000_DAP_MIX_LRSWAP                        0x4000
+#define SGTL5000_DAP_LRSWAP                    0x2000
+#define SGTL5000_DAC_LRSWAP                    0x1000
+#define SGTL5000_I2S_OUT_LRSWAP                        0x0400
+#define SGTL5000_DAP_MIX_SEL_MASK              0x0300
+#define SGTL5000_DAP_MIX_SEL_SHIFT             8
+#define SGTL5000_DAP_MIX_SEL_WIDTH             2
+#define SGTL5000_DAP_MIX_SEL_ADC               0x0
+#define SGTL5000_DAP_MIX_SEL_I2S_IN            0x1
+#define SGTL5000_DAP_SEL_MASK                  0x00c0
+#define SGTL5000_DAP_SEL_SHIFT                 6
+#define SGTL5000_DAP_SEL_WIDTH                 2
+#define SGTL5000_DAP_SEL_ADC                   0x0
+#define SGTL5000_DAP_SEL_I2S_IN                        0x1
+#define SGTL5000_DAC_SEL_MASK                  0x0030
+#define SGTL5000_DAC_SEL_SHIFT                 4
+#define SGTL5000_DAC_SEL_WIDTH                 2
+#define SGTL5000_DAC_SEL_ADC                   0x0
+#define SGTL5000_DAC_SEL_I2S_IN                        0x1
+#define SGTL5000_DAC_SEL_DAP                   0x3
+#define SGTL5000_I2S_OUT_SEL_MASK              0x0003
+#define SGTL5000_I2S_OUT_SEL_SHIFT             0
+#define SGTL5000_I2S_OUT_SEL_WIDTH             2
+#define SGTL5000_I2S_OUT_SEL_ADC               0x0
+#define SGTL5000_I2S_OUT_SEL_I2S_IN            0x1
+#define SGTL5000_I2S_OUT_SEL_DAP               0x3
+
+/*
+ * SGTL5000_CHIP_ADCDAC_CTRL
+ */
+#define SGTL5000_VOL_BUSY_DAC_RIGHT            0x2000
+#define SGTL5000_VOL_BUSY_DAC_LEFT             0x1000
+#define SGTL5000_DAC_VOL_RAMP_EN               0x0200
+#define SGTL5000_DAC_VOL_RAMP_EXPO             0x0100
+#define SGTL5000_DAC_MUTE_RIGHT                        0x0008
+#define SGTL5000_DAC_MUTE_LEFT                 0x0004
+#define SGTL5000_ADC_HPF_FREEZE                        0x0002
+#define SGTL5000_ADC_HPF_BYPASS                        0x0001
+
+/*
+ * SGTL5000_CHIP_DAC_VOL
+ */
+#define SGTL5000_DAC_VOL_RIGHT_MASK            0xff00
+#define SGTL5000_DAC_VOL_RIGHT_SHIFT           8
+#define SGTL5000_DAC_VOL_RIGHT_WIDTH           8
+#define SGTL5000_DAC_VOL_LEFT_MASK             0x00ff
+#define SGTL5000_DAC_VOL_LEFT_SHIFT            0
+#define SGTL5000_DAC_VOL_LEFT_WIDTH            8
+
+/*
+ * SGTL5000_CHIP_PAD_STRENGTH
+ */
+#define SGTL5000_PAD_I2S_LRCLK_MASK            0x0300
+#define SGTL5000_PAD_I2S_LRCLK_SHIFT           8
+#define SGTL5000_PAD_I2S_LRCLK_WIDTH           2
+#define SGTL5000_PAD_I2S_SCLK_MASK             0x00c0
+#define SGTL5000_PAD_I2S_SCLK_SHIFT            6
+#define SGTL5000_PAD_I2S_SCLK_WIDTH            2
+#define SGTL5000_PAD_I2S_DOUT_MASK             0x0030
+#define SGTL5000_PAD_I2S_DOUT_SHIFT            4
+#define SGTL5000_PAD_I2S_DOUT_WIDTH            2
+#define SGTL5000_PAD_I2C_SDA_MASK              0x000c
+#define SGTL5000_PAD_I2C_SDA_SHIFT             2
+#define SGTL5000_PAD_I2C_SDA_WIDTH             2
+#define SGTL5000_PAD_I2C_SCL_MASK              0x0003
+#define SGTL5000_PAD_I2C_SCL_SHIFT             0
+#define SGTL5000_PAD_I2C_SCL_WIDTH             2
+
+/*
+ * SGTL5000_CHIP_ANA_ADC_CTRL
+ */
+#define SGTL5000_ADC_VOL_M6DB                  0x0100
+#define SGTL5000_ADC_VOL_RIGHT_MASK            0x00f0
+#define SGTL5000_ADC_VOL_RIGHT_SHIFT           4
+#define SGTL5000_ADC_VOL_RIGHT_WIDTH           4
+#define SGTL5000_ADC_VOL_LEFT_MASK             0x000f
+#define SGTL5000_ADC_VOL_LEFT_SHIFT            0
+#define SGTL5000_ADC_VOL_LEFT_WIDTH            4
+
+/*
+ * SGTL5000_CHIP_ANA_HP_CTRL
+ */
+#define SGTL5000_HP_VOL_RIGHT_MASK             0x7f00
+#define SGTL5000_HP_VOL_RIGHT_SHIFT            8
+#define SGTL5000_HP_VOL_RIGHT_WIDTH            7
+#define SGTL5000_HP_VOL_LEFT_MASK              0x007f
+#define SGTL5000_HP_VOL_LEFT_SHIFT             0
+#define SGTL5000_HP_VOL_LEFT_WIDTH             7
+
+/*
+ * SGTL5000_CHIP_ANA_CTRL
+ */
+#define SGTL5000_LINE_OUT_MUTE                 0x0100
+#define SGTL5000_HP_SEL_MASK                   0x0040
+#define SGTL5000_HP_SEL_SHIFT                  6
+#define SGTL5000_HP_SEL_WIDTH                  1
+#define SGTL5000_HP_SEL_DAC                    0x0
+#define SGTL5000_HP_SEL_LINE_IN                        0x1
+#define SGTL5000_HP_ZCD_EN                     0x0020
+#define SGTL5000_HP_MUTE                       0x0010
+#define SGTL5000_ADC_SEL_MASK                  0x0004
+#define SGTL5000_ADC_SEL_SHIFT                 2
+#define SGTL5000_ADC_SEL_WIDTH                 1
+#define SGTL5000_ADC_SEL_MIC                   0x0
+#define SGTL5000_ADC_SEL_LINE_IN               0x1
+#define SGTL5000_ADC_ZCD_EN                    0x0002
+#define SGTL5000_ADC_MUTE                      0x0001
+
+/*
+ * SGTL5000_CHIP_LINREG_CTRL
+ */
+#define SGTL5000_VDDC_MAN_ASSN_MASK            0x0040
+#define SGTL5000_VDDC_MAN_ASSN_SHIFT           6
+#define SGTL5000_VDDC_MAN_ASSN_WIDTH           1
+#define SGTL5000_VDDC_MAN_ASSN_VDDA            0x0
+#define SGTL5000_VDDC_MAN_ASSN_VDDIO           0x1
+#define SGTL5000_VDDC_ASSN_OVRD                        0x0020
+#define SGTL5000_LINREG_VDDD_MASK              0x000f
+#define SGTL5000_LINREG_VDDD_SHIFT             0
+#define SGTL5000_LINREG_VDDD_WIDTH             4
+
+/*
+ * SGTL5000_CHIP_REF_CTRL
+ */
+#define SGTL5000_ANA_GND_MASK                  0x01f0
+#define SGTL5000_ANA_GND_SHIFT                 4
+#define SGTL5000_ANA_GND_WIDTH                 5
+#define SGTL5000_ANA_GND_BASE                  800     /* mv */
+#define SGTL5000_ANA_GND_STP                   25      /*mv */
+#define SGTL5000_BIAS_CTRL_MASK                        0x000e
+#define SGTL5000_BIAS_CTRL_SHIFT               1
+#define SGTL5000_BIAS_CTRL_WIDTH               3
+#define SGTL5000_SMALL_POP                     0x0001
+
+/*
+ * SGTL5000_CHIP_MIC_CTRL
+ */
+#define SGTL5000_BIAS_R_MASK                   0x0200
+#define SGTL5000_BIAS_R_SHIFT                  8
+#define SGTL5000_BIAS_R_WIDTH                  2
+#define SGTL5000_BIAS_R_off                    0x0
+#define SGTL5000_BIAS_R_2K                     0x1
+#define SGTL5000_BIAS_R_4k                     0x2
+#define SGTL5000_BIAS_R_8k                     0x3
+#define SGTL5000_BIAS_VOLT_MASK                        0x0070
+#define SGTL5000_BIAS_VOLT_SHIFT               4
+#define SGTL5000_BIAS_VOLT_WIDTH               3
+#define SGTL5000_MIC_GAIN_MASK                 0x0003
+#define SGTL5000_MIC_GAIN_SHIFT                        0
+#define SGTL5000_MIC_GAIN_WIDTH                        2
+
+/*
+ * SGTL5000_CHIP_LINE_OUT_CTRL
+ */
+#define SGTL5000_LINE_OUT_CURRENT_MASK         0x0f00
+#define SGTL5000_LINE_OUT_CURRENT_SHIFT                8
+#define SGTL5000_LINE_OUT_CURRENT_WIDTH                4
+#define SGTL5000_LINE_OUT_CURRENT_180u         0x0
+#define SGTL5000_LINE_OUT_CURRENT_270u         0x1
+#define SGTL5000_LINE_OUT_CURRENT_360u         0x3
+#define SGTL5000_LINE_OUT_CURRENT_450u         0x7
+#define SGTL5000_LINE_OUT_CURRENT_540u         0xf
+#define SGTL5000_LINE_OUT_GND_MASK             0x003f
+#define SGTL5000_LINE_OUT_GND_SHIFT            0
+#define SGTL5000_LINE_OUT_GND_WIDTH            6
+#define SGTL5000_LINE_OUT_GND_BASE             800     /* mv */
+#define SGTL5000_LINE_OUT_GND_STP              25
+#define SGTL5000_LINE_OUT_GND_MAX              0x23
+
+/*
+ * SGTL5000_CHIP_LINE_OUT_VOL
+ */
+#define SGTL5000_LINE_OUT_VOL_RIGHT_MASK       0x1f00
+#define SGTL5000_LINE_OUT_VOL_RIGHT_SHIFT      8
+#define SGTL5000_LINE_OUT_VOL_RIGHT_WIDTH      5
+#define SGTL5000_LINE_OUT_VOL_LEFT_MASK                0x001f
+#define SGTL5000_LINE_OUT_VOL_LEFT_SHIFT       0
+#define SGTL5000_LINE_OUT_VOL_LEFT_WIDTH       5
+
+/*
+ * SGTL5000_CHIP_ANA_POWER
+ */
+#define SGTL5000_DAC_STEREO                    0x4000
+#define SGTL5000_LINREG_SIMPLE_POWERUP         0x2000
+#define SGTL5000_STARTUP_POWERUP               0x1000
+#define SGTL5000_VDDC_CHRGPMP_POWERUP          0x0800
+#define SGTL5000_PLL_POWERUP                   0x0400
+#define SGTL5000_LINEREG_D_POWERUP             0x0200
+#define SGTL5000_VCOAMP_POWERUP                        0x0100
+#define SGTL5000_VAG_POWERUP                   0x0080
+#define SGTL5000_ADC_STEREO                    0x0040
+#define SGTL5000_REFTOP_POWERUP                        0x0020
+#define SGTL5000_HP_POWERUP                    0x0010
+#define SGTL5000_DAC_POWERUP                   0x0008
+#define SGTL5000_CAPLESS_HP_POWERUP            0x0004
+#define SGTL5000_ADC_POWERUP                   0x0002
+#define SGTL5000_LINE_OUT_POWERUP              0x0001
+
+/*
+ * SGTL5000_CHIP_PLL_CTRL
+ */
+#define SGTL5000_PLL_INT_DIV_MASK              0xf800
+#define SGTL5000_PLL_INT_DIV_SHIFT             11
+#define SGTL5000_PLL_INT_DIV_WIDTH             5
+#define SGTL5000_PLL_FRAC_DIV_MASK             0x0700
+#define SGTL5000_PLL_FRAC_DIV_SHIFT            0
+#define SGTL5000_PLL_FRAC_DIV_WIDTH            11
+
+/*
+ * SGTL5000_CHIP_CLK_TOP_CTRL
+ */
+#define SGTL5000_INT_OSC_EN                    0x0800
+#define SGTL5000_INPUT_FREQ_DIV2               0x0008
+
+/*
+ * SGTL5000_CHIP_ANA_STATUS
+ */
+#define SGTL5000_HP_LRSHORT                    0x0200
+#define SGTL5000_CAPLESS_SHORT                 0x0100
+#define SGTL5000_PLL_LOCKED                    0x0010
+
+/*
+ * SGTL5000_CHIP_SHORT_CTRL
+ */
+#define SGTL5000_LVLADJR_MASK                  0x7000
+#define SGTL5000_LVLADJR_SHIFT                 12
+#define SGTL5000_LVLADJR_WIDTH                 3
+#define SGTL5000_LVLADJL_MASK                  0x0700
+#define SGTL5000_LVLADJL_SHIFT                 8
+#define SGTL5000_LVLADJL_WIDTH                 3
+#define SGTL5000_LVLADJC_MASK                  0x0070
+#define SGTL5000_LVLADJC_SHIFT                 4
+#define SGTL5000_LVLADJC_WIDTH                 3
+#define SGTL5000_LR_SHORT_MOD_MASK             0x000c
+#define SGTL5000_LR_SHORT_MOD_SHIFT            2
+#define SGTL5000_LR_SHORT_MOD_WIDTH            2
+#define SGTL5000_CM_SHORT_MOD_MASK             0x0003
+#define SGTL5000_CM_SHORT_MOD_SHIFT            0
+#define SGTL5000_CM_SHORT_MOD_WIDTH            2
+
+/*
+ *SGTL5000_CHIP_ANA_TEST2
+ */
+#define SGTL5000_MONO_DAC                      0x1000
+
+/*
+ * SGTL5000_DAP_CTRL
+ */
+#define SGTL5000_DAP_MIX_EN                    0x0010
+#define SGTL5000_DAP_EN                                0x0001
+
+#define SGTL5000_SYSCLK                                0x00
+#define SGTL5000_LRCLK                         0x01
+
+#endif
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
new file mode 100644 (file)
index 0000000..2a30eae
--- /dev/null
@@ -0,0 +1,949 @@
+/*
+ *  sn95031.c -  TI sn95031 Codec driver
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/intel_scu_ipc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+#include "sn95031.h"
+
+#define SN95031_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100)
+#define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+/* adc helper functions */
+
+/* enables mic bias voltage */
+static void sn95031_enable_mic_bias(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, SN95031_VAUD, BIT(2)|BIT(1)|BIT(0));
+       snd_soc_update_bits(codec, SN95031_MICBIAS, BIT(2), BIT(2));
+}
+
+/* Enable/Disable the ADC depending on the argument */
+static void configure_adc(struct snd_soc_codec *sn95031_codec, int val)
+{
+       int value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
+
+       if (val) {
+               /* Enable and start the ADC */
+               value |= (SN95031_ADC_ENBL | SN95031_ADC_START);
+               value &= (~SN95031_ADC_NO_LOOP);
+       } else {
+               /* Just stop the ADC */
+               value &= (~SN95031_ADC_START);
+       }
+       snd_soc_write(sn95031_codec, SN95031_ADC1CNTL1, value);
+}
+
+/*
+ * finds an empty channel for conversion
+ * If the ADC is not enabled then start using 0th channel
+ * itself. Otherwise find an empty channel by looking for a
+ * channel in which the stopbit is set to 1. returns the index
+ * of the first free channel if succeeds or an error code.
+ *
+ * Context: can sleep
+ *
+ */
+static int find_free_channel(struct snd_soc_codec *sn95031_codec)
+{
+       int ret = 0, i, value;
+
+       /* check whether ADC is enabled */
+       value = snd_soc_read(sn95031_codec, SN95031_ADC1CNTL1);
+
+       if ((value & SN95031_ADC_ENBL) == 0)
+               return 0;
+
+       /* ADC is already enabled; Looking for an empty channel */
+       for (i = 0; i < SN95031_ADC_CHANLS_MAX; i++) {
+               value = snd_soc_read(sn95031_codec,
+                               SN95031_ADC_CHNL_START_ADDR + i);
+               if (value & SN95031_STOPBIT_MASK) {
+                       ret = i;
+                       break;
+               }
+       }
+       return (ret > SN95031_ADC_LOOP_MAX) ? (-EINVAL) : ret;
+}
+
+/* Initialize the ADC for reading micbias values. Can sleep. */
+static int sn95031_initialize_adc(struct snd_soc_codec *sn95031_codec)
+{
+       int base_addr, chnl_addr;
+       int value;
+       static int channel_index;
+
+       /* Index of the first channel in which the stop bit is set */
+       channel_index = find_free_channel(sn95031_codec);
+       if (channel_index < 0) {
+               pr_err("No free ADC channels");
+               return channel_index;
+       }
+
+       base_addr = SN95031_ADC_CHNL_START_ADDR + channel_index;
+
+       if (!(channel_index == 0 || channel_index ==  SN95031_ADC_LOOP_MAX)) {
+               /* Reset stop bit for channels other than 0 and 12 */
+               value = snd_soc_read(sn95031_codec, base_addr);
+               /* Set the stop bit to zero */
+               snd_soc_write(sn95031_codec, base_addr, value & 0xEF);
+               /* Index of the first free channel */
+               base_addr++;
+               channel_index++;
+       }
+
+       /* Since this is the last channel, set the stop bit
+          to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
+       snd_soc_write(sn95031_codec, base_addr,
+                               SN95031_AUDIO_DETECT_CODE | 0x10);
+
+       chnl_addr = SN95031_ADC_DATA_START_ADDR + 2 * channel_index;
+       pr_debug("mid_initialize : %x", chnl_addr);
+       configure_adc(sn95031_codec, 1);
+       return chnl_addr;
+}
+
+
+/* reads the ADC registers and gets the mic bias value in mV. */
+static unsigned int sn95031_get_mic_bias(struct snd_soc_codec *codec)
+{
+       u16 adc_adr = sn95031_initialize_adc(codec);
+       u16 adc_val1, adc_val2;
+       unsigned int mic_bias;
+
+       sn95031_enable_mic_bias(codec);
+
+       /* Enable the sound card for conversion before reading */
+       snd_soc_write(codec, SN95031_ADC1CNTL3, 0x05);
+       /* Re-toggle the RRDATARD bit */
+       snd_soc_write(codec, SN95031_ADC1CNTL3, 0x04);
+
+       /* Read the higher bits of data */
+       msleep(1000);
+       adc_val1 = snd_soc_read(codec, adc_adr);
+       adc_adr++;
+       adc_val2 = snd_soc_read(codec, adc_adr);
+
+       /* Adding lower two bits to the higher bits */
+       mic_bias = (adc_val1 << 2) + (adc_val2 & 3);
+       mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000;
+       pr_debug("mic bias = %dmV\n", mic_bias);
+       return mic_bias;
+}
+EXPORT_SYMBOL_GPL(sn95031_get_mic_bias);
+/*end - adc helper functions */
+
+static inline unsigned int sn95031_read(struct snd_soc_codec *codec,
+                       unsigned int reg)
+{
+       u8 value = 0;
+       int ret;
+
+       ret = intel_scu_ipc_ioread8(reg, &value);
+       if (ret)
+               pr_err("read of %x failed, err %d\n", reg, ret);
+       return value;
+
+}
+
+static inline int sn95031_write(struct snd_soc_codec *codec,
+                       unsigned int reg, unsigned int value)
+{
+       int ret;
+
+       ret = intel_scu_ipc_iowrite8(reg, value);
+       if (ret)
+               pr_err("write of %x failed, err %d\n", reg, ret);
+       return ret;
+}
+
+static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
+               enum snd_soc_bias_level level)
+{
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+                       pr_debug("vaud_bias powering up pll\n");
+                       /* power up the pll */
+                       snd_soc_write(codec, SN95031_AUDPLLCTRL, BIT(5));
+                       /* enable pcm 2 */
+                       snd_soc_update_bits(codec, SN95031_PCM2C2,
+                                       BIT(0), BIT(0));
+               }
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+                       pr_debug("vaud_bias power up rail\n");
+                       /* power up the rail */
+                       snd_soc_write(codec, SN95031_VAUD,
+                                       BIT(2)|BIT(1)|BIT(0));
+                       msleep(1);
+               } else if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) {
+                       /* turn off pcm */
+                       pr_debug("vaud_bias power dn pcm\n");
+                       snd_soc_update_bits(codec, SN95031_PCM2C2, BIT(0), 0);
+                       snd_soc_write(codec, SN95031_AUDPLLCTRL, 0);
+               }
+               break;
+
+
+       case SND_SOC_BIAS_OFF:
+               pr_debug("vaud_bias _OFF doing rail shutdown\n");
+               snd_soc_write(codec, SN95031_VAUD, BIT(3));
+               break;
+       }
+
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+static int sn95031_vhs_event(struct snd_soc_dapm_widget *w,
+                   struct snd_kcontrol *kcontrol, int event)
+{
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               pr_debug("VHS SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
+               /* power up the rail */
+               snd_soc_write(w->codec, SN95031_VHSP, 0x3D);
+               snd_soc_write(w->codec, SN95031_VHSN, 0x3F);
+               msleep(1);
+       } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+               pr_debug("VHS SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
+               snd_soc_write(w->codec, SN95031_VHSP, 0xC4);
+               snd_soc_write(w->codec, SN95031_VHSN, 0x04);
+       }
+       return 0;
+}
+
+static int sn95031_vihf_event(struct snd_soc_dapm_widget *w,
+                   struct snd_kcontrol *kcontrol, int event)
+{
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               pr_debug("VIHF SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
+               /* power up the rail */
+               snd_soc_write(w->codec, SN95031_VIHF, 0x27);
+               msleep(1);
+       } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+               pr_debug("VIHF SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
+               snd_soc_write(w->codec, SN95031_VIHF, 0x24);
+       }
+       return 0;
+}
+
+static int sn95031_dmic12_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       unsigned int ldo = 0, clk_dir = 0, data_dir = 0;
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               ldo = BIT(5)|BIT(4);
+               clk_dir = BIT(0);
+               data_dir = BIT(7);
+       }
+       /* program DMIC LDO, clock and set clock */
+       snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo);
+       snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(0), clk_dir);
+       snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(7), data_dir);
+       return 0;
+}
+
+static int sn95031_dmic34_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       unsigned int ldo = 0, clk_dir = 0, data_dir = 0;
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               ldo = BIT(5)|BIT(4);
+               clk_dir = BIT(2);
+               data_dir = BIT(1);
+       }
+       /* program DMIC LDO, clock and set clock */
+       snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(5)|BIT(4), ldo);
+       snd_soc_update_bits(w->codec, SN95031_DMICBUF0123, BIT(2), clk_dir);
+       snd_soc_update_bits(w->codec, SN95031_DMICBUF45, BIT(1), data_dir);
+       return 0;
+}
+
+static int sn95031_dmic56_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       unsigned int ldo = 0;
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               ldo = BIT(7)|BIT(6);
+
+       /* program DMIC LDO */
+       snd_soc_update_bits(w->codec, SN95031_MICBIAS, BIT(7)|BIT(6), ldo);
+       return 0;
+}
+
+/* mux controls */
+static const char *sn95031_mic_texts[] = { "AMIC", "LineIn" };
+
+static const struct soc_enum sn95031_micl_enum =
+       SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 1, 2, sn95031_mic_texts);
+
+static const struct snd_kcontrol_new sn95031_micl_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_micl_enum);
+
+static const struct soc_enum sn95031_micr_enum =
+       SOC_ENUM_SINGLE(SN95031_ADCCONFIG, 3, 2, sn95031_mic_texts);
+
+static const struct snd_kcontrol_new sn95031_micr_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_micr_enum);
+
+static const char *sn95031_input_texts[] = {   "DMIC1", "DMIC2", "DMIC3",
+                                               "DMIC4", "DMIC5", "DMIC6",
+                                               "ADC Left", "ADC Right" };
+
+static const struct soc_enum sn95031_input1_enum =
+       SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 0, 8, sn95031_input_texts);
+
+static const struct snd_kcontrol_new sn95031_input1_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_input1_enum);
+
+static const struct soc_enum sn95031_input2_enum =
+       SOC_ENUM_SINGLE(SN95031_AUDIOMUX12, 4, 8, sn95031_input_texts);
+
+static const struct snd_kcontrol_new sn95031_input2_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_input2_enum);
+
+static const struct soc_enum sn95031_input3_enum =
+       SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 0, 8, sn95031_input_texts);
+
+static const struct snd_kcontrol_new sn95031_input3_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_input3_enum);
+
+static const struct soc_enum sn95031_input4_enum =
+       SOC_ENUM_SINGLE(SN95031_AUDIOMUX34, 4, 8, sn95031_input_texts);
+
+static const struct snd_kcontrol_new sn95031_input4_mux_control =
+       SOC_DAPM_ENUM("Route", sn95031_input4_enum);
+
+/* capture path controls */
+
+static const char *sn95031_micmode_text[] = {"Single Ended", "Differential"};
+
+/* 0dB to 30dB in 10dB steps */
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 10, 0);
+
+static const struct soc_enum sn95031_micmode1_enum =
+       SOC_ENUM_SINGLE(SN95031_MICAMP1, 1, 2, sn95031_micmode_text);
+static const struct soc_enum sn95031_micmode2_enum =
+       SOC_ENUM_SINGLE(SN95031_MICAMP2, 1, 2, sn95031_micmode_text);
+
+static const char *sn95031_dmic_cfg_text[] = {"GPO", "DMIC"};
+
+static const struct soc_enum sn95031_dmic12_cfg_enum =
+       SOC_ENUM_SINGLE(SN95031_DMICMUX, 0, 2, sn95031_dmic_cfg_text);
+static const struct soc_enum sn95031_dmic34_cfg_enum =
+       SOC_ENUM_SINGLE(SN95031_DMICMUX, 1, 2, sn95031_dmic_cfg_text);
+static const struct soc_enum sn95031_dmic56_cfg_enum =
+       SOC_ENUM_SINGLE(SN95031_DMICMUX, 2, 2, sn95031_dmic_cfg_text);
+
+static const struct snd_kcontrol_new sn95031_snd_controls[] = {
+       SOC_ENUM("Mic1Mode Capture Route", sn95031_micmode1_enum),
+       SOC_ENUM("Mic2Mode Capture Route", sn95031_micmode2_enum),
+       SOC_ENUM("DMIC12 Capture Route", sn95031_dmic12_cfg_enum),
+       SOC_ENUM("DMIC34 Capture Route", sn95031_dmic34_cfg_enum),
+       SOC_ENUM("DMIC56 Capture Route", sn95031_dmic56_cfg_enum),
+       SOC_SINGLE_TLV("Mic1 Capture Volume", SN95031_MICAMP1,
+                       2, 4, 0, mic_tlv),
+       SOC_SINGLE_TLV("Mic2 Capture Volume", SN95031_MICAMP2,
+                       2, 4, 0, mic_tlv),
+};
+
+/* DAPM widgets */
+static const struct snd_soc_dapm_widget sn95031_dapm_widgets[] = {
+
+       /* all end points mic, hs etc */
+       SND_SOC_DAPM_OUTPUT("HPOUTL"),
+       SND_SOC_DAPM_OUTPUT("HPOUTR"),
+       SND_SOC_DAPM_OUTPUT("EPOUT"),
+       SND_SOC_DAPM_OUTPUT("IHFOUTL"),
+       SND_SOC_DAPM_OUTPUT("IHFOUTR"),
+       SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+       SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+       SND_SOC_DAPM_OUTPUT("VIB1OUT"),
+       SND_SOC_DAPM_OUTPUT("VIB2OUT"),
+
+       SND_SOC_DAPM_INPUT("AMIC1"), /* headset mic */
+       SND_SOC_DAPM_INPUT("AMIC2"),
+       SND_SOC_DAPM_INPUT("DMIC1"),
+       SND_SOC_DAPM_INPUT("DMIC2"),
+       SND_SOC_DAPM_INPUT("DMIC3"),
+       SND_SOC_DAPM_INPUT("DMIC4"),
+       SND_SOC_DAPM_INPUT("DMIC5"),
+       SND_SOC_DAPM_INPUT("DMIC6"),
+       SND_SOC_DAPM_INPUT("LINEINL"),
+       SND_SOC_DAPM_INPUT("LINEINR"),
+
+       SND_SOC_DAPM_MICBIAS("AMIC1Bias", SN95031_MICBIAS, 2, 0),
+       SND_SOC_DAPM_MICBIAS("AMIC2Bias", SN95031_MICBIAS, 3, 0),
+       SND_SOC_DAPM_MICBIAS("DMIC12Bias", SN95031_DMICMUX, 3, 0),
+       SND_SOC_DAPM_MICBIAS("DMIC34Bias", SN95031_DMICMUX, 4, 0),
+       SND_SOC_DAPM_MICBIAS("DMIC56Bias", SN95031_DMICMUX, 5, 0),
+
+       SND_SOC_DAPM_SUPPLY("DMIC12supply", SN95031_DMICLK, 0, 0,
+                               sn95031_dmic12_event,
+                               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("DMIC34supply", SN95031_DMICLK, 1, 0,
+                               sn95031_dmic34_event,
+                               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("DMIC56supply", SN95031_DMICLK, 2, 0,
+                               sn95031_dmic56_event,
+                               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       SND_SOC_DAPM_AIF_OUT("PCM_Out", "Capture", 0,
+                       SND_SOC_NOPM, 0, 0),
+
+       SND_SOC_DAPM_SUPPLY("Headset Rail", SND_SOC_NOPM, 0, 0,
+                       sn95031_vhs_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_SUPPLY("Speaker Rail", SND_SOC_NOPM, 0, 0,
+                       sn95031_vihf_event,
+                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       /* playback path driver enables */
+       SND_SOC_DAPM_PGA("Headset Left Playback",
+                       SN95031_DRIVEREN, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Headset Right Playback",
+                       SN95031_DRIVEREN, 1, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Speaker Left Playback",
+                       SN95031_DRIVEREN, 2, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Speaker Right Playback",
+                       SN95031_DRIVEREN, 3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Vibra1 Playback",
+                       SN95031_DRIVEREN, 4, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Vibra2 Playback",
+                       SN95031_DRIVEREN, 5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Earpiece Playback",
+                       SN95031_DRIVEREN, 6, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Lineout Left Playback",
+                       SN95031_LOCTL, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Lineout Right Playback",
+                       SN95031_LOCTL, 4, 0, NULL, 0),
+
+       /* playback path filter enable */
+       SND_SOC_DAPM_PGA("Headset Left Filter",
+                       SN95031_HSEPRXCTRL, 4, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Headset Right Filter",
+                       SN95031_HSEPRXCTRL, 5, 0,  NULL, 0),
+       SND_SOC_DAPM_PGA("Speaker Left Filter",
+                       SN95031_IHFRXCTRL, 0, 0,  NULL, 0),
+       SND_SOC_DAPM_PGA("Speaker Right Filter",
+                       SN95031_IHFRXCTRL, 1, 0,  NULL, 0),
+
+       /* DACs */
+       SND_SOC_DAPM_DAC("HSDAC Left", "Headset",
+                       SN95031_DACCONFIG, 0, 0),
+       SND_SOC_DAPM_DAC("HSDAC Right", "Headset",
+                       SN95031_DACCONFIG, 1, 0),
+       SND_SOC_DAPM_DAC("IHFDAC Left", "Speaker",
+                       SN95031_DACCONFIG, 2, 0),
+       SND_SOC_DAPM_DAC("IHFDAC Right", "Speaker",
+                       SN95031_DACCONFIG, 3, 0),
+       SND_SOC_DAPM_DAC("Vibra1 DAC", "Vibra1",
+                       SN95031_VIB1C5, 1, 0),
+       SND_SOC_DAPM_DAC("Vibra2 DAC", "Vibra2",
+                       SN95031_VIB2C5, 1, 0),
+
+       /* capture widgets */
+       SND_SOC_DAPM_PGA("LineIn Enable Left", SN95031_MICAMP1,
+                               7, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("LineIn Enable Right", SN95031_MICAMP2,
+                               7, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA("MIC1 Enable", SN95031_MICAMP1, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("MIC2 Enable", SN95031_MICAMP2, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("TX1 Enable", SN95031_AUDIOTXEN, 2, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("TX2 Enable", SN95031_AUDIOTXEN, 3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("TX3 Enable", SN95031_AUDIOTXEN, 4, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("TX4 Enable", SN95031_AUDIOTXEN, 5, 0, NULL, 0),
+
+       /* ADC have null stream as they will be turned ON by TX path */
+       SND_SOC_DAPM_ADC("ADC Left", NULL,
+                       SN95031_ADCCONFIG, 0, 0),
+       SND_SOC_DAPM_ADC("ADC Right", NULL,
+                       SN95031_ADCCONFIG, 2, 0),
+
+       SND_SOC_DAPM_MUX("Mic_InputL Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_micl_mux_control),
+       SND_SOC_DAPM_MUX("Mic_InputR Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_micr_mux_control),
+
+       SND_SOC_DAPM_MUX("Txpath1 Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_input1_mux_control),
+       SND_SOC_DAPM_MUX("Txpath2 Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_input2_mux_control),
+       SND_SOC_DAPM_MUX("Txpath3 Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_input3_mux_control),
+       SND_SOC_DAPM_MUX("Txpath4 Capture Route",
+                       SND_SOC_NOPM, 0, 0, &sn95031_input4_mux_control),
+
+};
+
+static const struct snd_soc_dapm_route sn95031_audio_map[] = {
+       /* headset and earpiece map */
+       { "HPOUTL", NULL, "Headset Rail"},
+       { "HPOUTR", NULL, "Headset Rail"},
+       { "HPOUTL", NULL, "Headset Left Playback" },
+       { "HPOUTR", NULL, "Headset Right Playback" },
+       { "EPOUT", NULL, "Earpiece Playback" },
+       { "Headset Left Playback", NULL, "Headset Left Filter"},
+       { "Headset Right Playback", NULL, "Headset Right Filter"},
+       { "Earpiece Playback", NULL, "Headset Left Filter"},
+       { "Headset Left Filter", NULL, "HSDAC Left"},
+       { "Headset Right Filter", NULL, "HSDAC Right"},
+
+       /* speaker map */
+       { "IHFOUTL", NULL, "Speaker Rail"},
+       { "IHFOUTR", NULL, "Speaker Rail"},
+       { "IHFOUTL", "NULL", "Speaker Left Playback"},
+       { "IHFOUTR", "NULL", "Speaker Right Playback"},
+       { "Speaker Left Playback", NULL, "Speaker Left Filter"},
+       { "Speaker Right Playback", NULL, "Speaker Right Filter"},
+       { "Speaker Left Filter", NULL, "IHFDAC Left"},
+       { "Speaker Right Filter", NULL, "IHFDAC Right"},
+
+       /* vibra map */
+       { "VIB1OUT", NULL, "Vibra1 Playback"},
+       { "Vibra1 Playback", NULL, "Vibra1 DAC"},
+
+       { "VIB2OUT", NULL, "Vibra2 Playback"},
+       { "Vibra2 Playback", NULL, "Vibra2 DAC"},
+
+       /* lineout */
+       { "LINEOUTL", NULL, "Lineout Left Playback"},
+       { "LINEOUTR", NULL, "Lineout Right Playback"},
+       { "Lineout Left Playback", NULL, "Headset Left Filter"},
+       { "Lineout Left Playback", NULL, "Speaker Left Filter"},
+       { "Lineout Left Playback", NULL, "Vibra1 DAC"},
+       { "Lineout Right Playback", NULL, "Headset Right Filter"},
+       { "Lineout Right Playback", NULL, "Speaker Right Filter"},
+       { "Lineout Right Playback", NULL, "Vibra2 DAC"},
+
+       /* Headset (AMIC1) mic */
+       { "AMIC1Bias", NULL, "AMIC1"},
+       { "MIC1 Enable", NULL, "AMIC1Bias"},
+       { "Mic_InputL Capture Route", "AMIC", "MIC1 Enable"},
+
+       /* AMIC2 */
+       { "AMIC2Bias", NULL, "AMIC2"},
+       { "MIC2 Enable", NULL, "AMIC2Bias"},
+       { "Mic_InputR Capture Route", "AMIC", "MIC2 Enable"},
+
+
+       /* Linein */
+       { "LineIn Enable Left", NULL, "LINEINL"},
+       { "LineIn Enable Right", NULL, "LINEINR"},
+       { "Mic_InputL Capture Route", "LineIn", "LineIn Enable Left"},
+       { "Mic_InputR Capture Route", "LineIn", "LineIn Enable Right"},
+
+       /* ADC connection */
+       { "ADC Left", NULL, "Mic_InputL Capture Route"},
+       { "ADC Right", NULL, "Mic_InputR Capture Route"},
+
+       /*DMIC connections */
+       { "DMIC1", NULL, "DMIC12supply"},
+       { "DMIC2", NULL, "DMIC12supply"},
+       { "DMIC3", NULL, "DMIC34supply"},
+       { "DMIC4", NULL, "DMIC34supply"},
+       { "DMIC5", NULL, "DMIC56supply"},
+       { "DMIC6", NULL, "DMIC56supply"},
+
+       { "DMIC12Bias", NULL, "DMIC1"},
+       { "DMIC12Bias", NULL, "DMIC2"},
+       { "DMIC34Bias", NULL, "DMIC3"},
+       { "DMIC34Bias", NULL, "DMIC4"},
+       { "DMIC56Bias", NULL, "DMIC5"},
+       { "DMIC56Bias", NULL, "DMIC6"},
+
+       /*TX path inputs*/
+       { "Txpath1 Capture Route", "ADC Left", "ADC Left"},
+       { "Txpath2 Capture Route", "ADC Left", "ADC Left"},
+       { "Txpath3 Capture Route", "ADC Left", "ADC Left"},
+       { "Txpath4 Capture Route", "ADC Left", "ADC Left"},
+       { "Txpath1 Capture Route", "ADC Right", "ADC Right"},
+       { "Txpath2 Capture Route", "ADC Right", "ADC Right"},
+       { "Txpath3 Capture Route", "ADC Right", "ADC Right"},
+       { "Txpath4 Capture Route", "ADC Right", "ADC Right"},
+       { "Txpath1 Capture Route", "DMIC1", "DMIC1"},
+       { "Txpath2 Capture Route", "DMIC1", "DMIC1"},
+       { "Txpath3 Capture Route", "DMIC1", "DMIC1"},
+       { "Txpath4 Capture Route", "DMIC1", "DMIC1"},
+       { "Txpath1 Capture Route", "DMIC2", "DMIC2"},
+       { "Txpath2 Capture Route", "DMIC2", "DMIC2"},
+       { "Txpath3 Capture Route", "DMIC2", "DMIC2"},
+       { "Txpath4 Capture Route", "DMIC2", "DMIC2"},
+       { "Txpath1 Capture Route", "DMIC3", "DMIC3"},
+       { "Txpath2 Capture Route", "DMIC3", "DMIC3"},
+       { "Txpath3 Capture Route", "DMIC3", "DMIC3"},
+       { "Txpath4 Capture Route", "DMIC3", "DMIC3"},
+       { "Txpath1 Capture Route", "DMIC4", "DMIC4"},
+       { "Txpath2 Capture Route", "DMIC4", "DMIC4"},
+       { "Txpath3 Capture Route", "DMIC4", "DMIC4"},
+       { "Txpath4 Capture Route", "DMIC4", "DMIC4"},
+       { "Txpath1 Capture Route", "DMIC5", "DMIC5"},
+       { "Txpath2 Capture Route", "DMIC5", "DMIC5"},
+       { "Txpath3 Capture Route", "DMIC5", "DMIC5"},
+       { "Txpath4 Capture Route", "DMIC5", "DMIC5"},
+       { "Txpath1 Capture Route", "DMIC6", "DMIC6"},
+       { "Txpath2 Capture Route", "DMIC6", "DMIC6"},
+       { "Txpath3 Capture Route", "DMIC6", "DMIC6"},
+       { "Txpath4 Capture Route", "DMIC6", "DMIC6"},
+
+       /* tx path */
+       { "TX1 Enable", NULL, "Txpath1 Capture Route"},
+       { "TX2 Enable", NULL, "Txpath2 Capture Route"},
+       { "TX3 Enable", NULL, "Txpath3 Capture Route"},
+       { "TX4 Enable", NULL, "Txpath4 Capture Route"},
+       { "PCM_Out", NULL, "TX1 Enable"},
+       { "PCM_Out", NULL, "TX2 Enable"},
+       { "PCM_Out", NULL, "TX3 Enable"},
+       { "PCM_Out", NULL, "TX4 Enable"},
+
+};
+
+/* speaker and headset mutes, for audio pops and clicks */
+static int sn95031_pcm_hs_mute(struct snd_soc_dai *dai, int mute)
+{
+       snd_soc_update_bits(dai->codec,
+                       SN95031_HSLVOLCTRL, BIT(7), (!mute << 7));
+       snd_soc_update_bits(dai->codec,
+                       SN95031_HSRVOLCTRL, BIT(7), (!mute << 7));
+       return 0;
+}
+
+static int sn95031_pcm_spkr_mute(struct snd_soc_dai *dai, int mute)
+{
+       snd_soc_update_bits(dai->codec,
+                       SN95031_IHFLVOLCTRL, BIT(7), (!mute << 7));
+       snd_soc_update_bits(dai->codec,
+                       SN95031_IHFRVOLCTRL, BIT(7), (!mute << 7));
+       return 0;
+}
+
+int sn95031_pcm_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       unsigned int format, rate;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               format = BIT(4)|BIT(5);
+               break;
+
+       case SNDRV_PCM_FORMAT_S24_LE:
+               format = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+       snd_soc_update_bits(dai->codec, SN95031_PCM2C2,
+                       BIT(4)|BIT(5), format);
+
+       switch (params_rate(params)) {
+       case 48000:
+               pr_debug("RATE_48000\n");
+               rate = 0;
+               break;
+
+       case 44100:
+               pr_debug("RATE_44100\n");
+               rate = BIT(7);
+               break;
+
+       default:
+               pr_err("ERR rate %d\n", params_rate(params));
+               return -EINVAL;
+       }
+       snd_soc_update_bits(dai->codec, SN95031_PCM1C1, BIT(7), rate);
+
+       return 0;
+}
+
+/* Codec DAI section */
+static struct snd_soc_dai_ops sn95031_headset_dai_ops = {
+       .digital_mute   = sn95031_pcm_hs_mute,
+       .hw_params      = sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_speaker_dai_ops = {
+       .digital_mute   = sn95031_pcm_spkr_mute,
+       .hw_params      = sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_vib1_dai_ops = {
+       .hw_params      = sn95031_pcm_hw_params,
+};
+
+static struct snd_soc_dai_ops sn95031_vib2_dai_ops = {
+       .hw_params      = sn95031_pcm_hw_params,
+};
+
+struct snd_soc_dai_driver sn95031_dais[] = {
+{
+       .name = "SN95031 Headset",
+       .playback = {
+               .stream_name = "Headset",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SN95031_RATES,
+               .formats = SN95031_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = 5,
+               .rates = SN95031_RATES,
+               .formats = SN95031_FORMATS,
+       },
+       .ops = &sn95031_headset_dai_ops,
+},
+{      .name = "SN95031 Speaker",
+       .playback = {
+               .stream_name = "Speaker",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SN95031_RATES,
+               .formats = SN95031_FORMATS,
+       },
+       .ops = &sn95031_speaker_dai_ops,
+},
+{      .name = "SN95031 Vibra1",
+       .playback = {
+               .stream_name = "Vibra1",
+               .channels_min = 1,
+               .channels_max = 1,
+               .rates = SN95031_RATES,
+               .formats = SN95031_FORMATS,
+       },
+       .ops = &sn95031_vib1_dai_ops,
+},
+{      .name = "SN95031 Vibra2",
+       .playback = {
+               .stream_name = "Vibra2",
+               .channels_min = 1,
+               .channels_max = 1,
+               .rates = SN95031_RATES,
+               .formats = SN95031_FORMATS,
+       },
+       .ops = &sn95031_vib2_dai_ops,
+},
+};
+
+static inline void sn95031_disable_jack_btn(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, SN95031_BTNCTRL2, 0x00);
+}
+
+static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec)
+{
+       snd_soc_write(codec, SN95031_BTNCTRL1, 0x77);
+       snd_soc_write(codec, SN95031_BTNCTRL2, 0x01);
+}
+
+static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack)
+{
+       int micbias = sn95031_get_mic_bias(mfld_jack->codec);
+
+       int jack_type = snd_soc_jack_get_type(mfld_jack, micbias);
+
+       pr_debug("jack type detected = %d\n", jack_type);
+       if (jack_type == SND_JACK_HEADSET)
+               sn95031_enable_jack_btn(mfld_jack->codec);
+       return jack_type;
+}
+
+void sn95031_jack_detection(struct mfld_jack_data *jack_data)
+{
+       unsigned int status;
+       unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET;
+
+       pr_debug("interrupt id read in sram = 0x%x\n", jack_data->intr_id);
+       if (jack_data->intr_id & 0x1) {
+               pr_debug("short_push detected\n");
+               status = SND_JACK_HEADSET | SND_JACK_BTN_0;
+       } else if (jack_data->intr_id & 0x2) {
+               pr_debug("long_push detected\n");
+               status = SND_JACK_HEADSET | SND_JACK_BTN_1;
+       } else if (jack_data->intr_id & 0x4) {
+               pr_debug("headset or headphones inserted\n");
+               status = sn95031_get_headset_state(jack_data->mfld_jack);
+       } else if (jack_data->intr_id & 0x8) {
+               pr_debug("headset or headphones removed\n");
+               status = 0;
+               sn95031_disable_jack_btn(jack_data->mfld_jack->codec);
+       } else {
+               pr_err("unidentified interrupt\n");
+               return;
+       }
+
+       snd_soc_jack_report(jack_data->mfld_jack, status, mask);
+       /*button pressed and released so we send explicit button release */
+       if ((status & SND_JACK_BTN_0) | (status & SND_JACK_BTN_1))
+               snd_soc_jack_report(jack_data->mfld_jack,
+                               SND_JACK_HEADSET, mask);
+}
+EXPORT_SYMBOL_GPL(sn95031_jack_detection);
+
+/* codec registration */
+static int sn95031_codec_probe(struct snd_soc_codec *codec)
+{
+       int ret;
+
+       pr_debug("codec_probe called\n");
+
+       codec->dapm.bias_level = SND_SOC_BIAS_OFF;
+       codec->dapm.idle_bias_off = 1;
+
+       /* PCM interface config
+        * This sets the pcm rx slot conguration to max 6 slots
+        * for max 4 dais (2 stereo and 2 mono)
+        */
+       snd_soc_write(codec, SN95031_PCM2RXSLOT01, 0x10);
+       snd_soc_write(codec, SN95031_PCM2RXSLOT23, 0x32);
+       snd_soc_write(codec, SN95031_PCM2RXSLOT45, 0x54);
+       snd_soc_write(codec, SN95031_PCM2TXSLOT01, 0x10);
+       snd_soc_write(codec, SN95031_PCM2TXSLOT23, 0x32);
+       /* pcm port setting
+        * This sets the pcm port to slave and clock at 19.2Mhz which
+        * can support 6slots, sampling rate set per stream in hw-params
+        */
+       snd_soc_write(codec, SN95031_PCM1C1, 0x00);
+       snd_soc_write(codec, SN95031_PCM2C1, 0x01);
+       snd_soc_write(codec, SN95031_PCM2C2, 0x0A);
+       snd_soc_write(codec, SN95031_HSMIXER, BIT(0)|BIT(4));
+       /* vendor vibra workround, the vibras are muted by
+        * custom register so unmute them
+        */
+       snd_soc_write(codec, SN95031_SSR5, 0x80);
+       snd_soc_write(codec, SN95031_SSR6, 0x80);
+       snd_soc_write(codec, SN95031_VIB1C5, 0x00);
+       snd_soc_write(codec, SN95031_VIB2C5, 0x00);
+       /* configure vibras for pcm port */
+       snd_soc_write(codec, SN95031_VIB1C3, 0x00);
+       snd_soc_write(codec, SN95031_VIB2C3, 0x00);
+
+       /* soft mute ramp time */
+       snd_soc_write(codec, SN95031_SOFTMUTE, 0x3);
+       /* fix the initial volume at 1dB,
+        * default in +9dB,
+        * 1dB give optimal swing on DAC, amps
+        */
+       snd_soc_write(codec, SN95031_HSLVOLCTRL, 0x08);
+       snd_soc_write(codec, SN95031_HSRVOLCTRL, 0x08);
+       snd_soc_write(codec, SN95031_IHFLVOLCTRL, 0x08);
+       snd_soc_write(codec, SN95031_IHFRVOLCTRL, 0x08);
+       /* dac mode and lineout workaround */
+       snd_soc_write(codec, SN95031_SSR2, 0x10);
+       snd_soc_write(codec, SN95031_SSR3, 0x40);
+
+       snd_soc_add_controls(codec, sn95031_snd_controls,
+                            ARRAY_SIZE(sn95031_snd_controls));
+
+       ret = snd_soc_dapm_new_controls(&codec->dapm, sn95031_dapm_widgets,
+                               ARRAY_SIZE(sn95031_dapm_widgets));
+       if (ret)
+               pr_err("soc_dapm_new_control failed %d", ret);
+       ret = snd_soc_dapm_add_routes(&codec->dapm, sn95031_audio_map,
+                               ARRAY_SIZE(sn95031_audio_map));
+       if (ret)
+               pr_err("soc_dapm_add_routes failed %d", ret);
+
+       return ret;
+}
+
+static int sn95031_codec_remove(struct snd_soc_codec *codec)
+{
+       pr_debug("codec_remove called\n");
+       sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF);
+
+       return 0;
+}
+
+struct snd_soc_codec_driver sn95031_codec = {
+       .probe          = sn95031_codec_probe,
+       .remove         = sn95031_codec_remove,
+       .read           = sn95031_read,
+       .write          = sn95031_write,
+       .set_bias_level = sn95031_set_vaud_bias,
+};
+
+static int __devinit sn95031_device_probe(struct platform_device *pdev)
+{
+       pr_debug("codec device probe called for %s\n", dev_name(&pdev->dev));
+       return snd_soc_register_codec(&pdev->dev, &sn95031_codec,
+                       sn95031_dais, ARRAY_SIZE(sn95031_dais));
+}
+
+static int __devexit sn95031_device_remove(struct platform_device *pdev)
+{
+       pr_debug("codec device remove called\n");
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static struct platform_driver sn95031_codec_driver = {
+       .driver         = {
+               .name           = "sn95031",
+               .owner          = THIS_MODULE,
+       },
+       .probe          = sn95031_device_probe,
+       .remove         = sn95031_device_remove,
+};
+
+static int __init sn95031_init(void)
+{
+       pr_debug("driver init called\n");
+       return platform_driver_register(&sn95031_codec_driver);
+}
+module_init(sn95031_init);
+
+static void __exit sn95031_exit(void)
+{
+       pr_debug("driver exit called\n");
+       platform_driver_unregister(&sn95031_codec_driver);
+}
+module_exit(sn95031_exit);
+
+MODULE_DESCRIPTION("ASoC TI SN95031 codec driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sn95031");
diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h
new file mode 100644 (file)
index 0000000..20376d2
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ *  sn95031.h - TI sn95031 Codec driver
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#ifndef _SN95031_H
+#define _SN95031_H
+
+/*register map*/
+#define SN95031_VAUD                   0xDB
+#define SN95031_VHSP                   0xDC
+#define SN95031_VHSN                   0xDD
+#define SN95031_VIHF                   0xC9
+
+#define SN95031_AUDPLLCTRL             0x240
+#define SN95031_DMICBUF0123            0x241
+#define SN95031_DMICBUF45              0x242
+#define SN95031_DMICGPO                        0x244
+#define SN95031_DMICMUX                        0x245
+#define SN95031_DMICLK                 0x246
+#define SN95031_MICBIAS                        0x247
+#define SN95031_ADCCONFIG              0x248
+#define SN95031_MICAMP1                        0x249
+#define SN95031_MICAMP2                        0x24A
+#define SN95031_NOISEMUX               0x24B
+#define SN95031_AUDIOMUX12             0x24C
+#define SN95031_AUDIOMUX34             0x24D
+#define SN95031_AUDIOSINC              0x24E
+#define SN95031_AUDIOTXEN              0x24F
+#define SN95031_HSEPRXCTRL             0x250
+#define SN95031_IHFRXCTRL              0x251
+#define SN95031_HSMIXER                        0x256
+#define SN95031_DACCONFIG              0x257
+#define SN95031_SOFTMUTE               0x258
+#define SN95031_HSLVOLCTRL             0x259
+#define SN95031_HSRVOLCTRL             0x25A
+#define SN95031_IHFLVOLCTRL            0x25B
+#define SN95031_IHFRVOLCTRL            0x25C
+#define SN95031_DRIVEREN               0x25D
+#define SN95031_LOCTL                  0x25E
+#define SN95031_VIB1C1                 0x25F
+#define SN95031_VIB1C2                 0x260
+#define SN95031_VIB1C3                 0x261
+#define SN95031_VIB1SPIPCM1            0x262
+#define SN95031_VIB1SPIPCM2            0x263
+#define SN95031_VIB1C5                 0x264
+#define SN95031_VIB2C1                 0x265
+#define SN95031_VIB2C2                 0x266
+#define SN95031_VIB2C3                 0x267
+#define SN95031_VIB2SPIPCM1            0x268
+#define SN95031_VIB2SPIPCM2            0x269
+#define SN95031_VIB2C5                 0x26A
+#define SN95031_BTNCTRL1               0x26B
+#define SN95031_BTNCTRL2               0x26C
+#define SN95031_PCM1TXSLOT01           0x26D
+#define SN95031_PCM1TXSLOT23           0x26E
+#define SN95031_PCM1TXSLOT45           0x26F
+#define SN95031_PCM1RXSLOT0_3          0x270
+#define SN95031_PCM1RXSLOT45           0x271
+#define SN95031_PCM2TXSLOT01           0x272
+#define SN95031_PCM2TXSLOT23           0x273
+#define SN95031_PCM2TXSLOT45           0x274
+#define SN95031_PCM2RXSLOT01           0x275
+#define SN95031_PCM2RXSLOT23           0x276
+#define SN95031_PCM2RXSLOT45           0x277
+#define SN95031_PCM1C1                 0x278
+#define SN95031_PCM1C2                 0x279
+#define SN95031_PCM1C3                 0x27A
+#define SN95031_PCM2C1                 0x27B
+#define SN95031_PCM2C2                 0x27C
+/*end codec register defn*/
+
+/*vendor defn these are not part of avp*/
+#define SN95031_SSR2                   0x381
+#define SN95031_SSR3                   0x382
+#define SN95031_SSR5                   0x384
+#define SN95031_SSR6                   0x385
+
+/* ADC registers */
+
+#define SN95031_ADC1CNTL1 0x1C0
+#define SN95031_ADC_ENBL 0x10
+#define SN95031_ADC_START 0x08
+#define SN95031_ADC1CNTL3 0x1C2
+#define SN95031_ADCTHERM_ENBL 0x04
+#define SN95031_ADCRRDATA_ENBL 0x05
+#define SN95031_STOPBIT_MASK 16
+#define SN95031_ADCTHERM_MASK 4
+#define SN95031_ADC_CHANLS_MAX 15 /* Number of ADC channels */
+#define SN95031_ADC_LOOP_MAX (SN95031_ADC_CHANLS_MAX - 1)
+#define SN95031_ADC_NO_LOOP 0x07
+#define SN95031_AUDIO_GPIO_CTRL 0x070
+
+/* ADC channel code values */
+#define SN95031_AUDIO_DETECT_CODE 0x06
+
+/* ADC base addresses */
+#define SN95031_ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
+#define SN95031_ADC_DATA_START_ADDR 0x1D4  /* increments by 2 */
+/* multipier to convert to mV */
+#define SN95031_ADC_ONE_LSB_MULTIPLIER 2346
+
+
+struct mfld_jack_data {
+       int intr_id;
+       int micbias_vol;
+       struct snd_soc_jack *mfld_jack;
+};
+
+extern void sn95031_jack_detection(struct mfld_jack_data *jack_data);
+
+#endif
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
new file mode 100644 (file)
index 0000000..e93b9d1
--- /dev/null
@@ -0,0 +1,794 @@
+/*
+ * linux/sound/soc/codecs/tlv320aic32x4.c
+ *
+ * Copyright 2011 Vista Silicon S.L.
+ *
+ * Author: Javier Martin <javier.martin@vista-silicon.com>
+ *
+ * Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+
+#include <sound/tlv320aic32x4.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "tlv320aic32x4.h"
+
+struct aic32x4_rate_divs {
+       u32 mclk;
+       u32 rate;
+       u8 p_val;
+       u8 pll_j;
+       u16 pll_d;
+       u16 dosr;
+       u8 ndac;
+       u8 mdac;
+       u8 aosr;
+       u8 nadc;
+       u8 madc;
+       u8 blck_N;
+};
+
+struct aic32x4_priv {
+       u32 sysclk;
+       s32 master;
+       u8 page_no;
+       void *control_data;
+       u32 power_cfg;
+       u32 micpga_routing;
+       bool swapdacs;
+};
+
+/* 0dB min, 1dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_step_1, 0, 100, 0);
+/* 0dB min, 0.5dB steps */
+static DECLARE_TLV_DB_SCALE(tlv_step_0_5, 0, 50, 0);
+
+static const struct snd_kcontrol_new aic32x4_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("PCM Playback Volume", AIC32X4_LDACVOL,
+                       AIC32X4_RDACVOL, 0, 0x30, 0, tlv_step_0_5),
+       SOC_DOUBLE_R_TLV("HP Driver Gain Volume", AIC32X4_HPLGAIN,
+                       AIC32X4_HPRGAIN, 0, 0x1D, 0, tlv_step_1),
+       SOC_DOUBLE_R_TLV("LO Driver Gain Volume", AIC32X4_LOLGAIN,
+                       AIC32X4_LORGAIN, 0, 0x1D, 0, tlv_step_1),
+       SOC_DOUBLE_R("HP DAC Playback Switch", AIC32X4_HPLGAIN,
+                       AIC32X4_HPRGAIN, 6, 0x01, 1),
+       SOC_DOUBLE_R("LO DAC Playback Switch", AIC32X4_LOLGAIN,
+                       AIC32X4_LORGAIN, 6, 0x01, 1),
+       SOC_DOUBLE_R("Mic PGA Switch", AIC32X4_LMICPGAVOL,
+                       AIC32X4_RMICPGAVOL, 7, 0x01, 1),
+
+       SOC_SINGLE("ADCFGA Left Mute Switch", AIC32X4_ADCFGA, 7, 1, 0),
+       SOC_SINGLE("ADCFGA Right Mute Switch", AIC32X4_ADCFGA, 3, 1, 0),
+
+       SOC_DOUBLE_R_TLV("ADC Level Volume", AIC32X4_LADCVOL,
+                       AIC32X4_RADCVOL, 0, 0x28, 0, tlv_step_0_5),
+       SOC_DOUBLE_R_TLV("PGA Level Volume", AIC32X4_LMICPGAVOL,
+                       AIC32X4_RMICPGAVOL, 0, 0x5f, 0, tlv_step_0_5),
+
+       SOC_SINGLE("Auto-mute Switch", AIC32X4_DACMUTE, 4, 7, 0),
+
+       SOC_SINGLE("AGC Left Switch", AIC32X4_LAGC1, 7, 1, 0),
+       SOC_SINGLE("AGC Right Switch", AIC32X4_RAGC1, 7, 1, 0),
+       SOC_DOUBLE_R("AGC Target Level", AIC32X4_LAGC1, AIC32X4_RAGC1,
+                       4, 0x07, 0),
+       SOC_DOUBLE_R("AGC Gain Hysteresis", AIC32X4_LAGC1, AIC32X4_RAGC1,
+                       0, 0x03, 0),
+       SOC_DOUBLE_R("AGC Hysteresis", AIC32X4_LAGC2, AIC32X4_RAGC2,
+                       6, 0x03, 0),
+       SOC_DOUBLE_R("AGC Noise Threshold", AIC32X4_LAGC2, AIC32X4_RAGC2,
+                       1, 0x1F, 0),
+       SOC_DOUBLE_R("AGC Max PGA", AIC32X4_LAGC3, AIC32X4_RAGC3,
+                       0, 0x7F, 0),
+       SOC_DOUBLE_R("AGC Attack Time", AIC32X4_LAGC4, AIC32X4_RAGC4,
+                       3, 0x1F, 0),
+       SOC_DOUBLE_R("AGC Decay Time", AIC32X4_LAGC5, AIC32X4_RAGC5,
+                       3, 0x1F, 0),
+       SOC_DOUBLE_R("AGC Noise Debounce", AIC32X4_LAGC6, AIC32X4_RAGC6,
+                       0, 0x1F, 0),
+       SOC_DOUBLE_R("AGC Signal Debounce", AIC32X4_LAGC7, AIC32X4_RAGC7,
+                       0, 0x0F, 0),
+};
+
+static const struct aic32x4_rate_divs aic32x4_divs[] = {
+       /* 8k rate */
+       {AIC32X4_FREQ_12000000, 8000, 1, 7, 6800, 768, 5, 3, 128, 5, 18, 24},
+       {AIC32X4_FREQ_24000000, 8000, 2, 7, 6800, 768, 15, 1, 64, 45, 4, 24},
+       {AIC32X4_FREQ_25000000, 8000, 2, 7, 3728, 768, 15, 1, 64, 45, 4, 24},
+       /* 11.025k rate */
+       {AIC32X4_FREQ_12000000, 11025, 1, 7, 5264, 512, 8, 2, 128, 8, 8, 16},
+       {AIC32X4_FREQ_24000000, 11025, 2, 7, 5264, 512, 16, 1, 64, 32, 4, 16},
+       /* 16k rate */
+       {AIC32X4_FREQ_12000000, 16000, 1, 7, 6800, 384, 5, 3, 128, 5, 9, 12},
+       {AIC32X4_FREQ_24000000, 16000, 2, 7, 6800, 384, 15, 1, 64, 18, 5, 12},
+       {AIC32X4_FREQ_25000000, 16000, 2, 7, 3728, 384, 15, 1, 64, 18, 5, 12},
+       /* 22.05k rate */
+       {AIC32X4_FREQ_12000000, 22050, 1, 7, 5264, 256, 4, 4, 128, 4, 8, 8},
+       {AIC32X4_FREQ_24000000, 22050, 2, 7, 5264, 256, 16, 1, 64, 16, 4, 8},
+       {AIC32X4_FREQ_25000000, 22050, 2, 7, 2253, 256, 16, 1, 64, 16, 4, 8},
+       /* 32k rate */
+       {AIC32X4_FREQ_12000000, 32000, 1, 7, 1680, 192, 2, 7, 64, 2, 21, 6},
+       {AIC32X4_FREQ_24000000, 32000, 2, 7, 1680, 192, 7, 2, 64, 7, 6, 6},
+       /* 44.1k rate */
+       {AIC32X4_FREQ_12000000, 44100, 1, 7, 5264, 128, 2, 8, 128, 2, 8, 4},
+       {AIC32X4_FREQ_24000000, 44100, 2, 7, 5264, 128, 8, 2, 64, 8, 4, 4},
+       {AIC32X4_FREQ_25000000, 44100, 2, 7, 2253, 128, 8, 2, 64, 8, 4, 4},
+       /* 48k rate */
+       {AIC32X4_FREQ_12000000, 48000, 1, 8, 1920, 128, 2, 8, 128, 2, 8, 4},
+       {AIC32X4_FREQ_24000000, 48000, 2, 8, 1920, 128, 8, 2, 64, 8, 4, 4},
+       {AIC32X4_FREQ_25000000, 48000, 2, 7, 8643, 128, 8, 2, 64, 8, 4, 4}
+};
+
+static const struct snd_kcontrol_new hpl_output_mixer_controls[] = {
+       SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_HPLROUTE, 3, 1, 0),
+       SOC_DAPM_SINGLE("IN1_L Switch", AIC32X4_HPLROUTE, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new hpr_output_mixer_controls[] = {
+       SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_HPRROUTE, 3, 1, 0),
+       SOC_DAPM_SINGLE("IN1_R Switch", AIC32X4_HPRROUTE, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new lol_output_mixer_controls[] = {
+       SOC_DAPM_SINGLE("L_DAC Switch", AIC32X4_LOLROUTE, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new lor_output_mixer_controls[] = {
+       SOC_DAPM_SINGLE("R_DAC Switch", AIC32X4_LORROUTE, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new left_input_mixer_controls[] = {
+       SOC_DAPM_SINGLE("IN1_L P Switch", AIC32X4_LMICPGAPIN, 6, 1, 0),
+       SOC_DAPM_SINGLE("IN2_L P Switch", AIC32X4_LMICPGAPIN, 4, 1, 0),
+       SOC_DAPM_SINGLE("IN3_L P Switch", AIC32X4_LMICPGAPIN, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new right_input_mixer_controls[] = {
+       SOC_DAPM_SINGLE("IN1_R P Switch", AIC32X4_RMICPGAPIN, 6, 1, 0),
+       SOC_DAPM_SINGLE("IN2_R P Switch", AIC32X4_RMICPGAPIN, 4, 1, 0),
+       SOC_DAPM_SINGLE("IN3_R P Switch", AIC32X4_RMICPGAPIN, 2, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = {
+       SND_SOC_DAPM_DAC("Left DAC", "Left Playback", AIC32X4_DACSETUP, 7, 0),
+       SND_SOC_DAPM_MIXER("HPL Output Mixer", SND_SOC_NOPM, 0, 0,
+                          &hpl_output_mixer_controls[0],
+                          ARRAY_SIZE(hpl_output_mixer_controls)),
+       SND_SOC_DAPM_PGA("HPL Power", AIC32X4_OUTPWRCTL, 5, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("LOL Output Mixer", SND_SOC_NOPM, 0, 0,
+                          &lol_output_mixer_controls[0],
+                          ARRAY_SIZE(lol_output_mixer_controls)),
+       SND_SOC_DAPM_PGA("LOL Power", AIC32X4_OUTPWRCTL, 3, 0, NULL, 0),
+
+       SND_SOC_DAPM_DAC("Right DAC", "Right Playback", AIC32X4_DACSETUP, 6, 0),
+       SND_SOC_DAPM_MIXER("HPR Output Mixer", SND_SOC_NOPM, 0, 0,
+                          &hpr_output_mixer_controls[0],
+                          ARRAY_SIZE(hpr_output_mixer_controls)),
+       SND_SOC_DAPM_PGA("HPR Power", AIC32X4_OUTPWRCTL, 4, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("LOR Output Mixer", SND_SOC_NOPM, 0, 0,
+                          &lor_output_mixer_controls[0],
+                          ARRAY_SIZE(lor_output_mixer_controls)),
+       SND_SOC_DAPM_PGA("LOR Power", AIC32X4_OUTPWRCTL, 2, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("Left Input Mixer", SND_SOC_NOPM, 0, 0,
+                          &left_input_mixer_controls[0],
+                          ARRAY_SIZE(left_input_mixer_controls)),
+       SND_SOC_DAPM_MIXER("Right Input Mixer", SND_SOC_NOPM, 0, 0,
+                          &right_input_mixer_controls[0],
+                          ARRAY_SIZE(right_input_mixer_controls)),
+       SND_SOC_DAPM_ADC("Left ADC", "Left Capture", AIC32X4_ADCSETUP, 7, 0),
+       SND_SOC_DAPM_ADC("Right ADC", "Right Capture", AIC32X4_ADCSETUP, 6, 0),
+       SND_SOC_DAPM_MICBIAS("Mic Bias", AIC32X4_MICBIAS, 6, 0),
+
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+       SND_SOC_DAPM_OUTPUT("LOL"),
+       SND_SOC_DAPM_OUTPUT("LOR"),
+       SND_SOC_DAPM_INPUT("IN1_L"),
+       SND_SOC_DAPM_INPUT("IN1_R"),
+       SND_SOC_DAPM_INPUT("IN2_L"),
+       SND_SOC_DAPM_INPUT("IN2_R"),
+       SND_SOC_DAPM_INPUT("IN3_L"),
+       SND_SOC_DAPM_INPUT("IN3_R"),
+};
+
+static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = {
+       /* Left Output */
+       {"HPL Output Mixer", "L_DAC Switch", "Left DAC"},
+       {"HPL Output Mixer", "IN1_L Switch", "IN1_L"},
+
+       {"HPL Power", NULL, "HPL Output Mixer"},
+       {"HPL", NULL, "HPL Power"},
+
+       {"LOL Output Mixer", "L_DAC Switch", "Left DAC"},
+
+       {"LOL Power", NULL, "LOL Output Mixer"},
+       {"LOL", NULL, "LOL Power"},
+
+       /* Right Output */
+       {"HPR Output Mixer", "R_DAC Switch", "Right DAC"},
+       {"HPR Output Mixer", "IN1_R Switch", "IN1_R"},
+
+       {"HPR Power", NULL, "HPR Output Mixer"},
+       {"HPR", NULL, "HPR Power"},
+
+       {"LOR Output Mixer", "R_DAC Switch", "Right DAC"},
+
+       {"LOR Power", NULL, "LOR Output Mixer"},
+       {"LOR", NULL, "LOR Power"},
+
+       /* Left input */
+       {"Left Input Mixer", "IN1_L P Switch", "IN1_L"},
+       {"Left Input Mixer", "IN2_L P Switch", "IN2_L"},
+       {"Left Input Mixer", "IN3_L P Switch", "IN3_L"},
+
+       {"Left ADC", NULL, "Left Input Mixer"},
+
+       /* Right Input */
+       {"Right Input Mixer", "IN1_R P Switch", "IN1_R"},
+       {"Right Input Mixer", "IN2_R P Switch", "IN2_R"},
+       {"Right Input Mixer", "IN3_R P Switch", "IN3_R"},
+
+       {"Right ADC", NULL, "Right Input Mixer"},
+};
+
+static inline int aic32x4_change_page(struct snd_soc_codec *codec,
+                                       unsigned int new_page)
+{
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       u8 data[2];
+       int ret;
+
+       data[0] = 0x00;
+       data[1] = new_page & 0xff;
+
+       ret = codec->hw_write(codec->control_data, data, 2);
+       if (ret == 2) {
+               aic32x4->page_no = new_page;
+               return 0;
+       } else {
+               return ret;
+       }
+}
+
+static int aic32x4_write(struct snd_soc_codec *codec, unsigned int reg,
+                               unsigned int val)
+{
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       unsigned int page = reg / 128;
+       unsigned int fixed_reg = reg % 128;
+       u8 data[2];
+       int ret;
+
+       /* A write to AIC32X4_PSEL is really a non-explicit page change */
+       if (reg == AIC32X4_PSEL)
+               return aic32x4_change_page(codec, val);
+
+       if (aic32x4->page_no != page) {
+               ret = aic32x4_change_page(codec, page);
+               if (ret != 0)
+                       return ret;
+       }
+
+       data[0] = fixed_reg & 0xff;
+       data[1] = val & 0xff;
+
+       if (codec->hw_write(codec->control_data, data, 2) == 2)
+               return 0;
+       else
+               return -EIO;
+}
+
+static unsigned int aic32x4_read(struct snd_soc_codec *codec, unsigned int reg)
+{
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       unsigned int page = reg / 128;
+       unsigned int fixed_reg = reg % 128;
+       int ret;
+
+       if (aic32x4->page_no != page) {
+               ret = aic32x4_change_page(codec, page);
+               if (ret != 0)
+                       return ret;
+       }
+       return i2c_smbus_read_byte_data(codec->control_data, fixed_reg & 0xff);
+}
+
+static inline int aic32x4_get_divs(int mclk, int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(aic32x4_divs); i++) {
+               if ((aic32x4_divs[i].rate == rate)
+                   && (aic32x4_divs[i].mclk == mclk)) {
+                       return i;
+               }
+       }
+       printk(KERN_ERR "aic32x4: master clock and sample rate is not supported\n");
+       return -EINVAL;
+}
+
+static int aic32x4_add_widgets(struct snd_soc_codec *codec)
+{
+       snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets,
+                                 ARRAY_SIZE(aic32x4_dapm_widgets));
+
+       snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes,
+                               ARRAY_SIZE(aic32x4_dapm_routes));
+
+       snd_soc_dapm_new_widgets(&codec->dapm);
+       return 0;
+}
+
+static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+                                 int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+
+       switch (freq) {
+       case AIC32X4_FREQ_12000000:
+       case AIC32X4_FREQ_24000000:
+       case AIC32X4_FREQ_25000000:
+               aic32x4->sysclk = freq;
+               return 0;
+       }
+       printk(KERN_ERR "aic32x4: invalid frequency to set DAI system clock\n");
+       return -EINVAL;
+}
+
+static int aic32x4_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       u8 iface_reg_1;
+       u8 iface_reg_2;
+       u8 iface_reg_3;
+
+       iface_reg_1 = snd_soc_read(codec, AIC32X4_IFACE1);
+       iface_reg_1 = iface_reg_1 & ~(3 << 6 | 3 << 2);
+       iface_reg_2 = snd_soc_read(codec, AIC32X4_IFACE2);
+       iface_reg_2 = 0;
+       iface_reg_3 = snd_soc_read(codec, AIC32X4_IFACE3);
+       iface_reg_3 = iface_reg_3 & ~(1 << 3);
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               aic32x4->master = 1;
+               iface_reg_1 |= AIC32X4_BCLKMASTER | AIC32X4_WCLKMASTER;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               aic32x4->master = 0;
+               break;
+       default:
+               printk(KERN_ERR "aic32x4: invalid DAI master/slave interface\n");
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT);
+               iface_reg_3 |= (1 << 3); /* invert bit clock */
+               iface_reg_2 = 0x01; /* add offset 1 */
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               iface_reg_1 |= (AIC32X4_DSP_MODE << AIC32X4_PLLJ_SHIFT);
+               iface_reg_3 |= (1 << 3); /* invert bit clock */
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               iface_reg_1 |=
+                       (AIC32X4_RIGHT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT);
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               iface_reg_1 |=
+                       (AIC32X4_LEFT_JUSTIFIED_MODE << AIC32X4_PLLJ_SHIFT);
+               break;
+       default:
+               printk(KERN_ERR "aic32x4: invalid DAI interface format\n");
+               return -EINVAL;
+       }
+
+       snd_soc_write(codec, AIC32X4_IFACE1, iface_reg_1);
+       snd_soc_write(codec, AIC32X4_IFACE2, iface_reg_2);
+       snd_soc_write(codec, AIC32X4_IFACE3, iface_reg_3);
+       return 0;
+}
+
+static int aic32x4_hw_params(struct snd_pcm_substream *substream,
+                            struct snd_pcm_hw_params *params,
+                            struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       u8 data;
+       int i;
+
+       i = aic32x4_get_divs(aic32x4->sysclk, params_rate(params));
+       if (i < 0) {
+               printk(KERN_ERR "aic32x4: sampling rate not supported\n");
+               return i;
+       }
+
+       /* Use PLL as CODEC_CLKIN and DAC_MOD_CLK as BDIV_CLKIN */
+       snd_soc_write(codec, AIC32X4_CLKMUX, AIC32X4_PLLCLKIN);
+       snd_soc_write(codec, AIC32X4_IFACE3, AIC32X4_DACMOD2BCLK);
+
+       /* We will fix R value to 1 and will make P & J=K.D as varialble */
+       data = snd_soc_read(codec, AIC32X4_PLLPR);
+       data &= ~(7 << 4);
+       snd_soc_write(codec, AIC32X4_PLLPR,
+                     (data | (aic32x4_divs[i].p_val << 4) | 0x01));
+
+       snd_soc_write(codec, AIC32X4_PLLJ, aic32x4_divs[i].pll_j);
+
+       snd_soc_write(codec, AIC32X4_PLLDMSB, (aic32x4_divs[i].pll_d >> 8));
+       snd_soc_write(codec, AIC32X4_PLLDLSB,
+                     (aic32x4_divs[i].pll_d & 0xff));
+
+       /* NDAC divider value */
+       data = snd_soc_read(codec, AIC32X4_NDAC);
+       data &= ~(0x7f);
+       snd_soc_write(codec, AIC32X4_NDAC, data | aic32x4_divs[i].ndac);
+
+       /* MDAC divider value */
+       data = snd_soc_read(codec, AIC32X4_MDAC);
+       data &= ~(0x7f);
+       snd_soc_write(codec, AIC32X4_MDAC, data | aic32x4_divs[i].mdac);
+
+       /* DOSR MSB & LSB values */
+       snd_soc_write(codec, AIC32X4_DOSRMSB, aic32x4_divs[i].dosr >> 8);
+       snd_soc_write(codec, AIC32X4_DOSRLSB,
+                     (aic32x4_divs[i].dosr & 0xff));
+
+       /* NADC divider value */
+       data = snd_soc_read(codec, AIC32X4_NADC);
+       data &= ~(0x7f);
+       snd_soc_write(codec, AIC32X4_NADC, data | aic32x4_divs[i].nadc);
+
+       /* MADC divider value */
+       data = snd_soc_read(codec, AIC32X4_MADC);
+       data &= ~(0x7f);
+       snd_soc_write(codec, AIC32X4_MADC, data | aic32x4_divs[i].madc);
+
+       /* AOSR value */
+       snd_soc_write(codec, AIC32X4_AOSR, aic32x4_divs[i].aosr);
+
+       /* BCLK N divider */
+       data = snd_soc_read(codec, AIC32X4_BCLKN);
+       data &= ~(0x7f);
+       snd_soc_write(codec, AIC32X4_BCLKN, data | aic32x4_divs[i].blck_N);
+
+       data = snd_soc_read(codec, AIC32X4_IFACE1);
+       data = data & ~(3 << 4);
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               data |= (AIC32X4_WORD_LEN_20BITS << AIC32X4_DOSRMSB_SHIFT);
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               data |= (AIC32X4_WORD_LEN_24BITS << AIC32X4_DOSRMSB_SHIFT);
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               data |= (AIC32X4_WORD_LEN_32BITS << AIC32X4_DOSRMSB_SHIFT);
+               break;
+       }
+       snd_soc_write(codec, AIC32X4_IFACE1, data);
+
+       return 0;
+}
+
+static int aic32x4_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       u8 dac_reg;
+
+       dac_reg = snd_soc_read(codec, AIC32X4_DACMUTE) & ~AIC32X4_MUTEON;
+       if (mute)
+               snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg | AIC32X4_MUTEON);
+       else
+               snd_soc_write(codec, AIC32X4_DACMUTE, dac_reg);
+       return 0;
+}
+
+static int aic32x4_set_bias_level(struct snd_soc_codec *codec,
+                                 enum snd_soc_bias_level level)
+{
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       u8 value;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               if (aic32x4->master) {
+                       /* Switch on PLL */
+                       value = snd_soc_read(codec, AIC32X4_PLLPR);
+                       snd_soc_write(codec, AIC32X4_PLLPR,
+                                     (value | AIC32X4_PLLEN));
+
+                       /* Switch on NDAC Divider */
+                       value = snd_soc_read(codec, AIC32X4_NDAC);
+                       snd_soc_write(codec, AIC32X4_NDAC,
+                                     value | AIC32X4_NDACEN);
+
+                       /* Switch on MDAC Divider */
+                       value = snd_soc_read(codec, AIC32X4_MDAC);
+                       snd_soc_write(codec, AIC32X4_MDAC,
+                                     value | AIC32X4_MDACEN);
+
+                       /* Switch on NADC Divider */
+                       value = snd_soc_read(codec, AIC32X4_NADC);
+                       snd_soc_write(codec, AIC32X4_NADC,
+                                     value | AIC32X4_MDACEN);
+
+                       /* Switch on MADC Divider */
+                       value = snd_soc_read(codec, AIC32X4_MADC);
+                       snd_soc_write(codec, AIC32X4_MADC,
+                                     value | AIC32X4_MDACEN);
+
+                       /* Switch on BCLK_N Divider */
+                       value = snd_soc_read(codec, AIC32X4_BCLKN);
+                       snd_soc_write(codec, AIC32X4_BCLKN,
+                                     value | AIC32X4_BCLKEN);
+               }
+               break;
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               if (aic32x4->master) {
+                       /* Switch off PLL */
+                       value = snd_soc_read(codec, AIC32X4_PLLPR);
+                       snd_soc_write(codec, AIC32X4_PLLPR,
+                                     (value & ~AIC32X4_PLLEN));
+
+                       /* Switch off NDAC Divider */
+                       value = snd_soc_read(codec, AIC32X4_NDAC);
+                       snd_soc_write(codec, AIC32X4_NDAC,
+                                     value & ~AIC32X4_NDACEN);
+
+                       /* Switch off MDAC Divider */
+                       value = snd_soc_read(codec, AIC32X4_MDAC);
+                       snd_soc_write(codec, AIC32X4_MDAC,
+                                     value & ~AIC32X4_MDACEN);
+
+                       /* Switch off NADC Divider */
+                       value = snd_soc_read(codec, AIC32X4_NADC);
+                       snd_soc_write(codec, AIC32X4_NADC,
+                                     value & ~AIC32X4_NDACEN);
+
+                       /* Switch off MADC Divider */
+                       value = snd_soc_read(codec, AIC32X4_MADC);
+                       snd_soc_write(codec, AIC32X4_MADC,
+                                     value & ~AIC32X4_MDACEN);
+                       value = snd_soc_read(codec, AIC32X4_BCLKN);
+
+                       /* Switch off BCLK_N Divider */
+                       snd_soc_write(codec, AIC32X4_BCLKN,
+                                     value & ~AIC32X4_BCLKEN);
+               }
+               break;
+       case SND_SOC_BIAS_OFF:
+               break;
+       }
+       codec->dapm.bias_level = level;
+       return 0;
+}
+
+#define AIC32X4_RATES  SNDRV_PCM_RATE_8000_48000
+#define AIC32X4_FORMATS        (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+                        | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops aic32x4_ops = {
+       .hw_params = aic32x4_hw_params,
+       .digital_mute = aic32x4_mute,
+       .set_fmt = aic32x4_set_dai_fmt,
+       .set_sysclk = aic32x4_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver aic32x4_dai = {
+       .name = "tlv320aic32x4-hifi",
+       .playback = {
+                    .stream_name = "Playback",
+                    .channels_min = 1,
+                    .channels_max = 2,
+                    .rates = AIC32X4_RATES,
+                    .formats = AIC32X4_FORMATS,},
+       .capture = {
+                   .stream_name = "Capture",
+                   .channels_min = 1,
+                   .channels_max = 2,
+                   .rates = AIC32X4_RATES,
+                   .formats = AIC32X4_FORMATS,},
+       .ops = &aic32x4_ops,
+       .symmetric_rates = 1,
+};
+
+static int aic32x4_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+       aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static int aic32x4_resume(struct snd_soc_codec *codec)
+{
+       aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       return 0;
+}
+
+static int aic32x4_probe(struct snd_soc_codec *codec)
+{
+       struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
+       u32 tmp_reg;
+
+       codec->hw_write = (hw_write_t) i2c_master_send;
+       codec->control_data = aic32x4->control_data;
+
+       snd_soc_write(codec, AIC32X4_RESET, 0x01);
+
+       /* Power platform configuration */
+       if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
+               snd_soc_write(codec, AIC32X4_MICBIAS, AIC32X4_MICBIAS_LDOIN |
+                                                     AIC32X4_MICBIAS_2075V);
+       }
+       if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE) {
+               snd_soc_write(codec, AIC32X4_PWRCFG, AIC32X4_AVDDWEAKDISABLE);
+       }
+       if (aic32x4->power_cfg & AIC32X4_PWR_AIC32X4_LDO_ENABLE) {
+               snd_soc_write(codec, AIC32X4_LDOCTL, AIC32X4_LDOCTLEN);
+       }
+       tmp_reg = snd_soc_read(codec, AIC32X4_CMMODE);
+       if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_LDOIN_RANGE_18_36) {
+               tmp_reg |= AIC32X4_LDOIN_18_36;
+       }
+       if (aic32x4->power_cfg & AIC32X4_PWR_CMMODE_HP_LDOIN_POWERED) {
+               tmp_reg |= AIC32X4_LDOIN2HP;
+       }
+       snd_soc_write(codec, AIC32X4_CMMODE, tmp_reg);
+
+       /* Do DACs need to be swapped? */
+       if (aic32x4->swapdacs) {
+               snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2RCHN | AIC32X4_RDAC2LCHN);
+       } else {
+               snd_soc_write(codec, AIC32X4_DACSETUP, AIC32X4_LDAC2LCHN | AIC32X4_RDAC2RCHN);
+       }
+
+       /* Mic PGA routing */
+       if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_LMIC_IN2R_10K) {
+               snd_soc_write(codec, AIC32X4_LMICPGANIN, AIC32X4_LMICPGANIN_IN2R_10K);
+       }
+       if (aic32x4->micpga_routing | AIC32X4_MICPGA_ROUTE_RMIC_IN1L_10K) {
+               snd_soc_write(codec, AIC32X4_RMICPGANIN, AIC32X4_RMICPGANIN_IN1L_10K);
+       }
+
+       aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       snd_soc_add_controls(codec, aic32x4_snd_controls,
+                            ARRAY_SIZE(aic32x4_snd_controls));
+       aic32x4_add_widgets(codec);
+
+       return 0;
+}
+
+static int aic32x4_remove(struct snd_soc_codec *codec)
+{
+       aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF);
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
+       .read = aic32x4_read,
+       .write = aic32x4_write,
+       .probe = aic32x4_probe,
+       .remove = aic32x4_remove,
+       .suspend = aic32x4_suspend,
+       .resume = aic32x4_resume,
+       .set_bias_level = aic32x4_set_bias_level,
+};
+
+static __devinit int aic32x4_i2c_probe(struct i2c_client *i2c,
+                                     const struct i2c_device_id *id)
+{
+       struct aic32x4_pdata *pdata = i2c->dev.platform_data;
+       struct aic32x4_priv *aic32x4;
+       int ret;
+
+       aic32x4 = kzalloc(sizeof(struct aic32x4_priv), GFP_KERNEL);
+       if (aic32x4 == NULL)
+               return -ENOMEM;
+
+       aic32x4->control_data = i2c;
+       i2c_set_clientdata(i2c, aic32x4);
+
+       if (pdata) {
+               aic32x4->power_cfg = pdata->power_cfg;
+               aic32x4->swapdacs = pdata->swapdacs;
+               aic32x4->micpga_routing = pdata->micpga_routing;
+       } else {
+               aic32x4->power_cfg = 0;
+               aic32x4->swapdacs = false;
+               aic32x4->micpga_routing = 0;
+       }
+
+       ret = snd_soc_register_codec(&i2c->dev,
+                       &soc_codec_dev_aic32x4, &aic32x4_dai, 1);
+       if (ret < 0)
+               kfree(aic32x4);
+       return ret;
+}
+
+static __devexit int aic32x4_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       kfree(i2c_get_clientdata(client));
+       return 0;
+}
+
+static const struct i2c_device_id aic32x4_i2c_id[] = {
+       { "tlv320aic32x4", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
+
+static struct i2c_driver aic32x4_i2c_driver = {
+       .driver = {
+               .name = "tlv320aic32x4",
+               .owner = THIS_MODULE,
+       },
+       .probe =    aic32x4_i2c_probe,
+       .remove =   __devexit_p(aic32x4_i2c_remove),
+       .id_table = aic32x4_i2c_id,
+};
+
+static int __init aic32x4_modinit(void)
+{
+       int ret = 0;
+
+       ret = i2c_add_driver(&aic32x4_i2c_driver);
+       if (ret != 0) {
+               printk(KERN_ERR "Failed to register aic32x4 I2C driver: %d\n",
+                      ret);
+       }
+       return ret;
+}
+module_init(aic32x4_modinit);
+
+static void __exit aic32x4_exit(void)
+{
+       i2c_del_driver(&aic32x4_i2c_driver);
+}
+module_exit(aic32x4_exit);
+
+MODULE_DESCRIPTION("ASoC tlv320aic32x4 codec driver");
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h
new file mode 100644 (file)
index 0000000..aae2b24
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * tlv320aic32x4.h
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#ifndef _TLV320AIC32X4_H
+#define _TLV320AIC32X4_H
+
+/* tlv320aic32x4 register space (in decimal to match datasheet) */
+
+#define AIC32X4_PAGE1          128
+
+#define        AIC32X4_PSEL            0
+#define        AIC32X4_RESET           1
+#define        AIC32X4_CLKMUX          4
+#define        AIC32X4_PLLPR           5
+#define        AIC32X4_PLLJ            6
+#define        AIC32X4_PLLDMSB         7
+#define        AIC32X4_PLLDLSB         8
+#define        AIC32X4_NDAC            11
+#define        AIC32X4_MDAC            12
+#define AIC32X4_DOSRMSB                13
+#define AIC32X4_DOSRLSB                14
+#define        AIC32X4_NADC            18
+#define        AIC32X4_MADC            19
+#define AIC32X4_AOSR           20
+#define AIC32X4_CLKMUX2                25
+#define AIC32X4_CLKOUTM                26
+#define AIC32X4_IFACE1         27
+#define AIC32X4_IFACE2         28
+#define AIC32X4_IFACE3         29
+#define AIC32X4_BCLKN          30
+#define AIC32X4_IFACE4         31
+#define AIC32X4_IFACE5         32
+#define AIC32X4_IFACE6         33
+#define AIC32X4_DOUTCTL                53
+#define AIC32X4_DINCTL         54
+#define AIC32X4_DACSPB         60
+#define AIC32X4_ADCSPB         61
+#define AIC32X4_DACSETUP       63
+#define AIC32X4_DACMUTE                64
+#define AIC32X4_LDACVOL                65
+#define AIC32X4_RDACVOL                66
+#define AIC32X4_ADCSETUP       81
+#define        AIC32X4_ADCFGA          82
+#define AIC32X4_LADCVOL                83
+#define AIC32X4_RADCVOL                84
+#define AIC32X4_LAGC1          86
+#define AIC32X4_LAGC2          87
+#define AIC32X4_LAGC3          88
+#define AIC32X4_LAGC4          89
+#define AIC32X4_LAGC5          90
+#define AIC32X4_LAGC6          91
+#define AIC32X4_LAGC7          92
+#define AIC32X4_RAGC1          94
+#define AIC32X4_RAGC2          95
+#define AIC32X4_RAGC3          96
+#define AIC32X4_RAGC4          97
+#define AIC32X4_RAGC5          98
+#define AIC32X4_RAGC6          99
+#define AIC32X4_RAGC7          100
+#define AIC32X4_PWRCFG         (AIC32X4_PAGE1 + 1)
+#define AIC32X4_LDOCTL         (AIC32X4_PAGE1 + 2)
+#define AIC32X4_OUTPWRCTL      (AIC32X4_PAGE1 + 9)
+#define AIC32X4_CMMODE         (AIC32X4_PAGE1 + 10)
+#define AIC32X4_HPLROUTE       (AIC32X4_PAGE1 + 12)
+#define AIC32X4_HPRROUTE       (AIC32X4_PAGE1 + 13)
+#define AIC32X4_LOLROUTE       (AIC32X4_PAGE1 + 14)
+#define AIC32X4_LORROUTE       (AIC32X4_PAGE1 + 15)
+#define        AIC32X4_HPLGAIN         (AIC32X4_PAGE1 + 16)
+#define        AIC32X4_HPRGAIN         (AIC32X4_PAGE1 + 17)
+#define        AIC32X4_LOLGAIN         (AIC32X4_PAGE1 + 18)
+#define        AIC32X4_LORGAIN         (AIC32X4_PAGE1 + 19)
+#define AIC32X4_HEADSTART      (AIC32X4_PAGE1 + 20)
+#define AIC32X4_MICBIAS                (AIC32X4_PAGE1 + 51)
+#define AIC32X4_LMICPGAPIN     (AIC32X4_PAGE1 + 52)
+#define AIC32X4_LMICPGANIN     (AIC32X4_PAGE1 + 54)
+#define AIC32X4_RMICPGAPIN     (AIC32X4_PAGE1 + 55)
+#define AIC32X4_RMICPGANIN     (AIC32X4_PAGE1 + 57)
+#define AIC32X4_FLOATINGINPUT  (AIC32X4_PAGE1 + 58)
+#define AIC32X4_LMICPGAVOL     (AIC32X4_PAGE1 + 59)
+#define AIC32X4_RMICPGAVOL     (AIC32X4_PAGE1 + 60)
+
+#define AIC32X4_FREQ_12000000 12000000
+#define AIC32X4_FREQ_24000000 24000000
+#define AIC32X4_FREQ_25000000 25000000
+
+#define AIC32X4_WORD_LEN_16BITS                0x00
+#define AIC32X4_WORD_LEN_20BITS                0x01
+#define AIC32X4_WORD_LEN_24BITS                0x02
+#define AIC32X4_WORD_LEN_32BITS                0x03
+
+#define AIC32X4_I2S_MODE               0x00
+#define AIC32X4_DSP_MODE               0x01
+#define AIC32X4_RIGHT_JUSTIFIED_MODE   0x02
+#define AIC32X4_LEFT_JUSTIFIED_MODE    0x03
+
+#define AIC32X4_AVDDWEAKDISABLE                0x08
+#define AIC32X4_LDOCTLEN               0x01
+
+#define AIC32X4_LDOIN_18_36            0x01
+#define AIC32X4_LDOIN2HP               0x02
+
+#define AIC32X4_DACSPBLOCK_MASK                0x1f
+#define AIC32X4_ADCSPBLOCK_MASK                0x1f
+
+#define AIC32X4_PLLJ_SHIFT             6
+#define AIC32X4_DOSRMSB_SHIFT          4
+
+#define AIC32X4_PLLCLKIN               0x03
+
+#define AIC32X4_MICBIAS_LDOIN          0x08
+#define AIC32X4_MICBIAS_2075V          0x60
+
+#define AIC32X4_LMICPGANIN_IN2R_10K    0x10
+#define AIC32X4_RMICPGANIN_IN1L_10K    0x10
+
+#define AIC32X4_LMICPGAVOL_NOGAIN      0x80
+#define AIC32X4_RMICPGAVOL_NOGAIN      0x80
+
+#define AIC32X4_BCLKMASTER             0x08
+#define AIC32X4_WCLKMASTER             0x04
+#define AIC32X4_PLLEN                  (0x01 << 7)
+#define AIC32X4_NDACEN                 (0x01 << 7)
+#define AIC32X4_MDACEN                 (0x01 << 7)
+#define AIC32X4_NADCEN                 (0x01 << 7)
+#define AIC32X4_MADCEN                 (0x01 << 7)
+#define AIC32X4_BCLKEN                 (0x01 << 7)
+#define AIC32X4_DACEN                  (0x03 << 6)
+#define AIC32X4_RDAC2LCHN              (0x02 << 2)
+#define AIC32X4_LDAC2RCHN              (0x02 << 4)
+#define AIC32X4_LDAC2LCHN              (0x01 << 4)
+#define AIC32X4_RDAC2RCHN              (0x01 << 2)
+
+#define AIC32X4_SSTEP2WCLK             0x01
+#define AIC32X4_MUTEON                 0x0C
+#define        AIC32X4_DACMOD2BCLK             0x01
+
+#endif                         /* _TLV320AIC32X4_H */
index 71d7be8..00b6d87 100644 (file)
@@ -1615,6 +1615,7 @@ static const struct i2c_device_id tlv320dac33_i2c_id[] = {
        },
        { },
 };
+MODULE_DEVICE_TABLE(i2c, tlv320dac33_i2c_id);
 
 static struct i2c_driver tlv320dac33_i2c_driver = {
        .driver = {
index 4bbf1b1..482fcdb 100644 (file)
@@ -724,8 +724,8 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
-void twl6040_hs_jack_report(struct snd_soc_codec *codec,
-                               struct snd_soc_jack *jack, int report)
+static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
+                                  struct snd_soc_jack *jack, int report)
 {
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
        int status;
index 80ddf4f..a3b9cbb 100644 (file)
@@ -836,24 +836,25 @@ static void wm2000_i2c_shutdown(struct i2c_client *i2c)
 }
 
 #ifdef CONFIG_PM
-static int wm2000_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg)
+static int wm2000_i2c_suspend(struct device *dev)
 {
+       struct i2c_client *i2c = to_i2c_client(dev);
        struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
 
        return wm2000_anc_transition(wm2000, ANC_OFF);
 }
 
-static int wm2000_i2c_resume(struct i2c_client *i2c)
+static int wm2000_i2c_resume(struct device *dev)
 {
+       struct i2c_client *i2c = to_i2c_client(dev);
        struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev);
 
        return wm2000_anc_set_mode(wm2000);
 }
-#else
-#define wm2000_i2c_suspend NULL
-#define wm2000_i2c_resume NULL
 #endif
 
+static SIMPLE_DEV_PM_OPS(wm2000_pm, wm2000_i2c_suspend, wm2000_i2c_resume);
+
 static const struct i2c_device_id wm2000_i2c_id[] = {
        { "wm2000", 0 },
        { }
@@ -864,11 +865,10 @@ static struct i2c_driver wm2000_i2c_driver = {
        .driver = {
                .name = "wm2000",
                .owner = THIS_MODULE,
+               .pm = &wm2000_pm,
        },
        .probe = wm2000_i2c_probe,
        .remove = __devexit_p(wm2000_i2c_remove),
-       .suspend = wm2000_i2c_suspend,
-       .resume = wm2000_i2c_resume,
        .shutdown = wm2000_i2c_shutdown,
        .id_table = wm2000_i2c_id,
 };
index 5eb2f50..4fd4d8d 100644 (file)
@@ -58,7 +58,7 @@ static const u16 wm8523_reg[WM8523_REGISTER_COUNT] = {
        0x0000,     /* R8 - ZERO_DETECT */
 };
 
-static int wm8523_volatile_register(unsigned int reg)
+static int wm8523_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM8523_DEVICE_ID:
@@ -414,7 +414,6 @@ static int wm8523_resume(struct snd_soc_codec *codec)
 static int wm8523_probe(struct snd_soc_codec *codec)
 {
        struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
-       u16 *reg_cache = codec->reg_cache;
        int ret, i;
 
        codec->hw_write = (hw_write_t)i2c_master_send;
@@ -471,8 +470,9 @@ static int wm8523_probe(struct snd_soc_codec *codec)
        }
 
        /* Change some default settings - latch VU and enable ZC */
-       reg_cache[WM8523_DAC_GAINR] |= WM8523_DACR_VU;
-       reg_cache[WM8523_DAC_CTRL3] |= WM8523_ZC;
+       snd_soc_update_bits(codec, WM8523_DAC_GAINR,
+                           WM8523_DACR_VU, WM8523_DACR_VU);
+       snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC);
 
        wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
index 494f2d3..25af901 100644 (file)
@@ -421,7 +421,6 @@ static int wm8741_resume(struct snd_soc_codec *codec)
 static int wm8741_probe(struct snd_soc_codec *codec)
 {
        struct wm8741_priv *wm8741 = snd_soc_codec_get_drvdata(codec);
-       u16 *reg_cache = codec->reg_cache;
        int ret = 0;
 
        ret = snd_soc_codec_set_cache_io(codec, 7, 9, wm8741->control_type);
@@ -437,10 +436,14 @@ static int wm8741_probe(struct snd_soc_codec *codec)
        }
 
        /* Change some default settings - latch VU */
-       reg_cache[WM8741_DACLLSB_ATTENUATION] |= WM8741_UPDATELL;
-       reg_cache[WM8741_DACLMSB_ATTENUATION] |= WM8741_UPDATELM;
-       reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERL;
-       reg_cache[WM8741_DACRLSB_ATTENUATION] |= WM8741_UPDATERM;
+       snd_soc_update_bits(codec, WM8741_DACLLSB_ATTENUATION,
+                           WM8741_UPDATELL, WM8741_UPDATELL);
+       snd_soc_update_bits(codec, WM8741_DACLMSB_ATTENUATION,
+                           WM8741_UPDATELM, WM8741_UPDATELM);
+       snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+                           WM8741_UPDATERL, WM8741_UPDATERL);
+       snd_soc_update_bits(codec, WM8741_DACRLSB_ATTENUATION,
+                           WM8741_UPDATERM, WM8741_UPDATERM);
 
        snd_soc_add_controls(codec, wm8741_snd_controls,
                             ARRAY_SIZE(wm8741_snd_controls));
index 79b02ae..3f09dee 100644 (file)
@@ -55,8 +55,10 @@ static int caps_charge = 2000;
 module_param(caps_charge, int, 0);
 MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
 
-static void wm8753_set_dai_mode(struct snd_soc_codec *codec,
-               struct snd_soc_dai *dai, unsigned int hifi);
+static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec,
+               unsigned int fmt);
+static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec,
+               unsigned int fmt);
 
 /*
  * wm8753 register cache
@@ -87,6 +89,10 @@ struct wm8753_priv {
        enum snd_soc_control_type control_type;
        unsigned int sysclk;
        unsigned int pcmclk;
+
+       unsigned int voice_fmt;
+       unsigned int hifi_fmt;
+
        int dai_func;
 };
 
@@ -170,9 +176,9 @@ static int wm8753_get_dai(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       int mode = snd_soc_read(codec, WM8753_IOCTL);
+       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
 
-       ucontrol->value.integer.value[0] = (mode & 0xc) >> 2;
+       ucontrol->value.integer.value[0] = wm8753->dai_func;
        return 0;
 }
 
@@ -180,16 +186,26 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       int mode = snd_soc_read(codec, WM8753_IOCTL);
        struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+       u16 ioctl;
+
+       if (codec->active)
+               return -EBUSY;
+
+       ioctl = snd_soc_read(codec, WM8753_IOCTL);
+
+       wm8753->dai_func = ucontrol->value.integer.value[0];
+
+       if (((ioctl >> 2) & 0x3) == wm8753->dai_func)
+               return 1;
+
+       ioctl = (ioctl & 0x1f3) | (wm8753->dai_func << 2);
+       snd_soc_write(codec, WM8753_IOCTL, ioctl);
 
-       if (((mode & 0xc) >> 2) == ucontrol->value.integer.value[0])
-               return 0;
 
-       mode &= 0xfff3;
-       mode |= (ucontrol->value.integer.value[0] << 2);
+       wm8753_hifi_write_dai_fmt(codec, wm8753->hifi_fmt);
+       wm8753_voice_write_dai_fmt(codec, wm8753->voice_fmt);
 
-       wm8753->dai_func =  ucontrol->value.integer.value[0];
        return 1;
 }
 
@@ -828,10 +844,9 @@ static int wm8753_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 /*
  * Set's ADC and Voice DAC format.
  */
-static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 voice = snd_soc_read(codec, WM8753_PCM) & 0x01ec;
 
        /* interface format */
@@ -858,13 +873,6 @@ static int wm8753_vdac_adc_set_dai_fmt(struct snd_soc_dai *codec_dai,
        return 0;
 }
 
-static int wm8753_pcm_startup(struct snd_pcm_substream *substream,
-                               struct snd_soc_dai *dai)
-{
-       wm8753_set_dai_mode(dai->codec, dai, 0);
-       return 0;
-}
-
 /*
  * Set PCM DAI bit size and sample rate.
  */
@@ -905,10 +913,9 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
 /*
  * Set's PCM dai fmt and BCLK.
  */
-static int wm8753_pcm_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_pcm_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 voice, ioctl;
 
        voice = snd_soc_read(codec, WM8753_PCM) & 0x011f;
@@ -999,10 +1006,9 @@ static int wm8753_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
 /*
  * Set's HiFi DAC format.
  */
-static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_hdac_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 hifi = snd_soc_read(codec, WM8753_HIFI) & 0x01e0;
 
        /* interface format */
@@ -1032,10 +1038,9 @@ static int wm8753_hdac_set_dai_fmt(struct snd_soc_dai *codec_dai,
 /*
  * Set's I2S DAI format.
  */
-static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_i2s_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 ioctl, hifi;
 
        hifi = snd_soc_read(codec, WM8753_HIFI) & 0x011f;
@@ -1098,13 +1103,6 @@ static int wm8753_i2s_set_dai_fmt(struct snd_soc_dai *codec_dai,
        return 0;
 }
 
-static int wm8753_i2s_startup(struct snd_pcm_substream *substream,
-                               struct snd_soc_dai *dai)
-{
-       wm8753_set_dai_mode(dai->codec, dai, 1);
-       return 0;
-}
-
 /*
  * Set PCM DAI bit size and sample rate.
  */
@@ -1147,61 +1145,117 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int wm8753_mode1v_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_mode1v_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 clock;
 
        /* set clk source as pcmclk */
        clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
        snd_soc_write(codec, WM8753_CLOCK, clock);
 
-       if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
-               return -EINVAL;
-       return wm8753_pcm_set_dai_fmt(codec_dai, fmt);
+       return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
 }
 
-static int wm8753_mode1h_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_mode1h_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
-               return -EINVAL;
-       return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
+       return wm8753_hdac_set_dai_fmt(codec, fmt);
 }
 
-static int wm8753_mode2_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_mode2_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 clock;
 
        /* set clk source as pcmclk */
        clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
        snd_soc_write(codec, WM8753_CLOCK, clock);
 
-       if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
-               return -EINVAL;
-       return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
+       return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
 }
 
-static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_dai *codec_dai,
+static int wm8753_mode3_4_set_dai_fmt(struct snd_soc_codec *codec,
                unsigned int fmt)
 {
-       struct snd_soc_codec *codec = codec_dai->codec;
        u16 clock;
 
        /* set clk source as mclk */
        clock = snd_soc_read(codec, WM8753_CLOCK) & 0xfffb;
        snd_soc_write(codec, WM8753_CLOCK, clock | 0x4);
 
-       if (wm8753_hdac_set_dai_fmt(codec_dai, fmt) < 0)
+       if (wm8753_hdac_set_dai_fmt(codec, fmt) < 0)
                return -EINVAL;
-       if (wm8753_vdac_adc_set_dai_fmt(codec_dai, fmt) < 0)
-               return -EINVAL;
-       return wm8753_i2s_set_dai_fmt(codec_dai, fmt);
+       return wm8753_vdac_adc_set_dai_fmt(codec, fmt);
 }
 
+static int wm8753_hifi_write_dai_fmt(struct snd_soc_codec *codec,
+               unsigned int fmt)
+{
+       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       switch (wm8753->dai_func) {
+       case 0:
+               ret = wm8753_mode1h_set_dai_fmt(codec, fmt);
+               break;
+       case 1:
+               ret = wm8753_mode2_set_dai_fmt(codec, fmt);
+               break;
+       case 2:
+       case 3:
+               ret = wm8753_mode3_4_set_dai_fmt(codec, fmt);
+               break;
+       default:
+                break;
+       }
+       if (ret)
+               return ret;
+
+       return wm8753_i2s_set_dai_fmt(codec, fmt);
+}
+
+static int wm8753_hifi_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+
+       wm8753->hifi_fmt = fmt;
+
+       return wm8753_hifi_write_dai_fmt(codec, fmt);
+};
+
+static int wm8753_voice_write_dai_fmt(struct snd_soc_codec *codec,
+               unsigned int fmt)
+{
+       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       if (wm8753->dai_func != 0)
+               return 0;
+
+       ret = wm8753_mode1v_set_dai_fmt(codec, fmt);
+       if (ret)
+               return ret;
+       ret = wm8753_pcm_set_dai_fmt(codec, fmt);
+       if (ret)
+               return ret;
+
+       return 0;
+};
+
+static int wm8753_voice_set_dai_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
+
+       wm8753->voice_fmt = fmt;
+
+       return wm8753_voice_write_dai_fmt(codec, fmt);
+};
+
 static int wm8753_mute(struct snd_soc_dai *dai, int mute)
 {
        struct snd_soc_codec *codec = dai->codec;
@@ -1268,57 +1322,25 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec,
  * 3. Voice disabled - HIFI over HIFI
  * 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
  */
-static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode1 = {
-       .startup = wm8753_i2s_startup,
+static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode = {
        .hw_params      = wm8753_i2s_hw_params,
        .digital_mute   = wm8753_mute,
-       .set_fmt        = wm8753_mode1h_set_dai_fmt,
-       .set_clkdiv     = wm8753_set_dai_clkdiv,
-       .set_pll        = wm8753_set_dai_pll,
-       .set_sysclk     = wm8753_set_dai_sysclk,
-};
-
-static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode1 = {
-       .startup = wm8753_pcm_startup,
-       .hw_params      = wm8753_pcm_hw_params,
-       .digital_mute   = wm8753_mute,
-       .set_fmt        = wm8753_mode1v_set_dai_fmt,
+       .set_fmt        = wm8753_hifi_set_dai_fmt,
        .set_clkdiv     = wm8753_set_dai_clkdiv,
        .set_pll        = wm8753_set_dai_pll,
        .set_sysclk     = wm8753_set_dai_sysclk,
 };
 
-static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode2 = {
-       .startup = wm8753_pcm_startup,
+static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode = {
        .hw_params      = wm8753_pcm_hw_params,
        .digital_mute   = wm8753_mute,
-       .set_fmt        = wm8753_mode2_set_dai_fmt,
-       .set_clkdiv     = wm8753_set_dai_clkdiv,
-       .set_pll        = wm8753_set_dai_pll,
-       .set_sysclk     = wm8753_set_dai_sysclk,
-};
-
-static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode3        = {
-       .startup = wm8753_i2s_startup,
-       .hw_params      = wm8753_i2s_hw_params,
-       .digital_mute   = wm8753_mute,
-       .set_fmt        = wm8753_mode3_4_set_dai_fmt,
-       .set_clkdiv     = wm8753_set_dai_clkdiv,
-       .set_pll        = wm8753_set_dai_pll,
-       .set_sysclk     = wm8753_set_dai_sysclk,
-};
-
-static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode4        = {
-       .startup = wm8753_i2s_startup,
-       .hw_params      = wm8753_i2s_hw_params,
-       .digital_mute   = wm8753_mute,
-       .set_fmt        = wm8753_mode3_4_set_dai_fmt,
+       .set_fmt        = wm8753_voice_set_dai_fmt,
        .set_clkdiv     = wm8753_set_dai_clkdiv,
        .set_pll        = wm8753_set_dai_pll,
        .set_sysclk     = wm8753_set_dai_sysclk,
 };
 
-static struct snd_soc_dai_driver wm8753_all_dai[] = {
+static struct snd_soc_dai_driver wm8753_dai[] = {
 /* DAI HiFi mode 1 */
 {      .name = "wm8753-hifi",
        .playback = {
@@ -1326,14 +1348,16 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = {
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS},
+               .formats = WM8753_FORMATS
+       },
        .capture = { /* dummy for fast DAI switching */
                .stream_name = "Capture",
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS},
-       .ops = &wm8753_dai_ops_hifi_mode1,
+               .formats = WM8753_FORMATS
+       },
+       .ops = &wm8753_dai_ops_hifi_mode,
 },
 /* DAI Voice mode 1 */
 {      .name = "wm8753-voice",
@@ -1342,97 +1366,19 @@ static struct snd_soc_dai_driver wm8753_all_dai[] = {
                .channels_min = 1,
                .channels_max = 1,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .capture = {
-               .stream_name = "Capture",
-               .channels_min = 1,
-               .channels_max = 2,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .ops = &wm8753_dai_ops_voice_mode1,
-},
-/* DAI HiFi mode 2 - dummy */
-{      .name = "wm8753-hifi",
-},
-/* DAI Voice mode 2 */
-{      .name = "wm8753-voice",
-       .playback = {
-               .stream_name = "Voice Playback",
-               .channels_min = 1,
-               .channels_max = 1,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .capture = {
-               .stream_name = "Capture",
-               .channels_min = 1,
-               .channels_max = 2,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .ops = &wm8753_dai_ops_voice_mode2,
-},
-/* DAI HiFi mode 3 */
-{      .name = "wm8753-hifi",
-       .playback = {
-               .stream_name = "HiFi Playback",
-               .channels_min = 1,
-               .channels_max = 2,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .capture = {
-               .stream_name = "Capture",
-               .channels_min = 1,
-               .channels_max = 2,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .ops = &wm8753_dai_ops_hifi_mode3,
-},
-/* DAI Voice mode 3 - dummy */
-{      .name = "wm8753-voice",
-},
-/* DAI HiFi mode 4 */
-{      .name = "wm8753-hifi",
-       .playback = {
-               .stream_name = "HiFi Playback",
-               .channels_min = 1,
-               .channels_max = 2,
-               .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
+               .formats = WM8753_FORMATS,
+       },
        .capture = {
                .stream_name = "Capture",
                .channels_min = 1,
                .channels_max = 2,
                .rates = WM8753_RATES,
-               .formats = WM8753_FORMATS,},
-       .ops = &wm8753_dai_ops_hifi_mode4,
-},
-/* DAI Voice mode 4 - dummy */
-{      .name = "wm8753-voice",
-},
-};
-
-static struct snd_soc_dai_driver wm8753_dai[] = {
-       {
-               .name = "wm8753-aif0",
-       },
-       {
-               .name = "wm8753-aif1",
+               .formats = WM8753_FORMATS,
        },
+       .ops = &wm8753_dai_ops_voice_mode,
+},
 };
 
-static void wm8753_set_dai_mode(struct snd_soc_codec *codec,
-               struct snd_soc_dai *dai, unsigned int hifi)
-{
-       struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec);
-
-       if (wm8753->dai_func < 4) {
-               if (hifi)
-                       dai->driver = &wm8753_all_dai[wm8753->dai_func << 1];
-               else
-                       dai->driver = &wm8753_all_dai[(wm8753->dai_func << 1) + 1];
-       }
-       snd_soc_write(codec, WM8753_IOCTL, wm8753->dai_func);
-}
-
 static void wm8753_work(struct work_struct *work)
 {
        struct snd_soc_dapm_context *dapm =
index 6dae1b4..6785688 100644 (file)
@@ -175,7 +175,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static int wm8804_volatile(unsigned int reg)
+static int wm8804_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM8804_RST_DEVID1:
index cd09599..449ea09 100644 (file)
@@ -180,7 +180,7 @@ static const u16 wm8900_reg_defaults[WM8900_MAXREG] = {
        /* Remaining registers all zero */
 };
 
-static int wm8900_volatile_register(unsigned int reg)
+static int wm8900_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM8900_REG_ID:
index 017d99c..ae1cadf 100644 (file)
@@ -2,6 +2,7 @@
  * wm8903.c  --  WM8903 ALSA SoC Audio driver
  *
  * Copyright 2008 Wolfson Microelectronics
+ * Copyright 2011 NVIDIA, Inc.
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
@@ -19,6 +20,7 @@
 #include <linux/init.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
@@ -213,6 +215,7 @@ static u16 wm8903_reg_defaults[] = {
 };
 
 struct wm8903_priv {
+       struct snd_soc_codec *codec;
 
        int sysclk;
        int irq;
@@ -220,25 +223,36 @@ struct wm8903_priv {
        int fs;
        int deemph;
 
+       int dcs_pending;
+       int dcs_cache[4];
+
        /* Reference count */
        int class_w_users;
 
-       struct completion wseq;
-
        struct snd_soc_jack *mic_jack;
        int mic_det;
        int mic_short;
        int mic_last_report;
        int mic_delay;
+
+#ifdef CONFIG_GPIOLIB
+       struct gpio_chip gpio_chip;
+#endif
 };
 
-static int wm8903_volatile_register(unsigned int reg)
+static int wm8903_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
        switch (reg) {
        case WM8903_SW_RESET_AND_ID:
        case WM8903_REVISION_NUMBER:
        case WM8903_INTERRUPT_STATUS_1:
        case WM8903_WRITE_SEQUENCER_4:
+       case WM8903_POWER_MANAGEMENT_3:
+       case WM8903_POWER_MANAGEMENT_2:
+       case WM8903_DC_SERVO_READBACK_1:
+       case WM8903_DC_SERVO_READBACK_2:
+       case WM8903_DC_SERVO_READBACK_3:
+       case WM8903_DC_SERVO_READBACK_4:
                return 1;
 
        default:
@@ -246,50 +260,6 @@ static int wm8903_volatile_register(unsigned int reg)
        }
 }
 
-static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start)
-{
-       u16 reg[5];
-       struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
-
-       BUG_ON(start > 48);
-
-       /* Enable the sequencer if it's not already on */
-       reg[0] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_0);
-       snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0,
-                     reg[0] | WM8903_WSEQ_ENA);
-
-       dev_dbg(codec->dev, "Starting sequence at %d\n", start);
-
-       snd_soc_write(codec, WM8903_WRITE_SEQUENCER_3,
-                    start | WM8903_WSEQ_START);
-
-       /* Wait for it to complete.  If we have the interrupt wired up then
-        * that will break us out of the poll early.
-        */
-       do {
-               wait_for_completion_timeout(&wm8903->wseq,
-                                           msecs_to_jiffies(10));
-
-               reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4);
-       } while (reg[4] & WM8903_WSEQ_BUSY);
-
-       dev_dbg(codec->dev, "Sequence complete\n");
-
-       /* Disable the sequencer again if we enabled it */
-       snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, reg[0]);
-
-       return 0;
-}
-
-static void wm8903_sync_reg_cache(struct snd_soc_codec *codec, u16 *cache)
-{
-       int i;
-
-       /* There really ought to be something better we can do here :/ */
-       for (i = 0; i < ARRAY_SIZE(wm8903_reg_defaults); i++)
-               cache[i] = codec->hw_read(codec, i);
-}
-
 static void wm8903_reset(struct snd_soc_codec *codec)
 {
        snd_soc_write(codec, WM8903_SW_RESET_AND_ID, 0);
@@ -297,11 +267,6 @@ static void wm8903_reset(struct snd_soc_codec *codec)
               sizeof(wm8903_reg_defaults));
 }
 
-#define WM8903_OUTPUT_SHORT 0x8
-#define WM8903_OUTPUT_OUT   0x4
-#define WM8903_OUTPUT_INT   0x2
-#define WM8903_OUTPUT_IN    0x1
-
 static int wm8903_cp_event(struct snd_soc_dapm_widget *w,
                           struct snd_kcontrol *kcontrol, int event)
 {
@@ -311,97 +276,101 @@ static int wm8903_cp_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
-/*
- * Event for headphone and line out amplifier power changes.  Special
- * power up/down sequences are required in order to maximise pop/click
- * performance.
- */
-static int wm8903_output_event(struct snd_soc_dapm_widget *w,
-                              struct snd_kcontrol *kcontrol, int event)
+static int wm8903_dcs_event(struct snd_soc_dapm_widget *w,
+                           struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = w->codec;
-       u16 val;
-       u16 reg;
-       u16 dcs_reg;
-       u16 dcs_bit;
-       int shift;
+       struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
 
-       switch (w->reg) {
-       case WM8903_POWER_MANAGEMENT_2:
-               reg = WM8903_ANALOGUE_HP_0;
-               dcs_bit = 0 + w->shift;
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               wm8903->dcs_pending |= 1 << w->shift;
                break;
-       case WM8903_POWER_MANAGEMENT_3:
-               reg = WM8903_ANALOGUE_LINEOUT_0;
-               dcs_bit = 2 + w->shift;
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
+                                   1 << w->shift, 0);
                break;
-       default:
-               BUG();
-               return -EINVAL;  /* Spurious warning from some compilers */
        }
 
-       switch (w->shift) {
-       case 0:
-               shift = 0;
-               break;
-       case 1:
-               shift = 4;
-               break;
-       default:
-               BUG();
-               return -EINVAL;  /* Spurious warning from some compilers */
-       }
+       return 0;
+}
 
-       if (event & SND_SOC_DAPM_PRE_PMU) {
-               val = snd_soc_read(codec, reg);
+#define WM8903_DCS_MODE_WRITE_STOP 0
+#define WM8903_DCS_MODE_START_STOP 2
 
-               /* Short the output */
-               val &= ~(WM8903_OUTPUT_SHORT << shift);
-               snd_soc_write(codec, reg, val);
-       }
+static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
+                               enum snd_soc_dapm_type event, int subseq)
+{
+       struct snd_soc_codec *codec = container_of(dapm,
+                                                  struct snd_soc_codec, dapm);
+       struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+       int dcs_mode = WM8903_DCS_MODE_WRITE_STOP;
+       int i, val;
 
-       if (event & SND_SOC_DAPM_POST_PMU) {
-               val = snd_soc_read(codec, reg);
+       /* Complete any pending DC servo starts */
+       if (wm8903->dcs_pending) {
+               dev_dbg(codec->dev, "Starting DC servo for %x\n",
+                       wm8903->dcs_pending);
 
-               val |= (WM8903_OUTPUT_IN << shift);
-               snd_soc_write(codec, reg, val);
+               /* If we've no cached values then we need to do startup */
+               for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) {
+                       if (!(wm8903->dcs_pending & (1 << i)))
+                               continue;
 
-               val |= (WM8903_OUTPUT_INT << shift);
-               snd_soc_write(codec, reg, val);
+                       if (wm8903->dcs_cache[i]) {
+                               dev_dbg(codec->dev,
+                                       "Restore DC servo %d value %x\n",
+                                       3 - i, wm8903->dcs_cache[i]);
+
+                               snd_soc_write(codec, WM8903_DC_SERVO_4 + i,
+                                             wm8903->dcs_cache[i] & 0xff);
+                       } else {
+                               dev_dbg(codec->dev,
+                                       "Calibrate DC servo %d\n", 3 - i);
+                               dcs_mode = WM8903_DCS_MODE_START_STOP;
+                       }
+               }
 
-               /* Turn on the output ENA_OUTP */
-               val |= (WM8903_OUTPUT_OUT << shift);
-               snd_soc_write(codec, reg, val);
+               /* Don't trust the cache for analogue */
+               if (wm8903->class_w_users)
+                       dcs_mode = WM8903_DCS_MODE_START_STOP;
 
-               /* Enable the DC servo */
-               dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0);
-               dcs_reg |= dcs_bit;
-               snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg);
+               snd_soc_update_bits(codec, WM8903_DC_SERVO_2,
+                                   WM8903_DCS_MODE_MASK, dcs_mode);
 
-               /* Remove the short */
-               val |= (WM8903_OUTPUT_SHORT << shift);
-               snd_soc_write(codec, reg, val);
-       }
+               snd_soc_update_bits(codec, WM8903_DC_SERVO_0,
+                                   WM8903_DCS_ENA_MASK, wm8903->dcs_pending);
 
-       if (event & SND_SOC_DAPM_PRE_PMD) {
-               val = snd_soc_read(codec, reg);
+               switch (dcs_mode) {
+               case WM8903_DCS_MODE_WRITE_STOP:
+                       break;
 
-               /* Short the output */
-               val &= ~(WM8903_OUTPUT_SHORT << shift);
-               snd_soc_write(codec, reg, val);
+               case WM8903_DCS_MODE_START_STOP:
+                       msleep(270);
 
-               /* Disable the DC servo */
-               dcs_reg = snd_soc_read(codec, WM8903_DC_SERVO_0);
-               dcs_reg &= ~dcs_bit;
-               snd_soc_write(codec, WM8903_DC_SERVO_0, dcs_reg);
+                       /* Cache the measured offsets for digital */
+                       if (wm8903->class_w_users)
+                               break;
 
-               /* Then disable the intermediate and output stages */
-               val &= ~((WM8903_OUTPUT_OUT | WM8903_OUTPUT_INT |
-                         WM8903_OUTPUT_IN) << shift);
-               snd_soc_write(codec, reg, val);
-       }
+                       for (i = 0; i < ARRAY_SIZE(wm8903->dcs_cache); i++) {
+                               if (!(wm8903->dcs_pending & (1 << i)))
+                                       continue;
 
-       return 0;
+                               val = snd_soc_read(codec,
+                                                  WM8903_DC_SERVO_READBACK_1 + i);
+                               dev_dbg(codec->dev, "DC servo %d: %x\n",
+                                       3 - i, val);
+                               wm8903->dcs_cache[i] = val;
+                       }
+                       break;
+
+               default:
+                       pr_warn("DCS mode %d delay not set\n", dcs_mode);
+                       break;
+               }
+
+               wm8903->dcs_pending = 0;
+       }
 }
 
 /*
@@ -667,6 +636,22 @@ static const struct soc_enum lsidetone_enum =
 static const struct soc_enum rsidetone_enum =
        SOC_ENUM_SINGLE(WM8903_DAC_DIGITAL_0, 0, 3, sidetone_text);
 
+static const char *aif_text[] = {
+       "Left", "Right"
+};
+
+static const struct soc_enum lcapture_enum =
+       SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 7, 2, aif_text);
+
+static const struct soc_enum rcapture_enum =
+       SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 6, 2, aif_text);
+
+static const struct soc_enum lplay_enum =
+       SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 5, 2, aif_text);
+
+static const struct soc_enum rplay_enum =
+       SOC_ENUM_SINGLE(WM8903_AUDIO_INTERFACE_0, 4, 2, aif_text);
+
 static const struct snd_kcontrol_new wm8903_snd_controls[] = {
 
 /* Input PGAs - No TLV since the scale depends on PGA mode */
@@ -784,6 +769,18 @@ static const struct snd_kcontrol_new lsidetone_mux =
 static const struct snd_kcontrol_new rsidetone_mux =
        SOC_DAPM_ENUM("DACR Sidetone Mux", rsidetone_enum);
 
+static const struct snd_kcontrol_new lcapture_mux =
+       SOC_DAPM_ENUM("Left Capture Mux", lcapture_enum);
+
+static const struct snd_kcontrol_new rcapture_mux =
+       SOC_DAPM_ENUM("Right Capture Mux", rcapture_enum);
+
+static const struct snd_kcontrol_new lplay_mux =
+       SOC_DAPM_ENUM("Left Playback Mux", lplay_enum);
+
+static const struct snd_kcontrol_new rplay_mux =
+       SOC_DAPM_ENUM("Right Playback Mux", rplay_enum);
+
 static const struct snd_kcontrol_new left_output_mixer[] = {
 SOC_DAPM_SINGLE("DACL Switch", WM8903_ANALOGUE_LEFT_MIX_0, 3, 1, 0),
 SOC_DAPM_SINGLE("DACR Switch", WM8903_ANALOGUE_LEFT_MIX_0, 2, 1, 0),
@@ -847,14 +844,26 @@ SND_SOC_DAPM_MUX("Right Input Mode Mux", SND_SOC_NOPM, 0, 0, &rinput_mode_mux),
 SND_SOC_DAPM_PGA("Left Input PGA", WM8903_POWER_MANAGEMENT_0, 1, 0, NULL, 0),
 SND_SOC_DAPM_PGA("Right Input PGA", WM8903_POWER_MANAGEMENT_0, 0, 0, NULL, 0),
 
-SND_SOC_DAPM_ADC("ADCL", "Left HiFi Capture", WM8903_POWER_MANAGEMENT_6, 1, 0),
-SND_SOC_DAPM_ADC("ADCR", "Right HiFi Capture", WM8903_POWER_MANAGEMENT_6, 0, 0),
+SND_SOC_DAPM_ADC("ADCL", NULL, WM8903_POWER_MANAGEMENT_6, 1, 0),
+SND_SOC_DAPM_ADC("ADCR", NULL, WM8903_POWER_MANAGEMENT_6, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Capture Mux", SND_SOC_NOPM, 0, 0, &lcapture_mux),
+SND_SOC_DAPM_MUX("Right Capture Mux", SND_SOC_NOPM, 0, 0, &rcapture_mux),
+
+SND_SOC_DAPM_AIF_OUT("AIFTXL", "Left HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_OUT("AIFTXR", "Right HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
 
 SND_SOC_DAPM_MUX("DACL Sidetone", SND_SOC_NOPM, 0, 0, &lsidetone_mux),
 SND_SOC_DAPM_MUX("DACR Sidetone", SND_SOC_NOPM, 0, 0, &rsidetone_mux),
 
-SND_SOC_DAPM_DAC("DACL", "Left Playback", WM8903_POWER_MANAGEMENT_6, 3, 0),
-SND_SOC_DAPM_DAC("DACR", "Right Playback", WM8903_POWER_MANAGEMENT_6, 2, 0),
+SND_SOC_DAPM_AIF_IN("AIFRXL", "Left Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFRXR", "Right Playback", 0, SND_SOC_NOPM, 0, 0),
+
+SND_SOC_DAPM_MUX("Left Playback Mux", SND_SOC_NOPM, 0, 0, &lplay_mux),
+SND_SOC_DAPM_MUX("Right Playback Mux", SND_SOC_NOPM, 0, 0, &rplay_mux),
+
+SND_SOC_DAPM_DAC("DACL", NULL, WM8903_POWER_MANAGEMENT_6, 3, 0),
+SND_SOC_DAPM_DAC("DACR", NULL, WM8903_POWER_MANAGEMENT_6, 2, 0),
 
 SND_SOC_DAPM_MIXER("Left Output Mixer", WM8903_POWER_MANAGEMENT_1, 1, 0,
                   left_output_mixer, ARRAY_SIZE(left_output_mixer)),
@@ -866,23 +875,45 @@ SND_SOC_DAPM_MIXER("Left Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 1, 0,
 SND_SOC_DAPM_MIXER("Right Speaker Mixer", WM8903_POWER_MANAGEMENT_4, 0, 0,
                   right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
 
-SND_SOC_DAPM_PGA_E("Left Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
-                  1, 0, NULL, 0, wm8903_output_event,
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD),
-SND_SOC_DAPM_PGA_E("Right Headphone Output PGA", WM8903_POWER_MANAGEMENT_2,
-                  0, 0, NULL, 0, wm8903_output_event,
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD),
-
-SND_SOC_DAPM_PGA_E("Left Line Output PGA", WM8903_POWER_MANAGEMENT_3, 1, 0,
-                  NULL, 0, wm8903_output_event,
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD),
-SND_SOC_DAPM_PGA_E("Right Line Output PGA", WM8903_POWER_MANAGEMENT_3, 0, 0,
-                  NULL, 0, wm8903_output_event,
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
-                  SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_S("Left Headphone Output PGA", 0, WM8903_ANALOGUE_HP_0,
+                  4, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("Right Headphone Output PGA", 0, WM8903_ANALOGUE_HP_0,
+                  0, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("Left Line Output PGA", 0, WM8903_ANALOGUE_LINEOUT_0, 4, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("Right Line Output PGA", 0, WM8903_ANALOGUE_LINEOUT_0, 0, 0,
+                  NULL, 0),
+
+SND_SOC_DAPM_PGA_S("HPL_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 7, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPL_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 6, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPL_ENA_DLY", 1, WM8903_ANALOGUE_HP_0, 5, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPR_RMV_SHORT", 4, WM8903_ANALOGUE_HP_0, 3, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPR_ENA_OUTP", 3, WM8903_ANALOGUE_HP_0, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPR_ENA_DLY", 1, WM8903_ANALOGUE_HP_0, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_S("LINEOUTL_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 7, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 6, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTL_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 5, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTR_RMV_SHORT", 4, WM8903_ANALOGUE_LINEOUT_0, 3, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_OUTP", 3, WM8903_ANALOGUE_LINEOUT_0, 2, 0,
+                  NULL, 0),
+SND_SOC_DAPM_PGA_S("LINEOUTR_ENA_DLY", 1, WM8903_ANALOGUE_LINEOUT_0, 1, 0,
+                  NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("DCS Master", WM8903_DC_SERVO_0, 4, 0, NULL, 0),
+SND_SOC_DAPM_PGA_S("HPL_DCS", 3, SND_SOC_NOPM, 3, 0, wm8903_dcs_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_S("HPR_DCS", 3, SND_SOC_NOPM, 2, 0, wm8903_dcs_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_S("LINEOUTL_DCS", 3, SND_SOC_NOPM, 1, 0, wm8903_dcs_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_S("LINEOUTR_DCS", 3, SND_SOC_NOPM, 0, 0, wm8903_dcs_event,
+                  SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 
 SND_SOC_DAPM_PGA("Left Speaker PGA", WM8903_POWER_MANAGEMENT_5, 1, 0,
                 NULL, 0),
@@ -892,10 +923,18 @@ SND_SOC_DAPM_PGA("Right Speaker PGA", WM8903_POWER_MANAGEMENT_5, 0, 0,
 SND_SOC_DAPM_SUPPLY("Charge Pump", WM8903_CHARGE_PUMP_0, 0, 0,
                    wm8903_cp_event, SND_SOC_DAPM_POST_PMU),
 SND_SOC_DAPM_SUPPLY("CLK_DSP", WM8903_CLOCK_RATES_2, 1, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("CLK_SYS", WM8903_CLOCK_RATES_2, 2, 0, NULL, 0),
 };
 
 static const struct snd_soc_dapm_route intercon[] = {
 
+       { "CLK_DSP", NULL, "CLK_SYS" },
+       { "Mic Bias", NULL, "CLK_SYS" },
+       { "HPL_DCS", NULL, "CLK_SYS" },
+       { "HPR_DCS", NULL, "CLK_SYS" },
+       { "LINEOUTL_DCS", NULL, "CLK_SYS" },
+       { "LINEOUTR_DCS", NULL, "CLK_SYS" },
+
        { "Left Input Mux", "IN1L", "IN1L" },
        { "Left Input Mux", "IN2L", "IN2L" },
        { "Left Input Mux", "IN3L", "IN3L" },
@@ -936,18 +975,36 @@ static const struct snd_soc_dapm_route intercon[] = {
        { "Left Input PGA", NULL, "Left Input Mode Mux" },
        { "Right Input PGA", NULL, "Right Input Mode Mux" },
 
+       { "Left Capture Mux", "Left", "ADCL" },
+       { "Left Capture Mux", "Right", "ADCR" },
+
+       { "Right Capture Mux", "Left", "ADCL" },
+       { "Right Capture Mux", "Right", "ADCR" },
+
+       { "AIFTXL", NULL, "Left Capture Mux" },
+       { "AIFTXR", NULL, "Right Capture Mux" },
+
        { "ADCL", NULL, "Left Input PGA" },
        { "ADCL", NULL, "CLK_DSP" },
        { "ADCR", NULL, "Right Input PGA" },
        { "ADCR", NULL, "CLK_DSP" },
 
+       { "Left Playback Mux", "Left", "AIFRXL" },
+       { "Left Playback Mux", "Right", "AIFRXR" },
+
+       { "Right Playback Mux", "Left", "AIFRXL" },
+       { "Right Playback Mux", "Right", "AIFRXR" },
+
        { "DACL Sidetone", "Left", "ADCL" },
        { "DACL Sidetone", "Right", "ADCR" },
        { "DACR Sidetone", "Left", "ADCL" },
        { "DACR Sidetone", "Right", "ADCR" },
 
+       { "DACL", NULL, "Left Playback Mux" },
        { "DACL", NULL, "DACL Sidetone" },
        { "DACL", NULL, "CLK_DSP" },
+
+       { "DACR", NULL, "Right Playback Mux" },
        { "DACR", NULL, "DACR Sidetone" },
        { "DACR", NULL, "CLK_DSP" },
 
@@ -980,11 +1037,35 @@ static const struct snd_soc_dapm_route intercon[] = {
        { "Left Speaker PGA", NULL, "Left Speaker Mixer" },
        { "Right Speaker PGA", NULL, "Right Speaker Mixer" },
 
-       { "HPOUTL", NULL, "Left Headphone Output PGA" },
-       { "HPOUTR", NULL, "Right Headphone Output PGA" },
+       { "HPL_ENA_DLY", NULL, "Left Headphone Output PGA" },
+       { "HPR_ENA_DLY", NULL, "Right Headphone Output PGA" },
+       { "LINEOUTL_ENA_DLY", NULL, "Left Line Output PGA" },
+       { "LINEOUTR_ENA_DLY", NULL, "Right Line Output PGA" },
+
+       { "HPL_DCS", NULL, "DCS Master" },
+       { "HPR_DCS", NULL, "DCS Master" },
+       { "LINEOUTL_DCS", NULL, "DCS Master" },
+       { "LINEOUTR_DCS", NULL, "DCS Master" },
+