فهرست منبع

Merge branch 'topic/asoc' into for-linus

Takashi Iwai 14 سال پیش
والد
کامیت
27b92d4ff2
100فایلهای تغییر یافته به همراه10569 افزوده شده و 880 حذف شده
  1. 0 10
      arch/arm/mach-shmobile/board-ag5evm.c
  2. 7 6
      arch/arm/mach-shmobile/board-ap4evb.c
  3. 7 6
      arch/arm/mach-shmobile/board-mackerel.c
  4. 22 0
      arch/arm/mach-tegra/include/mach/harmony_audio.h
  5. 1 5
      arch/sh/boards/mach-ecovec24/setup.c
  6. 1 5
      arch/sh/boards/mach-se/7724/setup.c
  7. 10 2
      include/linux/mfd/wm8994/pdata.h
  8. 2 0
      include/linux/mfd/wm8994/registers.h
  9. 2 0
      include/sound/control.h
  10. 24 0
      include/sound/cs4271.h
  11. 19 57
      include/sound/sh_fsi.h
  12. 16 0
      include/sound/soc-dapm.h
  13. 135 12
      include/sound/soc.h
  14. 31 0
      include/sound/tlv320aic32x4.h
  15. 19 1
      include/sound/wm8903.h
  16. 6 3
      include/sound/wm9081.h
  17. 25 0
      include/trace/events/asoc.h
  18. 46 0
      sound/core/control.c
  19. 2 0
      sound/soc/Kconfig
  20. 2 0
      sound/soc/Makefile
  21. 33 1
      sound/soc/codecs/Kconfig
  22. 17 1
      sound/soc/codecs/Makefile
  23. 0 1
      sound/soc/codecs/ak4104.c
  24. 24 0
      sound/soc/codecs/ak4642.c
  25. 3 5
      sound/soc/codecs/cs4270.c
  26. 667 0
      sound/soc/codecs/cs4271.c
  27. 72 0
      sound/soc/codecs/dfbmcs320.c
  28. 276 0
      sound/soc/codecs/lm4857.c
  29. 1 1
      sound/soc/codecs/max98088.c
  30. 389 0
      sound/soc/codecs/max9850.c
  31. 38 0
      sound/soc/codecs/max9850.h
  32. 1513 0
      sound/soc/codecs/sgtl5000.c
  33. 400 0
      sound/soc/codecs/sgtl5000.h
  34. 949 0
      sound/soc/codecs/sn95031.c
  35. 132 0
      sound/soc/codecs/sn95031.h
  36. 794 0
      sound/soc/codecs/tlv320aic32x4.c
  37. 143 0
      sound/soc/codecs/tlv320aic32x4.h
  38. 1 0
      sound/soc/codecs/tlv320dac33.c
  39. 2 2
      sound/soc/codecs/twl6040.c
  40. 7 7
      sound/soc/codecs/wm2000.c
  41. 4 4
      sound/soc/codecs/wm8523.c
  42. 8 5
      sound/soc/codecs/wm8741.c
  43. 121 175
      sound/soc/codecs/wm8753.c
  44. 1 1
      sound/soc/codecs/wm8804.c
  45. 1 1
      sound/soc/codecs/wm8900.c
  46. 459 182
      sound/soc/codecs/wm8903.c
  47. 8 0
      sound/soc/codecs/wm8903.h
  48. 27 16
      sound/soc/codecs/wm8904.c
  49. 19 8
      sound/soc/codecs/wm8955.c
  50. 1 1
      sound/soc/codecs/wm8961.c
  51. 23 13
      sound/soc/codecs/wm8962.c
  52. 11 8
      sound/soc/codecs/wm8978.c
  53. 1427 0
      sound/soc/codecs/wm8991.c
  54. 833 0
      sound/soc/codecs/wm8991.h
  55. 1 1
      sound/soc/codecs/wm8993.c
  56. 6 6
      sound/soc/codecs/wm8994-tables.c
  57. 74 63
      sound/soc/codecs/wm8994.c
  58. 1 1
      sound/soc/codecs/wm8994.h
  59. 98 5
      sound/soc/codecs/wm8995.c
  60. 37 47
      sound/soc/codecs/wm9081.c
  61. 26 19
      sound/soc/codecs/wm9090.c
  62. 2 1
      sound/soc/codecs/wm_hubs.c
  63. 19 9
      sound/soc/davinci/davinci-i2s.c
  64. 20 9
      sound/soc/davinci/davinci-mcasp.c
  65. 9 0
      sound/soc/ep93xx/Kconfig
  66. 2 0
      sound/soc/ep93xx/Makefile
  67. 142 0
      sound/soc/ep93xx/edb93xx.c
  68. 0 1
      sound/soc/ep93xx/ep93xx-ac97.c
  69. 17 14
      sound/soc/ep93xx/ep93xx-i2s.c
  70. 2 2
      sound/soc/ep93xx/ep93xx-pcm.c
  71. 2 4
      sound/soc/fsl/mpc8610_hpcd.c
  72. 2 4
      sound/soc/fsl/p1022_ds.c
  73. 12 1
      sound/soc/imx/Kconfig
  74. 2 0
      sound/soc/imx/Makefile
  75. 2 1
      sound/soc/imx/eukrea-tlv320.c
  76. 4 1
      sound/soc/imx/imx-ssi.c
  77. 137 0
      sound/soc/imx/mx27vis-aic32x4.c
  78. 14 0
      sound/soc/mid-x86/Kconfig
  79. 5 0
      sound/soc/mid-x86/Makefile
  80. 452 0
      sound/soc/mid-x86/mfld_machine.c
  81. 474 0
      sound/soc/mid-x86/sst_platform.c
  82. 63 0
      sound/soc/mid-x86/sst_platform.h
  83. 1 0
      sound/soc/omap/Kconfig
  84. 123 8
      sound/soc/omap/rx51.c
  85. 10 10
      sound/soc/pxa/raumfeld.c
  86. 2 2
      sound/soc/pxa/tosa.c
  87. 6 1
      sound/soc/pxa/z2.c
  88. 4 5
      sound/soc/pxa/zylonite.c
  89. 6 13
      sound/soc/samsung/Kconfig
  90. 0 2
      sound/soc/samsung/Makefile
  91. 4 4
      sound/soc/samsung/ac97.c
  92. 0 21
      sound/soc/samsung/ac97.h
  93. 5 8
      sound/soc/samsung/dma.c
  94. 0 8
      sound/soc/samsung/dma.h
  95. 1 9
      sound/soc/samsung/goni_wm8994.c
  96. 0 9
      sound/soc/samsung/h1940_uda1380.c
  97. 1 2
      sound/soc/samsung/i2s.c
  98. 0 11
      sound/soc/samsung/jive_wm8750.c
  99. 0 32
      sound/soc/samsung/lm4857.h
  100. 0 7
      sound/soc/samsung/ln2440sbc_alc650.c

+ 0 - 10
arch/arm/mach-shmobile/board-ag5evm.c

@@ -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[] = {

+ 7 - 6
arch/arm/mach-shmobile/board-ap4evb.c

@@ -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,

+ 7 - 6
arch/arm/mach-shmobile/board-mackerel.c

@@ -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,

+ 22 - 0
arch/arm/mach-tegra/include/mach/harmony_audio.h

@@ -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;
+};

+ 1 - 5
arch/sh/boards/mach-ecovec24/setup.c

@@ -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[] = {

+ 1 - 5
arch/sh/boards/mach-se/7724/setup.c

@@ -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[] = {

+ 10 - 2
include/linux/mfd/wm8994/pdata.h

@@ -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

+ 2 - 0
include/linux/mfd/wm8994/registers.h

@@ -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

+ 2 - 0
include/sound/control.h

@@ -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);
 

+ 24 - 0
include/sound/cs4271.h

@@ -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 */

+ 19 - 57
include/sound/sh_fsi.h

@@ -15,67 +15,29 @@
 #define FSI_PORT_A	0
 #define FSI_PORT_B	1
 
-/* flags format
-
- * 0xABCDEEFF
- *
- * A:  channel size for TDM (input)
- * B:  channel size for TDM (ooutput)
- * C:  inversion
- * D:  mode
- * E:  input format
- * F:  output format
- */
-
 #include <linux/clk.h>
 #include <sound/soc.h>
 
-/* TDM channel */
-#define SH_FSI_SET_CH_I(x)	((x & 0xF) << 28)
-#define SH_FSI_SET_CH_O(x)	((x & 0xF) << 24)
-
-#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)
 
 
 /*

+ 16 - 0
include/sound/soc-dapm.h

@@ -157,6 +157,18 @@
 	.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = 1, \
 	.event = wevent, .event_flags = wflags}
 
+/* additional sequencing control within an event type */
+#define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
+	wevent, wflags) \
+{	.id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
+	.invert = winvert, .event = wevent, .event_flags = wflags, \
+	.subseq = wsubseq}
+#define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \
+	wflags)	\
+{	.id = snd_soc_dapm_supply, .name = wname, .reg = wreg,	\
+	.shift = wshift, .invert = winvert, .event = wevent, \
+	.event_flags = wflags, .subseq = wsubseq}
+
 /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
 #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
 	wevent, wflags) \
@@ -450,6 +462,7 @@ 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 */

+ 135 - 12
include/sound/soc.h

@@ -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,
@@ -366,6 +386,22 @@ int snd_soc_get_volsw_2r_sx(struct snd_kcontrol *kcontrol,
 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
  *
@@ -380,6 +416,24 @@ struct snd_soc_jack_pin {
 	bool invert;
 };
 
+/**
+ * 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
  *
@@ -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

+ 31 - 0
include/sound/tlv320aic32x4.h

@@ -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

+ 19 - 1
include/sound/wm8903.h

@@ -32,6 +32,21 @@
 #define WM8903_MICBIAS_ENA_SHIFT                     0  /* MICBIAS_ENA */
 #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
  */
@@ -227,6 +242,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

+ 6 - 3
include/sound/wm9081.h

@@ -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

+ 25 - 0
include/trace/events/asoc.h

@@ -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 */

+ 46 - 0
sound/core/control.c

@@ -465,6 +465,52 @@ error:
 	return ret;
 }
 
+/**
+ * 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

+ 2 - 0
sound/soc/Kconfig

@@ -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

+ 2 - 0
sound/soc/Makefile

@@ -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/

+ 33 - 1
sound/soc/codecs/Kconfig

@@ -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
-

+ 17 - 1
sound/soc/codecs/Makefile

@@ -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

+ 0 - 1
sound/soc/codecs/ak4104.c

@@ -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);

+ 24 - 0
sound/soc/codecs/ak4642.c

@@ -116,6 +116,12 @@
 #define BCKO_MASK	(1 << 3)
 #define BCKO_64		BCKO_MASK
 
+#define DIF_MASK	(3 << 0)
+#define DSP		(0 << 0)
+#define RIGHT_J		(1 << 0)
+#define LEFT_J		(2 << 0)
+#define I2S		(3 << 0)
+
 /* MD_CTL2 */
 #define FS0		(1 << 0)
 #define FS1		(1 << 1)
@@ -354,6 +360,24 @@ 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;
 }
 

+ 3 - 5
sound/soc/codecs/cs4270.c

@@ -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);

+ 667 - 0
sound/soc/codecs/cs4271.c

@@ -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");

+ 72 - 0
sound/soc/codecs/dfbmcs320.c

@@ -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");

+ 276 - 0
sound/soc/codecs/lm4857.c

@@ -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");

+ 1 - 1
sound/soc/codecs/max98088.c

@@ -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;
 }

+ 389 - 0
sound/soc/codecs/max9850.c

@@ -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");

+ 38 - 0
sound/soc/codecs/max9850.h

@@ -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

+ 1513 - 0
sound/soc/codecs/sgtl5000.c

@@ -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");

+ 400 - 0
sound/soc/codecs/sgtl5000.h

@@ -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

+ 949 - 0
sound/soc/codecs/sn95031.c

@@ -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");

+ 132 - 0
sound/soc/codecs/sn95031.h

@@ -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

+ 794 - 0
sound/soc/codecs/tlv320aic32x4.c

@@ -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");

+ 143 - 0
sound/soc/codecs/tlv320aic32x4.h

@@ -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 */

+ 1 - 0
sound/soc/codecs/tlv320dac33.c

@@ -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 = {

+ 2 - 2
sound/soc/codecs/twl6040.c

@@ -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;

+ 7 - 7
sound/soc/codecs/wm2000.c

@@ -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,
 };

+ 4 - 4
sound/soc/codecs/wm8523.c

@@ -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);
 

+ 8 - 5
sound/soc/codecs/wm8741.c

@@ -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));

+ 121 - 175
sound/soc/codecs/wm8753.c

@@ -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 =

+ 1 - 1
sound/soc/codecs/wm8804.c

@@ -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:

+ 1 - 1
sound/soc/codecs/wm8900.c

@@ -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:

+ 459 - 182
sound/soc/codecs/wm8903.c

@@ -2,6 +2,7 @@
  * wm8903.c  --  WM8903 ALSA SoC Audio driver
  *
  * Copyright 2008 Wolfson Microelectronics
+ * Copyright 2011 NVIDIA, Inc.
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
@@ -19,6 +20,7 @@
 #include <linux/init.h>
 #include <linux/completion.h>
 #include <linux/delay.h>
+#include <linux/gpio.h>
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
@@ -213,6 +215,7 @@ 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" },
+
+	{ "HPL_DCS", NULL, "HPL_ENA_DLY" },
+	{ "HPR_DCS", NULL, "HPR_ENA_DLY" },
+	{ "LINEOUTL_DCS", NULL, "LINEOUTL_ENA_DLY" },
+	{ "LINEOUTR_DCS", NULL, "LINEOUTR_ENA_DLY" },
 
-	{ "LINEOUTL", NULL, "Left Line Output PGA" },
-	{ "LINEOUTR", NULL, "Right Line Output PGA" },
+	{ "HPL_ENA_OUTP", NULL, "HPL_DCS" },
+	{ "HPR_ENA_OUTP", NULL, "HPR_DCS" },
+	{ "LINEOUTL_ENA_OUTP", NULL, "LINEOUTL_DCS" },
+	{ "LINEOUTR_ENA_OUTP", NULL, "LINEOUTR_DCS" },
+
+	{ "HPL_RMV_SHORT", NULL, "HPL_ENA_OUTP" },
+	{ "HPR_RMV_SHORT", NULL, "HPR_ENA_OUTP" },
+	{ "LINEOUTL_RMV_SHORT", NULL, "LINEOUTL_ENA_OUTP" },
+	{ "LINEOUTR_RMV_SHORT", NULL, "LINEOUTR_ENA_OUTP" },
+
+	{ "HPOUTL", NULL, "HPL_RMV_SHORT" },
+	{ "HPOUTR", NULL, "HPR_RMV_SHORT" },
+	{ "LINEOUTL", NULL, "LINEOUTL_RMV_SHORT" },
+	{ "LINEOUTR", NULL, "LINEOUTR_RMV_SHORT" },
 
 	{ "LOP", NULL, "Left Speaker PGA" },
 	{ "LON", NULL, "Left Speaker PGA" },
@@ -1012,29 +1093,71 @@ static int wm8903_add_widgets(struct snd_soc_codec *codec)
 static int wm8903_set_bias_level(struct snd_soc_codec *codec,
 				 enum snd_soc_bias_level level)
 {
-	u16 reg;
-
 	switch (level) {
 	case SND_SOC_BIAS_ON:
+		break;
+
 	case SND_SOC_BIAS_PREPARE:
-		reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);
-		reg &= ~(WM8903_VMID_RES_MASK);
-		reg |= WM8903_VMID_RES_50K;
-		snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);
+		snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+				    WM8903_VMID_RES_MASK,
+				    WM8903_VMID_RES_50K);
 		break;
 
 	case SND_SOC_BIAS_STANDBY:
 		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
-			snd_soc_write(codec, WM8903_CLOCK_RATES_2,
-				     WM8903_CLK_SYS_ENA);
-
-			/* Change DC servo dither level in startup sequence */
-			snd_soc_write(codec, WM8903_WRITE_SEQUENCER_0, 0x11);
-			snd_soc_write(codec, WM8903_WRITE_SEQUENCER_1, 0x1257);
-			snd_soc_write(codec, WM8903_WRITE_SEQUENCER_2, 0x2);
-
-			wm8903_run_sequence(codec, 0);
-			wm8903_sync_reg_cache(codec, codec->reg_cache);
+			snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+					    WM8903_POBCTRL | WM8903_ISEL_MASK |
+					    WM8903_STARTUP_BIAS_ENA |
+					    WM8903_BIAS_ENA,
+					    WM8903_POBCTRL |
+					    (2 << WM8903_ISEL_SHIFT) |
+					    WM8903_STARTUP_BIAS_ENA);
+
+			snd_soc_update_bits(codec,
+					    WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0,
+					    WM8903_SPK_DISCHARGE,
+					    WM8903_SPK_DISCHARGE);
+
+			msleep(33);
+
+			snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5,
+					    WM8903_SPKL_ENA | WM8903_SPKR_ENA,
+					    WM8903_SPKL_ENA | WM8903_SPKR_ENA);
+
+			snd_soc_update_bits(codec,
+					    WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0,
+					    WM8903_SPK_DISCHARGE, 0);
+
+			snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+					    WM8903_VMID_TIE_ENA |
+					    WM8903_BUFIO_ENA |
+					    WM8903_VMID_IO_ENA |
+					    WM8903_VMID_SOFT_MASK |
+					    WM8903_VMID_RES_MASK |
+					    WM8903_VMID_BUF_ENA,
+					    WM8903_VMID_TIE_ENA |
+					    WM8903_BUFIO_ENA |
+					    WM8903_VMID_IO_ENA |
+					    (2 << WM8903_VMID_SOFT_SHIFT) |
+					    WM8903_VMID_RES_250K |
+					    WM8903_VMID_BUF_ENA);
+
+			msleep(129);
+
+			snd_soc_update_bits(codec, WM8903_POWER_MANAGEMENT_5,
+					    WM8903_SPKL_ENA | WM8903_SPKR_ENA,
+					    0);
+
+			snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+					    WM8903_VMID_SOFT_MASK, 0);
+
+			snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+					    WM8903_VMID_RES_MASK,
+					    WM8903_VMID_RES_50K);
+
+			snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+					    WM8903_BIAS_ENA | WM8903_POBCTRL,
+					    WM8903_BIAS_ENA);
 
 			/* By default no bypass paths are enabled so
 			 * enable Class W support.
@@ -1047,17 +1170,32 @@ static int wm8903_set_bias_level(struct snd_soc_codec *codec,
 					    WM8903_CP_DYN_V);
 		}
 
-		reg = snd_soc_read(codec, WM8903_VMID_CONTROL_0);
-		reg &= ~(WM8903_VMID_RES_MASK);
-		reg |= WM8903_VMID_RES_250K;
-		snd_soc_write(codec, WM8903_VMID_CONTROL_0, reg);
+		snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+				    WM8903_VMID_RES_MASK,
+				    WM8903_VMID_RES_250K);
 		break;
 
 	case SND_SOC_BIAS_OFF:
-		wm8903_run_sequence(codec, 32);
-		reg = snd_soc_read(codec, WM8903_CLOCK_RATES_2);
-		reg &= ~WM8903_CLK_SYS_ENA;
-		snd_soc_write(codec, WM8903_CLOCK_RATES_2, reg);
+		snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+				    WM8903_BIAS_ENA, 0);
+
+		snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+				    WM8903_VMID_SOFT_MASK,
+				    2 << WM8903_VMID_SOFT_SHIFT);
+
+		snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+				    WM8903_VMID_BUF_ENA, 0);
+
+		msleep(290);
+
+		snd_soc_update_bits(codec, WM8903_VMID_CONTROL_0,
+				    WM8903_VMID_TIE_ENA | WM8903_BUFIO_ENA |
+				    WM8903_VMID_IO_ENA | WM8903_VMID_RES_MASK |
+				    WM8903_VMID_SOFT_MASK |
+				    WM8903_VMID_BUF_ENA, 0);
+
+		snd_soc_update_bits(codec, WM8903_BIAS_CONTROL_0,
+				    WM8903_STARTUP_BIAS_ENA, 0);
 		break;
 	}
 
@@ -1510,8 +1648,7 @@ static irqreturn_t wm8903_irq(int irq, void *data)
 	int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask;
 
 	if (int_val & WM8903_WSEQ_BUSY_EINT) {
-		dev_dbg(codec->dev, "Write sequencer done\n");
-		complete(&wm8903->wseq);
+		dev_warn(codec->dev, "Write sequencer done\n");
 	}
 
 	/*
@@ -1635,6 +1772,120 @@ static int wm8903_resume(struct snd_soc_codec *codec)
 	return 0;
 }
 
+#ifdef CONFIG_GPIOLIB
+static inline struct wm8903_priv *gpio_to_wm8903(struct gpio_chip *chip)
+{
+	return container_of(chip, struct wm8903_priv, gpio_chip);
+}
+
+static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+	if (offset >= WM8903_NUM_GPIO)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
+{
+	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+	struct snd_soc_codec *codec = wm8903->codec;
+	unsigned int mask, val;
+
+	mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK;
+	val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) |
+		WM8903_GP1_DIR;
+
+	return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
+				   mask, val);
+}
+
+static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+	struct snd_soc_codec *codec = wm8903->codec;
+	int reg;
+
+	reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset);
+
+	return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT;
+}
+
+static int wm8903_gpio_direction_out(struct gpio_chip *chip,
+				     unsigned offset, int value)
+{
+	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+	struct snd_soc_codec *codec = wm8903->codec;
+	unsigned int mask, val;
+
+	mask = WM8903_GP1_FN_MASK | WM8903_GP1_DIR_MASK | WM8903_GP1_LVL_MASK;
+	val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) |
+		(value << WM8903_GP2_LVL_SHIFT);
+
+	return snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
+				   mask, val);
+}
+
+static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
+	struct snd_soc_codec *codec = wm8903->codec;
+
+	snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
+			    WM8903_GP1_LVL_MASK,
+			    !!value << WM8903_GP1_LVL_SHIFT);
+}
+
+static struct gpio_chip wm8903_template_chip = {
+	.label			= "wm8903",
+	.owner			= THIS_MODULE,
+	.request		= wm8903_gpio_request,
+	.direction_input	= wm8903_gpio_direction_in,
+	.get			= wm8903_gpio_get,
+	.direction_output	= wm8903_gpio_direction_out,
+	.set			= wm8903_gpio_set,
+	.can_sleep		= 1,
+};
+
+static void wm8903_init_gpio(struct snd_soc_codec *codec)
+{
+	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+	struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev);
+	int ret;
+
+	wm8903->gpio_chip = wm8903_template_chip;
+	wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO;
+	wm8903->gpio_chip.dev = codec->dev;
+
+	if (pdata && pdata->gpio_base)
+		wm8903->gpio_chip.base = pdata->gpio_base;
+	else
+		wm8903->gpio_chip.base = -1;
+
+	ret = gpiochip_add(&wm8903->gpio_chip);
+	if (ret != 0)
+		dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
+static void wm8903_free_gpio(struct snd_soc_codec *codec)
+{
+	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	ret = gpiochip_remove(&wm8903->gpio_chip);
+	if (ret != 0)
+		dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
+}
+#else
+static void wm8903_init_gpio(struct snd_soc_codec *codec)
+{
+}
+
+static void wm8903_free_gpio(struct snd_soc_codec *codec)
+{
+}
+#endif
+
 static int wm8903_probe(struct snd_soc_codec *codec)
 {
 	struct wm8903_platform_data *pdata = dev_get_platdata(codec->dev);
@@ -1643,7 +1894,7 @@ static int wm8903_probe(struct snd_soc_codec *codec)
 	int trigger, irq_pol;
 	u16 val;
 
-	init_completion(&wm8903->wseq);
+	wm8903->codec = codec;
 
 	ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_I2C);
 	if (ret != 0) {
@@ -1659,19 +1910,33 @@ static int wm8903_probe(struct snd_soc_codec *codec)
 	}
 
 	val = snd_soc_read(codec, WM8903_REVISION_NUMBER);
-	dev_info(codec->dev, "WM8903 revision %d\n",
-		 val & WM8903_CHIP_REV_MASK);
+	dev_info(codec->dev, "WM8903 revision %c\n",
+		 (val & WM8903_CHIP_REV_MASK) + 'A');
 
 	wm8903_reset(codec);
 
 	/* Set up GPIOs and microphone detection */
 	if (pdata) {
+		bool mic_gpio = false;
+
 		for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) {
-			if (!pdata->gpio_cfg[i])
+			if (pdata->gpio_cfg[i] == WM8903_GPIO_NO_CONFIG)
 				continue;
 
 			snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i,
 				      pdata->gpio_cfg[i] & 0xffff);
+
+			val = (pdata->gpio_cfg[i] & WM8903_GP1_FN_MASK)
+				>> WM8903_GP1_FN_SHIFT;
+
+			switch (val) {
+			case WM8903_GPn_FN_MICBIAS_CURRENT_DETECT:
+			case WM8903_GPn_FN_MICBIAS_SHORT_DETECT:
+				mic_gpio = true;
+				break;
+			default:
+				break;
+			}
 		}
 
 		snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0,
@@ -1682,6 +1947,14 @@ static int wm8903_probe(struct snd_soc_codec *codec)
 			snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0,
 					    WM8903_WSEQ_ENA, WM8903_WSEQ_ENA);
 
+		/* If microphone detection is enabled by pdata but
+		 * detected via IRQ then interrupts can be lost before
+		 * the machine driver has set up microphone detection
+		 * IRQs as the IRQs are clear on read.  The detection
+		 * will be enabled when the machine driver configures.
+		 */
+		WARN_ON(!mic_gpio && (pdata->micdet_cfg & WM8903_MICDET_ENA));
+
 		wm8903->mic_delay = pdata->micdet_delay;
 	}
 	
@@ -1741,20 +2014,23 @@ static int wm8903_probe(struct snd_soc_codec *codec)
 	snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val);
 
 	/* Enable DAC soft mute by default */
-	val = snd_soc_read(codec, WM8903_DAC_DIGITAL_1);
-	val |= WM8903_DAC_MUTEMODE;
-	snd_soc_write(codec, WM8903_DAC_DIGITAL_1, val);
+	snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1,
+			    WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE,
+			    WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE);
 
 	snd_soc_add_controls(codec, wm8903_snd_controls,
 				ARRAY_SIZE(wm8903_snd_controls));
 	wm8903_add_widgets(codec);
 
+	wm8903_init_gpio(codec);
+
 	return ret;
 }
 
 /* power down chip */
 static int wm8903_remove(struct snd_soc_codec *codec)
 {
+	wm8903_free_gpio(codec);
 	wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	return 0;
 }
@@ -1769,6 +2045,7 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8903 = {
 	.reg_word_size = sizeof(u16),
 	.reg_cache_default = wm8903_reg_defaults,
 	.volatile_register = wm8903_volatile_register,
+	.seq_notifier = wm8903_seq_notifier,
 };
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
@@ -1807,7 +2084,7 @@ MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id);
 
 static struct i2c_driver wm8903_i2c_driver = {
 	.driver = {
-		.name = "wm8903-codec",
+		.name = "wm8903",
 		.owner = THIS_MODULE,
 	},
 	.probe =    wm8903_i2c_probe,

+ 8 - 0
sound/soc/codecs/wm8903.h

@@ -75,6 +75,14 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec,
 #define WM8903_ANALOGUE_SPK_OUTPUT_CONTROL_0    0x41
 #define WM8903_DC_SERVO_0                       0x43
 #define WM8903_DC_SERVO_2                       0x45
+#define WM8903_DC_SERVO_4			0x47
+#define WM8903_DC_SERVO_5			0x48
+#define WM8903_DC_SERVO_6			0x49
+#define WM8903_DC_SERVO_7			0x4A
+#define WM8903_DC_SERVO_READBACK_1		0x51
+#define WM8903_DC_SERVO_READBACK_2		0x52
+#define WM8903_DC_SERVO_READBACK_3		0x53
+#define WM8903_DC_SERVO_READBACK_4		0x54
 #define WM8903_ANALOGUE_HP_0                    0x5A
 #define WM8903_ANALOGUE_LINEOUT_0               0x5E
 #define WM8903_CHARGE_PUMP_0                    0x62

+ 27 - 16
sound/soc/codecs/wm8904.c

@@ -596,7 +596,7 @@ static struct {
 	{ 0x003F, 0x003F, 0 }, /* R248 - FLL NCO Test 1 */
 };
 
-static int wm8904_volatile_register(unsigned int reg)
+static int wm8904_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	return wm8904_access[reg].vol;
 }
@@ -2436,19 +2436,28 @@ static int wm8904_probe(struct snd_soc_codec *codec)
 	}
 
 	/* Change some default settings - latch VU and enable ZC */
-	reg_cache[WM8904_ADC_DIGITAL_VOLUME_LEFT] |= WM8904_ADC_VU;
-	reg_cache[WM8904_ADC_DIGITAL_VOLUME_RIGHT] |= WM8904_ADC_VU;
-	reg_cache[WM8904_DAC_DIGITAL_VOLUME_LEFT] |= WM8904_DAC_VU;
-	reg_cache[WM8904_DAC_DIGITAL_VOLUME_RIGHT] |= WM8904_DAC_VU;
-	reg_cache[WM8904_ANALOGUE_OUT1_LEFT] |= WM8904_HPOUT_VU |
-		WM8904_HPOUTLZC;
-	reg_cache[WM8904_ANALOGUE_OUT1_RIGHT] |= WM8904_HPOUT_VU |
-		WM8904_HPOUTRZC;
-	reg_cache[WM8904_ANALOGUE_OUT2_LEFT] |= WM8904_LINEOUT_VU |
-		WM8904_LINEOUTLZC;
-	reg_cache[WM8904_ANALOGUE_OUT2_RIGHT] |= WM8904_LINEOUT_VU |
-		WM8904_LINEOUTRZC;
-	reg_cache[WM8904_CLOCK_RATES_0] &= ~WM8904_SR_MODE;
+	snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_LEFT,
+			    WM8904_ADC_VU, WM8904_ADC_VU);
+	snd_soc_update_bits(codec, WM8904_ADC_DIGITAL_VOLUME_RIGHT,
+			    WM8904_ADC_VU, WM8904_ADC_VU);
+	snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_LEFT,
+			    WM8904_DAC_VU, WM8904_DAC_VU);
+	snd_soc_update_bits(codec, WM8904_DAC_DIGITAL_VOLUME_RIGHT,
+			    WM8904_DAC_VU, WM8904_DAC_VU);
+	snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_LEFT,
+			    WM8904_HPOUT_VU | WM8904_HPOUTLZC,
+			    WM8904_HPOUT_VU | WM8904_HPOUTLZC);
+	snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT1_RIGHT,
+			    WM8904_HPOUT_VU | WM8904_HPOUTRZC,
+			    WM8904_HPOUT_VU | WM8904_HPOUTRZC);
+	snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_LEFT,
+			    WM8904_LINEOUT_VU | WM8904_LINEOUTLZC,
+			    WM8904_LINEOUT_VU | WM8904_LINEOUTLZC);
+	snd_soc_update_bits(codec, WM8904_ANALOGUE_OUT2_RIGHT,
+			    WM8904_LINEOUT_VU | WM8904_LINEOUTRZC,
+			    WM8904_LINEOUT_VU | WM8904_LINEOUTRZC);
+	snd_soc_update_bits(codec, WM8904_CLOCK_RATES_0,
+			    WM8904_SR_MODE, 0);
 
 	/* Apply configuration from the platform data. */
 	if (wm8904->pdata) {
@@ -2469,10 +2478,12 @@ static int wm8904_probe(struct snd_soc_codec *codec)
 	/* Set Class W by default - this will be managed by the Class
 	 * G widget at runtime where bypass paths are available.
 	 */
-	reg_cache[WM8904_CLASS_W_0] |= WM8904_CP_DYN_PWR;
+	snd_soc_update_bits(codec, WM8904_CLASS_W_0,
+			    WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR);
 
 	/* Use normal bias source */
-	reg_cache[WM8904_BIAS_CONTROL_0] &= ~WM8904_POBCTRL;
+	snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
+			    WM8904_POBCTRL, 0);
 
 	wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 

+ 19 - 8
sound/soc/codecs/wm8955.c

@@ -934,16 +934,27 @@ static int wm8955_probe(struct snd_soc_codec *codec)
 	}
 
 	/* Change some default settings - latch VU and enable ZC */
-	reg_cache[WM8955_LEFT_DAC_VOLUME] |= WM8955_LDVU;
-	reg_cache[WM8955_RIGHT_DAC_VOLUME] |= WM8955_RDVU;
-	reg_cache[WM8955_LOUT1_VOLUME] |= WM8955_LO1VU | WM8955_LO1ZC;
-	reg_cache[WM8955_ROUT1_VOLUME] |= WM8955_RO1VU | WM8955_RO1ZC;
-	reg_cache[WM8955_LOUT2_VOLUME] |= WM8955_LO2VU | WM8955_LO2ZC;
-	reg_cache[WM8955_ROUT2_VOLUME] |= WM8955_RO2VU | WM8955_RO2ZC;
-	reg_cache[WM8955_MONOOUT_VOLUME] |= WM8955_MOZC;
+	snd_soc_update_bits(codec, WM8955_LEFT_DAC_VOLUME,
+			    WM8955_LDVU, WM8955_LDVU);
+	snd_soc_update_bits(codec, WM8955_RIGHT_DAC_VOLUME,
+			    WM8955_RDVU, WM8955_RDVU);
+	snd_soc_update_bits(codec, WM8955_LOUT1_VOLUME,
+			    WM8955_LO1VU | WM8955_LO1ZC,
+			    WM8955_LO1VU | WM8955_LO1ZC);
+	snd_soc_update_bits(codec, WM8955_ROUT1_VOLUME,
+			    WM8955_RO1VU | WM8955_RO1ZC,
+			    WM8955_RO1VU | WM8955_RO1ZC);
+	snd_soc_update_bits(codec, WM8955_LOUT2_VOLUME,
+			    WM8955_LO2VU | WM8955_LO2ZC,
+			    WM8955_LO2VU | WM8955_LO2ZC);
+	snd_soc_update_bits(codec, WM8955_ROUT2_VOLUME,
+			    WM8955_RO2VU | WM8955_RO2ZC,
+			    WM8955_RO2VU | WM8955_RO2ZC);
+	snd_soc_update_bits(codec, WM8955_MONOOUT_VOLUME,
+			    WM8955_MOZC, WM8955_MOZC);
 
 	/* Also enable adaptive bass boost by default */
-	reg_cache[WM8955_BASS_CONTROL] |= WM8955_BB;
+	snd_soc_update_bits(codec, WM8955_BASS_CONTROL, WM8955_BB, WM8955_BB);
 
 	/* Set platform data values */
 	if (pdata) {

+ 1 - 1
sound/soc/codecs/wm8961.c

@@ -291,7 +291,7 @@ struct wm8961_priv {
 	int sysclk;
 };
 
-static int wm8961_volatile_register(unsigned int reg)
+static int wm8961_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM8961_SOFTWARE_RESET:

+ 23 - 13
sound/soc/codecs/wm8962.c

@@ -1938,7 +1938,7 @@ static const struct wm8962_reg_access {
 	[21139] = { 0xFFFF, 0xFFFF, 0x0000 }, /* R21139 - VSS_XTS32_0 */
 };
 
-static int wm8962_volatile_register(unsigned int reg)
+static int wm8962_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	if (wm8962_reg_access[reg].vol)
 		return 1;
@@ -1946,7 +1946,7 @@ static int wm8962_volatile_register(unsigned int reg)
 		return 0;
 }
 
-static int wm8962_readable_register(unsigned int reg)
+static int wm8962_readable_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	if (wm8962_reg_access[reg].read)
 		return 1;
@@ -3635,7 +3635,7 @@ static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 	struct snd_soc_codec *codec = wm8962->codec;
 
 	snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset,
-			    WM8962_GP2_LVL, value << WM8962_GP2_LVL_SHIFT);
+			    WM8962_GP2_LVL, !!value << WM8962_GP2_LVL_SHIFT);
 }
 
 static int wm8962_gpio_direction_out(struct gpio_chip *chip,
@@ -3822,16 +3822,26 @@ static int wm8962_probe(struct snd_soc_codec *codec)
 	}
 
 	/* Latch volume update bits */
-	reg_cache[WM8962_LEFT_INPUT_VOLUME] |= WM8962_IN_VU;
-	reg_cache[WM8962_RIGHT_INPUT_VOLUME] |= WM8962_IN_VU;
-	reg_cache[WM8962_LEFT_ADC_VOLUME] |= WM8962_ADC_VU;
-	reg_cache[WM8962_RIGHT_ADC_VOLUME] |= WM8962_ADC_VU;
-	reg_cache[WM8962_LEFT_DAC_VOLUME] |= WM8962_DAC_VU;
-	reg_cache[WM8962_RIGHT_DAC_VOLUME] |= WM8962_DAC_VU;
-	reg_cache[WM8962_SPKOUTL_VOLUME] |= WM8962_SPKOUT_VU;
-	reg_cache[WM8962_SPKOUTR_VOLUME] |= WM8962_SPKOUT_VU;
-	reg_cache[WM8962_HPOUTL_VOLUME] |= WM8962_HPOUT_VU;
-	reg_cache[WM8962_HPOUTR_VOLUME] |= WM8962_HPOUT_VU;
+	snd_soc_update_bits(codec, WM8962_LEFT_INPUT_VOLUME,
+			    WM8962_IN_VU, WM8962_IN_VU);
+	snd_soc_update_bits(codec, WM8962_RIGHT_INPUT_VOLUME,
+			    WM8962_IN_VU, WM8962_IN_VU);
+	snd_soc_update_bits(codec, WM8962_LEFT_ADC_VOLUME,
+			    WM8962_ADC_VU, WM8962_ADC_VU);
+	snd_soc_update_bits(codec, WM8962_RIGHT_ADC_VOLUME,
+			    WM8962_ADC_VU, WM8962_ADC_VU);
+	snd_soc_update_bits(codec, WM8962_LEFT_DAC_VOLUME,
+			    WM8962_DAC_VU, WM8962_DAC_VU);
+	snd_soc_update_bits(codec, WM8962_RIGHT_DAC_VOLUME,
+			    WM8962_DAC_VU, WM8962_DAC_VU);
+	snd_soc_update_bits(codec, WM8962_SPKOUTL_VOLUME,
+			    WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
+	snd_soc_update_bits(codec, WM8962_SPKOUTR_VOLUME,
+			    WM8962_SPKOUT_VU, WM8962_SPKOUT_VU);
+	snd_soc_update_bits(codec, WM8962_HPOUTL_VOLUME,
+			    WM8962_HPOUT_VU, WM8962_HPOUT_VU);
+	snd_soc_update_bits(codec, WM8962_HPOUTR_VOLUME,
+			    WM8962_HPOUT_VU, WM8962_HPOUT_VU);
 
 	wm8962_add_widgets(codec);
 

+ 11 - 8
sound/soc/codecs/wm8978.c

@@ -93,6 +93,7 @@ static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0);
 static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0);
 static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1);
+static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0);
 
 static const struct snd_kcontrol_new wm8978_snd_controls[] = {
 
@@ -144,19 +145,19 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = {
 
 	SOC_SINGLE("DAC Playback Limiter Threshold",
 		WM8978_DAC_LIMITER_2, 4, 7, 0),
-	SOC_SINGLE("DAC Playback Limiter Boost",
-		WM8978_DAC_LIMITER_2, 0, 15, 0),
+	SOC_SINGLE_TLV("DAC Playback Limiter Volume",
+		WM8978_DAC_LIMITER_2, 0, 12, 0, limiter_tlv),
 
 	SOC_ENUM("ALC Enable Switch", alc1),
 	SOC_SINGLE("ALC Capture Min Gain", WM8978_ALC_CONTROL_1, 0, 7, 0),
 	SOC_SINGLE("ALC Capture Max Gain", WM8978_ALC_CONTROL_1, 3, 7, 0),
 
-	SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 7, 0),
+	SOC_SINGLE("ALC Capture Hold", WM8978_ALC_CONTROL_2, 4, 10, 0),
 	SOC_SINGLE("ALC Capture Target", WM8978_ALC_CONTROL_2, 0, 15, 0),
 
 	SOC_ENUM("ALC Capture Mode", alc3),
-	SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 15, 0),
-	SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 15, 0),
+	SOC_SINGLE("ALC Capture Decay", WM8978_ALC_CONTROL_3, 4, 10, 0),
+	SOC_SINGLE("ALC Capture Attack", WM8978_ALC_CONTROL_3, 0, 10, 0),
 
 	SOC_SINGLE("ALC Capture Noise Gate Switch", WM8978_NOISE_GATE, 3, 1, 0),
 	SOC_SINGLE("ALC Capture Noise Gate Threshold",
@@ -211,8 +212,10 @@ static const struct snd_kcontrol_new wm8978_snd_controls[] = {
 		WM8978_LOUT2_SPK_CONTROL, WM8978_ROUT2_SPK_CONTROL, 6, 1, 1),
 
 	/* DAC / ADC oversampling */
-	SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL, 8, 1, 0),
-	SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL, 8, 1, 0),
+	SOC_SINGLE("DAC 128x Oversampling Switch", WM8978_DAC_CONTROL,
+		   5, 1, 0),
+	SOC_SINGLE("ADC 128x Oversampling Switch", WM8978_ADC_CONTROL,
+		   5, 1, 0),
 };
 
 /* Mixer #1: Output (OUT1, OUT2) Mixer: mix AUX, Input mixer output and DAC */
@@ -965,7 +968,7 @@ static int wm8978_probe(struct snd_soc_codec *codec)
 	 * written.
 	 */
 	for (i = 0; i < ARRAY_SIZE(update_reg); i++)
-		((u16 *)codec->reg_cache)[update_reg[i]] |= 0x100;
+		snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100);
 
 	/* Reset the codec */
 	ret = snd_soc_write(codec, WM8978_RESET, 0);

+ 1427 - 0
sound/soc/codecs/wm8991.c

@@ -0,0 +1,1427 @@
+/*
+ * wm8991.c  --  WM8991 ALSA Soc Audio driver
+ *
+ * Copyright 2007-2010 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include "wm8991.h"
+
+struct wm8991_priv {
+	enum snd_soc_control_type control_type;
+	unsigned int pcmclk;
+};
+
+static const u16 wm8991_reg_defs[] = {
+	0x8991,     /* R0  - Reset */
+	0x0000,     /* R1  - Power Management (1) */
+	0x6000,     /* R2  - Power Management (2) */
+	0x0000,     /* R3  - Power Management (3) */
+	0x4050,     /* R4  - Audio Interface (1) */
+	0x4000,     /* R5  - Audio Interface (2) */
+	0x01C8,     /* R6  - Clocking (1) */
+	0x0000,     /* R7  - Clocking (2) */
+	0x0040,     /* R8  - Audio Interface (3) */
+	0x0040,     /* R9  - Audio Interface (4) */
+	0x0004,     /* R10 - DAC CTRL */
+	0x00C0,     /* R11 - Left DAC Digital Volume */
+	0x00C0,     /* R12 - Right DAC Digital Volume */
+	0x0000,     /* R13 - Digital Side Tone */
+	0x0100,     /* R14 - ADC CTRL */
+	0x00C0,     /* R15 - Left ADC Digital Volume */
+	0x00C0,     /* R16 - Right ADC Digital Volume */
+	0x0000,     /* R17 */
+	0x0000,     /* R18 - GPIO CTRL 1 */
+	0x1000,     /* R19 - GPIO1 & GPIO2 */
+	0x1010,     /* R20 - GPIO3 & GPIO4 */
+	0x1010,     /* R21 - GPIO5 & GPIO6 */
+	0x8000,     /* R22 - GPIOCTRL 2 */
+	0x0800,     /* R23 - GPIO_POL */
+	0x008B,     /* R24 - Left Line Input 1&2 Volume */
+	0x008B,     /* R25 - Left Line Input 3&4 Volume */
+	0x008B,     /* R26 - Right Line Input 1&2 Volume */
+	0x008B,     /* R27 - Right Line Input 3&4 Volume */
+	0x0000,     /* R28 - Left Output Volume */
+	0x0000,     /* R29 - Right Output Volume */
+	0x0066,     /* R30 - Line Outputs Volume */
+	0x0022,     /* R31 - Out3/4 Volume */
+	0x0079,     /* R32 - Left OPGA Volume */
+	0x0079,     /* R33 - Right OPGA Volume */
+	0x0003,     /* R34 - Speaker Volume */
+	0x0003,     /* R35 - ClassD1 */
+	0x0000,     /* R36 */
+	0x0100,     /* R37 - ClassD3 */
+	0x0000,     /* R38 */
+	0x0000,     /* R39 - Input Mixer1 */
+	0x0000,     /* R40 - Input Mixer2 */
+	0x0000,     /* R41 - Input Mixer3 */
+	0x0000,     /* R42 - Input Mixer4 */
+	0x0000,     /* R43 - Input Mixer5 */
+	0x0000,     /* R44 - Input Mixer6 */
+	0x0000,     /* R45 - Output Mixer1 */
+	0x0000,     /* R46 - Output Mixer2 */
+	0x0000,     /* R47 - Output Mixer3 */
+	0x0000,     /* R48 - Output Mixer4 */
+	0x0000,     /* R49 - Output Mixer5 */
+	0x0000,     /* R50 - Output Mixer6 */
+	0x0180,     /* R51 - Out3/4 Mixer */
+	0x0000,     /* R52 - Line Mixer1 */
+	0x0000,     /* R53 - Line Mixer2 */
+	0x0000,     /* R54 - Speaker Mixer */
+	0x0000,     /* R55 - Additional Control */
+	0x0000,     /* R56 - AntiPOP1 */
+	0x0000,     /* R57 - AntiPOP2 */
+	0x0000,     /* R58 - MICBIAS */
+	0x0000,     /* R59 */
+	0x0008,     /* R60 - PLL1 */
+	0x0031,     /* R61 - PLL2 */
+	0x0026,     /* R62 - PLL3 */
+};
+
+#define wm8991_reset(c) snd_soc_write(c, WM8991_RESET, 0)
+
+static const unsigned int rec_mix_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 7, TLV_DB_LINEAR_ITEM(-1500, 600),
+};
+
+static const unsigned int in_pga_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 0x1F, TLV_DB_LINEAR_ITEM(-1650, 3000),
+};
+
+static const unsigned int out_mix_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 7, TLV_DB_LINEAR_ITEM(0, -2100),
+};
+
+static const unsigned int out_pga_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 127, TLV_DB_LINEAR_ITEM(-7300, 600),
+};
+
+static const unsigned int out_omix_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 7, TLV_DB_LINEAR_ITEM(-600, 0),
+};
+
+static const unsigned int out_dac_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 255, TLV_DB_LINEAR_ITEM(-7163, 0),
+};
+
+static const unsigned int in_adc_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 255, TLV_DB_LINEAR_ITEM(-7163, 1763),
+};
+
+static const unsigned int out_sidetone_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 31, TLV_DB_LINEAR_ITEM(-3600, 0),
+};
+
+static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value & 0xff;
+	int ret;
+	u16 val;
+
+	ret = snd_soc_put_volsw(kcontrol, ucontrol);
+	if (ret < 0)
+		return ret;
+
+	/* now hit the volume update bits (always bit 8) */
+	val = snd_soc_read(codec, reg);
+	return snd_soc_write(codec, reg, val | 0x0100);
+}
+
+static const char *wm8991_digital_sidetone[] =
+{"None", "Left ADC", "Right ADC", "Reserved"};
+
+static const struct soc_enum wm8991_left_digital_sidetone_enum =
+	SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
+			WM8991_ADC_TO_DACL_SHIFT,
+			WM8991_ADC_TO_DACL_MASK,
+			wm8991_digital_sidetone);
+
+static const struct soc_enum wm8991_right_digital_sidetone_enum =
+	SOC_ENUM_SINGLE(WM8991_DIGITAL_SIDE_TONE,
+			WM8991_ADC_TO_DACR_SHIFT,
+			WM8991_ADC_TO_DACR_MASK,
+			wm8991_digital_sidetone);
+
+static const char *wm8991_adcmode[] =
+{"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"};
+
+static const struct soc_enum wm8991_right_adcmode_enum =
+	SOC_ENUM_SINGLE(WM8991_ADC_CTRL,
+			WM8991_ADC_HPF_CUT_SHIFT,
+			WM8991_ADC_HPF_CUT_MASK,
+			wm8991_adcmode);
+
+static const struct snd_kcontrol_new wm8991_snd_controls[] = {
+	/* INMIXL */
+	SOC_SINGLE("LIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L12MNBST_BIT, 1, 0),
+	SOC_SINGLE("LIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_L34MNBST_BIT, 1, 0),
+	/* INMIXR */
+	SOC_SINGLE("RIN12 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R12MNBST_BIT, 1, 0),
+	SOC_SINGLE("RIN34 PGA Boost", WM8991_INPUT_MIXER3, WM8991_R34MNBST_BIT, 1, 0),
+
+	/* LOMIX */
+	SOC_SINGLE_TLV("LOMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER3,
+		WM8991_LLI3LOVOL_SHIFT, WM8991_LLI3LOVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("LOMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
+		WM8991_LR12LOVOL_SHIFT, WM8991_LR12LOVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("LOMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER3,
+		WM8991_LL12LOVOL_SHIFT, WM8991_LL12LOVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("LOMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER5,
+		WM8991_LRI3LOVOL_SHIFT, WM8991_LRI3LOVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("LOMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
+		WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("LOMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER5,
+		WM8991_LRBLOVOL_SHIFT, WM8991_LRBLOVOL_MASK, 1, out_mix_tlv),
+
+	/* ROMIX */
+	SOC_SINGLE_TLV("ROMIX RIN3 Bypass Volume", WM8991_OUTPUT_MIXER4,
+		WM8991_RRI3ROVOL_SHIFT, WM8991_RRI3ROVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("ROMIX LIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
+		WM8991_RL12ROVOL_SHIFT, WM8991_RL12ROVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("ROMIX RIN12 PGA Bypass Volume", WM8991_OUTPUT_MIXER4,
+		WM8991_RR12ROVOL_SHIFT, WM8991_RR12ROVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("ROMIX LIN3 Bypass Volume", WM8991_OUTPUT_MIXER6,
+		WM8991_RLI3ROVOL_SHIFT, WM8991_RLI3ROVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("ROMIX AINLMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
+		WM8991_RLBROVOL_SHIFT, WM8991_RLBROVOL_MASK, 1, out_mix_tlv),
+	SOC_SINGLE_TLV("ROMIX AINRMUX Bypass Volume", WM8991_OUTPUT_MIXER6,
+		WM8991_RRBROVOL_SHIFT, WM8991_RRBROVOL_MASK, 1, out_mix_tlv),
+
+	/* LOUT */
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOUT Volume", WM8991_LEFT_OUTPUT_VOLUME,
+		WM8991_LOUTVOL_SHIFT, WM8991_LOUTVOL_MASK, 0, out_pga_tlv),
+	SOC_SINGLE("LOUT ZC", WM8991_LEFT_OUTPUT_VOLUME, WM8991_LOZC_BIT, 1, 0),
+
+	/* ROUT */
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROUT Volume", WM8991_RIGHT_OUTPUT_VOLUME,
+		WM8991_ROUTVOL_SHIFT, WM8991_ROUTVOL_MASK, 0, out_pga_tlv),
+	SOC_SINGLE("ROUT ZC", WM8991_RIGHT_OUTPUT_VOLUME, WM8991_ROZC_BIT, 1, 0),
+
+	/* LOPGA */
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("LOPGA Volume", WM8991_LEFT_OPGA_VOLUME,
+		WM8991_LOPGAVOL_SHIFT, WM8991_LOPGAVOL_MASK, 0, out_pga_tlv),
+	SOC_SINGLE("LOPGA ZC Switch", WM8991_LEFT_OPGA_VOLUME,
+		WM8991_LOPGAZC_BIT, 1, 0),
+
+	/* ROPGA */
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("ROPGA Volume", WM8991_RIGHT_OPGA_VOLUME,
+		WM8991_ROPGAVOL_SHIFT, WM8991_ROPGAVOL_MASK, 0, out_pga_tlv),
+	SOC_SINGLE("ROPGA ZC Switch", WM8991_RIGHT_OPGA_VOLUME,
+		WM8991_ROPGAZC_BIT, 1, 0),
+
+	SOC_SINGLE("LON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_LONMUTE_BIT, 1, 0),
+	SOC_SINGLE("LOP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_LOPMUTE_BIT, 1, 0),
+	SOC_SINGLE("LOP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_LOATTN_BIT, 1, 0),
+	SOC_SINGLE("RON Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_RONMUTE_BIT, 1, 0),
+	SOC_SINGLE("ROP Mute Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_ROPMUTE_BIT, 1, 0),
+	SOC_SINGLE("ROP Attenuation Switch", WM8991_LINE_OUTPUTS_VOLUME,
+		WM8991_ROATTN_BIT, 1, 0),
+
+	SOC_SINGLE("OUT3 Mute Switch", WM8991_OUT3_4_VOLUME,
+		WM8991_OUT3MUTE_BIT, 1, 0),
+	SOC_SINGLE("OUT3 Attenuation Switch", WM8991_OUT3_4_VOLUME,
+		WM8991_OUT3ATTN_BIT, 1, 0),
+
+	SOC_SINGLE("OUT4 Mute Switch", WM8991_OUT3_4_VOLUME,
+		WM8991_OUT4MUTE_BIT, 1, 0),
+	SOC_SINGLE("OUT4 Attenuation Switch", WM8991_OUT3_4_VOLUME,
+		WM8991_OUT4ATTN_BIT, 1, 0),
+
+	SOC_SINGLE("Speaker Mode Switch", WM8991_CLASSD1,
+		WM8991_CDMODE_BIT, 1, 0),
+
+	SOC_SINGLE("Speaker Output Attenuation Volume", WM8991_SPEAKER_VOLUME,
+		WM8991_SPKVOL_SHIFT, WM8991_SPKVOL_MASK, 0),
+	SOC_SINGLE("Speaker DC Boost Volume", WM8991_CLASSD3,
+		WM8991_DCGAIN_SHIFT, WM8991_DCGAIN_MASK, 0),
+	SOC_SINGLE("Speaker AC Boost Volume", WM8991_CLASSD3,
+		WM8991_ACGAIN_SHIFT, WM8991_ACGAIN_MASK, 0),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left DAC Digital Volume",
+		WM8991_LEFT_DAC_DIGITAL_VOLUME,
+		WM8991_DACL_VOL_SHIFT,
+		WM8991_DACL_VOL_MASK,
+		0,
+		out_dac_tlv),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right DAC Digital Volume",
+		WM8991_RIGHT_DAC_DIGITAL_VOLUME,
+		WM8991_DACR_VOL_SHIFT,
+		WM8991_DACR_VOL_MASK,
+		0,
+		out_dac_tlv),
+
+	SOC_ENUM("Left Digital Sidetone", wm8991_left_digital_sidetone_enum),
+	SOC_ENUM("Right Digital Sidetone", wm8991_right_digital_sidetone_enum),
+
+	SOC_SINGLE_TLV("Left Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
+		WM8991_ADCL_DAC_SVOL_SHIFT, WM8991_ADCL_DAC_SVOL_MASK, 0,
+		out_sidetone_tlv),
+	SOC_SINGLE_TLV("Right Digital Sidetone Volume", WM8991_DIGITAL_SIDE_TONE,
+		WM8991_ADCR_DAC_SVOL_SHIFT, WM8991_ADCR_DAC_SVOL_MASK, 0,
+		out_sidetone_tlv),
+
+	SOC_SINGLE("ADC Digital High Pass Filter Switch", WM8991_ADC_CTRL,
+		WM8991_ADC_HPF_ENA_BIT, 1, 0),
+
+	SOC_ENUM("ADC HPF Mode", wm8991_right_adcmode_enum),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("Left ADC Digital Volume",
+		WM8991_LEFT_ADC_DIGITAL_VOLUME,
+		WM8991_ADCL_VOL_SHIFT,
+		WM8991_ADCL_VOL_MASK,
+		0,
+		in_adc_tlv),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("Right ADC Digital Volume",
+		WM8991_RIGHT_ADC_DIGITAL_VOLUME,
+		WM8991_ADCR_VOL_SHIFT,
+		WM8991_ADCR_VOL_MASK,
+		0,
+		in_adc_tlv),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN12 Volume",
+		WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+		WM8991_LIN12VOL_SHIFT,
+		WM8991_LIN12VOL_MASK,
+		0,
+		in_pga_tlv),
+
+	SOC_SINGLE("LIN12 ZC Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+		WM8991_LI12ZC_BIT, 1, 0),
+
+	SOC_SINGLE("LIN12 Mute Switch", WM8991_LEFT_LINE_INPUT_1_2_VOLUME,
+		WM8991_LI12MUTE_BIT, 1, 0),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("LIN34 Volume",
+		WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+		WM8991_LIN34VOL_SHIFT,
+		WM8991_LIN34VOL_MASK,
+		0,
+		in_pga_tlv),
+
+	SOC_SINGLE("LIN34 ZC Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+		WM8991_LI34ZC_BIT, 1, 0),
+
+	SOC_SINGLE("LIN34 Mute Switch", WM8991_LEFT_LINE_INPUT_3_4_VOLUME,
+		WM8991_LI34MUTE_BIT, 1, 0),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN12 Volume",
+		WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+		WM8991_RIN12VOL_SHIFT,
+		WM8991_RIN12VOL_MASK,
+		0,
+		in_pga_tlv),
+
+	SOC_SINGLE("RIN12 ZC Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+		WM8991_RI12ZC_BIT, 1, 0),
+
+	SOC_SINGLE("RIN12 Mute Switch", WM8991_RIGHT_LINE_INPUT_1_2_VOLUME,
+		WM8991_RI12MUTE_BIT, 1, 0),
+
+	SOC_WM899X_OUTPGA_SINGLE_R_TLV("RIN34 Volume",
+		WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+		WM8991_RIN34VOL_SHIFT,
+		WM8991_RIN34VOL_MASK,
+		0,
+		in_pga_tlv),
+
+	SOC_SINGLE("RIN34 ZC Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+		WM8991_RI34ZC_BIT, 1, 0),
+
+	SOC_SINGLE("RIN34 Mute Switch", WM8991_RIGHT_LINE_INPUT_3_4_VOLUME,
+		WM8991_RI34MUTE_BIT, 1, 0),
+};
+
+/*
+ * _DAPM_ Controls
+ */
+static int inmixer_event(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol, int event)
+{
+	u16 reg, fakepower;
+
+	reg = snd_soc_read(w->codec, WM8991_POWER_MANAGEMENT_2);
+	fakepower = snd_soc_read(w->codec, WM8991_INTDRIVBITS);
+
+	if (fakepower & ((1 << WM8991_INMIXL_PWR_BIT) |
+			 (1 << WM8991_AINLMUX_PWR_BIT)))
+		reg |= WM8991_AINL_ENA;
+	else
+		reg &= ~WM8991_AINL_ENA;
+
+	if (fakepower & ((1 << WM8991_INMIXR_PWR_BIT) |
+			 (1 << WM8991_AINRMUX_PWR_BIT)))
+		reg |= WM8991_AINR_ENA;
+	else
+		reg &= ~WM8991_AINL_ENA;
+
+	snd_soc_write(w->codec, WM8991_POWER_MANAGEMENT_2, reg);
+	return 0;
+}
+
+static int outmixer_event(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *kcontrol, int event)
+{
+	u32 reg_shift = kcontrol->private_value & 0xfff;
+	int ret = 0;
+	u16 reg;
+
+	switch (reg_shift) {
+	case WM8991_SPEAKER_MIXER | (WM8991_LDSPK_BIT << 8):
+		reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER1);
+		if (reg & WM8991_LDLO) {
+			printk(KERN_WARNING
+			       "Cannot set as Output Mixer 1 LDLO Set\n");
+			ret = -1;
+		}
+		break;
+
+	case WM8991_SPEAKER_MIXER | (WM8991_RDSPK_BIT << 8):
+		reg = snd_soc_read(w->codec, WM8991_OUTPUT_MIXER2);
+		if (reg & WM8991_RDRO) {
+			printk(KERN_WARNING
+			       "Cannot set as Output Mixer 2 RDRO Set\n");
+			ret = -1;
+		}
+		break;
+
+	case WM8991_OUTPUT_MIXER1 | (WM8991_LDLO_BIT << 8):
+		reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER);
+		if (reg & WM8991_LDSPK) {
+			printk(KERN_WARNING
+			       "Cannot set as Speaker Mixer LDSPK Set\n");
+			ret = -1;
+		}
+		break;
+
+	case WM8991_OUTPUT_MIXER2 | (WM8991_RDRO_BIT << 8):
+		reg = snd_soc_read(w->codec, WM8991_SPEAKER_MIXER);
+		if (reg & WM8991_RDSPK) {
+			printk(KERN_WARNING
+			       "Cannot set as Speaker Mixer RDSPK Set\n");
+			ret = -1;
+		}
+		break;
+	}
+
+	return ret;
+}
+
+/* INMIX dB values */
+static const unsigned int in_mix_tlv[] = {
+	TLV_DB_RANGE_HEAD(1),
+	0, 7, TLV_DB_LINEAR_ITEM(-1200, 600),
+};
+
+/* Left In PGA Connections */
+static const struct snd_kcontrol_new wm8991_dapm_lin12_pga_controls[] = {
+	SOC_DAPM_SINGLE("LIN1 Switch", WM8991_INPUT_MIXER2, WM8991_LMN1_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LIN2 Switch", WM8991_INPUT_MIXER2, WM8991_LMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8991_dapm_lin34_pga_controls[] = {
+	SOC_DAPM_SINGLE("LIN3 Switch", WM8991_INPUT_MIXER2, WM8991_LMN3_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LIN4 Switch", WM8991_INPUT_MIXER2, WM8991_LMP4_BIT, 1, 0),
+};
+
+/* Right In PGA Connections */
+static const struct snd_kcontrol_new wm8991_dapm_rin12_pga_controls[] = {
+	SOC_DAPM_SINGLE("RIN1 Switch", WM8991_INPUT_MIXER2, WM8991_RMN1_BIT, 1, 0),
+	SOC_DAPM_SINGLE("RIN2 Switch", WM8991_INPUT_MIXER2, WM8991_RMP2_BIT, 1, 0),
+};
+
+static const struct snd_kcontrol_new wm8991_dapm_rin34_pga_controls[] = {
+	SOC_DAPM_SINGLE("RIN3 Switch", WM8991_INPUT_MIXER2, WM8991_RMN3_BIT, 1, 0),
+	SOC_DAPM_SINGLE("RIN4 Switch", WM8991_INPUT_MIXER2, WM8991_RMP4_BIT, 1, 0),
+};
+
+/* INMIXL */
+static const struct snd_kcontrol_new wm8991_dapm_inmixl_controls[] = {
+	SOC_DAPM_SINGLE_TLV("Record Left Volume", WM8991_INPUT_MIXER3,
+		WM8991_LDBVOL_SHIFT, WM8991_LDBVOL_MASK, 0, in_mix_tlv),
+	SOC_DAPM_SINGLE_TLV("LIN2 Volume", WM8991_INPUT_MIXER5, WM8991_LI2BVOL_SHIFT,
+		7, 0, in_mix_tlv),
+	SOC_DAPM_SINGLE("LINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
+		1, 0),
+	SOC_DAPM_SINGLE("LINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
+		1, 0),
+};
+
+/* INMIXR */
+static const struct snd_kcontrol_new wm8991_dapm_inmixr_controls[] = {
+	SOC_DAPM_SINGLE_TLV("Record Right Volume", WM8991_INPUT_MIXER4,
+		WM8991_RDBVOL_SHIFT, WM8991_RDBVOL_MASK, 0, in_mix_tlv),
+	SOC_DAPM_SINGLE_TLV("RIN2 Volume", WM8991_INPUT_MIXER6, WM8991_RI2BVOL_SHIFT,
+		7, 0, in_mix_tlv),
+	SOC_DAPM_SINGLE("RINPGA12 Switch", WM8991_INPUT_MIXER3, WM8991_L12MNB_BIT,
+		1, 0),
+	SOC_DAPM_SINGLE("RINPGA34 Switch", WM8991_INPUT_MIXER3, WM8991_L34MNB_BIT,
+		1, 0),
+};
+
+/* AINLMUX */
+static const char *wm8991_ainlmux[] =
+{"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"};
+
+static const struct soc_enum wm8991_ainlmux_enum =
+	SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINLMODE_SHIFT,
+			ARRAY_SIZE(wm8991_ainlmux), wm8991_ainlmux);
+
+static const struct snd_kcontrol_new wm8991_dapm_ainlmux_controls =
+	SOC_DAPM_ENUM("Route", wm8991_ainlmux_enum);
+
+/* DIFFINL */
+
+/* AINRMUX */
+static const char *wm8991_ainrmux[] =
+{"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"};
+
+static const struct soc_enum wm8991_ainrmux_enum =
+	SOC_ENUM_SINGLE(WM8991_INPUT_MIXER1, WM8991_AINRMODE_SHIFT,
+			ARRAY_SIZE(wm8991_ainrmux), wm8991_ainrmux);
+
+static const struct snd_kcontrol_new wm8991_dapm_ainrmux_controls =
+	SOC_DAPM_ENUM("Route", wm8991_ainrmux_enum);
+
+/* RXVOICE */
+static const struct snd_kcontrol_new wm8991_dapm_rxvoice_controls[] = {
+	SOC_DAPM_SINGLE_TLV("LIN4RXN", WM8991_INPUT_MIXER5, WM8991_LR4BVOL_SHIFT,
+		WM8991_LR4BVOL_MASK, 0, in_mix_tlv),
+	SOC_DAPM_SINGLE_TLV("RIN4RXP", WM8991_INPUT_MIXER6, WM8991_RL4BVOL_SHIFT,
+		WM8991_RL4BVOL_MASK, 0, in_mix_tlv),
+};
+
+/* LOMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lomix_controls[] = {
+	SOC_DAPM_SINGLE("LOMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LRBLO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LLBLO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LRI3LO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LLI3LO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LR12LO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LL12LO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOMIX Left DAC Switch", WM8991_OUTPUT_MIXER1,
+		WM8991_LDLO_BIT, 1, 0),
+};
+
+/* ROMIX */
+static const struct snd_kcontrol_new wm8991_dapm_romix_controls[] = {
+	SOC_DAPM_SINGLE("ROMIX Left ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RLBRO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX Right ADC Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RRBRO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX LIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RLI3RO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX RIN3 Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RRI3RO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX LIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RL12RO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX RIN12 PGA Bypass Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RR12RO_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROMIX Right DAC Switch", WM8991_OUTPUT_MIXER2,
+		WM8991_RDRO_BIT, 1, 0),
+};
+
+/* LONMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lonmix_controls[] = {
+	SOC_DAPM_SINGLE("LONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
+		WM8991_LLOPGALON_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER1,
+		WM8991_LROPGALON_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LONMIX Inverted LOP Switch", WM8991_LINE_MIXER1,
+		WM8991_LOPLON_BIT, 1, 0),
+};
+
+/* LOPMIX */
+static const struct snd_kcontrol_new wm8991_dapm_lopmix_controls[] = {
+	SOC_DAPM_SINGLE("LOPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER1,
+		WM8991_LR12LOP_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER1,
+		WM8991_LL12LOP_BIT, 1, 0),
+	SOC_DAPM_SINGLE("LOPMIX Left Mixer PGA Switch", WM8991_LINE_MIXER1,
+		WM8991_LLOPGALOP_BIT, 1, 0),
+};
+
+/* RONMIX */
+static const struct snd_kcontrol_new wm8991_dapm_ronmix_controls[] = {
+	SOC_DAPM_SINGLE("RONMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
+		WM8991_RROPGARON_BIT, 1, 0),
+	SOC_DAPM_SINGLE("RONMIX Left Mixer PGA Switch", WM8991_LINE_MIXER2,
+		WM8991_RLOPGARON_BIT, 1, 0),
+	SOC_DAPM_SINGLE("RONMIX Inverted ROP Switch", WM8991_LINE_MIXER2,
+		WM8991_ROPRON_BIT, 1, 0),
+};
+
+/* ROPMIX */
+static const struct snd_kcontrol_new wm8991_dapm_ropmix_controls[] = {
+	SOC_DAPM_SINGLE("ROPMIX Left Mic Bypass Switch", WM8991_LINE_MIXER2,
+		WM8991_RL12ROP_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROPMIX Right Mic Bypass Switch", WM8991_LINE_MIXER2,
+		WM8991_RR12ROP_BIT, 1, 0),
+	SOC_DAPM_SINGLE("ROPMIX Right Mixer PGA Switch", WM8991_LINE_MIXER2,
+		WM8991_RROPGAROP_BIT, 1, 0),
+};
+
+/* OUT3MIX */
+static const struct snd_kcontrol_new wm8991_dapm_out3mix_controls[] = {
+	SOC_DAPM_SINGLE("OUT3MIX LIN4RXN Bypass Switch", WM8991_OUT3_4_MIXER,
+		WM8991_LI4O3_BIT, 1, 0),
+	SOC_DAPM_SINGLE("OUT3MIX Left Out PGA Switch", WM8991_OUT3_4_MIXER,
+		WM8991_LPGAO3_BIT, 1, 0),
+};
+
+/* OUT4MIX */
+static const struct snd_kcontrol_new wm8991_dapm_out4mix_controls[] = {
+	SOC_DAPM_SINGLE("OUT4MIX Right Out PGA Switch", WM8991_OUT3_4_MIXER,
+		WM8991_RPGAO4_BIT, 1, 0),
+	SOC_DAPM_SINGLE("OUT4MIX RIN4RXP Bypass Switch", WM8991_OUT3_4_MIXER,
+		WM8991_RI4O4_BIT, 1, 0),
+};
+
+/* SPKMIX */
+static const struct snd_kcontrol_new wm8991_dapm_spkmix_controls[] = {
+	SOC_DAPM_SINGLE("SPKMIX LIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
+		WM8991_LI2SPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX LADC Bypass Switch", WM8991_SPEAKER_MIXER,
+		WM8991_LB2SPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX Left Mixer PGA Switch", WM8991_SPEAKER_MIXER,
+		WM8991_LOPGASPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX Left DAC Switch", WM8991_SPEAKER_MIXER,
+		WM8991_LDSPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX Right DAC Switch", WM8991_SPEAKER_MIXER,
+		WM8991_RDSPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX Right Mixer PGA Switch", WM8991_SPEAKER_MIXER,
+		WM8991_ROPGASPK_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX RADC Bypass Switch", WM8991_SPEAKER_MIXER,
+		WM8991_RL12ROP_BIT, 1, 0),
+	SOC_DAPM_SINGLE("SPKMIX RIN2 Bypass Switch", WM8991_SPEAKER_MIXER,
+		WM8991_RI2SPK_BIT, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget wm8991_dapm_widgets[] = {
+	/* Input Side */
+	/* Input Lines */
+	SND_SOC_DAPM_INPUT("LIN1"),
+	SND_SOC_DAPM_INPUT("LIN2"),
+	SND_SOC_DAPM_INPUT("LIN3"),
+	SND_SOC_DAPM_INPUT("LIN4RXN"),
+	SND_SOC_DAPM_INPUT("RIN3"),
+	SND_SOC_DAPM_INPUT("RIN4RXP"),
+	SND_SOC_DAPM_INPUT("RIN1"),
+	SND_SOC_DAPM_INPUT("RIN2"),
+	SND_SOC_DAPM_INPUT("Internal ADC Source"),
+
+	/* DACs */
+	SND_SOC_DAPM_ADC("Left ADC", "Left Capture", WM8991_POWER_MANAGEMENT_2,
+		WM8991_ADCL_ENA_BIT, 0),
+	SND_SOC_DAPM_ADC("Right ADC", "Right Capture", WM8991_POWER_MANAGEMENT_2,
+		WM8991_ADCR_ENA_BIT, 0),
+
+	/* Input PGAs */
+	SND_SOC_DAPM_MIXER("LIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN12_ENA_BIT,
+		0, &wm8991_dapm_lin12_pga_controls[0],
+		ARRAY_SIZE(wm8991_dapm_lin12_pga_controls)),
+	SND_SOC_DAPM_MIXER("LIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_LIN34_ENA_BIT,
+		0, &wm8991_dapm_lin34_pga_controls[0],
+		ARRAY_SIZE(wm8991_dapm_lin34_pga_controls)),
+	SND_SOC_DAPM_MIXER("RIN12 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN12_ENA_BIT,
+		0, &wm8991_dapm_rin12_pga_controls[0],
+		ARRAY_SIZE(wm8991_dapm_rin12_pga_controls)),
+	SND_SOC_DAPM_MIXER("RIN34 PGA", WM8991_POWER_MANAGEMENT_2, WM8991_RIN34_ENA_BIT,
+		0, &wm8991_dapm_rin34_pga_controls[0],
+		ARRAY_SIZE(wm8991_dapm_rin34_pga_controls)),
+
+	/* INMIXL */
+	SND_SOC_DAPM_MIXER_E("INMIXL", WM8991_INTDRIVBITS, WM8991_INMIXL_PWR_BIT, 0,
+		&wm8991_dapm_inmixl_controls[0],
+		ARRAY_SIZE(wm8991_dapm_inmixl_controls),
+		inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* AINLMUX */
+	SND_SOC_DAPM_MUX_E("AINLMUX", WM8991_INTDRIVBITS, WM8991_AINLMUX_PWR_BIT, 0,
+		&wm8991_dapm_ainlmux_controls, inmixer_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* INMIXR */
+	SND_SOC_DAPM_MIXER_E("INMIXR", WM8991_INTDRIVBITS, WM8991_INMIXR_PWR_BIT, 0,
+		&wm8991_dapm_inmixr_controls[0],
+		ARRAY_SIZE(wm8991_dapm_inmixr_controls),
+		inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* AINRMUX */
+	SND_SOC_DAPM_MUX_E("AINRMUX", WM8991_INTDRIVBITS, WM8991_AINRMUX_PWR_BIT, 0,
+		&wm8991_dapm_ainrmux_controls, inmixer_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* Output Side */
+	/* DACs */
+	SND_SOC_DAPM_DAC("Left DAC", "Left Playback", WM8991_POWER_MANAGEMENT_3,
+		WM8991_DACL_ENA_BIT, 0),
+	SND_SOC_DAPM_DAC("Right DAC", "Right Playback", WM8991_POWER_MANAGEMENT_3,
+		WM8991_DACR_ENA_BIT, 0),
+
+	/* LOMIX */
+	SND_SOC_DAPM_MIXER_E("LOMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOMIX_ENA_BIT,
+		0, &wm8991_dapm_lomix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_lomix_controls),
+		outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+	/* LONMIX */
+	SND_SOC_DAPM_MIXER("LONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LON_ENA_BIT, 0,
+		&wm8991_dapm_lonmix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_lonmix_controls)),
+
+	/* LOPMIX */
+	SND_SOC_DAPM_MIXER("LOPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_LOP_ENA_BIT, 0,
+		&wm8991_dapm_lopmix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_lopmix_controls)),
+
+	/* OUT3MIX */
+	SND_SOC_DAPM_MIXER("OUT3MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT3_ENA_BIT, 0,
+		&wm8991_dapm_out3mix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_out3mix_controls)),
+
+	/* SPKMIX */
+	SND_SOC_DAPM_MIXER_E("SPKMIX", WM8991_POWER_MANAGEMENT_1, WM8991_SPK_ENA_BIT, 0,
+		&wm8991_dapm_spkmix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_spkmix_controls), outmixer_event,
+		SND_SOC_DAPM_PRE_REG),
+
+	/* OUT4MIX */
+	SND_SOC_DAPM_MIXER("OUT4MIX", WM8991_POWER_MANAGEMENT_1, WM8991_OUT4_ENA_BIT, 0,
+		&wm8991_dapm_out4mix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_out4mix_controls)),
+
+	/* ROPMIX */
+	SND_SOC_DAPM_MIXER("ROPMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROP_ENA_BIT, 0,
+		&wm8991_dapm_ropmix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_ropmix_controls)),
+
+	/* RONMIX */
+	SND_SOC_DAPM_MIXER("RONMIX", WM8991_POWER_MANAGEMENT_3, WM8991_RON_ENA_BIT, 0,
+		&wm8991_dapm_ronmix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_ronmix_controls)),
+
+	/* ROMIX */
+	SND_SOC_DAPM_MIXER_E("ROMIX", WM8991_POWER_MANAGEMENT_3, WM8991_ROMIX_ENA_BIT,
+		0, &wm8991_dapm_romix_controls[0],
+		ARRAY_SIZE(wm8991_dapm_romix_controls),
+		outmixer_event, SND_SOC_DAPM_PRE_REG),
+
+	/* LOUT PGA */
+	SND_SOC_DAPM_PGA("LOUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_LOUT_ENA_BIT, 0,
+		NULL, 0),
+
+	/* ROUT PGA */
+	SND_SOC_DAPM_PGA("ROUT PGA", WM8991_POWER_MANAGEMENT_1, WM8991_ROUT_ENA_BIT, 0,
+		NULL, 0),
+
+	/* LOPGA */
+	SND_SOC_DAPM_PGA("LOPGA", WM8991_POWER_MANAGEMENT_3, WM8991_LOPGA_ENA_BIT, 0,
+		NULL, 0),
+
+	/* ROPGA */
+	SND_SOC_DAPM_PGA("ROPGA", WM8991_POWER_MANAGEMENT_3, WM8991_ROPGA_ENA_BIT, 0,
+		NULL, 0),
+
+	/* MICBIAS */
+	SND_SOC_DAPM_MICBIAS("MICBIAS", WM8991_POWER_MANAGEMENT_1,
+		WM8991_MICBIAS_ENA_BIT, 0),
+
+	SND_SOC_DAPM_OUTPUT("LON"),
+	SND_SOC_DAPM_OUTPUT("LOP"),
+	SND_SOC_DAPM_OUTPUT("OUT3"),
+	SND_SOC_DAPM_OUTPUT("LOUT"),
+	SND_SOC_DAPM_OUTPUT("SPKN"),
+	SND_SOC_DAPM_OUTPUT("SPKP"),
+	SND_SOC_DAPM_OUTPUT("ROUT"),
+	SND_SOC_DAPM_OUTPUT("OUT4"),
+	SND_SOC_DAPM_OUTPUT("ROP"),
+	SND_SOC_DAPM_OUTPUT("RON"),
+	SND_SOC_DAPM_OUTPUT("OUT"),
+
+	SND_SOC_DAPM_OUTPUT("Internal DAC Sink"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* Make DACs turn on when playing even if not mixed into any outputs */
+	{"Internal DAC Sink", NULL, "Left DAC"},
+	{"Internal DAC Sink", NULL, "Right DAC"},
+
+	/* Make ADCs turn on when recording even if not mixed from any inputs */
+	{"Left ADC", NULL, "Internal ADC Source"},
+	{"Right ADC", NULL, "Internal ADC Source"},
+
+	/* Input Side */
+	/* LIN12 PGA */
+	{"LIN12 PGA", "LIN1 Switch", "LIN1"},
+	{"LIN12 PGA", "LIN2 Switch", "LIN2"},
+	/* LIN34 PGA */
+	{"LIN34 PGA", "LIN3 Switch", "LIN3"},
+	{"LIN34 PGA", "LIN4 Switch", "LIN4RXN"},
+	/* INMIXL */
+	{"INMIXL", "Record Left Volume", "LOMIX"},
+	{"INMIXL", "LIN2 Volume", "LIN2"},
+	{"INMIXL", "LINPGA12 Switch", "LIN12 PGA"},
+	{"INMIXL", "LINPGA34 Switch", "LIN34 PGA"},
+	/* AINLMUX */
+	{"AINLMUX", "INMIXL Mix", "INMIXL"},
+	{"AINLMUX", "DIFFINL Mix", "LIN12 PGA"},
+	{"AINLMUX", "DIFFINL Mix", "LIN34 PGA"},
+	{"AINLMUX", "RXVOICE Mix", "LIN4RXN"},
+	{"AINLMUX", "RXVOICE Mix", "RIN4RXP"},
+	/* ADC */
+	{"Left ADC", NULL, "AINLMUX"},
+
+	/* RIN12 PGA */
+	{"RIN12 PGA", "RIN1 Switch", "RIN1"},
+	{"RIN12 PGA", "RIN2 Switch", "RIN2"},
+	/* RIN34 PGA */
+	{"RIN34 PGA", "RIN3 Switch", "RIN3"},
+	{"RIN34 PGA", "RIN4 Switch", "RIN4RXP"},
+	/* INMIXL */
+	{"INMIXR", "Record Right Volume", "ROMIX"},
+	{"INMIXR", "RIN2 Volume", "RIN2"},
+	{"INMIXR", "RINPGA12 Switch", "RIN12 PGA"},
+	{"INMIXR", "RINPGA34 Switch", "RIN34 PGA"},
+	/* AINRMUX */
+	{"AINRMUX", "INMIXR Mix", "INMIXR"},
+	{"AINRMUX", "DIFFINR Mix", "RIN12 PGA"},
+	{"AINRMUX", "DIFFINR Mix", "RIN34 PGA"},
+	{"AINRMUX", "RXVOICE Mix", "LIN4RXN"},
+	{"AINRMUX", "RXVOICE Mix", "RIN4RXP"},
+	/* ADC */
+	{"Right ADC", NULL, "AINRMUX"},
+
+	/* LOMIX */
+	{"LOMIX", "LOMIX RIN3 Bypass Switch", "RIN3"},
+	{"LOMIX", "LOMIX LIN3 Bypass Switch", "LIN3"},
+	{"LOMIX", "LOMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+	{"LOMIX", "LOMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+	{"LOMIX", "LOMIX Right ADC Bypass Switch", "AINRMUX"},
+	{"LOMIX", "LOMIX Left ADC Bypass Switch", "AINLMUX"},
+	{"LOMIX", "LOMIX Left DAC Switch", "Left DAC"},
+
+	/* ROMIX */
+	{"ROMIX", "ROMIX RIN3 Bypass Switch", "RIN3"},
+	{"ROMIX", "ROMIX LIN3 Bypass Switch", "LIN3"},
+	{"ROMIX", "ROMIX LIN12 PGA Bypass Switch", "LIN12 PGA"},
+	{"ROMIX", "ROMIX RIN12 PGA Bypass Switch", "RIN12 PGA"},
+	{"ROMIX", "ROMIX Right ADC Bypass Switch", "AINRMUX"},
+	{"ROMIX", "ROMIX Left ADC Bypass Switch", "AINLMUX"},
+	{"ROMIX", "ROMIX Right DAC Switch", "Right DAC"},
+
+	/* SPKMIX */
+	{"SPKMIX", "SPKMIX LIN2 Bypass Switch", "LIN2"},
+	{"SPKMIX", "SPKMIX RIN2 Bypass Switch", "RIN2"},
+	{"SPKMIX", "SPKMIX LADC Bypass Switch", "AINLMUX"},
+	{"SPKMIX", "SPKMIX RADC Bypass Switch", "AINRMUX"},
+	{"SPKMIX", "SPKMIX Left Mixer PGA Switch", "LOPGA"},
+	{"SPKMIX", "SPKMIX Right Mixer PGA Switch", "ROPGA"},
+	{"SPKMIX", "SPKMIX Right DAC Switch", "Right DAC"},
+	{"SPKMIX", "SPKMIX Left DAC Switch", "Right DAC"},
+
+	/* LONMIX */
+	{"LONMIX", "LONMIX Left Mixer PGA Switch", "LOPGA"},
+	{"LONMIX", "LONMIX Right Mixer PGA Switch", "ROPGA"},
+	{"LONMIX", "LONMIX Inverted LOP Switch", "LOPMIX"},
+
+	/* LOPMIX */
+	{"LOPMIX", "LOPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+	{"LOPMIX", "LOPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+	{"LOPMIX", "LOPMIX Left Mixer PGA Switch", "LOPGA"},
+
+	/* OUT3MIX */
+	{"OUT3MIX", "OUT3MIX LIN4RXN Bypass Switch", "LIN4RXN"},
+	{"OUT3MIX", "OUT3MIX Left Out PGA Switch", "LOPGA"},
+
+	/* OUT4MIX */
+	{"OUT4MIX", "OUT4MIX Right Out PGA Switch", "ROPGA"},
+	{"OUT4MIX", "OUT4MIX RIN4RXP Bypass Switch", "RIN4RXP"},
+
+	/* RONMIX */
+	{"RONMIX", "RONMIX Right Mixer PGA Switch", "ROPGA"},
+	{"RONMIX", "RONMIX Left Mixer PGA Switch", "LOPGA"},
+	{"RONMIX", "RONMIX Inverted ROP Switch", "ROPMIX"},
+
+	/* ROPMIX */
+	{"ROPMIX", "ROPMIX Left Mic Bypass Switch", "LIN12 PGA"},
+	{"ROPMIX", "ROPMIX Right Mic Bypass Switch", "RIN12 PGA"},
+	{"ROPMIX", "ROPMIX Right Mixer PGA Switch", "ROPGA"},
+
+	/* Out Mixer PGAs */
+	{"LOPGA", NULL, "LOMIX"},
+	{"ROPGA", NULL, "ROMIX"},
+
+	{"LOUT PGA", NULL, "LOMIX"},
+	{"ROUT PGA", NULL, "ROMIX"},
+
+	/* Output Pins */
+	{"LON", NULL, "LONMIX"},
+	{"LOP", NULL, "LOPMIX"},
+	{"OUT", NULL, "OUT3MIX"},
+	{"LOUT", NULL, "LOUT PGA"},
+	{"SPKN", NULL, "SPKMIX"},
+	{"ROUT", NULL, "ROUT PGA"},
+	{"OUT4", NULL, "OUT4MIX"},
+	{"ROP", NULL, "ROPMIX"},
+	{"RON", NULL, "RONMIX"},
+};
+
+/* PLL divisors */
+struct _pll_div {
+	u32 div2;
+	u32 n;
+	u32 k;
+};
+
+/* The size in bits of the pll divide multiplied by 10
+ * to allow rounding later */
+#define FIXED_PLL_SIZE ((1 << 16) * 10)
+
+static void pll_factors(struct _pll_div *pll_div, unsigned int target,
+			unsigned int source)
+{
+	u64 Kpart;
+	unsigned int K, Ndiv, Nmod;
+
+
+	Ndiv = target / source;
+	if (Ndiv < 6) {
+		source >>= 1;
+		pll_div->div2 = 1;
+		Ndiv = target / source;
+	} else
+		pll_div->div2 = 0;
+
+	if ((Ndiv < 6) || (Ndiv > 12))
+		printk(KERN_WARNING
+		       "WM8991 N value outwith recommended range! N = %d\n", Ndiv);
+
+	pll_div->n = Ndiv;
+	Nmod = target % source;
+	Kpart = FIXED_PLL_SIZE * (long long)Nmod;
+
+	do_div(Kpart, source);
+
+	K = Kpart & 0xFFFFFFFF;
+
+	/* Check if we need to round */
+	if ((K % 10) >= 5)
+		K += 5;
+
+	/* Move down to proper range now rounding is done */
+	K /= 10;
+
+	pll_div->k = K;
+}
+
+static int wm8991_set_dai_pll(struct snd_soc_dai *codec_dai,
+			      int pll_id, int src, unsigned int freq_in, unsigned int freq_out)
+{
+	u16 reg;
+	struct snd_soc_codec *codec = codec_dai->codec;
+	struct _pll_div pll_div;
+
+	if (freq_in && freq_out) {
+		pll_factors(&pll_div, freq_out * 4, freq_in);
+
+		/* Turn on PLL */
+		reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+		reg |= WM8991_PLL_ENA;
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+
+		/* sysclk comes from PLL */
+		reg = snd_soc_read(codec, WM8991_CLOCKING_2);
+		snd_soc_write(codec, WM8991_CLOCKING_2, reg | WM8991_SYSCLK_SRC);
+
+		/* set up N , fractional mode and pre-divisor if neccessary */
+		snd_soc_write(codec, WM8991_PLL1, pll_div.n | WM8991_SDM |
+			      (pll_div.div2 ? WM8991_PRESCALE : 0));
+		snd_soc_write(codec, WM8991_PLL2, (u8)(pll_div.k>>8));
+		snd_soc_write(codec, WM8991_PLL3, (u8)(pll_div.k & 0xFF));
+	} else {
+		/* Turn on PLL */
+		reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+		reg &= ~WM8991_PLL_ENA;
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg);
+	}
+	return 0;
+}
+
+/*
+ * Set's ADC and Voice DAC format.
+ */
+static int wm8991_set_dai_fmt(struct snd_soc_dai *codec_dai,
+			      unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 audio1, audio3;
+
+	audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1);
+	audio3 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_3);
+
+	/* set master/slave audio interface */
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		audio3 &= ~WM8991_AIF_MSTR1;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		audio3 |= WM8991_AIF_MSTR1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	audio1 &= ~WM8991_AIF_FMT_MASK;
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		audio1 |= WM8991_AIF_TMF_I2S;
+		audio1 &= ~WM8991_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		audio1 |= WM8991_AIF_TMF_RIGHTJ;
+		audio1 &= ~WM8991_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		audio1 |= WM8991_AIF_TMF_LEFTJ;
+		audio1 &= ~WM8991_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		audio1 |= WM8991_AIF_TMF_DSP;
+		audio1 &= ~WM8991_AIF_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		audio1 |= WM8991_AIF_TMF_DSP | WM8991_AIF_LRCLK_INV;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
+	snd_soc_write(codec, WM8991_AUDIO_INTERFACE_3, audio3);
+	return 0;
+}
+
+static int wm8991_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
+				 int div_id, int div)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u16 reg;
+
+	switch (div_id) {
+	case WM8991_MCLK_DIV:
+		reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+		      ~WM8991_MCLK_DIV_MASK;
+		snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+		break;
+	case WM8991_DACCLK_DIV:
+		reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+		      ~WM8991_DAC_CLKDIV_MASK;
+		snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+		break;
+	case WM8991_ADCCLK_DIV:
+		reg = snd_soc_read(codec, WM8991_CLOCKING_2) &
+		      ~WM8991_ADC_CLKDIV_MASK;
+		snd_soc_write(codec, WM8991_CLOCKING_2, reg | div);
+		break;
+	case WM8991_BCLK_DIV:
+		reg = snd_soc_read(codec, WM8991_CLOCKING_1) &
+		      ~WM8991_BCLK_DIV_MASK;
+		snd_soc_write(codec, WM8991_CLOCKING_1, reg | div);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * Set PCM DAI bit size and sample rate.
+ */
+static int wm8991_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 audio1 = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_1);
+
+	audio1 &= ~WM8991_AIF_WL_MASK;
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		audio1 |= WM8991_AIF_WL_20BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		audio1 |= WM8991_AIF_WL_24BITS;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		audio1 |= WM8991_AIF_WL_32BITS;
+		break;
+	}
+
+	snd_soc_write(codec, WM8991_AUDIO_INTERFACE_1, audio1);
+	return 0;
+}
+
+static int wm8991_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u16 val;
+
+	val  = snd_soc_read(codec, WM8991_DAC_CTRL) & ~WM8991_DAC_MUTE;
+	if (mute)
+		snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
+	else
+		snd_soc_write(codec, WM8991_DAC_CTRL, val);
+	return 0;
+}
+
+static int wm8991_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	u16 val;
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+
+	case SND_SOC_BIAS_PREPARE:
+		/* VMID=2*50k */
+		val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+		      ~WM8991_VMID_MODE_MASK;
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x2);
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+			snd_soc_cache_sync(codec);
+			/* Enable all output discharge bits */
+			snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+				      WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
+				      WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
+				      WM8991_DIS_ROUT);
+
+			/* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */
+			snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+				      WM8991_BUFDCOPEN | WM8991_POBCTRL |
+				      WM8991_VMIDTOG);
+
+			/* Delay to allow output caps to discharge */
+			msleep(300);
+
+			/* Disable VMIDTOG */
+			snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+				      WM8991_BUFDCOPEN | WM8991_POBCTRL);
+
+			/* disable all output discharge bits */
+			snd_soc_write(codec, WM8991_ANTIPOP1, 0);
+
+			/* Enable outputs */
+			snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1b00);
+
+			msleep(50);
+
+			/* Enable VMID at 2x50k */
+			snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f02);
+
+			msleep(100);
+
+			/* Enable VREF */
+			snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+
+			msleep(600);
+
+			/* Enable BUFIOEN */
+			snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+				      WM8991_BUFDCOPEN | WM8991_POBCTRL |
+				      WM8991_BUFIOEN);
+
+			/* Disable outputs */
+			snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x3);
+
+			/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+			snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_BUFIOEN);
+		}
+
+		/* VMID=2*250k */
+		val = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1) &
+		      ~WM8991_VMID_MODE_MASK;
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, val | 0x4);
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		/* Enable POBCTRL and SOFT_ST */
+		snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+			      WM8991_POBCTRL | WM8991_BUFIOEN);
+
+		/* Enable POBCTRL, SOFT_ST and BUFDCOPEN */
+		snd_soc_write(codec, WM8991_ANTIPOP2, WM8991_SOFTST |
+			      WM8991_BUFDCOPEN | WM8991_POBCTRL |
+			      WM8991_BUFIOEN);
+
+		/* mute DAC */
+		val = snd_soc_read(codec, WM8991_DAC_CTRL);
+		snd_soc_write(codec, WM8991_DAC_CTRL, val | WM8991_DAC_MUTE);
+
+		/* Enable any disabled outputs */
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f03);
+
+		/* Disable VMID */
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x1f01);
+
+		msleep(300);
+
+		/* Enable all output discharge bits */
+		snd_soc_write(codec, WM8991_ANTIPOP1, WM8991_DIS_LLINE |
+			      WM8991_DIS_RLINE | WM8991_DIS_OUT3 |
+			      WM8991_DIS_OUT4 | WM8991_DIS_LOUT |
+			      WM8991_DIS_ROUT);
+
+		/* Disable VREF */
+		snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, 0x0);
+
+		/* disable POBCTRL, SOFT_ST and BUFDCOPEN */
+		snd_soc_write(codec, WM8991_ANTIPOP2, 0x0);
+		codec->cache_sync = 1;
+		break;
+	}
+
+	codec->dapm.bias_level = level;
+	return 0;
+}
+
+static int wm8991_suspend(struct snd_soc_codec *codec, pm_message_t state)
+{
+	wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8991_resume(struct snd_soc_codec *codec)
+{
+	wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+	return 0;
+}
+
+/* power down chip */
+static int wm8991_remove(struct snd_soc_codec *codec)
+{
+	wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
+	return 0;
+}
+
+static int wm8991_probe(struct snd_soc_codec *codec)
+{
+	struct wm8991_priv *wm8991;
+	int ret;
+	unsigned int reg;
+
+	wm8991 = snd_soc_codec_get_drvdata(codec);
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8991->control_type);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret);
+		return ret;
+	}
+
+	ret = wm8991_reset(codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to issue reset\n");
+		return ret;
+	}
+
+	wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	reg = snd_soc_read(codec, WM8991_AUDIO_INTERFACE_4);
+	snd_soc_write(codec, WM8991_AUDIO_INTERFACE_4, reg | WM8991_ALRCGPIO1);
+
+	reg = snd_soc_read(codec, WM8991_GPIO1_GPIO2) &
+	      ~WM8991_GPIO1_SEL_MASK;
+	snd_soc_write(codec, WM8991_GPIO1_GPIO2, reg | 1);
+
+	reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_1);
+	snd_soc_write(codec, WM8991_POWER_MANAGEMENT_1, reg | WM8991_VREF_ENA|
+		      WM8991_VMID_MODE_MASK);
+
+	reg = snd_soc_read(codec, WM8991_POWER_MANAGEMENT_2);
+	snd_soc_write(codec, WM8991_POWER_MANAGEMENT_2, reg | WM8991_OPCLK_ENA);
+
+	snd_soc_write(codec, WM8991_DAC_CTRL, 0);
+	snd_soc_write(codec, WM8991_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
+	snd_soc_write(codec, WM8991_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
+
+	snd_soc_add_controls(codec, wm8991_snd_controls,
+			     ARRAY_SIZE(wm8991_snd_controls));
+
+	snd_soc_dapm_new_controls(&codec->dapm, wm8991_dapm_widgets,
+				  ARRAY_SIZE(wm8991_dapm_widgets));
+	snd_soc_dapm_add_routes(&codec->dapm, audio_map,
+				ARRAY_SIZE(audio_map));
+	return 0;
+}
+
+#define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops wm8991_ops = {
+	.hw_params = wm8991_hw_params,
+	.digital_mute = wm8991_mute,
+	.set_fmt = wm8991_set_dai_fmt,
+	.set_clkdiv = wm8991_set_dai_clkdiv,
+	.set_pll = wm8991_set_dai_pll
+};
+
+/*
+ * The WM8991 supports 2 different and mutually exclusive DAI
+ * configurations.
+ *
+ * 1. ADC/DAC on Primary Interface
+ * 2. ADC on Primary Interface/DAC on secondary
+ */
+static struct snd_soc_dai_driver wm8991_dai = {
+	/* ADC/DAC on primary */
+	.name = "wm8991",
+	.id = 1,
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = WM8991_FORMATS
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_96000,
+		.formats = WM8991_FORMATS
+	},
+	.ops = &wm8991_ops
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8991 = {
+	.probe = wm8991_probe,
+	.remove = wm8991_remove,
+	.suspend = wm8991_suspend,
+	.resume = wm8991_resume,
+	.set_bias_level = wm8991_set_bias_level,
+	.reg_cache_size = WM8991_MAX_REGISTER + 1,
+	.reg_word_size = sizeof(u16),
+	.reg_cache_default = wm8991_reg_defs
+};
+
+static __devinit int wm8991_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct wm8991_priv *wm8991;
+	int ret;
+
+	wm8991 = kzalloc(sizeof *wm8991, GFP_KERNEL);
+	if (!wm8991)
+		return -ENOMEM;
+
+	wm8991->control_type = SND_SOC_I2C;
+	i2c_set_clientdata(i2c, wm8991);
+
+	ret = snd_soc_register_codec(&i2c->dev,
+				     &soc_codec_dev_wm8991, &wm8991_dai, 1);
+	if (ret < 0)
+		kfree(wm8991);
+	return ret;
+}
+
+static __devexit int wm8991_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	kfree(i2c_get_clientdata(client));
+	return 0;
+}
+
+static const struct i2c_device_id wm8991_i2c_id[] = {
+	{ "wm8991", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id);
+
+static struct i2c_driver wm8991_i2c_driver = {
+	.driver = {
+		.name = "wm8991",
+		.owner = THIS_MODULE,
+	},
+	.probe = wm8991_i2c_probe,
+	.remove = __devexit_p(wm8991_i2c_remove),
+	.id_table = wm8991_i2c_id,
+};
+
+static int __init wm8991_modinit(void)
+{
+	int ret;
+	ret = i2c_add_driver(&wm8991_i2c_driver);
+	if (ret != 0) {
+		printk(KERN_ERR "Failed to register WM8991 I2C driver: %d\n",
+		       ret);
+	}
+	return 0;
+}
+module_init(wm8991_modinit);
+
+static void __exit wm8991_exit(void)
+{
+	i2c_del_driver(&wm8991_i2c_driver);
+}
+module_exit(wm8991_exit);
+
+MODULE_DESCRIPTION("ASoC WM8991 driver");
+MODULE_AUTHOR("Graeme Gregory");
+MODULE_LICENSE("GPL");

+ 833 - 0
sound/soc/codecs/wm8991.h

@@ -0,0 +1,833 @@
+/*
+ * wm8991.h  --  audio driver for WM8991
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#ifndef _WM8991_H
+#define _WM8991_H
+
+/*
+ * Register values.
+ */
+#define WM8991_RESET                            0x00
+#define WM8991_POWER_MANAGEMENT_1               0x01
+#define WM8991_POWER_MANAGEMENT_2               0x02
+#define WM8991_POWER_MANAGEMENT_3               0x03
+#define WM8991_AUDIO_INTERFACE_1                0x04
+#define WM8991_AUDIO_INTERFACE_2                0x05
+#define WM8991_CLOCKING_1                       0x06
+#define WM8991_CLOCKING_2                       0x07
+#define WM8991_AUDIO_INTERFACE_3                0x08
+#define WM8991_AUDIO_INTERFACE_4                0x09
+#define WM8991_DAC_CTRL                         0x0A
+#define WM8991_LEFT_DAC_DIGITAL_VOLUME          0x0B
+#define WM8991_RIGHT_DAC_DIGITAL_VOLUME         0x0C
+#define WM8991_DIGITAL_SIDE_TONE                0x0D
+#define WM8991_ADC_CTRL                         0x0E
+#define WM8991_LEFT_ADC_DIGITAL_VOLUME          0x0F
+#define WM8991_RIGHT_ADC_DIGITAL_VOLUME         0x10
+#define WM8991_GPIO_CTRL_1                      0x12
+#define WM8991_GPIO1_GPIO2                      0x13
+#define WM8991_GPIO3_GPIO4                      0x14
+#define WM8991_GPIO5_GPIO6                      0x15
+#define WM8991_GPIOCTRL_2                       0x16
+#define WM8991_GPIO_POL                         0x17
+#define WM8991_LEFT_LINE_INPUT_1_2_VOLUME       0x18
+#define WM8991_LEFT_LINE_INPUT_3_4_VOLUME       0x19
+#define WM8991_RIGHT_LINE_INPUT_1_2_VOLUME      0x1A
+#define WM8991_RIGHT_LINE_INPUT_3_4_VOLUME      0x1B
+#define WM8991_LEFT_OUTPUT_VOLUME               0x1C
+#define WM8991_RIGHT_OUTPUT_VOLUME              0x1D
+#define WM8991_LINE_OUTPUTS_VOLUME              0x1E
+#define WM8991_OUT3_4_VOLUME                    0x1F
+#define WM8991_LEFT_OPGA_VOLUME                 0x20
+#define WM8991_RIGHT_OPGA_VOLUME                0x21
+#define WM8991_SPEAKER_VOLUME                   0x22
+#define WM8991_CLASSD1                          0x23
+#define WM8991_CLASSD3                          0x25
+#define WM8991_INPUT_MIXER1                     0x27
+#define WM8991_INPUT_MIXER2                     0x28
+#define WM8991_INPUT_MIXER3                     0x29
+#define WM8991_INPUT_MIXER4                     0x2A
+#define WM8991_INPUT_MIXER5                     0x2B
+#define WM8991_INPUT_MIXER6                     0x2C
+#define WM8991_OUTPUT_MIXER1                    0x2D
+#define WM8991_OUTPUT_MIXER2                    0x2E
+#define WM8991_OUTPUT_MIXER3                    0x2F
+#define WM8991_OUTPUT_MIXER4                    0x30
+#define WM8991_OUTPUT_MIXER5                    0x31
+#define WM8991_OUTPUT_MIXER6                    0x32
+#define WM8991_OUT3_4_MIXER                     0x33
+#define WM8991_LINE_MIXER1                      0x34
+#define WM8991_LINE_MIXER2                      0x35
+#define WM8991_SPEAKER_MIXER                    0x36
+#define WM8991_ADDITIONAL_CONTROL               0x37
+#define WM8991_ANTIPOP1                         0x38
+#define WM8991_ANTIPOP2                         0x39
+#define WM8991_MICBIAS                          0x3A
+#define WM8991_PLL1                             0x3C
+#define WM8991_PLL2                             0x3D
+#define WM8991_PLL3                             0x3E
+#define WM8991_INTDRIVBITS			0x3F
+
+#define WM8991_REGISTER_COUNT                   60
+#define WM8991_MAX_REGISTER                     0x3F
+
+/*
+ * Field Definitions.
+ */
+
+/*
+ * R0 (0x00) - Reset
+ */
+#define WM8991_SW_RESET_CHIP_ID_MASK            0xFFFF  /* SW_RESET_CHIP_ID - [15:0] */
+
+/*
+ * R1 (0x01) - Power Management (1)
+ */
+#define WM8991_SPK_ENA                          0x1000  /* SPK_ENA */
+#define WM8991_SPK_ENA_BIT			12
+#define WM8991_OUT3_ENA                         0x0800  /* OUT3_ENA */
+#define WM8991_OUT3_ENA_BIT			11
+#define WM8991_OUT4_ENA                         0x0400  /* OUT4_ENA */
+#define WM8991_OUT4_ENA_BIT			10
+#define WM8991_LOUT_ENA                         0x0200  /* LOUT_ENA */
+#define WM8991_LOUT_ENA_BIT			9
+#define WM8991_ROUT_ENA                         0x0100  /* ROUT_ENA */
+#define WM8991_ROUT_ENA_BIT			8
+#define WM8991_MICBIAS_ENA                      0x0010  /* MICBIAS_ENA */
+#define WM8991_MICBIAS_ENA_BIT			4
+#define WM8991_VMID_MODE_MASK                   0x0006  /* VMID_MODE - [2:1] */
+#define WM8991_VREF_ENA                         0x0001  /* VREF_ENA */
+#define WM8991_VREF_ENA_BIT			0
+
+/*
+ * R2 (0x02) - Power Management (2)
+ */
+#define WM8991_PLL_ENA                          0x8000  /* PLL_ENA */
+#define WM8991_PLL_ENA_BIT			15
+#define WM8991_TSHUT_ENA                        0x4000  /* TSHUT_ENA */
+#define WM8991_TSHUT_ENA_BIT			14
+#define WM8991_TSHUT_OPDIS                      0x2000  /* TSHUT_OPDIS */
+#define WM8991_TSHUT_OPDIS_BIT			13
+#define WM8991_OPCLK_ENA                        0x0800  /* OPCLK_ENA */
+#define WM8991_OPCLK_ENA_BIT			11
+#define WM8991_AINL_ENA                         0x0200  /* AINL_ENA */
+#define WM8991_AINL_ENA_BIT			9
+#define WM8991_AINR_ENA                         0x0100  /* AINR_ENA */
+#define WM8991_AINR_ENA_BIT			8
+#define WM8991_LIN34_ENA                        0x0080  /* LIN34_ENA */
+#define WM8991_LIN34_ENA_BIT			7
+#define WM8991_LIN12_ENA                        0x0040  /* LIN12_ENA */
+#define WM8991_LIN12_ENA_BIT			6
+#define WM8991_RIN34_ENA                        0x0020  /* RIN34_ENA */
+#define WM8991_RIN34_ENA_BIT			5
+#define WM8991_RIN12_ENA                        0x0010  /* RIN12_ENA */
+#define WM8991_RIN12_ENA_BIT			4
+#define WM8991_ADCL_ENA                         0x0002  /* ADCL_ENA */
+#define WM8991_ADCL_ENA_BIT			1
+#define WM8991_ADCR_ENA                         0x0001  /* ADCR_ENA */
+#define WM8991_ADCR_ENA_BIT			0
+
+/*
+ * R3 (0x03) - Power Management (3)
+ */
+#define WM8991_LON_ENA                          0x2000  /* LON_ENA */
+#define WM8991_LON_ENA_BIT			13
+#define WM8991_LOP_ENA                          0x1000  /* LOP_ENA */
+#define WM8991_LOP_ENA_BIT			12
+#define WM8991_RON_ENA                          0x0800  /* RON_ENA */
+#define WM8991_RON_ENA_BIT			11
+#define WM8991_ROP_ENA                          0x0400  /* ROP_ENA */
+#define WM8991_ROP_ENA_BIT			10
+#define WM8991_LOPGA_ENA                        0x0080  /* LOPGA_ENA */
+#define WM8991_LOPGA_ENA_BIT			7
+#define WM8991_ROPGA_ENA                        0x0040  /* ROPGA_ENA */
+#define WM8991_ROPGA_ENA_BIT			6
+#define WM8991_LOMIX_ENA                        0x0020  /* LOMIX_ENA */
+#define WM8991_LOMIX_ENA_BIT			5
+#define WM8991_ROMIX_ENA                        0x0010  /* ROMIX_ENA */
+#define WM8991_ROMIX_ENA_BIT			4
+#define WM8991_DACL_ENA                         0x0002  /* DACL_ENA */
+#define WM8991_DACL_ENA_BIT			1
+#define WM8991_DACR_ENA                         0x0001  /* DACR_ENA */
+#define WM8991_DACR_ENA_BIT			0
+
+/*
+ * R4 (0x04) - Audio Interface (1)
+ */
+#define WM8991_AIFADCL_SRC                      0x8000  /* AIFADCL_SRC */
+#define WM8991_AIFADCR_SRC                      0x4000  /* AIFADCR_SRC */
+#define WM8991_AIFADC_TDM                       0x2000  /* AIFADC_TDM */
+#define WM8991_AIFADC_TDM_CHAN                  0x1000  /* AIFADC_TDM_CHAN */
+#define WM8991_AIF_BCLK_INV                     0x0100  /* AIF_BCLK_INV */
+#define WM8991_AIF_LRCLK_INV                    0x0080  /* AIF_LRCLK_INV */
+#define WM8991_AIF_WL_MASK                      0x0060  /* AIF_WL - [6:5] */
+#define WM8991_AIF_WL_16BITS			(0 << 5)
+#define WM8991_AIF_WL_20BITS			(1 << 5)
+#define WM8991_AIF_WL_24BITS			(2 << 5)
+#define WM8991_AIF_WL_32BITS			(3 << 5)
+#define WM8991_AIF_FMT_MASK                     0x0018  /* AIF_FMT - [4:3] */
+#define WM8991_AIF_TMF_RIGHTJ			(0 << 3)
+#define WM8991_AIF_TMF_LEFTJ			(1 << 3)
+#define WM8991_AIF_TMF_I2S			(2 << 3)
+#define WM8991_AIF_TMF_DSP			(3 << 3)
+
+/*
+ * R5 (0x05) - Audio Interface (2)
+ */
+#define WM8991_DACL_SRC                         0x8000  /* DACL_SRC */
+#define WM8991_DACR_SRC                         0x4000  /* DACR_SRC */
+#define WM8991_AIFDAC_TDM                       0x2000  /* AIFDAC_TDM */
+#define WM8991_AIFDAC_TDM_CHAN                  0x1000  /* AIFDAC_TDM_CHAN */
+#define WM8991_DAC_BOOST_MASK                   0x0C00  /* DAC_BOOST - [11:10] */
+#define WM8991_DAC_COMP                         0x0010  /* DAC_COMP */
+#define WM8991_DAC_COMPMODE                     0x0008  /* DAC_COMPMODE */
+#define WM8991_ADC_COMP                         0x0004  /* ADC_COMP */
+#define WM8991_ADC_COMPMODE                     0x0002  /* ADC_COMPMODE */
+#define WM8991_LOOPBACK                         0x0001  /* LOOPBACK */
+
+/*
+ * R6 (0x06) - Clocking (1)
+ */
+#define WM8991_TOCLK_RATE                       0x8000  /* TOCLK_RATE */
+#define WM8991_TOCLK_ENA                        0x4000  /* TOCLK_ENA */
+#define WM8991_OPCLKDIV_MASK                    0x1E00  /* OPCLKDIV - [12:9] */
+#define WM8991_DCLKDIV_MASK                     0x01C0  /* DCLKDIV - [8:6] */
+#define WM8991_BCLK_DIV_MASK                    0x001E  /* BCLK_DIV - [4:1] */
+#define WM8991_BCLK_DIV_1			(0x0 << 1)
+#define WM8991_BCLK_DIV_1_5			(0x1 << 1)
+#define WM8991_BCLK_DIV_2			(0x2 << 1)
+#define WM8991_BCLK_DIV_3			(0x3 << 1)
+#define WM8991_BCLK_DIV_4			(0x4 << 1)
+#define WM8991_BCLK_DIV_5_5			(0x5 << 1)
+#define WM8991_BCLK_DIV_6			(0x6 << 1)
+#define WM8991_BCLK_DIV_8			(0x7 << 1)
+#define WM8991_BCLK_DIV_11			(0x8 << 1)
+#define WM8991_BCLK_DIV_12			(0x9 << 1)
+#define WM8991_BCLK_DIV_16			(0xA << 1)
+#define WM8991_BCLK_DIV_22			(0xB << 1)
+#define WM8991_BCLK_DIV_24			(0xC << 1)
+#define WM8991_BCLK_DIV_32			(0xD << 1)
+#define WM8991_BCLK_DIV_44			(0xE << 1)
+#define WM8991_BCLK_DIV_48			(0xF << 1)
+
+/*
+ * R7 (0x07) - Clocking (2)
+ */
+#define WM8991_MCLK_SRC                         0x8000  /* MCLK_SRC */
+#define WM8991_SYSCLK_SRC                       0x4000  /* SYSCLK_SRC */
+#define WM8991_CLK_FORCE                        0x2000  /* CLK_FORCE */
+#define WM8991_MCLK_DIV_MASK                    0x1800  /* MCLK_DIV - [12:11] */
+#define WM8991_MCLK_DIV_1			(0 << 11)
+#define WM8991_MCLK_DIV_2			( 2 << 11)
+#define WM8991_MCLK_INV                         0x0400  /* MCLK_INV */
+#define WM8991_ADC_CLKDIV_MASK                  0x00E0  /* ADC_CLKDIV - [7:5] */
+#define WM8991_ADC_CLKDIV_1			(0 << 5)
+#define WM8991_ADC_CLKDIV_1_5			(1 << 5)
+#define WM8991_ADC_CLKDIV_2			(2 << 5)
+#define WM8991_ADC_CLKDIV_3			(3 << 5)
+#define WM8991_ADC_CLKDIV_4			(4 << 5)
+#define WM8991_ADC_CLKDIV_5_5			(5 << 5)
+#define WM8991_ADC_CLKDIV_6			(6 << 5)
+#define WM8991_DAC_CLKDIV_MASK                  0x001C  /* DAC_CLKDIV - [4:2] */
+#define WM8991_DAC_CLKDIV_1			(0 << 2)
+#define WM8991_DAC_CLKDIV_1_5			(1 << 2)
+#define WM8991_DAC_CLKDIV_2			(2 << 2)
+#define WM8991_DAC_CLKDIV_3			(3 << 2)
+#define WM8991_DAC_CLKDIV_4			(4 << 2)
+#define WM8991_DAC_CLKDIV_5_5			(5 << 2)
+#define WM8991_DAC_CLKDIV_6			(6 << 2)
+
+/*
+ * R8 (0x08) - Audio Interface (3)
+ */
+#define WM8991_AIF_MSTR1                        0x8000  /* AIF_MSTR1 */
+#define WM8991_AIF_MSTR2                        0x4000  /* AIF_MSTR2 */
+#define WM8991_AIF_SEL                          0x2000  /* AIF_SEL */
+#define WM8991_ADCLRC_DIR                       0x0800  /* ADCLRC_DIR */
+#define WM8991_ADCLRC_RATE_MASK                 0x07FF  /* ADCLRC_RATE - [10:0] */
+
+/*
+ * R9 (0x09) - Audio Interface (4)
+ */
+#define WM8991_ALRCGPIO1                        0x8000  /* ALRCGPIO1 */
+#define WM8991_ALRCBGPIO6                       0x4000  /* ALRCBGPIO6 */
+#define WM8991_AIF_TRIS                         0x2000  /* AIF_TRIS */
+#define WM8991_DACLRC_DIR                       0x0800  /* DACLRC_DIR */
+#define WM8991_DACLRC_RATE_MASK                 0x07FF  /* DACLRC_RATE - [10:0] */
+
+/*
+ * R10 (0x0A) - DAC CTRL
+ */
+#define WM8991_AIF_LRCLKRATE                    0x0400  /* AIF_LRCLKRATE */
+#define WM8991_DAC_MONO                         0x0200  /* DAC_MONO */
+#define WM8991_DAC_SB_FILT                      0x0100  /* DAC_SB_FILT */
+#define WM8991_DAC_MUTERATE                     0x0080  /* DAC_MUTERATE */
+#define WM8991_DAC_MUTEMODE                     0x0040  /* DAC_MUTEMODE */
+#define WM8991_DEEMP_MASK                       0x0030  /* DEEMP - [5:4] */
+#define WM8991_DAC_MUTE                         0x0004  /* DAC_MUTE */
+#define WM8991_DACL_DATINV                      0x0002  /* DACL_DATINV */
+#define WM8991_DACR_DATINV                      0x0001  /* DACR_DATINV */
+
+/*
+ * R11 (0x0B) - Left DAC Digital Volume
+ */
+#define WM8991_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8991_DACL_VOL_MASK                    0x00FF  /* DACL_VOL - [7:0] */
+#define WM8991_DACL_VOL_SHIFT			0
+/*
+ * R12 (0x0C) - Right DAC Digital Volume
+ */
+#define WM8991_DAC_VU                           0x0100  /* DAC_VU */
+#define WM8991_DACR_VOL_MASK                    0x00FF  /* DACR_VOL - [7:0] */
+#define WM8991_DACR_VOL_SHIFT			0
+/*
+ * R13 (0x0D) - Digital Side Tone
+ */
+#define WM8991_ADCL_DAC_SVOL_MASK               0x0F  /* ADCL_DAC_SVOL - [12:9] */
+#define WM8991_ADCL_DAC_SVOL_SHIFT		9
+#define WM8991_ADCR_DAC_SVOL_MASK               0x0F  /* ADCR_DAC_SVOL - [8:5] */
+#define WM8991_ADCR_DAC_SVOL_SHIFT		5
+#define WM8991_ADC_TO_DACL_MASK                 0x03  /* ADC_TO_DACL - [3:2] */
+#define WM8991_ADC_TO_DACL_SHIFT		2
+#define WM8991_ADC_TO_DACR_MASK                 0x03  /* ADC_TO_DACR - [1:0] */
+#define WM8991_ADC_TO_DACR_SHIFT		0
+
+/*
+ * R14 (0x0E) - ADC CTRL
+ */
+#define WM8991_ADC_HPF_ENA                      0x0100  /* ADC_HPF_ENA */
+#define WM8991_ADC_HPF_ENA_BIT			8
+#define WM8991_ADC_HPF_CUT_MASK                 0x03  /* ADC_HPF_CUT - [6:5] */
+#define WM8991_ADC_HPF_CUT_SHIFT		5
+#define WM8991_ADCL_DATINV                      0x0002  /* ADCL_DATINV */
+#define WM8991_ADCL_DATINV_BIT			1
+#define WM8991_ADCR_DATINV                      0x0001  /* ADCR_DATINV */
+#define WM8991_ADCR_DATINV_BIT			0
+
+/*
+ * R15 (0x0F) - Left ADC Digital Volume
+ */
+#define WM8991_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8991_ADCL_VOL_MASK                    0x00FF  /* ADCL_VOL - [7:0] */
+#define WM8991_ADCL_VOL_SHIFT			0
+
+/*
+ * R16 (0x10) - Right ADC Digital Volume
+ */
+#define WM8991_ADC_VU                           0x0100  /* ADC_VU */
+#define WM8991_ADCR_VOL_MASK                    0x00FF  /* ADCR_VOL - [7:0] */
+#define WM8991_ADCR_VOL_SHIFT			0
+
+/*
+ * R18 (0x12) - GPIO CTRL 1
+ */
+#define WM8991_IRQ                              0x1000  /* IRQ */
+#define WM8991_TEMPOK                           0x0800  /* TEMPOK */
+#define WM8991_MICSHRT                          0x0400  /* MICSHRT */
+#define WM8991_MICDET                           0x0200  /* MICDET */
+#define WM8991_PLL_LCK                          0x0100  /* PLL_LCK */
+#define WM8991_GPI8_STATUS                      0x0080  /* GPI8_STATUS */
+#define WM8991_GPI7_STATUS                      0x0040  /* GPI7_STATUS */
+#define WM8991_GPIO6_STATUS                     0x0020  /* GPIO6_STATUS */
+#define WM8991_GPIO5_STATUS                     0x0010  /* GPIO5_STATUS */
+#define WM8991_GPIO4_STATUS                     0x0008  /* GPIO4_STATUS */
+#define WM8991_GPIO3_STATUS                     0x0004  /* GPIO3_STATUS */
+#define WM8991_GPIO2_STATUS                     0x0002  /* GPIO2_STATUS */
+#define WM8991_GPIO1_STATUS                     0x0001  /* GPIO1_STATUS */
+
+/*
+ * R19 (0x13) - GPIO1 & GPIO2
+ */
+#define WM8991_GPIO2_DEB_ENA                    0x8000  /* GPIO2_DEB_ENA */
+#define WM8991_GPIO2_IRQ_ENA                    0x4000  /* GPIO2_IRQ_ENA */
+#define WM8991_GPIO2_PU                         0x2000  /* GPIO2_PU */
+#define WM8991_GPIO2_PD                         0x1000  /* GPIO2_PD */
+#define WM8991_GPIO2_SEL_MASK                   0x0F00  /* GPIO2_SEL - [11:8] */
+#define WM8991_GPIO1_DEB_ENA                    0x0080  /* GPIO1_DEB_ENA */
+#define WM8991_GPIO1_IRQ_ENA                    0x0040  /* GPIO1_IRQ_ENA */
+#define WM8991_GPIO1_PU                         0x0020  /* GPIO1_PU */
+#define WM8991_GPIO1_PD                         0x0010  /* GPIO1_PD */
+#define WM8991_GPIO1_SEL_MASK                   0x000F  /* GPIO1_SEL - [3:0] */
+
+/*
+ * R20 (0x14) - GPIO3 & GPIO4
+ */
+#define WM8991_GPIO4_DEB_ENA                    0x8000  /* GPIO4_DEB_ENA */
+#define WM8991_GPIO4_IRQ_ENA                    0x4000  /* GPIO4_IRQ_ENA */
+#define WM8991_GPIO4_PU                         0x2000  /* GPIO4_PU */
+#define WM8991_GPIO4_PD                         0x1000  /* GPIO4_PD */
+#define WM8991_GPIO4_SEL_MASK                   0x0F00  /* GPIO4_SEL - [11:8] */
+#define WM8991_GPIO3_DEB_ENA                    0x0080  /* GPIO3_DEB_ENA */
+#define WM8991_GPIO3_IRQ_ENA                    0x0040  /* GPIO3_IRQ_ENA */
+#define WM8991_GPIO3_PU                         0x0020  /* GPIO3_PU */
+#define WM8991_GPIO3_PD                         0x0010  /* GPIO3_PD */
+#define WM8991_GPIO3_SEL_MASK                   0x000F  /* GPIO3_SEL - [3:0] */
+
+/*
+ * R21 (0x15) - GPIO5 & GPIO6
+ */
+#define WM8991_GPIO6_DEB_ENA                    0x8000  /* GPIO6_DEB_ENA */
+#define WM8991_GPIO6_IRQ_ENA                    0x4000  /* GPIO6_IRQ_ENA */
+#define WM8991_GPIO6_PU                         0x2000  /* GPIO6_PU */
+#define WM8991_GPIO6_PD                         0x1000  /* GPIO6_PD */
+#define WM8991_GPIO6_SEL_MASK                   0x0F00  /* GPIO6_SEL - [11:8] */
+#define WM8991_GPIO5_DEB_ENA                    0x0080  /* GPIO5_DEB_ENA */
+#define WM8991_GPIO5_IRQ_ENA                    0x0040  /* GPIO5_IRQ_ENA */
+#define WM8991_GPIO5_PU                         0x0020  /* GPIO5_PU */
+#define WM8991_GPIO5_PD                         0x0010  /* GPIO5_PD */
+#define WM8991_GPIO5_SEL_MASK                   0x000F  /* GPIO5_SEL - [3:0] */
+
+/*
+ * R22 (0x16) - GPIOCTRL 2
+ */
+#define WM8991_RD_3W_ENA                        0x8000  /* RD_3W_ENA */
+#define WM8991_MODE_3W4W                        0x4000  /* MODE_3W4W */
+#define WM8991_TEMPOK_IRQ_ENA                   0x0800  /* TEMPOK_IRQ_ENA */
+#define WM8991_MICSHRT_IRQ_ENA                  0x0400  /* MICSHRT_IRQ_ENA */
+#define WM8991_MICDET_IRQ_ENA                   0x0200  /* MICDET_IRQ_ENA */
+#define WM8991_PLL_LCK_IRQ_ENA                  0x0100  /* PLL_LCK_IRQ_ENA */
+#define WM8991_GPI8_DEB_ENA                     0x0080  /* GPI8_DEB_ENA */
+#define WM8991_GPI8_IRQ_ENA                     0x0040  /* GPI8_IRQ_ENA */
+#define WM8991_GPI8_ENA                         0x0010  /* GPI8_ENA */
+#define WM8991_GPI7_DEB_ENA                     0x0008  /* GPI7_DEB_ENA */
+#define WM8991_GPI7_IRQ_ENA                     0x0004  /* GPI7_IRQ_ENA */
+#define WM8991_GPI7_ENA                         0x0001  /* GPI7_ENA */
+
+/*
+ * R23 (0x17) - GPIO_POL
+ */
+#define WM8991_IRQ_INV                          0x1000  /* IRQ_INV */
+#define WM8991_TEMPOK_POL                       0x0800  /* TEMPOK_POL */
+#define WM8991_MICSHRT_POL                      0x0400  /* MICSHRT_POL */
+#define WM8991_MICDET_POL                       0x0200  /* MICDET_POL */
+#define WM8991_PLL_LCK_POL                      0x0100  /* PLL_LCK_POL */
+#define WM8991_GPI8_POL                         0x0080  /* GPI8_POL */
+#define WM8991_GPI7_POL                         0x0040  /* GPI7_POL */
+#define WM8991_GPIO6_POL                        0x0020  /* GPIO6_POL */
+#define WM8991_GPIO5_POL                        0x0010  /* GPIO5_POL */
+#define WM8991_GPIO4_POL                        0x0008  /* GPIO4_POL */
+#define WM8991_GPIO3_POL                        0x0004  /* GPIO3_POL */
+#define WM8991_GPIO2_POL                        0x0002  /* GPIO2_POL */
+#define WM8991_GPIO1_POL                        0x0001  /* GPIO1_POL */
+
+/*
+ * R24 (0x18) - Left Line Input 1&2 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_LI12MUTE                         0x0080  /* LI12MUTE */
+#define WM8991_LI12MUTE_BIT			7
+#define WM8991_LI12ZC                           0x0040  /* LI12ZC */
+#define WM8991_LI12ZC_BIT			6
+#define WM8991_LIN12VOL_MASK                    0x001F  /* LIN12VOL - [4:0] */
+#define WM8991_LIN12VOL_SHIFT			0
+/*
+ * R25 (0x19) - Left Line Input 3&4 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_LI34MUTE                         0x0080  /* LI34MUTE */
+#define WM8991_LI34MUTE_BIT			7
+#define WM8991_LI34ZC                           0x0040  /* LI34ZC */
+#define WM8991_LI34ZC_BIT			6
+#define WM8991_LIN34VOL_MASK                    0x001F  /* LIN34VOL - [4:0] */
+#define WM8991_LIN34VOL_SHIFT			0
+
+/*
+ * R26 (0x1A) - Right Line Input 1&2 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_RI12MUTE                         0x0080  /* RI12MUTE */
+#define WM8991_RI12MUTE_BIT			7
+#define WM8991_RI12ZC                           0x0040  /* RI12ZC */
+#define WM8991_RI12ZC_BIT			6
+#define WM8991_RIN12VOL_MASK                    0x001F  /* RIN12VOL - [4:0] */
+#define WM8991_RIN12VOL_SHIFT			0
+
+/*
+ * R27 (0x1B) - Right Line Input 3&4 Volume
+ */
+#define WM8991_IPVU                             0x0100  /* IPVU */
+#define WM8991_RI34MUTE                         0x0080  /* RI34MUTE */
+#define WM8991_RI34MUTE_BIT			7
+#define WM8991_RI34ZC                           0x0040  /* RI34ZC */
+#define WM8991_RI34ZC_BIT			6
+#define WM8991_RIN34VOL_MASK                    0x001F  /* RIN34VOL - [4:0] */
+#define WM8991_RIN34VOL_SHIFT			0
+
+/*
+ * R28 (0x1C) - Left Output Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_LOZC                             0x0080  /* LOZC */
+#define WM8991_LOZC_BIT				7
+#define WM8991_LOUTVOL_MASK                     0x007F  /* LOUTVOL - [6:0] */
+#define WM8991_LOUTVOL_SHIFT			0
+/*
+ * R29 (0x1D) - Right Output Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_ROZC                             0x0080  /* ROZC */
+#define WM8991_ROZC_BIT				7
+#define WM8991_ROUTVOL_MASK                     0x007F  /* ROUTVOL - [6:0] */
+#define WM8991_ROUTVOL_SHIFT			0
+/*
+ * R30 (0x1E) - Line Outputs Volume
+ */
+#define WM8991_LONMUTE                          0x0040  /* LONMUTE */
+#define WM8991_LONMUTE_BIT			6
+#define WM8991_LOPMUTE                          0x0020  /* LOPMUTE */
+#define WM8991_LOPMUTE_BIT			5
+#define WM8991_LOATTN                           0x0010  /* LOATTN */
+#define WM8991_LOATTN_BIT			4
+#define WM8991_RONMUTE                          0x0004  /* RONMUTE */
+#define WM8991_RONMUTE_BIT			2
+#define WM8991_ROPMUTE                          0x0002  /* ROPMUTE */
+#define WM8991_ROPMUTE_BIT			1
+#define WM8991_ROATTN                           0x0001  /* ROATTN */
+#define WM8991_ROATTN_BIT			0
+
+/*
+ * R31 (0x1F) - Out3/4 Volume
+ */
+#define WM8991_OUT3MUTE                         0x0020  /* OUT3MUTE */
+#define WM8991_OUT3MUTE_BIT			5
+#define WM8991_OUT3ATTN                         0x0010  /* OUT3ATTN */
+#define WM8991_OUT3ATTN_BIT			4
+#define WM8991_OUT4MUTE                         0x0002  /* OUT4MUTE */
+#define WM8991_OUT4MUTE_BIT			1
+#define WM8991_OUT4ATTN                         0x0001  /* OUT4ATTN */
+#define WM8991_OUT4ATTN_BIT			0
+
+/*
+ * R32 (0x20) - Left OPGA Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_LOPGAZC                          0x0080  /* LOPGAZC */
+#define WM8991_LOPGAZC_BIT			7
+#define WM8991_LOPGAVOL_MASK                    0x007F  /* LOPGAVOL - [6:0] */
+#define WM8991_LOPGAVOL_SHIFT			0
+
+/*
+ * R33 (0x21) - Right OPGA Volume
+ */
+#define WM8991_OPVU                             0x0100  /* OPVU */
+#define WM8991_ROPGAZC                          0x0080  /* ROPGAZC */
+#define WM8991_ROPGAZC_BIT			7
+#define WM8991_ROPGAVOL_MASK                    0x007F  /* ROPGAVOL - [6:0] */
+#define WM8991_ROPGAVOL_SHIFT			0
+/*
+ * R34 (0x22) - Speaker Volume
+ */
+#define WM8991_SPKVOL_MASK                      0x0003  /* SPKVOL - [1:0] */
+#define WM8991_SPKVOL_SHIFT			0
+
+/*
+ * R35 (0x23) - ClassD1
+ */
+#define WM8991_CDMODE                           0x0100  /* CDMODE */
+#define WM8991_CDMODE_BIT			8
+
+/*
+ * R37 (0x25) - ClassD3
+ */
+#define WM8991_DCGAIN_MASK                      0x0007  /* DCGAIN - [5:3] */
+#define WM8991_DCGAIN_SHIFT			3
+#define WM8991_ACGAIN_MASK                      0x0007  /* ACGAIN - [2:0] */
+#define WM8991_ACGAIN_SHIFT			0
+/*
+ * R39 (0x27) - Input Mixer1
+ */
+#define WM8991_AINLMODE_MASK                    0x000C  /* AINLMODE - [3:2] */
+#define WM8991_AINLMODE_SHIFT			2
+#define WM8991_AINRMODE_MASK                    0x0003  /* AINRMODE - [1:0] */
+#define WM8991_AINRMODE_SHIFT			0
+
+/*
+ * R40 (0x28) - Input Mixer2
+ */
+#define WM8991_LMP4								0x0080	/* LMP4 */
+#define WM8991_LMP4_BIT                         7		/* LMP4 */
+#define WM8991_LMN3                             0x0040  /* LMN3 */
+#define WM8991_LMN3_BIT                         6       /* LMN3 */
+#define WM8991_LMP2                             0x0020  /* LMP2 */
+#define WM8991_LMP2_BIT                         5       /* LMP2 */
+#define WM8991_LMN1                             0x0010  /* LMN1 */
+#define WM8991_LMN1_BIT                         4       /* LMN1 */
+#define WM8991_RMP4                             0x0008  /* RMP4 */
+#define WM8991_RMP4_BIT                         3       /* RMP4 */
+#define WM8991_RMN3                             0x0004  /* RMN3 */
+#define WM8991_RMN3_BIT                         2       /* RMN3 */
+#define WM8991_RMP2                             0x0002  /* RMP2 */
+#define WM8991_RMP2_BIT                         1       /* RMP2 */
+#define WM8991_RMN1                             0x0001  /* RMN1 */
+#define WM8991_RMN1_BIT                         0       /* RMN1 */
+
+/*
+ * R41 (0x29) - Input Mixer3
+ */
+#define WM8991_L34MNB                           0x0100  /* L34MNB */
+#define WM8991_L34MNB_BIT			8
+#define WM8991_L34MNBST                         0x0080  /* L34MNBST */
+#define WM8991_L34MNBST_BIT			7
+#define WM8991_L12MNB                           0x0020  /* L12MNB */
+#define WM8991_L12MNB_BIT			5
+#define WM8991_L12MNBST                         0x0010  /* L12MNBST */
+#define WM8991_L12MNBST_BIT			4
+#define WM8991_LDBVOL_MASK                      0x0007  /* LDBVOL - [2:0] */
+#define WM8991_LDBVOL_SHIFT			0
+
+/*
+ * R42 (0x2A) - Input Mixer4
+ */
+#define WM8991_R34MNB                           0x0100  /* R34MNB */
+#define WM8991_R34MNB_BIT			8
+#define WM8991_R34MNBST                         0x0080  /* R34MNBST */
+#define WM8991_R34MNBST_BIT			7
+#define WM8991_R12MNB                           0x0020  /* R12MNB */
+#define WM8991_R12MNB_BIT			5
+#define WM8991_R12MNBST                         0x0010  /* R12MNBST */
+#define WM8991_R12MNBST_BIT			4
+#define WM8991_RDBVOL_MASK                      0x0007  /* RDBVOL - [2:0] */
+#define WM8991_RDBVOL_SHIFT			0
+
+/*
+ * R43 (0x2B) - Input Mixer5
+ */
+#define WM8991_LI2BVOL_MASK                     0x07  /* LI2BVOL - [8:6] */
+#define WM8991_LI2BVOL_SHIFT			6
+#define WM8991_LR4BVOL_MASK                     0x07  /* LR4BVOL - [5:3] */
+#define WM8991_LR4BVOL_SHIFT			3
+#define WM8991_LL4BVOL_MASK                     0x07  /* LL4BVOL - [2:0] */
+#define WM8991_LL4BVOL_SHIFT			0
+
+/*
+ * R44 (0x2C) - Input Mixer6
+ */
+#define WM8991_RI2BVOL_MASK                     0x07  /* RI2BVOL - [8:6] */
+#define WM8991_RI2BVOL_SHIFT			6
+#define WM8991_RL4BVOL_MASK                     0x07  /* RL4BVOL - [5:3] */
+#define WM8991_RL4BVOL_SHIFT			3
+#define WM8991_RR4BVOL_MASK                     0x07  /* RR4BVOL - [2:0] */
+#define WM8991_RR4BVOL_SHIFT			0
+
+/*
+ * R45 (0x2D) - Output Mixer1
+ */
+#define WM8991_LRBLO                            0x0080  /* LRBLO */
+#define WM8991_LRBLO_BIT			7
+#define WM8991_LLBLO                            0x0040  /* LLBLO */
+#define WM8991_LLBLO_BIT			6
+#define WM8991_LRI3LO                           0x0020  /* LRI3LO */
+#define WM8991_LRI3LO_BIT			5
+#define WM8991_LLI3LO                           0x0010  /* LLI3LO */
+#define WM8991_LLI3LO_BIT			4
+#define WM8991_LR12LO                           0x0008  /* LR12LO */
+#define WM8991_LR12LO_BIT			3
+#define WM8991_LL12LO                           0x0004  /* LL12LO */
+#define WM8991_LL12LO_BIT			2
+#define WM8991_LDLO                             0x0001  /* LDLO */
+#define WM8991_LDLO_BIT				0
+
+/*
+ * R46 (0x2E) - Output Mixer2
+ */
+#define WM8991_RLBRO                            0x0080  /* RLBRO */
+#define WM8991_RLBRO_BIT			7
+#define WM8991_RRBRO                            0x0040  /* RRBRO */
+#define WM8991_RRBRO_BIT			6
+#define WM8991_RLI3RO                           0x0020  /* RLI3RO */
+#define WM8991_RLI3RO_BIT			5
+#define WM8991_RRI3RO                           0x0010  /* RRI3RO */
+#define WM8991_RRI3RO_BIT			4
+#define WM8991_RL12RO                           0x0008  /* RL12RO */
+#define WM8991_RL12RO_BIT			3
+#define WM8991_RR12RO                           0x0004  /* RR12RO */
+#define WM8991_RR12RO_BIT			2
+#define WM8991_RDRO                             0x0001  /* RDRO */
+#define WM8991_RDRO_BIT				0
+
+/*
+ * R47 (0x2F) - Output Mixer3
+ */
+#define WM8991_LLI3LOVOL_MASK                   0x07  /* LLI3LOVOL - [8:6] */
+#define WM8991_LLI3LOVOL_SHIFT			6
+#define WM8991_LR12LOVOL_MASK                   0x07  /* LR12LOVOL - [5:3] */
+#define WM8991_LR12LOVOL_SHIFT			3
+#define WM8991_LL12LOVOL_MASK                   0x07  /* LL12LOVOL - [2:0] */
+#define WM8991_LL12LOVOL_SHIFT			0
+
+/*
+ * R48 (0x30) - Output Mixer4
+ */
+#define WM8991_RRI3ROVOL_MASK                   0x07  /* RRI3ROVOL - [8:6] */
+#define WM8991_RRI3ROVOL_SHIFT			6
+#define WM8991_RL12ROVOL_MASK                   0x07  /* RL12ROVOL - [5:3] */
+#define WM8991_RL12ROVOL_SHIFT			3
+#define WM8991_RR12ROVOL_MASK                   0x07  /* RR12ROVOL - [2:0] */
+#define WM8991_RR12ROVOL_SHIFT			0
+
+/*
+ * R49 (0x31) - Output Mixer5
+ */
+#define WM8991_LRI3LOVOL_MASK                   0x07  /* LRI3LOVOL - [8:6] */
+#define WM8991_LRI3LOVOL_SHIFT			6
+#define WM8991_LRBLOVOL_MASK                    0x07  /* LRBLOVOL - [5:3] */
+#define WM8991_LRBLOVOL_SHIFT			3
+#define WM8991_LLBLOVOL_MASK                    0x07  /* LLBLOVOL - [2:0] */
+#define WM8991_LLBLOVOL_SHIFT			0
+
+/*
+ * R50 (0x32) - Output Mixer6
+ */
+#define WM8991_RLI3ROVOL_MASK                   0x07  /* RLI3ROVOL - [8:6] */
+#define WM8991_RLI3ROVOL_SHIFT			6
+#define WM8991_RLBROVOL_MASK                    0x07  /* RLBROVOL - [5:3] */
+#define WM8991_RLBROVOL_SHIFT			3
+#define WM8991_RRBROVOL_MASK                    0x07  /* RRBROVOL - [2:0] */
+#define WM8991_RRBROVOL_SHIFT			0
+
+/*
+ * R51 (0x33) - Out3/4 Mixer
+ */
+#define WM8991_VSEL_MASK                        0x0180  /* VSEL - [8:7] */
+#define WM8991_LI4O3                            0x0020  /* LI4O3 */
+#define WM8991_LI4O3_BIT			5
+#define WM8991_LPGAO3                           0x0010  /* LPGAO3 */
+#define WM8991_LPGAO3_BIT			4
+#define WM8991_RI4O4                            0x0002  /* RI4O4 */
+#define WM8991_RI4O4_BIT			1
+#define WM8991_RPGAO4                           0x0001  /* RPGAO4 */
+#define WM8991_RPGAO4_BIT			0
+/*
+ * R52 (0x34) - Line Mixer1
+ */
+#define WM8991_LLOPGALON                        0x0040  /* LLOPGALON */
+#define WM8991_LLOPGALON_BIT			6
+#define WM8991_LROPGALON                        0x0020  /* LROPGALON */
+#define WM8991_LROPGALON_BIT			5
+#define WM8991_LOPLON                           0x0010  /* LOPLON */
+#define WM8991_LOPLON_BIT			4
+#define WM8991_LR12LOP                          0x0004  /* LR12LOP */
+#define WM8991_LR12LOP_BIT			2
+#define WM8991_LL12LOP                          0x0002  /* LL12LOP */
+#define WM8991_LL12LOP_BIT			1
+#define WM8991_LLOPGALOP                        0x0001  /* LLOPGALOP */
+#define WM8991_LLOPGALOP_BIT			0
+/*
+ * R53 (0x35) - Line Mixer2
+ */
+#define WM8991_RROPGARON                        0x0040  /* RROPGARON */
+#define WM8991_RROPGARON_BIT			6
+#define WM8991_RLOPGARON                        0x0020  /* RLOPGARON */
+#define WM8991_RLOPGARON_BIT			5
+#define WM8991_ROPRON                           0x0010  /* ROPRON */
+#define WM8991_ROPRON_BIT			4
+#define WM8991_RL12ROP                          0x0004  /* RL12ROP */
+#define WM8991_RL12ROP_BIT			2
+#define WM8991_RR12ROP                          0x0002  /* RR12ROP */
+#define WM8991_RR12ROP_BIT			1
+#define WM8991_RROPGAROP                        0x0001  /* RROPGAROP */
+#define WM8991_RROPGAROP_BIT			0
+
+/*
+ * R54 (0x36) - Speaker Mixer
+ */
+#define WM8991_LB2SPK                           0x0080  /* LB2SPK */
+#define WM8991_LB2SPK_BIT			7
+#define WM8991_RB2SPK                           0x0040  /* RB2SPK */
+#define WM8991_RB2SPK_BIT			6
+#define WM8991_LI2SPK                           0x0020  /* LI2SPK */
+#define WM8991_LI2SPK_BIT			5
+#define WM8991_RI2SPK                           0x0010  /* RI2SPK */
+#define WM8991_RI2SPK_BIT			4
+#define WM8991_LOPGASPK                         0x0008  /* LOPGASPK */
+#define WM8991_LOPGASPK_BIT			3
+#define WM8991_ROPGASPK                         0x0004  /* ROPGASPK */
+#define WM8991_ROPGASPK_BIT			2
+#define WM8991_LDSPK                            0x0002  /* LDSPK */
+#define WM8991_LDSPK_BIT			1
+#define WM8991_RDSPK                            0x0001  /* RDSPK */
+#define WM8991_RDSPK_BIT			0
+
+/*
+ * R55 (0x37) - Additional Control
+ */
+#define WM8991_VROI                             0x0001  /* VROI */
+
+/*
+ * R56 (0x38) - AntiPOP1
+ */
+#define WM8991_DIS_LLINE                        0x0020  /* DIS_LLINE */
+#define WM8991_DIS_RLINE                        0x0010  /* DIS_RLINE */
+#define WM8991_DIS_OUT3                         0x0008  /* DIS_OUT3 */
+#define WM8991_DIS_OUT4                         0x0004  /* DIS_OUT4 */
+#define WM8991_DIS_LOUT                         0x0002  /* DIS_LOUT */
+#define WM8991_DIS_ROUT                         0x0001  /* DIS_ROUT */
+
+/*
+ * R57 (0x39) - AntiPOP2
+ */
+#define WM8991_SOFTST                           0x0040  /* SOFTST */
+#define WM8991_BUFIOEN                          0x0008  /* BUFIOEN */
+#define WM8991_BUFDCOPEN                        0x0004  /* BUFDCOPEN */
+#define WM8991_POBCTRL                          0x0002  /* POBCTRL */
+#define WM8991_VMIDTOG                          0x0001  /* VMIDTOG */
+
+/*
+ * R58 (0x3A) - MICBIAS
+ */
+#define WM8991_MCDSCTH_MASK                     0x00C0  /* MCDSCTH - [7:6] */
+#define WM8991_MCDTHR_MASK                      0x0038  /* MCDTHR - [5:3] */
+#define WM8991_MCD                              0x0004  /* MCD */
+#define WM8991_MBSEL                            0x0001  /* MBSEL */
+
+/*
+ * R60 (0x3C) - PLL1
+ */
+#define WM8991_SDM                              0x0080  /* SDM */
+#define WM8991_PRESCALE                         0x0040  /* PRESCALE */
+#define WM8991_PLLN_MASK                        0x000F  /* PLLN - [3:0] */
+
+/*
+ * R61 (0x3D) - PLL2
+ */
+#define WM8991_PLLK1_MASK                       0x00FF  /* PLLK1 - [7:0] */
+
+/*
+ * R62 (0x3E) - PLL3
+ */
+#define WM8991_PLLK2_MASK                       0x00FF  /* PLLK2 - [7:0] */
+
+/*
+ * R63 (0x3F) - Internal Driver Bits
+ */
+#define WM8991_INMIXL_PWR_BIT			0
+#define WM8991_AINLMUX_PWR_BIT			1
+#define WM8991_INMIXR_PWR_BIT			2
+#define WM8991_AINRMUX_PWR_BIT			3
+
+#define WM8991_MCLK_DIV 0
+#define WM8991_DACCLK_DIV 1
+#define WM8991_ADCCLK_DIV 2
+#define WM8991_BCLK_DIV 3
+
+#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
+					 tlv_array) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
+		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+	.tlv.p = (tlv_array), \
+	.info = snd_soc_info_volsw, \
+	.get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
+	.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+
+#endif /* _WM8991_H */

+ 1 - 1
sound/soc/codecs/wm8993.c

@@ -242,7 +242,7 @@ struct wm8993_priv {
 	int fll_src;
 };
 
-static int wm8993_volatile(unsigned int reg)
+static int wm8993_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM8993_SOFTWARE_RESET:

+ 6 - 6
sound/soc/codecs/wm8994-tables.c

@@ -62,8 +62,8 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
 	{ 0x00FF, 0x00FF }, /* R58    - MICBIAS */
 	{ 0x000F, 0x000F }, /* R59    - LDO 1 */
 	{ 0x0007, 0x0007 }, /* R60    - LDO 2 */
-	{ 0x0000, 0x0000 }, /* R61 */
-	{ 0x0000, 0x0000 }, /* R62 */
+	{ 0xFFFF, 0xFFFF }, /* R61 */
+	{ 0xFFFF, 0xFFFF }, /* R62 */
 	{ 0x0000, 0x0000 }, /* R63 */
 	{ 0x0000, 0x0000 }, /* R64 */
 	{ 0x0000, 0x0000 }, /* R65 */
@@ -209,9 +209,9 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
 	{ 0x0000, 0x0000 }, /* R205 */
 	{ 0x0000, 0x0000 }, /* R206 */
 	{ 0x0000, 0x0000 }, /* R207 */
-	{ 0x0000, 0x0000 }, /* R208 */
-	{ 0x0000, 0x0000 }, /* R209 */
-	{ 0x0000, 0x0000 }, /* R210 */
+	{ 0xFFFF, 0xFFFF }, /* R208 */
+	{ 0xFFFF, 0xFFFF }, /* R209 */
+	{ 0xFFFF, 0xFFFF }, /* R210 */
 	{ 0x0000, 0x0000 }, /* R211 */
 	{ 0x0000, 0x0000 }, /* R212 */
 	{ 0x0000, 0x0000 }, /* R213 */
@@ -1573,7 +1573,7 @@ const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE] = {
 	{ 0x03C3, 0x03C3 }, /* R1569  - Sidetone */
 };
 
-const __devinitdata u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
+const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE] = {
 	0x8994,     /* R0     - Software Reset */
 	0x0000,     /* R1     - Power Management (1) */
 	0x6000,     /* R2     - Power Management (2) */

+ 74 - 63
sound/soc/codecs/wm8994.c

@@ -102,8 +102,7 @@ struct wm8994_priv {
 
 	wm8958_micdet_cb jack_cb;
 	void *jack_cb_data;
-	bool jack_is_mic;
-	bool jack_is_video;
+	int micdet_irq;
 
 	int revision;
 	struct wm8994_pdata *pdata;
@@ -115,7 +114,7 @@ struct wm8994_priv {
 	unsigned int aif2clk_disable:1;
 };
 
-static int wm8994_readable(unsigned int reg)
+static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM8994_GPIO_1:
@@ -142,7 +141,7 @@ static int wm8994_readable(unsigned int reg)
 	return wm8994_access_masks[reg].readable != 0;
 }
 
-static int wm8994_volatile(unsigned int reg)
+static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
 	if (reg >= WM8994_CACHE_SIZE)
 		return 1;
@@ -170,7 +169,7 @@ static int wm8994_write(struct snd_soc_codec *codec, unsigned int reg,
 
 	BUG_ON(reg > WM8994_MAX_REGISTER);
 
-	if (!wm8994_volatile(reg)) {
+	if (!wm8994_volatile(codec, reg)) {
 		ret = snd_soc_cache_write(codec, reg, value);
 		if (ret != 0)
 			dev_err(codec->dev, "Cache write to %x failed: %d\n",
@@ -188,7 +187,7 @@ static unsigned int wm8994_read(struct snd_soc_codec *codec,
 
 	BUG_ON(reg > WM8994_MAX_REGISTER);
 
-	if (!wm8994_volatile(reg) && wm8994_readable(reg) &&
+	if (!wm8994_volatile(codec, reg) && wm8994_readable(codec, reg) &&
 	    reg < codec->driver->reg_cache_size) {
 		ret = snd_soc_cache_read(codec, reg, &val);
 		if (ret >= 0)
@@ -529,7 +528,7 @@ static int wm8994_get_retune_mobile_enum(struct snd_kcontrol *kcontrol,
 					 struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-	struct wm8994_priv *wm8994 =snd_soc_codec_get_drvdata(codec);
+	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 	int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
 
 	ucontrol->value.enumerated.item[0] = wm8994->retune_mobile_cfg[block];
@@ -1103,6 +1102,13 @@ static int adc_mux_ev(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
+static int micbias_ev(struct snd_soc_dapm_widget *w,
+		      struct snd_kcontrol *kcontrol, int event)
+{
+	late_enable_ev(w, kcontrol, event);
+	return 0;
+}
+
 static int dac_ev(struct snd_soc_dapm_widget *w,
 		  struct snd_kcontrol *kcontrol, int event)
 {
@@ -1418,7 +1424,7 @@ SND_SOC_DAPM_DAC_E("DAC1R", NULL, SND_SOC_NOPM, 0, 0,
 
 static const struct snd_soc_dapm_widget wm8994_dac_widgets[] = {
 SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0),
-SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0),
+SND_SOC_DAPM_DAC("DAC2R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0),
 SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0),
 SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0),
 };
@@ -1440,6 +1446,10 @@ SND_SOC_DAPM_INPUT("DMIC1DAT"),
 SND_SOC_DAPM_INPUT("DMIC2DAT"),
 SND_SOC_DAPM_INPUT("Clock"),
 
+SND_SOC_DAPM_MICBIAS("MICBIAS", WM8994_MICBIAS, 2, 0),
+SND_SOC_DAPM_SUPPLY_S("MICBIAS Supply", 1, SND_SOC_NOPM, 0, 0, micbias_ev,
+		      SND_SOC_DAPM_PRE_PMU),
+
 SND_SOC_DAPM_SUPPLY("CLK_SYS", SND_SOC_NOPM, 0, 0, clk_sys_event,
 		    SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
 
@@ -1755,6 +1765,8 @@ static const struct snd_soc_dapm_route wm8994_revd_intercon[] = {
 	{ "AIF2DACDAT", NULL, "AIF1DACDAT" },
 	{ "AIF1ADCDAT", NULL, "AIF2ADCDAT" },
 	{ "AIF2ADCDAT", NULL, "AIF1ADCDAT" },
+	{ "MICBIAS", NULL, "CLK_SYS" },
+	{ "MICBIAS", NULL, "MICBIAS Supply" },
 };
 
 static const struct snd_soc_dapm_route wm8994_intercon[] = {
@@ -2883,6 +2895,13 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994)
 	else
 		snd_soc_add_controls(wm8994->codec, wm8994_eq_controls,
 				     ARRAY_SIZE(wm8994_eq_controls));
+
+	for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) {
+		if (pdata->micbias[i]) {
+			snd_soc_write(codec, WM8958_MICBIAS1 + i,
+				pdata->micbias[i] & 0xffff);
+		}
+	}
 }
 
 /**
@@ -2993,46 +3012,18 @@ static void wm8958_default_micdet(u16 status, void *data)
 	int report = 0;
 
 	/* If nothing present then clear our statuses */
-	if (!(status & WM8958_MICD_STS)) {
-		wm8994->jack_is_video = false;
-		wm8994->jack_is_mic = false;
+	if (!(status & WM8958_MICD_STS))
 		goto done;
-	}
 
-	/* Assume anything over 475 ohms is a microphone and remember
-	 * that we've seen one (since buttons override it) */
-	if (status & 0x600)
-		wm8994->jack_is_mic = true;
-	if (wm8994->jack_is_mic)
-		report |= SND_JACK_MICROPHONE;
-
-	/* Video has an impedence of approximately 75 ohms; assume
-	 * this isn't used as a button and remember it since buttons
-	 * override it. */
-	if (status & 0x40)
-		wm8994->jack_is_video = true;
-	if (wm8994->jack_is_video)
-		report |= SND_JACK_VIDEOOUT;
+	report = SND_JACK_MICROPHONE;
 
 	/* Everything else is buttons; just assign slots */
-	if (status & 0x4)
+	if (status & 0x1c0)
 		report |= SND_JACK_BTN_0;
-	if (status & 0x8)
-		report |= SND_JACK_BTN_1;
-	if (status & 0x10)
-		report |= SND_JACK_BTN_2;
-	if (status & 0x20)
-		report |= SND_JACK_BTN_3;
-	if (status & 0x80)
-		report |= SND_JACK_BTN_4;
-	if (status & 0x100)
-		report |= SND_JACK_BTN_5;
 
 done:
 	snd_soc_jack_report(wm8994->micdet[0].jack, report,
-			    SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 |
-			    SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5 |
-			    SND_JACK_MICROPHONE | SND_JACK_VIDEOOUT);
+			    SND_JACK_BTN_0 | SND_JACK_MICROPHONE);
 }
 
 /**
@@ -3131,13 +3122,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 	wm8994->pdata = dev_get_platdata(codec->dev->parent);
 	wm8994->codec = codec;
 
+	if (wm8994->pdata && wm8994->pdata->micdet_irq)
+		wm8994->micdet_irq = wm8994->pdata->micdet_irq;
+	else if (wm8994->pdata && wm8994->pdata->irq_base)
+		wm8994->micdet_irq = wm8994->pdata->irq_base +
+				     WM8994_IRQ_MIC1_DET;
+
 	pm_runtime_enable(codec->dev);
 	pm_runtime_resume(codec->dev);
 
 	/* Read our current status back from the chip - we don't want to
 	 * reset as this may interfere with the GPIO or LDO operation. */
 	for (i = 0; i < WM8994_CACHE_SIZE; i++) {
-		if (!wm8994_readable(i) || wm8994_volatile(i))
+		if (!wm8994_readable(codec, i) || wm8994_volatile(codec, i))
 			continue;
 
 		ret = wm8994_reg_read(codec->control_data, i);
@@ -3179,14 +3176,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 
 	switch (control->type) {
 	case WM8994:
-		ret = wm8994_request_irq(codec->control_data,
-					 WM8994_IRQ_MIC1_DET,
-					 wm8994_mic_irq, "Mic 1 detect",
-					 wm8994);
-		if (ret != 0)
-			dev_warn(codec->dev,
-				 "Failed to request Mic1 detect IRQ: %d\n",
-				 ret);
+		if (wm8994->micdet_irq) {
+			ret = request_threaded_irq(wm8994->micdet_irq, NULL,
+						   wm8994_mic_irq,
+						   IRQF_TRIGGER_RISING,
+						   "Mic1 detect",
+						   wm8994);
+			if (ret != 0)
+				dev_warn(codec->dev,
+					 "Failed to request Mic1 detect IRQ: %d\n",
+					 ret);
+		}
 
 		ret = wm8994_request_irq(codec->control_data,
 					 WM8994_IRQ_MIC1_SHRT,
@@ -3217,15 +3217,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 		break;
 
 	case WM8958:
-		ret = wm8994_request_irq(codec->control_data,
-					 WM8994_IRQ_MIC1_DET,
-					 wm8958_mic_irq, "Mic detect",
-					 wm8994);
-		if (ret != 0)
-			dev_warn(codec->dev,
-				 "Failed to request Mic detect IRQ: %d\n",
-				 ret);
-		break;
+		if (wm8994->micdet_irq) {
+			ret = request_threaded_irq(wm8994->micdet_irq, NULL,
+						   wm8958_mic_irq,
+						   IRQF_TRIGGER_RISING,
+						   "Mic detect",
+						   wm8994);
+			if (ret != 0)
+				dev_warn(codec->dev,
+					 "Failed to request Mic detect IRQ: %d\n",
+					 ret);
+		}
 	}
 
 	/* Remember if AIFnLRCLK is configured as a GPIO.  This should be
@@ -3325,6 +3327,12 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 	case WM8958:
 		snd_soc_add_controls(codec, wm8958_snd_controls,
 				     ARRAY_SIZE(wm8958_snd_controls));
+		snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets,
+					  ARRAY_SIZE(wm8994_lateclk_widgets));
+		snd_soc_dapm_new_controls(dapm, wm8994_adc_widgets,
+					  ARRAY_SIZE(wm8994_adc_widgets));
+		snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets,
+					  ARRAY_SIZE(wm8994_dac_widgets));
 		snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets,
 					  ARRAY_SIZE(wm8958_dapm_widgets));
 		break;
@@ -3350,6 +3358,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 		}
 		break;
 	case WM8958:
+		snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon,
+					ARRAY_SIZE(wm8994_lateclk_intercon));
 		snd_soc_dapm_add_routes(dapm, wm8958_intercon,
 					ARRAY_SIZE(wm8958_intercon));
 		break;
@@ -3361,7 +3371,8 @@ err_irq:
 	wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994);
 	wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994);
 	wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994);
-	wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994);
+	if (wm8994->micdet_irq)
+		free_irq(wm8994->micdet_irq, wm8994);
 err:
 	kfree(wm8994);
 	return ret;
@@ -3378,8 +3389,8 @@ static int  wm8994_codec_remove(struct snd_soc_codec *codec)
 
 	switch (control->type) {
 	case WM8994:
-		wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT,
-				wm8994);
+		if (wm8994->micdet_irq)
+			free_irq(wm8994->micdet_irq, wm8994);
 		wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET,
 				wm8994);
 		wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT,
@@ -3389,8 +3400,8 @@ static int  wm8994_codec_remove(struct snd_soc_codec *codec)
 		break;
 
 	case WM8958:
-		wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET,
-				wm8994);
+		if (wm8994->micdet_irq)
+			free_irq(wm8994->micdet_irq, wm8994);
 		break;
 	}
 	kfree(wm8994->retune_mobile_texts);

+ 1 - 1
sound/soc/codecs/wm8994.h

@@ -43,6 +43,6 @@ struct wm8994_access_mask {
 };
 
 extern const struct wm8994_access_mask wm8994_access_masks[WM8994_CACHE_SIZE];
-extern const __devinitdata  u16 wm8994_reg_defaults[WM8994_CACHE_SIZE];
+extern const u16 wm8994_reg_defaults[WM8994_CACHE_SIZE];
 
 #endif

+ 98 - 5
sound/soc/codecs/wm8995.c

@@ -19,6 +19,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -30,6 +31,18 @@
 
 #include "wm8995.h"
 
+#define WM8995_NUM_SUPPLIES 8
+static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = {
+	"DCVDD",
+	"DBVDD1",
+	"DBVDD2",
+	"DBVDD3",
+	"AVDD1",
+	"AVDD2",
+	"CPVDD",
+	"MICVDD"
+};
+
 static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = {
 	[0]     = 0x8995, [5]     = 0x0100, [16]    = 0x000b, [17]    = 0x000b,
 	[24]    = 0x02c0, [25]    = 0x02c0, [26]    = 0x02c0, [27]    = 0x02c0,
@@ -126,8 +139,37 @@ struct wm8995_priv {
 	int mclk[2];
 	int aifclk[2];
 	struct fll_config fll[2], fll_suspend[2];
+	struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES];
+	struct notifier_block disable_nb[WM8995_NUM_SUPPLIES];
+	struct snd_soc_codec *codec;
 };
 
+/*
+ * We can't use the same notifier block for more than one supply and
+ * there's no way I can see to get from a callback to the caller
+ * except container_of().
+ */
+#define WM8995_REGULATOR_EVENT(n) \
+static int wm8995_regulator_event_##n(struct notifier_block *nb, \
+				      unsigned long event, void *data)    \
+{ \
+	struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \
+				     disable_nb[n]); \
+	if (event & REGULATOR_EVENT_DISABLE) { \
+		wm8995->codec->cache_sync = 1; \
+	} \
+	return 0; \
+}
+
+WM8995_REGULATOR_EVENT(0)
+WM8995_REGULATOR_EVENT(1)
+WM8995_REGULATOR_EVENT(2)
+WM8995_REGULATOR_EVENT(3)
+WM8995_REGULATOR_EVENT(4)
+WM8995_REGULATOR_EVENT(5)
+WM8995_REGULATOR_EVENT(6)
+WM8995_REGULATOR_EVENT(7)
+
 static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
 static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0);
 static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0);
@@ -909,7 +951,7 @@ static const struct snd_soc_dapm_route wm8995_intercon[] = {
 	{ "SPK2R", NULL, "SPK2R Driver" }
 };
 
-static int wm8995_volatile(unsigned int reg)
+static int wm8995_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
 	/* out of bounds registers are generally considered
 	 * volatile to support register banks that are partially
@@ -1483,6 +1525,11 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
 		break;
 	case SND_SOC_BIAS_STANDBY:
 		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+			ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
+						    wm8995->supplies);
+			if (ret)
+				return ret;
+
 			ret = snd_soc_cache_sync(codec);
 			if (ret) {
 				dev_err(codec->dev,
@@ -1492,12 +1539,13 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
 
 			snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
 					    WM8995_BG_ENA_MASK, WM8995_BG_ENA);
-
 		}
 		break;
 	case SND_SOC_BIAS_OFF:
 		snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
 				    WM8995_BG_ENA_MASK, 0);
+		regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies),
+				       wm8995->supplies);
 		break;
 	}
 
@@ -1536,10 +1584,12 @@ static int wm8995_remove(struct snd_soc_codec *codec)
 static int wm8995_probe(struct snd_soc_codec *codec)
 {
 	struct wm8995_priv *wm8995;
+	int i;
 	int ret;
 
 	codec->dapm.idle_bias_off = 1;
 	wm8995 = snd_soc_codec_get_drvdata(codec);
+	wm8995->codec = codec;
 
 	ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type);
 	if (ret < 0) {
@@ -1547,21 +1597,58 @@ static int wm8995_probe(struct snd_soc_codec *codec)
 		return ret;
 	}
 
+	for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++)
+		wm8995->supplies[i].supply = wm8995_supply_names[i];
+
+	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies),
+				 wm8995->supplies);
+	if (ret) {
+		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
+		return ret;
+	}
+
+	wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0;
+	wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1;
+	wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2;
+	wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3;
+	wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4;
+	wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5;
+	wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6;
+	wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7;
+
+	/* This should really be moved into the regulator core */
+	for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) {
+		ret = regulator_register_notifier(wm8995->supplies[i].consumer,
+						  &wm8995->disable_nb[i]);
+		if (ret) {
+			dev_err(codec->dev,
+				"Failed to register regulator notifier: %d\n",
+				ret);
+		}
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
+				    wm8995->supplies);
+	if (ret) {
+		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
+		goto err_reg_get;
+	}
+
 	ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to read device ID: %d\n", ret);
-		return ret;
+		goto err_reg_enable;
 	}
 
 	if (ret != 0x8995) {
 		dev_err(codec->dev, "Invalid device ID: %#x\n", ret);
-		return -EINVAL;
+		goto err_reg_enable;
 	}
 
 	ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0);
 	if (ret < 0) {
 		dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
-		return ret;
+		goto err_reg_enable;
 	}
 
 	wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1596,6 +1683,12 @@ static int wm8995_probe(struct snd_soc_codec *codec)
 				ARRAY_SIZE(wm8995_intercon));
 
 	return 0;
+
+err_reg_enable:
+	regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
+err_reg_get:
+	regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
+	return ret;
 }
 
 #define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\

+ 37 - 47
sound/soc/codecs/wm9081.c

@@ -167,10 +167,10 @@ struct wm9081_priv {
 	int fll_fref;
 	int fll_fout;
 	int tdm_width;
-	struct wm9081_retune_mobile_config *retune;
+	struct wm9081_pdata pdata;
 };
 
-static int wm9081_volatile_register(unsigned int reg)
+static int wm9081_volatile_register(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM9081_SOFTWARE_RESET:
@@ -389,27 +389,6 @@ SOC_DAPM_SINGLE("IN2 Switch", WM9081_ANALOGUE_MIXER, 2, 1, 0),
 SOC_DAPM_SINGLE("Playback Switch", WM9081_ANALOGUE_MIXER, 4, 1, 0),
 };
 
-static int speaker_event(struct snd_soc_dapm_widget *w,
-			 struct snd_kcontrol *kcontrol, int event)
-{
-	struct snd_soc_codec *codec = w->codec;
-	unsigned int reg = snd_soc_read(codec, WM9081_POWER_MANAGEMENT);
-
-	switch (event) {
-	case SND_SOC_DAPM_POST_PMU:
-		reg |= WM9081_SPK_ENA;
-		break;
-
-	case SND_SOC_DAPM_PRE_PMD:
-		reg &= ~WM9081_SPK_ENA;
-		break;
-	}
-
-	snd_soc_write(codec, WM9081_POWER_MANAGEMENT, reg);
-
-	return 0;
-}
-
 struct _fll_div {
 	u16 fll_fratio;
 	u16 fll_outdiv;
@@ -747,9 +726,8 @@ SND_SOC_DAPM_MIXER_NAMED_CTL("Mixer", SND_SOC_NOPM, 0, 0,
 
 SND_SOC_DAPM_PGA("LINEOUT PGA", WM9081_POWER_MANAGEMENT, 4, 0, NULL, 0),
 
-SND_SOC_DAPM_PGA_E("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0,
-		   speaker_event,
-		   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA("Speaker PGA", WM9081_POWER_MANAGEMENT, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Speaker", WM9081_POWER_MANAGEMENT, 1, 0, NULL, 0),
 
 SND_SOC_DAPM_OUTPUT("LINEOUT"),
 SND_SOC_DAPM_OUTPUT("SPKN"),
@@ -762,7 +740,7 @@ SND_SOC_DAPM_SUPPLY("TOCLK", WM9081_CLOCK_CONTROL_3, 2, 0, NULL, 0),
 };
 
 
-static const struct snd_soc_dapm_route audio_paths[] = {
+static const struct snd_soc_dapm_route wm9081_audio_paths[] = {
 	{ "DAC", NULL, "CLK_SYS" },
 	{ "DAC", NULL, "CLK_DSP" },
 
@@ -780,8 +758,10 @@ static const struct snd_soc_dapm_route audio_paths[] = {
 	{ "Speaker PGA", NULL, "TOCLK" },
 	{ "Speaker PGA", NULL, "CLK_SYS" },
 
-	{ "SPKN", NULL, "Speaker PGA" },
-	{ "SPKP", NULL, "Speaker PGA" },
+	{ "Speaker", NULL, "Speaker PGA" },
+
+	{ "SPKN", NULL, "Speaker" },
+	{ "SPKP", NULL, "Speaker" },
 };
 
 static int wm9081_set_bias_level(struct snd_soc_codec *codec,
@@ -1082,21 +1062,22 @@ static int wm9081_hw_params(struct snd_pcm_substream *substream,
 	aif4 |= wm9081->bclk / wm9081->fs;
 
 	/* Apply a ReTune Mobile configuration if it's in use */
-	if (wm9081->retune) {
-		struct wm9081_retune_mobile_config *retune = wm9081->retune;
+	if (wm9081->pdata.num_retune_configs) {
+		struct wm9081_pdata *pdata = &wm9081->pdata;
 		struct wm9081_retune_mobile_setting *s;
 		int eq1;
 
 		best = 0;
-		best_val = abs(retune->configs[0].rate - wm9081->fs);
-		for (i = 0; i < retune->num_configs; i++) {
-			cur_val = abs(retune->configs[i].rate - wm9081->fs);
+		best_val = abs(pdata->retune_configs[0].rate - wm9081->fs);
+		for (i = 0; i < pdata->num_retune_configs; i++) {
+			cur_val = abs(pdata->retune_configs[i].rate -
+				      wm9081->fs);
 			if (cur_val < best_val) {
 				best_val = cur_val;
 				best = i;
 			}
 		}
-		s = &retune->configs[best];
+		s = &pdata->retune_configs[best];
 
 		dev_dbg(codec->dev, "ReTune Mobile %s tuned for %dHz\n",
 			s->name, s->rate);
@@ -1139,10 +1120,9 @@ static int wm9081_digital_mute(struct snd_soc_dai *codec_dai, int mute)
 	return 0;
 }
 
-static int wm9081_set_sysclk(struct snd_soc_dai *codec_dai,
+static int wm9081_set_sysclk(struct snd_soc_codec *codec,
 			     int clk_id, unsigned int freq, int dir)
 {
-	struct snd_soc_codec *codec = codec_dai->codec;
 	struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
 
 	switch (clk_id) {
@@ -1207,7 +1187,6 @@ static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
 
 static struct snd_soc_dai_ops wm9081_dai_ops = {
 	.hw_params = wm9081_hw_params,
-	.set_sysclk = wm9081_set_sysclk,
 	.set_fmt = wm9081_set_dai_fmt,
 	.digital_mute = wm9081_digital_mute,
 	.set_tdm_slot = wm9081_set_tdm_slot,
@@ -1231,7 +1210,6 @@ static struct snd_soc_dai_driver wm9081_dai = {
 static int wm9081_probe(struct snd_soc_codec *codec)
 {
 	struct wm9081_priv *wm9081 = snd_soc_codec_get_drvdata(codec);
-	struct snd_soc_dapm_context *dapm = &codec->dapm;
 	int ret;
 	u16 reg;
 
@@ -1255,6 +1233,14 @@ static int wm9081_probe(struct snd_soc_codec *codec)
 		return ret;
 	}
 
+	reg = 0;
+	if (wm9081->pdata.irq_high)
+		reg |= WM9081_IRQ_POL;
+	if (!wm9081->pdata.irq_cmos)
+		reg |= WM9081_IRQ_OP_CTRL;
+	snd_soc_update_bits(codec, WM9081_INTERRUPT_CONTROL,
+			    WM9081_IRQ_POL | WM9081_IRQ_OP_CTRL, reg);
+
 	wm9081_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 	/* Enable zero cross by default */
@@ -1266,17 +1252,13 @@ static int wm9081_probe(struct snd_soc_codec *codec)
 
 	snd_soc_add_controls(codec, wm9081_snd_controls,
 			     ARRAY_SIZE(wm9081_snd_controls));
-	if (!wm9081->retune) {
+	if (!wm9081->pdata.num_retune_configs) {
 		dev_dbg(codec->dev,
 			"No ReTune Mobile data, using normal EQ\n");
 		snd_soc_add_controls(codec, wm9081_eq_controls,
 				     ARRAY_SIZE(wm9081_eq_controls));
 	}
 
-	snd_soc_dapm_new_controls(dapm, wm9081_dapm_widgets,
-				  ARRAY_SIZE(wm9081_dapm_widgets));
-	snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
-
 	return ret;
 }
 
@@ -1320,11 +1302,19 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9081 = {
 	.remove = 	wm9081_remove,
 	.suspend =	wm9081_suspend,
 	.resume =	wm9081_resume,
+
+	.set_sysclk = wm9081_set_sysclk,
 	.set_bias_level = wm9081_set_bias_level,
+
 	.reg_cache_size = ARRAY_SIZE(wm9081_reg_defaults),
 	.reg_word_size = sizeof(u16),
 	.reg_cache_default = wm9081_reg_defaults,
 	.volatile_register = wm9081_volatile_register,
+
+	.dapm_widgets	  = wm9081_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets),
+	.dapm_routes     = wm9081_audio_paths,
+	.num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths),
 };
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
@@ -1343,8 +1333,8 @@ static __devinit int wm9081_i2c_probe(struct i2c_client *i2c,
 	wm9081->control_data = i2c;
 
 	if (dev_get_platdata(&i2c->dev))
-		memcpy(&wm9081->retune, dev_get_platdata(&i2c->dev),
-		       sizeof(wm9081->retune));
+		memcpy(&wm9081->pdata, dev_get_platdata(&i2c->dev),
+		       sizeof(wm9081->pdata));
 
 	ret = snd_soc_register_codec(&i2c->dev,
 			&soc_codec_dev_wm9081, &wm9081_dai, 1);
@@ -1368,7 +1358,7 @@ MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id);
 
 static struct i2c_driver wm9081_i2c_driver = {
 	.driver = {
-		.name = "wm9081-codec",
+		.name = "wm9081",
 		.owner = THIS_MODULE,
 	},
 	.probe =    wm9081_i2c_probe,

+ 26 - 19
sound/soc/codecs/wm9090.c

@@ -144,7 +144,7 @@ struct wm9090_priv {
 	void *control_data;
 };
 
-static int wm9090_volatile(unsigned int reg)
+static int wm9090_volatile(struct snd_soc_codec *codec, unsigned int reg)
 {
 	switch (reg) {
 	case WM9090_SOFTWARE_RESET:
@@ -518,7 +518,7 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
 			for (i = 1; i < codec->driver->reg_cache_size; i++) {
 				if (reg_cache[i] == wm9090_reg_defaults[i])
 					continue;
-				if (wm9090_volatile(i))
+				if (wm9090_volatile(codec, i))
 					continue;
 
 				ret = snd_soc_write(codec, i, reg_cache[i]);
@@ -551,7 +551,6 @@ static int wm9090_set_bias_level(struct snd_soc_codec *codec,
 static int wm9090_probe(struct snd_soc_codec *codec)
 {
 	struct wm9090_priv *wm9090 = snd_soc_codec_get_drvdata(codec);
-	u16 *reg_cache = codec->reg_cache;
 	int ret;
 
 	codec->control_data = wm9090->control_data;
@@ -576,22 +575,30 @@ static int wm9090_probe(struct snd_soc_codec *codec)
 	/* Configure some defaults; they will be written out when we
 	 * bring the bias up.
 	 */
-	reg_cache[WM9090_IN1_LINE_INPUT_A_VOLUME] |= WM9090_IN1_VU
-		| WM9090_IN1A_ZC;
-	reg_cache[WM9090_IN1_LINE_INPUT_B_VOLUME] |= WM9090_IN1_VU
-		| WM9090_IN1B_ZC;
-	reg_cache[WM9090_IN2_LINE_INPUT_A_VOLUME] |= WM9090_IN2_VU
-		| WM9090_IN2A_ZC;
-	reg_cache[WM9090_IN2_LINE_INPUT_B_VOLUME] |= WM9090_IN2_VU
-		| WM9090_IN2B_ZC;
-	reg_cache[WM9090_SPEAKER_VOLUME_LEFT] |=
-		WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC;
-	reg_cache[WM9090_LEFT_OUTPUT_VOLUME] |=
-		WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC;
-	reg_cache[WM9090_RIGHT_OUTPUT_VOLUME] |=
-		WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC;
-
-	reg_cache[WM9090_CLOCKING_1] |= WM9090_TOCLK_ENA;
+	snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_A_VOLUME,
+			    WM9090_IN1_VU | WM9090_IN1A_ZC,
+			    WM9090_IN1_VU | WM9090_IN1A_ZC);
+	snd_soc_update_bits(codec, WM9090_IN1_LINE_INPUT_B_VOLUME,
+			    WM9090_IN1_VU | WM9090_IN1B_ZC,
+			    WM9090_IN1_VU | WM9090_IN1B_ZC);
+	snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_A_VOLUME,
+			    WM9090_IN2_VU | WM9090_IN2A_ZC,
+			    WM9090_IN2_VU | WM9090_IN2A_ZC);
+	snd_soc_update_bits(codec, WM9090_IN2_LINE_INPUT_B_VOLUME,
+			    WM9090_IN2_VU | WM9090_IN2B_ZC,
+			    WM9090_IN2_VU | WM9090_IN2B_ZC);
+	snd_soc_update_bits(codec, WM9090_SPEAKER_VOLUME_LEFT,
+			    WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC,
+			    WM9090_SPKOUT_VU | WM9090_SPKOUTL_ZC);
+	snd_soc_update_bits(codec, WM9090_LEFT_OUTPUT_VOLUME,
+			    WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC,
+			    WM9090_HPOUT1_VU | WM9090_HPOUT1L_ZC);
+	snd_soc_update_bits(codec, WM9090_RIGHT_OUTPUT_VOLUME,
+			    WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC,
+			    WM9090_HPOUT1_VU | WM9090_HPOUT1R_ZC);
+
+	snd_soc_update_bits(codec, WM9090_CLOCKING_1,
+			    WM9090_TOCLK_ENA, WM9090_TOCLK_ENA);
 
 	wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 

+ 2 - 1
sound/soc/codecs/wm_hubs.c

@@ -82,7 +82,8 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, unsigned int op)
 	} while (reg & op && count < 400);
 
 	if (reg & op)
-		dev_err(codec->dev, "Timed out waiting for DC Servo\n");
+		dev_err(codec->dev, "Timed out waiting for DC Servo %x\n",
+			op);
 }
 
 /*

+ 19 - 9
sound/soc/davinci/davinci-i2s.c

@@ -658,7 +658,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
+	ioarea = request_mem_region(mem->start, resource_size(mem),
 				    pdev->name);
 	if (!ioarea) {
 		dev_err(&pdev->dev, "McBSP region already claimed\n");
@@ -694,20 +694,25 @@ static int davinci_i2s_probe(struct platform_device *pdev)
 	}
 	clk_enable(dev->clk);
 
-	dev->base = (void __iomem *)IO_ADDRESS(mem->start);
+	dev->base = ioremap(mem->start, resource_size(mem));
+	if (!dev->base) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_release_clk;
+	}
 
 	dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr =
-	    (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG);
+	    (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
 
 	dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr =
-	    (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG);
+	    (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG);
 
 	/* first TX, then RX */
 	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 	if (!res) {
 		dev_err(&pdev->dev, "no DMA resource\n");
 		ret = -ENXIO;
-		goto err_free_mem;
+		goto err_iounmap;
 	}
 	dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start;
 
@@ -715,7 +720,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
 	if (!res) {
 		dev_err(&pdev->dev, "no DMA resource\n");
 		ret = -ENXIO;
-		goto err_free_mem;
+		goto err_iounmap;
 	}
 	dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start;
 	dev->dev = &pdev->dev;
@@ -724,14 +729,19 @@ static int davinci_i2s_probe(struct platform_device *pdev)
 
 	ret = snd_soc_register_dai(&pdev->dev, &davinci_i2s_dai);
 	if (ret != 0)
-		goto err_free_mem;
+		goto err_iounmap;
 
 	return 0;
 
+err_iounmap:
+	iounmap(dev->base);
+err_release_clk:
+	clk_disable(dev->clk);
+	clk_put(dev->clk);
 err_free_mem:
 	kfree(dev);
 err_release_region:
-	release_mem_region(mem->start, (mem->end - mem->start) + 1);
+	release_mem_region(mem->start, resource_size(mem));
 
 	return ret;
 }
@@ -747,7 +757,7 @@ static int davinci_i2s_remove(struct platform_device *pdev)
 	dev->clk = NULL;
 	kfree(dev);
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	release_mem_region(mem->start, (mem->end - mem->start) + 1);
+	release_mem_region(mem->start, resource_size(mem));
 
 	return 0;
 }

+ 20 - 9
sound/soc/davinci/davinci-mcasp.c

@@ -868,7 +868,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 	}
 
 	ioarea = request_mem_region(mem->start,
-			(mem->end - mem->start) + 1, pdev->name);
+			resource_size(mem), pdev->name);
 	if (!ioarea) {
 		dev_err(&pdev->dev, "Audio region already claimed\n");
 		ret = -EBUSY;
@@ -885,7 +885,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 	clk_enable(dev->clk);
 	dev->clk_active = 1;
 
-	dev->base = (void __iomem *)IO_ADDRESS(mem->start);
+	dev->base = ioremap(mem->start, resource_size(mem));
+	if (!dev->base) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		ret = -ENOMEM;
+		goto err_release_clk;
+	}
+
 	dev->op_mode = pdata->op_mode;
 	dev->tdm_slots = pdata->tdm_slots;
 	dev->num_serializer = pdata->num_serializer;
@@ -899,14 +905,14 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 	dma_data->asp_chan_q = pdata->asp_chan_q;
 	dma_data->ram_chan_q = pdata->ram_chan_q;
 	dma_data->dma_addr = (dma_addr_t) (pdata->tx_dma_offset +
-							io_v2p(dev->base));
+							mem->start);
 
 	/* first TX, then RX */
 	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
 	if (!res) {
 		dev_err(&pdev->dev, "no DMA resource\n");
 		ret = -ENODEV;
-		goto err_release_region;
+		goto err_iounmap;
 	}
 
 	dma_data->channel = res->start;
@@ -915,13 +921,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 	dma_data->asp_chan_q = pdata->asp_chan_q;
 	dma_data->ram_chan_q = pdata->ram_chan_q;
 	dma_data->dma_addr = (dma_addr_t)(pdata->rx_dma_offset +
-							io_v2p(dev->base));
+							mem->start);
 
 	res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
 	if (!res) {
 		dev_err(&pdev->dev, "no DMA resource\n");
 		ret = -ENODEV;
-		goto err_release_region;
+		goto err_iounmap;
 	}
 
 	dma_data->channel = res->start;
@@ -929,11 +935,16 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 	ret = snd_soc_register_dai(&pdev->dev, &davinci_mcasp_dai[pdata->op_mode]);
 
 	if (ret != 0)
-		goto err_release_region;
+		goto err_iounmap;
 	return 0;
 
+err_iounmap:
+	iounmap(dev->base);
+err_release_clk:
+	clk_disable(dev->clk);
+	clk_put(dev->clk);
 err_release_region:
-	release_mem_region(mem->start, (mem->end - mem->start) + 1);
+	release_mem_region(mem->start, resource_size(mem));
 err_release_data:
 	kfree(dev);
 
@@ -951,7 +962,7 @@ static int davinci_mcasp_remove(struct platform_device *pdev)
 	dev->clk = NULL;
 
 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	release_mem_region(mem->start, (mem->end - mem->start) + 1);
+	release_mem_region(mem->start, resource_size(mem));
 
 	kfree(dev);
 

+ 9 - 0
sound/soc/ep93xx/Kconfig

@@ -30,3 +30,12 @@ config SND_EP93XX_SOC_SIMONE
 	help
 	  Say Y or M here if you want to add support for AC97 audio on the
 	  Simplemachines Sim.One board.
+
+config SND_EP93XX_SOC_EDB93XX
+	tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
+	depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
+	select SND_EP93XX_SOC_I2S
+	select SND_SOC_CS4271
+	help
+	  Say Y or M here if you want to add support for I2S audio on the
+	  Cirrus Logic EDB93xx boards.

+ 2 - 0
sound/soc/ep93xx/Makefile

@@ -10,6 +10,8 @@ obj-$(CONFIG_SND_EP93XX_SOC_AC97)		+= snd-soc-ep93xx-ac97.o
 # EP93XX Machine Support
 snd-soc-snappercl15-objs			:= snappercl15.o
 snd-soc-simone-objs				:= simone.o
+snd-soc-edb93xx-objs				:= edb93xx.o
 
 obj-$(CONFIG_SND_EP93XX_SOC_SNAPPERCL15)	+= snd-soc-snappercl15.o
 obj-$(CONFIG_SND_EP93XX_SOC_SIMONE)		+= snd-soc-simone.o
+obj-$(CONFIG_SND_EP93XX_SOC_EDB93XX)		+= snd-soc-edb93xx.o

+ 142 - 0
sound/soc/ep93xx/edb93xx.c

@@ -0,0 +1,142 @@
+/*
+ * SoC audio for EDB93xx
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver support CS4271 codec being master or slave, working
+ * in control port mode, connected either via SPI or I2C.
+ * The data format accepted is I2S or left-justified.
+ * DAPM support not implemented.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <asm/mach-types.h>
+#include <mach/hardware.h>
+#include "ep93xx-pcm.h"
+
+#define edb93xx_has_audio() (machine_is_edb9301() ||	\
+			     machine_is_edb9302() ||	\
+			     machine_is_edb9302a() ||	\
+			     machine_is_edb9307a() ||	\
+			     machine_is_edb9315a())
+
+static int edb93xx_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int err;
+	unsigned int mclk_rate;
+	unsigned int rate = params_rate(params);
+
+	/*
+	 * According to CS4271 datasheet we use MCLK/LRCK=256 for
+	 * rates below 50kHz and 128 for higher sample rates
+	 */
+	if (rate < 50000)
+		mclk_rate = rate * 64 * 4;
+	else
+		mclk_rate = rate * 64 * 2;
+
+	err = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_IF |
+				  SND_SOC_DAIFMT_CBS_CFS);
+	if (err)
+		return err;
+
+	err = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
+				  SND_SOC_DAIFMT_NB_IF |
+				  SND_SOC_DAIFMT_CBS_CFS);
+	if (err)
+		return err;
+
+	err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate,
+				     SND_SOC_CLOCK_IN);
+	if (err)
+		return err;
+
+	return snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate,
+				      SND_SOC_CLOCK_OUT);
+}
+
+static struct snd_soc_ops edb93xx_ops = {
+	.hw_params	= edb93xx_hw_params,
+};
+
+static struct snd_soc_dai_link edb93xx_dai = {
+	.name		= "CS4271",
+	.stream_name	= "CS4271 HiFi",
+	.platform_name	= "ep93xx-pcm-audio",
+	.cpu_dai_name	= "ep93xx-i2s",
+	.codec_name	= "spi0.0",
+	.codec_dai_name	= "cs4271-hifi",
+	.ops		= &edb93xx_ops,
+};
+
+static struct snd_soc_card snd_soc_edb93xx = {
+	.name		= "EDB93XX",
+	.dai_link	= &edb93xx_dai,
+	.num_links	= 1,
+};
+
+static struct platform_device *edb93xx_snd_device;
+
+static int __init edb93xx_init(void)
+{
+	int ret;
+
+	if (!edb93xx_has_audio())
+		return -ENODEV;
+
+	ret = ep93xx_i2s_acquire(EP93XX_SYSCON_DEVCFG_I2SONAC97,
+				 EP93XX_SYSCON_I2SCLKDIV_ORIDE |
+				 EP93XX_SYSCON_I2SCLKDIV_SPOL);
+	if (ret)
+		return ret;
+
+	edb93xx_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!edb93xx_snd_device) {
+		ret = -ENOMEM;
+		goto free_i2s;
+	}
+
+	platform_set_drvdata(edb93xx_snd_device, &snd_soc_edb93xx);
+	ret = platform_device_add(edb93xx_snd_device);
+	if (ret)
+		goto device_put;
+
+	return 0;
+
+device_put:
+	platform_device_put(edb93xx_snd_device);
+free_i2s:
+	ep93xx_i2s_release();
+	return ret;
+}
+module_init(edb93xx_init);
+
+static void __exit edb93xx_exit(void)
+{
+	platform_device_unregister(edb93xx_snd_device);
+	ep93xx_i2s_release();
+}
+module_exit(edb93xx_exit);
+
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_DESCRIPTION("ALSA SoC EDB93xx");
+MODULE_LICENSE("GPL");

+ 0 - 1
sound/soc/ep93xx/ep93xx-ac97.c

@@ -253,7 +253,6 @@ static int ep93xx_ac97_trigger(struct snd_pcm_substream *substream,
 	struct ep93xx_ac97_info *info = snd_soc_dai_get_drvdata(dai);
 	unsigned v = 0;
 
-
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
 	case SNDRV_PCM_TRIGGER_RESUME:

+ 17 - 14
sound/soc/ep93xx/ep93xx-i2s.c

@@ -242,7 +242,7 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
 {
 	struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
 	unsigned word_len, div, sdiv, lrdiv;
-	int found = 0, err;
+	int err;
 
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S16_LE:
@@ -275,15 +275,14 @@ static int ep93xx_i2s_hw_params(struct snd_pcm_substream *substream,
 	 * the codec uses.
 	 */
 	div = clk_get_rate(info->mclk) / params_rate(params);
-	for (sdiv = 2; sdiv <= 4; sdiv += 2)
-		for (lrdiv = 64; lrdiv <= 128; lrdiv <<= 1)
-			if (sdiv * lrdiv == div) {
-				found = 1;
-				goto out;
-			}
-out:
-	if (!found)
-		return -EINVAL;
+	sdiv = 4;
+	if (div > (256 + 512) / 2) {
+		lrdiv = 128;
+	} else {
+		lrdiv = 64;
+		if (div < (128 + 256) / 2)
+			sdiv = 2;
+	}
 
 	err = clk_set_rate(info->sclk, clk_get_rate(info->mclk) / sdiv);
 	if (err)
@@ -314,10 +313,12 @@ static int ep93xx_i2s_suspend(struct snd_soc_dai *dai)
 	struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
 
 	if (!dai->active)
-		return;
+		return 0;
 
 	ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_PLAYBACK);
 	ep93xx_i2s_disable(info, SNDRV_PCM_STREAM_CAPTURE);
+
+	return 0;
 }
 
 static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
@@ -325,10 +326,12 @@ static int ep93xx_i2s_resume(struct snd_soc_dai *dai)
 	struct ep93xx_i2s_info *info = snd_soc_dai_get_drvdata(dai);
 
 	if (!dai->active)
-		return;
+		return 0;
 
 	ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_PLAYBACK);
 	ep93xx_i2s_enable(info, SNDRV_PCM_STREAM_CAPTURE);
+
+	return 0;
 }
 #else
 #define ep93xx_i2s_suspend	NULL
@@ -352,13 +355,13 @@ static struct snd_soc_dai_driver ep93xx_i2s_dai = {
 	.playback	= {
 		.channels_min	= 2,
 		.channels_max	= 2,
-		.rates		= SNDRV_PCM_RATE_8000_96000,
+		.rates		= SNDRV_PCM_RATE_8000_192000,
 		.formats	= EP93XX_I2S_FORMATS,
 	},
 	.capture	= {
 		 .channels_min	= 2,
 		 .channels_max	= 2,
-		 .rates		= SNDRV_PCM_RATE_8000_96000,
+		 .rates		= SNDRV_PCM_RATE_8000_192000,
 		 .formats	= EP93XX_I2S_FORMATS,
 	},
 	.ops		= &ep93xx_i2s_dai_ops,

+ 2 - 2
sound/soc/ep93xx/ep93xx-pcm.c

@@ -35,9 +35,9 @@ static const struct snd_pcm_hardware ep93xx_pcm_hardware = {
 				   SNDRV_PCM_INFO_INTERLEAVED	|
 				   SNDRV_PCM_INFO_BLOCK_TRANSFER),
 				   
-	.rates			= SNDRV_PCM_RATE_8000_96000,
+	.rates			= SNDRV_PCM_RATE_8000_192000,
 	.rate_min		= SNDRV_PCM_RATE_8000,
-	.rate_max		= SNDRV_PCM_RATE_96000,
+	.rate_max		= SNDRV_PCM_RATE_192000,
 	
 	.formats		= (SNDRV_PCM_FMTBIT_S16_LE |
 				   SNDRV_PCM_FMTBIT_S24_LE |

+ 2 - 4
sound/soc/fsl/mpc8610_hpcd.c

@@ -53,9 +53,8 @@ struct mpc8610_hpcd_data {
  *
  * Here we program the DMACR and PMUXCR registers.
  */
-static int mpc8610_hpcd_machine_probe(struct platform_device *sound_device)
+static int mpc8610_hpcd_machine_probe(struct snd_soc_card *card)
 {
-	struct snd_soc_card *card = platform_get_drvdata(sound_device);
 	struct mpc8610_hpcd_data *machine_data =
 		container_of(card, struct mpc8610_hpcd_data, card);
 	struct ccsr_guts_86xx __iomem *guts;
@@ -138,9 +137,8 @@ static int mpc8610_hpcd_startup(struct snd_pcm_substream *substream)
  * This function is called to remove the sound device for one SSI.  We
  * de-program the DMACR and PMUXCR register.
  */
-static int mpc8610_hpcd_machine_remove(struct platform_device *sound_device)
+static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card)
 {
-	struct snd_soc_card *card = platform_get_drvdata(sound_device);
 	struct mpc8610_hpcd_data *machine_data =
 		container_of(card, struct mpc8610_hpcd_data, card);
 	struct ccsr_guts_86xx __iomem *guts;

+ 2 - 4
sound/soc/fsl/p1022_ds.c

@@ -85,9 +85,8 @@ struct machine_data {
  *
  * Here we program the DMACR and PMUXCR registers.
  */
-static int p1022_ds_machine_probe(struct platform_device *sound_device)
+static int p1022_ds_machine_probe(struct snd_soc_card *card)
 {
-	struct snd_soc_card *card = platform_get_drvdata(sound_device);
 	struct machine_data *mdata =
 		container_of(card, struct machine_data, card);
 	struct ccsr_guts_85xx __iomem *guts;
@@ -160,9 +159,8 @@ static int p1022_ds_startup(struct snd_pcm_substream *substream)
  * This function is called to remove the sound device for one SSI.  We
  * de-program the DMACR and PMUXCR register.
  */
-static int p1022_ds_machine_remove(struct platform_device *sound_device)
+static int p1022_ds_machine_remove(struct snd_soc_card *card)
 {
-	struct snd_soc_card *card = platform_get_drvdata(sound_device);
 	struct machine_data *mdata =
 		container_of(card, struct machine_data, card);
 	struct ccsr_guts_85xx __iomem *guts;

+ 12 - 1
sound/soc/imx/Kconfig

@@ -30,6 +30,16 @@ config SND_MXC_SOC_WM1133_EV1
 	  Enable support for audio on the i.MX31ADS with the WM1133-EV1
 	  PMIC board with WM8835x fitted.
 
+config SND_SOC_MX27VIS_AIC32X4
+	tristate "SoC audio support for Visstrim M10 boards"
+	depends on MACH_IMX27_VISSTRIM_M10
+	select SND_SOC_TVL320AIC32X4
+	select SND_MXC_SOC_SSI
+	select SND_MXC_SOC_MX2
+	help
+	  Say Y if you want to add support for SoC audio on Visstrim SM10
+	  board with TLV320AIC32X4 codec.
+
 config SND_SOC_PHYCORE_AC97
 	tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards"
 	depends on MACH_PCM043 || MACH_PCA100
@@ -44,7 +54,8 @@ config SND_SOC_EUKREA_TLV320
 	tristate "Eukrea TLV320"
 	depends on MACH_EUKREA_MBIMX27_BASEBOARD \
 		|| MACH_EUKREA_MBIMXSD25_BASEBOARD \
-		|| MACH_EUKREA_MBIMXSD35_BASEBOARD
+		|| MACH_EUKREA_MBIMXSD35_BASEBOARD \
+		|| MACH_EUKREA_MBIMXSD51_BASEBOARD
 	select SND_SOC_TLV320AIC23
 	select SND_MXC_SOC_SSI
 	select SND_MXC_SOC_FIQ

+ 2 - 0
sound/soc/imx/Makefile

@@ -10,8 +10,10 @@ obj-$(CONFIG_SND_MXC_SOC_MX2) += snd-soc-imx-mx2.o
 # i.MX Machine Support
 snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o
 snd-soc-phycore-ac97-objs := phycore-ac97.o
+snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
 snd-soc-wm1133-ev1-objs := wm1133-ev1.o
 
 obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
 obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o
+obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
 obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o

+ 2 - 1
sound/soc/imx/eukrea-tlv320.c

@@ -98,7 +98,8 @@ static int __init eukrea_tlv320_init(void)
 	int ret;
 
 	if (!machine_is_eukrea_cpuimx27() && !machine_is_eukrea_cpuimx25sd()
-		&& !machine_is_eukrea_cpuimx35sd())
+		&& !machine_is_eukrea_cpuimx35sd()
+		&& !machine_is_eukrea_cpuimx51sd())
 		/* return happy. We might run on a totally different machine */
 		return 0;
 

+ 4 - 1
sound/soc/imx/imx-ssi.c

@@ -108,7 +108,7 @@ static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
 		break;
 	case SND_SOC_DAIFMT_DSP_B:
 		/* data on rising edge of bclk, frame high with data */
-		strcr |= SSI_STCR_TFSL;
+		strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0;
 		break;
 	case SND_SOC_DAIFMT_DSP_A:
 		/* data on rising edge of bclk, frame high 1clk before data */
@@ -656,6 +656,9 @@ static int imx_ssi_probe(struct platform_device *pdev)
 	ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0;
 	ssi->dma_params_tx.dma_addr = res->start + SSI_STX0;
 
+	ssi->dma_params_tx.burstsize = 4;
+	ssi->dma_params_rx.burstsize = 4;
+
 	res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0");
 	if (res)
 		ssi->dma_params_tx.dma = res->start;

+ 137 - 0
sound/soc/imx/mx27vis-aic32x4.c

@@ -0,0 +1,137 @@
+/*
+ * mx27vis-aic32x4.c
+ *
+ * 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 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/device.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <asm/mach-types.h>
+#include <mach/audmux.h>
+
+#include "../codecs/tlv320aic32x4.h"
+#include "imx-ssi.h"
+
+static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int ret;
+	u32 dai_format;
+
+	dai_format = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF |
+		SND_SOC_DAIFMT_CBM_CFM;
+
+	/* set codec DAI configuration */
+	snd_soc_dai_set_fmt(codec_dai, dai_format);
+
+	/* set cpu DAI configuration */
+	snd_soc_dai_set_fmt(cpu_dai, dai_format);
+
+	ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+				     25000000, SND_SOC_CLOCK_OUT);
+	if (ret) {
+		pr_err("%s: failed setting codec sysclk\n", __func__);
+		return ret;
+	}
+
+	ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0,
+				SND_SOC_CLOCK_IN);
+	if (ret) {
+		pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_ops mx27vis_aic32x4_snd_ops = {
+	.hw_params	= mx27vis_aic32x4_hw_params,
+};
+
+static struct snd_soc_dai_link mx27vis_aic32x4_dai = {
+	.name		= "tlv320aic32x4",
+	.stream_name	= "TLV320AIC32X4",
+	.codec_dai_name	= "tlv320aic32x4-hifi",
+	.platform_name	= "imx-pcm-audio.0",
+	.codec_name	= "tlv320aic32x4.0-0018",
+	.cpu_dai_name	= "imx-ssi.0",
+	.ops		= &mx27vis_aic32x4_snd_ops,
+};
+
+static struct snd_soc_card mx27vis_aic32x4 = {
+	.name		= "visstrim_m10-audio",
+	.dai_link	= &mx27vis_aic32x4_dai,
+	.num_links	= 1,
+};
+
+static struct platform_device *mx27vis_aic32x4_snd_device;
+
+static int __init mx27vis_aic32x4_init(void)
+{
+	int ret;
+
+	mx27vis_aic32x4_snd_device = platform_device_alloc("soc-audio", -1);
+	if (!mx27vis_aic32x4_snd_device)
+		return -ENOMEM;
+
+	platform_set_drvdata(mx27vis_aic32x4_snd_device, &mx27vis_aic32x4);
+	ret = platform_device_add(mx27vis_aic32x4_snd_device);
+
+	if (ret) {
+		printk(KERN_ERR "ASoC: Platform device allocation failed\n");
+		platform_device_put(mx27vis_aic32x4_snd_device);
+	}
+
+	/* Connect SSI0 as clock slave to SSI1 external pins */
+	mxc_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
+			MXC_AUDMUX_V1_PCR_SYN |
+			MXC_AUDMUX_V1_PCR_TFSDIR |
+			MXC_AUDMUX_V1_PCR_TCLKDIR |
+			MXC_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) |
+			MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1)
+	);
+	mxc_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1,
+			MXC_AUDMUX_V1_PCR_SYN |
+			MXC_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0)
+	);
+
+	return ret;
+}
+
+static void __exit mx27vis_aic32x4_exit(void)
+{
+	platform_device_unregister(mx27vis_aic32x4_snd_device);
+}
+
+module_init(mx27vis_aic32x4_init);
+module_exit(mx27vis_aic32x4_exit);
+
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com>");
+MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim");
+MODULE_LICENSE("GPL");

+ 14 - 0
sound/soc/mid-x86/Kconfig

@@ -0,0 +1,14 @@
+config SND_MFLD_MACHINE
+	tristate "SOC Machine Audio driver for Intel Medfield MID platform"
+	depends on INTEL_SCU_IPC
+	depends on SND_INTEL_SST
+	select SND_SOC_SN95031
+	select SND_SST_PLATFORM
+	help
+          This adds support for ASoC machine driver for Intel(R) MID Medfield platform
+          used as alsa device in audio substem in Intel(R) MID devices
+          Say Y if you have such a device
+          If unsure select "N".
+
+config SND_SST_PLATFORM
+	tristate

+ 5 - 0
sound/soc/mid-x86/Makefile

@@ -0,0 +1,5 @@
+snd-soc-sst-platform-objs := sst_platform.o
+snd-soc-mfld-machine-objs := mfld_machine.o
+
+obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o
+obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o

+ 452 - 0
sound/soc/mid-x86/mfld_machine.c

@@ -0,0 +1,452 @@
+/*
+ *  mfld_machine.c - ASoc Machine driver for Intel Medfield MID platform
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../codecs/sn95031.h"
+
+#define MID_MONO 1
+#define MID_STEREO 2
+#define MID_MAX_CAP 5
+#define MFLD_JACK_INSERT 0x04
+
+enum soc_mic_bias_zones {
+	MFLD_MV_START = 0,
+	/* mic bias volutage range for Headphones*/
+	MFLD_MV_HP = 400,
+	/* mic bias volutage range for American Headset*/
+	MFLD_MV_AM_HS = 650,
+	/* mic bias volutage range for Headset*/
+	MFLD_MV_HS = 2000,
+	MFLD_MV_UNDEFINED,
+};
+
+static unsigned int	hs_switch;
+static unsigned int	lo_dac;
+
+struct mfld_mc_private {
+	struct platform_device *socdev;
+	void __iomem *int_base;
+	struct snd_soc_codec *codec;
+	u8 interrupt_status;
+};
+
+struct snd_soc_jack mfld_jack;
+
+/*Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin mfld_jack_pins[] = {
+	{
+		.pin = "Headphones",
+		.mask = SND_JACK_HEADPHONE,
+	},
+	{
+		.pin = "AMIC1",
+		.mask = SND_JACK_MICROPHONE,
+	},
+};
+
+/* jack detection voltage zones */
+static struct snd_soc_jack_zone mfld_zones[] = {
+	{MFLD_MV_START, MFLD_MV_AM_HS, SND_JACK_HEADPHONE},
+	{MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET},
+};
+
+/* sound card controls */
+static const char *headset_switch_text[] = {"Earpiece", "Headset"};
+
+static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
+
+static const struct soc_enum headset_enum =
+	SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
+
+static const struct soc_enum lo_enum =
+	SOC_ENUM_SINGLE_EXT(4, lo_text);
+
+static int headset_get_switch(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = hs_switch;
+	return 0;
+}
+
+static int headset_set_switch(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+	if (ucontrol->value.integer.value[0] == hs_switch)
+		return 0;
+
+	if (ucontrol->value.integer.value[0]) {
+		pr_debug("hs_set HS path\n");
+		snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
+		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+	} else {
+		pr_debug("hs_set EP path\n");
+		snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
+		snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+	}
+	snd_soc_dapm_sync(&codec->dapm);
+	hs_switch = ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static void lo_enable_out_pins(struct snd_soc_codec *codec)
+{
+	snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");
+	snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");
+	snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");
+	snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");
+	snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");
+	snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");
+	if (hs_switch) {
+		snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
+		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+	} else {
+		snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
+		snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+	}
+}
+
+static int lo_get_switch(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = lo_dac;
+	return 0;
+}
+
+static int lo_set_switch(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+
+	if (ucontrol->value.integer.value[0] == lo_dac)
+		return 0;
+
+	/* we dont want to work with last state of lineout so just enable all
+	 * pins and then disable pins not required
+	 */
+	lo_enable_out_pins(codec);
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		pr_debug("set vibra path\n");
+		snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT");
+		snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT");
+		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0);
+		break;
+
+	case 1:
+		pr_debug("set hs  path\n");
+		snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
+		snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22);
+		break;
+
+	case 2:
+		pr_debug("set spkr path\n");
+		snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL");
+		snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR");
+		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44);
+		break;
+
+	case 3:
+		pr_debug("set null path\n");
+		snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL");
+		snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR");
+		snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66);
+		break;
+	}
+	snd_soc_dapm_sync(&codec->dapm);
+	lo_dac = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static const struct snd_kcontrol_new mfld_snd_controls[] = {
+	SOC_ENUM_EXT("Playback Switch", headset_enum,
+			headset_get_switch, headset_set_switch),
+	SOC_ENUM_EXT("Lineout Mux", lo_enum,
+			lo_get_switch, lo_set_switch),
+};
+
+static const struct snd_soc_dapm_widget mfld_widgets[] = {
+	SND_SOC_DAPM_HP("Headphones", NULL),
+	SND_SOC_DAPM_MIC("Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mfld_map[] = {
+	{"Headphones", NULL, "HPOUTR"},
+	{"Headphones", NULL, "HPOUTL"},
+	{"Mic", NULL, "AMIC1"},
+};
+
+static void mfld_jack_check(unsigned int intr_status)
+{
+	struct mfld_jack_data jack_data;
+
+	jack_data.mfld_jack = &mfld_jack;
+	jack_data.intr_id = intr_status;
+
+	sn95031_jack_detection(&jack_data);
+	/* TODO: add american headset detection post gpiolib support */
+}
+
+static int mfld_init(struct snd_soc_pcm_runtime *runtime)
+{
+	struct snd_soc_codec *codec = runtime->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	int ret_val;
+
+	/* Add jack sense widgets */
+	snd_soc_dapm_new_controls(dapm, mfld_widgets, ARRAY_SIZE(mfld_widgets));
+
+	/* Set up the map */
+	snd_soc_dapm_add_routes(dapm, mfld_map, ARRAY_SIZE(mfld_map));
+
+	/* always connected */
+	snd_soc_dapm_enable_pin(dapm, "Headphones");
+	snd_soc_dapm_enable_pin(dapm, "Mic");
+	snd_soc_dapm_sync(dapm);
+
+	ret_val = snd_soc_add_controls(codec, mfld_snd_controls,
+				ARRAY_SIZE(mfld_snd_controls));
+	if (ret_val) {
+		pr_err("soc_add_controls failed %d", ret_val);
+		return ret_val;
+	}
+	/* default is earpiece pin, userspace sets it explcitly */
+	snd_soc_dapm_disable_pin(dapm, "Headphones");
+	/* default is lineout NC, userspace sets it explcitly */
+	snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
+	snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
+	lo_dac = 3;
+	hs_switch = 0;
+	/* we dont use linein in this so set to NC */
+	snd_soc_dapm_disable_pin(dapm, "LINEINL");
+	snd_soc_dapm_disable_pin(dapm, "LINEINR");
+	snd_soc_dapm_sync(dapm);
+
+	/* Headset and button jack detection */
+	ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack",
+			SND_JACK_HEADSET | SND_JACK_BTN_0 |
+			SND_JACK_BTN_1, &mfld_jack);
+	if (ret_val) {
+		pr_err("jack creation failed\n");
+		return ret_val;
+	}
+
+	ret_val = snd_soc_jack_add_pins(&mfld_jack,
+			ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins);
+	if (ret_val) {
+		pr_err("adding jack pins failed\n");
+		return ret_val;
+	}
+	ret_val = snd_soc_jack_add_zones(&mfld_jack,
+			ARRAY_SIZE(mfld_zones), mfld_zones);
+	if (ret_val) {
+		pr_err("adding jack zones failed\n");
+		return ret_val;
+	}
+
+	/* we want to check if anything is inserted at boot,
+	 * so send a fake event to codec and it will read adc
+	 * to find if anything is there or not */
+	mfld_jack_check(MFLD_JACK_INSERT);
+	return ret_val;
+}
+
+struct snd_soc_dai_link mfld_msic_dailink[] = {
+	{
+		.name = "Medfield Headset",
+		.stream_name = "Headset",
+		.cpu_dai_name = "Headset-cpu-dai",
+		.codec_dai_name = "SN95031 Headset",
+		.codec_name = "sn95031",
+		.platform_name = "sst-platform",
+		.init = mfld_init,
+	},
+	{
+		.name = "Medfield Speaker",
+		.stream_name = "Speaker",
+		.cpu_dai_name = "Speaker-cpu-dai",
+		.codec_dai_name = "SN95031 Speaker",
+		.codec_name = "sn95031",
+		.platform_name = "sst-platform",
+		.init = NULL,
+	},
+	{
+		.name = "Medfield Vibra",
+		.stream_name = "Vibra1",
+		.cpu_dai_name = "Vibra1-cpu-dai",
+		.codec_dai_name = "SN95031 Vibra1",
+		.codec_name = "sn95031",
+		.platform_name = "sst-platform",
+		.init = NULL,
+	},
+	{
+		.name = "Medfield Haptics",
+		.stream_name = "Vibra2",
+		.cpu_dai_name = "Vibra2-cpu-dai",
+		.codec_dai_name = "SN95031 Vibra2",
+		.codec_name = "sn95031",
+		.platform_name = "sst-platform",
+		.init = NULL,
+	},
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_mfld = {
+	.name = "medfield_audio",
+	.dai_link = mfld_msic_dailink,
+	.num_links = ARRAY_SIZE(mfld_msic_dailink),
+};
+
+static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
+{
+	struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
+
+	memcpy_fromio(&mc_private->interrupt_status,
+			((void *)(mc_private->int_base)),
+			sizeof(u8));
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t snd_mfld_jack_detection(int irq, void *data)
+{
+	struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
+
+	if (mfld_jack.codec == NULL)
+		return IRQ_HANDLED;
+	mfld_jack_check(mc_drv_ctx->interrupt_status);
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit snd_mfld_mc_probe(struct platform_device *pdev)
+{
+	int ret_val = 0, irq;
+	struct mfld_mc_private *mc_drv_ctx;
+	struct resource *irq_mem;
+
+	pr_debug("snd_mfld_mc_probe called\n");
+
+	/* retrive the irq number */
+	irq = platform_get_irq(pdev, 0);
+
+	/* audio interrupt base of SRAM location where
+	 * interrupts are stored by System FW */
+	mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC);
+	if (!mc_drv_ctx) {
+		pr_err("allocation failed\n");
+		return -ENOMEM;
+	}
+
+	irq_mem = platform_get_resource_byname(
+				pdev, IORESOURCE_MEM, "IRQ_BASE");
+	if (!irq_mem) {
+		pr_err("no mem resource given\n");
+		ret_val = -ENODEV;
+		goto unalloc;
+	}
+	mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start,
+					resource_size(irq_mem));
+	if (!mc_drv_ctx->int_base) {
+		pr_err("Mapping of cache failed\n");
+		ret_val = -ENOMEM;
+		goto unalloc;
+	}
+	/* register for interrupt */
+	ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,
+			snd_mfld_jack_detection,
+			IRQF_SHARED, pdev->dev.driver->name, mc_drv_ctx);
+	if (ret_val) {
+		pr_err("cannot register IRQ\n");
+		goto unalloc;
+	}
+	/* register the soc card */
+	snd_soc_card_mfld.dev = &pdev->dev;
+	ret_val = snd_soc_register_card(&snd_soc_card_mfld);
+	if (ret_val) {
+		pr_debug("snd_soc_register_card failed %d\n", ret_val);
+		goto freeirq;
+	}
+	platform_set_drvdata(pdev, mc_drv_ctx);
+	pr_debug("successfully exited probe\n");
+	return ret_val;
+
+freeirq:
+	free_irq(irq, mc_drv_ctx);
+unalloc:
+	kfree(mc_drv_ctx);
+	return ret_val;
+}
+
+static int __devexit snd_mfld_mc_remove(struct platform_device *pdev)
+{
+	struct mfld_mc_private *mc_drv_ctx = platform_get_drvdata(pdev);
+
+	pr_debug("snd_mfld_mc_remove called\n");
+	free_irq(platform_get_irq(pdev, 0), mc_drv_ctx);
+	snd_soc_unregister_card(&snd_soc_card_mfld);
+	kfree(mc_drv_ctx);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver snd_mfld_mc_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "msic_audio",
+	},
+	.probe = snd_mfld_mc_probe,
+	.remove = __devexit_p(snd_mfld_mc_remove),
+};
+
+static int __init snd_mfld_driver_init(void)
+{
+	pr_debug("snd_mfld_driver_init called\n");
+	return platform_driver_register(&snd_mfld_mc_driver);
+}
+module_init(snd_mfld_driver_init);
+
+static void __exit snd_mfld_driver_exit(void)
+{
+	pr_debug("snd_mfld_driver_exit called\n");
+	platform_driver_unregister(&snd_mfld_mc_driver);
+}
+module_exit(snd_mfld_driver_exit);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Machine driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:msic-audio");

+ 474 - 0
sound/soc/mid-x86/sst_platform.c

@@ -0,0 +1,474 @@
+/*
+ *  sst_platform.c - Intel MID Platform driver
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../../../drivers/staging/intel_sst/intel_sst_ioctl.h"
+#include "../../../drivers/staging/intel_sst/intel_sst.h"
+#include "sst_platform.h"
+
+static struct snd_pcm_hardware sst_platform_pcm_hw = {
+	.info =	(SNDRV_PCM_INFO_INTERLEAVED |
+			SNDRV_PCM_INFO_DOUBLE |
+			SNDRV_PCM_INFO_PAUSE |
+			SNDRV_PCM_INFO_RESUME |
+			SNDRV_PCM_INFO_MMAP|
+			SNDRV_PCM_INFO_MMAP_VALID |
+			SNDRV_PCM_INFO_BLOCK_TRANSFER |
+			SNDRV_PCM_INFO_SYNC_START),
+	.formats = (SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_U16 |
+			SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_U24 |
+			SNDRV_PCM_FMTBIT_S32 | SNDRV_PCM_FMTBIT_U32),
+	.rates = (SNDRV_PCM_RATE_8000|
+			SNDRV_PCM_RATE_44100 |
+			SNDRV_PCM_RATE_48000),
+	.rate_min = SST_MIN_RATE,
+	.rate_max = SST_MAX_RATE,
+	.channels_min =	SST_MIN_CHANNEL,
+	.channels_max =	SST_MAX_CHANNEL,
+	.buffer_bytes_max = SST_MAX_BUFFER,
+	.period_bytes_min = SST_MIN_PERIOD_BYTES,
+	.period_bytes_max = SST_MAX_PERIOD_BYTES,
+	.periods_min = SST_MIN_PERIODS,
+	.periods_max = SST_MAX_PERIODS,
+	.fifo_size = SST_FIFO_SIZE,
+};
+
+/* MFLD - MSIC */
+struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+	.name = "Headset-cpu-dai",
+	.id = 0,
+	.playback = {
+		.channels_min = SST_STEREO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 5,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	},
+},
+{
+	.name = "Speaker-cpu-dai",
+	.id = 1,
+	.playback = {
+		.channels_min = SST_MONO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	},
+},
+{
+	.name = "Vibra1-cpu-dai",
+	.id = 2,
+	.playback = {
+		.channels_min = SST_MONO,
+		.channels_max = SST_MONO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	},
+},
+{
+	.name = "Vibra2-cpu-dai",
+	.id = 3,
+	.playback = {
+		.channels_min = SST_MONO,
+		.channels_max = SST_STEREO,
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S24_LE,
+	},
+},
+};
+
+/* helper functions */
+static inline void sst_set_stream_status(struct sst_runtime_stream *stream,
+					int state)
+{
+	spin_lock(&stream->status_lock);
+	stream->stream_status = state;
+	spin_unlock(&stream->status_lock);
+}
+
+static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
+{
+	int state;
+
+	spin_lock(&stream->status_lock);
+	state = stream->stream_status;
+	spin_unlock(&stream->status_lock);
+	return state;
+}
+
+static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
+				struct snd_sst_stream_params *param)
+{
+
+	param->uc.pcm_params.codec = SST_CODEC_TYPE_PCM;
+	param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;
+	param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
+	param->uc.pcm_params.reserved = 0;
+	param->uc.pcm_params.sfreq = substream->runtime->rate;
+	param->uc.pcm_params.ring_buffer_size =
+					snd_pcm_lib_buffer_bytes(substream);
+	param->uc.pcm_params.period_count = substream->runtime->period_size;
+	param->uc.pcm_params.ring_buffer_addr =
+				virt_to_phys(substream->dma_buffer.area);
+	pr_debug("period_cnt = %d\n", param->uc.pcm_params.period_count);
+	pr_debug("sfreq= %d, wd_sz = %d\n",
+		 param->uc.pcm_params.sfreq, param->uc.pcm_params.pcm_wd_sz);
+}
+
+static int sst_platform_alloc_stream(struct snd_pcm_substream *substream)
+{
+	struct sst_runtime_stream *stream =
+			substream->runtime->private_data;
+	struct snd_sst_stream_params param = {{{0,},},};
+	struct snd_sst_params str_params = {0};
+	int ret_val;
+
+	/* set codec params and inform SST driver the same */
+	sst_fill_pcm_params(substream, &param);
+	substream->runtime->dma_area = substream->dma_buffer.area;
+	str_params.sparams = param;
+	str_params.codec =  param.uc.pcm_params.codec;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		str_params.ops = STREAM_OPS_PLAYBACK;
+		str_params.device_type = substream->pcm->device + 1;
+		pr_debug("Playbck stream,Device %d\n",
+					substream->pcm->device);
+	} else {
+		str_params.ops = STREAM_OPS_CAPTURE;
+		str_params.device_type = SND_SST_DEVICE_CAPTURE;
+		pr_debug("Capture stream,Device %d\n",
+					substream->pcm->device);
+	}
+	ret_val = stream->sstdrv_ops->pcm_control->open(&str_params);
+	pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val);
+	if (ret_val < 0)
+		return ret_val;
+
+	stream->stream_info.str_id = ret_val;
+	pr_debug("str id :  %d\n", stream->stream_info.str_id);
+	return ret_val;
+}
+
+static void sst_period_elapsed(void *mad_substream)
+{
+	struct snd_pcm_substream *substream = mad_substream;
+	struct sst_runtime_stream *stream;
+	int status;
+
+	if (!substream || !substream->runtime)
+		return;
+	stream = substream->runtime->private_data;
+	if (!stream)
+		return;
+	status = sst_get_stream_status(stream);
+	if (status != SST_PLATFORM_RUNNING)
+		return;
+	snd_pcm_period_elapsed(substream);
+}
+
+static int sst_platform_init_stream(struct snd_pcm_substream *substream)
+{
+	struct sst_runtime_stream *stream =
+			substream->runtime->private_data;
+	int ret_val;
+
+	pr_debug("setting buffer ptr param\n");
+	sst_set_stream_status(stream, SST_PLATFORM_INIT);
+	stream->stream_info.period_elapsed = sst_period_elapsed;
+	stream->stream_info.mad_substream = substream;
+	stream->stream_info.buffer_ptr = 0;
+	stream->stream_info.sfreq = substream->runtime->rate;
+	ret_val = stream->sstdrv_ops->pcm_control->device_control(
+			SST_SND_STREAM_INIT, &stream->stream_info);
+	if (ret_val)
+		pr_err("control_set ret error %d\n", ret_val);
+	return ret_val;
+
+}
+/* end -- helper functions */
+
+static int sst_platform_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime;
+	struct sst_runtime_stream *stream;
+	int ret_val = 0;
+
+	pr_debug("sst_platform_open called\n");
+	runtime = substream->runtime;
+	runtime->hw = sst_platform_pcm_hw;
+	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+	if (!stream)
+		return -ENOMEM;
+	spin_lock_init(&stream->status_lock);
+	stream->stream_info.str_id = 0;
+	sst_set_stream_status(stream, SST_PLATFORM_INIT);
+	stream->stream_info.mad_substream = substream;
+	/* allocate memory for SST API set */
+	stream->sstdrv_ops = kzalloc(sizeof(*stream->sstdrv_ops),
+							GFP_KERNEL);
+	if (!stream->sstdrv_ops) {
+		pr_err("sst: mem allocation for ops fail\n");
+		kfree(stream);
+		return -ENOMEM;
+	}
+	stream->sstdrv_ops->vendor_id = MSIC_VENDOR_ID;
+	/* registering with SST driver to get access to SST APIs to use */
+	ret_val = register_sst_card(stream->sstdrv_ops);
+	if (ret_val) {
+		pr_err("sst: sst card registration failed\n");
+		return ret_val;
+	}
+	runtime->private_data = stream;
+	return snd_pcm_hw_constraint_integer(runtime,
+			 SNDRV_PCM_HW_PARAM_PERIODS);
+}
+
+static int sst_platform_close(struct snd_pcm_substream *substream)
+{
+	struct sst_runtime_stream *stream;
+	int ret_val = 0, str_id;
+
+	pr_debug("sst_platform_close called\n");
+	stream = substream->runtime->private_data;
+	str_id = stream->stream_info.str_id;
+	if (str_id)
+		ret_val = stream->sstdrv_ops->pcm_control->close(str_id);
+	kfree(stream->sstdrv_ops);
+	kfree(stream);
+	return ret_val;
+}
+
+static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct sst_runtime_stream *stream;
+	int ret_val = 0, str_id;
+
+	pr_debug("sst_platform_pcm_prepare called\n");
+	stream = substream->runtime->private_data;
+	str_id = stream->stream_info.str_id;
+	if (stream->stream_info.str_id) {
+		ret_val = stream->sstdrv_ops->pcm_control->device_control(
+					SST_SND_DROP, &str_id);
+		return ret_val;
+	}
+
+	ret_val = sst_platform_alloc_stream(substream);
+	if (ret_val < 0)
+		return ret_val;
+	snprintf(substream->pcm->id, sizeof(substream->pcm->id),
+			"%d", stream->stream_info.str_id);
+
+	ret_val = sst_platform_init_stream(substream);
+	if (ret_val)
+		return ret_val;
+	substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER;
+	return ret_val;
+}
+
+static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
+					int cmd)
+{
+	int ret_val = 0, str_id;
+	struct sst_runtime_stream *stream;
+	int str_cmd, status;
+
+	pr_debug("sst_platform_pcm_trigger called\n");
+	stream = substream->runtime->private_data;
+	str_id = stream->stream_info.str_id;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		pr_debug("sst: Trigger Start\n");
+		str_cmd = SST_SND_START;
+		status = SST_PLATFORM_RUNNING;
+		stream->stream_info.mad_substream = substream;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		pr_debug("sst: in stop\n");
+		str_cmd = SST_SND_DROP;
+		status = SST_PLATFORM_DROPPED;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		pr_debug("sst: in pause\n");
+		str_cmd = SST_SND_PAUSE;
+		status = SST_PLATFORM_PAUSED;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		pr_debug("sst: in pause release\n");
+		str_cmd = SST_SND_RESUME;
+		status = SST_PLATFORM_RUNNING;
+		break;
+	default:
+		return -EINVAL;
+	}
+	ret_val = stream->sstdrv_ops->pcm_control->device_control(str_cmd,
+								&str_id);
+	if (!ret_val)
+		sst_set_stream_status(stream, status);
+
+	return ret_val;
+}
+
+
+static snd_pcm_uframes_t sst_platform_pcm_pointer
+			(struct snd_pcm_substream *substream)
+{
+	struct sst_runtime_stream *stream;
+	int ret_val, status;
+	struct pcm_stream_info *str_info;
+
+	stream = substream->runtime->private_data;
+	status = sst_get_stream_status(stream);
+	if (status == SST_PLATFORM_INIT)
+		return 0;
+	str_info = &stream->stream_info;
+	ret_val = stream->sstdrv_ops->pcm_control->device_control(
+				SST_SND_BUFFER_POINTER, str_info);
+	if (ret_val) {
+		pr_err("sst: error code = %d\n", ret_val);
+		return ret_val;
+	}
+	return stream->stream_info.buffer_ptr;
+}
+
+static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params)
+{
+	snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+	memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
+
+	return 0;
+}
+
+static struct snd_pcm_ops sst_platform_ops = {
+	.open = sst_platform_open,
+	.close = sst_platform_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.prepare = sst_platform_pcm_prepare,
+	.trigger = sst_platform_pcm_trigger,
+	.pointer = sst_platform_pcm_pointer,
+	.hw_params = sst_platform_pcm_hw_params,
+};
+
+static void sst_pcm_free(struct snd_pcm *pcm)
+{
+	pr_debug("sst_pcm_free called\n");
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+int sst_pcm_new(struct snd_card *card, struct snd_soc_dai *dai,
+			struct snd_pcm *pcm)
+{
+	int retval = 0;
+
+	pr_debug("sst_pcm_new called\n");
+	if (dai->driver->playback.channels_min ||
+			dai->driver->capture.channels_min) {
+		retval =  snd_pcm_lib_preallocate_pages_for_all(pcm,
+			SNDRV_DMA_TYPE_CONTINUOUS,
+			snd_dma_continuous_data(GFP_KERNEL),
+			SST_MIN_BUFFER, SST_MAX_BUFFER);
+		if (retval) {
+			pr_err("dma buffer allocationf fail\n");
+			return retval;
+		}
+	}
+	return retval;
+}
+struct snd_soc_platform_driver sst_soc_platform_drv = {
+	.ops		= &sst_platform_ops,
+	.pcm_new	= sst_pcm_new,
+	.pcm_free	= sst_pcm_free,
+};
+
+static int sst_platform_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	pr_debug("sst_platform_probe called\n");
+	ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
+	if (ret) {
+		pr_err("registering soc platform failed\n");
+		return ret;
+	}
+
+	ret = snd_soc_register_dais(&pdev->dev,
+				sst_platform_dai, ARRAY_SIZE(sst_platform_dai));
+	if (ret) {
+		pr_err("registering cpu dais failed\n");
+		snd_soc_unregister_platform(&pdev->dev);
+	}
+	return ret;
+}
+
+static int sst_platform_remove(struct platform_device *pdev)
+{
+
+	snd_soc_unregister_dais(&pdev->dev, ARRAY_SIZE(sst_platform_dai));
+	snd_soc_unregister_platform(&pdev->dev);
+	pr_debug("sst_platform_remove sucess\n");
+	return 0;
+}
+
+static struct platform_driver sst_platform_driver = {
+	.driver		= {
+		.name		= "sst-platform",
+		.owner		= THIS_MODULE,
+	},
+	.probe		= sst_platform_probe,
+	.remove		= sst_platform_remove,
+};
+
+static int __init sst_soc_platform_init(void)
+{
+	pr_debug("sst_soc_platform_init called\n");
+	return  platform_driver_register(&sst_platform_driver);
+}
+module_init(sst_soc_platform_init);
+
+static void __exit sst_soc_platform_exit(void)
+{
+	platform_driver_unregister(&sst_platform_driver);
+	pr_debug("sst_soc_platform_exit sucess\n");
+}
+module_exit(sst_soc_platform_exit);
+
+MODULE_DESCRIPTION("ASoC Intel(R) MID Platform driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:sst-platform");

+ 63 - 0
sound/soc/mid-x86/sst_platform.h

@@ -0,0 +1,63 @@
+/*
+ *  sst_platform.h - Intel MID Platform driver header file
+ *
+ *  Copyright (C) 2010 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *
+ */
+
+#ifndef __SST_PLATFORMDRV_H__
+#define __SST_PLATFORMDRV_H__
+
+#define SST_MONO		1
+#define SST_STEREO		2
+#define SST_MAX_CAP		5
+
+#define SST_MIN_RATE		8000
+#define SST_MAX_RATE		48000
+#define SST_MIN_CHANNEL		1
+#define SST_MAX_CHANNEL		5
+#define SST_MAX_BUFFER		(800*1024)
+#define SST_MIN_BUFFER		(800*1024)
+#define SST_MIN_PERIOD_BYTES	32
+#define SST_MAX_PERIOD_BYTES	SST_MAX_BUFFER
+#define SST_MIN_PERIODS		2
+#define SST_MAX_PERIODS		(1024*2)
+#define SST_FIFO_SIZE		0
+#define SST_CARD_NAMES		"intel_mid_card"
+#define MSIC_VENDOR_ID		3
+
+struct sst_runtime_stream {
+	int     stream_status;
+	struct pcm_stream_info stream_info;
+	struct intel_sst_card_ops *sstdrv_ops;
+	spinlock_t	status_lock;
+};
+
+enum sst_drv_status {
+	SST_PLATFORM_INIT = 1,
+	SST_PLATFORM_STARTED,
+	SST_PLATFORM_RUNNING,
+	SST_PLATFORM_PAUSED,
+	SST_PLATFORM_DROPPED,
+};
+
+#endif

+ 1 - 0
sound/soc/omap/Kconfig

@@ -24,6 +24,7 @@ config SND_OMAP_SOC_RX51
 	select OMAP_MCBSP
 	select SND_OMAP_SOC_MCBSP
 	select SND_SOC_TLV320AIC3X
+	select SND_SOC_TPA6130A2
 	help
 	  Say Y if you want to add support for SoC audio on Nokia RX-51
 	  hardware. This is also known as Nokia N900 product.

+ 123 - 8
sound/soc/omap/rx51.c

@@ -31,6 +31,7 @@
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <plat/mcbsp.h>
+#include "../codecs/tpa6130a2.h"
 
 #include <asm/mach-types.h>
 
@@ -39,6 +40,7 @@
 
 #define RX51_TVOUT_SEL_GPIO		40
 #define RX51_JACK_DETECT_GPIO		177
+#define RX51_ECI_SW_GPIO		182
 /*
  * REVISIT: TWL4030 GPIO base in RX-51. Now statically defined to 192. This
  * gpio is reserved in arch/arm/mach-omap2/board-rx51-peripherals.c
@@ -47,7 +49,9 @@
 
 enum {
 	RX51_JACK_DISABLED,
-	RX51_JACK_TVOUT,		/* tv-out */
+	RX51_JACK_TVOUT,		/* tv-out with stereo output */
+	RX51_JACK_HP,			/* headphone: stereo output, no mic */
+	RX51_JACK_HS,			/* headset: stereo output with mic */
 };
 
 static int rx51_spk_func;
@@ -57,6 +61,19 @@ static int rx51_jack_func;
 static void rx51_ext_control(struct snd_soc_codec *codec)
 {
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	int hp = 0, hs = 0, tvout = 0;
+
+	switch (rx51_jack_func) {
+	case RX51_JACK_TVOUT:
+		tvout = 1;
+		hp = 1;
+		break;
+	case RX51_JACK_HS:
+		hs = 1;
+	case RX51_JACK_HP:
+		hp = 1;
+		break;
+	}
 
 	if (rx51_spk_func)
 		snd_soc_dapm_enable_pin(dapm, "Ext Spk");
@@ -66,9 +83,16 @@ static void rx51_ext_control(struct snd_soc_codec *codec)
 		snd_soc_dapm_enable_pin(dapm, "DMic");
 	else
 		snd_soc_dapm_disable_pin(dapm, "DMic");
+	if (hp)
+		snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
+	else
+		snd_soc_dapm_disable_pin(dapm, "Headphone Jack");
+	if (hs)
+		snd_soc_dapm_enable_pin(dapm, "HS Mic");
+	else
+		snd_soc_dapm_disable_pin(dapm, "HS Mic");
 
-	gpio_set_value(RX51_TVOUT_SEL_GPIO,
-		       rx51_jack_func == RX51_JACK_TVOUT);
+	gpio_set_value(RX51_TVOUT_SEL_GPIO, tvout);
 
 	snd_soc_dapm_sync(dapm);
 }
@@ -153,6 +177,19 @@ static int rx51_spk_event(struct snd_soc_dapm_widget *w,
 	return 0;
 }
 
+static int rx51_hp_event(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *k, int event)
+{
+	struct snd_soc_codec *codec = w->dapm->codec;
+
+	if (SND_SOC_DAPM_EVENT_ON(event))
+		tpa6130a2_stereo_enable(codec, 1);
+	else
+		tpa6130a2_stereo_enable(codec, 0);
+
+	return 0;
+}
+
 static int rx51_get_input(struct snd_kcontrol *kcontrol,
 			  struct snd_ctl_elem_value *ucontrol)
 {
@@ -203,7 +240,7 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
 	{
 		.gpio = RX51_JACK_DETECT_GPIO,
 		.name = "avdet-gpio",
-		.report = SND_JACK_VIDEOOUT,
+		.report = SND_JACK_HEADSET,
 		.invert = 1,
 		.debounce_time = 200,
 	},
@@ -212,19 +249,38 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
 static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
 	SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event),
 	SND_SOC_DAPM_MIC("DMic", NULL),
+	SND_SOC_DAPM_HP("Headphone Jack", rx51_hp_event),
+	SND_SOC_DAPM_MIC("HS Mic", NULL),
+	SND_SOC_DAPM_LINE("FM Transmitter", NULL),
+};
+
+static const struct snd_soc_dapm_widget aic34_dapm_widgetsb[] = {
+	SND_SOC_DAPM_SPK("Earphone", NULL),
 };
 
 static const struct snd_soc_dapm_route audio_map[] = {
 	{"Ext Spk", NULL, "HPLOUT"},
 	{"Ext Spk", NULL, "HPROUT"},
+	{"Headphone Jack", NULL, "LLOUT"},
+	{"Headphone Jack", NULL, "RLOUT"},
+	{"FM Transmitter", NULL, "LLOUT"},
+	{"FM Transmitter", NULL, "RLOUT"},
 
 	{"DMic Rate 64", NULL, "Mic Bias 2V"},
 	{"Mic Bias 2V", NULL, "DMic"},
 };
 
+static const struct snd_soc_dapm_route audio_mapb[] = {
+	{"b LINE2R", NULL, "MONO_LOUT"},
+	{"Earphone", NULL, "b HPLOUT"},
+
+	{"LINE1L", NULL, "b Mic Bias 2.5V"},
+	{"b Mic Bias 2.5V", NULL, "HS Mic"}
+};
+
 static const char *spk_function[] = {"Off", "On"};
 static const char *input_function[] = {"ADC", "Digital Mic"};
-static const char *jack_function[] = {"Off", "TV-OUT"};
+static const char *jack_function[] = {"Off", "TV-OUT", "Headphone", "Headset"};
 
 static const struct soc_enum rx51_enum[] = {
 	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
@@ -239,6 +295,11 @@ static const struct snd_kcontrol_new aic34_rx51_controls[] = {
 		     rx51_get_input, rx51_set_input),
 	SOC_ENUM_EXT("Jack Function", rx51_enum[2],
 		     rx51_get_jack, rx51_set_jack),
+	SOC_DAPM_PIN_SWITCH("FM Transmitter"),
+};
+
+static const struct snd_kcontrol_new aic34_rx51_controlsb[] = {
+	SOC_DAPM_PIN_SWITCH("Earphone"),
 };
 
 static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
@@ -265,11 +326,21 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
 	/* Set up RX-51 specific audio path audio_map */
 	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
 
+	err = tpa6130a2_add_controls(codec);
+	if (err < 0)
+		return err;
+	snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42);
+
+	err = omap_mcbsp_st_add_controls(codec, 1);
+	if (err < 0)
+		return err;
+
 	snd_soc_dapm_sync(dapm);
 
 	/* AV jack detection */
 	err = snd_soc_jack_new(codec, "AV Jack",
-			       SND_JACK_VIDEOOUT, &rx51_av_jack);
+			       SND_JACK_HEADSET | SND_JACK_VIDEOOUT,
+			       &rx51_av_jack);
 	if (err)
 		return err;
 	err = snd_soc_jack_add_gpios(&rx51_av_jack,
@@ -279,6 +350,24 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
 	return err;
 }
 
+static int rx51_aic34b_init(struct snd_soc_dapm_context *dapm)
+{
+	int err;
+
+	err = snd_soc_add_controls(dapm->codec, aic34_rx51_controlsb,
+				   ARRAY_SIZE(aic34_rx51_controlsb));
+	if (err < 0)
+		return err;
+
+	err = snd_soc_dapm_new_controls(dapm, aic34_dapm_widgetsb,
+					ARRAY_SIZE(aic34_dapm_widgetsb));
+	if (err < 0)
+		return 0;
+
+	return snd_soc_dapm_add_routes(dapm, audio_mapb,
+				       ARRAY_SIZE(audio_mapb));
+}
+
 /* Digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link rx51_dai[] = {
 	{
@@ -293,11 +382,30 @@ static struct snd_soc_dai_link rx51_dai[] = {
 	},
 };
 
+struct snd_soc_aux_dev rx51_aux_dev[] = {
+	{
+		.name = "TLV320AIC34b",
+		.codec_name = "tlv320aic3x-codec.2-0019",
+		.init = rx51_aic34b_init,
+	},
+};
+
+static struct snd_soc_codec_conf rx51_codec_conf[] = {
+	{
+		.dev_name = "tlv320aic3x-codec.2-0019",
+		.name_prefix = "b",
+	},
+};
+
 /* Audio card */
 static struct snd_soc_card rx51_sound_card = {
 	.name = "RX-51",
 	.dai_link = rx51_dai,
 	.num_links = ARRAY_SIZE(rx51_dai),
+	.aux_dev = rx51_aux_dev,
+	.num_aux_devs = ARRAY_SIZE(rx51_aux_dev),
+	.codec_conf = rx51_codec_conf,
+	.num_configs = ARRAY_SIZE(rx51_codec_conf),
 };
 
 static struct platform_device *rx51_snd_device;
@@ -309,10 +417,14 @@ static int __init rx51_soc_init(void)
 	if (!machine_is_nokia_rx51())
 		return -ENODEV;
 
-	err = gpio_request(RX51_TVOUT_SEL_GPIO, "tvout_sel");
+	err = gpio_request_one(RX51_TVOUT_SEL_GPIO,
+			       GPIOF_DIR_OUT | GPIOF_INIT_LOW, "tvout_sel");
 	if (err)
 		goto err_gpio_tvout_sel;
-	gpio_direction_output(RX51_TVOUT_SEL_GPIO, 0);
+	err = gpio_request_one(RX51_ECI_SW_GPIO,
+			       GPIOF_DIR_OUT | GPIOF_INIT_HIGH, "eci_sw");
+	if (err)
+		goto err_gpio_eci_sw;
 
 	rx51_snd_device = platform_device_alloc("soc-audio", -1);
 	if (!rx51_snd_device) {
@@ -330,6 +442,8 @@ static int __init rx51_soc_init(void)
 err2:
 	platform_device_put(rx51_snd_device);
 err1:
+	gpio_free(RX51_ECI_SW_GPIO);
+err_gpio_eci_sw:
 	gpio_free(RX51_TVOUT_SEL_GPIO);
 err_gpio_tvout_sel:
 
@@ -342,6 +456,7 @@ static void __exit rx51_soc_exit(void)
 				rx51_av_jack_gpios);
 
 	platform_device_unregister(rx51_snd_device);
+	gpio_free(RX51_ECI_SW_GPIO);
 	gpio_free(RX51_TVOUT_SEL_GPIO);
 }
 

+ 10 - 10
sound/soc/pxa/raumfeld.c

@@ -151,13 +151,13 @@ static struct snd_soc_ops raumfeld_cs4270_ops = {
 	.hw_params = raumfeld_cs4270_hw_params,
 };
 
-static int raumfeld_line_suspend(struct platform_device *pdev, pm_message_t state)
+static int raumfeld_line_suspend(struct snd_soc_card *card)
 {
 	raumfeld_enable_audio(false);
 	return 0;
 }
 
-static int raumfeld_line_resume(struct platform_device *pdev)
+static int raumfeld_line_resume(struct snd_soc_card *card)
 {
 	raumfeld_enable_audio(true);
 	return 0;
@@ -229,19 +229,19 @@ static struct snd_soc_dai_link raumfeld_dai[] = {
 {
 	.name		= "ak4104",
 	.stream_name	= "Playback",
-	.cpu_dai_name = "pxa-ssp-dai.1",
-	.codec_dai_name = "ak4104-hifi",
-	.platform_name = "pxa-pcm-audio",
+	.cpu_dai_name	= "pxa-ssp-dai.1",
+	.codec_dai_name	= "ak4104-hifi",
+	.platform_name	= "pxa-pcm-audio",
 	.ops		= &raumfeld_ak4104_ops,
-	.codec_name = "ak4104-codec.0",
+	.codec_name	= "ak4104-codec.0",
 },
 {
 	.name		= "CS4270",
 	.stream_name	= "CS4270",
-	.cpu_dai_name = "pxa-ssp-dai.0",
-	.platform_name = "pxa-pcm-audio",
-	.codec_dai_name = "cs4270-hifi",
-	.codec_name = "cs4270-codec.0-0048",
+	.cpu_dai_name	= "pxa-ssp-dai.0",
+	.platform_name	= "pxa-pcm-audio",
+	.codec_dai_name	= "cs4270-hifi",
+	.codec_name	= "cs4270-codec.0-0048",
 	.ops		= &raumfeld_cs4270_ops,
 },};
 

+ 2 - 2
sound/soc/pxa/tosa.c

@@ -237,7 +237,7 @@ static struct snd_soc_dai_link tosa_dai[] = {
 },
 };
 
-static int tosa_probe(struct platform_device *dev)
+static int tosa_probe(struct snd_soc_card *card)
 {
 	int ret;
 
@@ -251,7 +251,7 @@ static int tosa_probe(struct platform_device *dev)
 	return ret;
 }
 
-static int tosa_remove(struct platform_device *dev)
+static int tosa_remove(struct snd_soc_card *card)
 {
 	gpio_free(TOSA_GPIO_L_MUTE);
 	return 0;

+ 6 - 1
sound/soc/pxa/z2.c

@@ -95,6 +95,11 @@ static struct snd_soc_jack_pin hs_jack_pins[] = {
 		.pin	= "Headphone Jack",
 		.mask	= SND_JACK_HEADPHONE,
 	},
+	{
+		.pin    = "Ext Spk",
+		.mask   = SND_JACK_HEADPHONE,
+		.invert = 1
+	},
 };
 
 /* Headset jack detection gpios */
@@ -147,7 +152,7 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd)
 	snd_soc_dapm_disable_pin(dapm, "LINPUT3");
 	snd_soc_dapm_disable_pin(dapm, "RINPUT3");
 	snd_soc_dapm_disable_pin(dapm, "OUT3");
-	snd_soc_dapm_disable_pin(dapm, "MONO");
+	snd_soc_dapm_disable_pin(dapm, "MONO1");
 
 	/* Add z2 specific widgets */
 	snd_soc_dapm_new_controls(dapm, wm8750_dapm_widgets,

+ 4 - 5
sound/soc/pxa/zylonite.c

@@ -189,7 +189,7 @@ static struct snd_soc_dai_link zylonite_dai[] = {
 },
 };
 
-static int zylonite_probe(struct platform_device *pdev)
+static int zylonite_probe(struct snd_soc_card *card)
 {
 	int ret;
 
@@ -216,7 +216,7 @@ static int zylonite_probe(struct platform_device *pdev)
 	return 0;
 }
 
-static int zylonite_remove(struct platform_device *pdev)
+static int zylonite_remove(struct snd_soc_card *card)
 {
 	if (clk_pout) {
 		clk_disable(pout);
@@ -226,8 +226,7 @@ static int zylonite_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static int zylonite_suspend_post(struct platform_device *pdev,
-				 pm_message_t state)
+static int zylonite_suspend_post(struct snd_soc_card *card)
 {
 	if (clk_pout)
 		clk_disable(pout);
@@ -235,7 +234,7 @@ static int zylonite_suspend_post(struct platform_device *pdev,
 	return 0;
 }
 
-static int zylonite_resume_pre(struct platform_device *pdev)
+static int zylonite_resume_pre(struct snd_soc_card *card)
 {
 	int ret = 0;
 

+ 6 - 13
sound/soc/samsung/Kconfig

@@ -35,23 +35,16 @@ config SND_SAMSUNG_I2S
 	tristate
 
 config SND_SOC_SAMSUNG_NEO1973_WM8753
-	tristate "SoC I2S Audio support for NEO1973 - WM8753"
-	depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA01
+	tristate "Audio support for Openmoko Neo1973 Smartphones (GTA01/GTA02)"
+	depends on SND_SOC_SAMSUNG && (MACH_NEO1973_GTA01 || MACH_NEO1973_GTA02)
 	select SND_S3C24XX_I2S
 	select SND_SOC_WM8753
+	select SND_SOC_LM4857 if MACH_NEO1973_GTA01
+	select SND_SOC_DFBMCS320
 	help
-	  Say Y if you want to add support for SoC audio on smdk2440
-	  with the WM8753.
+	  Say Y here to enable audio support for the Openmoko Neo1973
+	  Smartphones.
 
-config SND_SOC_SAMSUNG_NEO1973_GTA02_WM8753
-	tristate "Audio support for the Openmoko Neo FreeRunner (GTA02)"
-	depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA02
-	select SND_S3C24XX_I2S
-	select SND_SOC_WM8753
-	help
-	  This driver provides audio support for the Openmoko Neo FreeRunner
-	  smartphone.
-	  
 config SND_SOC_SAMSUNG_JIVE_WM8750
 	tristate "SoC I2S Audio support for Jive"
 	depends on SND_SOC_SAMSUNG && MACH_JIVE

+ 0 - 2
sound/soc/samsung/Makefile

@@ -20,7 +20,6 @@ obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-i2s.o
 # S3C24XX Machine Support
 snd-soc-jive-wm8750-objs := jive_wm8750.o
 snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
-snd-soc-neo1973-gta02-wm8753-objs := neo1973_gta02_wm8753.o
 snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
 snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
 snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
@@ -38,7 +37,6 @@ snd-soc-smdk-spdif-objs := smdk_spdif.o
 
 obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
-obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_GTA02_WM8753) += snd-soc-neo1973-gta02-wm8753.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK2443_WM9710) += snd-soc-smdk2443-wm9710.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_LN2440SBC_ALC650) += snd-soc-ln2440sbc-alc650.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X) += snd-soc-s3c24xx-uda134x.o

+ 4 - 4
sound/soc/samsung/ac97.c

@@ -12,24 +12,24 @@
  * published by the Free Software Foundation.
  */
 
-#include <linux/init.h>
-#include <linux/module.h>
 #include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
 
 #include <sound/soc.h>
 
-#include <plat/regs-ac97.h>
 #include <mach/dma.h>
+#include <plat/regs-ac97.h>
 #include <plat/audio.h>
 
 #include "dma.h"
-#include "ac97.h"
 
 #define AC_CMD_ADDR(x) (x << 16)
 #define AC_CMD_DATA(x) (x & 0xffff)
 
+#define S3C_AC97_DAI_PCM 0
+#define S3C_AC97_DAI_MIC 1
+
 struct s3c_ac97_info {
 	struct clk         *ac97_clk;
 	void __iomem	   *regs;

+ 0 - 21
sound/soc/samsung/ac97.h

@@ -1,21 +0,0 @@
-/* sound/soc/samsung/ac97.h
- *
- * ALSA SoC Audio Layer - S3C AC97 Controller driver
- *	Evolved from s3c2443-ac97.h
- *
- * Copyright (c) 2010 Samsung Electronics Co. Ltd
- *	Author: Jaswinder Singh <jassi.brar@samsung.com>
- *	Credits: Graeme Gregory, Sean Choi
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#ifndef __S3C_AC97_H_
-#define __S3C_AC97_H_
-
-#define S3C_AC97_DAI_PCM 0
-#define S3C_AC97_DAI_MIC 1
-
-#endif /* __S3C_AC97_H_ */

+ 5 - 8
sound/soc/samsung/dma.c

@@ -14,17 +14,11 @@
  *  option) any later version.
  */
 
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/dma-mapping.h>
 
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <asm/dma.h>
 #include <mach/hardware.h>
@@ -32,6 +26,9 @@
 
 #include "dma.h"
 
+#define ST_RUNNING		(1<<0)
+#define ST_OPENED		(1<<1)
+
 static const struct snd_pcm_hardware dma_hardware = {
 	.info			= SNDRV_PCM_INFO_INTERLEAVED |
 				    SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -313,7 +310,7 @@ dma_pointer(struct snd_pcm_substream *substream)
 	/* we seem to be getting the odd error from the pcm library due
 	 * to out-of-bounds pointers. this is maybe due to the dma engine
 	 * not having loaded the new values for the channel before being
-	 * callled... (todo - fix )
+	 * called... (todo - fix )
 	 */
 
 	if (res >= snd_pcm_lib_buffer_bytes(substream)) {

+ 0 - 8
sound/soc/samsung/dma.h

@@ -12,9 +12,6 @@
 #ifndef _S3C_AUDIO_H
 #define _S3C_AUDIO_H
 
-#define ST_RUNNING		(1<<0)
-#define ST_OPENED		(1<<1)
-
 struct s3c_dma_params {
 	struct s3c2410_dma_client *client;	/* stream identifier */
 	int channel;				/* Channel ID */
@@ -22,9 +19,4 @@ struct s3c_dma_params {
 	int dma_size;			/* Size of the DMA transfer */
 };
 
-#define S3C24XX_DAI_I2S			0
-
-/* platform data */
-extern struct snd_ac97_bus_ops s3c24xx_ac97_ops;
-
 #endif

+ 1 - 9
sound/soc/samsung/goni_wm8994.c

@@ -11,21 +11,13 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
+
 #include <asm/mach-types.h>
 #include <mach/gpio.h>
-#include <mach/regs-clock.h>
 
-#include <linux/mfd/wm8994/core.h>
-#include <linux/mfd/wm8994/registers.h>
 #include "../codecs/wm8994.h"
-#include "dma.h"
-#include "i2s.h"
 
 #define MACHINE_NAME	0
 #define CPU_VOICE_DAI	1

+ 0 - 9
sound/soc/samsung/h1940_uda1380.c

@@ -13,25 +13,16 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
 #include <linux/gpio.h>
 
 #include <sound/soc.h>
-#include <sound/uda1380.h>
 #include <sound/jack.h>
 
 #include <plat/regs-iis.h>
-
 #include <mach/h1940-latch.h>
-
 #include <asm/mach-types.h>
 
-#include "dma.h"
 #include "s3c24xx-i2s.h"
-#include "../codecs/uda1380.h"
 
 static unsigned int rates[] = {
 	11025,

+ 1 - 2
sound/soc/samsung/i2s.c

@@ -15,9 +15,8 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
 #include <sound/soc.h>
+#include <sound/pcm_params.h>
 
 #include <plat/audio.h>
 

+ 0 - 11
sound/soc/samsung/jive_wm8750.c

@@ -11,22 +11,11 @@
  * published by the Free Software Foundation.
 */
 
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/clk.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
 #include <asm/mach-types.h>
 
-#include "dma.h"
 #include "s3c2412-i2s.h"
-
 #include "../codecs/wm8750.h"
 
 static const struct snd_soc_dapm_route audio_map[] = {

+ 0 - 32
sound/soc/samsung/lm4857.h

@@ -1,32 +0,0 @@
-/*
- * lm4857.h  --  ALSA Soc Audio Layer
- *
- * Copyright 2007 Wolfson Microelectronics PLC.
- * Author: Graeme Gregory
- *         graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- *  Revision history
- *    18th Jun 2007   Initial version.
- */
-
-#ifndef LM4857_H_
-#define LM4857_H_
-
-/* 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
-
-#endif /*LM4857_H_*/
-

+ 0 - 7
sound/soc/samsung/ln2440sbc_alc650.c

@@ -16,15 +16,8 @@
  *
  */
 
-#include <linux/module.h>
-#include <linux/device.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
 #include <sound/soc.h>
 
-#include "dma.h"
-#include "ac97.h"
-
 static struct snd_soc_card ln2440sbc;
 
 static struct snd_soc_dai_link ln2440sbc_dai[] = {

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است