소스 검색

Merge tag 'asoc-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next

ASoC: Updates for 3.6

This has been a pretty quiet release - very little activity in framework
terms, mostly just a few new drivers and updates:

- Added the ability to add and remove DAPM paths dynamically, mostly for
  reparenting on clock changes.
- New machine drivers for Marvell Brownstone, ST-Ericsson Ux500
  reference platform and ttc-dkp.
- New CPU drivers for Blackfin BF6xx SPORTs in I2S mode, Marvell MMP,
  Synopsis Designware I2S controllers, and SPEAr DMA and S/PDIF
- New CODEC drivers for Dialog DA732x, ST STA529, ST-Ericsson AB8500, TI
  Isabelle and Wolfson Microelectronics WM5102 and WM5110
Takashi Iwai 13 년 전
부모
커밋
4609ed6b1f
100개의 변경된 파일14697개의 추가작업 그리고 1010개의 파일을 삭제
  1. 2 0
      MAINTAINERS
  2. 14 0
      arch/arm/mach-ux500/board-mop500.c
  3. 6 0
      include/linux/dmaengine.h
  4. 52 0
      include/linux/mfd/abx500/ab8500-codec.h
  5. 2 0
      include/linux/mfd/abx500/ab8500.h
  6. 22 0
      include/linux/platform_data/mmp_audio.h
  7. 69 0
      include/sound/designware_i2s.h
  8. 1 0
      include/sound/dmaengine_pcm.h
  9. 11 0
      include/sound/pcm.h
  10. 12 0
      include/sound/soc-dapm.h
  11. 72 5
      include/sound/soc.h
  12. 35 0
      include/sound/spear_dma.h
  13. 29 0
      include/sound/spear_spdif.h
  14. 1 0
      sound/soc/Kconfig
  15. 1 0
      sound/soc/Makefile
  16. 15 6
      sound/soc/blackfin/Kconfig
  17. 4 0
      sound/soc/blackfin/Makefile
  18. 234 0
      sound/soc/blackfin/bf6xx-i2s.c
  19. 422 0
      sound/soc/blackfin/bf6xx-sport.c
  20. 82 0
      sound/soc/blackfin/bf6xx-sport.h
  21. 31 0
      sound/soc/codecs/Kconfig
  22. 17 2
      sound/soc/codecs/Makefile
  23. 2522 0
      sound/soc/codecs/ab8500-codec.c
  24. 590 0
      sound/soc/codecs/ab8500-codec.h
  25. 0 6
      sound/soc/codecs/ac97.c
  26. 937 0
      sound/soc/codecs/arizona.c
  27. 159 0
      sound/soc/codecs/arizona.h
  28. 4 15
      sound/soc/codecs/cs42l52.c
  29. 5 15
      sound/soc/codecs/cs42l73.c
  30. 1627 0
      sound/soc/codecs/da732x.c
  31. 133 0
      sound/soc/codecs/da732x.h
  32. 654 0
      sound/soc/codecs/da732x_reg.h
  33. 1176 0
      sound/soc/codecs/isabelle.c
  34. 143 0
      sound/soc/codecs/isabelle.h
  35. 1 2
      sound/soc/codecs/lm49453.c
  36. 3 2
      sound/soc/codecs/max98095.c
  37. 1 4
      sound/soc/codecs/ml26124.c
  38. 67 0
      sound/soc/codecs/spdif_receiver.c
  39. 442 0
      sound/soc/codecs/sta529.c
  40. 38 2
      sound/soc/codecs/tlv320aic3x.c
  41. 26 1
      sound/soc/codecs/tlv320aic3x.h
  42. 1 1
      sound/soc/codecs/twl6040.c
  43. 5 2
      sound/soc/codecs/wm1250-ev1.c
  44. 31 1
      sound/soc/codecs/wm2000.c
  45. 1 1
      sound/soc/codecs/wm5100-tables.c
  46. 1 10
      sound/soc/codecs/wm5100.c
  47. 903 0
      sound/soc/codecs/wm5102.c
  48. 21 0
      sound/soc/codecs/wm5102.h
  49. 950 0
      sound/soc/codecs/wm5110.c
  50. 21 0
      sound/soc/codecs/wm5110.h
  51. 4 18
      sound/soc/codecs/wm8350.c
  52. 1 1
      sound/soc/codecs/wm8400.c
  53. 1 1
      sound/soc/codecs/wm8580.c
  54. 1 0
      sound/soc/codecs/wm8731.c
  55. 1 1
      sound/soc/codecs/wm8741.c
  56. 1 1
      sound/soc/codecs/wm8753.c
  57. 1 1
      sound/soc/codecs/wm8776.c
  58. 1 1
      sound/soc/codecs/wm8804.c
  59. 162 154
      sound/soc/codecs/wm8903.c
  60. 109 163
      sound/soc/codecs/wm8904.c
  61. 2 0
      sound/soc/codecs/wm8960.c
  62. 2 0
      sound/soc/codecs/wm8961.c
  63. 7 1
      sound/soc/codecs/wm8962.c
  64. 1 1
      sound/soc/codecs/wm8993.c
  65. 5 41
      sound/soc/codecs/wm8994.c
  66. 215 372
      sound/soc/codecs/wm8996.c
  67. 1 1
      sound/soc/codecs/wm9081.c
  68. 1 1
      sound/soc/codecs/wm9090.c
  69. 1 1
      sound/soc/codecs/wm9712.c
  70. 1 1
      sound/soc/codecs/wm9713.c
  71. 1 1
      sound/soc/codecs/wm_hubs.c
  72. 9 0
      sound/soc/dwc/Kconfig
  73. 3 0
      sound/soc/dwc/Makefile
  74. 455 0
      sound/soc/dwc/designware_i2s.c
  75. 1 1
      sound/soc/ep93xx/ep93xx-pcm.c
  76. 1 1
      sound/soc/fsl/imx-audmux.c
  77. 1 0
      sound/soc/fsl/imx-audmux.h
  78. 33 16
      sound/soc/fsl/imx-mc13783.c
  79. 1 1
      sound/soc/fsl/imx-pcm-dma.c
  80. 1 2
      sound/soc/fsl/imx-sgtl5000.c
  81. 1 1
      sound/soc/mxs/mxs-pcm.c
  82. 1 1
      sound/soc/mxs/mxs-sgtl5000.c
  83. 42 0
      sound/soc/pxa/Kconfig
  84. 8 0
      sound/soc/pxa/Makefile
  85. 174 0
      sound/soc/pxa/brownstone.c
  86. 297 0
      sound/soc/pxa/mmp-pcm.c
  87. 480 0
      sound/soc/pxa/mmp-sspa.c
  88. 92 0
      sound/soc/pxa/mmp-sspa.h
  89. 173 0
      sound/soc/pxa/ttc-dkb.c
  90. 7 0
      sound/soc/samsung/littlemill.c
  91. 3 7
      sound/soc/samsung/s3c2412-i2s.c
  92. 3 7
      sound/soc/samsung/s3c24xx-i2s.c
  93. 23 13
      sound/soc/samsung/smdk_wm8994.c
  94. 24 24
      sound/soc/sh/fsi.c
  95. 264 64
      sound/soc/soc-core.c
  96. 143 21
      sound/soc/soc-dapm.c
  97. 30 3
      sound/soc/soc-dmaengine-pcm.c
  98. 10 5
      sound/soc/soc-io.c
  99. 5 7
      sound/soc/soc-pcm.c
  100. 297 0
      sound/soc/spear/spdif_in.c

+ 2 - 0
MAINTAINERS

@@ -6747,9 +6747,11 @@ F:	include/linux/tifm.h
 
 
 TI LM49xxx FAMILY ASoC CODEC DRIVERS
 TI LM49xxx FAMILY ASoC CODEC DRIVERS
 M:	M R Swami Reddy <mr.swami.reddy@ti.com>
 M:	M R Swami Reddy <mr.swami.reddy@ti.com>
+M:	Vishwas A Deshpande <vishwas.a.deshpande@ti.com>
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
 L:	alsa-devel@alsa-project.org (moderated for non-subscribers)
 S:	Maintained
 S:	Maintained
 F:	sound/soc/codecs/lm49453*
 F:	sound/soc/codecs/lm49453*
+F:	sound/soc/codecs/isabelle*
 
 
 TI TWL4030 SERIES SOC CODEC DRIVER
 TI TWL4030 SERIES SOC CODEC DRIVER
 M:	Peter Ujfalusi <peter.ujfalusi@ti.com>
 M:	Peter Ujfalusi <peter.ujfalusi@ti.com>

+ 14 - 0
arch/arm/mach-ux500/board-mop500.c

@@ -25,6 +25,7 @@
 #include <linux/mfd/tc3589x.h>
 #include <linux/mfd/tc3589x.h>
 #include <linux/mfd/tps6105x.h>
 #include <linux/mfd/tps6105x.h>
 #include <linux/mfd/abx500/ab8500-gpio.h>
 #include <linux/mfd/abx500/ab8500-gpio.h>
+#include <linux/mfd/abx500/ab8500-codec.h>
 #include <linux/leds-lp5521.h>
 #include <linux/leds-lp5521.h>
 #include <linux/input.h>
 #include <linux/input.h>
 #include <linux/smsc911x.h>
 #include <linux/smsc911x.h>
@@ -97,6 +98,18 @@ static struct ab8500_gpio_platform_data ab8500_gpio_pdata = {
 					0x7A, 0x00, 0x00},
 					0x7A, 0x00, 0x00},
 };
 };
 
 
+/* ab8500-codec */
+static struct ab8500_codec_platform_data ab8500_codec_pdata = {
+	.amics =  {
+		.mic1_type = AMIC_TYPE_DIFFERENTIAL,
+		.mic2_type = AMIC_TYPE_DIFFERENTIAL,
+		.mic1a_micbias = AMIC_MICBIAS_VAMIC1,
+		.mic1b_micbias = AMIC_MICBIAS_VAMIC1,
+		.mic2_micbias = AMIC_MICBIAS_VAMIC2
+	},
+	.ear_cmv = EAR_CMV_0_95V
+};
+
 static struct gpio_keys_button snowball_key_array[] = {
 static struct gpio_keys_button snowball_key_array[] = {
 	{
 	{
 		.gpio           = 32,
 		.gpio           = 32,
@@ -195,6 +208,7 @@ static struct ab8500_platform_data ab8500_platdata = {
 	.regulator	= ab8500_regulators,
 	.regulator	= ab8500_regulators,
 	.num_regulator	= ARRAY_SIZE(ab8500_regulators),
 	.num_regulator	= ARRAY_SIZE(ab8500_regulators),
 	.gpio		= &ab8500_gpio_pdata,
 	.gpio		= &ab8500_gpio_pdata,
+	.codec		= &ab8500_codec_pdata,
 };
 };
 
 
 static struct resource ab8500_resources[] = {
 static struct resource ab8500_resources[] = {

+ 6 - 0
include/linux/dmaengine.h

@@ -670,6 +670,12 @@ static inline int dmaengine_resume(struct dma_chan *chan)
 	return dmaengine_device_control(chan, DMA_RESUME, 0);
 	return dmaengine_device_control(chan, DMA_RESUME, 0);
 }
 }
 
 
+static inline enum dma_status dmaengine_tx_status(struct dma_chan *chan,
+	dma_cookie_t cookie, struct dma_tx_state *state)
+{
+	return chan->device->device_tx_status(chan, cookie, state);
+}
+
 static inline dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
 static inline dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
 {
 {
 	return desc->tx_submit(desc);
 	return desc->tx_submit(desc);

+ 52 - 0
include/linux/mfd/abx500/ab8500-codec.h

@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 AB8500_CORE_CODEC_H
+#define AB8500_CORE_CODEC_H
+
+/* Mic-types */
+enum amic_type {
+	AMIC_TYPE_SINGLE_ENDED,
+	AMIC_TYPE_DIFFERENTIAL
+};
+
+/* Mic-biases */
+enum amic_micbias {
+	AMIC_MICBIAS_VAMIC1,
+	AMIC_MICBIAS_VAMIC2
+};
+
+/* Bias-voltage */
+enum ear_cm_voltage {
+	EAR_CMV_0_95V,
+	EAR_CMV_1_10V,
+	EAR_CMV_1_27V,
+	EAR_CMV_1_58V
+};
+
+/* Analog microphone settings */
+struct amic_settings {
+	enum amic_type mic1_type;
+	enum amic_type mic2_type;
+	enum amic_micbias mic1a_micbias;
+	enum amic_micbias mic1b_micbias;
+	enum amic_micbias mic2_micbias;
+};
+
+/* Platform data structure for the audio-parts of the AB8500 */
+struct ab8500_codec_platform_data {
+	struct amic_settings amics;
+	enum ear_cm_voltage ear_cmv;
+};
+
+#endif

+ 2 - 0
include/linux/mfd/abx500/ab8500.h

@@ -266,6 +266,7 @@ struct ab8500 {
 struct regulator_reg_init;
 struct regulator_reg_init;
 struct regulator_init_data;
 struct regulator_init_data;
 struct ab8500_gpio_platform_data;
 struct ab8500_gpio_platform_data;
+struct ab8500_codec_platform_data;
 
 
 /**
 /**
  * struct ab8500_platform_data - AB8500 platform data
  * struct ab8500_platform_data - AB8500 platform data
@@ -284,6 +285,7 @@ struct ab8500_platform_data {
 	int num_regulator;
 	int num_regulator;
 	struct regulator_init_data *regulator;
 	struct regulator_init_data *regulator;
 	struct ab8500_gpio_platform_data *gpio;
 	struct ab8500_gpio_platform_data *gpio;
+	struct ab8500_codec_platform_data *codec;
 };
 };
 
 
 extern int __devinit ab8500_init(struct ab8500 *ab8500,
 extern int __devinit ab8500_init(struct ab8500 *ab8500,

+ 22 - 0
include/linux/platform_data/mmp_audio.h

@@ -0,0 +1,22 @@
+/*
+ *  MMP Platform AUDIO Management
+ *
+ *  Copyright (c) 2011 Marvell Semiconductors 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 MMP_AUDIO_H
+#define MMP_AUDIO_H
+
+struct mmp_audio_platdata {
+	u32 period_max_capture;
+	u32 buffer_max_capture;
+	u32 period_max_playback;
+	u32 buffer_max_playback;
+};
+
+#endif /* MMP_AUDIO_H */

+ 69 - 0
include/sound/designware_i2s.h

@@ -0,0 +1,69 @@
+/*
+ * Copyright (ST) 2012 Rajeev Kumar (rajeev-dlh.kumar@st.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __SOUND_DESIGNWARE_I2S_H
+#define __SOUND_DESIGNWARE_I2S_H
+
+#include <linux/dmaengine.h>
+#include <linux/types.h>
+
+/*
+ * struct i2s_clk_config_data - represent i2s clk configuration data
+ * @chan_nr: number of channel
+ * @data_width: number of bits per sample (8/16/24/32 bit)
+ * @sample_rate: sampling frequency (8Khz, 16Khz, 32Khz, 44Khz, 48Khz)
+ */
+struct i2s_clk_config_data {
+	int chan_nr;
+	u32 data_width;
+	u32 sample_rate;
+};
+
+struct i2s_platform_data {
+	#define DWC_I2S_PLAY	(1 << 0)
+	#define DWC_I2S_RECORD	(1 << 1)
+	unsigned int cap;
+	int channel;
+	u32 snd_fmts;
+	u32 snd_rates;
+
+	void *play_dma_data;
+	void *capture_dma_data;
+	bool (*filter)(struct dma_chan *chan, void *slave);
+	int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
+};
+
+struct i2s_dma_data {
+	void *data;
+	dma_addr_t addr;
+	u32 max_burst;
+	enum dma_slave_buswidth addr_width;
+	bool (*filter)(struct dma_chan *chan, void *slave);
+};
+
+/* I2S DMA registers */
+#define I2S_RXDMA		0x01C0
+#define I2S_TXDMA		0x01C8
+
+#define TWO_CHANNEL_SUPPORT	2	/* up to 2.0 */
+#define FOUR_CHANNEL_SUPPORT	4	/* up to 3.1 */
+#define SIX_CHANNEL_SUPPORT	6	/* up to 5.1 */
+#define EIGHT_CHANNEL_SUPPORT	8	/* up to 7.1 */
+
+#endif /*  __SOUND_DESIGNWARE_I2S_H */

+ 1 - 0
include/sound/dmaengine_pcm.h

@@ -39,6 +39,7 @@ int snd_hwparams_to_dma_slave_config(const struct snd_pcm_substream *substream,
 	const struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config);
 	const struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config);
 int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd);
 int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd);
 snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream);
 snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream);
+snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream);
 
 
 int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
 int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
 	dma_filter_fn filter_fn, void *filter_data);
 	dma_filter_fn filter_fn, void *filter_data);

+ 11 - 0
include/sound/pcm.h

@@ -1074,4 +1074,15 @@ static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
 
 
 const char *snd_pcm_format_name(snd_pcm_format_t format);
 const char *snd_pcm_format_name(snd_pcm_format_t format);
 
 
+/**
+ * Get a string naming the direction of a stream
+ */
+static inline const char *snd_pcm_stream_str(struct snd_pcm_substream *substream)
+{
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return "Playback";
+	else
+		return "Capture";
+}
+
 #endif /* __SOUND_PCM_H */
 #endif /* __SOUND_PCM_H */

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

@@ -229,6 +229,10 @@ struct device;
 {	.id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
 {	.id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
 	.shift = wshift, .invert = winvert, \
 	.shift = wshift, .invert = winvert, \
 	.event = wevent, .event_flags = wflags}
 	.event = wevent, .event_flags = wflags}
+#define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \
+{	.id = snd_soc_dapm_clock_supply, .name = wname, \
+	.reg = SND_SOC_NOPM, .event = dapm_clock_event, \
+	.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }
 
 
 /* generic widgets */
 /* generic widgets */
 #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
 #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
@@ -245,6 +249,7 @@ struct device;
 	.reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \
 	.reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \
 	.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }
 	.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }
 
 
+
 /* dapm kcontrol types */
 /* dapm kcontrol types */
 #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
 #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
@@ -327,6 +332,8 @@ int dapm_reg_event(struct snd_soc_dapm_widget *w,
 		   struct snd_kcontrol *kcontrol, int event);
 		   struct snd_kcontrol *kcontrol, int event);
 int dapm_regulator_event(struct snd_soc_dapm_widget *w,
 int dapm_regulator_event(struct snd_soc_dapm_widget *w,
 			 struct snd_kcontrol *kcontrol, int event);
 			 struct snd_kcontrol *kcontrol, int event);
+int dapm_clock_event(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol, int event);
 
 
 /* dapm controls */
 /* dapm controls */
 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
@@ -367,6 +374,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm);
 void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
 void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
 int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 			    const struct snd_soc_dapm_route *route, int num);
 			    const struct snd_soc_dapm_route *route, int num);
+int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
+			    const struct snd_soc_dapm_route *route, int num);
 int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
 int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
 			     const struct snd_soc_dapm_route *route, int num);
 			     const struct snd_soc_dapm_route *route, int num);
 
 
@@ -432,6 +441,7 @@ enum snd_soc_dapm_type {
 	snd_soc_dapm_post,			/* machine specific post widget - exec last */
 	snd_soc_dapm_post,			/* machine specific post widget - exec last */
 	snd_soc_dapm_supply,		/* power/clock supply */
 	snd_soc_dapm_supply,		/* power/clock supply */
 	snd_soc_dapm_regulator_supply,	/* external regulator */
 	snd_soc_dapm_regulator_supply,	/* external regulator */
+	snd_soc_dapm_clock_supply,	/* external clock */
 	snd_soc_dapm_aif_in,		/* audio interface input */
 	snd_soc_dapm_aif_in,		/* audio interface input */
 	snd_soc_dapm_aif_out,		/* audio interface output */
 	snd_soc_dapm_aif_out,		/* audio interface output */
 	snd_soc_dapm_siggen,		/* signal generator */
 	snd_soc_dapm_siggen,		/* signal generator */
@@ -537,6 +547,8 @@ struct snd_soc_dapm_widget {
 	struct list_head dirty;
 	struct list_head dirty;
 	int inputs;
 	int inputs;
 	int outputs;
 	int outputs;
+
+	struct clk *clk;
 };
 };
 
 
 struct snd_soc_dapm_update {
 struct snd_soc_dapm_update {

+ 72 - 5
include/sound/soc.h

@@ -42,11 +42,22 @@
 	((unsigned long)&(struct soc_mixer_control) \
 	((unsigned long)&(struct soc_mixer_control) \
 	{.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
 	{.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
 	.max = xmax, .platform_max = xmax, .invert = xinvert})
 	.max = xmax, .platform_max = xmax, .invert = xinvert})
+#define SOC_DOUBLE_R_RANGE_VALUE(xlreg, xrreg, xshift, xmin, xmax, xinvert) \
+	((unsigned long)&(struct soc_mixer_control) \
+	{.reg = xlreg, .rreg = xrreg, .shift = xshift, .rshift = xshift, \
+	.min = xmin, .max = xmax, .platform_max = xmax, .invert = xinvert})
 #define SOC_SINGLE(xname, reg, shift, max, invert) \
 #define SOC_SINGLE(xname, reg, shift, max, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
 	.put = snd_soc_put_volsw, \
 	.put = snd_soc_put_volsw, \
 	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
 	.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+#define SOC_SINGLE_RANGE(xname, xreg, xshift, xmin, xmax, xinvert) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+	.info = snd_soc_info_volsw_range, .get = snd_soc_get_volsw_range, \
+	.put = snd_soc_put_volsw_range, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = xreg, .shift = xshift, .min = xmin,\
+		 .max = xmax, .platform_max = xmax, .invert = xinvert} }
 #define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
 #define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
@@ -67,6 +78,16 @@
 		{.reg = xreg, .rreg = xreg, \
 		{.reg = xreg, .rreg = xreg, \
 		.shift = xshift, .rshift = xshift, \
 		.shift = xshift, .rshift = xshift, \
 		.max = xmax, .min = xmin} }
 		.max = xmax, .min = xmin} }
+#define SOC_SINGLE_RANGE_TLV(xname, xreg, xshift, xmin, xmax, xinvert, 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_range, \
+	.get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \
+	.private_value = (unsigned long)&(struct soc_mixer_control) \
+		{.reg = xreg, .shift = xshift, .min = xmin,\
+		 .max = xmax, .platform_max = xmax, .invert = xinvert} }
 #define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \
 #define SOC_DOUBLE(xname, reg, shift_left, shift_right, max, invert) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
 	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
@@ -79,6 +100,13 @@
 	.get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
 	.get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
 	.private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
 	.private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
 					    xmax, xinvert) }
 					    xmax, xinvert) }
+#define SOC_DOUBLE_R_RANGE(xname, reg_left, reg_right, xshift, xmin, \
+			   xmax, xinvert)		\
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
+	.info = snd_soc_info_volsw_range, \
+	.get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \
+	.private_value = SOC_DOUBLE_R_RANGE_VALUE(reg_left, reg_right, \
+					    xshift, xmin, xmax, xinvert) }
 #define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \
 #define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
 {	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
@@ -97,6 +125,16 @@
 	.get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
 	.get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
 	.private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
 	.private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
 					    xmax, xinvert) }
 					    xmax, xinvert) }
+#define SOC_DOUBLE_R_RANGE_TLV(xname, reg_left, reg_right, xshift, xmin, \
+			       xmax, xinvert, 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_range, \
+	.get = snd_soc_get_volsw_range, .put = snd_soc_put_volsw_range, \
+	.private_value = SOC_DOUBLE_R_RANGE_VALUE(reg_left, reg_right, \
+					    xshift, xmin, xmax, xinvert) }
 #define SOC_DOUBLE_R_SX_TLV(xname, xreg, xrreg, xshift, xmin, xmax, tlv_array) \
 #define SOC_DOUBLE_R_SX_TLV(xname, xreg, xrreg, xshift, xmin, xmax, tlv_array) \
 {       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
 {       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
 	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
@@ -460,6 +498,12 @@ int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
 	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
 int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
 	struct snd_ctl_elem_value *ucontrol);
 	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo);
+int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
+int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol);
 int snd_soc_limit_volume(struct snd_soc_codec *codec,
 int snd_soc_limit_volume(struct snd_soc_codec *codec,
 	const char *name, int max);
 	const char *name, int max);
 int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
 int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
@@ -785,13 +829,36 @@ struct snd_soc_dai_link {
 	/* config - must be set by machine driver */
 	/* config - must be set by machine driver */
 	const char *name;			/* Codec name */
 	const char *name;			/* Codec name */
 	const char *stream_name;		/* Stream name */
 	const char *stream_name;		/* Stream name */
-	const char *codec_name;		/* for multi-codec */
-	const struct device_node *codec_of_node;
-	const char *platform_name;	/* for multi-platform */
-	const struct device_node *platform_of_node;
+	/*
+	 * You MAY specify the link's CPU-side device, either by device name,
+	 * or by DT/OF node, but not both. If this information is omitted,
+	 * the CPU-side DAI is matched using .cpu_dai_name only, which hence
+	 * must be globally unique. These fields are currently typically used
+	 * only for codec to codec links, or systems using device tree.
+	 */
+	const char *cpu_name;
+	const struct device_node *cpu_of_node;
+	/*
+	 * You MAY specify the DAI name of the CPU DAI. If this information is
+	 * omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node
+	 * only, which only works well when that device exposes a single DAI.
+	 */
 	const char *cpu_dai_name;
 	const char *cpu_dai_name;
-	const struct device_node *cpu_dai_of_node;
+	/*
+	 * You MUST specify the link's codec, either by device name, or by
+	 * DT/OF node, but not both.
+	 */
+	const char *codec_name;
+	const struct device_node *codec_of_node;
+	/* You MUST specify the DAI name within the codec */
 	const char *codec_dai_name;
 	const char *codec_dai_name;
+	/*
+	 * You MAY specify the link's platform/PCM/DMA driver, either by
+	 * device name, or by DT/OF node, but not both. Some forms of link
+	 * do not need a platform.
+	 */
+	const char *platform_name;
+	const struct device_node *platform_of_node;
 	int be_id;	/* optional ID for machine driver BE identification */
 	int be_id;	/* optional ID for machine driver BE identification */
 
 
 	const struct snd_soc_pcm_stream *params;
 	const struct snd_soc_pcm_stream *params;

+ 35 - 0
include/sound/spear_dma.h

@@ -0,0 +1,35 @@
+/*
+* linux/spear_dma.h
+*
+* Copyright (ST) 2012 Rajeev Kumar (rajeev-dlh.kumar@st.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*
+*/
+
+#ifndef SPEAR_DMA_H
+#define SPEAR_DMA_H
+
+#include <linux/dmaengine.h>
+
+struct spear_dma_data {
+	void *data;
+	dma_addr_t addr;
+	u32 max_burst;
+	enum dma_slave_buswidth addr_width;
+	bool (*filter)(struct dma_chan *chan, void *slave);
+};
+
+#endif /* SPEAR_DMA_H */

+ 29 - 0
include/sound/spear_spdif.h

@@ -0,0 +1,29 @@
+/*
+ * Copyright (ST) 2012 Vipin Kumar (vipin.kumar@st.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __SOUND_SPDIF_H
+#define __SOUND_SPDIF_H
+
+struct spear_spdif_platform_data {
+	/* DMA params */
+	void *dma_params;
+	bool (*filter)(struct dma_chan *chan, void *slave);
+	void (*reset_perip)(void);
+};
+
+#endif /* SOUND_SPDIF_H */

+ 1 - 0
sound/soc/Kconfig

@@ -33,6 +33,7 @@ source "sound/soc/atmel/Kconfig"
 source "sound/soc/au1x/Kconfig"
 source "sound/soc/au1x/Kconfig"
 source "sound/soc/blackfin/Kconfig"
 source "sound/soc/blackfin/Kconfig"
 source "sound/soc/davinci/Kconfig"
 source "sound/soc/davinci/Kconfig"
+source "sound/soc/dwc/Kconfig"
 source "sound/soc/ep93xx/Kconfig"
 source "sound/soc/ep93xx/Kconfig"
 source "sound/soc/fsl/Kconfig"
 source "sound/soc/fsl/Kconfig"
 source "sound/soc/jz4740/Kconfig"
 source "sound/soc/jz4740/Kconfig"

+ 1 - 0
sound/soc/Makefile

@@ -11,6 +11,7 @@ obj-$(CONFIG_SND_SOC)	+= atmel/
 obj-$(CONFIG_SND_SOC)	+= au1x/
 obj-$(CONFIG_SND_SOC)	+= au1x/
 obj-$(CONFIG_SND_SOC)	+= blackfin/
 obj-$(CONFIG_SND_SOC)	+= blackfin/
 obj-$(CONFIG_SND_SOC)	+= davinci/
 obj-$(CONFIG_SND_SOC)	+= davinci/
+obj-$(CONFIG_SND_SOC)	+= dwc/
 obj-$(CONFIG_SND_SOC)	+= ep93xx/
 obj-$(CONFIG_SND_SOC)	+= ep93xx/
 obj-$(CONFIG_SND_SOC)	+= fsl/
 obj-$(CONFIG_SND_SOC)	+= fsl/
 obj-$(CONFIG_SND_SOC)	+= jz4740/
 obj-$(CONFIG_SND_SOC)	+= jz4740/

+ 15 - 6
sound/soc/blackfin/Kconfig

@@ -1,7 +1,8 @@
 config SND_BF5XX_I2S
 config SND_BF5XX_I2S
-	tristate "SoC I2S Audio for the ADI BF5xx chip"
+	tristate "SoC I2S Audio for the ADI Blackfin chip"
 	depends on BLACKFIN
 	depends on BLACKFIN
-	select SND_BF5XX_SOC_SPORT
+	select SND_BF5XX_SOC_SPORT if !BF60x
+	select SND_BF6XX_SOC_SPORT if BF60x
 	help
 	help
 	  Say Y or M if you want to add support for codecs attached to
 	  Say Y or M if you want to add support for codecs attached to
 	  the Blackfin SPORT (synchronous serial ports) interface in I2S
 	  the Blackfin SPORT (synchronous serial ports) interface in I2S
@@ -9,12 +10,14 @@ config SND_BF5XX_I2S
 	  You will also need to select the audio interfaces to support below.
 	  You will also need to select the audio interfaces to support below.
 
 
 config SND_BF5XX_SOC_SSM2602
 config SND_BF5XX_SOC_SSM2602
-	tristate "SoC SSM2602 Audio support for BF52x ezkit"
+	tristate "SoC SSM2602 Audio Codec Add-On Card support"
 	depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
 	depends on SND_BF5XX_I2S && (SPI_MASTER || I2C)
-	select SND_BF5XX_SOC_I2S
+	select SND_BF5XX_SOC_I2S if !BF60x
+	select SND_BF6XX_SOC_I2S if BF60x
 	select SND_SOC_SSM2602
 	select SND_SOC_SSM2602
 	help
 	help
-	  Say Y if you want to add support for SoC audio on BF527-EZKIT.
+	  Say Y if you want to add support for the Analog Devices
+	  SSM2602 Audio Codec Add-On Card.
 
 
 config SND_SOC_BFIN_EVAL_ADAU1701
 config SND_SOC_BFIN_EVAL_ADAU1701
 	tristate "Support for the EVAL-ADAU1701MINIZ board on Blackfin eval boards"
 	tristate "Support for the EVAL-ADAU1701MINIZ board on Blackfin eval boards"
@@ -162,9 +165,15 @@ config SND_BF5XX_SOC_AD1980
 config SND_BF5XX_SOC_SPORT
 config SND_BF5XX_SOC_SPORT
 	tristate
 	tristate
 
 
+config SND_BF6XX_SOC_SPORT
+	tristate
+
 config SND_BF5XX_SOC_I2S
 config SND_BF5XX_SOC_I2S
 	tristate
 	tristate
 
 
+config SND_BF6XX_SOC_I2S
+	tristate
+
 config SND_BF5XX_SOC_TDM
 config SND_BF5XX_SOC_TDM
 	tristate
 	tristate
 
 
@@ -173,7 +182,7 @@ config SND_BF5XX_SOC_AC97
 
 
 config SND_BF5XX_SPORT_NUM
 config SND_BF5XX_SPORT_NUM
 	int "Set a SPORT for Sound chip"
 	int "Set a SPORT for Sound chip"
-	depends on (SND_BF5XX_I2S || SND_BF5XX_AC97 || SND_BF5XX_TDM)
+	depends on (SND_BF5XX_SOC_SPORT || SND_BF6XX_SOC_SPORT)
 	range 0 3 if BF54x
 	range 0 3 if BF54x
 	range 0 1 if !BF54x
 	range 0 1 if !BF54x
 	default 0
 	default 0

+ 4 - 0
sound/soc/blackfin/Makefile

@@ -3,16 +3,20 @@ snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
 snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
 snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
 snd-bf5xx-tdm-objs := bf5xx-tdm-pcm.o
 snd-bf5xx-tdm-objs := bf5xx-tdm-pcm.o
 snd-soc-bf5xx-sport-objs := bf5xx-sport.o
 snd-soc-bf5xx-sport-objs := bf5xx-sport.o
+snd-soc-bf6xx-sport-objs := bf6xx-sport.o
 snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
 snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
 snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
 snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
+snd-soc-bf6xx-i2s-objs := bf6xx-i2s.o
 snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o
 snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o
 
 
 obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
 obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
 obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
 obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
 obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.o
 obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.o
 obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
 obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
+obj-$(CONFIG_SND_BF6XX_SOC_SPORT) += snd-soc-bf6xx-sport.o
 obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o
 obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o
 obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
 obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
+obj-$(CONFIG_SND_BF6XX_SOC_I2S) += snd-soc-bf6xx-i2s.o
 obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o
 obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o
 
 
 # Blackfin Machine Support
 # Blackfin Machine Support

+ 234 - 0
sound/soc/blackfin/bf6xx-i2s.c

@@ -0,0 +1,234 @@
+/*
+ * bf6xx-i2s.c - Analog Devices BF6XX i2s interface driver
+ *
+ * Copyright (c) 2012 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#include "bf6xx-sport.h"
+
+struct sport_params param;
+
+static int bfin_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+		unsigned int fmt)
+{
+	struct sport_device *sport = snd_soc_dai_get_drvdata(cpu_dai);
+	struct device *dev = &sport->pdev->dev;
+	int ret = 0;
+
+	param.spctl &= ~(SPORT_CTL_OPMODE | SPORT_CTL_CKRE | SPORT_CTL_FSR
+			| SPORT_CTL_LFS | SPORT_CTL_LAFS);
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		param.spctl |= SPORT_CTL_OPMODE | SPORT_CTL_CKRE
+			| SPORT_CTL_LFS;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		param.spctl |= SPORT_CTL_FSR;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		param.spctl |= SPORT_CTL_OPMODE | SPORT_CTL_LFS
+			| SPORT_CTL_LAFS;
+		break;
+	default:
+		dev_err(dev, "%s: Unknown DAI format type\n", __func__);
+		ret = -EINVAL;
+		break;
+	}
+
+	param.spctl &= ~(SPORT_CTL_ICLK | SPORT_CTL_IFS);
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS:
+	case SND_SOC_DAIFMT_CBM_CFS:
+	case SND_SOC_DAIFMT_CBS_CFM:
+		ret = -EINVAL;
+		break;
+	default:
+		dev_err(dev, "%s: Unknown DAI master type\n", __func__);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int bfin_i2s_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
+	struct device *dev = &sport->pdev->dev;
+	int ret = 0;
+
+	param.spctl &= ~SPORT_CTL_SLEN;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		param.spctl |= 0x70;
+		sport->wdsize = 1;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		param.spctl |= 0xf0;
+		sport->wdsize = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		param.spctl |= 0x170;
+		sport->wdsize = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		param.spctl |= 0x1f0;
+		sport->wdsize = 4;
+		break;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = sport_set_tx_params(sport, &param);
+		if (ret) {
+			dev_err(dev, "SPORT tx is busy!\n");
+			return ret;
+		}
+	} else {
+		ret = sport_set_rx_params(sport, &param);
+		if (ret) {
+			dev_err(dev, "SPORT rx is busy!\n");
+			return ret;
+		}
+	}
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int bfin_i2s_suspend(struct snd_soc_dai *dai)
+{
+	struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
+
+	if (dai->capture_active)
+		sport_rx_stop(sport);
+	if (dai->playback_active)
+		sport_tx_stop(sport);
+	return 0;
+}
+
+static int bfin_i2s_resume(struct snd_soc_dai *dai)
+{
+	struct sport_device *sport = snd_soc_dai_get_drvdata(dai);
+	struct device *dev = &sport->pdev->dev;
+	int ret;
+
+	ret = sport_set_tx_params(sport, &param);
+	if (ret) {
+		dev_err(dev, "SPORT tx is busy!\n");
+		return ret;
+	}
+	ret = sport_set_rx_params(sport, &param);
+	if (ret) {
+		dev_err(dev, "SPORT rx is busy!\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+#else
+#define bfin_i2s_suspend NULL
+#define bfin_i2s_resume NULL
+#endif
+
+#define BFIN_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
+		SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
+		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+		SNDRV_PCM_RATE_96000)
+
+#define BFIN_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+		SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops bfin_i2s_dai_ops = {
+	.hw_params	= bfin_i2s_hw_params,
+	.set_fmt	= bfin_i2s_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver bfin_i2s_dai = {
+	.suspend = bfin_i2s_suspend,
+	.resume = bfin_i2s_resume,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = BFIN_I2S_RATES,
+		.formats = BFIN_I2S_FORMATS,
+	},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = BFIN_I2S_RATES,
+		.formats = BFIN_I2S_FORMATS,
+	},
+	.ops = &bfin_i2s_dai_ops,
+};
+
+static int __devinit bfin_i2s_probe(struct platform_device *pdev)
+{
+	struct sport_device *sport;
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	sport = sport_create(pdev);
+	if (!sport)
+		return -ENODEV;
+
+	/* register with the ASoC layers */
+	ret = snd_soc_register_dai(dev, &bfin_i2s_dai);
+	if (ret) {
+		dev_err(dev, "Failed to register DAI: %d\n", ret);
+		sport_delete(sport);
+		return ret;
+	}
+	platform_set_drvdata(pdev, sport);
+
+	return 0;
+}
+
+static int __devexit bfin_i2s_remove(struct platform_device *pdev)
+{
+	struct sport_device *sport = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_dai(&pdev->dev);
+	sport_delete(sport);
+
+	return 0;
+}
+
+static struct platform_driver bfin_i2s_driver = {
+	.probe  = bfin_i2s_probe,
+	.remove = __devexit_p(bfin_i2s_remove),
+	.driver = {
+		.name = "bfin-i2s",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(bfin_i2s_driver);
+
+MODULE_DESCRIPTION("Analog Devices BF6XX i2s interface driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");

+ 422 - 0
sound/soc/blackfin/bf6xx-sport.c

@@ -0,0 +1,422 @@
+/*
+ * bf6xx_sport.c Analog Devices BF6XX SPORT driver
+ *
+ * Copyright (c) 2012 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <asm/blackfin.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+#include "bf6xx-sport.h"
+
+int sport_set_tx_params(struct sport_device *sport,
+			struct sport_params *params)
+{
+	if (sport->tx_regs->spctl & SPORT_CTL_SPENPRI)
+		return -EBUSY;
+	sport->tx_regs->spctl = params->spctl | SPORT_CTL_SPTRAN;
+	sport->tx_regs->div = params->div;
+	SSYNC();
+	return 0;
+}
+EXPORT_SYMBOL(sport_set_tx_params);
+
+int sport_set_rx_params(struct sport_device *sport,
+			struct sport_params *params)
+{
+	if (sport->rx_regs->spctl & SPORT_CTL_SPENPRI)
+		return -EBUSY;
+	sport->rx_regs->spctl = params->spctl & ~SPORT_CTL_SPTRAN;
+	sport->rx_regs->div = params->div;
+	SSYNC();
+	return 0;
+}
+EXPORT_SYMBOL(sport_set_rx_params);
+
+static int compute_wdsize(size_t wdsize)
+{
+	switch (wdsize) {
+	case 1:
+		return WDSIZE_8 | PSIZE_8;
+	case 2:
+		return WDSIZE_16 | PSIZE_16;
+	default:
+		return WDSIZE_32 | PSIZE_32;
+	}
+}
+
+void sport_tx_start(struct sport_device *sport)
+{
+	set_dma_next_desc_addr(sport->tx_dma_chan, sport->tx_desc);
+	set_dma_config(sport->tx_dma_chan, DMAFLOW_LIST | DI_EN
+			| compute_wdsize(sport->wdsize) | NDSIZE_6);
+	enable_dma(sport->tx_dma_chan);
+	sport->tx_regs->spctl |= SPORT_CTL_SPENPRI;
+	SSYNC();
+}
+EXPORT_SYMBOL(sport_tx_start);
+
+void sport_rx_start(struct sport_device *sport)
+{
+	set_dma_next_desc_addr(sport->rx_dma_chan, sport->rx_desc);
+	set_dma_config(sport->rx_dma_chan, DMAFLOW_LIST | DI_EN | WNR
+			| compute_wdsize(sport->wdsize) | NDSIZE_6);
+	enable_dma(sport->rx_dma_chan);
+	sport->rx_regs->spctl |= SPORT_CTL_SPENPRI;
+	SSYNC();
+}
+EXPORT_SYMBOL(sport_rx_start);
+
+void sport_tx_stop(struct sport_device *sport)
+{
+	sport->tx_regs->spctl &= ~SPORT_CTL_SPENPRI;
+	SSYNC();
+	disable_dma(sport->tx_dma_chan);
+}
+EXPORT_SYMBOL(sport_tx_stop);
+
+void sport_rx_stop(struct sport_device *sport)
+{
+	sport->rx_regs->spctl &= ~SPORT_CTL_SPENPRI;
+	SSYNC();
+	disable_dma(sport->rx_dma_chan);
+}
+EXPORT_SYMBOL(sport_rx_stop);
+
+void sport_set_tx_callback(struct sport_device *sport,
+		void (*tx_callback)(void *), void *tx_data)
+{
+	sport->tx_callback = tx_callback;
+	sport->tx_data = tx_data;
+}
+EXPORT_SYMBOL(sport_set_tx_callback);
+
+void sport_set_rx_callback(struct sport_device *sport,
+		void (*rx_callback)(void *), void *rx_data)
+{
+	sport->rx_callback = rx_callback;
+	sport->rx_data = rx_data;
+}
+EXPORT_SYMBOL(sport_set_rx_callback);
+
+static void setup_desc(struct dmasg *desc, void *buf, int fragcount,
+		size_t fragsize, unsigned int cfg,
+		unsigned int count, size_t wdsize)
+{
+
+	int i;
+
+	for (i = 0; i < fragcount; ++i) {
+		desc[i].next_desc_addr  = &(desc[i + 1]);
+		desc[i].start_addr = (unsigned long)buf + i*fragsize;
+		desc[i].cfg = cfg;
+		desc[i].x_count = count;
+		desc[i].x_modify = wdsize;
+		desc[i].y_count = 0;
+		desc[i].y_modify = 0;
+	}
+
+	/* make circular */
+	desc[fragcount-1].next_desc_addr = desc;
+}
+
+int sport_config_tx_dma(struct sport_device *sport, void *buf,
+		int fragcount, size_t fragsize)
+{
+	unsigned int count;
+	unsigned int cfg;
+	dma_addr_t addr;
+
+	count = fragsize/sport->wdsize;
+
+	if (sport->tx_desc)
+		dma_free_coherent(NULL, sport->tx_desc_size,
+				sport->tx_desc, 0);
+
+	sport->tx_desc = dma_alloc_coherent(NULL,
+			fragcount * sizeof(struct dmasg), &addr, 0);
+	sport->tx_desc_size = fragcount * sizeof(struct dmasg);
+	if (!sport->tx_desc)
+		return -ENOMEM;
+
+	sport->tx_buf = buf;
+	sport->tx_fragsize = fragsize;
+	sport->tx_frags = fragcount;
+	cfg = DMAFLOW_LIST | DI_EN | compute_wdsize(sport->wdsize) | NDSIZE_6;
+
+	setup_desc(sport->tx_desc, buf, fragcount, fragsize,
+			cfg|DMAEN, count, sport->wdsize);
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_config_tx_dma);
+
+int sport_config_rx_dma(struct sport_device *sport, void *buf,
+		int fragcount, size_t fragsize)
+{
+	unsigned int count;
+	unsigned int cfg;
+	dma_addr_t addr;
+
+	count = fragsize/sport->wdsize;
+
+	if (sport->rx_desc)
+		dma_free_coherent(NULL, sport->rx_desc_size,
+				sport->rx_desc, 0);
+
+	sport->rx_desc = dma_alloc_coherent(NULL,
+			fragcount * sizeof(struct dmasg), &addr, 0);
+	sport->rx_desc_size = fragcount * sizeof(struct dmasg);
+	if (!sport->rx_desc)
+		return -ENOMEM;
+
+	sport->rx_buf = buf;
+	sport->rx_fragsize = fragsize;
+	sport->rx_frags = fragcount;
+	cfg = DMAFLOW_LIST | DI_EN | compute_wdsize(sport->wdsize)
+		| WNR | NDSIZE_6;
+
+	setup_desc(sport->rx_desc, buf, fragcount, fragsize,
+			cfg|DMAEN, count, sport->wdsize);
+
+	return 0;
+}
+EXPORT_SYMBOL(sport_config_rx_dma);
+
+unsigned long sport_curr_offset_tx(struct sport_device *sport)
+{
+	unsigned long curr = get_dma_curr_addr(sport->tx_dma_chan);
+
+	return (unsigned char *)curr - sport->tx_buf;
+}
+EXPORT_SYMBOL(sport_curr_offset_tx);
+
+unsigned long sport_curr_offset_rx(struct sport_device *sport)
+{
+	unsigned long curr = get_dma_curr_addr(sport->rx_dma_chan);
+
+	return (unsigned char *)curr - sport->rx_buf;
+}
+EXPORT_SYMBOL(sport_curr_offset_rx);
+
+static irqreturn_t sport_tx_irq(int irq, void *dev_id)
+{
+	struct sport_device *sport = dev_id;
+	static unsigned long status;
+
+	status = get_dma_curr_irqstat(sport->tx_dma_chan);
+	if (status & (DMA_DONE|DMA_ERR)) {
+		clear_dma_irqstat(sport->tx_dma_chan);
+		SSYNC();
+	}
+	if (sport->tx_callback)
+		sport->tx_callback(sport->tx_data);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t sport_rx_irq(int irq, void *dev_id)
+{
+	struct sport_device *sport = dev_id;
+	unsigned long status;
+
+	status = get_dma_curr_irqstat(sport->rx_dma_chan);
+	if (status & (DMA_DONE|DMA_ERR)) {
+		clear_dma_irqstat(sport->rx_dma_chan);
+		SSYNC();
+	}
+	if (sport->rx_callback)
+		sport->rx_callback(sport->rx_data);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t sport_err_irq(int irq, void *dev_id)
+{
+	struct sport_device *sport = dev_id;
+	struct device *dev = &sport->pdev->dev;
+
+	if (sport->tx_regs->spctl & SPORT_CTL_DERRPRI)
+		dev_err(dev, "sport error: TUVF\n");
+	if (sport->rx_regs->spctl & SPORT_CTL_DERRPRI)
+		dev_err(dev, "sport error: ROVF\n");
+
+	return IRQ_HANDLED;
+}
+
+static int sport_get_resource(struct sport_device *sport)
+{
+	struct platform_device *pdev = sport->pdev;
+	struct device *dev = &pdev->dev;
+	struct bfin_snd_platform_data *pdata = dev->platform_data;
+	struct resource *res;
+
+	if (!pdata) {
+		dev_err(dev, "No platform data\n");
+		return -ENODEV;
+	}
+	sport->pin_req = pdata->pin_req;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "No tx MEM resource\n");
+		return -ENODEV;
+	}
+	sport->tx_regs = (struct sport_register *)res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(dev, "No rx MEM resource\n");
+		return -ENODEV;
+	}
+	sport->rx_regs = (struct sport_register *)res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+	if (!res) {
+		dev_err(dev, "No tx DMA resource\n");
+		return -ENODEV;
+	}
+	sport->tx_dma_chan = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+	if (!res) {
+		dev_err(dev, "No rx DMA resource\n");
+		return -ENODEV;
+	}
+	sport->rx_dma_chan = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_err(dev, "No tx error irq resource\n");
+		return -ENODEV;
+	}
+	sport->tx_err_irq = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+	if (!res) {
+		dev_err(dev, "No rx error irq resource\n");
+		return -ENODEV;
+	}
+	sport->rx_err_irq = res->start;
+
+	return 0;
+}
+
+static int sport_request_resource(struct sport_device *sport)
+{
+	struct device *dev = &sport->pdev->dev;
+	int ret;
+
+	ret = peripheral_request_list(sport->pin_req, "soc-audio");
+	if (ret) {
+		dev_err(dev, "Unable to request sport pin\n");
+		return ret;
+	}
+
+	ret = request_dma(sport->tx_dma_chan, "SPORT TX Data");
+	if (ret) {
+		dev_err(dev, "Unable to allocate DMA channel for sport tx\n");
+		goto err_tx_dma;
+	}
+	set_dma_callback(sport->tx_dma_chan, sport_tx_irq, sport);
+
+	ret = request_dma(sport->rx_dma_chan, "SPORT RX Data");
+	if (ret) {
+		dev_err(dev, "Unable to allocate DMA channel for sport rx\n");
+		goto err_rx_dma;
+	}
+	set_dma_callback(sport->rx_dma_chan, sport_rx_irq, sport);
+
+	ret = request_irq(sport->tx_err_irq, sport_err_irq,
+			0, "SPORT TX ERROR", sport);
+	if (ret) {
+		dev_err(dev, "Unable to allocate tx error IRQ for sport\n");
+		goto err_tx_irq;
+	}
+
+	ret = request_irq(sport->rx_err_irq, sport_err_irq,
+			0, "SPORT RX ERROR", sport);
+	if (ret) {
+		dev_err(dev, "Unable to allocate rx error IRQ for sport\n");
+		goto err_rx_irq;
+	}
+
+	return 0;
+err_rx_irq:
+	free_irq(sport->tx_err_irq, sport);
+err_tx_irq:
+	free_dma(sport->rx_dma_chan);
+err_rx_dma:
+	free_dma(sport->tx_dma_chan);
+err_tx_dma:
+	peripheral_free_list(sport->pin_req);
+	return ret;
+}
+
+static void sport_free_resource(struct sport_device *sport)
+{
+	free_irq(sport->rx_err_irq, sport);
+	free_irq(sport->tx_err_irq, sport);
+	free_dma(sport->rx_dma_chan);
+	free_dma(sport->tx_dma_chan);
+	peripheral_free_list(sport->pin_req);
+}
+
+struct sport_device *sport_create(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct sport_device *sport;
+	int ret;
+
+	sport = kzalloc(sizeof(*sport), GFP_KERNEL);
+	if (!sport) {
+		dev_err(dev, "Unable to allocate memory for sport device\n");
+		return NULL;
+	}
+	sport->pdev = pdev;
+
+	ret = sport_get_resource(sport);
+	if (ret) {
+		kfree(sport);
+		return NULL;
+	}
+
+	ret = sport_request_resource(sport);
+	if (ret) {
+		kfree(sport);
+		return NULL;
+	}
+
+	dev_dbg(dev, "SPORT create success\n");
+	return sport;
+}
+EXPORT_SYMBOL(sport_create);
+
+void sport_delete(struct sport_device *sport)
+{
+	sport_free_resource(sport);
+}
+EXPORT_SYMBOL(sport_delete);
+
+MODULE_DESCRIPTION("Analog Devices BF6XX SPORT driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");

+ 82 - 0
sound/soc/blackfin/bf6xx-sport.h

@@ -0,0 +1,82 @@
+/*
+ * bf6xx_sport - Analog Devices BF6XX SPORT driver
+ *
+ * Copyright (c) 2012 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _BF6XX_SPORT_H_
+#define _BF6XX_SPORT_H_
+
+#include <linux/platform_device.h>
+#include <asm/bfin_sport3.h>
+
+struct sport_device {
+	struct platform_device *pdev;
+	const unsigned short *pin_req;
+	struct sport_register *tx_regs;
+	struct sport_register *rx_regs;
+	int tx_dma_chan;
+	int rx_dma_chan;
+	int tx_err_irq;
+	int rx_err_irq;
+
+	void (*tx_callback)(void *data);
+	void *tx_data;
+	void (*rx_callback)(void *data);
+	void *rx_data;
+
+	struct dmasg *tx_desc;
+	struct dmasg *rx_desc;
+	unsigned int tx_desc_size;
+	unsigned int rx_desc_size;
+	unsigned char *tx_buf;
+	unsigned char *rx_buf;
+	unsigned int tx_fragsize;
+	unsigned int rx_fragsize;
+	unsigned int tx_frags;
+	unsigned int rx_frags;
+	unsigned int wdsize;
+};
+
+struct sport_params {
+	u32 spctl;
+	u32 div;
+};
+
+struct sport_device *sport_create(struct platform_device *pdev);
+void sport_delete(struct sport_device *sport);
+int sport_set_tx_params(struct sport_device *sport,
+		struct sport_params *params);
+int sport_set_rx_params(struct sport_device *sport,
+		struct sport_params *params);
+void sport_tx_start(struct sport_device *sport);
+void sport_rx_start(struct sport_device *sport);
+void sport_tx_stop(struct sport_device *sport);
+void sport_rx_stop(struct sport_device *sport);
+void sport_set_tx_callback(struct sport_device *sport,
+	void (*tx_callback)(void *), void *tx_data);
+void sport_set_rx_callback(struct sport_device *sport,
+	void (*rx_callback)(void *), void *rx_data);
+int sport_config_tx_dma(struct sport_device *sport, void *buf,
+	int fragcount, size_t fragsize);
+int sport_config_rx_dma(struct sport_device *sport, void *buf,
+	int fragcount, size_t fragsize);
+unsigned long sport_curr_offset_tx(struct sport_device *sport);
+unsigned long sport_curr_offset_rx(struct sport_device *sport);
+
+
+
+#endif

+ 31 - 0
sound/soc/codecs/Kconfig

@@ -12,6 +12,7 @@ config SND_SOC_ALL_CODECS
 	tristate "Build all ASoC CODEC drivers"
 	tristate "Build all ASoC CODEC drivers"
 	select SND_SOC_88PM860X if MFD_88PM860X
 	select SND_SOC_88PM860X if MFD_88PM860X
 	select SND_SOC_L3
 	select SND_SOC_L3
+	select SND_SOC_AB8500_CODEC if ABX500_CORE
 	select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
 	select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
 	select SND_SOC_AD1836 if SPI_MASTER
 	select SND_SOC_AD1836 if SPI_MASTER
 	select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
 	select SND_SOC_AD193X if SND_SOC_I2C_AND_SPI
@@ -35,7 +36,9 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_CX20442
 	select SND_SOC_CX20442
 	select SND_SOC_DA7210 if I2C
 	select SND_SOC_DA7210 if I2C
+	select SND_SOC_DA732X if I2C
 	select SND_SOC_DFBMCS320
 	select SND_SOC_DFBMCS320
+	select SND_SOC_ISABELLE if I2C
 	select SND_SOC_JZ4740_CODEC
 	select SND_SOC_JZ4740_CODEC
 	select SND_SOC_LM4857 if I2C
 	select SND_SOC_LM4857 if I2C
 	select SND_SOC_LM49453 if I2C
 	select SND_SOC_LM49453 if I2C
@@ -54,6 +57,7 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_SPDIF
 	select SND_SOC_SPDIF
 	select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_SSM2602 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_STA32X if I2C
 	select SND_SOC_STA32X if I2C
+	select SND_SOC_STA529 if I2C
 	select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
 	select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
 	select SND_SOC_TLV320AIC23 if I2C
 	select SND_SOC_TLV320AIC23 if I2C
 	select SND_SOC_TLV320AIC26 if SPI_MASTER
 	select SND_SOC_TLV320AIC26 if SPI_MASTER
@@ -70,6 +74,8 @@ config SND_SOC_ALL_CODECS
 	select SND_SOC_WM2000 if I2C
 	select SND_SOC_WM2000 if I2C
 	select SND_SOC_WM2200 if I2C
 	select SND_SOC_WM2200 if I2C
 	select SND_SOC_WM5100 if I2C
 	select SND_SOC_WM5100 if I2C
+	select SND_SOC_WM5102 if MFD_WM5102
+	select SND_SOC_WM5110 if MFD_WM5110
 	select SND_SOC_WM8350 if MFD_WM8350
 	select SND_SOC_WM8350 if MFD_WM8350
 	select SND_SOC_WM8400 if MFD_WM8400
 	select SND_SOC_WM8400 if MFD_WM8400
 	select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
 	select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
@@ -126,11 +132,21 @@ config SND_SOC_ALL_CODECS
 config SND_SOC_88PM860X
 config SND_SOC_88PM860X
 	tristate
 	tristate
 
 
+config SND_SOC_ARIZONA
+	tristate
+	default y if SND_SOC_WM5102=y
+	default y if SND_SOC_WM5110=y
+	default m if SND_SOC_WM5102=m
+	default m if SND_SOC_WM5110=m
+
 config SND_SOC_WM_HUBS
 config SND_SOC_WM_HUBS
 	tristate
 	tristate
 	default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y
 	default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y
 	default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m
 	default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m
 
 
+config SND_SOC_AB8500_CODEC
+	tristate
+
 config SND_SOC_AC97_CODEC
 config SND_SOC_AC97_CODEC
 	tristate
 	tristate
 	select SND_AC97_CODEC
 	select SND_AC97_CODEC
@@ -219,12 +235,18 @@ config SND_SOC_L3
 config SND_SOC_DA7210
 config SND_SOC_DA7210
         tristate
         tristate
 
 
+config SND_SOC_DA732X
+        tristate
+
 config SND_SOC_DFBMCS320
 config SND_SOC_DFBMCS320
 	tristate
 	tristate
 
 
 config SND_SOC_DMIC
 config SND_SOC_DMIC
 	tristate
 	tristate
 
 
+config SND_SOC_ISABELLE
+        tristate
+
 config SND_SOC_LM49453
 config SND_SOC_LM49453
 	tristate
 	tristate
 
 
@@ -266,6 +288,9 @@ config SND_SOC_SSM2602
 config SND_SOC_STA32X
 config SND_SOC_STA32X
 	tristate
 	tristate
 
 
+config SND_SOC_STA529
+	tristate
+
 config SND_SOC_STAC9766
 config SND_SOC_STAC9766
 	tristate
 	tristate
 
 
@@ -313,6 +338,12 @@ config SND_SOC_WM2200
 config SND_SOC_WM5100
 config SND_SOC_WM5100
 	tristate
 	tristate
 
 
+config SND_SOC_WM5102
+	tristate
+
+config SND_SOC_WM5110
+	tristate
+
 config SND_SOC_WM8350
 config SND_SOC_WM8350
 	tristate
 	tristate
 
 

+ 17 - 2
sound/soc/codecs/Makefile

@@ -1,4 +1,5 @@
 snd-soc-88pm860x-objs := 88pm860x-codec.o
 snd-soc-88pm860x-objs := 88pm860x-codec.o
+snd-soc-ab8500-codec-objs := ab8500-codec.o
 snd-soc-ac97-objs := ac97.o
 snd-soc-ac97-objs := ac97.o
 snd-soc-ad1836-objs := ad1836.o
 snd-soc-ad1836-objs := ad1836.o
 snd-soc-ad193x-objs := ad193x.o
 snd-soc-ad193x-objs := ad193x.o
@@ -13,6 +14,7 @@ snd-soc-ak4535-objs := ak4535.o
 snd-soc-ak4641-objs := ak4641.o
 snd-soc-ak4641-objs := ak4641.o
 snd-soc-ak4642-objs := ak4642.o
 snd-soc-ak4642-objs := ak4642.o
 snd-soc-ak4671-objs := ak4671.o
 snd-soc-ak4671-objs := ak4671.o
+snd-soc-arizona-objs := arizona.o
 snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cq93vc-objs := cq93vc.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs42l51-objs := cs42l51.o
 snd-soc-cs42l52-objs := cs42l52.o
 snd-soc-cs42l52-objs := cs42l52.o
@@ -21,8 +23,10 @@ snd-soc-cs4270-objs := cs4270.o
 snd-soc-cs4271-objs := cs4271.o
 snd-soc-cs4271-objs := cs4271.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-da7210-objs := da7210.o
+snd-soc-da732x-objs := da732x.o
 snd-soc-dfbmcs320-objs := dfbmcs320.o
 snd-soc-dfbmcs320-objs := dfbmcs320.o
 snd-soc-dmic-objs := dmic.o
 snd-soc-dmic-objs := dmic.o
+snd-soc-isabelle-objs := isabelle.o
 snd-soc-jz4740-codec-objs := jz4740.o
 snd-soc-jz4740-codec-objs := jz4740.o
 snd-soc-l3-objs := l3.o
 snd-soc-l3-objs := l3.o
 snd-soc-lm4857-objs := lm4857.o
 snd-soc-lm4857-objs := lm4857.o
@@ -41,9 +45,11 @@ snd-soc-alc5623-objs := alc5623.o
 snd-soc-alc5632-objs := alc5632.o
 snd-soc-alc5632-objs := alc5632.o
 snd-soc-sigmadsp-objs := sigmadsp.o
 snd-soc-sigmadsp-objs := sigmadsp.o
 snd-soc-sn95031-objs := sn95031.o
 snd-soc-sn95031-objs := sn95031.o
-snd-soc-spdif-objs := spdif_transciever.o
+snd-soc-spdif-tx-objs := spdif_transciever.o
+snd-soc-spdif-rx-objs := spdif_receiver.o
 snd-soc-ssm2602-objs := ssm2602.o
 snd-soc-ssm2602-objs := ssm2602.o
 snd-soc-sta32x-objs := sta32x.o
 snd-soc-sta32x-objs := sta32x.o
+snd-soc-sta529-objs := sta529.o
 snd-soc-stac9766-objs := stac9766.o
 snd-soc-stac9766-objs := stac9766.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
 snd-soc-tlv320aic26-objs := tlv320aic26.o
@@ -59,6 +65,8 @@ snd-soc-wm1250-ev1-objs := wm1250-ev1.o
 snd-soc-wm2000-objs := wm2000.o
 snd-soc-wm2000-objs := wm2000.o
 snd-soc-wm2200-objs := wm2200.o
 snd-soc-wm2200-objs := wm2200.o
 snd-soc-wm5100-objs := wm5100.o wm5100-tables.o
 snd-soc-wm5100-objs := wm5100.o wm5100-tables.o
+snd-soc-wm5102-objs := wm5102.o
+snd-soc-wm5110-objs := wm5110.o
 snd-soc-wm8350-objs := wm8350.o
 snd-soc-wm8350-objs := wm8350.o
 snd-soc-wm8400-objs := wm8400.o
 snd-soc-wm8400-objs := wm8400.o
 snd-soc-wm8510-objs := wm8510.o
 snd-soc-wm8510-objs := wm8510.o
@@ -108,6 +116,7 @@ snd-soc-max9877-objs := max9877.o
 snd-soc-tpa6130a2-objs := tpa6130a2.o
 snd-soc-tpa6130a2-objs := tpa6130a2.o
 
 
 obj-$(CONFIG_SND_SOC_88PM860X)	+= snd-soc-88pm860x.o
 obj-$(CONFIG_SND_SOC_88PM860X)	+= snd-soc-88pm860x.o
+obj-$(CONFIG_SND_SOC_AB8500_CODEC)	+= snd-soc-ab8500-codec.o
 obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o
 obj-$(CONFIG_SND_SOC_AC97_CODEC)	+= snd-soc-ac97.o
 obj-$(CONFIG_SND_SOC_AD1836)	+= snd-soc-ad1836.o
 obj-$(CONFIG_SND_SOC_AD1836)	+= snd-soc-ad1836.o
 obj-$(CONFIG_SND_SOC_AD193X)	+= snd-soc-ad193x.o
 obj-$(CONFIG_SND_SOC_AD193X)	+= snd-soc-ad193x.o
@@ -124,6 +133,7 @@ obj-$(CONFIG_SND_SOC_AK4642)	+= snd-soc-ak4642.o
 obj-$(CONFIG_SND_SOC_AK4671)	+= snd-soc-ak4671.o
 obj-$(CONFIG_SND_SOC_AK4671)	+= snd-soc-ak4671.o
 obj-$(CONFIG_SND_SOC_ALC5623)    += snd-soc-alc5623.o
 obj-$(CONFIG_SND_SOC_ALC5623)    += snd-soc-alc5623.o
 obj-$(CONFIG_SND_SOC_ALC5632)	+= snd-soc-alc5632.o
 obj-$(CONFIG_SND_SOC_ALC5632)	+= snd-soc-alc5632.o
+obj-$(CONFIG_SND_SOC_ARIZONA)	+= snd-soc-arizona.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
 obj-$(CONFIG_SND_SOC_CS42L51)	+= snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS42L51)	+= snd-soc-cs42l51.o
 obj-$(CONFIG_SND_SOC_CS42L52)	+= snd-soc-cs42l52.o
 obj-$(CONFIG_SND_SOC_CS42L52)	+= snd-soc-cs42l52.o
@@ -132,8 +142,10 @@ obj-$(CONFIG_SND_SOC_CS4270)	+= snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CS4271)	+= snd-soc-cs4271.o
 obj-$(CONFIG_SND_SOC_CS4271)	+= snd-soc-cs4271.o
 obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_CX20442)	+= snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_DA7210)	+= snd-soc-da7210.o
+obj-$(CONFIG_SND_SOC_DA732X)	+= snd-soc-da732x.o
 obj-$(CONFIG_SND_SOC_DFBMCS320)	+= snd-soc-dfbmcs320.o
 obj-$(CONFIG_SND_SOC_DFBMCS320)	+= snd-soc-dfbmcs320.o
 obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
 obj-$(CONFIG_SND_SOC_DMIC)	+= snd-soc-dmic.o
+obj-$(CONFIG_SND_SOC_ISABELLE)	+= snd-soc-isabelle.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_JZ4740_CODEC)	+= snd-soc-jz4740-codec.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_L3)	+= snd-soc-l3.o
 obj-$(CONFIG_SND_SOC_LM4857)	+= snd-soc-lm4857.o
 obj-$(CONFIG_SND_SOC_LM4857)	+= snd-soc-lm4857.o
@@ -150,9 +162,10 @@ obj-$(CONFIG_SND_SOC_RT5631)	+= snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
 obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
 obj-$(CONFIG_SND_SOC_SIGMADSP)	+= snd-soc-sigmadsp.o
 obj-$(CONFIG_SND_SOC_SIGMADSP)	+= snd-soc-sigmadsp.o
 obj-$(CONFIG_SND_SOC_SN95031)	+=snd-soc-sn95031.o
 obj-$(CONFIG_SND_SOC_SN95031)	+=snd-soc-sn95031.o
-obj-$(CONFIG_SND_SOC_SPDIF)	+= snd-soc-spdif.o
+obj-$(CONFIG_SND_SOC_SPDIF)	+= snd-soc-spdif-rx.o snd-soc-spdif-tx.o
 obj-$(CONFIG_SND_SOC_SSM2602)	+= snd-soc-ssm2602.o
 obj-$(CONFIG_SND_SOC_SSM2602)	+= snd-soc-ssm2602.o
 obj-$(CONFIG_SND_SOC_STA32X)   += snd-soc-sta32x.o
 obj-$(CONFIG_SND_SOC_STA32X)   += snd-soc-sta32x.o
+obj-$(CONFIG_SND_SOC_STA529)   += snd-soc-sta529.o
 obj-$(CONFIG_SND_SOC_STAC9766)	+= snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_STAC9766)	+= snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)	+= snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)	+= snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)	+= snd-soc-tlv320aic26.o
 obj-$(CONFIG_SND_SOC_TLV320AIC26)	+= snd-soc-tlv320aic26.o
@@ -168,6 +181,8 @@ obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
 obj-$(CONFIG_SND_SOC_WM2000)	+= snd-soc-wm2000.o
 obj-$(CONFIG_SND_SOC_WM2000)	+= snd-soc-wm2000.o
 obj-$(CONFIG_SND_SOC_WM2200)	+= snd-soc-wm2200.o
 obj-$(CONFIG_SND_SOC_WM2200)	+= snd-soc-wm2200.o
 obj-$(CONFIG_SND_SOC_WM5100)	+= snd-soc-wm5100.o
 obj-$(CONFIG_SND_SOC_WM5100)	+= snd-soc-wm5100.o
+obj-$(CONFIG_SND_SOC_WM5102)	+= snd-soc-wm5102.o
+obj-$(CONFIG_SND_SOC_WM5110)	+= snd-soc-wm5110.o
 obj-$(CONFIG_SND_SOC_WM8350)	+= snd-soc-wm8350.o
 obj-$(CONFIG_SND_SOC_WM8350)	+= snd-soc-wm8350.o
 obj-$(CONFIG_SND_SOC_WM8400)	+= snd-soc-wm8400.o
 obj-$(CONFIG_SND_SOC_WM8400)	+= snd-soc-wm8400.o
 obj-$(CONFIG_SND_SOC_WM8510)	+= snd-soc-wm8510.o
 obj-$(CONFIG_SND_SOC_WM8510)	+= snd-soc-wm8510.o

+ 2522 - 0
sound/soc/codecs/ab8500-codec.c

@@ -0,0 +1,2522 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ *         Kristoffer Karlsson <kristoffer.karlsson@stericsson.com>,
+ *         Roger Nilsson <roger.xr.nilsson@stericsson.com>,
+ *         for ST-Ericsson.
+ *
+ *         Based on the early work done by:
+ *         Mikko J. Lehto <mikko.lehto@symbio.com>,
+ *         Mikko Sarmanne <mikko.sarmanne@symbio.com>,
+ *         Jarmo K. Kuronen <jarmo.kuronen@symbio.com>,
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/mfd/abx500.h>
+#include <linux/mfd/abx500/ab8500-sysctrl.h>
+#include <linux/mfd/abx500/ab8500-codec.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "ab8500-codec.h"
+
+/* Macrocell value definitions */
+#define CLK_32K_OUT2_DISABLE			0x01
+#define INACTIVE_RESET_AUDIO			0x02
+#define ENABLE_AUDIO_CLK_TO_AUDIO_BLK		0x10
+#define ENABLE_VINTCORE12_SUPPLY		0x04
+#define GPIO27_DIR_OUTPUT			0x04
+#define GPIO29_DIR_OUTPUT			0x10
+#define GPIO31_DIR_OUTPUT			0x40
+
+/* Macrocell register definitions */
+#define AB8500_CTRL3_REG			0x0200
+#define AB8500_GPIO_DIR4_REG			0x1013
+
+/* Nr of FIR/IIR-coeff banks in ANC-block */
+#define AB8500_NR_OF_ANC_COEFF_BANKS		2
+
+/* Minimum duration to keep ANC IIR Init bit high or
+low before proceeding with the configuration sequence */
+#define AB8500_ANC_SM_DELAY			2000
+
+#define AB8500_FILTER_CONTROL(xname, xcount, xmin, xmax) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.info = filter_control_info, \
+	.get = filter_control_get, .put = filter_control_put, \
+	.private_value = (unsigned long)&(struct filter_control) \
+		{.count = xcount, .min = xmin, .max = xmax} }
+
+struct filter_control {
+	long min, max;
+	unsigned int count;
+	long value[128];
+};
+
+/* Sidetone states */
+static const char * const enum_sid_state[] = {
+	"Unconfigured",
+	"Apply FIR",
+	"FIR is configured",
+};
+enum sid_state {
+	SID_UNCONFIGURED = 0,
+	SID_APPLY_FIR = 1,
+	SID_FIR_CONFIGURED = 2,
+};
+
+static const char * const enum_anc_state[] = {
+	"Unconfigured",
+	"Apply FIR and IIR",
+	"FIR and IIR are configured",
+	"Apply FIR",
+	"FIR is configured",
+	"Apply IIR",
+	"IIR is configured"
+};
+enum anc_state {
+	ANC_UNCONFIGURED = 0,
+	ANC_APPLY_FIR_IIR = 1,
+	ANC_FIR_IIR_CONFIGURED = 2,
+	ANC_APPLY_FIR = 3,
+	ANC_FIR_CONFIGURED = 4,
+	ANC_APPLY_IIR = 5,
+	ANC_IIR_CONFIGURED = 6
+};
+
+/* Analog microphones */
+enum amic_idx {
+	AMIC_IDX_1A,
+	AMIC_IDX_1B,
+	AMIC_IDX_2
+};
+
+struct ab8500_codec_drvdata_dbg {
+	struct regulator *vaud;
+	struct regulator *vamic1;
+	struct regulator *vamic2;
+	struct regulator *vdmic;
+};
+
+/* Private data for AB8500 device-driver */
+struct ab8500_codec_drvdata {
+	/* Sidetone */
+	long *sid_fir_values;
+	enum sid_state sid_status;
+
+	/* ANC */
+	struct mutex anc_lock;
+	long *anc_fir_values;
+	long *anc_iir_values;
+	enum anc_state anc_status;
+};
+
+static inline const char *amic_micbias_str(enum amic_micbias micbias)
+{
+	switch (micbias) {
+	case AMIC_MICBIAS_VAMIC1:
+		return "VAMIC1";
+	case AMIC_MICBIAS_VAMIC2:
+		return "VAMIC2";
+	default:
+		return "Unknown";
+	}
+}
+
+static inline const char *amic_type_str(enum amic_type type)
+{
+	switch (type) {
+	case AMIC_TYPE_DIFFERENTIAL:
+		return "DIFFERENTIAL";
+	case AMIC_TYPE_SINGLE_ENDED:
+		return "SINGLE ENDED";
+	default:
+		return "Unknown";
+	}
+}
+
+/*
+ * Read'n'write functions
+ */
+
+/* Read a register from the audio-bank of AB8500 */
+static unsigned int ab8500_codec_read_reg(struct snd_soc_codec *codec,
+					unsigned int reg)
+{
+	int status;
+	unsigned int value = 0;
+
+	u8 value8;
+	status = abx500_get_register_interruptible(codec->dev, AB8500_AUDIO,
+						reg, &value8);
+	if (status < 0) {
+		dev_err(codec->dev,
+			"%s: ERROR: Register (0x%02x:0x%02x) read failed (%d).\n",
+			__func__, (u8)AB8500_AUDIO, (u8)reg, status);
+	} else {
+		dev_dbg(codec->dev,
+			"%s: Read 0x%02x from register 0x%02x:0x%02x\n",
+			__func__, value8, (u8)AB8500_AUDIO, (u8)reg);
+		value = (unsigned int)value8;
+	}
+
+	return value;
+}
+
+/* Write to a register in the audio-bank of AB8500 */
+static int ab8500_codec_write_reg(struct snd_soc_codec *codec,
+				unsigned int reg, unsigned int value)
+{
+	int status;
+
+	status = abx500_set_register_interruptible(codec->dev, AB8500_AUDIO,
+						reg, value);
+	if (status < 0)
+		dev_err(codec->dev,
+			"%s: ERROR: Register (%02x:%02x) write failed (%d).\n",
+			__func__, (u8)AB8500_AUDIO, (u8)reg, status);
+	else
+		dev_dbg(codec->dev,
+			"%s: Wrote 0x%02x into register %02x:%02x\n",
+			__func__, (u8)value, (u8)AB8500_AUDIO, (u8)reg);
+
+	return status;
+}
+
+/*
+ * Controls - DAPM
+ */
+
+/* Earpiece */
+
+/* Earpiece source selector */
+static const char * const enum_ear_lineout_source[] = {"Headset Left",
+						"Speaker Left"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ear_lineout_source, AB8500_DMICFILTCONF,
+			AB8500_DMICFILTCONF_DA3TOEAR, enum_ear_lineout_source);
+static const struct snd_kcontrol_new dapm_ear_lineout_source =
+	SOC_DAPM_ENUM("Earpiece or LineOut Mono Source",
+		dapm_enum_ear_lineout_source);
+
+/* LineOut */
+
+/* LineOut source selector */
+static const char * const enum_lineout_source[] = {"Mono Path", "Stereo Path"};
+static SOC_ENUM_DOUBLE_DECL(dapm_enum_lineout_source, AB8500_ANACONF5,
+			AB8500_ANACONF5_HSLDACTOLOL,
+			AB8500_ANACONF5_HSRDACTOLOR, enum_lineout_source);
+static const struct snd_kcontrol_new dapm_lineout_source[] = {
+	SOC_DAPM_ENUM("LineOut Source", dapm_enum_lineout_source),
+};
+
+/* Handsfree */
+
+/* Speaker Left - ANC selector */
+static const char * const enum_HFx_sel[] = {"Audio Path", "ANC"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_HFl_sel, AB8500_DIGMULTCONF2,
+			AB8500_DIGMULTCONF2_HFLSEL, enum_HFx_sel);
+static const struct snd_kcontrol_new dapm_HFl_select[] = {
+	SOC_DAPM_ENUM("Speaker Left Source", dapm_enum_HFl_sel),
+};
+
+/* Speaker Right - ANC selector */
+static SOC_ENUM_SINGLE_DECL(dapm_enum_HFr_sel, AB8500_DIGMULTCONF2,
+			AB8500_DIGMULTCONF2_HFRSEL, enum_HFx_sel);
+static const struct snd_kcontrol_new dapm_HFr_select[] = {
+	SOC_DAPM_ENUM("Speaker Right Source", dapm_enum_HFr_sel),
+};
+
+/* Mic 1 */
+
+/* Mic 1 - Mic 1a or 1b selector */
+static const char * const enum_mic1ab_sel[] = {"Mic 1b", "Mic 1a"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_mic1ab_sel, AB8500_ANACONF3,
+			AB8500_ANACONF3_MIC1SEL, enum_mic1ab_sel);
+static const struct snd_kcontrol_new dapm_mic1ab_mux[] = {
+	SOC_DAPM_ENUM("Mic 1a or 1b Select", dapm_enum_mic1ab_sel),
+};
+
+/* Mic 1 - AD3 - Mic 1 or DMic 3 selector */
+static const char * const enum_ad3_sel[] = {"Mic 1", "DMic 3"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad3_sel, AB8500_DIGMULTCONF1,
+			AB8500_DIGMULTCONF1_AD3SEL, enum_ad3_sel);
+static const struct snd_kcontrol_new dapm_ad3_select[] = {
+	SOC_DAPM_ENUM("AD3 Source Select", dapm_enum_ad3_sel),
+};
+
+/* Mic 1 - AD6 - Mic 1 or DMic 6 selector */
+static const char * const enum_ad6_sel[] = {"Mic 1", "DMic 6"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad6_sel, AB8500_DIGMULTCONF1,
+			AB8500_DIGMULTCONF1_AD6SEL, enum_ad6_sel);
+static const struct snd_kcontrol_new dapm_ad6_select[] = {
+	SOC_DAPM_ENUM("AD6 Source Select", dapm_enum_ad6_sel),
+};
+
+/* Mic 2 */
+
+/* Mic 2 - AD5 - Mic 2 or DMic 5 selector */
+static const char * const enum_ad5_sel[] = {"Mic 2", "DMic 5"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad5_sel, AB8500_DIGMULTCONF1,
+			AB8500_DIGMULTCONF1_AD5SEL, enum_ad5_sel);
+static const struct snd_kcontrol_new dapm_ad5_select[] = {
+	SOC_DAPM_ENUM("AD5 Source Select", dapm_enum_ad5_sel),
+};
+
+/* LineIn */
+
+/* LineIn left - AD1 - LineIn Left or DMic 1 selector */
+static const char * const enum_ad1_sel[] = {"LineIn Left", "DMic 1"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad1_sel, AB8500_DIGMULTCONF1,
+			AB8500_DIGMULTCONF1_AD1SEL, enum_ad1_sel);
+static const struct snd_kcontrol_new dapm_ad1_select[] = {
+	SOC_DAPM_ENUM("AD1 Source Select", dapm_enum_ad1_sel),
+};
+
+/* LineIn right - Mic 2 or LineIn Right selector */
+static const char * const enum_mic2lr_sel[] = {"Mic 2", "LineIn Right"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_mic2lr_sel, AB8500_ANACONF3,
+			AB8500_ANACONF3_LINRSEL, enum_mic2lr_sel);
+static const struct snd_kcontrol_new dapm_mic2lr_select[] = {
+	SOC_DAPM_ENUM("Mic 2 or LINR Select", dapm_enum_mic2lr_sel),
+};
+
+/* LineIn right - AD2 - LineIn Right or DMic2 selector */
+static const char * const enum_ad2_sel[] = {"LineIn Right", "DMic 2"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_ad2_sel, AB8500_DIGMULTCONF1,
+			AB8500_DIGMULTCONF1_AD2SEL, enum_ad2_sel);
+static const struct snd_kcontrol_new dapm_ad2_select[] = {
+	SOC_DAPM_ENUM("AD2 Source Select", dapm_enum_ad2_sel),
+};
+
+
+/* ANC */
+
+static const char * const enum_anc_in_sel[] = {"Mic 1 / DMic 6",
+					"Mic 2 / DMic 5"};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_anc_in_sel, AB8500_DMICFILTCONF,
+			AB8500_DMICFILTCONF_ANCINSEL, enum_anc_in_sel);
+static const struct snd_kcontrol_new dapm_anc_in_select[] = {
+	SOC_DAPM_ENUM("ANC Source", dapm_enum_anc_in_sel),
+};
+
+/* ANC - Enable/Disable */
+static const struct snd_kcontrol_new dapm_anc_enable[] = {
+	SOC_DAPM_SINGLE("Switch", AB8500_ANCCONF1,
+			AB8500_ANCCONF1_ENANC, 0, 0),
+};
+
+/* ANC to Earpiece - Mute */
+static const struct snd_kcontrol_new dapm_anc_ear_mute[] = {
+	SOC_DAPM_SINGLE("Switch", AB8500_DIGMULTCONF1,
+			AB8500_DIGMULTCONF1_ANCSEL, 1, 0),
+};
+
+
+
+/* Sidetone left */
+
+/* Sidetone left - Input selector */
+static const char * const enum_stfir1_in_sel[] = {
+	"LineIn Left", "LineIn Right", "Mic 1", "Headset Left"
+};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_stfir1_in_sel, AB8500_DIGMULTCONF2,
+			AB8500_DIGMULTCONF2_FIRSID1SEL, enum_stfir1_in_sel);
+static const struct snd_kcontrol_new dapm_stfir1_in_select[] = {
+	SOC_DAPM_ENUM("Sidetone Left Source", dapm_enum_stfir1_in_sel),
+};
+
+/* Sidetone right path */
+
+/* Sidetone right - Input selector */
+static const char * const enum_stfir2_in_sel[] = {
+	"LineIn Right", "Mic 1", "DMic 4", "Headset Right"
+};
+static SOC_ENUM_SINGLE_DECL(dapm_enum_stfir2_in_sel, AB8500_DIGMULTCONF2,
+			AB8500_DIGMULTCONF2_FIRSID2SEL, enum_stfir2_in_sel);
+static const struct snd_kcontrol_new dapm_stfir2_in_select[] = {
+	SOC_DAPM_ENUM("Sidetone Right Source", dapm_enum_stfir2_in_sel),
+};
+
+/* Vibra */
+
+static const char * const enum_pwm2vibx[] = {"Audio Path", "PWM Generator"};
+
+static SOC_ENUM_SINGLE_DECL(dapm_enum_pwm2vib1, AB8500_PWMGENCONF1,
+			AB8500_PWMGENCONF1_PWMTOVIB1, enum_pwm2vibx);
+
+static const struct snd_kcontrol_new dapm_pwm2vib1[] = {
+	SOC_DAPM_ENUM("Vibra 1 Controller", dapm_enum_pwm2vib1),
+};
+
+static SOC_ENUM_SINGLE_DECL(dapm_enum_pwm2vib2, AB8500_PWMGENCONF1,
+			AB8500_PWMGENCONF1_PWMTOVIB2, enum_pwm2vibx);
+
+static const struct snd_kcontrol_new dapm_pwm2vib2[] = {
+	SOC_DAPM_ENUM("Vibra 2 Controller", dapm_enum_pwm2vib2),
+};
+
+/*
+ * DAPM-widgets
+ */
+
+static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
+
+	/* Clocks */
+	SND_SOC_DAPM_CLOCK_SUPPLY("audioclk"),
+
+	/* Regulators */
+	SND_SOC_DAPM_REGULATOR_SUPPLY("V-AUD", 0),
+	SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC1", 0),
+	SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC2", 0),
+	SND_SOC_DAPM_REGULATOR_SUPPLY("V-DMIC", 0),
+
+	/* Power */
+	SND_SOC_DAPM_SUPPLY("Audio Power",
+			AB8500_POWERUP, AB8500_POWERUP_POWERUP, 0,
+			NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("Audio Analog Power",
+			AB8500_POWERUP, AB8500_POWERUP_ENANA, 0,
+			NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* Main supply node */
+	SND_SOC_DAPM_SUPPLY("Main Supply", SND_SOC_NOPM, 0, 0,
+			NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* DA/AD */
+
+	SND_SOC_DAPM_INPUT("ADC Input"),
+	SND_SOC_DAPM_ADC("ADC", "ab8500_0c", SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_OUTPUT("DAC Output"),
+
+	SND_SOC_DAPM_AIF_IN("DA_IN1", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("DA_IN2", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("DA_IN3", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("DA_IN4", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("DA_IN5", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("DA_IN6", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AD_OUT1", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AD_OUT2", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AD_OUT3", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AD_OUT4", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AD_OUT57", NULL, 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AD_OUT68", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+	/* Headset path */
+
+	SND_SOC_DAPM_SUPPLY("Charge Pump", AB8500_ANACONF5,
+			AB8500_ANACONF5_ENCPHS, 0, NULL, 0),
+
+	SND_SOC_DAPM_DAC("DA1 Enable", "ab8500_0p",
+			AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA1, 0),
+	SND_SOC_DAPM_DAC("DA2 Enable", "ab8500_0p",
+			AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA2, 0),
+
+	SND_SOC_DAPM_PGA("HSL Digital Volume", SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+	SND_SOC_DAPM_PGA("HSR Digital Volume", SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+
+	SND_SOC_DAPM_DAC("HSL DAC", "ab8500_0p",
+			AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHSL, 0),
+	SND_SOC_DAPM_DAC("HSR DAC", "ab8500_0p",
+			AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHSR, 0),
+	SND_SOC_DAPM_MIXER("HSL DAC Mute", AB8500_MUTECONF,
+			AB8500_MUTECONF_MUTDACHSL, 1,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("HSR DAC Mute", AB8500_MUTECONF,
+			AB8500_MUTECONF_MUTDACHSR, 1,
+			NULL, 0),
+	SND_SOC_DAPM_DAC("HSL DAC Driver", "ab8500_0p",
+			AB8500_ANACONF3, AB8500_ANACONF3_ENDRVHSL, 0),
+	SND_SOC_DAPM_DAC("HSR DAC Driver", "ab8500_0p",
+			AB8500_ANACONF3, AB8500_ANACONF3_ENDRVHSR, 0),
+
+	SND_SOC_DAPM_MIXER("HSL Mute",
+			AB8500_MUTECONF, AB8500_MUTECONF_MUTHSL, 1,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("HSR Mute",
+			AB8500_MUTECONF, AB8500_MUTECONF_MUTHSR, 1,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("HSL Enable",
+			AB8500_ANACONF4, AB8500_ANACONF4_ENHSL, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("HSR Enable",
+			AB8500_ANACONF4, AB8500_ANACONF4_ENHSR, 0,
+			NULL, 0),
+	SND_SOC_DAPM_PGA("HSL Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+	SND_SOC_DAPM_PGA("HSR Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+
+	SND_SOC_DAPM_OUTPUT("Headset Left"),
+	SND_SOC_DAPM_OUTPUT("Headset Right"),
+
+	/* LineOut path */
+
+	SND_SOC_DAPM_MUX("LineOut Source",
+			SND_SOC_NOPM, 0, 0, dapm_lineout_source),
+
+	SND_SOC_DAPM_MIXER("LOL Disable HFL",
+			AB8500_ANACONF4, AB8500_ANACONF4_ENHFL, 1,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("LOR Disable HFR",
+			AB8500_ANACONF4, AB8500_ANACONF4_ENHFR, 1,
+			NULL, 0),
+
+	SND_SOC_DAPM_MIXER("LOL Enable",
+			AB8500_ANACONF5, AB8500_ANACONF5_ENLOL, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("LOR Enable",
+			AB8500_ANACONF5, AB8500_ANACONF5_ENLOR, 0,
+			NULL, 0),
+
+	SND_SOC_DAPM_OUTPUT("LineOut Left"),
+	SND_SOC_DAPM_OUTPUT("LineOut Right"),
+
+	/* Earpiece path */
+
+	SND_SOC_DAPM_MUX("Earpiece or LineOut Mono Source",
+			SND_SOC_NOPM, 0, 0, &dapm_ear_lineout_source),
+	SND_SOC_DAPM_MIXER("EAR DAC",
+			AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACEAR, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("EAR Mute",
+			AB8500_MUTECONF, AB8500_MUTECONF_MUTEAR, 1,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("EAR Enable",
+			AB8500_ANACONF4, AB8500_ANACONF4_ENEAR, 0,
+			NULL, 0),
+
+	SND_SOC_DAPM_OUTPUT("Earpiece"),
+
+	/* Handsfree path */
+
+	SND_SOC_DAPM_MIXER("DA3 Channel Volume",
+			AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA3, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("DA4 Channel Volume",
+			AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA4, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MUX("Speaker Left Source",
+			SND_SOC_NOPM, 0, 0, dapm_HFl_select),
+	SND_SOC_DAPM_MUX("Speaker Right Source",
+			SND_SOC_NOPM, 0, 0, dapm_HFr_select),
+	SND_SOC_DAPM_MIXER("HFL DAC", AB8500_DAPATHCONF,
+			AB8500_DAPATHCONF_ENDACHFL, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("HFR DAC",
+			AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACHFR, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("DA4 or ANC path to HfR",
+			AB8500_DIGMULTCONF2, AB8500_DIGMULTCONF2_DATOHFREN, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("DA3 or ANC path to HfL",
+			AB8500_DIGMULTCONF2, AB8500_DIGMULTCONF2_DATOHFLEN, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("HFL Enable",
+			AB8500_ANACONF4, AB8500_ANACONF4_ENHFL, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("HFR Enable",
+			AB8500_ANACONF4, AB8500_ANACONF4_ENHFR, 0,
+			NULL, 0),
+
+	SND_SOC_DAPM_OUTPUT("Speaker Left"),
+	SND_SOC_DAPM_OUTPUT("Speaker Right"),
+
+	/* Vibrator path */
+
+	SND_SOC_DAPM_INPUT("PWMGEN1"),
+	SND_SOC_DAPM_INPUT("PWMGEN2"),
+
+	SND_SOC_DAPM_MIXER("DA5 Channel Volume",
+			AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA5, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("DA6 Channel Volume",
+			AB8500_DAPATHENA, AB8500_DAPATHENA_ENDA6, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("VIB1 DAC",
+			AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACVIB1, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("VIB2 DAC",
+			AB8500_DAPATHCONF, AB8500_DAPATHCONF_ENDACVIB2, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MUX("Vibra 1 Controller",
+			SND_SOC_NOPM, 0, 0, dapm_pwm2vib1),
+	SND_SOC_DAPM_MUX("Vibra 2 Controller",
+			SND_SOC_NOPM, 0, 0, dapm_pwm2vib2),
+	SND_SOC_DAPM_MIXER("VIB1 Enable",
+			AB8500_ANACONF4, AB8500_ANACONF4_ENVIB1, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("VIB2 Enable",
+			AB8500_ANACONF4, AB8500_ANACONF4_ENVIB2, 0,
+			NULL, 0),
+
+	SND_SOC_DAPM_OUTPUT("Vibra 1"),
+	SND_SOC_DAPM_OUTPUT("Vibra 2"),
+
+	/* Mic 1 */
+
+	SND_SOC_DAPM_INPUT("Mic 1"),
+
+	SND_SOC_DAPM_MUX("Mic 1a or 1b Select",
+			SND_SOC_NOPM, 0, 0, dapm_mic1ab_mux),
+	SND_SOC_DAPM_MIXER("MIC1 Mute",
+			AB8500_ANACONF2, AB8500_ANACONF2_MUTMIC1, 1,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("MIC1A V-AMICx Enable",
+			AB8500_ANACONF2, AB8500_ANACONF2_ENMIC1, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("MIC1B V-AMICx Enable",
+			AB8500_ANACONF2, AB8500_ANACONF2_ENMIC1, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("MIC1 ADC",
+			AB8500_ANACONF3, AB8500_ANACONF3_ENADCMIC, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MUX("AD3 Source Select",
+			SND_SOC_NOPM, 0, 0, dapm_ad3_select),
+	SND_SOC_DAPM_MIXER("AD3 Channel Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("AD3 Enable",
+			AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34, 0,
+			NULL, 0),
+
+	/* Mic 2 */
+
+	SND_SOC_DAPM_INPUT("Mic 2"),
+
+	SND_SOC_DAPM_MIXER("MIC2 Mute",
+			AB8500_ANACONF2, AB8500_ANACONF2_MUTMIC2, 1,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("MIC2 V-AMICx Enable", AB8500_ANACONF2,
+			AB8500_ANACONF2_ENMIC2, 0,
+			NULL, 0),
+
+	/* LineIn */
+
+	SND_SOC_DAPM_INPUT("LineIn Left"),
+	SND_SOC_DAPM_INPUT("LineIn Right"),
+
+	SND_SOC_DAPM_MIXER("LINL Mute",
+			AB8500_ANACONF2, AB8500_ANACONF2_MUTLINL, 1,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("LINR Mute",
+			AB8500_ANACONF2, AB8500_ANACONF2_MUTLINR, 1,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("LINL Enable", AB8500_ANACONF2,
+			AB8500_ANACONF2_ENLINL, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("LINR Enable", AB8500_ANACONF2,
+			AB8500_ANACONF2_ENLINR, 0,
+			NULL, 0),
+
+	/* LineIn Bypass path */
+	SND_SOC_DAPM_MIXER("LINL to HSL Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("LINR to HSR Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+
+	/* LineIn, Mic 2 */
+	SND_SOC_DAPM_MUX("Mic 2 or LINR Select",
+			SND_SOC_NOPM, 0, 0, dapm_mic2lr_select),
+	SND_SOC_DAPM_MIXER("LINL ADC", AB8500_ANACONF3,
+			AB8500_ANACONF3_ENADCLINL, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("LINR ADC", AB8500_ANACONF3,
+			AB8500_ANACONF3_ENADCLINR, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MUX("AD1 Source Select",
+			SND_SOC_NOPM, 0, 0, dapm_ad1_select),
+	SND_SOC_DAPM_MUX("AD2 Source Select",
+			SND_SOC_NOPM, 0, 0, dapm_ad2_select),
+	SND_SOC_DAPM_MIXER("AD1 Channel Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("AD2 Channel Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+
+	SND_SOC_DAPM_MIXER("AD12 Enable",
+			AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD12, 0,
+			NULL, 0),
+
+	/* HD Capture path */
+
+	SND_SOC_DAPM_MUX("AD5 Source Select",
+			SND_SOC_NOPM, 0, 0, dapm_ad5_select),
+	SND_SOC_DAPM_MUX("AD6 Source Select",
+			SND_SOC_NOPM, 0, 0, dapm_ad6_select),
+	SND_SOC_DAPM_MIXER("AD5 Channel Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("AD6 Channel Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("AD57 Enable",
+			AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("AD68 Enable",
+			AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD5768, 0,
+			NULL, 0),
+
+	/* Digital Microphone path */
+
+	SND_SOC_DAPM_INPUT("DMic 1"),
+	SND_SOC_DAPM_INPUT("DMic 2"),
+	SND_SOC_DAPM_INPUT("DMic 3"),
+	SND_SOC_DAPM_INPUT("DMic 4"),
+	SND_SOC_DAPM_INPUT("DMic 5"),
+	SND_SOC_DAPM_INPUT("DMic 6"),
+
+	SND_SOC_DAPM_MIXER("DMIC1",
+			AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC1, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("DMIC2",
+			AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC2, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("DMIC3",
+			AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC3, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("DMIC4",
+			AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC4, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("DMIC5",
+			AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC5, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("DMIC6",
+			AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC6, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("AD4 Channel Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("AD4 Enable",
+			AB8500_ADPATHENA, AB8500_ADPATHENA_ENAD34,
+			0, NULL, 0),
+
+	/* Acoustical Noise Cancellation path */
+
+	SND_SOC_DAPM_INPUT("ANC Configure Input"),
+	SND_SOC_DAPM_OUTPUT("ANC Configure Output"),
+
+	SND_SOC_DAPM_MUX("ANC Source",
+			SND_SOC_NOPM, 0, 0,
+			dapm_anc_in_select),
+	SND_SOC_DAPM_SWITCH("ANC",
+			SND_SOC_NOPM, 0, 0,
+			dapm_anc_enable),
+	SND_SOC_DAPM_SWITCH("ANC to Earpiece",
+			SND_SOC_NOPM, 0, 0,
+			dapm_anc_ear_mute),
+
+	/* Sidetone Filter path */
+
+	SND_SOC_DAPM_MUX("Sidetone Left Source",
+			SND_SOC_NOPM, 0, 0,
+			dapm_stfir1_in_select),
+	SND_SOC_DAPM_MUX("Sidetone Right Source",
+			SND_SOC_NOPM, 0, 0,
+			dapm_stfir2_in_select),
+	SND_SOC_DAPM_MIXER("STFIR1 Control",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("STFIR2 Control",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("STFIR1 Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+	SND_SOC_DAPM_MIXER("STFIR2 Volume",
+			SND_SOC_NOPM, 0, 0,
+			NULL, 0),
+};
+
+/*
+ * DAPM-routes
+ */
+static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
+	/* Power AB8500 audio-block when AD/DA is active */
+	{"Main Supply", NULL, "V-AUD"},
+	{"Main Supply", NULL, "audioclk"},
+	{"Main Supply", NULL, "Audio Power"},
+	{"Main Supply", NULL, "Audio Analog Power"},
+
+	{"DAC", NULL, "ab8500_0p"},
+	{"DAC", NULL, "Main Supply"},
+	{"ADC", NULL, "ab8500_0c"},
+	{"ADC", NULL, "Main Supply"},
+
+	/* ANC Configure */
+	{"ANC Configure Input", NULL, "Main Supply"},
+	{"ANC Configure Output", NULL, "ANC Configure Input"},
+
+	/* AD/DA */
+	{"ADC", NULL, "ADC Input"},
+	{"DAC Output", NULL, "DAC"},
+
+	/* Powerup charge pump if DA1/2 is in use */
+
+	{"DA_IN1", NULL, "ab8500_0p"},
+	{"DA_IN1", NULL, "Charge Pump"},
+	{"DA_IN2", NULL, "ab8500_0p"},
+	{"DA_IN2", NULL, "Charge Pump"},
+
+	/* Headset path */
+
+	{"DA1 Enable", NULL, "DA_IN1"},
+	{"DA2 Enable", NULL, "DA_IN2"},
+
+	{"HSL Digital Volume", NULL, "DA1 Enable"},
+	{"HSR Digital Volume", NULL, "DA2 Enable"},
+
+	{"HSL DAC", NULL, "HSL Digital Volume"},
+	{"HSR DAC", NULL, "HSR Digital Volume"},
+
+	{"HSL DAC Mute", NULL, "HSL DAC"},
+	{"HSR DAC Mute", NULL, "HSR DAC"},
+
+	{"HSL DAC Driver", NULL, "HSL DAC Mute"},
+	{"HSR DAC Driver", NULL, "HSR DAC Mute"},
+
+	{"HSL Mute", NULL, "HSL DAC Driver"},
+	{"HSR Mute", NULL, "HSR DAC Driver"},
+
+	{"HSL Enable", NULL, "HSL Mute"},
+	{"HSR Enable", NULL, "HSR Mute"},
+
+	{"HSL Volume", NULL, "HSL Enable"},
+	{"HSR Volume", NULL, "HSR Enable"},
+
+	{"Headset Left", NULL, "HSL Volume"},
+	{"Headset Right", NULL, "HSR Volume"},
+
+	/* HF or LineOut path */
+
+	{"DA_IN3", NULL, "ab8500_0p"},
+	{"DA3 Channel Volume", NULL, "DA_IN3"},
+	{"DA_IN4", NULL, "ab8500_0p"},
+	{"DA4 Channel Volume", NULL, "DA_IN4"},
+
+	{"Speaker Left Source", "Audio Path", "DA3 Channel Volume"},
+	{"Speaker Right Source", "Audio Path", "DA4 Channel Volume"},
+
+	{"DA3 or ANC path to HfL", NULL, "Speaker Left Source"},
+	{"DA4 or ANC path to HfR", NULL, "Speaker Right Source"},
+
+	/* HF path */
+
+	{"HFL DAC", NULL, "DA3 or ANC path to HfL"},
+	{"HFR DAC", NULL, "DA4 or ANC path to HfR"},
+
+	{"HFL Enable", NULL, "HFL DAC"},
+	{"HFR Enable", NULL, "HFR DAC"},
+
+	{"Speaker Left", NULL, "HFL Enable"},
+	{"Speaker Right", NULL, "HFR Enable"},
+
+	/* Earpiece path */
+
+	{"Earpiece or LineOut Mono Source", "Headset Left",
+		"HSL Digital Volume"},
+	{"Earpiece or LineOut Mono Source", "Speaker Left",
+		"DA3 or ANC path to HfL"},
+
+	{"EAR DAC", NULL, "Earpiece or LineOut Mono Source"},
+
+	{"EAR Mute", NULL, "EAR DAC"},
+
+	{"EAR Enable", NULL, "EAR Mute"},
+
+	{"Earpiece", NULL, "EAR Enable"},
+
+	/* LineOut path stereo */
+
+	{"LineOut Source", "Stereo Path", "HSL DAC Driver"},
+	{"LineOut Source", "Stereo Path", "HSR DAC Driver"},
+
+	/* LineOut path mono */
+
+	{"LineOut Source", "Mono Path", "EAR DAC"},
+
+	/* LineOut path */
+
+	{"LOL Disable HFL", NULL, "LineOut Source"},
+	{"LOR Disable HFR", NULL, "LineOut Source"},
+
+	{"LOL Enable", NULL, "LOL Disable HFL"},
+	{"LOR Enable", NULL, "LOR Disable HFR"},
+
+	{"LineOut Left", NULL, "LOL Enable"},
+	{"LineOut Right", NULL, "LOR Enable"},
+
+	/* Vibrator path */
+
+	{"DA_IN5", NULL, "ab8500_0p"},
+	{"DA5 Channel Volume", NULL, "DA_IN5"},
+	{"DA_IN6", NULL, "ab8500_0p"},
+	{"DA6 Channel Volume", NULL, "DA_IN6"},
+
+	{"VIB1 DAC", NULL, "DA5 Channel Volume"},
+	{"VIB2 DAC", NULL, "DA6 Channel Volume"},
+
+	{"Vibra 1 Controller", "Audio Path", "VIB1 DAC"},
+	{"Vibra 2 Controller", "Audio Path", "VIB2 DAC"},
+	{"Vibra 1 Controller", "PWM Generator", "PWMGEN1"},
+	{"Vibra 2 Controller", "PWM Generator", "PWMGEN2"},
+
+	{"VIB1 Enable", NULL, "Vibra 1 Controller"},
+	{"VIB2 Enable", NULL, "Vibra 2 Controller"},
+
+	{"Vibra 1", NULL, "VIB1 Enable"},
+	{"Vibra 2", NULL, "VIB2 Enable"},
+
+
+	/* Mic 2 */
+
+	{"MIC2 V-AMICx Enable", NULL, "Mic 2"},
+
+	/* LineIn */
+	{"LINL Mute", NULL, "LineIn Left"},
+	{"LINR Mute", NULL, "LineIn Right"},
+
+	{"LINL Enable", NULL, "LINL Mute"},
+	{"LINR Enable", NULL, "LINR Mute"},
+
+	/* LineIn, Mic 2 */
+	{"Mic 2 or LINR Select", "LineIn Right", "LINR Enable"},
+	{"Mic 2 or LINR Select", "Mic 2", "MIC2 V-AMICx Enable"},
+
+	{"LINL ADC", NULL, "LINL Enable"},
+	{"LINR ADC", NULL, "Mic 2 or LINR Select"},
+
+	{"AD1 Source Select", "LineIn Left", "LINL ADC"},
+	{"AD2 Source Select", "LineIn Right", "LINR ADC"},
+
+	{"AD1 Channel Volume", NULL, "AD1 Source Select"},
+	{"AD2 Channel Volume", NULL, "AD2 Source Select"},
+
+	{"AD12 Enable", NULL, "AD1 Channel Volume"},
+	{"AD12 Enable", NULL, "AD2 Channel Volume"},
+
+	{"AD_OUT1", NULL, "ab8500_0c"},
+	{"AD_OUT1", NULL, "AD12 Enable"},
+	{"AD_OUT2", NULL, "ab8500_0c"},
+	{"AD_OUT2", NULL, "AD12 Enable"},
+
+	/* Mic 1 */
+
+	{"MIC1 Mute", NULL, "Mic 1"},
+
+	{"MIC1A V-AMICx Enable", NULL, "MIC1 Mute"},
+	{"MIC1B V-AMICx Enable", NULL, "MIC1 Mute"},
+
+	{"Mic 1a or 1b Select", "Mic 1a", "MIC1A V-AMICx Enable"},
+	{"Mic 1a or 1b Select", "Mic 1b", "MIC1B V-AMICx Enable"},
+
+	{"MIC1 ADC", NULL, "Mic 1a or 1b Select"},
+
+	{"AD3 Source Select", "Mic 1", "MIC1 ADC"},
+
+	{"AD3 Channel Volume", NULL, "AD3 Source Select"},
+
+	{"AD3 Enable", NULL, "AD3 Channel Volume"},
+
+	{"AD_OUT3", NULL, "ab8500_0c"},
+	{"AD_OUT3", NULL, "AD3 Enable"},
+
+	/* HD Capture path */
+
+	{"AD5 Source Select", "Mic 2", "LINR ADC"},
+	{"AD6 Source Select", "Mic 1", "MIC1 ADC"},
+
+	{"AD5 Channel Volume", NULL, "AD5 Source Select"},
+	{"AD6 Channel Volume", NULL, "AD6 Source Select"},
+
+	{"AD57 Enable", NULL, "AD5 Channel Volume"},
+	{"AD68 Enable", NULL, "AD6 Channel Volume"},
+
+	{"AD_OUT57", NULL, "ab8500_0c"},
+	{"AD_OUT57", NULL, "AD57 Enable"},
+	{"AD_OUT68", NULL, "ab8500_0c"},
+	{"AD_OUT68", NULL, "AD68 Enable"},
+
+	/* Digital Microphone path */
+
+	{"DMic 1", NULL, "V-DMIC"},
+	{"DMic 2", NULL, "V-DMIC"},
+	{"DMic 3", NULL, "V-DMIC"},
+	{"DMic 4", NULL, "V-DMIC"},
+	{"DMic 5", NULL, "V-DMIC"},
+	{"DMic 6", NULL, "V-DMIC"},
+
+	{"AD1 Source Select", NULL, "DMic 1"},
+	{"AD2 Source Select", NULL, "DMic 2"},
+	{"AD3 Source Select", NULL, "DMic 3"},
+	{"AD5 Source Select", NULL, "DMic 5"},
+	{"AD6 Source Select", NULL, "DMic 6"},
+
+	{"AD4 Channel Volume", NULL, "DMic 4"},
+	{"AD4 Enable", NULL, "AD4 Channel Volume"},
+
+	{"AD_OUT4", NULL, "ab8500_0c"},
+	{"AD_OUT4", NULL, "AD4 Enable"},
+
+	/* LineIn Bypass path */
+
+	{"LINL to HSL Volume", NULL, "LINL Enable"},
+	{"LINR to HSR Volume", NULL, "LINR Enable"},
+
+	{"HSL DAC Driver", NULL, "LINL to HSL Volume"},
+	{"HSR DAC Driver", NULL, "LINR to HSR Volume"},
+
+	/* ANC path (Acoustic Noise Cancellation) */
+
+	{"ANC Source", "Mic 2 / DMic 5", "AD5 Channel Volume"},
+	{"ANC Source", "Mic 1 / DMic 6", "AD6 Channel Volume"},
+
+	{"ANC", "Switch", "ANC Source"},
+
+	{"Speaker Left Source", "ANC", "ANC"},
+	{"Speaker Right Source", "ANC", "ANC"},
+	{"ANC to Earpiece", "Switch", "ANC"},
+
+	{"HSL Digital Volume", NULL, "ANC to Earpiece"},
+
+	/* Sidetone Filter path */
+
+	{"Sidetone Left Source", "LineIn Left", "AD12 Enable"},
+	{"Sidetone Left Source", "LineIn Right", "AD12 Enable"},
+	{"Sidetone Left Source", "Mic 1", "AD3 Enable"},
+	{"Sidetone Left Source", "Headset Left", "DA_IN1"},
+	{"Sidetone Right Source", "LineIn Right", "AD12 Enable"},
+	{"Sidetone Right Source", "Mic 1", "AD3 Enable"},
+	{"Sidetone Right Source", "DMic 4", "AD4 Enable"},
+	{"Sidetone Right Source", "Headset Right", "DA_IN2"},
+
+	{"STFIR1 Control", NULL, "Sidetone Left Source"},
+	{"STFIR2 Control", NULL, "Sidetone Right Source"},
+
+	{"STFIR1 Volume", NULL, "STFIR1 Control"},
+	{"STFIR2 Volume", NULL, "STFIR2 Control"},
+
+	{"DA1 Enable", NULL, "STFIR1 Volume"},
+	{"DA2 Enable", NULL, "STFIR2 Volume"},
+};
+
+static const struct snd_soc_dapm_route ab8500_dapm_routes_mic1a_vamicx[] = {
+	{"MIC1A V-AMICx Enable", NULL, "V-AMIC1"},
+	{"MIC1A V-AMICx Enable", NULL, "V-AMIC2"},
+};
+
+static const struct snd_soc_dapm_route ab8500_dapm_routes_mic1b_vamicx[] = {
+	{"MIC1B V-AMICx Enable", NULL, "V-AMIC1"},
+	{"MIC1B V-AMICx Enable", NULL, "V-AMIC2"},
+};
+
+static const struct snd_soc_dapm_route ab8500_dapm_routes_mic2_vamicx[] = {
+	{"MIC2 V-AMICx Enable", NULL, "V-AMIC1"},
+	{"MIC2 V-AMICx Enable", NULL, "V-AMIC2"},
+};
+
+/* ANC FIR-coefficients configuration sequence */
+static void anc_fir(struct snd_soc_codec *codec,
+		unsigned int bnk, unsigned int par, unsigned int val)
+{
+	if (par == 0 && bnk == 0)
+		snd_soc_update_bits(codec, AB8500_ANCCONF1,
+			BIT(AB8500_ANCCONF1_ANCFIRUPDATE),
+			BIT(AB8500_ANCCONF1_ANCFIRUPDATE));
+
+	snd_soc_write(codec, AB8500_ANCCONF5, val >> 8 & 0xff);
+	snd_soc_write(codec, AB8500_ANCCONF6, val &  0xff);
+
+	if (par == AB8500_ANC_FIR_COEFFS - 1 && bnk == 1)
+		snd_soc_update_bits(codec, AB8500_ANCCONF1,
+			BIT(AB8500_ANCCONF1_ANCFIRUPDATE), 0);
+}
+
+/* ANC IIR-coefficients configuration sequence */
+static void anc_iir(struct snd_soc_codec *codec, unsigned int bnk,
+		unsigned int par, unsigned int val)
+{
+	if (par == 0) {
+		if (bnk == 0) {
+			snd_soc_update_bits(codec, AB8500_ANCCONF1,
+					BIT(AB8500_ANCCONF1_ANCIIRINIT),
+					BIT(AB8500_ANCCONF1_ANCIIRINIT));
+			usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY);
+			snd_soc_update_bits(codec, AB8500_ANCCONF1,
+					BIT(AB8500_ANCCONF1_ANCIIRINIT), 0);
+			usleep_range(AB8500_ANC_SM_DELAY, AB8500_ANC_SM_DELAY);
+		} else {
+			snd_soc_update_bits(codec, AB8500_ANCCONF1,
+					BIT(AB8500_ANCCONF1_ANCIIRUPDATE),
+					BIT(AB8500_ANCCONF1_ANCIIRUPDATE));
+		}
+	} else if (par > 3) {
+		snd_soc_write(codec, AB8500_ANCCONF7, 0);
+		snd_soc_write(codec, AB8500_ANCCONF8, val >> 16 & 0xff);
+	}
+
+	snd_soc_write(codec, AB8500_ANCCONF7, val >> 8 & 0xff);
+	snd_soc_write(codec, AB8500_ANCCONF8, val & 0xff);
+
+	if (par == AB8500_ANC_IIR_COEFFS - 1 && bnk == 1)
+		snd_soc_update_bits(codec, AB8500_ANCCONF1,
+			BIT(AB8500_ANCCONF1_ANCIIRUPDATE), 0);
+}
+
+/* ANC IIR-/FIR-coefficients configuration sequence */
+static void anc_configure(struct snd_soc_codec *codec,
+			bool apply_fir, bool apply_iir)
+{
+	struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+	unsigned int bnk, par, val;
+
+	dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+
+	if (apply_fir)
+		snd_soc_update_bits(codec, AB8500_ANCCONF1,
+			BIT(AB8500_ANCCONF1_ENANC), 0);
+
+	snd_soc_update_bits(codec, AB8500_ANCCONF1,
+		BIT(AB8500_ANCCONF1_ENANC), BIT(AB8500_ANCCONF1_ENANC));
+
+	if (apply_fir)
+		for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++)
+			for (par = 0; par < AB8500_ANC_FIR_COEFFS; par++) {
+				val = snd_soc_read(codec,
+						drvdata->anc_fir_values[par]);
+				anc_fir(codec, bnk, par, val);
+			}
+
+	if (apply_iir)
+		for (bnk = 0; bnk < AB8500_NR_OF_ANC_COEFF_BANKS; bnk++)
+			for (par = 0; par < AB8500_ANC_IIR_COEFFS; par++) {
+				val = snd_soc_read(codec,
+						drvdata->anc_iir_values[par]);
+				anc_iir(codec, bnk, par, val);
+			}
+
+	dev_dbg(codec->dev, "%s: Exit.\n", __func__);
+}
+
+/*
+ * Control-events
+ */
+
+static int sid_status_control_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+
+	mutex_lock(&codec->mutex);
+	ucontrol->value.integer.value[0] = drvdata->sid_status;
+	mutex_unlock(&codec->mutex);
+
+	return 0;
+}
+
+/* Write sidetone FIR-coefficients configuration sequence */
+static int sid_status_control_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+	unsigned int param, sidconf, val;
+	int status = 1;
+
+	dev_dbg(codec->dev, "%s: Enter\n", __func__);
+
+	if (ucontrol->value.integer.value[0] != SID_APPLY_FIR) {
+		dev_err(codec->dev,
+			"%s: ERROR: This control supports '%s' only!\n",
+			__func__, enum_sid_state[SID_APPLY_FIR]);
+		return -EIO;
+	}
+
+	mutex_lock(&codec->mutex);
+
+	sidconf = snd_soc_read(codec, AB8500_SIDFIRCONF);
+	if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) {
+		if ((sidconf & BIT(AB8500_SIDFIRCONF_ENFIRSIDS)) == 0) {
+			dev_err(codec->dev, "%s: Sidetone busy while off!\n",
+				__func__);
+			status = -EPERM;
+		} else {
+			status = -EBUSY;
+		}
+		goto out;
+	}
+
+	snd_soc_write(codec, AB8500_SIDFIRADR, 0);
+
+	for (param = 0; param < AB8500_SID_FIR_COEFFS; param++) {
+		val = snd_soc_read(codec, drvdata->sid_fir_values[param]);
+		snd_soc_write(codec, AB8500_SIDFIRCOEF1, val >> 8 & 0xff);
+		snd_soc_write(codec, AB8500_SIDFIRCOEF2, val & 0xff);
+	}
+
+	snd_soc_update_bits(codec, AB8500_SIDFIRADR,
+		BIT(AB8500_SIDFIRADR_FIRSIDSET),
+		BIT(AB8500_SIDFIRADR_FIRSIDSET));
+	snd_soc_update_bits(codec, AB8500_SIDFIRADR,
+		BIT(AB8500_SIDFIRADR_FIRSIDSET), 0);
+
+	drvdata->sid_status = SID_FIR_CONFIGURED;
+
+out:
+	mutex_unlock(&codec->mutex);
+
+	dev_dbg(codec->dev, "%s: Exit\n", __func__);
+
+	return status;
+}
+
+static int anc_status_control_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+
+	mutex_lock(&codec->mutex);
+	ucontrol->value.integer.value[0] = drvdata->anc_status;
+	mutex_unlock(&codec->mutex);
+
+	return 0;
+}
+
+static int anc_status_control_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
+	struct device *dev = codec->dev;
+	bool apply_fir, apply_iir;
+	int req, status;
+
+	dev_dbg(dev, "%s: Enter.\n", __func__);
+
+	mutex_lock(&drvdata->anc_lock);
+
+	req = ucontrol->value.integer.value[0];
+	if (req != ANC_APPLY_FIR_IIR && req != ANC_APPLY_FIR &&
+		req != ANC_APPLY_IIR) {
+		dev_err(dev, "%s: ERROR: Unsupported status to set '%s'!\n",
+			__func__, enum_anc_state[req]);
+		status = -EINVAL;
+		goto cleanup;
+	}
+	apply_fir = req == ANC_APPLY_FIR || req == ANC_APPLY_FIR_IIR;
+	apply_iir = req == ANC_APPLY_IIR || req == ANC_APPLY_FIR_IIR;
+
+	status = snd_soc_dapm_force_enable_pin(&codec->dapm,
+					"ANC Configure Input");
+	if (status < 0) {
+		dev_err(dev,
+			"%s: ERROR: Failed to enable power (status = %d)!\n",
+			__func__, status);
+		goto cleanup;
+	}
+	snd_soc_dapm_sync(&codec->dapm);
+
+	mutex_lock(&codec->mutex);
+	anc_configure(codec, apply_fir, apply_iir);
+	mutex_unlock(&codec->mutex);
+
+	if (apply_fir) {
+		if (drvdata->anc_status == ANC_IIR_CONFIGURED)
+			drvdata->anc_status = ANC_FIR_IIR_CONFIGURED;
+		else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED)
+			drvdata->anc_status =  ANC_FIR_CONFIGURED;
+	}
+	if (apply_iir) {
+		if (drvdata->anc_status == ANC_FIR_CONFIGURED)
+			drvdata->anc_status = ANC_FIR_IIR_CONFIGURED;
+		else if (drvdata->anc_status != ANC_FIR_IIR_CONFIGURED)
+			drvdata->anc_status =  ANC_IIR_CONFIGURED;
+	}
+
+	status = snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
+	snd_soc_dapm_sync(&codec->dapm);
+
+cleanup:
+	mutex_unlock(&drvdata->anc_lock);
+
+	if (status < 0)
+		dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n",
+			__func__, status);
+
+	dev_dbg(dev, "%s: Exit.\n", __func__);
+
+	return (status < 0) ? status : 1;
+}
+
+static int filter_control_info(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
+{
+	struct filter_control *fc =
+			(struct filter_control *)kcontrol->private_value;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = fc->count;
+	uinfo->value.integer.min = fc->min;
+	uinfo->value.integer.max = fc->max;
+
+	return 0;
+}
+
+static int filter_control_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct filter_control *fc =
+			(struct filter_control *)kcontrol->private_value;
+	unsigned int i;
+
+	mutex_lock(&codec->mutex);
+	for (i = 0; i < fc->count; i++)
+		ucontrol->value.integer.value[i] = fc->value[i];
+	mutex_unlock(&codec->mutex);
+
+	return 0;
+}
+
+static int filter_control_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct filter_control *fc =
+			(struct filter_control *)kcontrol->private_value;
+	unsigned int i;
+
+	mutex_lock(&codec->mutex);
+	for (i = 0; i < fc->count; i++)
+		fc->value[i] = ucontrol->value.integer.value[i];
+	mutex_unlock(&codec->mutex);
+
+	return 0;
+}
+
+/*
+ * Controls - Non-DAPM ASoC
+ */
+
+static DECLARE_TLV_DB_SCALE(adx_dig_gain_tlv, -3200, 100, 1);
+/* -32dB = Mute */
+
+static DECLARE_TLV_DB_SCALE(dax_dig_gain_tlv, -6300, 100, 1);
+/* -63dB = Mute */
+
+static DECLARE_TLV_DB_SCALE(hs_ear_dig_gain_tlv, -100, 100, 1);
+/* -1dB = Mute */
+
+static const unsigned int hs_gain_tlv[] = {
+	TLV_DB_RANGE_HEAD(2),
+	0, 3, TLV_DB_SCALE_ITEM(-3200, 400, 0),
+	4, 15, TLV_DB_SCALE_ITEM(-1800, 200, 0),
+};
+
+static DECLARE_TLV_DB_SCALE(mic_gain_tlv, 0, 100, 0);
+
+static DECLARE_TLV_DB_SCALE(lin_gain_tlv, -1000, 200, 0);
+
+static DECLARE_TLV_DB_SCALE(lin2hs_gain_tlv, -3800, 200, 1);
+/* -38dB = Mute */
+
+static const char * const enum_hsfadspeed[] = {"2ms", "0.5ms", "10.6ms",
+					"5ms"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_hsfadspeed,
+	AB8500_DIGMICCONF, AB8500_DIGMICCONF_HSFADSPEED, enum_hsfadspeed);
+
+static const char * const enum_envdetthre[] = {
+	"250mV", "300mV", "350mV", "400mV",
+	"450mV", "500mV", "550mV", "600mV",
+	"650mV", "700mV", "750mV", "800mV",
+	"850mV", "900mV", "950mV", "1.00V" };
+static SOC_ENUM_SINGLE_DECL(soc_enum_envdeththre,
+	AB8500_ENVCPCONF, AB8500_ENVCPCONF_ENVDETHTHRE, enum_envdetthre);
+static SOC_ENUM_SINGLE_DECL(soc_enum_envdetlthre,
+	AB8500_ENVCPCONF, AB8500_ENVCPCONF_ENVDETLTHRE, enum_envdetthre);
+static const char * const enum_envdettime[] = {
+	"26.6us", "53.2us", "106us",  "213us",
+	"426us",  "851us",  "1.70ms", "3.40ms",
+	"6.81ms", "13.6ms", "27.2ms", "54.5ms",
+	"109ms",  "218ms",  "436ms",  "872ms" };
+static SOC_ENUM_SINGLE_DECL(soc_enum_envdettime,
+	AB8500_SIGENVCONF, AB8500_SIGENVCONF_ENVDETTIME, enum_envdettime);
+
+static const char * const enum_sinc31[] = {"Sinc 3", "Sinc 1"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_hsesinc, AB8500_HSLEARDIGGAIN,
+			AB8500_HSLEARDIGGAIN_HSSINC1, enum_sinc31);
+
+static const char * const enum_fadespeed[] = {"1ms", "4ms", "8ms", "16ms"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_fadespeed, AB8500_HSRDIGGAIN,
+			AB8500_HSRDIGGAIN_FADESPEED, enum_fadespeed);
+
+/* Earpiece */
+
+static const char * const enum_lowpow[] = {"Normal", "Low Power"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_eardaclowpow, AB8500_ANACONF1,
+			AB8500_ANACONF1_EARDACLOWPOW, enum_lowpow);
+static SOC_ENUM_SINGLE_DECL(soc_enum_eardrvlowpow, AB8500_ANACONF1,
+			AB8500_ANACONF1_EARDRVLOWPOW, enum_lowpow);
+
+static const char * const enum_av_mode[] = {"Audio", "Voice"};
+static SOC_ENUM_DOUBLE_DECL(soc_enum_ad12voice, AB8500_ADFILTCONF,
+	AB8500_ADFILTCONF_AD1VOICE, AB8500_ADFILTCONF_AD2VOICE, enum_av_mode);
+static SOC_ENUM_DOUBLE_DECL(soc_enum_ad34voice, AB8500_ADFILTCONF,
+	AB8500_ADFILTCONF_AD3VOICE, AB8500_ADFILTCONF_AD4VOICE, enum_av_mode);
+
+/* DA */
+
+static SOC_ENUM_SINGLE_DECL(soc_enum_da12voice,
+			AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_DA12VOICE,
+			enum_av_mode);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da34voice,
+			AB8500_DASLOTCONF3, AB8500_DASLOTCONF3_DA34VOICE,
+			enum_av_mode);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da56voice,
+			AB8500_DASLOTCONF5, AB8500_DASLOTCONF5_DA56VOICE,
+			enum_av_mode);
+
+static const char * const enum_da2hslr[] = {"Sidetone", "Audio Path"};
+static SOC_ENUM_DOUBLE_DECL(soc_enum_da2hslr, AB8500_DIGMULTCONF1,
+			AB8500_DIGMULTCONF1_DATOHSLEN,
+			AB8500_DIGMULTCONF1_DATOHSREN, enum_da2hslr);
+
+static const char * const enum_sinc53[] = {"Sinc 5", "Sinc 3"};
+static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic12sinc, AB8500_DMICFILTCONF,
+			AB8500_DMICFILTCONF_DMIC1SINC3,
+			AB8500_DMICFILTCONF_DMIC2SINC3, enum_sinc53);
+static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic34sinc, AB8500_DMICFILTCONF,
+			AB8500_DMICFILTCONF_DMIC3SINC3,
+			AB8500_DMICFILTCONF_DMIC4SINC3, enum_sinc53);
+static SOC_ENUM_DOUBLE_DECL(soc_enum_dmic56sinc, AB8500_DMICFILTCONF,
+			AB8500_DMICFILTCONF_DMIC5SINC3,
+			AB8500_DMICFILTCONF_DMIC6SINC3, enum_sinc53);
+
+/* Digital interface - DA from slot mapping */
+static const char * const enum_da_from_slot_map[] = {"SLOT0",
+					"SLOT1",
+					"SLOT2",
+					"SLOT3",
+					"SLOT4",
+					"SLOT5",
+					"SLOT6",
+					"SLOT7",
+					"SLOT8",
+					"SLOT9",
+					"SLOT10",
+					"SLOT11",
+					"SLOT12",
+					"SLOT13",
+					"SLOT14",
+					"SLOT15",
+					"SLOT16",
+					"SLOT17",
+					"SLOT18",
+					"SLOT19",
+					"SLOT20",
+					"SLOT21",
+					"SLOT22",
+					"SLOT23",
+					"SLOT24",
+					"SLOT25",
+					"SLOT26",
+					"SLOT27",
+					"SLOT28",
+					"SLOT29",
+					"SLOT30",
+					"SLOT31"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_da1slotmap,
+			AB8500_DASLOTCONF1, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+			enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da2slotmap,
+			AB8500_DASLOTCONF2, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+			enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da3slotmap,
+			AB8500_DASLOTCONF3, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+			enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da4slotmap,
+			AB8500_DASLOTCONF4, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+			enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da5slotmap,
+			AB8500_DASLOTCONF5, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+			enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da6slotmap,
+			AB8500_DASLOTCONF6, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+			enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da7slotmap,
+			AB8500_DASLOTCONF7, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+			enum_da_from_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_da8slotmap,
+			AB8500_DASLOTCONF8, AB8500_DASLOTCONFX_SLTODAX_SHIFT,
+			enum_da_from_slot_map);
+
+/* Digital interface - AD to slot mapping */
+static const char * const enum_ad_to_slot_map[] = {"AD_OUT1",
+					"AD_OUT2",
+					"AD_OUT3",
+					"AD_OUT4",
+					"AD_OUT5",
+					"AD_OUT6",
+					"AD_OUT7",
+					"AD_OUT8",
+					"zeroes",
+					"tristate"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot0map,
+			AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot1map,
+			AB8500_ADSLOTSEL1, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot2map,
+			AB8500_ADSLOTSEL2, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot3map,
+			AB8500_ADSLOTSEL2, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot4map,
+			AB8500_ADSLOTSEL3, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot5map,
+			AB8500_ADSLOTSEL3, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot6map,
+			AB8500_ADSLOTSEL4, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot7map,
+			AB8500_ADSLOTSEL4, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot8map,
+			AB8500_ADSLOTSEL5, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot9map,
+			AB8500_ADSLOTSEL5, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot10map,
+			AB8500_ADSLOTSEL6, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot11map,
+			AB8500_ADSLOTSEL6, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot12map,
+			AB8500_ADSLOTSEL7, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot13map,
+			AB8500_ADSLOTSEL7, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot14map,
+			AB8500_ADSLOTSEL8, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot15map,
+			AB8500_ADSLOTSEL8, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot16map,
+			AB8500_ADSLOTSEL9, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot17map,
+			AB8500_ADSLOTSEL9, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot18map,
+			AB8500_ADSLOTSEL10, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot19map,
+			AB8500_ADSLOTSEL10, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot20map,
+			AB8500_ADSLOTSEL11, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot21map,
+			AB8500_ADSLOTSEL11, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot22map,
+			AB8500_ADSLOTSEL12, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot23map,
+			AB8500_ADSLOTSEL12, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot24map,
+			AB8500_ADSLOTSEL13, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot25map,
+			AB8500_ADSLOTSEL13, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot26map,
+			AB8500_ADSLOTSEL14, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot27map,
+			AB8500_ADSLOTSEL14, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot28map,
+			AB8500_ADSLOTSEL15, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot29map,
+			AB8500_ADSLOTSEL15, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot30map,
+			AB8500_ADSLOTSEL16, AB8500_ADSLOTSELX_EVEN_SHIFT,
+			enum_ad_to_slot_map);
+static SOC_ENUM_SINGLE_DECL(soc_enum_adslot31map,
+			AB8500_ADSLOTSEL16, AB8500_ADSLOTSELX_ODD_SHIFT,
+			enum_ad_to_slot_map);
+
+/* Digital interface - Burst mode */
+static const char * const enum_mask[] = {"Unmasked", "Masked"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomask,
+			AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFOMASK,
+			enum_mask);
+static const char * const enum_bitclk0[] = {"19_2_MHz", "38_4_MHz"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_bfifo19m2,
+			AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFO19M2,
+			enum_bitclk0);
+static const char * const enum_slavemaster[] = {"Slave", "Master"};
+static SOC_ENUM_SINGLE_DECL(soc_enum_bfifomast,
+			AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOMAST_SHIFT,
+			enum_slavemaster);
+
+/* Sidetone */
+static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_sidstate, enum_sid_state);
+
+/* ANC */
+static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_ancstate, enum_anc_state);
+
+static struct snd_kcontrol_new ab8500_ctrls[] = {
+	/* Charge pump */
+	SOC_ENUM("Charge Pump High Threshold For Low Voltage",
+		soc_enum_envdeththre),
+	SOC_ENUM("Charge Pump Low Threshold For Low Voltage",
+		soc_enum_envdetlthre),
+	SOC_SINGLE("Charge Pump Envelope Detection Switch",
+		AB8500_SIGENVCONF, AB8500_SIGENVCONF_ENVDETCPEN,
+		1, 0),
+	SOC_ENUM("Charge Pump Envelope Detection Decay Time",
+		soc_enum_envdettime),
+
+	/* Headset */
+	SOC_ENUM("Headset Mode", soc_enum_da12voice),
+	SOC_SINGLE("Headset High Pass Switch",
+		AB8500_ANACONF1, AB8500_ANACONF1_HSHPEN,
+		1, 0),
+	SOC_SINGLE("Headset Low Power Switch",
+		AB8500_ANACONF1, AB8500_ANACONF1_HSLOWPOW,
+		1, 0),
+	SOC_SINGLE("Headset DAC Low Power Switch",
+		AB8500_ANACONF1, AB8500_ANACONF1_DACLOWPOW1,
+		1, 0),
+	SOC_SINGLE("Headset DAC Drv Low Power Switch",
+		AB8500_ANACONF1, AB8500_ANACONF1_DACLOWPOW0,
+		1, 0),
+	SOC_ENUM("Headset Fade Speed", soc_enum_hsfadspeed),
+	SOC_ENUM("Headset Source", soc_enum_da2hslr),
+	SOC_ENUM("Headset Filter", soc_enum_hsesinc),
+	SOC_DOUBLE_R_TLV("Headset Master Volume",
+		AB8500_DADIGGAIN1, AB8500_DADIGGAIN2,
+		0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv),
+	SOC_DOUBLE_R_TLV("Headset Digital Volume",
+		AB8500_HSLEARDIGGAIN, AB8500_HSRDIGGAIN,
+		0, AB8500_HSLEARDIGGAIN_HSLDGAIN_MAX, 1, hs_ear_dig_gain_tlv),
+	SOC_DOUBLE_TLV("Headset Volume",
+		AB8500_ANAGAIN3,
+		AB8500_ANAGAIN3_HSLGAIN, AB8500_ANAGAIN3_HSRGAIN,
+		AB8500_ANAGAIN3_HSXGAIN_MAX, 1, hs_gain_tlv),
+
+	/* Earpiece */
+	SOC_ENUM("Earpiece DAC Mode",
+		soc_enum_eardaclowpow),
+	SOC_ENUM("Earpiece DAC Drv Mode",
+		soc_enum_eardrvlowpow),
+
+	/* HandsFree */
+	SOC_ENUM("HF Mode", soc_enum_da34voice),
+	SOC_SINGLE("HF and Headset Swap Switch",
+		AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_SWAPDA12_34,
+		1, 0),
+	SOC_DOUBLE("HF Low EMI Mode Switch",
+		AB8500_CLASSDCONF1,
+		AB8500_CLASSDCONF1_HFLSWAPEN, AB8500_CLASSDCONF1_HFRSWAPEN,
+		1, 0),
+	SOC_DOUBLE("HF FIR Bypass Switch",
+		AB8500_CLASSDCONF2,
+		AB8500_CLASSDCONF2_FIRBYP0, AB8500_CLASSDCONF2_FIRBYP1,
+		1, 0),
+	SOC_DOUBLE("HF High Volume Switch",
+		AB8500_CLASSDCONF2,
+		AB8500_CLASSDCONF2_HIGHVOLEN0, AB8500_CLASSDCONF2_HIGHVOLEN1,
+		1, 0),
+	SOC_SINGLE("HF L and R Bridge Switch",
+		AB8500_CLASSDCONF1, AB8500_CLASSDCONF1_PARLHF,
+		1, 0),
+	SOC_DOUBLE_R_TLV("HF Master Volume",
+		AB8500_DADIGGAIN3, AB8500_DADIGGAIN4,
+		0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv),
+
+	/* Vibra */
+	SOC_DOUBLE("Vibra High Volume Switch",
+		AB8500_CLASSDCONF2,
+		AB8500_CLASSDCONF2_HIGHVOLEN2, AB8500_CLASSDCONF2_HIGHVOLEN3,
+		1, 0),
+	SOC_DOUBLE("Vibra Low EMI Mode Switch",
+		AB8500_CLASSDCONF1,
+		AB8500_CLASSDCONF1_VIB1SWAPEN, AB8500_CLASSDCONF1_VIB2SWAPEN,
+		1, 0),
+	SOC_DOUBLE("Vibra FIR Bypass Switch",
+		AB8500_CLASSDCONF2,
+		AB8500_CLASSDCONF2_FIRBYP2, AB8500_CLASSDCONF2_FIRBYP3,
+		1, 0),
+	SOC_ENUM("Vibra Mode", soc_enum_da56voice),
+	SOC_DOUBLE_R("Vibra PWM Duty Cycle N",
+		AB8500_PWMGENCONF3, AB8500_PWMGENCONF5,
+		AB8500_PWMGENCONFX_PWMVIBXDUTCYC,
+		AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX, 0),
+	SOC_DOUBLE_R("Vibra PWM Duty Cycle P",
+		AB8500_PWMGENCONF2, AB8500_PWMGENCONF4,
+		AB8500_PWMGENCONFX_PWMVIBXDUTCYC,
+		AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX, 0),
+	SOC_SINGLE("Vibra 1 and 2 Bridge Switch",
+		AB8500_CLASSDCONF1, AB8500_CLASSDCONF1_PARLVIB,
+		1, 0),
+	SOC_DOUBLE_R_TLV("Vibra Master Volume",
+		AB8500_DADIGGAIN5, AB8500_DADIGGAIN6,
+		0, AB8500_DADIGGAINX_DAXGAIN_MAX, 1, dax_dig_gain_tlv),
+
+	/* HandsFree, Vibra */
+	SOC_SINGLE("ClassD High Pass Volume",
+		AB8500_CLASSDCONF3, AB8500_CLASSDCONF3_DITHHPGAIN,
+		AB8500_CLASSDCONF3_DITHHPGAIN_MAX, 0),
+	SOC_SINGLE("ClassD White Volume",
+		AB8500_CLASSDCONF3, AB8500_CLASSDCONF3_DITHWGAIN,
+		AB8500_CLASSDCONF3_DITHWGAIN_MAX, 0),
+
+	/* Mic 1, Mic 2, LineIn */
+	SOC_DOUBLE_R_TLV("Mic Master Volume",
+		AB8500_ADDIGGAIN3, AB8500_ADDIGGAIN4,
+		0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv),
+
+	/* Mic 1 */
+	SOC_SINGLE_TLV("Mic 1",
+		AB8500_ANAGAIN1,
+		AB8500_ANAGAINX_MICXGAIN,
+		AB8500_ANAGAINX_MICXGAIN_MAX, 0, mic_gain_tlv),
+	SOC_SINGLE("Mic 1 Low Power Switch",
+		AB8500_ANAGAIN1, AB8500_ANAGAINX_LOWPOWMICX,
+		1, 0),
+
+	/* Mic 2 */
+	SOC_DOUBLE("Mic High Pass Switch",
+		AB8500_ADFILTCONF,
+		AB8500_ADFILTCONF_AD3NH, AB8500_ADFILTCONF_AD4NH,
+		1, 1),
+	SOC_ENUM("Mic Mode", soc_enum_ad34voice),
+	SOC_ENUM("Mic Filter", soc_enum_dmic34sinc),
+	SOC_SINGLE_TLV("Mic 2",
+		AB8500_ANAGAIN2,
+		AB8500_ANAGAINX_MICXGAIN,
+		AB8500_ANAGAINX_MICXGAIN_MAX, 0, mic_gain_tlv),
+	SOC_SINGLE("Mic 2 Low Power Switch",
+		AB8500_ANAGAIN2, AB8500_ANAGAINX_LOWPOWMICX,
+		1, 0),
+
+	/* LineIn */
+	SOC_DOUBLE("LineIn High Pass Switch",
+		AB8500_ADFILTCONF,
+		AB8500_ADFILTCONF_AD1NH, AB8500_ADFILTCONF_AD2NH,
+		1, 1),
+	SOC_ENUM("LineIn Filter", soc_enum_dmic12sinc),
+	SOC_ENUM("LineIn Mode", soc_enum_ad12voice),
+	SOC_DOUBLE_R_TLV("LineIn Master Volume",
+		AB8500_ADDIGGAIN1, AB8500_ADDIGGAIN2,
+		0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv),
+	SOC_DOUBLE_TLV("LineIn",
+		AB8500_ANAGAIN4,
+		AB8500_ANAGAIN4_LINLGAIN, AB8500_ANAGAIN4_LINRGAIN,
+		AB8500_ANAGAIN4_LINXGAIN_MAX, 0, lin_gain_tlv),
+	SOC_DOUBLE_R_TLV("LineIn to Headset Volume",
+		AB8500_DIGLINHSLGAIN, AB8500_DIGLINHSRGAIN,
+		AB8500_DIGLINHSXGAIN_LINTOHSXGAIN,
+		AB8500_DIGLINHSXGAIN_LINTOHSXGAIN_MAX,
+		1, lin2hs_gain_tlv),
+
+	/* DMic */
+	SOC_ENUM("DMic Filter", soc_enum_dmic56sinc),
+	SOC_DOUBLE_R_TLV("DMic Master Volume",
+		AB8500_ADDIGGAIN5, AB8500_ADDIGGAIN6,
+		0, AB8500_ADDIGGAINX_ADXGAIN_MAX, 1, adx_dig_gain_tlv),
+
+	/* Digital gains */
+	SOC_ENUM("Digital Gain Fade Speed", soc_enum_fadespeed),
+
+	/* Analog loopback */
+	SOC_DOUBLE_R_TLV("Analog Loopback Volume",
+		AB8500_ADDIGLOOPGAIN1, AB8500_ADDIGLOOPGAIN2,
+		0, AB8500_ADDIGLOOPGAINX_ADXLBGAIN_MAX, 1, dax_dig_gain_tlv),
+
+	/* Digital interface - DA from slot mapping */
+	SOC_ENUM("Digital Interface DA 1 From Slot Map", soc_enum_da1slotmap),
+	SOC_ENUM("Digital Interface DA 2 From Slot Map", soc_enum_da2slotmap),
+	SOC_ENUM("Digital Interface DA 3 From Slot Map", soc_enum_da3slotmap),
+	SOC_ENUM("Digital Interface DA 4 From Slot Map", soc_enum_da4slotmap),
+	SOC_ENUM("Digital Interface DA 5 From Slot Map", soc_enum_da5slotmap),
+	SOC_ENUM("Digital Interface DA 6 From Slot Map", soc_enum_da6slotmap),
+	SOC_ENUM("Digital Interface DA 7 From Slot Map", soc_enum_da7slotmap),
+	SOC_ENUM("Digital Interface DA 8 From Slot Map", soc_enum_da8slotmap),
+
+	/* Digital interface - AD to slot mapping */
+	SOC_ENUM("Digital Interface AD To Slot 0 Map", soc_enum_adslot0map),
+	SOC_ENUM("Digital Interface AD To Slot 1 Map", soc_enum_adslot1map),
+	SOC_ENUM("Digital Interface AD To Slot 2 Map", soc_enum_adslot2map),
+	SOC_ENUM("Digital Interface AD To Slot 3 Map", soc_enum_adslot3map),
+	SOC_ENUM("Digital Interface AD To Slot 4 Map", soc_enum_adslot4map),
+	SOC_ENUM("Digital Interface AD To Slot 5 Map", soc_enum_adslot5map),
+	SOC_ENUM("Digital Interface AD To Slot 6 Map", soc_enum_adslot6map),
+	SOC_ENUM("Digital Interface AD To Slot 7 Map", soc_enum_adslot7map),
+	SOC_ENUM("Digital Interface AD To Slot 8 Map", soc_enum_adslot8map),
+	SOC_ENUM("Digital Interface AD To Slot 9 Map", soc_enum_adslot9map),
+	SOC_ENUM("Digital Interface AD To Slot 10 Map", soc_enum_adslot10map),
+	SOC_ENUM("Digital Interface AD To Slot 11 Map", soc_enum_adslot11map),
+	SOC_ENUM("Digital Interface AD To Slot 12 Map", soc_enum_adslot12map),
+	SOC_ENUM("Digital Interface AD To Slot 13 Map", soc_enum_adslot13map),
+	SOC_ENUM("Digital Interface AD To Slot 14 Map", soc_enum_adslot14map),
+	SOC_ENUM("Digital Interface AD To Slot 15 Map", soc_enum_adslot15map),
+	SOC_ENUM("Digital Interface AD To Slot 16 Map", soc_enum_adslot16map),
+	SOC_ENUM("Digital Interface AD To Slot 17 Map", soc_enum_adslot17map),
+	SOC_ENUM("Digital Interface AD To Slot 18 Map", soc_enum_adslot18map),
+	SOC_ENUM("Digital Interface AD To Slot 19 Map", soc_enum_adslot19map),
+	SOC_ENUM("Digital Interface AD To Slot 20 Map", soc_enum_adslot20map),
+	SOC_ENUM("Digital Interface AD To Slot 21 Map", soc_enum_adslot21map),
+	SOC_ENUM("Digital Interface AD To Slot 22 Map", soc_enum_adslot22map),
+	SOC_ENUM("Digital Interface AD To Slot 23 Map", soc_enum_adslot23map),
+	SOC_ENUM("Digital Interface AD To Slot 24 Map", soc_enum_adslot24map),
+	SOC_ENUM("Digital Interface AD To Slot 25 Map", soc_enum_adslot25map),
+	SOC_ENUM("Digital Interface AD To Slot 26 Map", soc_enum_adslot26map),
+	SOC_ENUM("Digital Interface AD To Slot 27 Map", soc_enum_adslot27map),
+	SOC_ENUM("Digital Interface AD To Slot 28 Map", soc_enum_adslot28map),
+	SOC_ENUM("Digital Interface AD To Slot 29 Map", soc_enum_adslot29map),
+	SOC_ENUM("Digital Interface AD To Slot 30 Map", soc_enum_adslot30map),
+	SOC_ENUM("Digital Interface AD To Slot 31 Map", soc_enum_adslot31map),
+
+	/* Digital interface - Loopback */
+	SOC_SINGLE("Digital Interface AD 1 Loopback Switch",
+		AB8500_DASLOTCONF1, AB8500_DASLOTCONF1_DAI7TOADO1,
+		1, 0),
+	SOC_SINGLE("Digital Interface AD 2 Loopback Switch",
+		AB8500_DASLOTCONF2, AB8500_DASLOTCONF2_DAI8TOADO2,
+		1, 0),
+	SOC_SINGLE("Digital Interface AD 3 Loopback Switch",
+		AB8500_DASLOTCONF3, AB8500_DASLOTCONF3_DAI7TOADO3,
+		1, 0),
+	SOC_SINGLE("Digital Interface AD 4 Loopback Switch",
+		AB8500_DASLOTCONF4, AB8500_DASLOTCONF4_DAI8TOADO4,
+		1, 0),
+	SOC_SINGLE("Digital Interface AD 5 Loopback Switch",
+		AB8500_DASLOTCONF5, AB8500_DASLOTCONF5_DAI7TOADO5,
+		1, 0),
+	SOC_SINGLE("Digital Interface AD 6 Loopback Switch",
+		AB8500_DASLOTCONF6, AB8500_DASLOTCONF6_DAI8TOADO6,
+		1, 0),
+	SOC_SINGLE("Digital Interface AD 7 Loopback Switch",
+		AB8500_DASLOTCONF7, AB8500_DASLOTCONF7_DAI8TOADO7,
+		1, 0),
+	SOC_SINGLE("Digital Interface AD 8 Loopback Switch",
+		AB8500_DASLOTCONF8, AB8500_DASLOTCONF8_DAI7TOADO8,
+		1, 0),
+
+	/* Digital interface - Burst FIFO */
+	SOC_SINGLE("Digital Interface 0 FIFO Enable Switch",
+		AB8500_DIGIFCONF3, AB8500_DIGIFCONF3_IF0BFIFOEN,
+		1, 0),
+	SOC_ENUM("Burst FIFO Mask", soc_enum_bfifomask),
+	SOC_ENUM("Burst FIFO Bit-clock Frequency", soc_enum_bfifo19m2),
+	SOC_SINGLE("Burst FIFO Threshold",
+		AB8500_FIFOCONF1, AB8500_FIFOCONF1_BFIFOINT_SHIFT,
+		AB8500_FIFOCONF1_BFIFOINT_MAX, 0),
+	SOC_SINGLE("Burst FIFO Length",
+		AB8500_FIFOCONF2, AB8500_FIFOCONF2_BFIFOTX_SHIFT,
+		AB8500_FIFOCONF2_BFIFOTX_MAX, 0),
+	SOC_SINGLE("Burst FIFO EOS Extra Slots",
+		AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFOEXSL_SHIFT,
+		AB8500_FIFOCONF3_BFIFOEXSL_MAX, 0),
+	SOC_SINGLE("Burst FIFO FS Extra Bit-clocks",
+		AB8500_FIFOCONF3, AB8500_FIFOCONF3_PREBITCLK0_SHIFT,
+		AB8500_FIFOCONF3_PREBITCLK0_MAX, 0),
+	SOC_ENUM("Burst FIFO Interface Mode", soc_enum_bfifomast),
+
+	SOC_SINGLE("Burst FIFO Interface Switch",
+		AB8500_FIFOCONF3, AB8500_FIFOCONF3_BFIFORUN_SHIFT,
+		1, 0),
+	SOC_SINGLE("Burst FIFO Switch Frame Number",
+		AB8500_FIFOCONF4, AB8500_FIFOCONF4_BFIFOFRAMSW_SHIFT,
+		AB8500_FIFOCONF4_BFIFOFRAMSW_MAX, 0),
+	SOC_SINGLE("Burst FIFO Wake Up Delay",
+		AB8500_FIFOCONF5, AB8500_FIFOCONF5_BFIFOWAKEUP_SHIFT,
+		AB8500_FIFOCONF5_BFIFOWAKEUP_MAX, 0),
+	SOC_SINGLE("Burst FIFO Samples In FIFO",
+		AB8500_FIFOCONF6, AB8500_FIFOCONF6_BFIFOSAMPLE_SHIFT,
+		AB8500_FIFOCONF6_BFIFOSAMPLE_MAX, 0),
+
+	/* ANC */
+	SOC_ENUM_EXT("ANC Status", soc_enum_ancstate,
+		anc_status_control_get, anc_status_control_put),
+	SOC_SINGLE_XR_SX("ANC Warp Delay Shift",
+		AB8500_ANCCONF2, 1, AB8500_ANCCONF2_SHIFT,
+		AB8500_ANCCONF2_MIN, AB8500_ANCCONF2_MAX, 0),
+	SOC_SINGLE_XR_SX("ANC FIR Output Shift",
+		AB8500_ANCCONF3, 1, AB8500_ANCCONF3_SHIFT,
+		AB8500_ANCCONF3_MIN, AB8500_ANCCONF3_MAX, 0),
+	SOC_SINGLE_XR_SX("ANC IIR Output Shift",
+		AB8500_ANCCONF4, 1, AB8500_ANCCONF4_SHIFT,
+		AB8500_ANCCONF4_MIN, AB8500_ANCCONF4_MAX, 0),
+	SOC_SINGLE_XR_SX("ANC Warp Delay",
+		AB8500_ANCCONF9, 2, AB8500_ANC_WARP_DELAY_SHIFT,
+		AB8500_ANC_WARP_DELAY_MIN, AB8500_ANC_WARP_DELAY_MAX, 0),
+
+	/* Sidetone */
+	SOC_ENUM_EXT("Sidetone Status", soc_enum_sidstate,
+		sid_status_control_get, sid_status_control_put),
+	SOC_SINGLE_STROBE("Sidetone Reset",
+		AB8500_SIDFIRADR, AB8500_SIDFIRADR_FIRSIDSET, 0),
+};
+
+static struct snd_kcontrol_new ab8500_filter_controls[] = {
+	AB8500_FILTER_CONTROL("ANC FIR Coefficients", AB8500_ANC_FIR_COEFFS,
+		AB8500_ANC_FIR_COEFF_MIN, AB8500_ANC_FIR_COEFF_MAX),
+	AB8500_FILTER_CONTROL("ANC IIR Coefficients", AB8500_ANC_IIR_COEFFS,
+		AB8500_ANC_IIR_COEFF_MIN, AB8500_ANC_IIR_COEFF_MAX),
+	AB8500_FILTER_CONTROL("Sidetone FIR Coefficients",
+			AB8500_SID_FIR_COEFFS, AB8500_SID_FIR_COEFF_MIN,
+			AB8500_SID_FIR_COEFF_MAX)
+};
+enum ab8500_filter {
+	AB8500_FILTER_ANC_FIR = 0,
+	AB8500_FILTER_ANC_IIR = 1,
+	AB8500_FILTER_SID_FIR = 2,
+};
+
+/*
+ * Extended interface for codec-driver
+ */
+
+static int ab8500_audio_init_audioblock(struct snd_soc_codec *codec)
+{
+	int status;
+
+	dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+
+	/* Reset audio-registers and disable 32kHz-clock output 2 */
+	status = ab8500_sysctrl_write(AB8500_STW4500CTRL3,
+				AB8500_STW4500CTRL3_CLK32KOUT2DIS |
+					AB8500_STW4500CTRL3_RESETAUDN,
+				AB8500_STW4500CTRL3_RESETAUDN);
+	if (status < 0)
+		return status;
+
+	return 0;
+}
+
+static int ab8500_audio_setup_mics(struct snd_soc_codec *codec,
+			struct amic_settings *amics)
+{
+	u8 value8;
+	unsigned int value;
+	int status;
+	const struct snd_soc_dapm_route *route;
+
+	dev_dbg(codec->dev, "%s: Enter.\n", __func__);
+
+	/* Set DMic-clocks to outputs */
+	status = abx500_get_register_interruptible(codec->dev, (u8)AB8500_MISC,
+						(u8)AB8500_GPIO_DIR4_REG,
+						&value8);
+	if (status < 0)
+		return status;
+	value = value8 | GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT |
+		GPIO31_DIR_OUTPUT;
+	status = abx500_set_register_interruptible(codec->dev,
+						(u8)AB8500_MISC,
+						(u8)AB8500_GPIO_DIR4_REG,
+						value);
+	if (status < 0)
+		return status;
+
+	/* Attach regulators to AMic DAPM-paths */
+	dev_dbg(codec->dev, "%s: Mic 1a regulator: %s\n", __func__,
+		amic_micbias_str(amics->mic1a_micbias));
+	route = &ab8500_dapm_routes_mic1a_vamicx[amics->mic1a_micbias];
+	status = snd_soc_dapm_add_routes(&codec->dapm, route, 1);
+	dev_dbg(codec->dev, "%s: Mic 1b regulator: %s\n", __func__,
+		amic_micbias_str(amics->mic1b_micbias));
+	route = &ab8500_dapm_routes_mic1b_vamicx[amics->mic1b_micbias];
+	status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1);
+	dev_dbg(codec->dev, "%s: Mic 2 regulator: %s\n", __func__,
+		amic_micbias_str(amics->mic2_micbias));
+	route = &ab8500_dapm_routes_mic2_vamicx[amics->mic2_micbias];
+	status |= snd_soc_dapm_add_routes(&codec->dapm, route, 1);
+	if (status < 0) {
+		dev_err(codec->dev,
+			"%s: Failed to add AMic-regulator DAPM-routes (%d).\n",
+			__func__, status);
+		return status;
+	}
+
+	/* Set AMic-configuration */
+	dev_dbg(codec->dev, "%s: Mic 1 mic-type: %s\n", __func__,
+		amic_type_str(amics->mic1_type));
+	snd_soc_update_bits(codec, AB8500_ANAGAIN1, AB8500_ANAGAINX_ENSEMICX,
+			amics->mic1_type == AMIC_TYPE_DIFFERENTIAL ?
+				0 : AB8500_ANAGAINX_ENSEMICX);
+	dev_dbg(codec->dev, "%s: Mic 2 mic-type: %s\n", __func__,
+		amic_type_str(amics->mic2_type));
+	snd_soc_update_bits(codec, AB8500_ANAGAIN2, AB8500_ANAGAINX_ENSEMICX,
+			amics->mic2_type == AMIC_TYPE_DIFFERENTIAL ?
+				0 : AB8500_ANAGAINX_ENSEMICX);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ab8500_audio_setup_mics);
+
+static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec,
+				enum ear_cm_voltage ear_cmv)
+{
+	char *cmv_str;
+
+	switch (ear_cmv) {
+	case EAR_CMV_0_95V:
+		cmv_str = "0.95V";
+		break;
+	case EAR_CMV_1_10V:
+		cmv_str = "1.10V";
+		break;
+	case EAR_CMV_1_27V:
+		cmv_str = "1.27V";
+		break;
+	case EAR_CMV_1_58V:
+		cmv_str = "1.58V";
+		break;
+	default:
+		dev_err(codec->dev,
+			"%s: Unknown earpiece CM-voltage (%d)!\n",
+			__func__, (int)ear_cmv);
+		return -EINVAL;
+	}
+	dev_dbg(codec->dev, "%s: Earpiece CM-voltage: %s\n", __func__,
+		cmv_str);
+	snd_soc_update_bits(codec, AB8500_ANACONF1, AB8500_ANACONF1_EARSELCM,
+			ear_cmv);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ab8500_audio_set_ear_cmv);
+
+static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai,
+				unsigned int delay)
+{
+	unsigned int mask, val;
+	struct snd_soc_codec *codec = dai->codec;
+
+	mask = BIT(AB8500_DIGIFCONF2_IF0DEL);
+	val = 0;
+
+	switch (delay) {
+	case 0:
+		break;
+	case 1:
+		val |= BIT(AB8500_DIGIFCONF2_IF0DEL);
+		break;
+	default:
+		dev_err(dai->codec->dev,
+			"%s: ERROR: Unsupported bit-delay (0x%x)!\n",
+			__func__, delay);
+		return -EINVAL;
+	}
+
+	dev_dbg(dai->codec->dev, "%s: IF0 Bit-delay: %d bits.\n",
+		__func__, delay);
+	snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+
+	return 0;
+}
+
+/* Gates clocking according format mask */
+static int ab8500_codec_set_dai_clock_gate(struct snd_soc_codec *codec,
+					unsigned int fmt)
+{
+	unsigned int mask;
+	unsigned int val;
+
+	mask = BIT(AB8500_DIGIFCONF1_ENMASTGEN) |
+			BIT(AB8500_DIGIFCONF1_ENFSBITCLK0);
+
+	val = BIT(AB8500_DIGIFCONF1_ENMASTGEN);
+
+	switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+	case SND_SOC_DAIFMT_CONT: /* continuous clock */
+		dev_dbg(codec->dev, "%s: IF0 Clock is continuous.\n",
+			__func__);
+		val |= BIT(AB8500_DIGIFCONF1_ENFSBITCLK0);
+		break;
+	case SND_SOC_DAIFMT_GATED: /* clock is gated */
+		dev_dbg(codec->dev, "%s: IF0 Clock is gated.\n",
+			__func__);
+		break;
+	default:
+		dev_err(codec->dev,
+			"%s: ERROR: Unsupported clock mask (0x%x)!\n",
+			__func__, fmt & SND_SOC_DAIFMT_CLOCK_MASK);
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
+
+	return 0;
+}
+
+static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	unsigned int mask;
+	unsigned int val;
+	struct snd_soc_codec *codec = dai->codec;
+	int status;
+
+	dev_dbg(codec->dev, "%s: Enter (fmt = 0x%x)\n", __func__, fmt);
+
+	mask = BIT(AB8500_DIGIFCONF3_IF1DATOIF0AD) |
+			BIT(AB8500_DIGIFCONF3_IF1CLKTOIF0CLK) |
+			BIT(AB8500_DIGIFCONF3_IF0BFIFOEN) |
+			BIT(AB8500_DIGIFCONF3_IF0MASTER);
+	val = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & FRM master */
+		dev_dbg(dai->codec->dev,
+			"%s: IF0 Master-mode: AB8500 master.\n", __func__);
+		val |= BIT(AB8500_DIGIFCONF3_IF0MASTER);
+		break;
+	case SND_SOC_DAIFMT_CBS_CFS: /* codec clk & FRM slave */
+		dev_dbg(dai->codec->dev,
+			"%s: IF0 Master-mode: AB8500 slave.\n", __func__);
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM: /* codec clk slave & FRM master */
+	case SND_SOC_DAIFMT_CBM_CFS: /* codec clk master & frame slave */
+		dev_err(dai->codec->dev,
+			"%s: ERROR: The device is either a master or a slave.\n",
+			__func__);
+	default:
+		dev_err(dai->codec->dev,
+			"%s: ERROR: Unsupporter master mask 0x%x\n",
+			__func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+		return -EINVAL;
+		break;
+	}
+
+	snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val);
+
+	/* Set clock gating */
+	status = ab8500_codec_set_dai_clock_gate(codec, fmt);
+	if (status) {
+		dev_err(dai->codec->dev,
+			"%s: ERRROR: Failed to set clock gate (%d).\n",
+			__func__, status);
+		return status;
+	}
+
+	/* Setting data transfer format */
+
+	mask = BIT(AB8500_DIGIFCONF2_IF0FORMAT0) |
+		BIT(AB8500_DIGIFCONF2_IF0FORMAT1) |
+		BIT(AB8500_DIGIFCONF2_FSYNC0P) |
+		BIT(AB8500_DIGIFCONF2_BITCLK0P);
+	val = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S: /* I2S mode */
+		dev_dbg(dai->codec->dev, "%s: IF0 Protocol: I2S\n", __func__);
+		val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT1);
+		ab8500_audio_set_bit_delay(dai, 0);
+		break;
+
+	case SND_SOC_DAIFMT_DSP_A: /* L data MSB after FRM LRC */
+		dev_dbg(dai->codec->dev,
+			"%s: IF0 Protocol: DSP A (TDM)\n", __func__);
+		val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0);
+		ab8500_audio_set_bit_delay(dai, 1);
+		break;
+
+	case SND_SOC_DAIFMT_DSP_B: /* L data MSB during FRM LRC */
+		dev_dbg(dai->codec->dev,
+			"%s: IF0 Protocol: DSP B (TDM)\n", __func__);
+		val |= BIT(AB8500_DIGIFCONF2_IF0FORMAT0);
+		ab8500_audio_set_bit_delay(dai, 0);
+		break;
+
+	default:
+		dev_err(dai->codec->dev,
+			"%s: ERROR: Unsupported format (0x%x)!\n",
+			__func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF: /* normal bit clock + frame */
+		dev_dbg(dai->codec->dev,
+			"%s: IF0: Normal bit clock, normal frame\n",
+			__func__);
+		break;
+	case SND_SOC_DAIFMT_NB_IF: /* normal BCLK + inv FRM */
+		dev_dbg(dai->codec->dev,
+			"%s: IF0: Normal bit clock, inverted frame\n",
+			__func__);
+		val |= BIT(AB8500_DIGIFCONF2_FSYNC0P);
+		break;
+	case SND_SOC_DAIFMT_IB_NF: /* invert BCLK + nor FRM */
+		dev_dbg(dai->codec->dev,
+			"%s: IF0: Inverted bit clock, normal frame\n",
+			__func__);
+		val |= BIT(AB8500_DIGIFCONF2_BITCLK0P);
+		break;
+	case SND_SOC_DAIFMT_IB_IF: /* invert BCLK + FRM */
+		dev_dbg(dai->codec->dev,
+			"%s: IF0: Inverted bit clock, inverted frame\n",
+			__func__);
+		val |= BIT(AB8500_DIGIFCONF2_FSYNC0P);
+		val |= BIT(AB8500_DIGIFCONF2_BITCLK0P);
+		break;
+	default:
+		dev_err(dai->codec->dev,
+			"%s: ERROR: Unsupported INV mask 0x%x\n",
+			__func__, fmt & SND_SOC_DAIFMT_INV_MASK);
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+
+	return 0;
+}
+
+static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
+		unsigned int tx_mask, unsigned int rx_mask,
+		int slots, int slot_width)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	unsigned int val, mask, slots_active;
+
+	mask = BIT(AB8500_DIGIFCONF2_IF0WL0) |
+		BIT(AB8500_DIGIFCONF2_IF0WL1);
+	val = 0;
+
+	switch (slot_width) {
+	case 16:
+		break;
+	case 20:
+		val |= BIT(AB8500_DIGIFCONF2_IF0WL0);
+		break;
+	case 24:
+		val |= BIT(AB8500_DIGIFCONF2_IF0WL1);
+		break;
+	case 32:
+		val |= BIT(AB8500_DIGIFCONF2_IF0WL1) |
+			BIT(AB8500_DIGIFCONF2_IF0WL0);
+		break;
+	default:
+		dev_err(dai->codec->dev, "%s: Unsupported slot-width 0x%x\n",
+			__func__, slot_width);
+		return -EINVAL;
+	}
+
+	dev_dbg(dai->codec->dev, "%s: IF0 slot-width: %d bits.\n",
+		__func__, slot_width);
+	snd_soc_update_bits(codec, AB8500_DIGIFCONF2, mask, val);
+
+	/* Setup TDM clocking according to slot count */
+	dev_dbg(dai->codec->dev, "%s: Slots, total: %d\n", __func__, slots);
+	mask = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
+			BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
+	switch (slots) {
+	case 2:
+		val = AB8500_MASK_NONE;
+		break;
+	case 4:
+		val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0);
+		break;
+	case 8:
+		val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
+		break;
+	case 16:
+		val = BIT(AB8500_DIGIFCONF1_IF0BITCLKOS0) |
+			BIT(AB8500_DIGIFCONF1_IF0BITCLKOS1);
+		break;
+	default:
+		dev_err(dai->codec->dev,
+			"%s: ERROR: Unsupported number of slots (%d)!\n",
+			__func__, slots);
+		return -EINVAL;
+	}
+	snd_soc_update_bits(codec, AB8500_DIGIFCONF1, mask, val);
+
+	/* Setup TDM DA according to active tx slots */
+	mask = AB8500_DASLOTCONFX_SLTODAX_MASK;
+	slots_active = hweight32(tx_mask);
+	dev_dbg(dai->codec->dev, "%s: Slots, active, TX: %d\n", __func__,
+		slots_active);
+	switch (slots_active) {
+	case 0:
+		break;
+	case 1:
+		/* Slot 9 -> DA_IN1 & DA_IN3 */
+		snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 11);
+		snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 11);
+		snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11);
+		snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11);
+		break;
+	case 2:
+		/* Slot 9 -> DA_IN1 & DA_IN3, Slot 11 -> DA_IN2 & DA_IN4 */
+		snd_soc_update_bits(codec, AB8500_DASLOTCONF1, mask, 9);
+		snd_soc_update_bits(codec, AB8500_DASLOTCONF3, mask, 9);
+		snd_soc_update_bits(codec, AB8500_DASLOTCONF2, mask, 11);
+		snd_soc_update_bits(codec, AB8500_DASLOTCONF4, mask, 11);
+
+		break;
+	case 8:
+		dev_dbg(dai->codec->dev,
+			"%s: In 8-channel mode DA-from-slot mapping is set manually.",
+			__func__);
+		break;
+	default:
+		dev_err(dai->codec->dev,
+			"%s: Unsupported number of active TX-slots (%d)!\n",
+			__func__, slots_active);
+		return -EINVAL;
+	}
+
+	/* Setup TDM AD according to active RX-slots */
+	slots_active = hweight32(rx_mask);
+	dev_dbg(dai->codec->dev, "%s: Slots, active, RX: %d\n", __func__,
+		slots_active);
+	switch (slots_active) {
+	case 0:
+		break;
+	case 1:
+		/* AD_OUT3 -> slot 0 & 1 */
+		snd_soc_update_bits(codec, AB8500_ADSLOTSEL1, AB8500_MASK_ALL,
+				AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN |
+				AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD);
+		break;
+	case 2:
+		/* AD_OUT3 -> slot 0, AD_OUT2 -> slot 1 */
+		snd_soc_update_bits(codec,
+				AB8500_ADSLOTSEL1,
+				AB8500_MASK_ALL,
+				AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN |
+				AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD);
+		break;
+	case 8:
+		dev_dbg(dai->codec->dev,
+			"%s: In 8-channel mode AD-to-slot mapping is set manually.",
+			__func__);
+		break;
+	default:
+		dev_err(dai->codec->dev,
+			"%s: Unsupported number of active RX-slots (%d)!\n",
+			__func__, slots_active);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct snd_soc_dai_driver ab8500_codec_dai[] = {
+	{
+		.name = "ab8500-codec-dai.0",
+		.id = 0,
+		.playback = {
+			.stream_name = "ab8500_0p",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = AB8500_SUPPORTED_RATE,
+			.formats = AB8500_SUPPORTED_FMT,
+		},
+		.ops = (struct snd_soc_dai_ops[]) {
+			{
+				.set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
+				.set_fmt = ab8500_codec_set_dai_fmt,
+			}
+		},
+		.symmetric_rates = 1
+	},
+	{
+		.name = "ab8500-codec-dai.1",
+		.id = 1,
+		.capture = {
+			.stream_name = "ab8500_0c",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = AB8500_SUPPORTED_RATE,
+			.formats = AB8500_SUPPORTED_FMT,
+		},
+		.ops = (struct snd_soc_dai_ops[]) {
+			{
+				.set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
+				.set_fmt = ab8500_codec_set_dai_fmt,
+			}
+		},
+		.symmetric_rates = 1
+	}
+};
+
+static int ab8500_codec_probe(struct snd_soc_codec *codec)
+{
+	struct device *dev = codec->dev;
+	struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev);
+	struct ab8500_platform_data *pdata;
+	struct filter_control *fc;
+	int status;
+
+	dev_dbg(dev, "%s: Enter.\n", __func__);
+
+	/* Setup AB8500 according to board-settings */
+	pdata = (struct ab8500_platform_data *)dev_get_platdata(dev->parent);
+	status = ab8500_audio_setup_mics(codec, &pdata->codec->amics);
+	if (status < 0) {
+		pr_err("%s: Failed to setup mics (%d)!\n", __func__, status);
+		return status;
+	}
+	status = ab8500_audio_set_ear_cmv(codec, pdata->codec->ear_cmv);
+	if (status < 0) {
+		pr_err("%s: Failed to set earpiece CM-voltage (%d)!\n",
+			__func__, status);
+		return status;
+	}
+
+	status = ab8500_audio_init_audioblock(codec);
+	if (status < 0) {
+		dev_err(dev, "%s: failed to init audio-block (%d)!\n",
+			__func__, status);
+		return status;
+	}
+
+	/* Override HW-defaults */
+	ab8500_codec_write_reg(codec,
+				AB8500_ANACONF5,
+				BIT(AB8500_ANACONF5_HSAUTOEN));
+	ab8500_codec_write_reg(codec,
+				AB8500_SHORTCIRCONF,
+				BIT(AB8500_SHORTCIRCONF_HSZCDDIS));
+
+	/* Add filter controls */
+	status = snd_soc_add_codec_controls(codec, ab8500_filter_controls,
+				ARRAY_SIZE(ab8500_filter_controls));
+	if (status < 0) {
+		dev_err(dev,
+			"%s: failed to add ab8500 filter controls (%d).\n",
+			__func__, status);
+		return status;
+	}
+	fc = (struct filter_control *)
+		&ab8500_filter_controls[AB8500_FILTER_ANC_FIR].private_value;
+	drvdata->anc_fir_values = (long *)fc->value;
+	fc = (struct filter_control *)
+		&ab8500_filter_controls[AB8500_FILTER_ANC_IIR].private_value;
+	drvdata->anc_iir_values = (long *)fc->value;
+	fc = (struct filter_control *)
+		&ab8500_filter_controls[AB8500_FILTER_SID_FIR].private_value;
+	drvdata->sid_fir_values = (long *)fc->value;
+
+	(void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
+
+	mutex_init(&drvdata->anc_lock);
+
+	return status;
+}
+
+static struct snd_soc_codec_driver ab8500_codec_driver = {
+	.probe =		ab8500_codec_probe,
+	.read =			ab8500_codec_read_reg,
+	.write =		ab8500_codec_write_reg,
+	.reg_word_size =	sizeof(u8),
+	.controls =		ab8500_ctrls,
+	.num_controls =		ARRAY_SIZE(ab8500_ctrls),
+	.dapm_widgets =		ab8500_dapm_widgets,
+	.num_dapm_widgets =	ARRAY_SIZE(ab8500_dapm_widgets),
+	.dapm_routes =		ab8500_dapm_routes,
+	.num_dapm_routes =	ARRAY_SIZE(ab8500_dapm_routes),
+};
+
+static int __devinit ab8500_codec_driver_probe(struct platform_device *pdev)
+{
+	int status;
+	struct ab8500_codec_drvdata *drvdata;
+
+	dev_dbg(&pdev->dev, "%s: Enter.\n", __func__);
+
+	/* Create driver private-data struct */
+	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct ab8500_codec_drvdata),
+			GFP_KERNEL);
+	drvdata->sid_status = SID_UNCONFIGURED;
+	drvdata->anc_status = ANC_UNCONFIGURED;
+	dev_set_drvdata(&pdev->dev, drvdata);
+
+	dev_dbg(&pdev->dev, "%s: Register codec.\n", __func__);
+	status = snd_soc_register_codec(&pdev->dev, &ab8500_codec_driver,
+				ab8500_codec_dai,
+				ARRAY_SIZE(ab8500_codec_dai));
+	if (status < 0)
+		dev_err(&pdev->dev,
+			"%s: Error: Failed to register codec (%d).\n",
+			__func__, status);
+
+	return status;
+}
+
+static int __devexit ab8500_codec_driver_remove(struct platform_device *pdev)
+{
+	dev_info(&pdev->dev, "%s Enter.\n", __func__);
+
+	snd_soc_unregister_codec(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver ab8500_codec_platform_driver = {
+	.driver	= {
+		.name	= "ab8500-codec",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ab8500_codec_driver_probe,
+	.remove		= __devexit_p(ab8500_codec_driver_remove),
+	.suspend	= NULL,
+	.resume		= NULL,
+};
+module_platform_driver(ab8500_codec_platform_driver);
+
+MODULE_LICENSE("GPL v2");

+ 590 - 0
sound/soc/codecs/ab8500-codec.h

@@ -0,0 +1,590 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2012
+ *
+ * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
+ *         Kristoffer Karlsson <kristoffer.karlsson@stericsson.com>,
+ *         Roger Nilsson <roger.xr.nilsson@stericsson.com>,
+ *         for ST-Ericsson.
+ *
+ *         Based on the early work done by:
+ *         Mikko J. Lehto <mikko.lehto@symbio.com>,
+ *         Mikko Sarmanne <mikko.sarmanne@symbio.com>,
+ *         for ST-Ericsson.
+ *
+ * License terms:
+ *
+ * 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 AB8500_CODEC_REGISTERS_H
+#define AB8500_CODEC_REGISTERS_H
+
+#define AB8500_SUPPORTED_RATE			(SNDRV_PCM_RATE_48000)
+#define AB8500_SUPPORTED_FMT			(SNDRV_PCM_FMTBIT_S16_LE)
+
+/* AB8500 audio bank (0x0d) register definitions */
+
+#define AB8500_POWERUP				0x00
+#define AB8500_AUDSWRESET			0x01
+#define AB8500_ADPATHENA			0x02
+#define AB8500_DAPATHENA			0x03
+#define AB8500_ANACONF1				0x04
+#define AB8500_ANACONF2				0x05
+#define AB8500_DIGMICCONF			0x06
+#define AB8500_ANACONF3				0x07
+#define AB8500_ANACONF4				0x08
+#define AB8500_DAPATHCONF			0x09
+#define AB8500_MUTECONF				0x0A
+#define AB8500_SHORTCIRCONF			0x0B
+#define AB8500_ANACONF5				0x0C
+#define AB8500_ENVCPCONF			0x0D
+#define AB8500_SIGENVCONF			0x0E
+#define AB8500_PWMGENCONF1			0x0F
+#define AB8500_PWMGENCONF2			0x10
+#define AB8500_PWMGENCONF3			0x11
+#define AB8500_PWMGENCONF4			0x12
+#define AB8500_PWMGENCONF5			0x13
+#define AB8500_ANAGAIN1				0x14
+#define AB8500_ANAGAIN2				0x15
+#define AB8500_ANAGAIN3				0x16
+#define AB8500_ANAGAIN4				0x17
+#define AB8500_DIGLINHSLGAIN			0x18
+#define AB8500_DIGLINHSRGAIN			0x19
+#define AB8500_ADFILTCONF			0x1A
+#define AB8500_DIGIFCONF1			0x1B
+#define AB8500_DIGIFCONF2			0x1C
+#define AB8500_DIGIFCONF3			0x1D
+#define AB8500_DIGIFCONF4			0x1E
+#define AB8500_ADSLOTSEL1			0x1F
+#define AB8500_ADSLOTSEL2			0x20
+#define AB8500_ADSLOTSEL3			0x21
+#define AB8500_ADSLOTSEL4			0x22
+#define AB8500_ADSLOTSEL5			0x23
+#define AB8500_ADSLOTSEL6			0x24
+#define AB8500_ADSLOTSEL7			0x25
+#define AB8500_ADSLOTSEL8			0x26
+#define AB8500_ADSLOTSEL9			0x27
+#define AB8500_ADSLOTSEL10			0x28
+#define AB8500_ADSLOTSEL11			0x29
+#define AB8500_ADSLOTSEL12			0x2A
+#define AB8500_ADSLOTSEL13			0x2B
+#define AB8500_ADSLOTSEL14			0x2C
+#define AB8500_ADSLOTSEL15			0x2D
+#define AB8500_ADSLOTSEL16			0x2E
+#define AB8500_ADSLOTHIZCTRL1			0x2F
+#define AB8500_ADSLOTHIZCTRL2			0x30
+#define AB8500_ADSLOTHIZCTRL3			0x31
+#define AB8500_ADSLOTHIZCTRL4			0x32
+#define AB8500_DASLOTCONF1			0x33
+#define AB8500_DASLOTCONF2			0x34
+#define AB8500_DASLOTCONF3			0x35
+#define AB8500_DASLOTCONF4			0x36
+#define AB8500_DASLOTCONF5			0x37
+#define AB8500_DASLOTCONF6			0x38
+#define AB8500_DASLOTCONF7			0x39
+#define AB8500_DASLOTCONF8			0x3A
+#define AB8500_CLASSDCONF1			0x3B
+#define AB8500_CLASSDCONF2			0x3C
+#define AB8500_CLASSDCONF3			0x3D
+#define AB8500_DMICFILTCONF			0x3E
+#define AB8500_DIGMULTCONF1			0x3F
+#define AB8500_DIGMULTCONF2			0x40
+#define AB8500_ADDIGGAIN1			0x41
+#define AB8500_ADDIGGAIN2			0x42
+#define AB8500_ADDIGGAIN3			0x43
+#define AB8500_ADDIGGAIN4			0x44
+#define AB8500_ADDIGGAIN5			0x45
+#define AB8500_ADDIGGAIN6			0x46
+#define AB8500_DADIGGAIN1			0x47
+#define AB8500_DADIGGAIN2			0x48
+#define AB8500_DADIGGAIN3			0x49
+#define AB8500_DADIGGAIN4			0x4A
+#define AB8500_DADIGGAIN5			0x4B
+#define AB8500_DADIGGAIN6			0x4C
+#define AB8500_ADDIGLOOPGAIN1			0x4D
+#define AB8500_ADDIGLOOPGAIN2			0x4E
+#define AB8500_HSLEARDIGGAIN			0x4F
+#define AB8500_HSRDIGGAIN			0x50
+#define AB8500_SIDFIRGAIN1			0x51
+#define AB8500_SIDFIRGAIN2			0x52
+#define AB8500_ANCCONF1				0x53
+#define AB8500_ANCCONF2				0x54
+#define AB8500_ANCCONF3				0x55
+#define AB8500_ANCCONF4				0x56
+#define AB8500_ANCCONF5				0x57
+#define AB8500_ANCCONF6				0x58
+#define AB8500_ANCCONF7				0x59
+#define AB8500_ANCCONF8				0x5A
+#define AB8500_ANCCONF9				0x5B
+#define AB8500_ANCCONF10			0x5C
+#define AB8500_ANCCONF11			0x5D
+#define AB8500_ANCCONF12			0x5E
+#define AB8500_ANCCONF13			0x5F
+#define AB8500_ANCCONF14			0x60
+#define AB8500_SIDFIRADR			0x61
+#define AB8500_SIDFIRCOEF1			0x62
+#define AB8500_SIDFIRCOEF2			0x63
+#define AB8500_SIDFIRCONF			0x64
+#define AB8500_AUDINTMASK1			0x65
+#define AB8500_AUDINTSOURCE1			0x66
+#define AB8500_AUDINTMASK2			0x67
+#define AB8500_AUDINTSOURCE2			0x68
+#define AB8500_FIFOCONF1			0x69
+#define AB8500_FIFOCONF2			0x6A
+#define AB8500_FIFOCONF3			0x6B
+#define AB8500_FIFOCONF4			0x6C
+#define AB8500_FIFOCONF5			0x6D
+#define AB8500_FIFOCONF6			0x6E
+#define AB8500_AUDREV				0x6F
+
+#define AB8500_FIRST_REG			AB8500_POWERUP
+#define AB8500_LAST_REG				AB8500_AUDREV
+#define AB8500_CACHEREGNUM			(AB8500_LAST_REG + 1)
+
+#define AB8500_MASK_ALL				0xFF
+#define AB8500_MASK_NONE			0x00
+
+/* AB8500_POWERUP */
+#define AB8500_POWERUP_POWERUP			7
+#define AB8500_POWERUP_ENANA			3
+
+/* AB8500_AUDSWRESET */
+#define AB8500_AUDSWRESET_SWRESET		7
+
+/* AB8500_ADPATHENA */
+#define AB8500_ADPATHENA_ENAD12			7
+#define AB8500_ADPATHENA_ENAD34			5
+#define AB8500_ADPATHENA_ENAD5768		3
+
+/* AB8500_DAPATHENA */
+#define AB8500_DAPATHENA_ENDA1			7
+#define AB8500_DAPATHENA_ENDA2			6
+#define AB8500_DAPATHENA_ENDA3			5
+#define AB8500_DAPATHENA_ENDA4			4
+#define AB8500_DAPATHENA_ENDA5			3
+#define AB8500_DAPATHENA_ENDA6			2
+
+/* AB8500_ANACONF1 */
+#define AB8500_ANACONF1_HSLOWPOW		7
+#define AB8500_ANACONF1_DACLOWPOW1		6
+#define AB8500_ANACONF1_DACLOWPOW0		5
+#define AB8500_ANACONF1_EARDACLOWPOW		4
+#define AB8500_ANACONF1_EARSELCM		2
+#define AB8500_ANACONF1_HSHPEN			1
+#define AB8500_ANACONF1_EARDRVLOWPOW		0
+
+/* AB8500_ANACONF2 */
+#define AB8500_ANACONF2_ENMIC1			7
+#define AB8500_ANACONF2_ENMIC2			6
+#define AB8500_ANACONF2_ENLINL			5
+#define AB8500_ANACONF2_ENLINR			4
+#define AB8500_ANACONF2_MUTMIC1			3
+#define AB8500_ANACONF2_MUTMIC2			2
+#define AB8500_ANACONF2_MUTLINL			1
+#define AB8500_ANACONF2_MUTLINR			0
+
+/* AB8500_DIGMICCONF */
+#define AB8500_DIGMICCONF_ENDMIC1		7
+#define AB8500_DIGMICCONF_ENDMIC2		6
+#define AB8500_DIGMICCONF_ENDMIC3		5
+#define AB8500_DIGMICCONF_ENDMIC4		4
+#define AB8500_DIGMICCONF_ENDMIC5		3
+#define AB8500_DIGMICCONF_ENDMIC6		2
+#define AB8500_DIGMICCONF_HSFADSPEED		0
+
+/* AB8500_ANACONF3 */
+#define AB8500_ANACONF3_MIC1SEL			7
+#define AB8500_ANACONF3_LINRSEL			6
+#define AB8500_ANACONF3_ENDRVHSL		5
+#define AB8500_ANACONF3_ENDRVHSR		4
+#define AB8500_ANACONF3_ENADCMIC		2
+#define AB8500_ANACONF3_ENADCLINL		1
+#define AB8500_ANACONF3_ENADCLINR		0
+
+/* AB8500_ANACONF4 */
+#define AB8500_ANACONF4_DISPDVSS		7
+#define AB8500_ANACONF4_ENEAR			6
+#define AB8500_ANACONF4_ENHSL			5
+#define AB8500_ANACONF4_ENHSR			4
+#define AB8500_ANACONF4_ENHFL			3
+#define AB8500_ANACONF4_ENHFR			2
+#define AB8500_ANACONF4_ENVIB1			1
+#define AB8500_ANACONF4_ENVIB2			0
+
+/* AB8500_DAPATHCONF */
+#define AB8500_DAPATHCONF_ENDACEAR		6
+#define AB8500_DAPATHCONF_ENDACHSL		5
+#define AB8500_DAPATHCONF_ENDACHSR		4
+#define AB8500_DAPATHCONF_ENDACHFL		3
+#define AB8500_DAPATHCONF_ENDACHFR		2
+#define AB8500_DAPATHCONF_ENDACVIB1		1
+#define AB8500_DAPATHCONF_ENDACVIB2		0
+
+/* AB8500_MUTECONF */
+#define AB8500_MUTECONF_MUTEAR			6
+#define AB8500_MUTECONF_MUTHSL			5
+#define AB8500_MUTECONF_MUTHSR			4
+#define AB8500_MUTECONF_MUTDACEAR		2
+#define AB8500_MUTECONF_MUTDACHSL		1
+#define AB8500_MUTECONF_MUTDACHSR		0
+
+/* AB8500_SHORTCIRCONF */
+#define AB8500_SHORTCIRCONF_ENSHORTPWD		7
+#define AB8500_SHORTCIRCONF_EARSHORTDIS		6
+#define AB8500_SHORTCIRCONF_HSSHORTDIS		5
+#define AB8500_SHORTCIRCONF_HSPULLDEN		4
+#define AB8500_SHORTCIRCONF_HSOSCEN		2
+#define AB8500_SHORTCIRCONF_HSFADDIS		1
+#define AB8500_SHORTCIRCONF_HSZCDDIS		0
+/* Zero cross should be disabled */
+
+/* AB8500_ANACONF5 */
+#define AB8500_ANACONF5_ENCPHS			7
+#define AB8500_ANACONF5_HSLDACTOLOL		5
+#define AB8500_ANACONF5_HSRDACTOLOR		4
+#define AB8500_ANACONF5_ENLOL			3
+#define AB8500_ANACONF5_ENLOR			2
+#define AB8500_ANACONF5_HSAUTOEN		0
+
+/* AB8500_ENVCPCONF */
+#define AB8500_ENVCPCONF_ENVDETHTHRE		4
+#define AB8500_ENVCPCONF_ENVDETLTHRE		0
+#define AB8500_ENVCPCONF_ENVDETHTHRE_MAX	0x0F
+#define AB8500_ENVCPCONF_ENVDETLTHRE_MAX	0x0F
+
+/* AB8500_SIGENVCONF */
+#define AB8500_SIGENVCONF_CPLVEN		5
+#define AB8500_SIGENVCONF_ENVDETCPEN		4
+#define AB8500_SIGENVCONF_ENVDETTIME		0
+#define AB8500_SIGENVCONF_ENVDETTIME_MAX	0x0F
+
+/* AB8500_PWMGENCONF1 */
+#define AB8500_PWMGENCONF1_PWMTOVIB1		7
+#define AB8500_PWMGENCONF1_PWMTOVIB2		6
+#define AB8500_PWMGENCONF1_PWM1CTRL		5
+#define AB8500_PWMGENCONF1_PWM2CTRL		4
+#define AB8500_PWMGENCONF1_PWM1NCTRL		3
+#define AB8500_PWMGENCONF1_PWM1PCTRL		2
+#define AB8500_PWMGENCONF1_PWM2NCTRL		1
+#define AB8500_PWMGENCONF1_PWM2PCTRL		0
+
+/* AB8500_PWMGENCONF2 */
+/* AB8500_PWMGENCONF3 */
+/* AB8500_PWMGENCONF4 */
+/* AB8500_PWMGENCONF5 */
+#define AB8500_PWMGENCONFX_PWMVIBXPOL		7
+#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC	0
+#define AB8500_PWMGENCONFX_PWMVIBXDUTCYC_MAX	0x64
+
+/* AB8500_ANAGAIN1 */
+/* AB8500_ANAGAIN2 */
+#define AB8500_ANAGAINX_ENSEMICX		7
+#define AB8500_ANAGAINX_LOWPOWMICX		6
+#define AB8500_ANAGAINX_MICXGAIN		0
+#define AB8500_ANAGAINX_MICXGAIN_MAX		0x1F
+
+/* AB8500_ANAGAIN3 */
+#define AB8500_ANAGAIN3_HSLGAIN			4
+#define AB8500_ANAGAIN3_HSRGAIN			0
+#define AB8500_ANAGAIN3_HSXGAIN_MAX		0x0F
+
+/* AB8500_ANAGAIN4 */
+#define AB8500_ANAGAIN4_LINLGAIN		4
+#define AB8500_ANAGAIN4_LINRGAIN		0
+#define AB8500_ANAGAIN4_LINXGAIN_MAX		0x0F
+
+/* AB8500_DIGLINHSLGAIN */
+/* AB8500_DIGLINHSRGAIN */
+#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN	0
+#define AB8500_DIGLINHSXGAIN_LINTOHSXGAIN_MAX	0x13
+
+/* AB8500_ADFILTCONF */
+#define AB8500_ADFILTCONF_AD1NH			7
+#define AB8500_ADFILTCONF_AD2NH			6
+#define AB8500_ADFILTCONF_AD3NH			5
+#define AB8500_ADFILTCONF_AD4NH			4
+#define AB8500_ADFILTCONF_AD1VOICE		3
+#define AB8500_ADFILTCONF_AD2VOICE		2
+#define AB8500_ADFILTCONF_AD3VOICE		1
+#define AB8500_ADFILTCONF_AD4VOICE		0
+
+/* AB8500_DIGIFCONF1 */
+#define AB8500_DIGIFCONF1_ENMASTGEN		7
+#define AB8500_DIGIFCONF1_IF1BITCLKOS1		6
+#define AB8500_DIGIFCONF1_IF1BITCLKOS0		5
+#define AB8500_DIGIFCONF1_ENFSBITCLK1		4
+#define AB8500_DIGIFCONF1_IF0BITCLKOS1		2
+#define AB8500_DIGIFCONF1_IF0BITCLKOS0		1
+#define AB8500_DIGIFCONF1_ENFSBITCLK0		0
+
+/* AB8500_DIGIFCONF2 */
+#define AB8500_DIGIFCONF2_FSYNC0P		6
+#define AB8500_DIGIFCONF2_BITCLK0P		5
+#define AB8500_DIGIFCONF2_IF0DEL		4
+#define AB8500_DIGIFCONF2_IF0FORMAT1		3
+#define AB8500_DIGIFCONF2_IF0FORMAT0		2
+#define AB8500_DIGIFCONF2_IF0WL1		1
+#define AB8500_DIGIFCONF2_IF0WL0		0
+
+/* AB8500_DIGIFCONF3 */
+#define AB8500_DIGIFCONF3_IF0DATOIF1AD		7
+#define AB8500_DIGIFCONF3_IF0CLKTOIF1CLK	6
+#define AB8500_DIGIFCONF3_IF1MASTER		5
+#define AB8500_DIGIFCONF3_IF1DATOIF0AD		3
+#define AB8500_DIGIFCONF3_IF1CLKTOIF0CLK	2
+#define AB8500_DIGIFCONF3_IF0MASTER		1
+#define AB8500_DIGIFCONF3_IF0BFIFOEN		0
+
+/* AB8500_DIGIFCONF4 */
+#define AB8500_DIGIFCONF4_FSYNC1P		6
+#define AB8500_DIGIFCONF4_BITCLK1P		5
+#define AB8500_DIGIFCONF4_IF1DEL		4
+#define AB8500_DIGIFCONF4_IF1FORMAT1		3
+#define AB8500_DIGIFCONF4_IF1FORMAT0		2
+#define AB8500_DIGIFCONF4_IF1WL1		1
+#define AB8500_DIGIFCONF4_IF1WL0		0
+
+/* AB8500_ADSLOTSELX */
+#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_ODD	0x00
+#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_ODD	0x01
+#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_ODD	0x02
+#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_ODD	0x03
+#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_ODD	0x04
+#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_ODD	0x05
+#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_ODD	0x06
+#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_ODD	0x07
+#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_ODD	0x08
+#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_ODD	0x0F
+#define AB8500_ADSLOTSELX_AD_OUT1_TO_SLOT_EVEN	0x00
+#define AB8500_ADSLOTSELX_AD_OUT2_TO_SLOT_EVEN	0x10
+#define AB8500_ADSLOTSELX_AD_OUT3_TO_SLOT_EVEN	0x20
+#define AB8500_ADSLOTSELX_AD_OUT4_TO_SLOT_EVEN	0x30
+#define AB8500_ADSLOTSELX_AD_OUT5_TO_SLOT_EVEN	0x40
+#define AB8500_ADSLOTSELX_AD_OUT6_TO_SLOT_EVEN	0x50
+#define AB8500_ADSLOTSELX_AD_OUT7_TO_SLOT_EVEN	0x60
+#define AB8500_ADSLOTSELX_AD_OUT8_TO_SLOT_EVEN	0x70
+#define AB8500_ADSLOTSELX_ZEROES_TO_SLOT_EVEN	0x80
+#define AB8500_ADSLOTSELX_TRISTATE_TO_SLOT_EVEN	0xF0
+#define AB8500_ADSLOTSELX_EVEN_SHIFT		0
+#define AB8500_ADSLOTSELX_ODD_SHIFT		4
+
+/* AB8500_ADSLOTHIZCTRL1 */
+/* AB8500_ADSLOTHIZCTRL2 */
+/* AB8500_ADSLOTHIZCTRL3 */
+/* AB8500_ADSLOTHIZCTRL4 */
+/* AB8500_DASLOTCONF1 */
+#define AB8500_DASLOTCONF1_DA12VOICE		7
+#define AB8500_DASLOTCONF1_SWAPDA12_34		6
+#define AB8500_DASLOTCONF1_DAI7TOADO1		5
+
+/* AB8500_DASLOTCONF2 */
+#define AB8500_DASLOTCONF2_DAI8TOADO2		5
+
+/* AB8500_DASLOTCONF3 */
+#define AB8500_DASLOTCONF3_DA34VOICE		7
+#define AB8500_DASLOTCONF3_DAI7TOADO3		5
+
+/* AB8500_DASLOTCONF4 */
+#define AB8500_DASLOTCONF4_DAI8TOADO4		5
+
+/* AB8500_DASLOTCONF5 */
+#define AB8500_DASLOTCONF5_DA56VOICE		7
+#define AB8500_DASLOTCONF5_DAI7TOADO5		5
+
+/* AB8500_DASLOTCONF6 */
+#define AB8500_DASLOTCONF6_DAI8TOADO6		5
+
+/* AB8500_DASLOTCONF7 */
+#define AB8500_DASLOTCONF7_DAI8TOADO7		5
+
+/* AB8500_DASLOTCONF8 */
+#define AB8500_DASLOTCONF8_DAI7TOADO8		5
+
+#define AB8500_DASLOTCONFX_SLTODAX_SHIFT	0
+#define AB8500_DASLOTCONFX_SLTODAX_MASK		0x1F
+
+/* AB8500_CLASSDCONF1 */
+#define AB8500_CLASSDCONF1_PARLHF		7
+#define AB8500_CLASSDCONF1_PARLVIB		6
+#define AB8500_CLASSDCONF1_VIB1SWAPEN		3
+#define AB8500_CLASSDCONF1_VIB2SWAPEN		2
+#define AB8500_CLASSDCONF1_HFLSWAPEN		1
+#define AB8500_CLASSDCONF1_HFRSWAPEN		0
+
+/* AB8500_CLASSDCONF2 */
+#define AB8500_CLASSDCONF2_FIRBYP3		7
+#define AB8500_CLASSDCONF2_FIRBYP2		6
+#define AB8500_CLASSDCONF2_FIRBYP1		5
+#define AB8500_CLASSDCONF2_FIRBYP0		4
+#define AB8500_CLASSDCONF2_HIGHVOLEN3		3
+#define AB8500_CLASSDCONF2_HIGHVOLEN2		2
+#define AB8500_CLASSDCONF2_HIGHVOLEN1		1
+#define AB8500_CLASSDCONF2_HIGHVOLEN0		0
+
+/* AB8500_CLASSDCONF3 */
+#define AB8500_CLASSDCONF3_DITHHPGAIN		4
+#define AB8500_CLASSDCONF3_DITHHPGAIN_MAX	0x0A
+#define AB8500_CLASSDCONF3_DITHWGAIN		0
+#define AB8500_CLASSDCONF3_DITHWGAIN_MAX	0x0A
+
+/* AB8500_DMICFILTCONF */
+#define AB8500_DMICFILTCONF_ANCINSEL		7
+#define AB8500_DMICFILTCONF_DA3TOEAR		6
+#define AB8500_DMICFILTCONF_DMIC1SINC3		5
+#define AB8500_DMICFILTCONF_DMIC2SINC3		4
+#define AB8500_DMICFILTCONF_DMIC3SINC3		3
+#define AB8500_DMICFILTCONF_DMIC4SINC3		2
+#define AB8500_DMICFILTCONF_DMIC5SINC3		1
+#define AB8500_DMICFILTCONF_DMIC6SINC3		0
+
+/* AB8500_DIGMULTCONF1 */
+#define AB8500_DIGMULTCONF1_DATOHSLEN		7
+#define AB8500_DIGMULTCONF1_DATOHSREN		6
+#define AB8500_DIGMULTCONF1_AD1SEL		5
+#define AB8500_DIGMULTCONF1_AD2SEL		4
+#define AB8500_DIGMULTCONF1_AD3SEL		3
+#define AB8500_DIGMULTCONF1_AD5SEL		2
+#define AB8500_DIGMULTCONF1_AD6SEL		1
+#define AB8500_DIGMULTCONF1_ANCSEL		0
+
+/* AB8500_DIGMULTCONF2 */
+#define AB8500_DIGMULTCONF2_DATOHFREN		7
+#define AB8500_DIGMULTCONF2_DATOHFLEN		6
+#define AB8500_DIGMULTCONF2_HFRSEL		5
+#define AB8500_DIGMULTCONF2_HFLSEL		4
+#define AB8500_DIGMULTCONF2_FIRSID1SEL		2
+#define AB8500_DIGMULTCONF2_FIRSID2SEL		0
+
+/* AB8500_ADDIGGAIN1 */
+/* AB8500_ADDIGGAIN2 */
+/* AB8500_ADDIGGAIN3 */
+/* AB8500_ADDIGGAIN4 */
+/* AB8500_ADDIGGAIN5 */
+/* AB8500_ADDIGGAIN6 */
+#define AB8500_ADDIGGAINX_FADEDISADX		6
+#define AB8500_ADDIGGAINX_ADXGAIN_MAX		0x3F
+
+/* AB8500_DADIGGAIN1 */
+/* AB8500_DADIGGAIN2 */
+/* AB8500_DADIGGAIN3 */
+/* AB8500_DADIGGAIN4 */
+/* AB8500_DADIGGAIN5 */
+/* AB8500_DADIGGAIN6 */
+#define AB8500_DADIGGAINX_FADEDISDAX		6
+#define AB8500_DADIGGAINX_DAXGAIN_MAX		0x3F
+
+/* AB8500_ADDIGLOOPGAIN1 */
+/* AB8500_ADDIGLOOPGAIN2 */
+#define AB8500_ADDIGLOOPGAINX_FADEDISADXL	6
+#define AB8500_ADDIGLOOPGAINX_ADXLBGAIN_MAX	0x3F
+
+/* AB8500_HSLEARDIGGAIN */
+#define AB8500_HSLEARDIGGAIN_HSSINC1		7
+#define AB8500_HSLEARDIGGAIN_FADEDISHSL		4
+#define AB8500_HSLEARDIGGAIN_HSLDGAIN_MAX	0x09
+
+/* AB8500_HSRDIGGAIN */
+#define AB8500_HSRDIGGAIN_FADESPEED		6
+#define AB8500_HSRDIGGAIN_FADEDISHSR		4
+#define AB8500_HSRDIGGAIN_HSRDGAIN_MAX		0x09
+
+/* AB8500_SIDFIRGAIN1 */
+/* AB8500_SIDFIRGAIN2 */
+#define AB8500_SIDFIRGAINX_FIRSIDXGAIN_MAX	0x1F
+
+/* AB8500_ANCCONF1 */
+#define AB8500_ANCCONF1_ANCIIRUPDATE		3
+#define AB8500_ANCCONF1_ENANC			2
+#define AB8500_ANCCONF1_ANCIIRINIT		1
+#define AB8500_ANCCONF1_ANCFIRUPDATE		0
+
+/* AB8500_ANCCONF2 */
+#define AB8500_ANCCONF2_SHIFT			5
+#define AB8500_ANCCONF2_MIN			-0x10
+#define AB8500_ANCCONF2_MAX			0xF
+
+/* AB8500_ANCCONF3 */
+#define AB8500_ANCCONF3_SHIFT			5
+#define AB8500_ANCCONF3_MIN			-0x10
+#define AB8500_ANCCONF3_MAX			0xF
+
+/* AB8500_ANCCONF4 */
+#define AB8500_ANCCONF4_SHIFT			5
+#define AB8500_ANCCONF4_MIN			-0x10
+#define AB8500_ANCCONF4_MAX			0xF
+
+/* AB8500_ANC_FIR_COEFFS */
+#define AB8500_ANC_FIR_COEFF_MIN		-0x8000
+#define AB8500_ANC_FIR_COEFF_MAX		0x7FFF
+#define AB8500_ANC_FIR_COEFFS			15
+
+/* AB8500_ANC_IIR_COEFFS */
+#define AB8500_ANC_IIR_COEFF_MIN		-0x800000
+#define AB8500_ANC_IIR_COEFF_MAX		0x7FFFFF
+#define AB8500_ANC_IIR_COEFFS			24
+/* AB8500_ANC_WARP_DELAY */
+#define AB8500_ANC_WARP_DELAY_SHIFT		16
+#define AB8500_ANC_WARP_DELAY_MIN		0x0000
+#define AB8500_ANC_WARP_DELAY_MAX		0xFFFF
+
+/* AB8500_ANCCONF11 */
+/* AB8500_ANCCONF12 */
+/* AB8500_ANCCONF13 */
+/* AB8500_ANCCONF14 */
+
+/* AB8500_SIDFIRADR */
+#define AB8500_SIDFIRADR_FIRSIDSET		7
+#define AB8500_SIDFIRADR_ADDRESS_SHIFT		0
+#define AB8500_SIDFIRADR_ADDRESS_MAX		0x7F
+
+/* AB8500_SIDFIRCOEF1 */
+/* AB8500_SIDFIRCOEF2 */
+#define AB8500_SID_FIR_COEFF_MIN		0
+#define AB8500_SID_FIR_COEFF_MAX		0xFFFF
+#define AB8500_SID_FIR_COEFFS			128
+
+/* AB8500_SIDFIRCONF */
+#define AB8500_SIDFIRCONF_ENFIRSIDS		2
+#define AB8500_SIDFIRCONF_FIRSIDSTOIF1		1
+#define AB8500_SIDFIRCONF_FIRSIDBUSY		0
+
+/* AB8500_AUDINTMASK1 */
+/* AB8500_AUDINTSOURCE1 */
+/* AB8500_AUDINTMASK2 */
+/* AB8500_AUDINTSOURCE2 */
+
+/* AB8500_FIFOCONF1 */
+#define AB8500_FIFOCONF1_BFIFOMASK		0x80
+#define AB8500_FIFOCONF1_BFIFO19M2		0x40
+#define AB8500_FIFOCONF1_BFIFOINT_SHIFT		0
+#define AB8500_FIFOCONF1_BFIFOINT_MAX		0x3F
+
+/* AB8500_FIFOCONF2 */
+#define AB8500_FIFOCONF2_BFIFOTX_SHIFT		0
+#define AB8500_FIFOCONF2_BFIFOTX_MAX		0xFF
+
+/* AB8500_FIFOCONF3 */
+#define AB8500_FIFOCONF3_BFIFOEXSL_SHIFT	5
+#define AB8500_FIFOCONF3_BFIFOEXSL_MAX		0x5
+#define AB8500_FIFOCONF3_PREBITCLK0_SHIFT	2
+#define AB8500_FIFOCONF3_PREBITCLK0_MAX		0x7
+#define AB8500_FIFOCONF3_BFIFOMAST_SHIFT	1
+#define AB8500_FIFOCONF3_BFIFORUN_SHIFT		0
+
+/* AB8500_FIFOCONF4 */
+#define AB8500_FIFOCONF4_BFIFOFRAMSW_SHIFT	0
+#define AB8500_FIFOCONF4_BFIFOFRAMSW_MAX	0xFF
+
+/* AB8500_FIFOCONF5 */
+#define AB8500_FIFOCONF5_BFIFOWAKEUP_SHIFT	0
+#define AB8500_FIFOCONF5_BFIFOWAKEUP_MAX	0xFF
+
+/* AB8500_FIFOCONF6 */
+#define AB8500_FIFOCONF6_BFIFOSAMPLE_SHIFT	0
+#define AB8500_FIFOCONF6_BFIFOSAMPLE_MAX	0xFF
+
+/* AB8500_AUDREV */
+
+#endif

+ 0 - 6
sound/soc/codecs/ac97.c

@@ -91,11 +91,6 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
 	return 0;
 	return 0;
 }
 }
 
 
-static int ac97_soc_remove(struct snd_soc_codec *codec)
-{
-	return 0;
-}
-
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 static int ac97_soc_suspend(struct snd_soc_codec *codec)
 static int ac97_soc_suspend(struct snd_soc_codec *codec)
 {
 {
@@ -119,7 +114,6 @@ static struct snd_soc_codec_driver soc_codec_dev_ac97 = {
 	.write =	ac97_write,
 	.write =	ac97_write,
 	.read =		ac97_read,
 	.read =		ac97_read,
 	.probe = 	ac97_soc_probe,
 	.probe = 	ac97_soc_probe,
-	.remove = 	ac97_soc_remove,
 	.suspend =	ac97_soc_suspend,
 	.suspend =	ac97_soc_suspend,
 	.resume =	ac97_soc_resume,
 	.resume =	ac97_soc_resume,
 };
 };

+ 937 - 0
sound/soc/codecs/arizona.c

@@ -0,0 +1,937 @@
+/*
+ * arizona.c - Wolfson Arizona class device shared support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/gcd.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+
+#define ARIZONA_AIF_BCLK_CTRL                   0x00
+#define ARIZONA_AIF_TX_PIN_CTRL                 0x01
+#define ARIZONA_AIF_RX_PIN_CTRL                 0x02
+#define ARIZONA_AIF_RATE_CTRL                   0x03
+#define ARIZONA_AIF_FORMAT                      0x04
+#define ARIZONA_AIF_TX_BCLK_RATE                0x05
+#define ARIZONA_AIF_RX_BCLK_RATE                0x06
+#define ARIZONA_AIF_FRAME_CTRL_1                0x07
+#define ARIZONA_AIF_FRAME_CTRL_2                0x08
+#define ARIZONA_AIF_FRAME_CTRL_3                0x09
+#define ARIZONA_AIF_FRAME_CTRL_4                0x0A
+#define ARIZONA_AIF_FRAME_CTRL_5                0x0B
+#define ARIZONA_AIF_FRAME_CTRL_6                0x0C
+#define ARIZONA_AIF_FRAME_CTRL_7                0x0D
+#define ARIZONA_AIF_FRAME_CTRL_8                0x0E
+#define ARIZONA_AIF_FRAME_CTRL_9                0x0F
+#define ARIZONA_AIF_FRAME_CTRL_10               0x10
+#define ARIZONA_AIF_FRAME_CTRL_11               0x11
+#define ARIZONA_AIF_FRAME_CTRL_12               0x12
+#define ARIZONA_AIF_FRAME_CTRL_13               0x13
+#define ARIZONA_AIF_FRAME_CTRL_14               0x14
+#define ARIZONA_AIF_FRAME_CTRL_15               0x15
+#define ARIZONA_AIF_FRAME_CTRL_16               0x16
+#define ARIZONA_AIF_FRAME_CTRL_17               0x17
+#define ARIZONA_AIF_FRAME_CTRL_18               0x18
+#define ARIZONA_AIF_TX_ENABLES                  0x19
+#define ARIZONA_AIF_RX_ENABLES                  0x1A
+#define ARIZONA_AIF_FORCE_WRITE                 0x1B
+
+#define arizona_fll_err(_fll, fmt, ...) \
+	dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define arizona_fll_warn(_fll, fmt, ...) \
+	dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define arizona_fll_dbg(_fll, fmt, ...) \
+	dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+
+#define arizona_aif_err(_dai, fmt, ...) \
+	dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define arizona_aif_warn(_dai, fmt, ...) \
+	dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define arizona_aif_dbg(_dai, fmt, ...) \
+	dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+
+const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
+	"None",
+	"Tone Generator 1",
+	"Tone Generator 2",
+	"Haptics",
+	"AEC",
+	"Mic Mute Mixer",
+	"Noise Generator",
+	"IN1L",
+	"IN1R",
+	"IN2L",
+	"IN2R",
+	"IN3L",
+	"IN3R",
+	"IN4L",
+	"IN4R",
+	"AIF1RX1",
+	"AIF1RX2",
+	"AIF1RX3",
+	"AIF1RX4",
+	"AIF1RX5",
+	"AIF1RX6",
+	"AIF1RX7",
+	"AIF1RX8",
+	"AIF2RX1",
+	"AIF2RX2",
+	"AIF3RX1",
+	"AIF3RX2",
+	"SLIMRX1",
+	"SLIMRX2",
+	"SLIMRX3",
+	"SLIMRX4",
+	"SLIMRX5",
+	"SLIMRX6",
+	"SLIMRX7",
+	"SLIMRX8",
+	"EQ1",
+	"EQ2",
+	"EQ3",
+	"EQ4",
+	"DRC1L",
+	"DRC1R",
+	"DRC2L",
+	"DRC2R",
+	"LHPF1",
+	"LHPF2",
+	"LHPF3",
+	"LHPF4",
+	"DSP1.1",
+	"DSP1.2",
+	"DSP1.3",
+	"DSP1.4",
+	"DSP1.5",
+	"DSP1.6",
+	"ASRC1L",
+	"ASRC1R",
+	"ASRC2L",
+	"ASRC2R",
+};
+EXPORT_SYMBOL_GPL(arizona_mixer_texts);
+
+int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
+	0x00,  /* None */
+	0x04,  /* Tone */
+	0x05,
+	0x06,  /* Haptics */
+	0x08,  /* AEC */
+	0x0c,  /* Noise mixer */
+	0x0d,  /* Comfort noise */
+	0x10,  /* IN1L */
+	0x11,
+	0x12,
+	0x13,
+	0x14,
+	0x15,
+	0x16,
+	0x17,
+	0x20,  /* AIF1RX1 */
+	0x21,
+	0x22,
+	0x23,
+	0x24,
+	0x25,
+	0x26,
+	0x27,
+	0x28,  /* AIF2RX1 */
+	0x29,
+	0x30,  /* AIF3RX1 */
+	0x31,
+	0x38,  /* SLIMRX1 */
+	0x39,
+	0x3a,
+	0x3b,
+	0x3c,
+	0x3d,
+	0x3e,
+	0x3f,
+	0x50,  /* EQ1 */
+	0x51,
+	0x52,
+	0x53,
+	0x58,  /* DRC1L */
+	0x59,
+	0x5a,
+	0x5b,
+	0x60,  /* LHPF1 */
+	0x61,
+	0x62,
+	0x63,
+	0x68,  /* DSP1.1 */
+	0x69,
+	0x6a,
+	0x6b,
+	0x6c,
+	0x6d,
+	0x90,  /* ASRC1L */
+	0x91,
+	0x92,
+	0x93,
+};
+EXPORT_SYMBOL_GPL(arizona_mixer_values);
+
+const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0);
+EXPORT_SYMBOL_GPL(arizona_mixer_tlv);
+
+static const char *arizona_lhpf_mode_text[] = {
+	"Low-pass", "High-pass"
+};
+
+const struct soc_enum arizona_lhpf1_mode =
+	SOC_ENUM_SINGLE(ARIZONA_HPLPF1_1, ARIZONA_LHPF1_MODE_SHIFT, 2,
+			arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf1_mode);
+
+const struct soc_enum arizona_lhpf2_mode =
+	SOC_ENUM_SINGLE(ARIZONA_HPLPF2_1, ARIZONA_LHPF2_MODE_SHIFT, 2,
+			arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf2_mode);
+
+const struct soc_enum arizona_lhpf3_mode =
+	SOC_ENUM_SINGLE(ARIZONA_HPLPF3_1, ARIZONA_LHPF3_MODE_SHIFT, 2,
+			arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf3_mode);
+
+const struct soc_enum arizona_lhpf4_mode =
+	SOC_ENUM_SINGLE(ARIZONA_HPLPF4_1, ARIZONA_LHPF4_MODE_SHIFT, 2,
+			arizona_lhpf_mode_text);
+EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
+
+int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+		  int event)
+{
+	return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_in_ev);
+
+int arizona_out_ev(struct snd_soc_dapm_widget *w,
+		   struct snd_kcontrol *kcontrol,
+		   int event)
+{
+	return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_out_ev);
+
+int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+		       int source, unsigned int freq, int dir)
+{
+	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct arizona *arizona = priv->arizona;
+	char *name;
+	unsigned int reg;
+	unsigned int mask = ARIZONA_SYSCLK_FREQ_MASK | ARIZONA_SYSCLK_SRC_MASK;
+	unsigned int val = source << ARIZONA_SYSCLK_SRC_SHIFT;
+	unsigned int *clk;
+
+	switch (clk_id) {
+	case ARIZONA_CLK_SYSCLK:
+		name = "SYSCLK";
+		reg = ARIZONA_SYSTEM_CLOCK_1;
+		clk = &priv->sysclk;
+		mask |= ARIZONA_SYSCLK_FRAC;
+		break;
+	case ARIZONA_CLK_ASYNCCLK:
+		name = "ASYNCCLK";
+		reg = ARIZONA_ASYNC_CLOCK_1;
+		clk = &priv->asyncclk;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (freq) {
+	case  5644800:
+	case  6144000:
+		break;
+	case 11289600:
+	case 12288000:
+		val |= 1 << ARIZONA_SYSCLK_FREQ_SHIFT;
+		break;
+	case 22579200:
+	case 24576000:
+		val |= 2 << ARIZONA_SYSCLK_FREQ_SHIFT;
+		break;
+	case 45158400:
+	case 49152000:
+		val |= 3 << ARIZONA_SYSCLK_FREQ_SHIFT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*clk = freq;
+
+	if (freq % 6144000)
+		val |= ARIZONA_SYSCLK_FRAC;
+
+	dev_dbg(arizona->dev, "%s set to %uHz", name, freq);
+
+	return regmap_update_bits(arizona->regmap, reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(arizona_set_sysclk);
+
+static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	int lrclk, bclk, mode, base;
+
+	base = dai->driver->base;
+
+	lrclk = 0;
+	bclk = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_A:
+		mode = 0;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		mode = 1;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		mode = 2;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		mode = 3;
+		break;
+	default:
+		arizona_aif_err(dai, "Unsupported DAI format %d\n",
+				fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		break;
+	case SND_SOC_DAIFMT_CBS_CFM:
+		lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFS:
+		bclk |= ARIZONA_AIF1_BCLK_MSTR;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		bclk |= ARIZONA_AIF1_BCLK_MSTR;
+		lrclk |= ARIZONA_AIF1TX_LRCLK_MSTR;
+		break;
+	default:
+		arizona_aif_err(dai, "Unsupported master mode %d\n",
+				fmt & SND_SOC_DAIFMT_MASTER_MASK);
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		break;
+	case SND_SOC_DAIFMT_IB_IF:
+		bclk |= ARIZONA_AIF1_BCLK_INV;
+		lrclk |= ARIZONA_AIF1TX_LRCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_IB_NF:
+		bclk |= ARIZONA_AIF1_BCLK_INV;
+		break;
+	case SND_SOC_DAIFMT_NB_IF:
+		lrclk |= ARIZONA_AIF1TX_LRCLK_INV;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL,
+			    ARIZONA_AIF1_BCLK_INV | ARIZONA_AIF1_BCLK_MSTR,
+			    bclk);
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_PIN_CTRL,
+			    ARIZONA_AIF1TX_LRCLK_INV |
+			    ARIZONA_AIF1TX_LRCLK_MSTR, lrclk);
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_PIN_CTRL,
+			    ARIZONA_AIF1RX_LRCLK_INV |
+			    ARIZONA_AIF1RX_LRCLK_MSTR, lrclk);
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_FORMAT,
+			    ARIZONA_AIF1_FMT_MASK, mode);
+
+	return 0;
+}
+
+static const int arizona_48k_bclk_rates[] = {
+	-1,
+	48000,
+	64000,
+	96000,
+	128000,
+	192000,
+	256000,
+	384000,
+	512000,
+	768000,
+	1024000,
+	1536000,
+	2048000,
+	3072000,
+	4096000,
+	6144000,
+	8192000,
+	12288000,
+	24576000,
+};
+
+static const unsigned int arizona_48k_rates[] = {
+	12000,
+	24000,
+	48000,
+	96000,
+	192000,
+	384000,
+	768000,
+	4000,
+	8000,
+	16000,
+	32000,
+	64000,
+	128000,
+	256000,
+	512000,
+};
+
+static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = {
+	.count	= ARRAY_SIZE(arizona_48k_rates),
+	.list	= arizona_48k_rates,
+};
+
+static const int arizona_44k1_bclk_rates[] = {
+	-1,
+	44100,
+	58800,
+	88200,
+	117600,
+	177640,
+	235200,
+	352800,
+	470400,
+	705600,
+	940800,
+	1411200,
+	1881600,
+	2882400,
+	3763200,
+	5644800,
+	7526400,
+	11289600,
+	22579200,
+};
+
+static const unsigned int arizona_44k1_rates[] = {
+	11025,
+	22050,
+	44100,
+	88200,
+	176400,
+	352800,
+	705600,
+};
+
+static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = {
+	.count	= ARRAY_SIZE(arizona_44k1_rates),
+	.list	= arizona_44k1_rates,
+};
+
+static int arizona_sr_vals[] = {
+	0,
+	12000,
+	24000,
+	48000,
+	96000,
+	192000,
+	384000,
+	768000,
+	0,
+	11025,
+	22050,
+	44100,
+	88200,
+	176400,
+	352800,
+	705600,
+	4000,
+	8000,
+	16000,
+	32000,
+	64000,
+	128000,
+	256000,
+	512000,
+};
+
+static int arizona_startup(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+	const struct snd_pcm_hw_constraint_list *constraint;
+	unsigned int base_rate;
+
+	switch (dai_priv->clk) {
+	case ARIZONA_CLK_SYSCLK:
+		base_rate = priv->sysclk;
+		break;
+	case ARIZONA_CLK_ASYNCCLK:
+		base_rate = priv->asyncclk;
+		break;
+	default:
+		return 0;
+	}
+
+	if (base_rate % 8000)
+		constraint = &arizona_44k1_constraint;
+	else
+		constraint = &arizona_48k_constraint;
+
+	return snd_pcm_hw_constraint_list(substream->runtime, 0,
+					  SNDRV_PCM_HW_PARAM_RATE,
+					  constraint);
+}
+
+static int arizona_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 arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+	int base = dai->driver->base;
+	const int *rates;
+	int i;
+	int bclk, lrclk, wl, frame, sr_val;
+
+	if (params_rate(params) % 8000)
+		rates = &arizona_44k1_bclk_rates[0];
+	else
+		rates = &arizona_48k_bclk_rates[0];
+
+	for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) {
+		if (rates[i] >= snd_soc_params_to_bclk(params) &&
+		    rates[i] % params_rate(params) == 0) {
+			bclk = i;
+			break;
+		}
+	}
+	if (i == ARRAY_SIZE(arizona_44k1_bclk_rates)) {
+		arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
+				params_rate(params));
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++)
+		if (arizona_sr_vals[i] == params_rate(params))
+			break;
+	if (i == ARRAY_SIZE(arizona_sr_vals)) {
+		arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
+				params_rate(params));
+		return -EINVAL;
+	}
+	sr_val = i;
+
+	lrclk = snd_soc_params_to_bclk(params) / params_rate(params);
+
+	arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n",
+			rates[bclk], rates[bclk] / lrclk);
+
+	wl = snd_pcm_format_width(params_format(params));
+	frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl;
+
+	/*
+	 * We will need to be more flexible than this in future,
+	 * currently we use a single sample rate for SYSCLK.
+	 */
+	switch (dai_priv->clk) {
+	case ARIZONA_CLK_SYSCLK:
+		snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
+				    ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
+		snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
+				    ARIZONA_AIF1_RATE_MASK, 0);
+		break;
+	case ARIZONA_CLK_ASYNCCLK:
+		snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
+				    ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val);
+		snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
+				    ARIZONA_AIF1_RATE_MASK, 8);
+		break;
+	default:
+		arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL,
+			    ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_TX_BCLK_RATE,
+			    ARIZONA_AIF1TX_BCPF_MASK, lrclk);
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_RX_BCLK_RATE,
+			    ARIZONA_AIF1RX_BCPF_MASK, lrclk);
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_1,
+			    ARIZONA_AIF1TX_WL_MASK |
+			    ARIZONA_AIF1TX_SLOT_LEN_MASK, frame);
+	snd_soc_update_bits(codec, base + ARIZONA_AIF_FRAME_CTRL_2,
+			    ARIZONA_AIF1RX_WL_MASK |
+			    ARIZONA_AIF1RX_SLOT_LEN_MASK, frame);
+
+	return 0;
+}
+
+static const char *arizona_dai_clk_str(int clk_id)
+{
+	switch (clk_id) {
+	case ARIZONA_CLK_SYSCLK:
+		return "SYSCLK";
+	case ARIZONA_CLK_ASYNCCLK:
+		return "ASYNCCLK";
+	default:
+		return "Unknown clock";
+	}
+}
+
+static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
+				  int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+	struct snd_soc_dapm_route routes[2];
+
+	switch (clk_id) {
+	case ARIZONA_CLK_SYSCLK:
+	case ARIZONA_CLK_ASYNCCLK:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (clk_id == dai_priv->clk)
+		return 0;
+
+	if (dai->active) {
+		dev_err(codec->dev, "Can't change clock on active DAI %d\n",
+			dai->id);
+		return -EBUSY;
+	}
+
+	memset(&routes, 0, sizeof(routes));
+	routes[0].sink = dai->driver->capture.stream_name;
+	routes[1].sink = dai->driver->playback.stream_name;
+
+	routes[0].source = arizona_dai_clk_str(dai_priv->clk);
+	routes[1].source = arizona_dai_clk_str(dai_priv->clk);
+	snd_soc_dapm_del_routes(&codec->dapm, routes, ARRAY_SIZE(routes));
+
+	routes[0].source = arizona_dai_clk_str(clk_id);
+	routes[1].source = arizona_dai_clk_str(clk_id);
+	snd_soc_dapm_add_routes(&codec->dapm, routes, ARRAY_SIZE(routes));
+
+	return snd_soc_dapm_sync(&codec->dapm);
+}
+
+const struct snd_soc_dai_ops arizona_dai_ops = {
+	.startup = arizona_startup,
+	.set_fmt = arizona_set_fmt,
+	.hw_params = arizona_hw_params,
+	.set_sysclk = arizona_dai_set_sysclk,
+};
+EXPORT_SYMBOL_GPL(arizona_dai_ops);
+
+int arizona_init_dai(struct arizona_priv *priv, int id)
+{
+	struct arizona_dai_priv *dai_priv = &priv->dai[id];
+
+	dai_priv->clk = ARIZONA_CLK_SYSCLK;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_dai);
+
+static irqreturn_t arizona_fll_lock(int irq, void *data)
+{
+	struct arizona_fll *fll = data;
+
+	arizona_fll_dbg(fll, "Locked\n");
+
+	complete(&fll->lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t arizona_fll_clock_ok(int irq, void *data)
+{
+	struct arizona_fll *fll = data;
+
+	arizona_fll_dbg(fll, "clock OK\n");
+
+	complete(&fll->ok);
+
+	return IRQ_HANDLED;
+}
+
+static struct {
+	unsigned int min;
+	unsigned int max;
+	u16 fratio;
+	int ratio;
+} fll_fratios[] = {
+	{       0,    64000, 4, 16 },
+	{   64000,   128000, 3,  8 },
+	{  128000,   256000, 2,  4 },
+	{  256000,  1000000, 1,  2 },
+	{ 1000000, 13500000, 0,  1 },
+};
+
+struct arizona_fll_cfg {
+	int n;
+	int theta;
+	int lambda;
+	int refdiv;
+	int outdiv;
+	int fratio;
+};
+
+static int arizona_calc_fll(struct arizona_fll *fll,
+			    struct arizona_fll_cfg *cfg,
+			    unsigned int Fref,
+			    unsigned int Fout)
+{
+	unsigned int target, div, gcd_fll;
+	int i, ratio;
+
+	arizona_fll_dbg(fll, "Fref=%u Fout=%u\n", Fref, Fout);
+
+	/* Fref must be <=13.5MHz */
+	div = 1;
+	cfg->refdiv = 0;
+	while ((Fref / div) > 13500000) {
+		div *= 2;
+		cfg->refdiv++;
+
+		if (div > 8) {
+			arizona_fll_err(fll,
+					"Can't scale %dMHz in to <=13.5MHz\n",
+					Fref);
+			return -EINVAL;
+		}
+	}
+
+	/* Apply the division for our remaining calculations */
+	Fref /= div;
+
+	/* Fvco should be over the targt; don't check the upper bound */
+	div = 1;
+	while (Fout * div < 90000000 * fll->vco_mult) {
+		div++;
+		if (div > 7) {
+			arizona_fll_err(fll, "No FLL_OUTDIV for Fout=%uHz\n",
+					Fout);
+			return -EINVAL;
+		}
+	}
+	target = Fout * div / fll->vco_mult;
+	cfg->outdiv = div;
+
+	arizona_fll_dbg(fll, "Fvco=%dHz\n", target);
+
+	/* Find an appropraite FLL_FRATIO and factor it out of the target */
+	for (i = 0; i < ARRAY_SIZE(fll_fratios); i++) {
+		if (fll_fratios[i].min <= Fref && Fref <= fll_fratios[i].max) {
+			cfg->fratio = fll_fratios[i].fratio;
+			ratio = fll_fratios[i].ratio;
+			break;
+		}
+	}
+	if (i == ARRAY_SIZE(fll_fratios)) {
+		arizona_fll_err(fll, "Unable to find FRATIO for Fref=%uHz\n",
+				Fref);
+		return -EINVAL;
+	}
+
+	cfg->n = target / (ratio * Fref);
+
+	if (target % Fref) {
+		gcd_fll = gcd(target, ratio * Fref);
+		arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll);
+
+		cfg->theta = (target - (cfg->n * ratio * Fref))
+			/ gcd_fll;
+		cfg->lambda = (ratio * Fref) / gcd_fll;
+	} else {
+		cfg->theta = 0;
+		cfg->lambda = 0;
+	}
+
+	arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
+			cfg->n, cfg->theta, cfg->lambda);
+	arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
+			cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv);
+
+	return 0;
+
+}
+
+static void arizona_apply_fll(struct arizona *arizona, unsigned int base,
+			      struct arizona_fll_cfg *cfg, int source)
+{
+	regmap_update_bits(arizona->regmap, base + 3,
+			   ARIZONA_FLL1_THETA_MASK, cfg->theta);
+	regmap_update_bits(arizona->regmap, base + 4,
+			   ARIZONA_FLL1_LAMBDA_MASK, cfg->lambda);
+	regmap_update_bits(arizona->regmap, base + 5,
+			   ARIZONA_FLL1_FRATIO_MASK,
+			   cfg->fratio << ARIZONA_FLL1_FRATIO_SHIFT);
+	regmap_update_bits(arizona->regmap, base + 6,
+			   ARIZONA_FLL1_CLK_REF_DIV_MASK |
+			   ARIZONA_FLL1_CLK_REF_SRC_MASK,
+			   cfg->refdiv << ARIZONA_FLL1_CLK_REF_DIV_SHIFT |
+			   source << ARIZONA_FLL1_CLK_REF_SRC_SHIFT);
+
+	regmap_update_bits(arizona->regmap, base + 2,
+			   ARIZONA_FLL1_CTRL_UPD | ARIZONA_FLL1_N_MASK,
+			   ARIZONA_FLL1_CTRL_UPD | cfg->n);
+}
+
+int arizona_set_fll(struct arizona_fll *fll, int source,
+		    unsigned int Fref, unsigned int Fout)
+{
+	struct arizona *arizona = fll->arizona;
+	struct arizona_fll_cfg cfg, sync;
+	unsigned int reg, val;
+	int syncsrc;
+	bool ena;
+	int ret;
+
+	ret = regmap_read(arizona->regmap, fll->base + 1, &reg);
+	if (ret != 0) {
+		arizona_fll_err(fll, "Failed to read current state: %d\n",
+				ret);
+		return ret;
+	}
+	ena = reg & ARIZONA_FLL1_ENA;
+
+	if (Fout) {
+		/* Do we have a 32kHz reference? */
+		regmap_read(arizona->regmap, ARIZONA_CLOCK_32K_1, &val);
+		switch (val & ARIZONA_CLK_32K_SRC_MASK) {
+		case ARIZONA_CLK_SRC_MCLK1:
+		case ARIZONA_CLK_SRC_MCLK2:
+			syncsrc = val & ARIZONA_CLK_32K_SRC_MASK;
+			break;
+		default:
+			syncsrc = -1;
+		}
+
+		if (source == syncsrc)
+			syncsrc = -1;
+
+		if (syncsrc >= 0) {
+			ret = arizona_calc_fll(fll, &sync, Fref, Fout);
+			if (ret != 0)
+				return ret;
+
+			ret = arizona_calc_fll(fll, &cfg, 32768, Fout);
+			if (ret != 0)
+				return ret;
+		} else {
+			ret = arizona_calc_fll(fll, &cfg, Fref, Fout);
+			if (ret != 0)
+				return ret;
+		}
+	} else {
+		regmap_update_bits(arizona->regmap, fll->base + 1,
+				   ARIZONA_FLL1_ENA, 0);
+		regmap_update_bits(arizona->regmap, fll->base + 0x11,
+				   ARIZONA_FLL1_SYNC_ENA, 0);
+
+		if (ena)
+			pm_runtime_put_autosuspend(arizona->dev);
+
+		return 0;
+	}
+
+	regmap_update_bits(arizona->regmap, fll->base + 5,
+			   ARIZONA_FLL1_OUTDIV_MASK,
+			   cfg.outdiv << ARIZONA_FLL1_OUTDIV_SHIFT);
+
+	if (syncsrc >= 0) {
+		arizona_apply_fll(arizona, fll->base, &cfg, syncsrc);
+		arizona_apply_fll(arizona, fll->base + 0x10, &sync, source);
+	} else {
+		arizona_apply_fll(arizona, fll->base, &cfg, source);
+	}
+
+	if (!ena)
+		pm_runtime_get(arizona->dev);
+
+	/* Clear any pending completions */
+	try_wait_for_completion(&fll->ok);
+
+	regmap_update_bits(arizona->regmap, fll->base + 1,
+			   ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
+	if (syncsrc >= 0)
+		regmap_update_bits(arizona->regmap, fll->base + 0x11,
+				   ARIZONA_FLL1_SYNC_ENA,
+				   ARIZONA_FLL1_SYNC_ENA);
+
+	ret = wait_for_completion_timeout(&fll->ok,
+					  msecs_to_jiffies(25));
+	if (ret == 0)
+		arizona_fll_warn(fll, "Timed out waiting for lock\n");
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_set_fll);
+
+int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
+		     int ok_irq, struct arizona_fll *fll)
+{
+	int ret;
+
+	init_completion(&fll->lock);
+	init_completion(&fll->ok);
+
+	fll->id = id;
+	fll->base = base;
+	fll->arizona = arizona;
+
+	snprintf(fll->lock_name, sizeof(fll->lock_name), "FLL%d lock", id);
+	snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name),
+		 "FLL%d clock OK", id);
+
+	ret = arizona_request_irq(arizona, lock_irq, fll->lock_name,
+				  arizona_fll_lock, fll);
+	if (ret != 0) {
+		dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n",
+			id, ret);
+	}
+
+	ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name,
+				  arizona_fll_clock_ok, fll);
+	if (ret != 0) {
+		dev_err(arizona->dev, "Failed to get FLL%d clock OK IRQ: %d\n",
+			id, ret);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_fll);
+
+MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");

+ 159 - 0
sound/soc/codecs/arizona.h

@@ -0,0 +1,159 @@
+/*
+ * arizona.h - Wolfson Arizona class device shared support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _ASOC_ARIZONA_H
+#define _ASOC_ARIZONA_H
+
+#include <linux/completion.h>
+
+#include <sound/soc.h>
+
+#define ARIZONA_CLK_SYSCLK   1
+#define ARIZONA_CLK_ASYNCCLK 2
+
+#define ARIZONA_CLK_SRC_MCLK1    0x0
+#define ARIZONA_CLK_SRC_MCLK2    0x1
+#define ARIZONA_CLK_SRC_FLL1     0x4
+#define ARIZONA_CLK_SRC_FLL2     0x5
+#define ARIZONA_CLK_SRC_AIF1BCLK 0x8
+#define ARIZONA_CLK_SRC_AIF2BCLK 0x9
+#define ARIZONA_CLK_SRC_AIF3BCLK 0xa
+
+#define ARIZONA_FLL_SRC_MCLK1      0
+#define ARIZONA_FLL_SRC_MCLK2      1
+#define ARIZONA_FLL_SRC_SLIMCLK    2
+#define ARIZONA_FLL_SRC_FLL1       3
+#define ARIZONA_FLL_SRC_FLL2       4
+#define ARIZONA_FLL_SRC_AIF1BCLK   5
+#define ARIZONA_FLL_SRC_AIF2BCLK   6
+#define ARIZONA_FLL_SRC_AIF3BCLK   7
+#define ARIZONA_FLL_SRC_AIF1LRCLK  8
+#define ARIZONA_FLL_SRC_AIF2LRCLK  9
+#define ARIZONA_FLL_SRC_AIF3LRCLK 10
+
+#define ARIZONA_MIXER_VOL_MASK             0x00FE
+#define ARIZONA_MIXER_VOL_SHIFT                 1
+#define ARIZONA_MIXER_VOL_WIDTH                 7
+
+#define ARIZONA_MAX_DAI 3
+
+struct arizona;
+
+struct arizona_dai_priv {
+	int clk;
+};
+
+struct arizona_priv {
+	struct arizona *arizona;
+	int sysclk;
+	int asyncclk;
+	struct arizona_dai_priv dai[ARIZONA_MAX_DAI];
+};
+
+#define ARIZONA_NUM_MIXER_INPUTS 57
+
+extern const unsigned int arizona_mixer_tlv[];
+extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
+extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
+
+#define ARIZONA_MIXER_CONTROLS(name, base) \
+	SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1,		\
+			     ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0,	\
+			     arizona_mixer_tlv),			\
+	SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 3,		\
+			     ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0,	\
+			     arizona_mixer_tlv),			\
+	SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 5,		\
+			     ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0,	\
+			     arizona_mixer_tlv),			\
+	SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 7,		\
+			     ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0,	\
+			     arizona_mixer_tlv)
+
+#define ARIZONA_MUX_ENUM_DECL(name, reg) \
+	SOC_VALUE_ENUM_SINGLE_DECL(name, reg, 0, 0xff,			\
+				   arizona_mixer_texts, arizona_mixer_values)
+
+#define ARIZONA_MUX_CTL_DECL(name) \
+	const struct snd_kcontrol_new name##_mux =	\
+		SOC_DAPM_VALUE_ENUM("Route", name##_enum)
+
+#define ARIZONA_MIXER_ENUMS(name, base_reg) \
+	static ARIZONA_MUX_ENUM_DECL(name##_in1_enum, base_reg);      \
+	static ARIZONA_MUX_ENUM_DECL(name##_in2_enum, base_reg + 2);  \
+	static ARIZONA_MUX_ENUM_DECL(name##_in3_enum, base_reg + 4);  \
+	static ARIZONA_MUX_ENUM_DECL(name##_in4_enum, base_reg + 6);  \
+	static ARIZONA_MUX_CTL_DECL(name##_in1); \
+	static ARIZONA_MUX_CTL_DECL(name##_in2); \
+	static ARIZONA_MUX_CTL_DECL(name##_in3); \
+	static ARIZONA_MUX_CTL_DECL(name##_in4)
+
+#define ARIZONA_MUX(name, ctrl) \
+	SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+#define ARIZONA_MIXER_WIDGETS(name, name_str)	\
+	ARIZONA_MUX(name_str " Input 1", &name##_in1_mux), \
+	ARIZONA_MUX(name_str " Input 2", &name##_in2_mux), \
+	ARIZONA_MUX(name_str " Input 3", &name##_in3_mux), \
+	ARIZONA_MUX(name_str " Input 4", &name##_in4_mux), \
+	SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define ARIZONA_MIXER_ROUTES(widget, name) \
+	{ widget, NULL, name " Mixer" },         \
+	{ name " Mixer", NULL, name " Input 1" }, \
+	{ name " Mixer", NULL, name " Input 2" }, \
+	{ name " Mixer", NULL, name " Input 3" }, \
+	{ name " Mixer", NULL, name " Input 4" }, \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Input 1"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Input 2"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \
+	ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
+
+extern const struct soc_enum arizona_lhpf1_mode;
+extern const struct soc_enum arizona_lhpf2_mode;
+extern const struct soc_enum arizona_lhpf3_mode;
+extern const struct soc_enum arizona_lhpf4_mode;
+
+extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
+			 struct snd_kcontrol *kcontrol,
+			 int event);
+extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *kcontrol,
+			  int event);
+
+extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+			      int source, unsigned int freq, int dir);
+
+extern const struct snd_soc_dai_ops arizona_dai_ops;
+
+#define ARIZONA_FLL_NAME_LEN 20
+
+struct arizona_fll {
+	struct arizona *arizona;
+	int id;
+	unsigned int base;
+	unsigned int vco_mult;
+	struct completion lock;
+	struct completion ok;
+
+	char lock_name[ARIZONA_FLL_NAME_LEN];
+	char clock_ok_name[ARIZONA_FLL_NAME_LEN];
+};
+
+extern int arizona_init_fll(struct arizona *arizona, int id, int base,
+			    int lock_irq, int ok_irq, struct arizona_fll *fll);
+extern int arizona_set_fll(struct arizona_fll *fll, int source,
+			   unsigned int Fref, unsigned int Fout);
+
+extern int arizona_init_dai(struct arizona_priv *priv, int dai);
+
+#endif

+ 4 - 15
sound/soc/codecs/cs42l52.c

@@ -14,7 +14,6 @@
 
 
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/moduleparam.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
@@ -1217,11 +1216,11 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
 		return -ENOMEM;
 		return -ENOMEM;
 	cs42l52->dev = &i2c_client->dev;
 	cs42l52->dev = &i2c_client->dev;
 
 
-	cs42l52->regmap = regmap_init_i2c(i2c_client, &cs42l52_regmap);
+	cs42l52->regmap = devm_regmap_init_i2c(i2c_client, &cs42l52_regmap);
 	if (IS_ERR(cs42l52->regmap)) {
 	if (IS_ERR(cs42l52->regmap)) {
 		ret = PTR_ERR(cs42l52->regmap);
 		ret = PTR_ERR(cs42l52->regmap);
 		dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
 		dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
-		goto err;
+		return ret;
 	}
 	}
 
 
 	i2c_set_clientdata(i2c_client, cs42l52);
 	i2c_set_clientdata(i2c_client, cs42l52);
@@ -1243,7 +1242,7 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
 		dev_err(&i2c_client->dev,
 		dev_err(&i2c_client->dev,
 			"CS42L52 Device ID (%X). Expected %X\n",
 			"CS42L52 Device ID (%X). Expected %X\n",
 			devid, CS42L52_CHIP_ID);
 			devid, CS42L52_CHIP_ID);
-		goto err_regmap;
+		return ret;
 	}
 	}
 
 
 	regcache_cache_only(cs42l52->regmap, true);
 	regcache_cache_only(cs42l52->regmap, true);
@@ -1251,23 +1250,13 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client,
 	ret =  snd_soc_register_codec(&i2c_client->dev,
 	ret =  snd_soc_register_codec(&i2c_client->dev,
 			&soc_codec_dev_cs42l52, &cs42l52_dai, 1);
 			&soc_codec_dev_cs42l52, &cs42l52_dai, 1);
 	if (ret < 0)
 	if (ret < 0)
-		goto err_regmap;
+		return ret;
 	return 0;
 	return 0;
-
-err_regmap:
-	regmap_exit(cs42l52->regmap);
-
-err:
-	return ret;
 }
 }
 
 
 static int cs42l52_i2c_remove(struct i2c_client *client)
 static int cs42l52_i2c_remove(struct i2c_client *client)
 {
 {
-	struct cs42l52_private *cs42l52 = i2c_get_clientdata(client);
-
 	snd_soc_unregister_codec(&client->dev);
 	snd_soc_unregister_codec(&client->dev);
-	regmap_exit(cs42l52->regmap);
-
 	return 0;
 	return 0;
 }
 }
 
 

+ 5 - 15
sound/soc/codecs/cs42l73.c

@@ -1362,11 +1362,11 @@ static __devinit int cs42l73_i2c_probe(struct i2c_client *i2c_client,
 
 
 	i2c_set_clientdata(i2c_client, cs42l73);
 	i2c_set_clientdata(i2c_client, cs42l73);
 
 
-	cs42l73->regmap = regmap_init_i2c(i2c_client, &cs42l73_regmap);
+	cs42l73->regmap = devm_regmap_init_i2c(i2c_client, &cs42l73_regmap);
 	if (IS_ERR(cs42l73->regmap)) {
 	if (IS_ERR(cs42l73->regmap)) {
 		ret = PTR_ERR(cs42l73->regmap);
 		ret = PTR_ERR(cs42l73->regmap);
 		dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
 		dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
-		goto err;
+		return ret;
 	}
 	}
 	/* initialize codec */
 	/* initialize codec */
 	ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_AB, &reg);
 	ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_AB, &reg);
@@ -1384,13 +1384,13 @@ static __devinit int cs42l73_i2c_probe(struct i2c_client *i2c_client,
 		dev_err(&i2c_client->dev,
 		dev_err(&i2c_client->dev,
 			"CS42L73 Device ID (%X). Expected %X\n",
 			"CS42L73 Device ID (%X). Expected %X\n",
 			devid, CS42L73_DEVID);
 			devid, CS42L73_DEVID);
-		goto err_regmap;
+		return ret;
 	}
 	}
 
 
 	ret = regmap_read(cs42l73->regmap, CS42L73_REVID, &reg);
 	ret = regmap_read(cs42l73->regmap, CS42L73_REVID, &reg);
 	if (ret < 0) {
 	if (ret < 0) {
 		dev_err(&i2c_client->dev, "Get Revision ID failed\n");
 		dev_err(&i2c_client->dev, "Get Revision ID failed\n");
-		goto err_regmap;
+		return ret;;
 	}
 	}
 
 
 	dev_info(&i2c_client->dev,
 	dev_info(&i2c_client->dev,
@@ -1402,23 +1402,13 @@ static __devinit int cs42l73_i2c_probe(struct i2c_client *i2c_client,
 			&soc_codec_dev_cs42l73, cs42l73_dai,
 			&soc_codec_dev_cs42l73, cs42l73_dai,
 			ARRAY_SIZE(cs42l73_dai));
 			ARRAY_SIZE(cs42l73_dai));
 	if (ret < 0)
 	if (ret < 0)
-		goto err_regmap;
+		return ret;
 	return 0;
 	return 0;
-
-err_regmap:
-	regmap_exit(cs42l73->regmap);
-
-err:
-	return ret;
 }
 }
 
 
 static __devexit int cs42l73_i2c_remove(struct i2c_client *client)
 static __devexit int cs42l73_i2c_remove(struct i2c_client *client)
 {
 {
-	struct cs42l73_private *cs42l73 = i2c_get_clientdata(client);
-
 	snd_soc_unregister_codec(&client->dev);
 	snd_soc_unregister_codec(&client->dev);
-	regmap_exit(cs42l73->regmap);
-
 	return 0;
 	return 0;
 }
 }
 
 

+ 1627 - 0
sound/soc/codecs/da732x.c

@@ -0,0 +1,1627 @@
+/*
+ * da732x.c --- Dialog DA732X ALSA SoC Audio Driver
+ *
+ * Copyright (C) 2012 Dialog Semiconductor GmbH
+ *
+ * Author: Michal Hajduk <Michal.Hajduk@diasemi.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.
+ */
+
+#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/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sysfs.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 "da732x.h"
+#include "da732x_reg.h"
+
+
+struct da732x_priv {
+	struct regmap *regmap;
+	struct snd_soc_codec *codec;
+
+	unsigned int sysclk;
+	bool pll_en;
+};
+
+/*
+ * da732x register cache - default settings
+ */
+static struct reg_default da732x_reg_cache[] = {
+	{ DA732X_REG_REF1		, 0x02 },
+	{ DA732X_REG_BIAS_EN		, 0x80 },
+	{ DA732X_REG_BIAS1		, 0x00 },
+	{ DA732X_REG_BIAS2		, 0x00 },
+	{ DA732X_REG_BIAS3		, 0x00 },
+	{ DA732X_REG_BIAS4		, 0x00 },
+	{ DA732X_REG_MICBIAS2		, 0x00 },
+	{ DA732X_REG_MICBIAS1		, 0x00 },
+	{ DA732X_REG_MICDET		, 0x00 },
+	{ DA732X_REG_MIC1_PRE		, 0x01 },
+	{ DA732X_REG_MIC1		, 0x40 },
+	{ DA732X_REG_MIC2_PRE		, 0x01 },
+	{ DA732X_REG_MIC2		, 0x40 },
+	{ DA732X_REG_AUX1L		, 0x75 },
+	{ DA732X_REG_AUX1R		, 0x75 },
+	{ DA732X_REG_MIC3_PRE		, 0x01 },
+	{ DA732X_REG_MIC3		, 0x40 },
+	{ DA732X_REG_INP_PINBIAS	, 0x00 },
+	{ DA732X_REG_INP_ZC_EN		, 0x00 },
+	{ DA732X_REG_INP_MUX		, 0x50 },
+	{ DA732X_REG_HP_DET		, 0x00 },
+	{ DA732X_REG_HPL_DAC_OFFSET	, 0x00 },
+	{ DA732X_REG_HPL_DAC_OFF_CNTL	, 0x00 },
+	{ DA732X_REG_HPL_OUT_OFFSET	, 0x00 },
+	{ DA732X_REG_HPL		, 0x40 },
+	{ DA732X_REG_HPL_VOL		, 0x0F },
+	{ DA732X_REG_HPR_DAC_OFFSET	, 0x00 },
+	{ DA732X_REG_HPR_DAC_OFF_CNTL	, 0x00 },
+	{ DA732X_REG_HPR_OUT_OFFSET	, 0x00 },
+	{ DA732X_REG_HPR		, 0x40 },
+	{ DA732X_REG_HPR_VOL		, 0x0F },
+	{ DA732X_REG_LIN2		, 0x4F },
+	{ DA732X_REG_LIN3		, 0x4F },
+	{ DA732X_REG_LIN4		, 0x4F },
+	{ DA732X_REG_OUT_ZC_EN		, 0x00 },
+	{ DA732X_REG_HP_LIN1_GNDSEL	, 0x00 },
+	{ DA732X_REG_CP_HP1		, 0x0C },
+	{ DA732X_REG_CP_HP2		, 0x03 },
+	{ DA732X_REG_CP_CTRL1		, 0x00 },
+	{ DA732X_REG_CP_CTRL2		, 0x99 },
+	{ DA732X_REG_CP_CTRL3		, 0x25 },
+	{ DA732X_REG_CP_LEVEL_MASK	, 0x3F },
+	{ DA732X_REG_CP_DET		, 0x00 },
+	{ DA732X_REG_CP_STATUS		, 0x00 },
+	{ DA732X_REG_CP_THRESH1		, 0x00 },
+	{ DA732X_REG_CP_THRESH2		, 0x00 },
+	{ DA732X_REG_CP_THRESH3		, 0x00 },
+	{ DA732X_REG_CP_THRESH4		, 0x00 },
+	{ DA732X_REG_CP_THRESH5		, 0x00 },
+	{ DA732X_REG_CP_THRESH6		, 0x00 },
+	{ DA732X_REG_CP_THRESH7		, 0x00 },
+	{ DA732X_REG_CP_THRESH8		, 0x00 },
+	{ DA732X_REG_PLL_DIV_LO		, 0x00 },
+	{ DA732X_REG_PLL_DIV_MID	, 0x00 },
+	{ DA732X_REG_PLL_DIV_HI		, 0x00 },
+	{ DA732X_REG_PLL_CTRL		, 0x02 },
+	{ DA732X_REG_CLK_CTRL		, 0xaa },
+	{ DA732X_REG_CLK_DSP		, 0x07 },
+	{ DA732X_REG_CLK_EN1		, 0x00 },
+	{ DA732X_REG_CLK_EN2		, 0x00 },
+	{ DA732X_REG_CLK_EN3		, 0x00 },
+	{ DA732X_REG_CLK_EN4		, 0x00 },
+	{ DA732X_REG_CLK_EN5		, 0x00 },
+	{ DA732X_REG_AIF_MCLK		, 0x00 },
+	{ DA732X_REG_AIFA1		, 0x02 },
+	{ DA732X_REG_AIFA2		, 0x00 },
+	{ DA732X_REG_AIFA3		, 0x08 },
+	{ DA732X_REG_AIFB1		, 0x02 },
+	{ DA732X_REG_AIFB2		, 0x00 },
+	{ DA732X_REG_AIFB3		, 0x08 },
+	{ DA732X_REG_PC_CTRL		, 0xC0 },
+	{ DA732X_REG_DATA_ROUTE		, 0x00 },
+	{ DA732X_REG_DSP_CTRL		, 0x00 },
+	{ DA732X_REG_CIF_CTRL2		, 0x00 },
+	{ DA732X_REG_HANDSHAKE		, 0x00 },
+	{ DA732X_REG_SPARE1_OUT		, 0x00 },
+	{ DA732X_REG_SPARE2_OUT		, 0x00 },
+	{ DA732X_REG_SPARE1_IN		, 0x00 },
+	{ DA732X_REG_ADC1_PD		, 0x00 },
+	{ DA732X_REG_ADC1_HPF		, 0x00 },
+	{ DA732X_REG_ADC1_SEL		, 0x00 },
+	{ DA732X_REG_ADC1_EQ12		, 0x00 },
+	{ DA732X_REG_ADC1_EQ34		, 0x00 },
+	{ DA732X_REG_ADC1_EQ5		, 0x00 },
+	{ DA732X_REG_ADC2_PD		, 0x00 },
+	{ DA732X_REG_ADC2_HPF		, 0x00 },
+	{ DA732X_REG_ADC2_SEL		, 0x00 },
+	{ DA732X_REG_ADC2_EQ12		, 0x00 },
+	{ DA732X_REG_ADC2_EQ34		, 0x00 },
+	{ DA732X_REG_ADC2_EQ5		, 0x00 },
+	{ DA732X_REG_DAC1_HPF		, 0x00 },
+	{ DA732X_REG_DAC1_L_VOL		, 0x00 },
+	{ DA732X_REG_DAC1_R_VOL		, 0x00 },
+	{ DA732X_REG_DAC1_SEL		, 0x00 },
+	{ DA732X_REG_DAC1_SOFTMUTE	, 0x00 },
+	{ DA732X_REG_DAC1_EQ12		, 0x00 },
+	{ DA732X_REG_DAC1_EQ34		, 0x00 },
+	{ DA732X_REG_DAC1_EQ5		, 0x00 },
+	{ DA732X_REG_DAC2_HPF		, 0x00 },
+	{ DA732X_REG_DAC2_L_VOL		, 0x00 },
+	{ DA732X_REG_DAC2_R_VOL		, 0x00 },
+	{ DA732X_REG_DAC2_SEL		, 0x00 },
+	{ DA732X_REG_DAC2_SOFTMUTE	, 0x00 },
+	{ DA732X_REG_DAC2_EQ12		, 0x00 },
+	{ DA732X_REG_DAC2_EQ34		, 0x00 },
+	{ DA732X_REG_DAC2_EQ5		, 0x00 },
+	{ DA732X_REG_DAC3_HPF		, 0x00 },
+	{ DA732X_REG_DAC3_VOL		, 0x00 },
+	{ DA732X_REG_DAC3_SEL		, 0x00 },
+	{ DA732X_REG_DAC3_SOFTMUTE	, 0x00 },
+	{ DA732X_REG_DAC3_EQ12		, 0x00 },
+	{ DA732X_REG_DAC3_EQ34		, 0x00 },
+	{ DA732X_REG_DAC3_EQ5		, 0x00 },
+	{ DA732X_REG_BIQ_BYP		, 0x00 },
+	{ DA732X_REG_DMA_CMD		, 0x00 },
+	{ DA732X_REG_DMA_ADDR0		, 0x00 },
+	{ DA732X_REG_DMA_ADDR1		, 0x00 },
+	{ DA732X_REG_DMA_DATA0		, 0x00 },
+	{ DA732X_REG_DMA_DATA1		, 0x00 },
+	{ DA732X_REG_DMA_DATA2		, 0x00 },
+	{ DA732X_REG_DMA_DATA3		, 0x00 },
+	{ DA732X_REG_UNLOCK		, 0x00 },
+};
+
+static inline int da732x_get_input_div(struct snd_soc_codec *codec, int sysclk)
+{
+	int val;
+	int ret;
+
+	if (sysclk < DA732X_MCLK_10MHZ) {
+		val = DA732X_MCLK_RET_0_10MHZ;
+		ret = DA732X_MCLK_VAL_0_10MHZ;
+	} else if ((sysclk >= DA732X_MCLK_10MHZ) &&
+	    (sysclk < DA732X_MCLK_20MHZ)) {
+		val = DA732X_MCLK_RET_10_20MHZ;
+		ret = DA732X_MCLK_VAL_10_20MHZ;
+	} else if ((sysclk >= DA732X_MCLK_20MHZ) &&
+	    (sysclk < DA732X_MCLK_40MHZ)) {
+		val = DA732X_MCLK_RET_20_40MHZ;
+		ret = DA732X_MCLK_VAL_20_40MHZ;
+	} else if ((sysclk >= DA732X_MCLK_40MHZ) &&
+	    (sysclk <= DA732X_MCLK_54MHZ)) {
+		val = DA732X_MCLK_RET_40_54MHZ;
+		ret = DA732X_MCLK_VAL_40_54MHZ;
+	} else {
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, DA732X_REG_PLL_CTRL, val);
+
+	return ret;
+}
+
+static void da732x_set_charge_pump(struct snd_soc_codec *codec, int state)
+{
+	switch (state) {
+	case DA732X_ENABLE_CP:
+		snd_soc_write(codec, DA732X_REG_CLK_EN2, DA732X_CP_CLK_EN);
+		snd_soc_write(codec, DA732X_REG_CP_HP2, DA732X_HP_CP_EN |
+			      DA732X_HP_CP_REG | DA732X_HP_CP_PULSESKIP);
+		snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA732X_CP_EN |
+			      DA732X_CP_CTRL_CPVDD1);
+		snd_soc_write(codec, DA732X_REG_CP_CTRL2,
+			      DA732X_CP_MANAGE_MAGNITUDE | DA732X_CP_BOOST);
+		snd_soc_write(codec, DA732X_REG_CP_CTRL3, DA732X_CP_1MHZ);
+		break;
+	case DA732X_DISABLE_CP:
+		snd_soc_write(codec, DA732X_REG_CLK_EN2, DA732X_CP_CLK_DIS);
+		snd_soc_write(codec, DA732X_REG_CP_HP2, DA732X_HP_CP_DIS);
+		snd_soc_write(codec, DA732X_REG_CP_CTRL1, DA723X_CP_DIS);
+		break;
+	default:
+		pr_err(KERN_ERR "Wrong charge pump state\n");
+		break;
+	}
+}
+
+static const DECLARE_TLV_DB_SCALE(mic_boost_tlv, DA732X_MIC_PRE_VOL_DB_MIN,
+				  DA732X_MIC_PRE_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(mic_pga_tlv, DA732X_MIC_VOL_DB_MIN,
+				  DA732X_MIC_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(aux_pga_tlv, DA732X_AUX_VOL_DB_MIN,
+				  DA732X_AUX_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(hp_pga_tlv, DA732X_HP_VOL_DB_MIN,
+				  DA732X_AUX_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(lin2_pga_tlv, DA732X_LIN2_VOL_DB_MIN,
+				  DA732X_LIN2_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(lin3_pga_tlv, DA732X_LIN3_VOL_DB_MIN,
+				  DA732X_LIN3_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(lin4_pga_tlv, DA732X_LIN4_VOL_DB_MIN,
+				  DA732X_LIN4_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(adc_pga_tlv, DA732X_ADC_VOL_DB_MIN,
+				  DA732X_ADC_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(dac_pga_tlv, DA732X_DAC_VOL_DB_MIN,
+				  DA732X_DAC_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(eq_band_pga_tlv, DA732X_EQ_BAND_VOL_DB_MIN,
+				  DA732X_EQ_BAND_VOL_DB_INC, 0);
+
+static const DECLARE_TLV_DB_SCALE(eq_overall_tlv, DA732X_EQ_OVERALL_VOL_DB_MIN,
+				  DA732X_EQ_OVERALL_VOL_DB_INC, 0);
+
+/* High Pass Filter */
+static const char *da732x_hpf_mode[] = {
+	"Disable", "Music", "Voice",
+};
+
+static const char *da732x_hpf_music[] = {
+	"1.8Hz", "3.75Hz", "7.5Hz", "15Hz",
+};
+
+static const char *da732x_hpf_voice[] = {
+	"2.5Hz", "25Hz", "50Hz", "100Hz",
+	"150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static const struct soc_enum da732x_dac1_hpf_mode_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_DAC1_HPF, DA732X_HPF_MODE_SHIFT,
+			DA732X_HPF_MODE_MAX, da732x_hpf_mode)
+};
+
+static const struct soc_enum da732x_dac2_hpf_mode_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_DAC2_HPF, DA732X_HPF_MODE_SHIFT,
+			DA732X_HPF_MODE_MAX, da732x_hpf_mode)
+};
+
+static const struct soc_enum da732x_dac3_hpf_mode_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_DAC3_HPF, DA732X_HPF_MODE_SHIFT,
+			DA732X_HPF_MODE_MAX, da732x_hpf_mode)
+};
+
+static const struct soc_enum da732x_adc1_hpf_mode_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_ADC1_HPF, DA732X_HPF_MODE_SHIFT,
+			DA732X_HPF_MODE_MAX, da732x_hpf_mode)
+};
+
+static const struct soc_enum da732x_adc2_hpf_mode_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_ADC2_HPF, DA732X_HPF_MODE_SHIFT,
+			DA732X_HPF_MODE_MAX, da732x_hpf_mode)
+};
+
+static const struct soc_enum da732x_dac1_hp_filter_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_DAC1_HPF, DA732X_HPF_MUSIC_SHIFT,
+			DA732X_HPF_MUSIC_MAX, da732x_hpf_music)
+};
+
+static const struct soc_enum da732x_dac2_hp_filter_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_DAC2_HPF, DA732X_HPF_MUSIC_SHIFT,
+			DA732X_HPF_MUSIC_MAX, da732x_hpf_music)
+};
+
+static const struct soc_enum da732x_dac3_hp_filter_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_DAC3_HPF, DA732X_HPF_MUSIC_SHIFT,
+			DA732X_HPF_MUSIC_MAX, da732x_hpf_music)
+};
+
+static const struct soc_enum da732x_adc1_hp_filter_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_ADC1_HPF, DA732X_HPF_MUSIC_SHIFT,
+			DA732X_HPF_MUSIC_MAX, da732x_hpf_music)
+};
+
+static const struct soc_enum da732x_adc2_hp_filter_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_ADC2_HPF, DA732X_HPF_MUSIC_SHIFT,
+			DA732X_HPF_MUSIC_MAX, da732x_hpf_music)
+};
+
+static const struct soc_enum da732x_dac1_voice_filter_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_DAC1_HPF, DA732X_HPF_VOICE_SHIFT,
+			DA732X_HPF_VOICE_MAX, da732x_hpf_voice)
+};
+
+static const struct soc_enum da732x_dac2_voice_filter_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_DAC2_HPF, DA732X_HPF_VOICE_SHIFT,
+			DA732X_HPF_VOICE_MAX, da732x_hpf_voice)
+};
+
+static const struct soc_enum da732x_dac3_voice_filter_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_DAC3_HPF, DA732X_HPF_VOICE_SHIFT,
+			DA732X_HPF_VOICE_MAX, da732x_hpf_voice)
+};
+
+static const struct soc_enum da732x_adc1_voice_filter_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_ADC1_HPF, DA732X_HPF_VOICE_SHIFT,
+			DA732X_HPF_VOICE_MAX, da732x_hpf_voice)
+};
+
+static const struct soc_enum da732x_adc2_voice_filter_enum[] = {
+	SOC_ENUM_SINGLE(DA732X_REG_ADC2_HPF, DA732X_HPF_VOICE_SHIFT,
+			DA732X_HPF_VOICE_MAX, da732x_hpf_voice)
+};
+
+
+static int da732x_hpf_set(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value;
+	unsigned int reg = enum_ctrl->reg;
+	unsigned int sel = ucontrol->value.integer.value[0];
+	unsigned int bits;
+
+	switch (sel) {
+	case DA732X_HPF_DISABLED:
+		bits = DA732X_HPF_DIS;
+		break;
+	case DA732X_HPF_VOICE:
+		bits = DA732X_HPF_VOICE_EN;
+		break;
+	case DA732X_HPF_MUSIC:
+		bits = DA732X_HPF_MUSIC_EN;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, reg, DA732X_HPF_MASK, bits);
+
+	return 0;
+}
+
+static int da732x_hpf_get(struct snd_kcontrol *kcontrol,
+			  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	struct soc_enum *enum_ctrl = (struct soc_enum *)kcontrol->private_value;
+	unsigned int reg = enum_ctrl->reg;
+	int val;
+
+	val = snd_soc_read(codec, reg) & DA732X_HPF_MASK;
+
+	switch (val) {
+	case DA732X_HPF_VOICE_EN:
+		ucontrol->value.integer.value[0] = DA732X_HPF_VOICE;
+		break;
+	case DA732X_HPF_MUSIC_EN:
+		ucontrol->value.integer.value[0] = DA732X_HPF_MUSIC;
+		break;
+	default:
+		ucontrol->value.integer.value[0] = DA732X_HPF_DISABLED;
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new da732x_snd_controls[] = {
+	/* Input PGAs */
+	SOC_SINGLE_RANGE_TLV("MIC1 Boost Volume", DA732X_REG_MIC1_PRE,
+			     DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN,
+			     DA732X_MICBOOST_MAX, 0, mic_boost_tlv),
+	SOC_SINGLE_RANGE_TLV("MIC2 Boost Volume", DA732X_REG_MIC2_PRE,
+			     DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN,
+			     DA732X_MICBOOST_MAX, 0, mic_boost_tlv),
+	SOC_SINGLE_RANGE_TLV("MIC3 Boost Volume", DA732X_REG_MIC3_PRE,
+			     DA732X_MICBOOST_SHIFT, DA732X_MICBOOST_MIN,
+			     DA732X_MICBOOST_MAX, 0, mic_boost_tlv),
+
+	/* MICs */
+	SOC_SINGLE("MIC1 Switch", DA732X_REG_MIC1, DA732X_MIC_MUTE_SHIFT,
+		   DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_SINGLE_RANGE_TLV("MIC1 Volume", DA732X_REG_MIC1,
+			     DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN,
+			     DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv),
+	SOC_SINGLE("MIC2 Switch", DA732X_REG_MIC2, DA732X_MIC_MUTE_SHIFT,
+		   DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_SINGLE_RANGE_TLV("MIC2 Volume", DA732X_REG_MIC2,
+			     DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN,
+			     DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv),
+	SOC_SINGLE("MIC3 Switch", DA732X_REG_MIC3, DA732X_MIC_MUTE_SHIFT,
+		   DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_SINGLE_RANGE_TLV("MIC3 Volume", DA732X_REG_MIC3,
+			     DA732X_MIC_VOL_SHIFT, DA732X_MIC_VOL_VAL_MIN,
+			     DA732X_MIC_VOL_VAL_MAX, 0, mic_pga_tlv),
+
+	/* AUXs */
+	SOC_SINGLE("AUX1L Switch", DA732X_REG_AUX1L, DA732X_AUX_MUTE_SHIFT,
+		   DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_SINGLE_TLV("AUX1L Volume", DA732X_REG_AUX1L,
+		       DA732X_AUX_VOL_SHIFT, DA732X_AUX_VOL_VAL_MAX,
+		       DA732X_NO_INVERT, aux_pga_tlv),
+	SOC_SINGLE("AUX1R Switch", DA732X_REG_AUX1R, DA732X_AUX_MUTE_SHIFT,
+		   DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_SINGLE_TLV("AUX1R Volume", DA732X_REG_AUX1R,
+		       DA732X_AUX_VOL_SHIFT, DA732X_AUX_VOL_VAL_MAX,
+		       DA732X_NO_INVERT, aux_pga_tlv),
+
+	/* ADCs */
+	SOC_DOUBLE_TLV("ADC1 Volume", DA732X_REG_ADC1_SEL,
+		       DA732X_ADCL_VOL_SHIFT, DA732X_ADCR_VOL_SHIFT,
+		       DA732X_ADC_VOL_VAL_MAX, DA732X_INVERT, adc_pga_tlv),
+
+	SOC_DOUBLE_TLV("ADC2 Volume", DA732X_REG_ADC2_SEL,
+		       DA732X_ADCL_VOL_SHIFT, DA732X_ADCR_VOL_SHIFT,
+		       DA732X_ADC_VOL_VAL_MAX, DA732X_INVERT, adc_pga_tlv),
+
+	/* DACs */
+	SOC_DOUBLE("Digital Playback DAC12 Switch", DA732X_REG_DAC1_SEL,
+		   DA732X_DACL_MUTE_SHIFT, DA732X_DACR_MUTE_SHIFT,
+		   DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_DOUBLE_R_TLV("Digital Playback DAC12 Volume", DA732X_REG_DAC1_L_VOL,
+			 DA732X_REG_DAC1_R_VOL, DA732X_DAC_VOL_SHIFT,
+			 DA732X_DAC_VOL_VAL_MAX, DA732X_INVERT, dac_pga_tlv),
+	SOC_SINGLE("Digital Playback DAC3 Switch", DA732X_REG_DAC2_SEL,
+		   DA732X_DACL_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_SINGLE_TLV("Digital Playback DAC3 Volume", DA732X_REG_DAC2_L_VOL,
+			DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX,
+			DA732X_INVERT, dac_pga_tlv),
+	SOC_SINGLE("Digital Playback DAC4 Switch", DA732X_REG_DAC2_SEL,
+		   DA732X_DACR_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_SINGLE_TLV("Digital Playback DAC4 Volume", DA732X_REG_DAC2_R_VOL,
+		       DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX,
+		       DA732X_INVERT, dac_pga_tlv),
+	SOC_SINGLE("Digital Playback DAC5 Switch", DA732X_REG_DAC3_SEL,
+		   DA732X_DACL_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_SINGLE_TLV("Digital Playback DAC5 Volume", DA732X_REG_DAC3_VOL,
+		       DA732X_DAC_VOL_SHIFT, DA732X_DAC_VOL_VAL_MAX,
+		       DA732X_INVERT, dac_pga_tlv),
+
+	/* High Pass Filters */
+	SOC_ENUM_EXT("DAC1 High Pass Filter Mode",
+		     da732x_dac1_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+	SOC_ENUM("DAC1 High Pass Filter", da732x_dac1_hp_filter_enum),
+	SOC_ENUM("DAC1 Voice Filter", da732x_dac1_voice_filter_enum),
+
+	SOC_ENUM_EXT("DAC2 High Pass Filter Mode",
+		     da732x_dac2_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+	SOC_ENUM("DAC2 High Pass Filter", da732x_dac2_hp_filter_enum),
+	SOC_ENUM("DAC2 Voice Filter", da732x_dac2_voice_filter_enum),
+
+	SOC_ENUM_EXT("DAC3 High Pass Filter Mode",
+		     da732x_dac3_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+	SOC_ENUM("DAC3 High Pass Filter", da732x_dac3_hp_filter_enum),
+	SOC_ENUM("DAC3 Filter Mode", da732x_dac3_voice_filter_enum),
+
+	SOC_ENUM_EXT("ADC1 High Pass Filter Mode",
+		     da732x_adc1_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+	SOC_ENUM("ADC1 High Pass Filter", da732x_adc1_hp_filter_enum),
+	SOC_ENUM("ADC1 Voice Filter", da732x_adc1_voice_filter_enum),
+
+	SOC_ENUM_EXT("ADC2 High Pass Filter Mode",
+		     da732x_adc2_hpf_mode_enum, da732x_hpf_get, da732x_hpf_set),
+	SOC_ENUM("ADC2 High Pass Filter", da732x_adc2_hp_filter_enum),
+	SOC_ENUM("ADC2 Voice Filter", da732x_adc2_voice_filter_enum),
+
+	/* Equalizers */
+	SOC_SINGLE("ADC1 EQ Switch", DA732X_REG_ADC1_EQ5,
+		   DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+	SOC_SINGLE_TLV("ADC1 EQ Band 1 Volume", DA732X_REG_ADC1_EQ12,
+		       DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("ADC1 EQ Band 2 Volume", DA732X_REG_ADC1_EQ12,
+		       DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("ADC1 EQ Band 3 Volume", DA732X_REG_ADC1_EQ34,
+		       DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("ADC1 EQ Band 4 Volume", DA732X_REG_ADC1_EQ34,
+		       DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("ADC1 EQ Band 5 Volume", DA732X_REG_ADC1_EQ5,
+		       DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("ADC1 EQ Overall Volume", DA732X_REG_ADC1_EQ5,
+		       DA732X_EQ_OVERALL_SHIFT, DA732X_EQ_OVERALL_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_overall_tlv),
+
+	SOC_SINGLE("ADC2 EQ Switch", DA732X_REG_ADC2_EQ5,
+		   DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+	SOC_SINGLE_TLV("ADC2 EQ Band 1 Volume", DA732X_REG_ADC2_EQ12,
+		       DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("ADC2 EQ Band 2 Volume", DA732X_REG_ADC2_EQ12,
+		       DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("ADC2 EQ Band 3 Volume", DA732X_REG_ADC2_EQ34,
+		       DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("ACD2 EQ Band 4 Volume", DA732X_REG_ADC2_EQ34,
+		       DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("ACD2 EQ Band 5 Volume", DA732X_REG_ADC2_EQ5,
+		       DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("ADC2 EQ Overall Volume", DA732X_REG_ADC1_EQ5,
+		       DA732X_EQ_OVERALL_SHIFT, DA732X_EQ_OVERALL_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_overall_tlv),
+
+	SOC_SINGLE("DAC1 EQ Switch", DA732X_REG_DAC1_EQ5,
+		   DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+	SOC_SINGLE_TLV("DAC1 EQ Band 1 Volume", DA732X_REG_DAC1_EQ12,
+		       DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC1 EQ Band 2 Volume", DA732X_REG_DAC1_EQ12,
+		       DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC1 EQ Band 3 Volume", DA732X_REG_DAC1_EQ34,
+		       DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC1 EQ Band 4 Volume", DA732X_REG_DAC1_EQ34,
+		       DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC1 EQ Band 5 Volume", DA732X_REG_DAC1_EQ5,
+		       DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+
+	SOC_SINGLE("DAC2 EQ Switch", DA732X_REG_DAC2_EQ5,
+		   DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+	SOC_SINGLE_TLV("DAC2 EQ Band 1 Volume", DA732X_REG_DAC2_EQ12,
+		       DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC2 EQ Band 2 Volume", DA732X_REG_DAC2_EQ12,
+		       DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC2 EQ Band 3 Volume", DA732X_REG_DAC2_EQ34,
+		       DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC2 EQ Band 4 Volume", DA732X_REG_DAC2_EQ34,
+		       DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC2 EQ Band 5 Volume", DA732X_REG_DAC2_EQ5,
+		       DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+
+	SOC_SINGLE("DAC3 EQ Switch", DA732X_REG_DAC3_EQ5,
+		   DA732X_EQ_EN_SHIFT, DA732X_EQ_EN_MAX, DA732X_NO_INVERT),
+	SOC_SINGLE_TLV("DAC3 EQ Band 1 Volume", DA732X_REG_DAC3_EQ12,
+		       DA732X_EQ_BAND1_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC3 EQ Band 2 Volume", DA732X_REG_DAC3_EQ12,
+		       DA732X_EQ_BAND2_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC3 EQ Band 3 Volume", DA732X_REG_DAC3_EQ34,
+		       DA732X_EQ_BAND3_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC3 EQ Band 4 Volume", DA732X_REG_DAC3_EQ34,
+		       DA732X_EQ_BAND4_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+	SOC_SINGLE_TLV("DAC3 EQ Band 5 Volume", DA732X_REG_DAC3_EQ5,
+		       DA732X_EQ_BAND5_SHIFT, DA732X_EQ_VOL_VAL_MAX,
+		       DA732X_INVERT, eq_band_pga_tlv),
+
+	/* Lineout 2 Reciever*/
+	SOC_SINGLE("Lineout 2 Switch", DA732X_REG_LIN2, DA732X_LOUT_MUTE_SHIFT,
+		   DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_SINGLE_TLV("Lineout 2 Volume", DA732X_REG_LIN2,
+		       DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX,
+		       DA732X_NO_INVERT, lin2_pga_tlv),
+
+	/* Lineout 3 SPEAKER*/
+	SOC_SINGLE("Lineout 3 Switch", DA732X_REG_LIN3, DA732X_LOUT_MUTE_SHIFT,
+		   DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_SINGLE_TLV("Lineout 3 Volume", DA732X_REG_LIN3,
+		       DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX,
+		       DA732X_NO_INVERT, lin3_pga_tlv),
+
+	/* Lineout 4 */
+	SOC_SINGLE("Lineout 4 Switch", DA732X_REG_LIN4, DA732X_LOUT_MUTE_SHIFT,
+		   DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_SINGLE_TLV("Lineout 4 Volume", DA732X_REG_LIN4,
+		       DA732X_LOUT_VOL_SHIFT, DA732X_LOUT_VOL_VAL_MAX,
+		       DA732X_NO_INVERT, lin4_pga_tlv),
+
+	/* Headphones */
+	SOC_DOUBLE_R("Headphone Switch", DA732X_REG_HPR, DA732X_REG_HPL,
+		     DA732X_HP_MUTE_SHIFT, DA732X_SWITCH_MAX, DA732X_INVERT),
+	SOC_DOUBLE_R_TLV("Headphone Volume", DA732X_REG_HPL_VOL,
+			 DA732X_REG_HPR_VOL, DA732X_HP_VOL_SHIFT,
+			 DA732X_HP_VOL_VAL_MAX, DA732X_NO_INVERT, hp_pga_tlv),
+};
+
+static int da732x_adc_event(struct snd_soc_dapm_widget *w,
+			    struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		switch (w->reg) {
+		case DA732X_REG_ADC1_PD:
+			snd_soc_update_bits(codec, DA732X_REG_CLK_EN3,
+					    DA732X_ADCA_BB_CLK_EN,
+					    DA732X_ADCA_BB_CLK_EN);
+			break;
+		case DA732X_REG_ADC2_PD:
+			snd_soc_update_bits(codec, DA732X_REG_CLK_EN3,
+					    DA732X_ADCC_BB_CLK_EN,
+					    DA732X_ADCC_BB_CLK_EN);
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		snd_soc_update_bits(codec, w->reg, DA732X_ADC_RST_MASK,
+				    DA732X_ADC_SET_ACT);
+		snd_soc_update_bits(codec, w->reg, DA732X_ADC_PD_MASK,
+				    DA732X_ADC_ON);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, w->reg, DA732X_ADC_PD_MASK,
+				    DA732X_ADC_OFF);
+		snd_soc_update_bits(codec, w->reg, DA732X_ADC_RST_MASK,
+				    DA732X_ADC_SET_RST);
+
+		switch (w->reg) {
+		case DA732X_REG_ADC1_PD:
+			snd_soc_update_bits(codec, DA732X_REG_CLK_EN3,
+					    DA732X_ADCA_BB_CLK_EN, 0);
+			break;
+		case DA732X_REG_ADC2_PD:
+			snd_soc_update_bits(codec, DA732X_REG_CLK_EN3,
+					    DA732X_ADCC_BB_CLK_EN, 0);
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int da732x_out_pga_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_update_bits(codec, w->reg,
+				    (1 << w->shift) | DA732X_OUT_HIZ_EN,
+				    (1 << w->shift) | DA732X_OUT_HIZ_EN);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, w->reg,
+				    (1 << w->shift) | DA732X_OUT_HIZ_EN,
+				    (1 << w->shift) | DA732X_OUT_HIZ_DIS);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const char *adcl_text[] = {
+	"AUX1L", "MIC1"
+};
+
+static const char *adcr_text[] = {
+	"AUX1R", "MIC2", "MIC3"
+};
+
+static const char *enable_text[] = {
+	"Disabled",
+	"Enabled"
+};
+
+/* ADC1LMUX */
+static const struct soc_enum adc1l_enum =
+	SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC1L_MUX_SEL_SHIFT,
+			DA732X_ADCL_MUX_MAX, adcl_text);
+static const struct snd_kcontrol_new adc1l_mux =
+	SOC_DAPM_ENUM("ADC Route", adc1l_enum);
+
+/* ADC1RMUX */
+static const struct soc_enum adc1r_enum =
+	SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC1R_MUX_SEL_SHIFT,
+			DA732X_ADCR_MUX_MAX, adcr_text);
+static const struct snd_kcontrol_new adc1r_mux =
+	SOC_DAPM_ENUM("ADC Route", adc1r_enum);
+
+/* ADC2LMUX */
+static const struct soc_enum adc2l_enum =
+	SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC2L_MUX_SEL_SHIFT,
+			DA732X_ADCL_MUX_MAX, adcl_text);
+static const struct snd_kcontrol_new adc2l_mux =
+	SOC_DAPM_ENUM("ADC Route", adc2l_enum);
+
+/* ADC2RMUX */
+static const struct soc_enum adc2r_enum =
+	SOC_ENUM_SINGLE(DA732X_REG_INP_MUX, DA732X_ADC2R_MUX_SEL_SHIFT,
+			DA732X_ADCR_MUX_MAX, adcr_text);
+
+static const struct snd_kcontrol_new adc2r_mux =
+	SOC_DAPM_ENUM("ADC Route", adc2r_enum);
+
+static const struct soc_enum da732x_hp_left_output =
+	SOC_ENUM_SINGLE(DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN_SHIFT,
+			DA732X_DAC_EN_MAX, enable_text);
+
+static const struct snd_kcontrol_new hpl_mux =
+	SOC_DAPM_ENUM("HPL Switch", da732x_hp_left_output);
+
+static const struct soc_enum da732x_hp_right_output =
+	SOC_ENUM_SINGLE(DA732X_REG_HPR, DA732X_HP_OUT_DAC_EN_SHIFT,
+			DA732X_DAC_EN_MAX, enable_text);
+
+static const struct snd_kcontrol_new hpr_mux =
+	SOC_DAPM_ENUM("HPR Switch", da732x_hp_right_output);
+
+static const struct soc_enum da732x_speaker_output =
+	SOC_ENUM_SINGLE(DA732X_REG_LIN3, DA732X_LOUT_DAC_EN_SHIFT,
+			DA732X_DAC_EN_MAX, enable_text);
+
+static const struct snd_kcontrol_new spk_mux =
+	SOC_DAPM_ENUM("SPK Switch", da732x_speaker_output);
+
+static const struct soc_enum da732x_lout4_output =
+	SOC_ENUM_SINGLE(DA732X_REG_LIN4, DA732X_LOUT_DAC_EN_SHIFT,
+			DA732X_DAC_EN_MAX, enable_text);
+
+static const struct snd_kcontrol_new lout4_mux =
+	SOC_DAPM_ENUM("LOUT4 Switch", da732x_lout4_output);
+
+static const struct soc_enum da732x_lout2_output =
+	SOC_ENUM_SINGLE(DA732X_REG_LIN2, DA732X_LOUT_DAC_EN_SHIFT,
+			DA732X_DAC_EN_MAX, enable_text);
+
+static const struct snd_kcontrol_new lout2_mux =
+	SOC_DAPM_ENUM("LOUT2 Switch", da732x_lout2_output);
+
+static const struct snd_soc_dapm_widget da732x_dapm_widgets[] = {
+	/* Supplies */
+	SND_SOC_DAPM_SUPPLY("ADC1 Supply", DA732X_REG_ADC1_PD, 0,
+			    DA732X_NO_INVERT, da732x_adc_event,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("ADC2 Supply", DA732X_REG_ADC2_PD, 0,
+			    DA732X_NO_INVERT, da732x_adc_event,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY("DAC1 CLK", DA732X_REG_CLK_EN4,
+			    DA732X_DACA_BB_CLK_SHIFT, DA732X_NO_INVERT,
+			    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("DAC2 CLK", DA732X_REG_CLK_EN4,
+			    DA732X_DACC_BB_CLK_SHIFT, DA732X_NO_INVERT,
+			    NULL, 0),
+	SND_SOC_DAPM_SUPPLY("DAC3 CLK", DA732X_REG_CLK_EN5,
+			    DA732X_DACE_BB_CLK_SHIFT, DA732X_NO_INVERT,
+			    NULL, 0),
+
+	/* Micbias */
+	SND_SOC_DAPM_SUPPLY("MICBIAS1", DA732X_REG_MICBIAS1,
+			    DA732X_MICBIAS_EN_SHIFT,
+			    DA732X_NO_INVERT, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("MICBIAS2", DA732X_REG_MICBIAS2,
+			    DA732X_MICBIAS_EN_SHIFT,
+			    DA732X_NO_INVERT, NULL, 0),
+
+	/* Inputs */
+	SND_SOC_DAPM_INPUT("MIC1"),
+	SND_SOC_DAPM_INPUT("MIC2"),
+	SND_SOC_DAPM_INPUT("MIC3"),
+	SND_SOC_DAPM_INPUT("AUX1L"),
+	SND_SOC_DAPM_INPUT("AUX1R"),
+
+	/* Outputs */
+	SND_SOC_DAPM_OUTPUT("HPL"),
+	SND_SOC_DAPM_OUTPUT("HPR"),
+	SND_SOC_DAPM_OUTPUT("LOUTL"),
+	SND_SOC_DAPM_OUTPUT("LOUTR"),
+	SND_SOC_DAPM_OUTPUT("ClassD"),
+
+	/* ADCs */
+	SND_SOC_DAPM_ADC("ADC1L", NULL, DA732X_REG_ADC1_SEL,
+			 DA732X_ADCL_EN_SHIFT, DA732X_NO_INVERT),
+	SND_SOC_DAPM_ADC("ADC1R", NULL, DA732X_REG_ADC1_SEL,
+			 DA732X_ADCR_EN_SHIFT, DA732X_NO_INVERT),
+	SND_SOC_DAPM_ADC("ADC2L", NULL, DA732X_REG_ADC2_SEL,
+			 DA732X_ADCL_EN_SHIFT, DA732X_NO_INVERT),
+	SND_SOC_DAPM_ADC("ADC2R", NULL, DA732X_REG_ADC2_SEL,
+			 DA732X_ADCR_EN_SHIFT, DA732X_NO_INVERT),
+
+	/* DACs */
+	SND_SOC_DAPM_DAC("DAC1L", NULL, DA732X_REG_DAC1_SEL,
+			 DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT),
+	SND_SOC_DAPM_DAC("DAC1R", NULL, DA732X_REG_DAC1_SEL,
+			 DA732X_DACR_EN_SHIFT, DA732X_NO_INVERT),
+	SND_SOC_DAPM_DAC("DAC2L", NULL, DA732X_REG_DAC2_SEL,
+			 DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT),
+	SND_SOC_DAPM_DAC("DAC2R", NULL, DA732X_REG_DAC2_SEL,
+			 DA732X_DACR_EN_SHIFT, DA732X_NO_INVERT),
+	SND_SOC_DAPM_DAC("DAC3", NULL, DA732X_REG_DAC3_SEL,
+			 DA732X_DACL_EN_SHIFT, DA732X_NO_INVERT),
+
+	/* Input Pgas */
+	SND_SOC_DAPM_PGA("MIC1 PGA", DA732X_REG_MIC1, DA732X_MIC_EN_SHIFT,
+			 0, NULL, 0),
+	SND_SOC_DAPM_PGA("MIC2 PGA", DA732X_REG_MIC2, DA732X_MIC_EN_SHIFT,
+			 0, NULL, 0),
+	SND_SOC_DAPM_PGA("MIC3 PGA", DA732X_REG_MIC3, DA732X_MIC_EN_SHIFT,
+			 0, NULL, 0),
+	SND_SOC_DAPM_PGA("AUX1L PGA", DA732X_REG_AUX1L, DA732X_AUX_EN_SHIFT,
+			 0, NULL, 0),
+	SND_SOC_DAPM_PGA("AUX1R PGA", DA732X_REG_AUX1R, DA732X_AUX_EN_SHIFT,
+			 0, NULL, 0),
+
+	SND_SOC_DAPM_PGA_E("HP Left", DA732X_REG_HPL, DA732X_HP_OUT_EN_SHIFT,
+			   0, NULL, 0, da732x_out_pga_event,
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("HP Right", DA732X_REG_HPR, DA732X_HP_OUT_EN_SHIFT,
+			   0, NULL, 0, da732x_out_pga_event,
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LIN2", DA732X_REG_LIN2, DA732X_LIN_OUT_EN_SHIFT,
+			   0, NULL, 0, da732x_out_pga_event,
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LIN3", DA732X_REG_LIN3, DA732X_LIN_OUT_EN_SHIFT,
+			   0, NULL, 0, da732x_out_pga_event,
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LIN4", DA732X_REG_LIN4, DA732X_LIN_OUT_EN_SHIFT,
+			   0, NULL, 0, da732x_out_pga_event,
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* MUXs */
+	SND_SOC_DAPM_MUX("ADC1 Left MUX", SND_SOC_NOPM, 0, 0, &adc1l_mux),
+	SND_SOC_DAPM_MUX("ADC1 Right MUX", SND_SOC_NOPM, 0, 0, &adc1r_mux),
+	SND_SOC_DAPM_MUX("ADC2 Left MUX", SND_SOC_NOPM, 0, 0, &adc2l_mux),
+	SND_SOC_DAPM_MUX("ADC2 Right MUX", SND_SOC_NOPM, 0, 0, &adc2r_mux),
+
+	SND_SOC_DAPM_MUX("HP Left MUX", SND_SOC_NOPM, 0, 0, &hpl_mux),
+	SND_SOC_DAPM_MUX("HP Right MUX", SND_SOC_NOPM, 0, 0, &hpr_mux),
+	SND_SOC_DAPM_MUX("Speaker MUX", SND_SOC_NOPM, 0, 0, &spk_mux),
+	SND_SOC_DAPM_MUX("LOUT2 MUX", SND_SOC_NOPM, 0, 0, &lout2_mux),
+	SND_SOC_DAPM_MUX("LOUT4 MUX", SND_SOC_NOPM, 0, 0, &lout4_mux),
+
+	/* AIF interfaces */
+	SND_SOC_DAPM_AIF_OUT("AIFA Output", "AIFA Capture", 0, DA732X_REG_AIFA3,
+			     DA732X_AIF_EN_SHIFT, 0),
+	SND_SOC_DAPM_AIF_IN("AIFA Input", "AIFA Playback", 0, DA732X_REG_AIFA3,
+			    DA732X_AIF_EN_SHIFT, 0),
+
+	SND_SOC_DAPM_AIF_OUT("AIFB Output", "AIFB Capture", 0, DA732X_REG_AIFB3,
+			     DA732X_AIF_EN_SHIFT, 0),
+	SND_SOC_DAPM_AIF_IN("AIFB Input", "AIFB Playback", 0, DA732X_REG_AIFB3,
+			    DA732X_AIF_EN_SHIFT, 0),
+};
+
+static const struct snd_soc_dapm_route da732x_dapm_routes[] = {
+	/* Inputs */
+	{"AUX1L PGA", "NULL", "AUX1L"},
+	{"AUX1R PGA", "NULL", "AUX1R"},
+	{"MIC1 PGA", NULL, "MIC1"},
+	{"MIC2 PGA", "NULL", "MIC2"},
+	{"MIC3 PGA", "NULL", "MIC3"},
+
+	/* Capture Path */
+	{"ADC1 Left MUX", "MIC1", "MIC1 PGA"},
+	{"ADC1 Left MUX", "AUX1L", "AUX1L PGA"},
+
+	{"ADC1 Right MUX", "AUX1R", "AUX1R PGA"},
+	{"ADC1 Right MUX", "MIC2", "MIC2 PGA"},
+	{"ADC1 Right MUX", "MIC3", "MIC3 PGA"},
+
+	{"ADC2 Left MUX", "AUX1L", "AUX1L PGA"},
+	{"ADC2 Left MUX", "MIC1", "MIC1 PGA"},
+
+	{"ADC2 Right MUX", "AUX1R", "AUX1R PGA"},
+	{"ADC2 Right MUX", "MIC2", "MIC2 PGA"},
+	{"ADC2 Right MUX", "MIC3", "MIC3 PGA"},
+
+	{"ADC1L", NULL, "ADC1 Supply"},
+	{"ADC1R", NULL, "ADC1 Supply"},
+	{"ADC2L", NULL, "ADC2 Supply"},
+	{"ADC2R", NULL, "ADC2 Supply"},
+
+	{"ADC1L", NULL, "ADC1 Left MUX"},
+	{"ADC1R", NULL, "ADC1 Right MUX"},
+	{"ADC2L", NULL, "ADC2 Left MUX"},
+	{"ADC2R", NULL, "ADC2 Right MUX"},
+
+	{"AIFA Output", NULL, "ADC1L"},
+	{"AIFA Output", NULL, "ADC1R"},
+	{"AIFB Output", NULL, "ADC2L"},
+	{"AIFB Output", NULL, "ADC2R"},
+
+	{"HP Left MUX", "Enabled", "AIFA Input"},
+	{"HP Right MUX", "Enabled", "AIFA Input"},
+	{"Speaker MUX", "Enabled", "AIFB Input"},
+	{"LOUT2 MUX", "Enabled", "AIFB Input"},
+	{"LOUT4 MUX", "Enabled", "AIFB Input"},
+
+	{"DAC1L", NULL, "DAC1 CLK"},
+	{"DAC1R", NULL, "DAC1 CLK"},
+	{"DAC2L", NULL, "DAC2 CLK"},
+	{"DAC2R", NULL, "DAC2 CLK"},
+	{"DAC3", NULL, "DAC3 CLK"},
+
+	{"DAC1L", NULL, "HP Left MUX"},
+	{"DAC1R", NULL, "HP Right MUX"},
+	{"DAC2L", NULL, "Speaker MUX"},
+	{"DAC2R", NULL, "LOUT4 MUX"},
+	{"DAC3", NULL, "LOUT2 MUX"},
+
+	/* Output Pgas */
+	{"HP Left", NULL, "DAC1L"},
+	{"HP Right", NULL, "DAC1R"},
+	{"LIN3", NULL, "DAC2L"},
+	{"LIN4", NULL, "DAC2R"},
+	{"LIN2", NULL, "DAC3"},
+
+	/* Outputs */
+	{"ClassD", NULL, "LIN3"},
+	{"LOUTL", NULL, "LIN2"},
+	{"LOUTR", NULL, "LIN4"},
+	{"HPL", NULL, "HP Left"},
+	{"HPR", NULL, "HP Right"},
+};
+
+static int da732x_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;
+	u32 aif = 0;
+	u32 reg_aif;
+	u32 fs;
+
+	reg_aif = dai->driver->base;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		aif |= DA732X_AIF_WORD_16;
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		aif |= DA732X_AIF_WORD_20;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		aif |= DA732X_AIF_WORD_24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		aif |= DA732X_AIF_WORD_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (params_rate(params)) {
+	case 8000:
+		fs = DA732X_SR_8KHZ;
+		break;
+	case 11025:
+		fs = DA732X_SR_11_025KHZ;
+		break;
+	case 12000:
+		fs = DA732X_SR_12KHZ;
+		break;
+	case 16000:
+		fs = DA732X_SR_16KHZ;
+		break;
+	case 22050:
+		fs = DA732X_SR_22_05KHZ;
+		break;
+	case 24000:
+		fs = DA732X_SR_24KHZ;
+		break;
+	case 32000:
+		fs = DA732X_SR_32KHZ;
+		break;
+	case 44100:
+		fs = DA732X_SR_44_1KHZ;
+		break;
+	case 48000:
+		fs = DA732X_SR_48KHZ;
+		break;
+	case 88100:
+		fs = DA732X_SR_88_1KHZ;
+		break;
+	case 96000:
+		fs = DA732X_SR_96KHZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, reg_aif, DA732X_AIF_WORD_MASK, aif);
+	snd_soc_update_bits(codec, DA732X_REG_CLK_CTRL, DA732X_SR1_MASK, fs);
+
+	return 0;
+}
+
+static int da732x_set_dai_fmt(struct snd_soc_dai *dai, u32 fmt)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	u32 aif_mclk, pc_count;
+	u32 reg_aif1, aif1;
+	u32 reg_aif3, aif3;
+
+	switch (dai->id) {
+	case DA732X_DAI_ID1:
+		reg_aif1 = DA732X_REG_AIFA1;
+		reg_aif3 = DA732X_REG_AIFA3;
+		pc_count = DA732X_PC_PULSE_AIFA | DA732X_PC_RESYNC_NOT_AUT |
+			   DA732X_PC_SAME;
+		break;
+	case DA732X_DAI_ID2:
+		reg_aif1 = DA732X_REG_AIFB1;
+		reg_aif3 = DA732X_REG_AIFB3;
+		pc_count = DA732X_PC_PULSE_AIFB | DA732X_PC_RESYNC_NOT_AUT |
+			   DA732X_PC_SAME;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		aif1 = DA732X_AIF_SLAVE;
+		aif_mclk = DA732X_AIFM_FRAME_64 | DA732X_AIFM_SRC_SEL_AIFA;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		aif1 = DA732X_AIF_CLK_FROM_SRC;
+		aif_mclk = DA732X_CLK_GENERATION_AIF_A;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		aif3 = DA732X_AIF_I2S_MODE;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		aif3 = DA732X_AIF_RIGHT_J_MODE;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		aif3 = DA732X_AIF_LEFT_J_MODE;
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		aif3 = DA732X_AIF_DSP_MODE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* Clock inversion */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_DSP_B:
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif3 |= DA732X_AIF_BCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case SND_SOC_DAIFMT_I2S:
+	case SND_SOC_DAIFMT_RIGHT_J:
+	case SND_SOC_DAIFMT_LEFT_J:
+		switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+		case SND_SOC_DAIFMT_NB_NF:
+			break;
+		case SND_SOC_DAIFMT_IB_IF:
+			aif3 |= DA732X_AIF_BCLK_INV | DA732X_AIF_WCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_IB_NF:
+			aif3 |= DA732X_AIF_BCLK_INV;
+			break;
+		case SND_SOC_DAIFMT_NB_IF:
+			aif3 |= DA732X_AIF_WCLK_INV;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_write(codec, DA732X_REG_AIF_MCLK, aif_mclk);
+	snd_soc_update_bits(codec, reg_aif1, DA732X_AIF1_CLK_MASK, aif1);
+	snd_soc_update_bits(codec, reg_aif3, DA732X_AIF_BCLK_INV |
+			    DA732X_AIF_WCLK_INV | DA732X_AIF_MODE_MASK, aif3);
+	snd_soc_write(codec, DA732X_REG_PC_CTRL, pc_count);
+
+	return 0;
+}
+
+
+
+static int da732x_set_dai_pll(struct snd_soc_codec *codec, int pll_id,
+			      int source, unsigned int freq_in,
+			      unsigned int freq_out)
+{
+	struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+	int fref, indiv;
+	u8 div_lo, div_mid, div_hi;
+	u64 frac_div;
+
+	/* Disable PLL */
+	if (freq_out == 0) {
+		snd_soc_update_bits(codec, DA732X_REG_PLL_CTRL,
+				    DA732X_PLL_EN, 0);
+		da732x->pll_en = false;
+		return 0;
+	}
+
+	if (da732x->pll_en)
+		return -EBUSY;
+
+	if (source == DA732X_SRCCLK_MCLK) {
+		/* Validate Sysclk rate */
+		switch (da732x->sysclk) {
+		case 11290000:
+		case 12288000:
+		case 22580000:
+		case 24576000:
+		case 45160000:
+		case 49152000:
+			snd_soc_write(codec, DA732X_REG_PLL_CTRL,
+				      DA732X_PLL_BYPASS);
+			return 0;
+		default:
+			dev_err(codec->dev,
+				"Cannot use PLL Bypass, invalid SYSCLK rate\n");
+			return -EINVAL;
+		}
+	}
+
+	indiv = da732x_get_input_div(codec, da732x->sysclk);
+	if (indiv < 0)
+		return indiv;
+
+	fref = (da732x->sysclk / indiv);
+	div_hi = freq_out / fref;
+	frac_div = (u64)(freq_out % fref) * 8192ULL;
+	do_div(frac_div, fref);
+	div_mid = (frac_div >> DA732X_1BYTE_SHIFT) & DA732X_U8_MASK;
+	div_lo = (frac_div) & DA732X_U8_MASK;
+
+	snd_soc_write(codec, DA732X_REG_PLL_DIV_LO, div_lo);
+	snd_soc_write(codec, DA732X_REG_PLL_DIV_MID, div_mid);
+	snd_soc_write(codec, DA732X_REG_PLL_DIV_HI, div_hi);
+
+	snd_soc_update_bits(codec, DA732X_REG_PLL_CTRL, DA732X_PLL_EN,
+			    DA732X_PLL_EN);
+
+	da732x->pll_en = true;
+
+	return 0;
+}
+
+static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+				 unsigned int freq, int dir)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+
+	da732x->sysclk = freq;
+
+	return 0;
+}
+
+#define DA732X_RATES	SNDRV_PCM_RATE_8000_96000
+
+#define	DA732X_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 da732x_dai1_ops = {
+	.hw_params	= da732x_hw_params,
+	.set_fmt	= da732x_set_dai_fmt,
+	.set_sysclk	= da732x_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_ops da732x_dai2_ops = {
+	.hw_params	= da732x_hw_params,
+	.set_fmt	= da732x_set_dai_fmt,
+	.set_sysclk	= da732x_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver da732x_dai[] = {
+	{
+		.name	= "DA732X_AIFA",
+		.id	= DA732X_DAI_ID1,
+		.base	= DA732X_REG_AIFA1,
+		.playback = {
+			.stream_name = "AIFA Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = DA732X_RATES,
+			.formats = DA732X_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIFA Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = DA732X_RATES,
+			.formats = DA732X_FORMATS,
+		},
+		.ops = &da732x_dai1_ops,
+	},
+	{
+		.name	= "DA732X_AIFB",
+		.id	= DA732X_DAI_ID2,
+		.base	= DA732X_REG_AIFB1,
+		.playback = {
+			.stream_name = "AIFB Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = DA732X_RATES,
+			.formats = DA732X_FORMATS,
+		},
+		.capture = {
+			.stream_name = "AIFB Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = DA732X_RATES,
+			.formats = DA732X_FORMATS,
+		},
+		.ops = &da732x_dai2_ops,
+	},
+};
+
+static const struct regmap_config da732x_regmap = {
+	.reg_bits		= 8,
+	.val_bits		= 8,
+
+	.max_register		= DA732X_MAX_REG,
+	.reg_defaults		= da732x_reg_cache,
+	.num_reg_defaults	= ARRAY_SIZE(da732x_reg_cache),
+	.cache_type		= REGCACHE_RBTREE,
+};
+
+
+static void da732x_dac_offset_adjust(struct snd_soc_codec *codec)
+{
+	u8 offset[DA732X_HP_DACS];
+	u8 sign[DA732X_HP_DACS];
+	u8 step = DA732X_DAC_OFFSET_STEP;
+
+	/* Initialize DAC offset calibration circuits and registers */
+	snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+		      DA732X_HP_DAC_OFFSET_TRIM_VAL);
+	snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+		      DA732X_HP_DAC_OFFSET_TRIM_VAL);
+	snd_soc_write(codec, DA732X_REG_HPL_DAC_OFF_CNTL,
+		      DA732X_HP_DAC_OFF_CALIBRATION |
+		      DA732X_HP_DAC_OFF_SCALE_STEPS);
+	snd_soc_write(codec, DA732X_REG_HPR_DAC_OFF_CNTL,
+		      DA732X_HP_DAC_OFF_CALIBRATION |
+		      DA732X_HP_DAC_OFF_SCALE_STEPS);
+
+	/* Wait for voltage stabilization */
+	msleep(DA732X_WAIT_FOR_STABILIZATION);
+
+	/* Check DAC offset sign */
+	sign[DA732X_HPL_DAC] = (codec->hw_read(codec, DA732X_REG_HPL_DAC_OFF_CNTL) &
+				DA732X_HP_DAC_OFF_CNTL_COMPO);
+	sign[DA732X_HPR_DAC] = (codec->hw_read(codec, DA732X_REG_HPR_DAC_OFF_CNTL) &
+				DA732X_HP_DAC_OFF_CNTL_COMPO);
+
+	/* Binary search DAC offset values (both channels at once) */
+	offset[DA732X_HPL_DAC] = sign[DA732X_HPL_DAC] << DA732X_HP_DAC_COMPO_SHIFT;
+	offset[DA732X_HPR_DAC] = sign[DA732X_HPR_DAC] << DA732X_HP_DAC_COMPO_SHIFT;
+
+	do {
+		offset[DA732X_HPL_DAC] |= step;
+		offset[DA732X_HPR_DAC] |= step;
+		snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+			      ~offset[DA732X_HPL_DAC] & DA732X_HP_DAC_OFF_MASK);
+		snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+			      ~offset[DA732X_HPR_DAC] & DA732X_HP_DAC_OFF_MASK);
+
+		msleep(DA732X_WAIT_FOR_STABILIZATION);
+
+		if ((codec->hw_read(codec, DA732X_REG_HPL_DAC_OFF_CNTL) &
+		     DA732X_HP_DAC_OFF_CNTL_COMPO) ^ sign[DA732X_HPL_DAC])
+			offset[DA732X_HPL_DAC] &= ~step;
+		if ((codec->hw_read(codec, DA732X_REG_HPR_DAC_OFF_CNTL) &
+		     DA732X_HP_DAC_OFF_CNTL_COMPO) ^ sign[DA732X_HPR_DAC])
+			offset[DA732X_HPR_DAC] &= ~step;
+
+		step >>= 1;
+	} while (step);
+
+	/* Write final DAC offsets to registers */
+	snd_soc_write(codec, DA732X_REG_HPL_DAC_OFFSET,
+		      ~offset[DA732X_HPL_DAC] &	DA732X_HP_DAC_OFF_MASK);
+	snd_soc_write(codec, DA732X_REG_HPR_DAC_OFFSET,
+		      ~offset[DA732X_HPR_DAC] &	DA732X_HP_DAC_OFF_MASK);
+
+	/* End DAC calibration mode */
+	snd_soc_write(codec, DA732X_REG_HPL_DAC_OFF_CNTL,
+		DA732X_HP_DAC_OFF_SCALE_STEPS);
+	snd_soc_write(codec, DA732X_REG_HPR_DAC_OFF_CNTL,
+		DA732X_HP_DAC_OFF_SCALE_STEPS);
+}
+
+static void da732x_output_offset_adjust(struct snd_soc_codec *codec)
+{
+	u8 offset[DA732X_HP_AMPS];
+	u8 sign[DA732X_HP_AMPS];
+	u8 step = DA732X_OUTPUT_OFFSET_STEP;
+
+	offset[DA732X_HPL_AMP] = DA732X_HP_OUT_TRIM_VAL;
+	offset[DA732X_HPR_AMP] = DA732X_HP_OUT_TRIM_VAL;
+
+	/* Initialize output offset calibration circuits and registers  */
+	snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
+	snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, DA732X_HP_OUT_TRIM_VAL);
+	snd_soc_write(codec, DA732X_REG_HPL,
+		      DA732X_HP_OUT_COMP | DA732X_HP_OUT_EN);
+	snd_soc_write(codec, DA732X_REG_HPR,
+		      DA732X_HP_OUT_COMP | DA732X_HP_OUT_EN);
+
+	/* Wait for voltage stabilization */
+	msleep(DA732X_WAIT_FOR_STABILIZATION);
+
+	/* Check output offset sign */
+	sign[DA732X_HPL_AMP] = codec->hw_read(codec, DA732X_REG_HPL) &
+			       DA732X_HP_OUT_COMPO;
+	sign[DA732X_HPR_AMP] = codec->hw_read(codec, DA732X_REG_HPR) &
+			       DA732X_HP_OUT_COMPO;
+
+	snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_OUT_COMP |
+		      (sign[DA732X_HPL_AMP] >> DA732X_HP_OUT_COMPO_SHIFT) |
+		      DA732X_HP_OUT_EN);
+	snd_soc_write(codec, DA732X_REG_HPR, DA732X_HP_OUT_COMP |
+		      (sign[DA732X_HPR_AMP] >> DA732X_HP_OUT_COMPO_SHIFT) |
+		      DA732X_HP_OUT_EN);
+
+	/* Binary search output offset values (both channels at once) */
+	do {
+		offset[DA732X_HPL_AMP] |= step;
+		offset[DA732X_HPR_AMP] |= step;
+		snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET,
+			      offset[DA732X_HPL_AMP]);
+		snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET,
+			      offset[DA732X_HPR_AMP]);
+
+		msleep(DA732X_WAIT_FOR_STABILIZATION);
+
+		if ((codec->hw_read(codec, DA732X_REG_HPL) &
+		     DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPL_AMP])
+			offset[DA732X_HPL_AMP] &= ~step;
+		if ((codec->hw_read(codec, DA732X_REG_HPR) &
+		     DA732X_HP_OUT_COMPO) ^ sign[DA732X_HPR_AMP])
+			offset[DA732X_HPR_AMP] &= ~step;
+
+		step >>= 1;
+	} while (step);
+
+	/* Write final DAC offsets to registers */
+	snd_soc_write(codec, DA732X_REG_HPL_OUT_OFFSET, offset[DA732X_HPL_AMP]);
+	snd_soc_write(codec, DA732X_REG_HPR_OUT_OFFSET, offset[DA732X_HPR_AMP]);
+}
+
+static void da732x_hp_dc_offset_cancellation(struct snd_soc_codec *codec)
+{
+	/* Make sure that we have Soft Mute enabled */
+	snd_soc_write(codec, DA732X_REG_DAC1_SOFTMUTE, DA732X_SOFTMUTE_EN |
+		      DA732X_GAIN_RAMPED | DA732X_16_SAMPLES);
+	snd_soc_write(codec, DA732X_REG_DAC1_SEL, DA732X_DACL_EN |
+		      DA732X_DACR_EN | DA732X_DACL_SDM | DA732X_DACR_SDM |
+		      DA732X_DACL_MUTE | DA732X_DACR_MUTE);
+	snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_OUT_DAC_EN |
+		      DA732X_HP_OUT_MUTE | DA732X_HP_OUT_EN);
+	snd_soc_write(codec, DA732X_REG_HPR, DA732X_HP_OUT_EN |
+		      DA732X_HP_OUT_MUTE | DA732X_HP_OUT_DAC_EN);
+
+	da732x_dac_offset_adjust(codec);
+	da732x_output_offset_adjust(codec);
+
+	snd_soc_write(codec, DA732X_REG_DAC1_SEL, DA732X_DACS_DIS);
+	snd_soc_write(codec, DA732X_REG_HPL, DA732X_HP_DIS);
+	snd_soc_write(codec, DA732X_REG_HPR, DA732X_HP_DIS);
+}
+
+static int da732x_set_bias_level(struct snd_soc_codec *codec,
+				 enum snd_soc_bias_level level)
+{
+	struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		snd_soc_update_bits(codec, DA732X_REG_BIAS_EN,
+				    DA732X_BIAS_BOOST_MASK,
+				    DA732X_BIAS_BOOST_100PC);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
+			/* Init Codec */
+			snd_soc_write(codec, DA732X_REG_REF1,
+				      DA732X_VMID_FASTCHG);
+			snd_soc_write(codec, DA732X_REG_BIAS_EN,
+				      DA732X_BIAS_EN);
+
+			mdelay(DA732X_STARTUP_DELAY);
+
+			/* Disable Fast Charge and enable DAC ref voltage */
+			snd_soc_write(codec, DA732X_REG_REF1,
+				      DA732X_REFBUFX2_EN);
+
+			/* Enable bypass DSP routing */
+			snd_soc_write(codec, DA732X_REG_DATA_ROUTE,
+				      DA732X_BYPASS_DSP);
+
+			/* Enable Digital subsystem */
+			snd_soc_write(codec, DA732X_REG_DSP_CTRL,
+				      DA732X_DIGITAL_EN);
+
+			snd_soc_write(codec, DA732X_REG_SPARE1_OUT,
+				      DA732X_HP_DRIVER_EN |
+				      DA732X_HP_GATE_LOW |
+				      DA732X_HP_LOOP_GAIN_CTRL);
+			snd_soc_write(codec, DA732X_REG_HP_LIN1_GNDSEL,
+				      DA732X_HP_OUT_GNDSEL);
+
+			da732x_set_charge_pump(codec, DA732X_ENABLE_CP);
+
+			snd_soc_write(codec, DA732X_REG_CLK_EN1,
+			      DA732X_SYS3_CLK_EN | DA732X_PC_CLK_EN);
+
+			/* Enable Zero Crossing */
+			snd_soc_write(codec, DA732X_REG_INP_ZC_EN,
+				      DA732X_MIC1_PRE_ZC_EN |
+				      DA732X_MIC1_ZC_EN |
+				      DA732X_MIC2_PRE_ZC_EN |
+				      DA732X_MIC2_ZC_EN |
+				      DA732X_AUXL_ZC_EN |
+				      DA732X_AUXR_ZC_EN |
+				      DA732X_MIC3_PRE_ZC_EN |
+				      DA732X_MIC3_ZC_EN);
+			snd_soc_write(codec, DA732X_REG_OUT_ZC_EN,
+				      DA732X_HPL_ZC_EN | DA732X_HPR_ZC_EN |
+				      DA732X_LIN2_ZC_EN | DA732X_LIN3_ZC_EN |
+				      DA732X_LIN4_ZC_EN);
+
+			da732x_hp_dc_offset_cancellation(codec);
+
+			regcache_cache_only(codec->control_data, false);
+			regcache_sync(codec->control_data);
+		} else {
+			snd_soc_update_bits(codec, DA732X_REG_BIAS_EN,
+					    DA732X_BIAS_BOOST_MASK,
+					    DA732X_BIAS_BOOST_50PC);
+			snd_soc_update_bits(codec, DA732X_REG_PLL_CTRL,
+					    DA732X_PLL_EN, 0);
+			da732x->pll_en = false;
+		}
+		break;
+	case SND_SOC_BIAS_OFF:
+		regcache_cache_only(codec->control_data, true);
+		da732x_set_charge_pump(codec, DA732X_DISABLE_CP);
+		snd_soc_update_bits(codec, DA732X_REG_BIAS_EN, DA732X_BIAS_EN,
+				    DA732X_BIAS_DIS);
+		da732x->pll_en = false;
+		break;
+	}
+
+	codec->dapm.bias_level = level;
+
+	return 0;
+}
+
+static int da732x_probe(struct snd_soc_codec *codec)
+{
+	struct da732x_priv *da732x = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	int ret = 0;
+
+	da732x->codec = codec;
+
+	dapm->idle_bias_off = false;
+
+	codec->control_data = da732x->regmap;
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
+	if (ret != 0) {
+		dev_err(codec->dev, "Failed to register codec.\n");
+		goto err;
+	}
+
+	da732x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+err:
+	return ret;
+}
+
+static int da732x_remove(struct snd_soc_codec *codec)
+{
+
+	da732x_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_da732x = {
+	.probe			= da732x_probe,
+	.remove			= da732x_remove,
+	.set_bias_level		= da732x_set_bias_level,
+	.controls		= da732x_snd_controls,
+	.num_controls		= ARRAY_SIZE(da732x_snd_controls),
+	.dapm_widgets		= da732x_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(da732x_dapm_widgets),
+	.dapm_routes		= da732x_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(da732x_dapm_routes),
+	.set_pll		= da732x_set_dai_pll,
+	.reg_cache_size		= ARRAY_SIZE(da732x_reg_cache),
+};
+
+static __devinit int da732x_i2c_probe(struct i2c_client *i2c,
+				      const struct i2c_device_id *id)
+{
+	struct da732x_priv *da732x;
+	unsigned int reg;
+	int ret;
+
+	da732x = devm_kzalloc(&i2c->dev, sizeof(struct da732x_priv),
+			      GFP_KERNEL);
+	if (!da732x)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, da732x);
+
+	da732x->regmap = devm_regmap_init_i2c(i2c, &da732x_regmap);
+	if (IS_ERR(da732x->regmap)) {
+		ret = PTR_ERR(da732x->regmap);
+		dev_err(&i2c->dev, "Failed to initialize regmap\n");
+		goto err;
+	}
+
+	ret = regmap_read(da732x->regmap, DA732X_REG_ID, &reg);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret);
+		goto err;
+	}
+
+	dev_info(&i2c->dev, "Revision: %d.%d\n",
+		 (reg & DA732X_ID_MAJOR_MASK), (reg & DA732X_ID_MINOR_MASK));
+
+	ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da732x,
+				     da732x_dai, ARRAY_SIZE(da732x_dai));
+	if (ret != 0)
+		dev_err(&i2c->dev, "Failed to register codec.\n");
+
+err:
+	return ret;
+}
+
+static __devexit int da732x_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id da732x_i2c_id[] = {
+	{ "da7320", 0},
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, da732x_i2c_id);
+
+static struct i2c_driver da732x_i2c_driver = {
+	.driver		= {
+		.name	= "da7320",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= da732x_i2c_probe,
+	.remove		= __devexit_p(da732x_i2c_remove),
+	.id_table	= da732x_i2c_id,
+};
+
+module_i2c_driver(da732x_i2c_driver);
+
+
+MODULE_DESCRIPTION("ASoC DA732X driver");
+MODULE_AUTHOR("Michal Hajduk <michal.hajduk@diasemi.com>");
+MODULE_LICENSE("GPL");

+ 133 - 0
sound/soc/codecs/da732x.h

@@ -0,0 +1,133 @@
+/*
+ * da732x.h -- Dialog DA732X ALSA SoC Audio Driver Header File
+ *
+ * Copyright (C) 2012 Dialog Semiconductor GmbH
+ *
+ * Author: Michal Hajduk <Michal.Hajduk@diasemi.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 __DA732X_H_
+#define __DA732X_H
+
+#include <sound/soc.h>
+
+/* General */
+#define	DA732X_U8_MASK			0xFF
+#define	DA732X_4BYTES			4
+#define	DA732X_3BYTES			3
+#define	DA732X_2BYTES			2
+#define	DA732X_1BYTE			1
+#define	DA732X_1BYTE_SHIFT		8
+#define	DA732X_2BYTES_SHIFT		16
+#define	DA732X_3BYTES_SHIFT		24
+#define	DA732X_4BYTES_SHIFT		32
+
+#define	DA732X_DACS_DIS			0x0
+#define	DA732X_HP_DIS			0x0
+#define	DA732X_CLEAR_REG		0x0
+
+/* Calibration */
+#define	DA732X_DAC_OFFSET_STEP		0x20
+#define	DA732X_OUTPUT_OFFSET_STEP	0x80
+#define	DA732X_HP_OUT_TRIM_VAL		0x0
+#define	DA732X_WAIT_FOR_STABILIZATION	1
+#define	DA732X_HPL_DAC			0
+#define	DA732X_HPR_DAC			1
+#define	DA732X_HP_DACS			2
+#define	DA732X_HPL_AMP			0
+#define	DA732X_HPR_AMP			1
+#define	DA732X_HP_AMPS			2
+
+/* Clock settings */
+#define DA732X_STARTUP_DELAY		100
+#define	DA732X_PLL_OUT_196608		196608000
+#define	DA732X_PLL_OUT_180634		180633600
+#define	DA732X_PLL_OUT_SRM		188620800
+#define	DA732X_MCLK_10MHZ		10000000
+#define	DA732X_MCLK_20MHZ		20000000
+#define	DA732X_MCLK_40MHZ		40000000
+#define	DA732X_MCLK_54MHZ		54000000
+#define	DA732X_MCLK_RET_0_10MHZ		0
+#define	DA732X_MCLK_VAL_0_10MHZ		1
+#define	DA732X_MCLK_RET_10_20MHZ	1
+#define	DA732X_MCLK_VAL_10_20MHZ	2
+#define	DA732X_MCLK_RET_20_40MHZ	2
+#define	DA732X_MCLK_VAL_20_40MHZ	4
+#define	DA732X_MCLK_RET_40_54MHZ	3
+#define	DA732X_MCLK_VAL_40_54MHZ	8
+#define	DA732X_DAI_ID1			0
+#define	DA732X_DAI_ID2			1
+#define	DA732X_SRCCLK_PLL		0
+#define	DA732X_SRCCLK_MCLK		1
+
+#define	DA732X_LIN_LP_VOL		0x4F
+#define	DA732X_LP_VOL			0x40
+
+/* Kcontrols */
+#define	DA732X_DAC_EN_MAX		2
+#define	DA732X_ADCL_MUX_MAX		2
+#define	DA732X_ADCR_MUX_MAX		3
+#define	DA732X_HPF_MODE_MAX		3
+#define	DA732X_HPF_MODE_SHIFT		4
+#define	DA732X_HPF_MUSIC_SHIFT		0
+#define	DA732X_HPF_MUSIC_MAX		4
+#define	DA732X_HPF_VOICE_SHIFT		4
+#define	DA732X_HPF_VOICE_MAX		8
+#define	DA732X_EQ_EN_MAX		1
+#define	DA732X_HPF_VOICE		1
+#define	DA732X_HPF_MUSIC		2
+#define	DA732X_HPF_DISABLED		0
+#define	DA732X_NO_INVERT		0
+#define	DA732X_INVERT			1
+#define	DA732X_SWITCH_MAX		1
+#define	DA732X_ENABLE_CP		1
+#define	DA732X_DISABLE_CP		0
+#define	DA732X_DISABLE_ALL_CLKS		0
+#define	DA732X_RESET_ADCS		0
+
+/* dB values */
+#define DA732X_MIC_VOL_DB_MIN		0
+#define DA732X_MIC_VOL_DB_INC		50
+#define DA732X_MIC_PRE_VOL_DB_MIN	0
+#define DA732X_MIC_PRE_VOL_DB_INC	600
+#define DA732X_AUX_VOL_DB_MIN		-6000
+#define DA732X_AUX_VOL_DB_INC		150
+#define DA732X_HP_VOL_DB_MIN		-2250
+#define DA732X_HP_VOL_DB_INC		150
+#define	DA732X_LIN2_VOL_DB_MIN		-1650
+#define	DA732X_LIN2_VOL_DB_INC		150
+#define	DA732X_LIN3_VOL_DB_MIN		-1650
+#define DA732X_LIN3_VOL_DB_INC		150
+#define	DA732X_LIN4_VOL_DB_MIN		-2250
+#define DA732X_LIN4_VOL_DB_INC		150
+#define	DA732X_EQ_BAND_VOL_DB_MIN	-1050
+#define	DA732X_EQ_BAND_VOL_DB_INC	150
+#define DA732X_DAC_VOL_DB_MIN		-7725
+#define DA732X_DAC_VOL_DB_INC		75
+#define DA732X_ADC_VOL_DB_MIN		0
+#define DA732X_ADC_VOL_DB_INC		-1
+#define	DA732X_EQ_OVERALL_VOL_DB_MIN	-1800
+#define	DA732X_EQ_OVERALL_VOL_DB_INC	600
+
+#define DA732X_SOC_ENUM_DOUBLE_R(xreg, xrreg, xmax, xtext) \
+	{.reg = xreg, .reg2 = xrreg, .max = xmax, .texts = xtext}
+
+enum da732x_sysctl {
+	DA732X_SR_8KHZ		= 0x1,
+	DA732X_SR_11_025KHZ	= 0x2,
+	DA732X_SR_12KHZ		= 0x3,
+	DA732X_SR_16KHZ		= 0x5,
+	DA732X_SR_22_05KHZ	= 0x6,
+	DA732X_SR_24KHZ		= 0x7,
+	DA732X_SR_32KHZ		= 0x9,
+	DA732X_SR_44_1KHZ	= 0xA,
+	DA732X_SR_48KHZ		= 0xB,
+	DA732X_SR_88_1KHZ	= 0xE,
+	DA732X_SR_96KHZ		= 0xF,
+};
+
+#endif /* __DA732X_H_ */

+ 654 - 0
sound/soc/codecs/da732x_reg.h

@@ -0,0 +1,654 @@
+/*
+ * da732x_reg.h --- Dialog DA732X ALSA SoC Audio Registers Header File
+ *
+ * Copyright (C) 2012 Dialog Semiconductor GmbH
+ *
+ * Author: Michal Hajduk <Michal.Hajduk@diasemi.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 __DA732X_REG_H_
+#define __DA732X_REG_H_
+
+/* DA732X registers */
+#define	DA732X_REG_STATUS_EXT		0x00
+#define DA732X_REG_STATUS		0x01
+#define DA732X_REG_REF1			0x02
+#define DA732X_REG_BIAS_EN		0x03
+#define DA732X_REG_BIAS1		0x04
+#define DA732X_REG_BIAS2		0x05
+#define DA732X_REG_BIAS3		0x06
+#define DA732X_REG_BIAS4		0x07
+#define DA732X_REG_MICBIAS2		0x0F
+#define DA732X_REG_MICBIAS1		0x10
+#define DA732X_REG_MICDET		0x11
+#define DA732X_REG_MIC1_PRE		0x12
+#define DA732X_REG_MIC1			0x13
+#define DA732X_REG_MIC2_PRE		0x14
+#define DA732X_REG_MIC2			0x15
+#define DA732X_REG_AUX1L		0x16
+#define DA732X_REG_AUX1R		0x17
+#define DA732X_REG_MIC3_PRE		0x18
+#define DA732X_REG_MIC3			0x19
+#define DA732X_REG_INP_PINBIAS		0x1A
+#define DA732X_REG_INP_ZC_EN		0x1B
+#define DA732X_REG_INP_MUX		0x1D
+#define DA732X_REG_HP_DET		0x20
+#define DA732X_REG_HPL_DAC_OFFSET	0x21
+#define DA732X_REG_HPL_DAC_OFF_CNTL	0x22
+#define DA732X_REG_HPL_OUT_OFFSET	0x23
+#define DA732X_REG_HPL			0x24
+#define DA732X_REG_HPL_VOL		0x25
+#define DA732X_REG_HPR_DAC_OFFSET	0x26
+#define DA732X_REG_HPR_DAC_OFF_CNTL	0x27
+#define DA732X_REG_HPR_OUT_OFFSET	0x28
+#define DA732X_REG_HPR			0x29
+#define DA732X_REG_HPR_VOL		0x2A
+#define DA732X_REG_LIN2			0x2B
+#define DA732X_REG_LIN3			0x2C
+#define DA732X_REG_LIN4			0x2D
+#define DA732X_REG_OUT_ZC_EN		0x2E
+#define DA732X_REG_HP_LIN1_GNDSEL	0x37
+#define DA732X_REG_CP_HP1		0x3A
+#define DA732X_REG_CP_HP2		0x3B
+#define DA732X_REG_CP_CTRL1		0x40
+#define DA732X_REG_CP_CTRL2		0x41
+#define DA732X_REG_CP_CTRL3		0x42
+#define DA732X_REG_CP_LEVEL_MASK	0x43
+#define DA732X_REG_CP_DET		0x44
+#define DA732X_REG_CP_STATUS		0x45
+#define DA732X_REG_CP_THRESH1		0x46
+#define DA732X_REG_CP_THRESH2		0x47
+#define DA732X_REG_CP_THRESH3		0x48
+#define DA732X_REG_CP_THRESH4		0x49
+#define DA732X_REG_CP_THRESH5		0x4A
+#define DA732X_REG_CP_THRESH6		0x4B
+#define DA732X_REG_CP_THRESH7		0x4C
+#define DA732X_REG_CP_THRESH8		0x4D
+#define DA732X_REG_PLL_DIV_LO		0x50
+#define DA732X_REG_PLL_DIV_MID		0x51
+#define DA732X_REG_PLL_DIV_HI		0x52
+#define DA732X_REG_PLL_CTRL		0x53
+#define DA732X_REG_CLK_CTRL		0x54
+#define DA732X_REG_CLK_DSP		0x5A
+#define DA732X_REG_CLK_EN1		0x5B
+#define DA732X_REG_CLK_EN2		0x5C
+#define DA732X_REG_CLK_EN3		0x5D
+#define DA732X_REG_CLK_EN4		0x5E
+#define DA732X_REG_CLK_EN5		0x5F
+#define DA732X_REG_AIF_MCLK		0x60
+#define DA732X_REG_AIFA1		0x61
+#define DA732X_REG_AIFA2		0x62
+#define DA732X_REG_AIFA3		0x63
+#define DA732X_REG_AIFB1		0x64
+#define DA732X_REG_AIFB2		0x65
+#define DA732X_REG_AIFB3		0x66
+#define DA732X_REG_PC_CTRL		0x6A
+#define DA732X_REG_DATA_ROUTE		0x70
+#define DA732X_REG_DSP_CTRL		0x71
+#define DA732X_REG_CIF_CTRL2		0x74
+#define DA732X_REG_HANDSHAKE		0x75
+#define DA732X_REG_MBOX0		0x76
+#define DA732X_REG_MBOX1		0x77
+#define DA732X_REG_MBOX2		0x78
+#define DA732X_REG_MBOX_STATUS		0x79
+#define DA732X_REG_SPARE1_OUT		0x7D
+#define DA732X_REG_SPARE2_OUT		0x7E
+#define DA732X_REG_SPARE1_IN		0x7F
+#define DA732X_REG_ID			0x81
+#define DA732X_REG_ADC1_PD		0x90
+#define DA732X_REG_ADC1_HPF		0x93
+#define DA732X_REG_ADC1_SEL		0x94
+#define DA732X_REG_ADC1_EQ12		0x95
+#define DA732X_REG_ADC1_EQ34		0x96
+#define DA732X_REG_ADC1_EQ5		0x97
+#define DA732X_REG_ADC2_PD		0x98
+#define DA732X_REG_ADC2_HPF		0x9B
+#define DA732X_REG_ADC2_SEL		0x9C
+#define DA732X_REG_ADC2_EQ12		0x9D
+#define DA732X_REG_ADC2_EQ34		0x9E
+#define DA732X_REG_ADC2_EQ5		0x9F
+#define DA732X_REG_DAC1_HPF		0xA0
+#define DA732X_REG_DAC1_L_VOL		0xA1
+#define DA732X_REG_DAC1_R_VOL		0xA2
+#define DA732X_REG_DAC1_SEL		0xA3
+#define DA732X_REG_DAC1_SOFTMUTE	0xA4
+#define DA732X_REG_DAC1_EQ12		0xA5
+#define DA732X_REG_DAC1_EQ34		0xA6
+#define DA732X_REG_DAC1_EQ5		0xA7
+#define DA732X_REG_DAC2_HPF		0xB0
+#define DA732X_REG_DAC2_L_VOL		0xB1
+#define DA732X_REG_DAC2_R_VOL		0xB2
+#define DA732X_REG_DAC2_SEL		0xB3
+#define DA732X_REG_DAC2_SOFTMUTE	0xB4
+#define DA732X_REG_DAC2_EQ12		0xB5
+#define DA732X_REG_DAC2_EQ34		0xB6
+#define DA732X_REG_DAC2_EQ5		0xB7
+#define DA732X_REG_DAC3_HPF		0xC0
+#define DA732X_REG_DAC3_VOL		0xC1
+#define DA732X_REG_DAC3_SEL		0xC3
+#define DA732X_REG_DAC3_SOFTMUTE	0xC4
+#define DA732X_REG_DAC3_EQ12		0xC5
+#define DA732X_REG_DAC3_EQ34		0xC6
+#define DA732X_REG_DAC3_EQ5		0xC7
+#define DA732X_REG_BIQ_BYP		0xD2
+#define DA732X_REG_DMA_CMD		0xD3
+#define DA732X_REG_DMA_ADDR0		0xD4
+#define DA732X_REG_DMA_ADDR1		0xD5
+#define DA732X_REG_DMA_DATA0		0xD6
+#define DA732X_REG_DMA_DATA1		0xD7
+#define DA732X_REG_DMA_DATA2		0xD8
+#define DA732X_REG_DMA_DATA3		0xD9
+#define DA732X_REG_DMA_STATUS		0xDA
+#define DA732X_REG_BROWNOUT		0xDF
+#define DA732X_REG_UNLOCK		0xE0
+
+#define	DA732X_MAX_REG			DA732X_REG_UNLOCK
+/*
+ * Bits
+ */
+
+/* DA732X_REG_STATUS_EXT (addr=0x00) */
+#define	DA732X_STATUS_EXT_DSP			(1 << 4)
+#define	DA732X_STATUS_EXT_CLEAR			(0 << 0)
+
+/* DA732X_REG_STATUS	(addr=0x01) */
+#define DA732X_STATUS_PLL_LOCK			(1 << 0)
+#define DA732X_STATUS_PLL_MCLK_DET		(1 << 1)
+#define DA732X_STATUS_HPDET_OUT			(1 << 2)
+#define DA732X_STATUS_INP_MIXDET_1		(1 << 3)
+#define DA732X_STATUS_INP_MIXDET_2		(1 << 4)
+#define DA732X_STATUS_BO_STATUS			(1 << 5)
+
+/* DA732X_REG_REF1	(addr=0x02) */
+#define DA732X_VMID_FASTCHG			(1 << 1)
+#define DA732X_VMID_FASTDISCHG			(1 << 2)
+#define DA732X_REFBUFX2_EN			(1 << 6)
+#define DA732X_REFBUFX2_DIS			(0 << 6)
+
+/* DA732X_REG_BIAS_EN	(addr=0x03) */
+#define DA732X_BIAS_BOOST_MASK			(3 << 0)
+#define DA732X_BIAS_BOOST_100PC			(0 << 0)
+#define DA732X_BIAS_BOOST_133PC			(1 << 0)
+#define DA732X_BIAS_BOOST_88PC			(2 << 0)
+#define DA732X_BIAS_BOOST_50PC			(3 << 0)
+#define DA732X_BIAS_EN				(1 << 7)
+#define DA732X_BIAS_DIS				(0 << 7)
+
+/* DA732X_REG_BIAS1	(addr=0x04) */
+#define DA732X_BIAS1_HP_DAC_BIAS_MASK		(3 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_100PC		(0 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_150PC		(1 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_50PC		(2 << 0)
+#define DA732X_BIAS1_HP_DAC_BIAS_75PC		(3 << 0)
+#define DA732X_BIAS1_HP_OUT_BIAS_MASK		(7 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_100PC		(0 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_125PC		(1 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_150PC		(2 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_175PC		(3 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_200PC		(4 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_250PC		(5 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_300PC		(6 << 4)
+#define DA732X_BIAS1_HP_OUT_BIAS_350PC		(7 << 4)
+
+/* DA732X_REG_BIAS2	(addr=0x05) */
+#define DA732X_BIAS2_LINE2_DAC_BIAS_MASK	(3 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_100PC	(0 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_150PC	(1 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_50PC	(2 << 0)
+#define DA732X_BIAS2_LINE2_DAC_BIAS_75PC	(3 << 0)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_MASK	(7 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_100PC	(0 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_125PC	(1 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_150PC	(2 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_175PC	(3 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_200PC	(4 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_250PC	(5 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_300PC	(6 << 4)
+#define DA732X_BIAS2_LINE2_OUT_BIAS_350PC	(7 << 4)
+
+/* DA732X_REG_BIAS3	(addr=0x06) */
+#define DA732X_BIAS3_LINE3_DAC_BIAS_MASK	(3 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_100PC	(0 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_150PC	(1 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_50PC	(2 << 0)
+#define DA732X_BIAS3_LINE3_DAC_BIAS_75PC	(3 << 0)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_MASK	(7 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_100PC	(0 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_125PC	(1 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_150PC	(2 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_175PC	(3 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_200PC	(4 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_250PC	(5 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_300PC	(6 << 4)
+#define DA732X_BIAS3_LINE3_OUT_BIAS_350PC	(7 << 4)
+
+/* DA732X_REG_BIAS4	(addr=0x07) */
+#define DA732X_BIAS4_LINE4_DAC_BIAS_MASK	(3 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_100PC	(0 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_150PC	(1 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_50PC	(2 << 0)
+#define DA732X_BIAS4_LINE4_DAC_BIAS_75PC	(3 << 0)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_MASK	(7 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_100PC	(0 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_125PC	(1 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_150PC	(2 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_175PC	(3 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_200PC	(4 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_250PC	(5 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_300PC	(6 << 4)
+#define DA732X_BIAS4_LINE4_OUT_BIAS_350PC	(7 << 4)
+
+/* DA732X_REG_SIF_VDD_SEL	(addr=0x08) */
+#define DA732X_SIF_VDD_SEL_AIFA_VDD2		(1 << 0)
+#define DA732X_SIF_VDD_SEL_AIFB_VDD2		(1 << 1)
+#define DA732X_SIF_VDD_SEL_CIFA_VDD2		(1 << 4)
+
+/* DA732X_REG_MICBIAS2/1	(addr=0x0F/0x10) */
+#define DA732X_MICBIAS_VOLTAGE_MASK		(0x0F << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V		(0x00 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V05		(0x01 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V1		(0x02 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V15		(0x03 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V2		(0x04 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V25		(0x05 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V3		(0x06 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V35		(0x07 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V4		(0x08 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V45		(0x09 << 0)
+#define DA732X_MICBIAS_VOLTAGE_2V5		(0x0A << 0)
+#define DA732X_MICBIAS_EN			(1 << 7)
+#define DA732X_MICBIAS_EN_SHIFT			7
+#define DA732X_MICBIAS_VOLTAGE_SHIFT		0
+#define	DA732X_MICBIAS_VOLTAGE_MAX		0x0B
+
+/* DA732X_REG_MICDET	(addr=0x11) */
+#define DA732X_MICDET_INP_MICRES		(1 << 0)
+#define DA732X_MICDET_INP_MICHOOK		(1 << 1)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_8MS	(0 << 0)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_16MS	(1 << 0)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_32MS	(2 << 0)
+#define DA732X_MICDET_INP_DEBOUNCE_PRD_64MS	(3 << 0)
+#define DA732X_MICDET_INP_MICDET_EN		(1 << 7)
+
+/* DA732X_REG_MIC1/2/3_PRE (addr=0x11/0x14/0x18) */
+#define	DA732X_MICBOOST_MASK			0x7
+#define	DA732X_MICBOOST_SHIFT			0
+#define	DA732X_MICBOOST_MIN			0x1
+#define	DA732X_MICBOOST_MAX			DA732X_MICBOOST_MASK
+
+/* DA732X_REG_MIC1/2/3	(addr=0x13/0x15/0x19) */
+#define	DA732X_MIC_VOL_SHIFT			0
+#define	DA732X_MIC_VOL_VAL_MASK			0x1F
+#define DA732X_MIC_MUTE_SHIFT			6
+#define DA732X_MIC_EN_SHIFT			7
+#define DA732X_MIC_VOL_VAL_MIN			0x7
+#define	DA732X_MIC_VOL_VAL_MAX			DA732X_MIC_VOL_VAL_MASK
+
+/* DA732X_REG_AUX1L/R	(addr=0x16/0x17) */
+#define	DA732X_AUX_VOL_SHIFT			0
+#define	DA732X_AUX_VOL_MASK			0x7
+#define DA732X_AUX_MUTE_SHIFT			6
+#define DA732X_AUX_EN_SHIFT			7
+#define	DA732X_AUX_VOL_VAL_MAX			DA732X_AUX_VOL_MASK
+
+/* DA732X_REG_INP_PINBIAS	(addr=0x1A) */
+#define DA732X_INP_MICL_PINBIAS_EN		(1 << 0)
+#define DA732X_INP_MICR_PINBIAS_EN		(1 << 1)
+#define DA732X_INP_AUX1L_PINBIAS_EN		(1 << 2)
+#define DA732X_INP_AUX1R_PINBIAS_EN		(1 << 3)
+#define DA732X_INP_AUX2_PINBIAS_EN		(1 << 4)
+
+/* DA732X_REG_INP_ZC_EN	(addr=0x1B) */
+#define	DA732X_MIC1_PRE_ZC_EN			(1 << 0)
+#define	DA732X_MIC1_ZC_EN			(1 << 1)
+#define	DA732X_MIC2_PRE_ZC_EN			(1 << 2)
+#define	DA732X_MIC2_ZC_EN			(1 << 3)
+#define	DA732X_AUXL_ZC_EN			(1 << 4)
+#define	DA732X_AUXR_ZC_EN			(1 << 5)
+#define	DA732X_MIC3_PRE_ZC_EN			(1 << 6)
+#define	DA732X_MIC3_ZC_EN			(1 << 7)
+
+/* DA732X_REG_INP_MUX	(addr=0x1D) */
+#define DA732X_INP_ADC1L_MUX_SEL_AUX1L		(0 << 0)
+#define DA732X_INP_ADC1L_MUX_SEL_MIC1		(1 << 0)
+#define DA732X_INP_ADC1R_MUX_SEL_MASK		(3 << 2)
+#define DA732X_INP_ADC1R_MUX_SEL_AUX1R		(0 << 2)
+#define DA732X_INP_ADC1R_MUX_SEL_MIC2		(1 << 2)
+#define DA732X_INP_ADC1R_MUX_SEL_MIC3		(2 << 2)
+#define DA732X_INP_ADC2L_MUX_SEL_AUX1L		(0 << 4)
+#define DA732X_INP_ADC2L_MUX_SEL_MICL		(1 << 4)
+#define DA732X_INP_ADC2R_MUX_SEL_MASK		(3 << 6)
+#define DA732X_INP_ADC2R_MUX_SEL_AUX1R		(0 << 6)
+#define DA732X_INP_ADC2R_MUX_SEL_MICR		(1 << 6)
+#define DA732X_INP_ADC2R_MUX_SEL_AUX2		(2 << 6)
+#define	DA732X_ADC1L_MUX_SEL_SHIFT		0
+#define	DA732X_ADC1R_MUX_SEL_SHIFT		2
+#define	DA732X_ADC2L_MUX_SEL_SHIFT		4
+#define	DA732X_ADC2R_MUX_SEL_SHIFT		6
+
+/* DA732X_REG_HP_DET		(addr=0x20) */
+#define DA732X_HP_DET_AZ			(1 << 0)
+#define DA732X_HP_DET_SEL1			(1 << 1)
+#define DA732X_HP_DET_IS_MASK			(3 << 2)
+#define DA732X_HP_DET_IS_0_5UA			(0 << 2)
+#define DA732X_HP_DET_IS_1UA			(1 << 2)
+#define DA732X_HP_DET_IS_2UA			(2 << 2)
+#define DA732X_HP_DET_IS_4UA			(3 << 2)
+#define DA732X_HP_DET_RS_MASK			(3 << 4)
+#define DA732X_HP_DET_RS_INFINITE		(0 << 4)
+#define DA732X_HP_DET_RS_100KOHM		(1 << 4)
+#define DA732X_HP_DET_RS_10KOHM			(2 << 4)
+#define DA732X_HP_DET_RS_1KOHM			(3 << 4)
+#define DA732X_HP_DET_EN			(1 << 7)
+
+/* DA732X_REG_HPL_DAC_OFFSET	(addr=0x21/0x26) */
+#define DA732X_HP_DAC_OFFSET_TRIM_MASK		(0x3F << 0)
+#define DA732X_HP_DAC_OFFSET_DAC_SIGN		(1 << 6)
+
+/* DA732X_REG_HPL_DAC_OFF_CNTL	(addr=0x22/0x27) */
+#define DA732X_HP_DAC_OFF_CNTL_CONT_MASK	(7 << 0)
+#define DA732X_HP_DAC_OFF_CNTL_COMPO		(1 << 3)
+#define	DA732X_HP_DAC_OFF_CALIBRATION		(1 << 0)
+#define	DA732X_HP_DAC_OFF_SCALE_STEPS		(1 << 1)
+#define	DA732X_HP_DAC_OFF_MASK			0x7F
+#define DA732X_HP_DAC_COMPO_SHIFT		3
+
+/* DA732X_REG_HPL_OUT_OFFSET	(addr=0x23/0x28) */
+#define DA732X_HP_OUT_OFFSET_MASK		(0xFF << 0)
+#define	DA732X_HP_DAC_OFFSET_TRIM_VAL		0x7F
+
+/* DA732X_REG_HPL/R	(addr=0x24/0x29) */
+#define DA732X_HP_OUT_SIGN			(1 << 0)
+#define DA732X_HP_OUT_COMP			(1 << 1)
+#define DA732X_HP_OUT_RESERVED			(1 << 2)
+#define DA732X_HP_OUT_COMPO			(1 << 3)
+#define DA732X_HP_OUT_DAC_EN			(1 << 4)
+#define DA732X_HP_OUT_HIZ_EN			(1 << 5)
+#define	DA732X_HP_OUT_HIZ_DIS			(0 << 5)
+#define DA732X_HP_OUT_MUTE			(1 << 6)
+#define DA732X_HP_OUT_EN			(1 << 7)
+#define	DA732X_HP_OUT_COMPO_SHIFT		3
+#define	DA732X_HP_OUT_DAC_EN_SHIFT		4
+#define	DA732X_HP_HIZ_SHIFT			5
+#define	DA732X_HP_MUTE_SHIFT			6
+#define DA732X_HP_OUT_EN_SHIFT			7
+
+#define DA732X_OUT_HIZ_EN			(1 << 5)
+#define	DA732X_OUT_HIZ_DIS			(0 << 5)
+
+/* DA732X_REG_HPL/R_VOL	(addr=0x25/0x2A) */
+#define	DA732X_HP_VOL_VAL_MASK			0xF
+#define	DA732X_HP_VOL_SHIFT			0
+#define	DA732X_HP_VOL_VAL_MAX			DA732X_HP_VOL_VAL_MASK
+
+/* DA732X_REG_LIN2/3/4	(addr=0x2B/0x2C/0x2D) */
+#define DA732X_LOUT_VOL_SHIFT			0
+#define DA732X_LOUT_VOL_MASK			0x0F
+#define DA732X_LOUT_DAC_OFF			(0 << 4)
+#define DA732X_LOUT_DAC_EN			(1 << 4)
+#define DA732X_LOUT_HIZ_N_DIS			(0 << 5)
+#define DA732X_LOUT_HIZ_N_EN			(1 << 5)
+#define DA732X_LOUT_UNMUTED			(0 << 6)
+#define DA732X_LOUT_MUTED			(1 << 6)
+#define DA732X_LOUT_EN				(0 << 7)
+#define DA732X_LOUT_DIS				(1 << 7)
+#define DA732X_LOUT_DAC_EN_SHIFT		4
+#define	DA732X_LOUT_MUTE_SHIFT			6
+#define DA732X_LIN_OUT_EN_SHIFT			7
+#define DA732X_LOUT_VOL_VAL_MAX			DA732X_LOUT_VOL_MASK
+
+/* DA732X_REG_OUT_ZC_EN		(addr=0x2E) */
+#define	DA732X_HPL_ZC_EN_SHIFT			0
+#define DA732X_HPR_ZC_EN_SHIFT			1
+#define DA732X_HPL_ZC_EN			(1 << 0)
+#define DA732X_HPL_ZC_DIS			(0 << 0)
+#define DA732X_HPR_ZC_EN			(1 << 1)
+#define DA732X_HPR_ZC_DIS			(0 << 1)
+#define DA732X_LIN2_ZC_EN			(1 << 2)
+#define DA732X_LIN2_ZC_DIS			(0 << 2)
+#define DA732X_LIN3_ZC_EN			(1 << 3)
+#define DA732X_LIN3_ZC_DIS			(0 << 3)
+#define DA732X_LIN4_ZC_EN			(1 << 4)
+#define DA732X_LIN4_ZC_DIS			(0 << 4)
+
+/* DA732X_REG_HP_LIN1_GNDSEL (addr=0x37) */
+#define	DA732X_HP_OUT_GNDSEL			(1 << 0)
+
+/* DA732X_REG_CP_HP2 (addr=0x3a) */
+#define	DA732X_HP_CP_PULSESKIP			(1 << 0)
+#define	DA732X_HP_CP_REG			(1 << 1)
+#define DA732X_HP_CP_EN				(1 << 3)
+#define DA732X_HP_CP_DIS			(0 << 3)
+
+/* DA732X_REG_CP_CTRL1 (addr=0x40) */
+#define	DA732X_CP_MODE_MASK			(7 << 1)
+#define	DA732X_CP_CTRL_STANDBY			(0 << 1)
+#define	DA732X_CP_CTRL_CPVDD6			(2 << 1)
+#define	DA732X_CP_CTRL_CPVDD5			(3 << 1)
+#define	DA732X_CP_CTRL_CPVDD4			(4 << 1)
+#define	DA732X_CP_CTRL_CPVDD3			(5 << 1)
+#define	DA732X_CP_CTRL_CPVDD2			(6 << 1)
+#define	DA732X_CP_CTRL_CPVDD1			(7 << 1)
+#define	DA723X_CP_DIS				(0 << 7)
+#define	DA732X_CP_EN				(1 << 7)
+
+/* DA732X_REG_CP_CTRL2 (addr=0x41) */
+#define	DA732X_CP_BOOST				(1 << 0)
+#define	DA732X_CP_MANAGE_MAGNITUDE		(2 << 2)
+
+/* DA732X_REG_CP_CTRL3 (addr=0x42) */
+#define	DA732X_CP_1MHZ				(0 << 0)
+#define	DA732X_CP_500KHZ			(1 << 0)
+#define	DA732X_CP_250KHZ			(2 << 0)
+#define	DA732X_CP_125KHZ			(3 << 0)
+#define	DA732X_CP_63KHZ				(4 << 0)
+#define	DA732X_CP_0KHZ				(5 << 0)
+
+/* DA732X_REG_PLL_CTRL (addr=0x53) */
+#define	DA732X_PLL_INDIV_MASK			(3 << 0)
+#define	DA732X_PLL_SRM_EN			(1 << 2)
+#define	DA732X_PLL_EN				(1 << 7)
+#define	DA732X_PLL_BYPASS			(0 << 0)
+
+/* DA732X_REG_CLK_CTRL (addr=0x54) */
+#define	DA732X_SR1_MASK				(0xF)
+#define	DA732X_SR2_MASK				(0xF0)
+
+/* DA732X_REG_CLK_DSP (addr=0x5A) */
+#define	DA732X_DSP_FREQ_MASK			(7 << 0)
+#define	DA732X_DSP_FREQ_12MHZ			(0 << 0)
+#define	DA732X_DSP_FREQ_24MHZ			(1 << 0)
+#define	DA732X_DSP_FREQ_36MHZ			(2 << 0)
+#define	DA732X_DSP_FREQ_48MHZ			(3 << 0)
+#define	DA732X_DSP_FREQ_60MHZ			(4 << 0)
+#define	DA732X_DSP_FREQ_72MHZ			(5 << 0)
+#define	DA732X_DSP_FREQ_84MHZ			(6 << 0)
+#define	DA732X_DSP_FREQ_96MHZ			(7 << 0)
+
+/* DA732X_REG_CLK_EN1 (addr=0x5B) */
+#define	DA732X_DSP_CLK_EN			(1 << 0)
+#define	DA732X_SYS3_CLK_EN			(1 << 1)
+#define	DA732X_DSP12_CLK_EN			(1 << 2)
+#define	DA732X_PC_CLK_EN			(1 << 3)
+#define	DA732X_MCLK_SQR_EN			(1 << 7)
+
+/* DA732X_REG_CLK_EN2 (addr=0x5C) */
+#define	DA732X_UART_CLK_EN			(1 << 1)
+#define	DA732X_CP_CLK_EN			(1 << 2)
+#define	DA732X_CP_CLK_DIS			(0 << 2)
+
+/* DA732X_REG_CLK_EN3 (addr=0x5D) */
+#define	DA732X_ADCA_BB_CLK_EN			(1 << 0)
+#define	DA732X_ADCC_BB_CLK_EN			(1 << 4)
+
+/* DA732X_REG_CLK_EN4 (addr=0x5E) */
+#define	DA732X_DACA_BB_CLK_EN			(1 << 0)
+#define	DA732X_DACC_BB_CLK_EN			(1 << 4)
+#define DA732X_DACA_BB_CLK_SHIFT		0
+#define DA732X_DACC_BB_CLK_SHIFT		4
+
+/* DA732X_REG_CLK_EN5 (addr=0x5F) */
+#define	DA732X_DACE_BB_CLK_EN			(1 << 0)
+#define DA732X_DACE_BB_CLK_SHIFT		0
+
+/* DA732X_REG_AIF_MCLK (addr=0x60) */
+#define DA732X_AIFM_FRAME_64			(1 << 2)
+#define	DA732X_AIFM_SRC_SEL_AIFA		(1 << 6)
+#define	DA732X_CLK_GENERATION_AIF_A		(1 << 4)
+#define	DA732X_NO_CLK_GENERATION		0x0
+
+/* DA732X_REG_AIFA1 (addr=0x61) */
+#define	DA732X_AIF_WORD_MASK			(0x3 << 0)
+#define	DA732X_AIF_WORD_16			(0 << 0)
+#define	DA732X_AIF_WORD_20			(1 << 0)
+#define	DA732X_AIF_WORD_24			(2 << 0)
+#define	DA732X_AIF_WORD_32			(3 << 0)
+#define	DA732X_AIF_TDM_MONO_SHIFT		(1 << 6)
+#define	DA732X_AIF1_CLK_MASK			(1 << 7)
+#define	DA732X_AIF_SLAVE			(0 << 7)
+#define DA732X_AIF_CLK_FROM_SRC			(1 << 7)
+
+/* DA732X_REG_AIFA3 (addr=0x63) */
+#define	DA732X_AIF_MODE_SHIFT			0
+#define	DA732X_AIF_MODE_MASK			0x3
+#define	DA732X_AIF_I2S_MODE			(0 << 0)
+#define	DA732X_AIF_LEFT_J_MODE			(1 << 0)
+#define	DA732X_AIF_RIGHT_J_MODE			(2 << 0)
+#define	DA732X_AIF_DSP_MODE			(3 << 0)
+#define DA732X_AIF_WCLK_INV			(1 << 4)
+#define DA732X_AIF_BCLK_INV			(1 << 5)
+#define	DA732X_AIF_EN				(1 << 7)
+#define	DA732X_AIF_EN_SHIFT			7
+
+/* DA732X_REG_PC_CTRL (addr=0x6a) */
+#define	DA732X_PC_PULSE_AIFA			(0 << 0)
+#define	DA732X_PC_PULSE_AIFB			(1 << 0)
+#define	DA732X_PC_RESYNC_AUT			(1 << 6)
+#define	DA732X_PC_RESYNC_NOT_AUT		(0 << 6)
+#define	DA732X_PC_SAME				(1 << 7)
+
+/* DA732X_REG_DATA_ROUTE (addr=0x70) */
+#define DA732X_ADC1_TO_AIFA			(0 << 0)
+#define DA732X_DSP_TO_AIFA			(1 << 0)
+#define DA732X_ADC2_TO_AIFB			(0 << 1)
+#define DA732X_DSP_TO_AIFB			(1 << 1)
+#define DA732X_AIFA_TO_DAC1L			(0 << 2)
+#define DA732X_DSP_TO_DAC1L			(1 << 2)
+#define DA732X_AIFA_TO_DAC1R			(0 << 3)
+#define DA732X_DSP_TO_DAC1R			(1 << 3)
+#define DA732X_AIFB_TO_DAC2L			(0 << 4)
+#define DA732X_DSP_TO_DAC2L			(1 << 4)
+#define DA732X_AIFB_TO_DAC2R			(0 << 5)
+#define DA732X_DSP_TO_DAC2R			(1 << 5)
+#define DA732X_AIFB_TO_DAC3			(0 << 6)
+#define DA732X_DSP_TO_DAC3			(1 << 6)
+#define	DA732X_BYPASS_DSP			(0 << 0)
+#define	DA732X_ALL_TO_DSP			(0x7F << 0)
+
+/* DA732X_REG_DSP_CTRL (addr=0x71) */
+#define	DA732X_DIGITAL_EN			(1 << 0)
+#define	DA732X_DIGITAL_RESET			(0 << 0)
+#define	DA732X_DSP_CORE_EN			(1 << 1)
+#define	DA732X_DSP_CORE_RESET			(0 << 1)
+
+/* DA732X_REG_SPARE1_OUT (addr=0x7D)*/
+#define	DA732X_HP_DRIVER_EN			(1 << 0)
+#define	DA732X_HP_GATE_LOW			(1 << 2)
+#define DA732X_HP_LOOP_GAIN_CTRL		(1 << 3)
+
+/* DA732X_REG_ID (addr=0x81)*/
+#define DA732X_ID_MINOR_MASK			(0xF << 0)
+#define DA732X_ID_MAJOR_MASK			(0xF << 4)
+
+/* DA732X_REG_ADC1/2_PD (addr=0x90/0x98) */
+#define	DA732X_ADC_RST_MASK			(0x3 << 0)
+#define	DA732X_ADC_PD_MASK			(0x3 << 2)
+#define	DA732X_ADC_SET_ACT			(0x3 << 0)
+#define	DA732X_ADC_SET_RST			(0x0 << 0)
+#define	DA732X_ADC_ON				(0x3 << 2)
+#define	DA732X_ADC_OFF				(0x0 << 2)
+
+/* DA732X_REG_ADC1/2_SEL (addr=0x94/0x9C) */
+#define	DA732X_ADC_VOL_VAL_MASK			0x7
+#define	DA732X_ADCL_VOL_SHIFT			0
+#define	DA732X_ADCR_VOL_SHIFT			4
+#define DA732X_ADCL_EN_SHIFT			2
+#define DA732X_ADCR_EN_SHIFT			3
+#define	DA732X_ADCL_EN				(1 << 2)
+#define	DA732X_ADCR_EN				(1 << 3)
+#define	DA732X_ADC_VOL_VAL_MAX			DA732X_ADC_VOL_VAL_MASK
+
+/*
+ * DA732X_REG_ADC1/2_HPF (addr=0x93/0x9b)
+ * DA732x_REG_DAC1/2/3_HPG	(addr=0xA5/0xB5/0xC5)
+ */
+#define	DA732X_HPF_MUSIC_EN			(1 << 3)
+#define	DA732X_HPF_VOICE_EN			((1 << 3) | (1 << 7))
+#define	DA732X_HPF_MASK				((1 << 3) | (1 << 7))
+#define DA732X_HPF_DIS				((0 << 3) | (0 << 7))
+
+/* DA732X_REG_DAC1/2/3_VOL */
+#define DA732X_DAC_VOL_VAL_MASK			0x7F
+#define DA732X_DAC_VOL_SHIFT			0
+#define DA732X_DAC_VOL_VAL_MAX			DA732X_DAC_VOL_VAL_MASK
+
+/* DA732X_REG_DAC1/2/3_SEL (addr=0xA3/0xB3/0xC3) */
+#define DA732X_DACL_EN_SHIFT			3
+#define	DA732X_DACR_EN_SHIFT			7
+#define DA732X_DACL_MUTE_SHIFT			2
+#define	DA732X_DACR_MUTE_SHIFT			6
+#define DA732X_DACL_EN				(1 << 3)
+#define	DA732X_DACR_EN				(1 << 7)
+#define	DA732X_DACL_SDM				(1 << 0)
+#define	DA732X_DACR_SDM				(1 << 4)
+#define	DA732X_DACL_MUTE			(1 << 2)
+#define	DA732X_DACR_MUTE			(1 << 6)
+
+/* DA732X_REG_DAC_SOFTMUTE (addr=0xA4/0xB4/0xC4) */
+#define	DA732X_SOFTMUTE_EN			(1 << 7)
+#define	DA732X_GAIN_RAMPED			(1 << 6)
+#define	DA732X_16_SAMPLES			(4 << 0)
+#define	DA732X_SOFTMUTE_MASK			(1 << 7)
+#define	DA732X_SOFTMUTE_SHIFT			7
+
+/*
+ * DA732x_REG_ADC1/2_EQ12	(addr=0x95/0x9D)
+ * DA732x_REG_ADC1/2_EQ34	(addr=0x96/0x9E)
+ * DA732x_REG_ADC1/2_EQ5	(addr=0x97/0x9F)
+ * DA732x_REG_DAC1/2/3_EQ12	(addr=0xA5/0xB5/0xC5)
+ * DA732x_REG_DAC1/2/3_EQ34	(addr=0xA6/0xB6/0xC6)
+ * DA732x_REG_DAC1/2/3_EQ5	(addr=0xA7/0xB7/0xB7)
+ */
+#define	DA732X_EQ_VOL_VAL_MASK			0xF
+#define	DA732X_EQ_BAND1_SHIFT			0
+#define	DA732X_EQ_BAND2_SHIFT			4
+#define	DA732X_EQ_BAND3_SHIFT			0
+#define	DA732X_EQ_BAND4_SHIFT			4
+#define	DA732X_EQ_BAND5_SHIFT			0
+#define	DA732X_EQ_OVERALL_SHIFT			4
+#define	DA732X_EQ_OVERALL_VOL_VAL_MASK		0x3
+#define	DA732X_EQ_DIS				(0 << 7)
+#define	DA732X_EQ_EN				(1 << 7)
+#define	DA732X_EQ_EN_SHIFT			7
+#define	DA732X_EQ_VOL_VAL_MAX			DA732X_EQ_VOL_VAL_MASK
+#define	DA732X_EQ_OVERALL_VOL_VAL_MAX		DA732X_EQ_OVERALL_VOL_VAL_MASK
+
+/* DA732X_REG_DMA_CMD (addr=0xD3) */
+#define	DA732X_SEL_DSP_DMA_MASK			(3 << 0)
+#define	DA732X_SEL_DSP_DMA_DIS			(0 << 0)
+#define	DA732X_SEL_DSP_DMA_PMEM			(1 << 0)
+#define	DA732X_SEL_DSP_DMA_XMEM			(2 << 0)
+#define	DA732X_SEL_DSP_DMA_YMEM			(3 << 0)
+#define	DA732X_DSP_RW_MASK			(1 << 4)
+#define	DA732X_DSP_DMA_WRITE			(0 << 4)
+#define	DA732X_DSP_DMA_READ			(1 << 4)
+
+/* DA732X_REG_DMA_STATUS (addr=0xDA) */
+#define	DA732X_DSP_DMA_FREE			(0 << 0)
+#define	DA732X_DSP_DMA_BUSY			(1 << 0)
+
+#endif /* __DA732X_REG_H_ */

+ 1176 - 0
sound/soc/codecs/isabelle.c

@@ -0,0 +1,1176 @@
+/*
+ * isabelle.c - Low power high fidelity audio codec driver
+ *
+ * Copyright (c) 2012 Texas Instruments, Inc
+ *
+ * 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.
+ *
+ *
+ * Initially based on sound/soc/codecs/twl6040.c
+ *
+ */
+#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/regmap.h>
+#include <linux/i2c.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/tlv.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <asm/div64.h>
+#include "isabelle.h"
+
+
+/* Register default values for ISABELLE driver. */
+static struct reg_default isabelle_reg_defs[] = {
+	{ 0, 0x00 },
+	{ 1, 0x00 },
+	{ 2, 0x00 },
+	{ 3, 0x00 },
+	{ 4, 0x00 },
+	{ 5, 0x00 },
+	{ 6, 0x00 },
+	{ 7, 0x00 },
+	{ 8, 0x00 },
+	{ 9, 0x00 },
+	{ 10, 0x00 },
+	{ 11, 0x00 },
+	{ 12, 0x00 },
+	{ 13, 0x00 },
+	{ 14, 0x00 },
+	{ 15, 0x00 },
+	{ 16, 0x00 },
+	{ 17, 0x00 },
+	{ 18, 0x00 },
+	{ 19, 0x00 },
+	{ 20, 0x00 },
+	{ 21, 0x02 },
+	{ 22, 0x02 },
+	{ 23, 0x02 },
+	{ 24, 0x02 },
+	{ 25, 0x0F },
+	{ 26, 0x8F },
+	{ 27, 0x0F },
+	{ 28, 0x8F },
+	{ 29, 0x00 },
+	{ 30, 0x00 },
+	{ 31, 0x00 },
+	{ 32, 0x00 },
+	{ 33, 0x00 },
+	{ 34, 0x00 },
+	{ 35, 0x00 },
+	{ 36, 0x00 },
+	{ 37, 0x00 },
+	{ 38, 0x00 },
+	{ 39, 0x00 },
+	{ 40, 0x00 },
+	{ 41, 0x00 },
+	{ 42, 0x00 },
+	{ 43, 0x00 },
+	{ 44, 0x00 },
+	{ 45, 0x00 },
+	{ 46, 0x00 },
+	{ 47, 0x00 },
+	{ 48, 0x00 },
+	{ 49, 0x00 },
+	{ 50, 0x00 },
+	{ 51, 0x00 },
+	{ 52, 0x00 },
+	{ 53, 0x00 },
+	{ 54, 0x00 },
+	{ 55, 0x00 },
+	{ 56, 0x00 },
+	{ 57, 0x00 },
+	{ 58, 0x00 },
+	{ 59, 0x00 },
+	{ 60, 0x00 },
+	{ 61, 0x00 },
+	{ 62, 0x00 },
+	{ 63, 0x00 },
+	{ 64, 0x00 },
+	{ 65, 0x00 },
+	{ 66, 0x00 },
+	{ 67, 0x00 },
+	{ 68, 0x00 },
+	{ 69, 0x90 },
+	{ 70, 0x90 },
+	{ 71, 0x90 },
+	{ 72, 0x00 },
+	{ 73, 0x00 },
+	{ 74, 0x00 },
+	{ 75, 0x00 },
+	{ 76, 0x00 },
+	{ 77, 0x00 },
+	{ 78, 0x00 },
+	{ 79, 0x00 },
+	{ 80, 0x00 },
+	{ 81, 0x00 },
+	{ 82, 0x00 },
+	{ 83, 0x00 },
+	{ 84, 0x00 },
+	{ 85, 0x07 },
+	{ 86, 0x00 },
+	{ 87, 0x00 },
+	{ 88, 0x00 },
+	{ 89, 0x07 },
+	{ 90, 0x80 },
+	{ 91, 0x07 },
+	{ 92, 0x07 },
+	{ 93, 0x00 },
+	{ 94, 0x00 },
+	{ 95, 0x00 },
+	{ 96, 0x00 },
+	{ 97, 0x00 },
+	{ 98, 0x00 },
+	{ 99, 0x00 },
+};
+
+static const char *isabelle_rx1_texts[] = {"VRX1", "ARX1"};
+static const char *isabelle_rx2_texts[] = {"VRX2", "ARX2"};
+
+static const struct soc_enum isabelle_rx1_enum[] = {
+	SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 3, 1, isabelle_rx1_texts),
+	SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 5, 1, isabelle_rx1_texts),
+};
+
+static const struct soc_enum isabelle_rx2_enum[] = {
+	SOC_ENUM_SINGLE(ISABELLE_VOICE_HPF_CFG_REG, 2, 1, isabelle_rx2_texts),
+	SOC_ENUM_SINGLE(ISABELLE_AUDIO_HPF_CFG_REG, 4, 1, isabelle_rx2_texts),
+};
+
+/* Headset DAC playback switches */
+static const struct snd_kcontrol_new rx1_mux_controls =
+	SOC_DAPM_ENUM("Route", isabelle_rx1_enum);
+
+static const struct snd_kcontrol_new rx2_mux_controls =
+	SOC_DAPM_ENUM("Route", isabelle_rx2_enum);
+
+/* TX input selection */
+static const char *isabelle_atx_texts[] = {"AMIC1", "DMIC"};
+static const char *isabelle_vtx_texts[] = {"AMIC2", "DMIC"};
+
+static const struct soc_enum isabelle_atx_enum[] = {
+	SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 7, 1, isabelle_atx_texts),
+	SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0, 1, isabelle_atx_texts),
+};
+
+static const struct soc_enum isabelle_vtx_enum[] = {
+	SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 6, 1, isabelle_vtx_texts),
+	SOC_ENUM_SINGLE(ISABELLE_DMIC_CFG_REG, 0, 1, isabelle_vtx_texts),
+};
+
+static const struct snd_kcontrol_new atx_mux_controls =
+	SOC_DAPM_ENUM("Route", isabelle_atx_enum);
+
+static const struct snd_kcontrol_new vtx_mux_controls =
+	SOC_DAPM_ENUM("Route", isabelle_vtx_enum);
+
+/* Left analog microphone selection */
+static const char *isabelle_amic1_texts[] = {
+	"Main Mic", "Headset Mic", "Aux/FM Left"};
+
+/* Left analog microphone selection */
+static const char *isabelle_amic2_texts[] = {"Sub Mic", "Aux/FM Right"};
+
+static const struct soc_enum isabelle_amic1_enum[] = {
+	SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 5,
+			ARRAY_SIZE(isabelle_amic1_texts),
+			isabelle_amic1_texts),
+};
+
+static const struct soc_enum isabelle_amic2_enum[] = {
+	SOC_ENUM_SINGLE(ISABELLE_AMIC_CFG_REG, 4,
+			ARRAY_SIZE(isabelle_amic2_texts),
+			isabelle_amic2_texts),
+};
+
+static const struct snd_kcontrol_new amic1_control =
+	SOC_DAPM_ENUM("Route", isabelle_amic1_enum);
+
+static const struct snd_kcontrol_new amic2_control =
+	SOC_DAPM_ENUM("Route", isabelle_amic2_enum);
+
+static const char *isabelle_st_audio_texts[] = {"ATX1", "ATX2"};
+
+static const char *isabelle_st_voice_texts[] = {"VTX1", "VTX2"};
+
+static const struct soc_enum isabelle_st_audio_enum[] = {
+	SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA1_CFG_REG, 7, 1,
+			isabelle_st_audio_texts),
+	SOC_ENUM_SINGLE(ISABELLE_ATX_STPGA2_CFG_REG, 7, 1,
+			isabelle_st_audio_texts),
+};
+
+static const struct soc_enum isabelle_st_voice_enum[] = {
+	SOC_ENUM_SINGLE(ISABELLE_VTX_STPGA1_CFG_REG, 7, 1,
+			isabelle_st_voice_texts),
+	SOC_ENUM_SINGLE(ISABELLE_VTX2_STPGA2_CFG_REG, 7, 1,
+			isabelle_st_voice_texts),
+};
+
+static const struct snd_kcontrol_new st_audio_control =
+	SOC_DAPM_ENUM("Route", isabelle_st_audio_enum);
+
+static const struct snd_kcontrol_new st_voice_control =
+	SOC_DAPM_ENUM("Route", isabelle_st_voice_enum);
+
+/* Mixer controls */
+static const struct snd_kcontrol_new isabelle_hs_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC1L Playback Switch", ISABELLE_HSDRV_CFG1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_HSDRV_CFG1_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_hs_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC1R Playback Switch", ISABELLE_HSDRV_CFG1_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_HSDRV_CFG1_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_hf_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC2L Playback Switch", ISABELLE_HFLPGA_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_HFLPGA_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_hf_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC2R Playback Switch", ISABELLE_HFRPGA_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_HFRPGA_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_ep_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC2L Playback Switch", ISABELLE_EARDRV_CFG1_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_EARDRV_CFG1_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_aux_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC3L Playback Switch", ISABELLE_LINEAMP_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("APGA1 Playback Switch", ISABELLE_LINEAMP_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_aux_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("DAC3R Playback Switch", ISABELLE_LINEAMP_CFG_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("APGA2 Playback Switch", ISABELLE_LINEAMP_CFG_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga1_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga1_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA1LR_IN_SEL_REG, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga2_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 4, 1, 0),
+SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA2L_IN_SEL_REG, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga2_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("USNC Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA2R_IN_SEL_REG, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga3_left_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX1 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("RX3 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 6, 1, 0),
+SOC_DAPM_SINGLE("RX5 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 5, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_dpga3_right_mixer_controls[] = {
+SOC_DAPM_SINGLE("RX2 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("RX4 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 2, 1, 0),
+SOC_DAPM_SINGLE("RX6 Playback Switch", ISABELLE_DPGA3LR_IN_SEL_REG, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx1_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DL1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx2_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("DL2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx3_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 3, 1, 0),
+SOC_DAPM_SINGLE("DL3 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx4_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 1, 1, 0),
+SOC_DAPM_SINGLE("DL4 Playback Switch", ISABELLE_RX_INPUT_CFG_REG, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx5_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST1 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 7, 1, 0),
+SOC_DAPM_SINGLE("DL5 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new isabelle_rx6_mixer_controls[] = {
+SOC_DAPM_SINGLE("ST2 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 5, 1, 0),
+SOC_DAPM_SINGLE("DL6 Playback Switch", ISABELLE_RX_INPUT_CFG2_REG, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new ep_path_enable_control =
+	SOC_DAPM_SINGLE("Switch", ISABELLE_EARDRV_CFG2_REG, 0, 1, 0);
+
+/* TLV Declarations */
+static const DECLARE_TLV_DB_SCALE(mic_amp_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(afm_amp_tlv, -3300, 300, 0);
+static const DECLARE_TLV_DB_SCALE(dac_tlv, -1200, 200, 0);
+static const DECLARE_TLV_DB_SCALE(hf_tlv, -5000, 200, 0);
+
+/* from -63 to 0 dB in 1 dB steps */
+static const DECLARE_TLV_DB_SCALE(dpga_tlv, -6300, 100, 1);
+
+/* from -63 to 9 dB in 1 dB steps */
+static const DECLARE_TLV_DB_SCALE(rx_tlv, -6300, 100, 1);
+
+static const DECLARE_TLV_DB_SCALE(st_tlv, -2700, 300, 1);
+static const DECLARE_TLV_DB_SCALE(tx_tlv, -600, 100, 0);
+
+static const struct snd_kcontrol_new isabelle_snd_controls[] = {
+	SOC_DOUBLE_TLV("Headset Playback Volume", ISABELLE_HSDRV_GAIN_REG,
+			4, 0, 0xF, 0, dac_tlv),
+	SOC_DOUBLE_R_TLV("Handsfree Playback Volume",
+			ISABELLE_HFLPGA_CFG_REG, ISABELLE_HFRPGA_CFG_REG,
+			0, 0x1F, 0, hf_tlv),
+	SOC_DOUBLE_TLV("Aux Playback Volume", ISABELLE_LINEAMP_GAIN_REG,
+			4, 0, 0xF, 0, dac_tlv),
+	SOC_SINGLE_TLV("Earpiece Playback Volume", ISABELLE_EARDRV_CFG1_REG,
+			0, 0xF, 0, dac_tlv),
+
+	SOC_DOUBLE_TLV("Aux FM Volume", ISABELLE_APGA_GAIN_REG, 4, 0, 0xF, 0,
+			afm_amp_tlv),
+	SOC_SINGLE_TLV("Mic1 Capture Volume", ISABELLE_MIC1_GAIN_REG, 3, 0x1F,
+			0, mic_amp_tlv),
+	SOC_SINGLE_TLV("Mic2 Capture Volume", ISABELLE_MIC2_GAIN_REG, 3, 0x1F,
+			0, mic_amp_tlv),
+
+	SOC_DOUBLE_R_TLV("DPGA1 Volume", ISABELLE_DPGA1L_GAIN_REG,
+			ISABELLE_DPGA1R_GAIN_REG, 0, 0x3F, 0, dpga_tlv),
+	SOC_DOUBLE_R_TLV("DPGA2 Volume", ISABELLE_DPGA2L_GAIN_REG,
+			ISABELLE_DPGA2R_GAIN_REG, 0, 0x3F, 0, dpga_tlv),
+	SOC_DOUBLE_R_TLV("DPGA3 Volume", ISABELLE_DPGA3L_GAIN_REG,
+			ISABELLE_DPGA3R_GAIN_REG, 0, 0x3F, 0, dpga_tlv),
+
+	SOC_SINGLE_TLV("Sidetone Audio TX1 Volume",
+			ISABELLE_ATX_STPGA1_CFG_REG, 0, 0xF, 0, st_tlv),
+	SOC_SINGLE_TLV("Sidetone Audio TX2 Volume",
+			ISABELLE_ATX_STPGA2_CFG_REG, 0, 0xF, 0, st_tlv),
+	SOC_SINGLE_TLV("Sidetone Voice TX1 Volume",
+			ISABELLE_VTX_STPGA1_CFG_REG, 0, 0xF, 0, st_tlv),
+	SOC_SINGLE_TLV("Sidetone Voice TX2 Volume",
+			ISABELLE_VTX2_STPGA2_CFG_REG, 0, 0xF, 0, st_tlv),
+
+	SOC_SINGLE_TLV("Audio TX1 Volume", ISABELLE_ATX1_DPGA_REG, 4, 0xF, 0,
+			tx_tlv),
+	SOC_SINGLE_TLV("Audio TX2 Volume", ISABELLE_ATX2_DPGA_REG, 4, 0xF, 0,
+			tx_tlv),
+	SOC_SINGLE_TLV("Voice TX1 Volume", ISABELLE_VTX1_DPGA_REG, 4, 0xF, 0,
+			tx_tlv),
+	SOC_SINGLE_TLV("Voice TX2 Volume", ISABELLE_VTX2_DPGA_REG, 4, 0xF, 0,
+			tx_tlv),
+
+	SOC_SINGLE_TLV("RX1 DPGA Volume", ISABELLE_RX1_DPGA_REG, 0, 0x3F, 0,
+			rx_tlv),
+	SOC_SINGLE_TLV("RX2 DPGA Volume", ISABELLE_RX2_DPGA_REG, 0, 0x3F, 0,
+			rx_tlv),
+	SOC_SINGLE_TLV("RX3 DPGA Volume", ISABELLE_RX3_DPGA_REG, 0, 0x3F, 0,
+			rx_tlv),
+	SOC_SINGLE_TLV("RX4 DPGA Volume", ISABELLE_RX4_DPGA_REG, 0, 0x3F, 0,
+			rx_tlv),
+	SOC_SINGLE_TLV("RX5 DPGA Volume", ISABELLE_RX5_DPGA_REG, 0, 0x3F, 0,
+			rx_tlv),
+	SOC_SINGLE_TLV("RX6 DPGA Volume", ISABELLE_RX6_DPGA_REG, 0, 0x3F, 0,
+			rx_tlv),
+
+	SOC_SINGLE("Headset Noise Gate", ISABELLE_HS_NG_CFG1_REG, 7, 1, 0),
+	SOC_SINGLE("Handsfree Noise Gate", ISABELLE_HF_NG_CFG1_REG, 7, 1, 0),
+
+	SOC_SINGLE("ATX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+		7, 1, 0),
+	SOC_SINGLE("ATX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+		6, 1, 0),
+	SOC_SINGLE("ARX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+		5, 1, 0),
+	SOC_SINGLE("ARX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+		4, 1, 0),
+	SOC_SINGLE("ARX3 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+		3, 1, 0),
+	SOC_SINGLE("ARX4 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+		2, 1, 0),
+	SOC_SINGLE("ARX5 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+		1, 1, 0),
+	SOC_SINGLE("ARX6 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+		0, 1, 0),
+	SOC_SINGLE("VRX1 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+		3, 1, 0),
+	SOC_SINGLE("VRX2 Filter Bypass Switch", ISABELLE_AUDIO_HPF_CFG_REG,
+		2, 1, 0),
+
+	SOC_SINGLE("ATX1 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+		7, 1, 0),
+	SOC_SINGLE("ATX2 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+		6, 1, 0),
+	SOC_SINGLE("VTX1 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+		5, 1, 0),
+	SOC_SINGLE("VTX2 Filter Enable Switch", ISABELLE_ALU_TX_EN_REG,
+		4, 1, 0),
+	SOC_SINGLE("RX1 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+		5, 1, 0),
+	SOC_SINGLE("RX2 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+		4, 1, 0),
+	SOC_SINGLE("RX3 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+		3, 1, 0),
+	SOC_SINGLE("RX4 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+		2, 1, 0),
+	SOC_SINGLE("RX5 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+		1, 1, 0),
+	SOC_SINGLE("RX6 Filter Enable Switch", ISABELLE_ALU_RX_EN_REG,
+		0, 1, 0),
+
+	SOC_SINGLE("ULATX12 Capture Switch", ISABELLE_ULATX12_INTF_CFG_REG,
+		7, 1, 0),
+
+	SOC_SINGLE("DL12 Playback Switch", ISABELLE_DL12_INTF_CFG_REG,
+		7, 1, 0),
+	SOC_SINGLE("DL34 Playback Switch", ISABELLE_DL34_INTF_CFG_REG,
+		7, 1, 0),
+	SOC_SINGLE("DL56 Playback Switch", ISABELLE_DL56_INTF_CFG_REG,
+		7, 1, 0),
+
+	/* DMIC Switch */
+	SOC_SINGLE("DMIC Switch", ISABELLE_DMIC_CFG_REG, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget isabelle_dapm_widgets[] = {
+	/* Inputs */
+	SND_SOC_DAPM_INPUT("MAINMIC"),
+	SND_SOC_DAPM_INPUT("HSMIC"),
+	SND_SOC_DAPM_INPUT("SUBMIC"),
+	SND_SOC_DAPM_INPUT("LINEIN1"),
+	SND_SOC_DAPM_INPUT("LINEIN2"),
+	SND_SOC_DAPM_INPUT("DMICDAT"),
+
+	/* Outputs */
+	SND_SOC_DAPM_OUTPUT("HSOL"),
+	SND_SOC_DAPM_OUTPUT("HSOR"),
+	SND_SOC_DAPM_OUTPUT("HFL"),
+	SND_SOC_DAPM_OUTPUT("HFR"),
+	SND_SOC_DAPM_OUTPUT("EP"),
+	SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+	SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+
+	SND_SOC_DAPM_PGA("DL1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("DL2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("DL3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("DL4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("DL5", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("DL6", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	/* Analog input muxes for the capture amplifiers */
+	SND_SOC_DAPM_MUX("Analog Left Capture Route",
+			SND_SOC_NOPM, 0, 0, &amic1_control),
+	SND_SOC_DAPM_MUX("Analog Right Capture Route",
+			SND_SOC_NOPM, 0, 0, &amic2_control),
+
+	SND_SOC_DAPM_MUX("Sidetone Audio Playback", SND_SOC_NOPM, 0, 0,
+			&st_audio_control),
+	SND_SOC_DAPM_MUX("Sidetone Voice Playback", SND_SOC_NOPM, 0, 0,
+			&st_voice_control),
+
+	/* AIF */
+	SND_SOC_DAPM_AIF_IN("INTF1_SDI", NULL, 0, ISABELLE_INTF_EN_REG, 7, 0),
+	SND_SOC_DAPM_AIF_IN("INTF2_SDI", NULL, 0, ISABELLE_INTF_EN_REG, 6, 0),
+
+	SND_SOC_DAPM_AIF_OUT("INTF1_SDO", NULL, 0, ISABELLE_INTF_EN_REG, 5, 0),
+	SND_SOC_DAPM_AIF_OUT("INTF2_SDO", NULL, 0, ISABELLE_INTF_EN_REG, 4, 0),
+
+	SND_SOC_DAPM_OUT_DRV("ULATX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("ULATX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("ULVTX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("ULVTX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	/* Analog Capture PGAs */
+	SND_SOC_DAPM_PGA("MicAmp1", ISABELLE_AMIC_CFG_REG, 5, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("MicAmp2", ISABELLE_AMIC_CFG_REG, 4, 0, NULL, 0),
+
+	/* Auxiliary FM PGAs */
+	SND_SOC_DAPM_PGA("APGA1", ISABELLE_APGA_CFG_REG, 7, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("APGA2", ISABELLE_APGA_CFG_REG, 6, 0, NULL, 0),
+
+	/* ADCs */
+	SND_SOC_DAPM_ADC("ADC1", "Left Front Capture",
+			ISABELLE_AMIC_CFG_REG, 7, 0),
+	SND_SOC_DAPM_ADC("ADC2", "Right Front Capture",
+			ISABELLE_AMIC_CFG_REG, 6, 0),
+
+	/* Microphone Bias */
+	SND_SOC_DAPM_SUPPLY("Headset Mic Bias", ISABELLE_ABIAS_CFG_REG,
+			3, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Main Mic Bias", ISABELLE_ABIAS_CFG_REG,
+			2, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Digital Mic1 Bias",
+			ISABELLE_DBIAS_CFG_REG, 3, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("Digital Mic2 Bias",
+			ISABELLE_DBIAS_CFG_REG, 2, 0, NULL, 0),
+
+	/* Mixers */
+	SND_SOC_DAPM_MIXER("Headset Left Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_hs_left_mixer_controls,
+			ARRAY_SIZE(isabelle_hs_left_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Headset Right Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_hs_right_mixer_controls,
+			ARRAY_SIZE(isabelle_hs_right_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Handsfree Left Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_hf_left_mixer_controls,
+			ARRAY_SIZE(isabelle_hf_left_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Handsfree Right Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_hf_right_mixer_controls,
+			ARRAY_SIZE(isabelle_hf_right_mixer_controls)),
+	SND_SOC_DAPM_MIXER("LINEOUT1 Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_aux_left_mixer_controls,
+			ARRAY_SIZE(isabelle_aux_left_mixer_controls)),
+	SND_SOC_DAPM_MIXER("LINEOUT2 Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_aux_right_mixer_controls,
+			ARRAY_SIZE(isabelle_aux_right_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Earphone Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_ep_mixer_controls,
+			ARRAY_SIZE(isabelle_ep_mixer_controls)),
+
+	SND_SOC_DAPM_MIXER("DPGA1L Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_dpga1_left_mixer_controls,
+			ARRAY_SIZE(isabelle_dpga1_left_mixer_controls)),
+	SND_SOC_DAPM_MIXER("DPGA1R Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_dpga1_right_mixer_controls,
+			ARRAY_SIZE(isabelle_dpga1_right_mixer_controls)),
+	SND_SOC_DAPM_MIXER("DPGA2L Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_dpga2_left_mixer_controls,
+			ARRAY_SIZE(isabelle_dpga2_left_mixer_controls)),
+	SND_SOC_DAPM_MIXER("DPGA2R Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_dpga2_right_mixer_controls,
+			ARRAY_SIZE(isabelle_dpga2_right_mixer_controls)),
+	SND_SOC_DAPM_MIXER("DPGA3L Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_dpga3_left_mixer_controls,
+			ARRAY_SIZE(isabelle_dpga3_left_mixer_controls)),
+	SND_SOC_DAPM_MIXER("DPGA3R Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_dpga3_right_mixer_controls,
+			ARRAY_SIZE(isabelle_dpga3_right_mixer_controls)),
+
+	SND_SOC_DAPM_MIXER("RX1 Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_rx1_mixer_controls,
+			ARRAY_SIZE(isabelle_rx1_mixer_controls)),
+	SND_SOC_DAPM_MIXER("RX2 Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_rx2_mixer_controls,
+			ARRAY_SIZE(isabelle_rx2_mixer_controls)),
+	SND_SOC_DAPM_MIXER("RX3 Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_rx3_mixer_controls,
+			ARRAY_SIZE(isabelle_rx3_mixer_controls)),
+	SND_SOC_DAPM_MIXER("RX4 Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_rx4_mixer_controls,
+			ARRAY_SIZE(isabelle_rx4_mixer_controls)),
+	SND_SOC_DAPM_MIXER("RX5 Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_rx5_mixer_controls,
+			ARRAY_SIZE(isabelle_rx5_mixer_controls)),
+	SND_SOC_DAPM_MIXER("RX6 Mixer", SND_SOC_NOPM, 0, 0,
+			isabelle_rx6_mixer_controls,
+			ARRAY_SIZE(isabelle_rx6_mixer_controls)),
+
+	/* DACs */
+	SND_SOC_DAPM_DAC("DAC1L", "Headset Playback", ISABELLE_DAC_CFG_REG,
+			5, 0),
+	SND_SOC_DAPM_DAC("DAC1R", "Headset Playback", ISABELLE_DAC_CFG_REG,
+			4, 0),
+	SND_SOC_DAPM_DAC("DAC2L", "Handsfree Playback", ISABELLE_DAC_CFG_REG,
+			3, 0),
+	SND_SOC_DAPM_DAC("DAC2R", "Handsfree Playback", ISABELLE_DAC_CFG_REG,
+			2, 0),
+	SND_SOC_DAPM_DAC("DAC3L", "Lineout Playback", ISABELLE_DAC_CFG_REG,
+			1, 0),
+	SND_SOC_DAPM_DAC("DAC3R", "Lineout Playback", ISABELLE_DAC_CFG_REG,
+			0, 0),
+
+	/* Analog Playback PGAs */
+	SND_SOC_DAPM_PGA("Sidetone Audio PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("Sidetone Voice PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("HF Left PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("HF Right PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("DPGA1L", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("DPGA1R", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("DPGA2L", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("DPGA2R", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("DPGA3L", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("DPGA3R", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	/* Analog Playback Mux */
+	SND_SOC_DAPM_MUX("RX1 Playback", ISABELLE_ALU_RX_EN_REG, 5, 0,
+			&rx1_mux_controls),
+	SND_SOC_DAPM_MUX("RX2 Playback", ISABELLE_ALU_RX_EN_REG, 4, 0,
+			&rx2_mux_controls),
+
+	/* TX Select */
+	SND_SOC_DAPM_MUX("ATX Select", ISABELLE_TX_INPUT_CFG_REG,
+			7, 0, &atx_mux_controls),
+	SND_SOC_DAPM_MUX("VTX Select", ISABELLE_TX_INPUT_CFG_REG,
+			6, 0, &vtx_mux_controls),
+
+	SND_SOC_DAPM_SWITCH("Earphone Playback", SND_SOC_NOPM, 0, 0,
+			&ep_path_enable_control),
+
+	/* Output Drivers */
+	SND_SOC_DAPM_OUT_DRV("HS Left Driver", ISABELLE_HSDRV_CFG2_REG,
+			1, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("HS Right Driver", ISABELLE_HSDRV_CFG2_REG,
+			0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("LINEOUT1 Left Driver", ISABELLE_LINEAMP_CFG_REG,
+			1, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("LINEOUT2 Right Driver", ISABELLE_LINEAMP_CFG_REG,
+			0, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("Earphone Driver", ISABELLE_EARDRV_CFG2_REG,
+			1, 0, NULL, 0),
+
+	SND_SOC_DAPM_OUT_DRV("HF Left Driver", ISABELLE_HFDRV_CFG_REG,
+			1, 0, NULL, 0),
+	SND_SOC_DAPM_OUT_DRV("HF Right Driver", ISABELLE_HFDRV_CFG_REG,
+			0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route isabelle_intercon[] = {
+	/* Interface mapping */
+	{ "DL1", "DL12 Playback Switch", "INTF1_SDI" },
+	{ "DL2", "DL12 Playback Switch", "INTF1_SDI" },
+	{ "DL3", "DL34 Playback Switch", "INTF1_SDI" },
+	{ "DL4", "DL34 Playback Switch", "INTF1_SDI" },
+	{ "DL5", "DL56 Playback Switch", "INTF1_SDI" },
+	{ "DL6", "DL56 Playback Switch", "INTF1_SDI" },
+
+	{ "DL1", "DL12 Playback Switch", "INTF2_SDI" },
+	{ "DL2", "DL12 Playback Switch", "INTF2_SDI" },
+	{ "DL3", "DL34 Playback Switch", "INTF2_SDI" },
+	{ "DL4", "DL34 Playback Switch", "INTF2_SDI" },
+	{ "DL5", "DL56 Playback Switch", "INTF2_SDI" },
+	{ "DL6", "DL56 Playback Switch", "INTF2_SDI" },
+
+	/* Input side mapping */
+	{ "Sidetone Audio PGA", NULL, "Sidetone Audio Playback" },
+	{ "Sidetone Voice PGA", NULL, "Sidetone Voice Playback" },
+
+	{ "RX1 Mixer", "ST1 Playback Switch", "Sidetone Audio PGA" },
+
+	{ "RX1 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" },
+	{ "RX1 Mixer", "DL1 Playback Switch", "DL1" },
+
+	{ "RX2 Mixer", "ST2 Playback Switch", "Sidetone Audio PGA" },
+
+	{ "RX2 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" },
+	{ "RX2 Mixer", "DL2 Playback Switch", "DL2" },
+
+	{ "RX3 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" },
+	{ "RX3 Mixer", "DL3 Playback Switch", "DL3" },
+
+	{ "RX4 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" },
+	{ "RX4 Mixer", "DL4 Playback Switch", "DL4" },
+
+	{ "RX5 Mixer", "ST1 Playback Switch", "Sidetone Voice PGA" },
+	{ "RX5 Mixer", "DL5 Playback Switch", "DL5" },
+
+	{ "RX6 Mixer", "ST2 Playback Switch", "Sidetone Voice PGA" },
+	{ "RX6 Mixer", "DL6 Playback Switch", "DL6" },
+
+	/* Capture path */
+	{ "Analog Left Capture Route", "Headset Mic", "HSMIC" },
+	{ "Analog Left Capture Route", "Main Mic", "MAINMIC" },
+	{ "Analog Left Capture Route", "Aux/FM Left", "LINEIN1" },
+
+	{ "Analog Right Capture Route", "Sub Mic", "SUBMIC" },
+	{ "Analog Right Capture Route", "Aux/FM Right", "LINEIN2" },
+
+	{ "MicAmp1", NULL, "Analog Left Capture Route" },
+	{ "MicAmp2", NULL, "Analog Right Capture Route" },
+
+	{ "ADC1", NULL, "MicAmp1" },
+	{ "ADC2", NULL, "MicAmp2" },
+
+	{ "ATX Select", "AMIC1", "ADC1" },
+	{ "ATX Select", "DMIC", "DMICDAT" },
+	{ "ATX Select", "AMIC2", "ADC2" },
+
+	{ "VTX Select", "AMIC1", "ADC1" },
+	{ "VTX Select", "DMIC", "DMICDAT" },
+	{ "VTX Select", "AMIC2", "ADC2" },
+
+	{ "ULATX1", "ATX1 Filter Enable Switch", "ATX Select" },
+	{ "ULATX1", "ATX1 Filter Bypass Switch", "ATX Select" },
+	{ "ULATX2", "ATX2 Filter Enable Switch", "ATX Select" },
+	{ "ULATX2", "ATX2 Filter Bypass Switch", "ATX Select" },
+
+	{ "ULVTX1", "VTX1 Filter Enable Switch", "VTX Select" },
+	{ "ULVTX1", "VTX1 Filter Bypass Switch", "VTX Select" },
+	{ "ULVTX2", "VTX2 Filter Enable Switch", "VTX Select" },
+	{ "ULVTX2", "VTX2 Filter Bypass Switch", "VTX Select" },
+
+	{ "INTF1_SDO", "ULATX12 Capture Switch", "ULATX1" },
+	{ "INTF1_SDO", "ULATX12 Capture Switch", "ULATX2" },
+	{ "INTF2_SDO", "ULATX12 Capture Switch", "ULATX1" },
+	{ "INTF2_SDO", "ULATX12 Capture Switch", "ULATX2" },
+
+	{ "INTF1_SDO", NULL, "ULVTX1" },
+	{ "INTF1_SDO", NULL, "ULVTX2" },
+	{ "INTF2_SDO", NULL, "ULVTX1" },
+	{ "INTF2_SDO", NULL, "ULVTX2" },
+
+	/* AFM Path */
+	{ "APGA1", NULL, "LINEIN1" },
+	{ "APGA2", NULL, "LINEIN2" },
+
+	{ "RX1 Playback", "VRX1 Filter Bypass Switch", "RX1 Mixer" },
+	{ "RX1 Playback", "ARX1 Filter Bypass Switch", "RX1 Mixer" },
+	{ "RX1 Playback", "RX1 Filter Enable Switch", "RX1 Mixer" },
+
+	{ "RX2 Playback", "VRX2 Filter Bypass Switch", "RX2 Mixer" },
+	{ "RX2 Playback", "ARX2 Filter Bypass Switch", "RX2 Mixer" },
+	{ "RX2 Playback", "RX2 Filter Enable Switch", "RX2 Mixer" },
+
+	{ "RX3 Playback", "ARX3 Filter Bypass Switch", "RX3 Mixer" },
+	{ "RX3 Playback", "RX3 Filter Enable Switch", "RX3 Mixer" },
+
+	{ "RX4 Playback", "ARX4 Filter Bypass Switch", "RX4 Mixer" },
+	{ "RX4 Playback", "RX4 Filter Enable Switch", "RX4 Mixer" },
+
+	{ "RX5 Playback", "ARX5 Filter Bypass Switch", "RX5 Mixer" },
+	{ "RX5 Playback", "RX5 Filter Enable Switch", "RX5 Mixer" },
+
+	{ "RX6 Playback", "ARX6 Filter Bypass Switch", "RX6 Mixer" },
+	{ "RX6 Playback", "RX6 Filter Enable Switch", "RX6 Mixer" },
+
+	{ "DPGA1L Mixer", "RX1 Playback Switch", "RX1 Playback" },
+	{ "DPGA1L Mixer", "RX3 Playback Switch", "RX3 Playback" },
+	{ "DPGA1L Mixer", "RX5 Playback Switch", "RX5 Playback" },
+
+	{ "DPGA1R Mixer", "RX2 Playback Switch", "RX2 Playback" },
+	{ "DPGA1R Mixer", "RX4 Playback Switch", "RX4 Playback" },
+	{ "DPGA1R Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+	{ "DPGA1L", NULL, "DPGA1L Mixer" },
+	{ "DPGA1R", NULL, "DPGA1R Mixer" },
+
+	{ "DAC1L", NULL, "DPGA1L" },
+	{ "DAC1R", NULL, "DPGA1R" },
+
+	{ "DPGA2L Mixer", "RX1 Playback Switch", "RX1 Playback" },
+	{ "DPGA2L Mixer", "RX2 Playback Switch", "RX2 Playback" },
+	{ "DPGA2L Mixer", "RX3 Playback Switch", "RX3 Playback" },
+	{ "DPGA2L Mixer", "RX4 Playback Switch", "RX4 Playback" },
+	{ "DPGA2L Mixer", "RX5 Playback Switch", "RX5 Playback" },
+	{ "DPGA2L Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+	{ "DPGA2R Mixer", "RX2 Playback Switch", "RX2 Playback" },
+	{ "DPGA2R Mixer", "RX4 Playback Switch", "RX4 Playback" },
+	{ "DPGA2R Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+	{ "DPGA2L", NULL, "DPGA2L Mixer" },
+	{ "DPGA2R", NULL, "DPGA2R Mixer" },
+
+	{ "DAC2L", NULL, "DPGA2L" },
+	{ "DAC2R", NULL, "DPGA2R" },
+
+	{ "DPGA3L Mixer", "RX1 Playback Switch", "RX1 Playback" },
+	{ "DPGA3L Mixer", "RX3 Playback Switch", "RX3 Playback" },
+	{ "DPGA3L Mixer", "RX5 Playback Switch", "RX5 Playback" },
+
+	{ "DPGA3R Mixer", "RX2 Playback Switch", "RX2 Playback" },
+	{ "DPGA3R Mixer", "RX4 Playback Switch", "RX4 Playback" },
+	{ "DPGA3R Mixer", "RX6 Playback Switch", "RX6 Playback" },
+
+	{ "DPGA3L", NULL, "DPGA3L Mixer" },
+	{ "DPGA3R", NULL, "DPGA3R Mixer" },
+
+	{ "DAC3L", NULL, "DPGA3L" },
+	{ "DAC3R", NULL, "DPGA3R" },
+
+	{ "Headset Left Mixer", "DAC1L Playback Switch", "DAC1L" },
+	{ "Headset Left Mixer", "APGA1 Playback Switch", "APGA1" },
+
+	{ "Headset Right Mixer", "DAC1R Playback Switch", "DAC1R" },
+	{ "Headset Right Mixer", "APGA2 Playback Switch", "APGA2" },
+
+	{ "HS Left Driver", NULL, "Headset Left Mixer" },
+	{ "HS Right Driver", NULL, "Headset Right Mixer" },
+
+	{ "HSOL", NULL, "HS Left Driver" },
+	{ "HSOR", NULL, "HS Right Driver" },
+
+	/* Earphone playback path */
+	{ "Earphone Mixer", "DAC2L Playback Switch", "DAC2L" },
+	{ "Earphone Mixer", "APGA1 Playback Switch", "APGA1" },
+
+	{ "Earphone Playback", "Switch", "Earphone Mixer" },
+	{ "Earphone Driver", NULL, "Earphone Playback" },
+	{ "EP", NULL, "Earphone Driver" },
+
+	{ "Handsfree Left Mixer", "DAC2L Playback Switch", "DAC2L" },
+	{ "Handsfree Left Mixer", "APGA1 Playback Switch", "APGA1" },
+
+	{ "Handsfree Right Mixer", "DAC2R Playback Switch", "DAC2R" },
+	{ "Handsfree Right Mixer", "APGA2 Playback Switch", "APGA2" },
+
+	{ "HF Left PGA", NULL, "Handsfree Left Mixer" },
+	{ "HF Right PGA", NULL, "Handsfree Right Mixer" },
+
+	{ "HF Left Driver", NULL, "HF Left PGA" },
+	{ "HF Right Driver", NULL, "HF Right PGA" },
+
+	{ "HFL", NULL, "HF Left Driver" },
+	{ "HFR", NULL, "HF Right Driver" },
+
+	{ "LINEOUT1 Mixer", "DAC3L Playback Switch", "DAC3L" },
+	{ "LINEOUT1 Mixer", "APGA1 Playback Switch", "APGA1" },
+
+	{ "LINEOUT2 Mixer", "DAC3R Playback Switch", "DAC3R" },
+	{ "LINEOUT2 Mixer", "APGA2 Playback Switch", "APGA2" },
+
+	{ "LINEOUT1 Driver", NULL, "LINEOUT1 Mixer" },
+	{ "LINEOUT2 Driver", NULL, "LINEOUT2 Mixer" },
+
+	{ "LINEOUT1", NULL, "LINEOUT1 Driver" },
+	{ "LINEOUT2", NULL, "LINEOUT2 Driver" },
+};
+
+static int isabelle_hs_mute(struct snd_soc_dai *dai, int mute)
+{
+	snd_soc_update_bits(dai->codec, ISABELLE_DAC1_SOFTRAMP_REG,
+			BIT(4), (mute ? BIT(4) : 0));
+
+	return 0;
+}
+
+static int isabelle_hf_mute(struct snd_soc_dai *dai, int mute)
+{
+	snd_soc_update_bits(dai->codec, ISABELLE_DAC2_SOFTRAMP_REG,
+			BIT(4), (mute ? BIT(4) : 0));
+
+	return 0;
+}
+
+static int isabelle_line_mute(struct snd_soc_dai *dai, int mute)
+{
+	snd_soc_update_bits(dai->codec, ISABELLE_DAC3_SOFTRAMP_REG,
+			BIT(4), (mute ? BIT(4) : 0));
+
+	return 0;
+}
+
+static int isabelle_set_bias_level(struct snd_soc_codec *codec,
+				enum snd_soc_bias_level level)
+{
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+
+	case SND_SOC_BIAS_STANDBY:
+		snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG,
+				ISABELLE_CHIP_EN, BIT(0));
+		break;
+
+	case SND_SOC_BIAS_OFF:
+		snd_soc_update_bits(codec, ISABELLE_PWR_EN_REG,
+				ISABELLE_CHIP_EN, 0);
+		break;
+	}
+
+	codec->dapm.bias_level = level;
+
+	return 0;
+}
+
+static int isabelle_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;
+	u16 aif = 0;
+	unsigned int fs_val = 0;
+
+	switch (params_rate(params)) {
+	case 8000:
+		fs_val = ISABELLE_FS_RATE_8;
+		break;
+	case 11025:
+		fs_val = ISABELLE_FS_RATE_11;
+		break;
+	case 12000:
+		fs_val = ISABELLE_FS_RATE_12;
+		break;
+	case 16000:
+		fs_val = ISABELLE_FS_RATE_16;
+		break;
+	case 22050:
+		fs_val = ISABELLE_FS_RATE_22;
+		break;
+	case 24000:
+		fs_val = ISABELLE_FS_RATE_24;
+		break;
+	case 32000:
+		fs_val = ISABELLE_FS_RATE_32;
+		break;
+	case 44100:
+		fs_val = ISABELLE_FS_RATE_44;
+		break;
+	case 48000:
+		fs_val = ISABELLE_FS_RATE_48;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, ISABELLE_FS_RATE_CFG_REG,
+			ISABELLE_FS_RATE_MASK, fs_val);
+
+	/* bit size */
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		aif |= ISABELLE_AIF_LENGTH_20;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		aif |= ISABELLE_AIF_LENGTH_32;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG,
+			ISABELLE_AIF_LENGTH_MASK, aif);
+
+	return 0;
+}
+
+static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	unsigned int aif_val = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		aif_val &= ~ISABELLE_AIF_MS;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		aif_val |= ISABELLE_AIF_MS;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		aif_val |= ISABELLE_I2S_MODE;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		aif_val |= ISABELLE_LEFT_J_MODE;
+		break;
+	case SND_SOC_DAIFMT_PDM:
+		aif_val |= ISABELLE_PDM_MODE;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, ISABELLE_INTF_CFG_REG,
+			(ISABELLE_AIF_MS | ISABELLE_AIF_FMT_MASK), aif_val);
+
+	return 0;
+}
+
+/* Rates supported by Isabelle driver */
+#define ISABELLE_RATES		SNDRV_PCM_RATE_8000_48000
+
+/* Formates supported by Isabelle driver. */
+#define ISABELLE_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE |\
+			SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops isabelle_hs_dai_ops = {
+	.hw_params	= isabelle_hw_params,
+	.set_fmt	= isabelle_set_dai_fmt,
+	.digital_mute	= isabelle_hs_mute,
+};
+
+static struct snd_soc_dai_ops isabelle_hf_dai_ops = {
+	.hw_params	= isabelle_hw_params,
+	.set_fmt	= isabelle_set_dai_fmt,
+	.digital_mute	= isabelle_hf_mute,
+};
+
+static struct snd_soc_dai_ops isabelle_line_dai_ops = {
+	.hw_params	= isabelle_hw_params,
+	.set_fmt	= isabelle_set_dai_fmt,
+	.digital_mute	= isabelle_line_mute,
+};
+
+static struct snd_soc_dai_ops isabelle_ul_dai_ops = {
+	.hw_params	= isabelle_hw_params,
+	.set_fmt	= isabelle_set_dai_fmt,
+};
+
+/* ISABELLE dai structure */
+static struct snd_soc_dai_driver isabelle_dai[] = {
+	{
+		.name = "isabelle-dl1",
+		.playback = {
+			.stream_name = "Headset Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = ISABELLE_RATES,
+			.formats = ISABELLE_FORMATS,
+		},
+		.ops = &isabelle_hs_dai_ops,
+	},
+	{
+		.name = "isabelle-dl2",
+		.playback = {
+			.stream_name = "Handsfree Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = ISABELLE_RATES,
+			.formats = ISABELLE_FORMATS,
+		},
+		.ops = &isabelle_hf_dai_ops,
+	},
+	{
+		.name = "isabelle-lineout",
+		.playback = {
+			.stream_name = "Lineout Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = ISABELLE_RATES,
+			.formats = ISABELLE_FORMATS,
+		},
+		.ops = &isabelle_line_dai_ops,
+	},
+	{
+		.name = "isabelle-ul",
+		.capture = {
+			.stream_name = "Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = ISABELLE_RATES,
+			.formats = ISABELLE_FORMATS,
+		},
+		.ops = &isabelle_ul_dai_ops,
+	},
+};
+
+static int isabelle_probe(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+
+	codec->control_data = dev_get_regmap(codec->dev, NULL);
+
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_isabelle = {
+	.probe = isabelle_probe,
+	.set_bias_level = isabelle_set_bias_level,
+	.controls = isabelle_snd_controls,
+	.num_controls = ARRAY_SIZE(isabelle_snd_controls),
+	.dapm_widgets = isabelle_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(isabelle_dapm_widgets),
+	.dapm_routes = isabelle_intercon,
+	.num_dapm_routes = ARRAY_SIZE(isabelle_intercon),
+	.idle_bias_off = true,
+};
+
+static const struct regmap_config isabelle_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = ISABELLE_MAX_REGISTER,
+	.reg_defaults = isabelle_reg_defs,
+	.num_reg_defaults = ARRAY_SIZE(isabelle_reg_defs),
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int __devinit isabelle_i2c_probe(struct i2c_client *i2c,
+					const struct i2c_device_id *id)
+{
+	struct regmap *isabelle_regmap;
+	int ret = 0;
+
+	isabelle_regmap = devm_regmap_init_i2c(i2c, &isabelle_regmap_config);
+	if (IS_ERR(isabelle_regmap)) {
+		ret = PTR_ERR(isabelle_regmap);
+		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+			ret);
+		return ret;
+	}
+	i2c_set_clientdata(i2c, isabelle_regmap);
+
+	ret =  snd_soc_register_codec(&i2c->dev,
+				&soc_codec_dev_isabelle, isabelle_dai,
+				ARRAY_SIZE(isabelle_dai));
+	if (ret < 0) {
+		dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int __devexit isabelle_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+	return 0;
+}
+
+static const struct i2c_device_id isabelle_i2c_id[] = {
+	{ "isabelle", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id);
+
+static struct i2c_driver isabelle_i2c_driver = {
+	.driver = {
+		.name = "isabelle",
+		.owner = THIS_MODULE,
+	},
+	.probe = isabelle_i2c_probe,
+	.remove = __devexit_p(isabelle_i2c_remove),
+	.id_table = isabelle_i2c_id,
+};
+
+module_i2c_driver(isabelle_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ISABELLE driver");
+MODULE_AUTHOR("Vishwas A Deshpande <vishwas.a.deshpande@ti.com>");
+MODULE_AUTHOR("M R Swami Reddy <MR.Swami.Reddy@ti.com>");
+MODULE_LICENSE("GPL v2");

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

@@ -0,0 +1,143 @@
+/*
+ * isabelle.h - Low power high fidelity audio codec driver header file
+ *
+ * Copyright (c) 2012 Texas Instruments, Inc
+ *
+ * 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.
+ *
+ */
+
+#ifndef _ISABELLE_H
+#define _ISABELLE_H
+
+#include <linux/bitops.h>
+
+/* ISABELLE REGISTERS */
+
+#define ISABELLE_PWR_CFG_REG		0x01
+#define ISABELLE_PWR_EN_REG		0x02
+#define ISABELLE_PS_EN1_REG		0x03
+#define ISABELLE_INT1_STATUS_REG	0x04
+#define ISABELLE_INT1_MASK_REG		0x05
+#define ISABELLE_INT2_STATUS_REG	0x06
+#define ISABELLE_INT2_MASK_REG		0x07
+#define ISABELLE_HKCTL1_REG		0x08
+#define ISABELLE_HKCTL2_REG		0x09
+#define ISABELLE_HKCTL3_REG		0x0A
+#define ISABELLE_ACCDET_STATUS_REG	0x0B
+#define ISABELLE_BUTTON_ID_REG		0x0C
+#define ISABELLE_PLL_CFG_REG		0x10
+#define ISABELLE_PLL_EN_REG		0x11
+#define ISABELLE_FS_RATE_CFG_REG	0x12
+#define ISABELLE_INTF_CFG_REG		0x13
+#define ISABELLE_INTF_EN_REG		0x14
+#define ISABELLE_ULATX12_INTF_CFG_REG	0x15
+#define ISABELLE_DL12_INTF_CFG_REG	0x16
+#define ISABELLE_DL34_INTF_CFG_REG	0x17
+#define ISABELLE_DL56_INTF_CFG_REG	0x18
+#define ISABELLE_ATX_STPGA1_CFG_REG	0x19
+#define ISABELLE_ATX_STPGA2_CFG_REG	0x1A
+#define ISABELLE_VTX_STPGA1_CFG_REG	0x1B
+#define ISABELLE_VTX2_STPGA2_CFG_REG	0x1C
+#define ISABELLE_ATX1_DPGA_REG		0x1D
+#define ISABELLE_ATX2_DPGA_REG		0x1E
+#define ISABELLE_VTX1_DPGA_REG		0x1F
+#define ISABELLE_VTX2_DPGA_REG		0x20
+#define ISABELLE_TX_INPUT_CFG_REG	0x21
+#define ISABELLE_RX_INPUT_CFG_REG	0x22
+#define ISABELLE_RX_INPUT_CFG2_REG	0x23
+#define ISABELLE_VOICE_HPF_CFG_REG	0x24
+#define ISABELLE_AUDIO_HPF_CFG_REG	0x25
+#define ISABELLE_RX1_DPGA_REG		0x26
+#define ISABELLE_RX2_DPGA_REG		0x27
+#define ISABELLE_RX3_DPGA_REG		0x28
+#define ISABELLE_RX4_DPGA_REG		0x29
+#define ISABELLE_RX5_DPGA_REG		0x2A
+#define ISABELLE_RX6_DPGA_REG		0x2B
+#define ISABELLE_ALU_TX_EN_REG		0x2C
+#define ISABELLE_ALU_RX_EN_REG		0x2D
+#define ISABELLE_IIR_RESYNC_REG		0x2E
+#define ISABELLE_ABIAS_CFG_REG		0x30
+#define ISABELLE_DBIAS_CFG_REG		0x31
+#define ISABELLE_MIC1_GAIN_REG		0x32
+#define ISABELLE_MIC2_GAIN_REG		0x33
+#define ISABELLE_AMIC_CFG_REG		0x34
+#define ISABELLE_DMIC_CFG_REG		0x35
+#define ISABELLE_APGA_GAIN_REG		0x36
+#define ISABELLE_APGA_CFG_REG		0x37
+#define ISABELLE_TX_GAIN_DLY_REG	0x38
+#define ISABELLE_RX_GAIN_DLY_REG	0x39
+#define ISABELLE_RX_PWR_CTRL_REG	0x3A
+#define ISABELLE_DPGA1LR_IN_SEL_REG	0x3B
+#define ISABELLE_DPGA1L_GAIN_REG	0x3C
+#define ISABELLE_DPGA1R_GAIN_REG	0x3D
+#define ISABELLE_DPGA2L_IN_SEL_REG	0x3E
+#define ISABELLE_DPGA2R_IN_SEL_REG	0x3F
+#define ISABELLE_DPGA2L_GAIN_REG	0x40
+#define ISABELLE_DPGA2R_GAIN_REG	0x41
+#define ISABELLE_DPGA3LR_IN_SEL_REG	0x42
+#define ISABELLE_DPGA3L_GAIN_REG	0x43
+#define ISABELLE_DPGA3R_GAIN_REG	0x44
+#define ISABELLE_DAC1_SOFTRAMP_REG	0x45
+#define ISABELLE_DAC2_SOFTRAMP_REG	0x46
+#define ISABELLE_DAC3_SOFTRAMP_REG	0x47
+#define ISABELLE_DAC_CFG_REG		0x48
+#define ISABELLE_EARDRV_CFG1_REG	0x49
+#define ISABELLE_EARDRV_CFG2_REG	0x4A
+#define ISABELLE_HSDRV_GAIN_REG		0x4B
+#define ISABELLE_HSDRV_CFG1_REG		0x4C
+#define ISABELLE_HSDRV_CFG2_REG		0x4D
+#define ISABELLE_HS_NG_CFG1_REG		0x4E
+#define ISABELLE_HS_NG_CFG2_REG		0x4F
+#define ISABELLE_LINEAMP_GAIN_REG	0x50
+#define ISABELLE_LINEAMP_CFG_REG	0x51
+#define ISABELLE_HFL_VOL_CTRL_REG	0x52
+#define ISABELLE_HFL_SFTVOL_CTRL_REG	0x53
+#define ISABELLE_HFL_LIM_CTRL_1_REG	0x54
+#define ISABELLE_HFL_LIM_CTRL_2_REG	0x55
+#define ISABELLE_HFR_VOL_CTRL_REG	0x56
+#define ISABELLE_HFR_SFTVOL_CTRL_REG	0x57
+#define ISABELLE_HFR_LIM_CTRL_1_REG	0x58
+#define ISABELLE_HFR_LIM_CTRL_2_REG	0x59
+#define ISABELLE_HF_MODE_REG		0x5A
+#define ISABELLE_HFLPGA_CFG_REG		0x5B
+#define ISABELLE_HFRPGA_CFG_REG		0x5C
+#define ISABELLE_HFDRV_CFG_REG		0x5D
+#define ISABELLE_PDMOUT_CFG1_REG	0x5E
+#define ISABELLE_PDMOUT_CFG2_REG	0x5F
+#define ISABELLE_PDMOUT_L_WM_REG	0x60
+#define ISABELLE_PDMOUT_R_WM_REG	0x61
+#define ISABELLE_HF_NG_CFG1_REG		0x62
+#define ISABELLE_HF_NG_CFG2_REG		0x63
+
+/* ISABELLE_PWR_EN_REG (0x02h) */
+#define ISABELLE_CHIP_EN		BIT(0)
+
+/* ISABELLE DAI FORMATS */
+#define ISABELLE_AIF_FMT_MASK		0x70
+#define ISABELLE_I2S_MODE		0x0
+#define ISABELLE_LEFT_J_MODE		0x1
+#define ISABELLE_PDM_MODE		0x2
+
+#define ISABELLE_AIF_LENGTH_MASK	0x30
+#define ISABELLE_AIF_LENGTH_20		0x00
+#define ISABELLE_AIF_LENGTH_32		0x10
+
+#define ISABELLE_AIF_MS			0x80
+
+#define ISABELLE_FS_RATE_MASK		0xF
+#define ISABELLE_FS_RATE_8		0x0
+#define ISABELLE_FS_RATE_11		0x1
+#define ISABELLE_FS_RATE_12		0x2
+#define ISABELLE_FS_RATE_16		0x4
+#define ISABELLE_FS_RATE_22		0x5
+#define ISABELLE_FS_RATE_24		0x6
+#define ISABELLE_FS_RATE_32		0x8
+#define ISABELLE_FS_RATE_44		0x9
+#define ISABELLE_FS_RATE_48		0xA
+
+#define ISABELLE_MAX_REGISTER		0xFF
+
+#endif

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

@@ -12,7 +12,6 @@
 
 
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/moduleparam.h>
-#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
@@ -1358,7 +1357,7 @@ static struct snd_soc_dai_ops lm49453_lineout_dai_ops = {
 };
 };
 
 
 /* LM49453 dai structure. */
 /* LM49453 dai structure. */
-static const struct snd_soc_dai_driver lm49453_dai[] = {
+static struct snd_soc_dai_driver lm49453_dai[] = {
 	{
 	{
 		.name = "LM49453 Headset",
 		.name = "LM49453 Headset",
 		.playback = {
 		.playback = {

+ 3 - 2
sound/soc/codecs/max98095.c

@@ -2216,7 +2216,7 @@ static irqreturn_t max98095_report_jack(int irq, void *data)
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
-int max98095_jack_detect_enable(struct snd_soc_codec *codec)
+static int max98095_jack_detect_enable(struct snd_soc_codec *codec)
 {
 {
 	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
 	struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
 	int ret = 0;
 	int ret = 0;
@@ -2245,7 +2245,7 @@ int max98095_jack_detect_enable(struct snd_soc_codec *codec)
 	return ret;
 	return ret;
 }
 }
 
 
-int max98095_jack_detect_disable(struct snd_soc_codec *codec)
+static int max98095_jack_detect_disable(struct snd_soc_codec *codec)
 {
 {
 	int ret = 0;
 	int ret = 0;
 
 
@@ -2286,6 +2286,7 @@ int max98095_jack_detect(struct snd_soc_codec *codec,
 	max98095_report_jack(client->irq, codec);
 	max98095_report_jack(client->irq, codec);
 	return 0;
 	return 0;
 }
 }
+EXPORT_SYMBOL_GPL(max98095_jack_detect);
 
 
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
 static int max98095_suspend(struct snd_soc_codec *codec)
 static int max98095_suspend(struct snd_soc_codec *codec)

+ 1 - 4
sound/soc/codecs/ml26124.c

@@ -638,7 +638,7 @@ static __devinit int ml26124_i2c_probe(struct i2c_client *i2c,
 
 
 	i2c_set_clientdata(i2c, priv);
 	i2c_set_clientdata(i2c, priv);
 
 
-	priv->regmap = regmap_init_i2c(i2c, &ml26124_i2c_regmap);
+	priv->regmap = devm_regmap_init_i2c(i2c, &ml26124_i2c_regmap);
 	if (IS_ERR(priv->regmap)) {
 	if (IS_ERR(priv->regmap)) {
 		ret = PTR_ERR(priv->regmap);
 		ret = PTR_ERR(priv->regmap);
 		dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret);
 		dev_err(&i2c->dev, "regmap_init_i2c() failed: %d\n", ret);
@@ -651,10 +651,7 @@ static __devinit int ml26124_i2c_probe(struct i2c_client *i2c,
 
 
 static __devexit int ml26124_i2c_remove(struct i2c_client *client)
 static __devexit int ml26124_i2c_remove(struct i2c_client *client)
 {
 {
-	struct ml26124_priv *priv = i2c_get_clientdata(client);
-
 	snd_soc_unregister_codec(&client->dev);
 	snd_soc_unregister_codec(&client->dev);
-	regmap_exit(priv->regmap);
 	return 0;
 	return 0;
 }
 }
 
 

+ 67 - 0
sound/soc/codecs/spdif_receiver.c

@@ -0,0 +1,67 @@
+/*
+ * ALSA SoC SPDIF DIR (Digital Interface Reciever) driver
+ *
+ * Based on ALSA SoC SPDIF DIT driver
+ *
+ *  This driver is used by controllers which can operate in DIR (SPDI/F) where
+ *  no codec is needed.  This file provides stub codec that can be used
+ *  in these configurations. SPEAr SPDIF IN Audio controller uses this driver.
+ *
+ * Author:      Vipin Kumar,  <vipin.kumar@st.com>
+ * Copyright:   (C) 2012  ST Microelectronics
+ *
+ * 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/slab.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+#define STUB_RATES	SNDRV_PCM_RATE_8000_192000
+#define STUB_FORMATS	(SNDRV_PCM_FMTBIT_S16_LE | \
+			SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
+
+static struct snd_soc_codec_driver soc_codec_spdif_dir;
+
+static struct snd_soc_dai_driver dir_stub_dai = {
+	.name		= "dir-hifi",
+	.capture	= {
+		.stream_name	= "Capture",
+		.channels_min	= 1,
+		.channels_max	= 384,
+		.rates		= STUB_RATES,
+		.formats	= STUB_FORMATS,
+	},
+};
+
+static int spdif_dir_probe(struct platform_device *pdev)
+{
+	return snd_soc_register_codec(&pdev->dev, &soc_codec_spdif_dir,
+			&dir_stub_dai, 1);
+}
+
+static int spdif_dir_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_codec(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver spdif_dir_driver = {
+	.probe		= spdif_dir_probe,
+	.remove		= spdif_dir_remove,
+	.driver		= {
+		.name	= "spdif-dir",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(spdif_dir_driver);
+
+MODULE_DESCRIPTION("ASoC SPDIF DIR driver");
+MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");
+MODULE_LICENSE("GPL");

+ 442 - 0
sound/soc/codecs/sta529.c

@@ -0,0 +1,442 @@
+/*
+ * ASoC codec driver for spear platform
+ *
+ * sound/soc/codecs/sta529.c -- spear ALSA Soc codec driver
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Rajeev Kumar <rajeev-dlh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+/* STA529 Register offsets */
+#define	 STA529_FFXCFG0		0x00
+#define	 STA529_FFXCFG1		0x01
+#define	 STA529_MVOL		0x02
+#define	 STA529_LVOL		0x03
+#define	 STA529_RVOL		0x04
+#define	 STA529_TTF0		0x05
+#define	 STA529_TTF1		0x06
+#define	 STA529_TTP0		0x07
+#define	 STA529_TTP1		0x08
+#define	 STA529_S2PCFG0		0x0A
+#define	 STA529_S2PCFG1		0x0B
+#define	 STA529_P2SCFG0		0x0C
+#define	 STA529_P2SCFG1		0x0D
+#define	 STA529_PLLCFG0		0x14
+#define	 STA529_PLLCFG1		0x15
+#define	 STA529_PLLCFG2		0x16
+#define	 STA529_PLLCFG3		0x17
+#define	 STA529_PLLPFE		0x18
+#define	 STA529_PLLST		0x19
+#define	 STA529_ADCCFG		0x1E /*mic_select*/
+#define	 STA529_CKOCFG		0x1F
+#define	 STA529_MISC		0x20
+#define	 STA529_PADST0		0x21
+#define	 STA529_PADST1		0x22
+#define	 STA529_FFXST		0x23
+#define	 STA529_PWMIN1		0x2D
+#define	 STA529_PWMIN2		0x2E
+#define	 STA529_POWST		0x32
+
+#define STA529_MAX_REGISTER	0x32
+
+#define STA529_RATES		(SNDRV_PCM_RATE_8000 | \
+				SNDRV_PCM_RATE_11025 | \
+				SNDRV_PCM_RATE_16000 | \
+				SNDRV_PCM_RATE_22050 | \
+				SNDRV_PCM_RATE_32000 | \
+				SNDRV_PCM_RATE_44100 | \
+				SNDRV_PCM_RATE_48000)
+
+#define STA529_FORMAT		(SNDRV_PCM_FMTBIT_S16_LE | \
+				SNDRV_PCM_FMTBIT_S24_LE | \
+				SNDRV_PCM_FMTBIT_S32_LE)
+#define	S2PC_VALUE		0x98
+#define CLOCK_OUT		0x60
+#define LEFT_J_DATA_FORMAT	0x10
+#define I2S_DATA_FORMAT		0x12
+#define RIGHT_J_DATA_FORMAT	0x14
+#define CODEC_MUTE_VAL		0x80
+
+#define POWER_CNTLMSAK		0x40
+#define POWER_STDBY		0x40
+#define FFX_MASK		0x80
+#define FFX_OFF			0x80
+#define POWER_UP		0x00
+#define FFX_CLK_ENB		0x01
+#define FFX_CLK_DIS		0x00
+#define FFX_CLK_MSK		0x01
+#define PLAY_FREQ_RANGE_MSK	0x70
+#define CAP_FREQ_RANGE_MSK	0x0C
+#define PDATA_LEN_MSK		0xC0
+#define BCLK_TO_FS_MSK		0x30
+#define AUDIO_MUTE_MSK		0x80
+
+static const struct reg_default sta529_reg_defaults[] = {
+	{ 0,  0x35 },     /* R0   - FFX Configuration reg 0 */
+	{ 1,  0xc8 },     /* R1   - FFX Configuration reg 1 */
+	{ 2,  0x50 },     /* R2   - Master Volume */
+	{ 3,  0x00 },     /* R3   - Left Volume */
+	{ 4,  0x00 },     /* R4  -  Right Volume */
+	{ 10, 0xb2 },     /* R10  - S2P Config Reg 0 */
+	{ 11, 0x41 },     /* R11  - S2P Config Reg 1 */
+	{ 12, 0x92 },     /* R12  - P2S Config Reg 0 */
+	{ 13, 0x41 },     /* R13  - P2S Config Reg 1 */
+	{ 30, 0xd2 },     /* R30  - ADC Config Reg */
+	{ 31, 0x40 },     /* R31  - clock Out Reg */
+	{ 32, 0x21 },     /* R32  - Misc Register */
+};
+
+struct sta529 {
+	struct regmap *regmap;
+};
+
+static bool sta529_readable(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+
+	case STA529_FFXCFG0:
+	case STA529_FFXCFG1:
+	case STA529_MVOL:
+	case STA529_LVOL:
+	case STA529_RVOL:
+	case STA529_S2PCFG0:
+	case STA529_S2PCFG1:
+	case STA529_P2SCFG0:
+	case STA529_P2SCFG1:
+	case STA529_ADCCFG:
+	case STA529_CKOCFG:
+	case STA529_MISC:
+		return true;
+	default:
+		return false;
+	}
+}
+
+
+static const char *pwm_mode_text[] = { "Binary", "Headphone", "Ternary",
+	"Phase-shift"};
+
+static const DECLARE_TLV_DB_SCALE(out_gain_tlv, -9150, 50, 0);
+static const DECLARE_TLV_DB_SCALE(master_vol_tlv, -12750, 50, 0);
+static const SOC_ENUM_SINGLE_DECL(pwm_src, STA529_FFXCFG1, 4, pwm_mode_text);
+
+static const struct snd_kcontrol_new sta529_snd_controls[] = {
+	SOC_DOUBLE_R_TLV("Digital Playback Volume", STA529_LVOL, STA529_RVOL, 0,
+			127, 0, out_gain_tlv),
+	SOC_SINGLE_TLV("Master Playback Volume", STA529_MVOL, 0, 127, 1,
+			master_vol_tlv),
+	SOC_ENUM("PWM Select", pwm_src),
+};
+
+static int sta529_set_bias_level(struct snd_soc_codec *codec, enum
+		snd_soc_bias_level level)
+{
+	struct sta529 *sta529 = snd_soc_codec_get_drvdata(codec);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+	case SND_SOC_BIAS_PREPARE:
+		snd_soc_update_bits(codec, STA529_FFXCFG0, POWER_CNTLMSAK,
+				POWER_UP);
+		snd_soc_update_bits(codec, STA529_MISC,	FFX_CLK_MSK,
+				FFX_CLK_ENB);
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+			regcache_sync(sta529->regmap);
+		snd_soc_update_bits(codec, STA529_FFXCFG0,
+					POWER_CNTLMSAK, POWER_STDBY);
+		/* Making FFX output to zero */
+		snd_soc_update_bits(codec, STA529_FFXCFG0, FFX_MASK,
+				FFX_OFF);
+		snd_soc_update_bits(codec, STA529_MISC, FFX_CLK_MSK,
+				FFX_CLK_DIS);
+		break;
+	case SND_SOC_BIAS_OFF:
+		break;
+	}
+
+	/*
+	 * store the label for powers down audio subsystem for suspend.This is
+	 * used by soc core layer
+	 */
+	codec->dapm.bias_level = level;
+
+	return 0;
+
+}
+
+static int sta529_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;
+	int pdata, play_freq_val, record_freq_val;
+	int bclk_to_fs_ratio;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		pdata = 1;
+		bclk_to_fs_ratio = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		pdata = 2;
+		bclk_to_fs_ratio = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		pdata = 3;
+		bclk_to_fs_ratio = 2;
+		break;
+	default:
+		dev_err(codec->dev, "Unsupported format\n");
+		return -EINVAL;
+	}
+
+	switch (params_rate(params)) {
+	case 8000:
+	case 11025:
+		play_freq_val = 0;
+		record_freq_val = 2;
+		break;
+	case 16000:
+	case 22050:
+		play_freq_val = 1;
+		record_freq_val = 0;
+		break;
+
+	case 32000:
+	case 44100:
+	case 48000:
+		play_freq_val = 2;
+		record_freq_val = 0;
+		break;
+	default:
+		dev_err(codec->dev, "Unsupported rate\n");
+		return -EINVAL;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		snd_soc_update_bits(codec, STA529_S2PCFG1, PDATA_LEN_MSK,
+				pdata << 6);
+		snd_soc_update_bits(codec, STA529_S2PCFG1, BCLK_TO_FS_MSK,
+				bclk_to_fs_ratio << 4);
+		snd_soc_update_bits(codec, STA529_MISC, PLAY_FREQ_RANGE_MSK,
+				play_freq_val << 4);
+	} else {
+		snd_soc_update_bits(codec, STA529_P2SCFG1, PDATA_LEN_MSK,
+				pdata << 6);
+		snd_soc_update_bits(codec, STA529_P2SCFG1, BCLK_TO_FS_MSK,
+				bclk_to_fs_ratio << 4);
+		snd_soc_update_bits(codec, STA529_MISC, CAP_FREQ_RANGE_MSK,
+				record_freq_val << 2);
+	}
+
+	return 0;
+}
+
+static int sta529_mute(struct snd_soc_dai *dai, int mute)
+{
+	u8 val = 0;
+
+	if (mute)
+		val |= CODEC_MUTE_VAL;
+
+	snd_soc_update_bits(dai->codec, STA529_FFXCFG0, AUDIO_MUTE_MSK, val);
+
+	return 0;
+}
+
+static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
+{
+	struct snd_soc_codec *codec = codec_dai->codec;
+	u8 mode = 0;
+
+	/* interface format */
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_LEFT_J:
+		mode = LEFT_J_DATA_FORMAT;
+		break;
+	case SND_SOC_DAIFMT_I2S:
+		mode = I2S_DATA_FORMAT;
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		mode = RIGHT_J_DATA_FORMAT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, STA529_S2PCFG0, 0x0D, mode);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops sta529_dai_ops = {
+	.hw_params	=	sta529_hw_params,
+	.set_fmt	=	sta529_set_dai_fmt,
+	.digital_mute	=	sta529_mute,
+};
+
+static struct snd_soc_dai_driver sta529_dai = {
+	.name = "sta529-audio",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = STA529_RATES,
+		.formats = STA529_FORMAT,
+	},
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = STA529_RATES,
+		.formats = STA529_FORMAT,
+	},
+	.ops	= &sta529_dai_ops,
+};
+
+static int sta529_probe(struct snd_soc_codec *codec)
+{
+	struct sta529 *sta529 = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	codec->control_data = sta529->regmap;
+	ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP);
+
+	if (ret < 0) {
+		dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+		return ret;
+	}
+	sta529_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+
+/* power down chip */
+static int sta529_remove(struct snd_soc_codec *codec)
+{
+	sta529_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int sta529_suspend(struct snd_soc_codec *codec)
+{
+	sta529_set_bias_level(codec, SND_SOC_BIAS_OFF);
+
+	return 0;
+}
+
+static int sta529_resume(struct snd_soc_codec *codec)
+{
+	sta529_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+	return 0;
+}
+
+struct snd_soc_codec_driver sta529_codec_driver = {
+	.probe = sta529_probe,
+	.remove = sta529_remove,
+	.set_bias_level = sta529_set_bias_level,
+	.suspend = sta529_suspend,
+	.resume = sta529_resume,
+	.controls = sta529_snd_controls,
+	.num_controls = ARRAY_SIZE(sta529_snd_controls),
+};
+
+static const struct regmap_config sta529_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+
+	.max_register = STA529_MAX_REGISTER,
+	.readable_reg = sta529_readable,
+
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = sta529_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(sta529_reg_defaults),
+};
+
+static __devinit int sta529_i2c_probe(struct i2c_client *i2c,
+		const struct i2c_device_id *id)
+{
+	struct sta529 *sta529;
+	int ret;
+
+	if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return -EINVAL;
+
+	sta529 = devm_kzalloc(&i2c->dev, sizeof(struct sta529), GFP_KERNEL);
+	if (sta529 == NULL) {
+		dev_err(&i2c->dev, "Can not allocate memory\n");
+		return -ENOMEM;
+	}
+
+	sta529->regmap = devm_regmap_init_i2c(i2c, &sta529_regmap);
+	if (IS_ERR(sta529->regmap)) {
+		ret = PTR_ERR(sta529->regmap);
+		dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret);
+		return ret;
+	}
+
+	i2c_set_clientdata(i2c, sta529);
+
+	ret = snd_soc_register_codec(&i2c->dev,
+			&sta529_codec_driver, &sta529_dai, 1);
+	if (ret != 0)
+		dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
+
+	return ret;
+}
+
+static int __devexit sta529_i2c_remove(struct i2c_client *client)
+{
+	snd_soc_unregister_codec(&client->dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id sta529_i2c_id[] = {
+	{ "sta529", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, sta529_i2c_id);
+
+static struct i2c_driver sta529_i2c_driver = {
+	.driver = {
+		.name = "sta529",
+		.owner = THIS_MODULE,
+	},
+	.probe		= sta529_i2c_probe,
+	.remove		= __devexit_p(sta529_i2c_remove),
+	.id_table	= sta529_i2c_id,
+};
+
+module_i2c_driver(sta529_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC STA529 codec driver");
+MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>");
+MODULE_LICENSE("GPL");

+ 38 - 2
sound/soc/codecs/tlv320aic3x.c

@@ -118,7 +118,9 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
 	0x00, 0x00, 0x00, 0x00,	/* 88 */
 	0x00, 0x00, 0x00, 0x00,	/* 88 */
 	0x00, 0x00, 0x00, 0x00,	/* 92 */
 	0x00, 0x00, 0x00, 0x00,	/* 92 */
 	0x00, 0x00, 0x00, 0x00,	/* 96 */
 	0x00, 0x00, 0x00, 0x00,	/* 96 */
-	0x00, 0x00, 0x02,	/* 100 */
+	0x00, 0x00, 0x02, 0x00,	/* 100 */
+	0x00, 0x00, 0x00, 0x00,	/* 104 */
+	0x00, 0x00,            	/* 108 */
 };
 };
 
 
 #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
 #define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
@@ -229,6 +231,25 @@ static const struct soc_enum aic3x_enum[] = {
 	SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
 	SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
 };
 };
 
 
+static const char *aic3x_agc_level[] =
+	{ "-5.5dB", "-8dB", "-10dB", "-12dB", "-14dB", "-17dB", "-20dB", "-24dB" };
+static const struct soc_enum aic3x_agc_level_enum[] = {
+	SOC_ENUM_SINGLE(LAGC_CTRL_A, 4, 8, aic3x_agc_level),
+	SOC_ENUM_SINGLE(RAGC_CTRL_A, 4, 8, aic3x_agc_level),
+};
+
+static const char *aic3x_agc_attack[] = { "8ms", "11ms", "16ms", "20ms" };
+static const struct soc_enum aic3x_agc_attack_enum[] = {
+	SOC_ENUM_SINGLE(LAGC_CTRL_A, 2, 4, aic3x_agc_attack),
+	SOC_ENUM_SINGLE(RAGC_CTRL_A, 2, 4, aic3x_agc_attack),
+};
+
+static const char *aic3x_agc_decay[] = { "100ms", "200ms", "400ms", "500ms" };
+static const struct soc_enum aic3x_agc_decay_enum[] = {
+	SOC_ENUM_SINGLE(LAGC_CTRL_A, 0, 4, aic3x_agc_decay),
+	SOC_ENUM_SINGLE(RAGC_CTRL_A, 0, 4, aic3x_agc_decay),
+};
+
 /*
 /*
  * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps
  * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps
  */
  */
@@ -353,6 +374,15 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
 	 * adjust PGA to max value when ADC is on and will never go back.
 	 * adjust PGA to max value when ADC is on and will never go back.
 	*/
 	*/
 	SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0),
 	SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0),
+	SOC_ENUM("Left AGC Target level", aic3x_agc_level_enum[0]),
+	SOC_ENUM("Right AGC Target level", aic3x_agc_level_enum[1]),
+	SOC_ENUM("Left AGC Attack time", aic3x_agc_attack_enum[0]),
+	SOC_ENUM("Right AGC Attack time", aic3x_agc_attack_enum[1]),
+	SOC_ENUM("Left AGC Decay time", aic3x_agc_decay_enum[0]),
+	SOC_ENUM("Right AGC Decay time", aic3x_agc_decay_enum[1]),
+
+	/* De-emphasis */
+	SOC_DOUBLE("De-emphasis Switch", AIC3X_CODEC_DFILT_CTRL, 2, 0, 0x01, 0),
 
 
 	/* Input */
 	/* Input */
 	SOC_DOUBLE_R_TLV("PGA Capture Volume", LADC_VOL, RADC_VOL,
 	SOC_DOUBLE_R_TLV("PGA Capture Volume", LADC_VOL, RADC_VOL,
@@ -368,7 +398,7 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
 static DECLARE_TLV_DB_SCALE(classd_amp_tlv, 0, 600, 0);
 static DECLARE_TLV_DB_SCALE(classd_amp_tlv, 0, 600, 0);
 
 
 static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl =
 static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl =
-	SOC_DOUBLE_TLV("Class-D Amplifier Gain", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv);
+	SOC_DOUBLE_TLV("Class-D Playback Volume", CLASSD_CTRL, 6, 4, 3, 0, classd_amp_tlv);
 
 
 /* Left DAC Mux */
 /* Left DAC Mux */
 static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
 static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
@@ -970,6 +1000,12 @@ static int aic3x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct snd_soc_codec *codec = codec_dai->codec;
 	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
 	struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
 
 
+	/* set clock on MCLK or GPIO2 or BCLK */
+	snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, PLLCLK_IN_MASK,
+				clk_id << PLLCLK_IN_SHIFT);
+	snd_soc_update_bits(codec, AIC3X_CLKGEN_CTRL_REG, CLKDIV_IN_MASK,
+				clk_id << CLKDIV_IN_SHIFT);
+
 	aic3x->sysclk = freq;
 	aic3x->sysclk = freq;
 	return 0;
 	return 0;
 }
 }

+ 26 - 1
sound/soc/codecs/tlv320aic3x.h

@@ -13,7 +13,7 @@
 #define _AIC3X_H
 #define _AIC3X_H
 
 
 /* AIC3X register space */
 /* AIC3X register space */
-#define AIC3X_CACHEREGNUM		103
+#define AIC3X_CACHEREGNUM		110
 
 
 /* Page select register */
 /* Page select register */
 #define AIC3X_PAGE_SELECT		0
 #define AIC3X_PAGE_SELECT		0
@@ -74,6 +74,8 @@
 #define HPLCOM_CFG			37
 #define HPLCOM_CFG			37
 /* Right High Power Output control registers */
 /* Right High Power Output control registers */
 #define HPRCOM_CFG			38
 #define HPRCOM_CFG			38
+/* High Power Output Stage Control Register */
+#define HPOUT_SC			40
 /* DAC Output Switching control registers */
 /* DAC Output Switching control registers */
 #define DAC_LINE_MUX			41
 #define DAC_LINE_MUX			41
 /* High Power Output Driver Pop Reduction registers */
 /* High Power Output Driver Pop Reduction registers */
@@ -148,6 +150,17 @@
 #define AIC3X_GPIOB_REG			101
 #define AIC3X_GPIOB_REG			101
 /* Clock generation control register */
 /* Clock generation control register */
 #define AIC3X_CLKGEN_CTRL_REG		102
 #define AIC3X_CLKGEN_CTRL_REG		102
+/* New AGC registers */
+#define LAGCN_ATTACK			103
+#define LAGCN_DECAY			104
+#define RAGCN_ATTACK			105
+#define RAGCN_DECAY			106
+/* New Programmable ADC Digital Path and I2C Bus Condition Register */
+#define NEW_ADC_DIGITALPATH		107
+/* Passive Analog Signal Bypass Selection During Powerdown Register */
+#define PASSIVE_BYPASS			108
+/* DAC Quiescent Current Adjustment Register */
+#define DAC_ICC_ADJ			109
 
 
 /* Page select register bits */
 /* Page select register bits */
 #define PAGE0_SELECT		0
 #define PAGE0_SELECT		0
@@ -163,6 +176,10 @@
 #define DUAL_RATE_MODE		((1 << 5) | (1 << 6))
 #define DUAL_RATE_MODE		((1 << 5) | (1 << 6))
 #define LDAC2LCH		(0x1 << 3)
 #define LDAC2LCH		(0x1 << 3)
 #define RDAC2RCH		(0x1 << 1)
 #define RDAC2RCH		(0x1 << 1)
+#define LDAC2RCH		(0x2 << 3)
+#define RDAC2LCH		(0x2 << 1)
+#define LDAC2MONOMIX		(0x3 << 3)
+#define RDAC2MONOMIX		(0x3 << 1)
 
 
 /* PLL registers bitfields */
 /* PLL registers bitfields */
 #define PLLP_SHIFT		0
 #define PLLP_SHIFT		0
@@ -179,6 +196,14 @@
 #define PLL_CLKIN_SHIFT		4
 #define PLL_CLKIN_SHIFT		4
 #define MCLK_SOURCE		0x0
 #define MCLK_SOURCE		0x0
 #define PLL_CLKDIV_SHIFT	0
 #define PLL_CLKDIV_SHIFT	0
+#define PLLCLK_IN_MASK		0x30
+#define PLLCLK_IN_SHIFT		4
+#define CLKDIV_IN_MASK		0xc0
+#define CLKDIV_IN_SHIFT		6
+/* clock in source */
+#define CLKIN_MCLK		0
+#define CLKIN_GPIO2		1
+#define CLKIN_BCLK		2
 
 
 /* Software reset register bits */
 /* Software reset register bits */
 #define SOFT_RESET		0x80
 #define SOFT_RESET		0x80

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

@@ -553,7 +553,7 @@ static const struct snd_kcontrol_new vibrar_mux_controls =
 
 
 /* Headset power mode */
 /* Headset power mode */
 static const char *twl6040_power_mode_texts[] = {
 static const char *twl6040_power_mode_texts[] = {
-	"Low-Power", "High-Perfomance",
+	"Low-Power", "High-Performance",
 };
 };
 
 
 static const struct soc_enum twl6040_power_mode_enum =
 static const struct soc_enum twl6040_power_mode_enum =

+ 5 - 2
sound/soc/codecs/wm1250-ev1.c

@@ -121,20 +121,23 @@ static const struct snd_soc_dai_ops wm1250_ev1_ops = {
 	.hw_params = wm1250_ev1_hw_params,
 	.hw_params = wm1250_ev1_hw_params,
 };
 };
 
 
+#define WM1250_EV1_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+			  SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_64000)
+
 static struct snd_soc_dai_driver wm1250_ev1_dai = {
 static struct snd_soc_dai_driver wm1250_ev1_dai = {
 	.name = "wm1250-ev1",
 	.name = "wm1250-ev1",
 	.playback = {
 	.playback = {
 		.stream_name = "Playback",
 		.stream_name = "Playback",
 		.channels_min = 1,
 		.channels_min = 1,
 		.channels_max = 2,
 		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_8000,
+		.rates = WM1250_EV1_RATES,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
 	},
 	.capture = {
 	.capture = {
 		.stream_name = "Capture",
 		.stream_name = "Capture",
 		.channels_min = 1,
 		.channels_min = 1,
 		.channels_max = 2,
 		.channels_max = 2,
-		.rates = SNDRV_PCM_RATE_8000,
+		.rates = WM1250_EV1_RATES,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 		.formats = SNDRV_PCM_FMTBIT_S16_LE,
 	},
 	},
 	.ops = &wm1250_ev1_ops,
 	.ops = &wm1250_ev1_ops,

+ 31 - 1
sound/soc/codecs/wm2000.c

@@ -1,7 +1,7 @@
 /*
 /*
  * wm2000.c  --  WM2000 ALSA Soc Audio driver
  * wm2000.c  --  WM2000 ALSA Soc Audio driver
  *
  *
- * Copyright 2008-2010 Wolfson Microelectronics PLC.
+ * Copyright 2008-2011 Wolfson Microelectronics PLC.
  *
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *
@@ -674,9 +674,39 @@ static int wm2000_resume(struct snd_soc_codec *codec)
 #define wm2000_resume NULL
 #define wm2000_resume NULL
 #endif
 #endif
 
 
+static bool wm2000_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case WM2000_REG_SYS_START:
+	case WM2000_REG_SPEECH_CLARITY:
+	case WM2000_REG_SYS_WATCHDOG:
+	case WM2000_REG_ANA_VMID_PD_TIME:
+	case WM2000_REG_ANA_VMID_PU_TIME:
+	case WM2000_REG_CAT_FLTR_INDX:
+	case WM2000_REG_CAT_GAIN_0:
+	case WM2000_REG_SYS_STATUS:
+	case WM2000_REG_SYS_MODE_CNTRL:
+	case WM2000_REG_SYS_START0:
+	case WM2000_REG_SYS_START1:
+	case WM2000_REG_ID1:
+	case WM2000_REG_ID2:
+	case WM2000_REG_REVISON:
+	case WM2000_REG_SYS_CTL1:
+	case WM2000_REG_SYS_CTL2:
+	case WM2000_REG_ANC_STAT:
+	case WM2000_REG_IF_CTL:
+		return true;
+	default:
+		return false;
+	}
+}
+
 static const struct regmap_config wm2000_regmap = {
 static const struct regmap_config wm2000_regmap = {
 	.reg_bits = 8,
 	.reg_bits = 8,
 	.val_bits = 8,
 	.val_bits = 8,
+
+	.max_register = WM2000_REG_IF_CTL,
+	.readable_reg = wm2000_readable_reg,
 };
 };
 
 
 static int wm2000_probe(struct snd_soc_codec *codec)
 static int wm2000_probe(struct snd_soc_codec *codec)

+ 1 - 1
sound/soc/codecs/wm5100-tables.c

@@ -1,7 +1,7 @@
 /*
 /*
  * wm5100-tables.c  --  WM5100 ALSA SoC Audio driver data
  * wm5100-tables.c  --  WM5100 ALSA SoC Audio driver data
  *
  *
- * Copyright 2011 Wolfson Microelectronics plc
+ * Copyright 2011-2 Wolfson Microelectronics plc
  *
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *

+ 1 - 10
sound/soc/codecs/wm5100.c

@@ -1,7 +1,7 @@
 /*
 /*
  * wm5100.c  --  WM5100 ALSA SoC Audio driver
  * wm5100.c  --  WM5100 ALSA SoC Audio driver
  *
  *
- * Copyright 2011 Wolfson Microelectronics plc
+ * Copyright 2011-2 Wolfson Microelectronics plc
  *
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *
@@ -2378,13 +2378,6 @@ static int wm5100_remove(struct snd_soc_codec *codec)
 	return 0;
 	return 0;
 }
 }
 
 
-static int wm5100_soc_volatile(struct snd_soc_codec *codec,
-			       unsigned int reg)
-{
-	return true;
-}
-
-
 static struct snd_soc_codec_driver soc_codec_dev_wm5100 = {
 static struct snd_soc_codec_driver soc_codec_dev_wm5100 = {
 	.probe =	wm5100_probe,
 	.probe =	wm5100_probe,
 	.remove =	wm5100_remove,
 	.remove =	wm5100_remove,
@@ -2392,8 +2385,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5100 = {
 	.set_sysclk = wm5100_set_sysclk,
 	.set_sysclk = wm5100_set_sysclk,
 	.set_pll = wm5100_set_fll,
 	.set_pll = wm5100_set_fll,
 	.idle_bias_off = 1,
 	.idle_bias_off = 1,
-	.reg_cache_size = WM5100_MAX_REGISTER,
-	.volatile_register = wm5100_soc_volatile,
 
 
 	.seq_notifier = wm5100_seq_notifier,
 	.seq_notifier = wm5100_seq_notifier,
 	.controls = wm5100_snd_controls,
 	.controls = wm5100_snd_controls,

+ 903 - 0
sound/soc/codecs/wm5102.c

@@ -0,0 +1,903 @@
+/*
+ * wm5102.c  --  WM5102 ALSA SoC Audio driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+#include "wm5102.h"
+
+struct wm5102_priv {
+	struct arizona_priv core;
+	struct arizona_fll fll[2];
+};
+
+static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new wm5102_snd_controls[] = {
+SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
+	   ARIZONA_IN1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
+	   ARIZONA_IN2_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
+	   ARIZONA_IN3_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL,
+		       ARIZONA_IN1R_CONTROL,
+		       ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL,
+		       ARIZONA_IN2R_CONTROL,
+		       ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL,
+		       ARIZONA_IN3R_CONTROL,
+		       ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+	     ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+	     ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+	     ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+		 ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+		 ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+		 ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+
+ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+
+ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
+		   ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5,
+		   ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_ENA),
+
+ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+
+SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+
+ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR,
+	       ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv),
+
+ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT2L", ARIZONA_OUT2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT2R", ARIZONA_OUT2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_1L,
+	   ARIZONA_OUT1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("OUT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_2L,
+	   ARIZONA_OUT2_OSR_SHIFT, 1, 0),
+SOC_SINGLE("EPOUT High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L,
+	   ARIZONA_OUT3_OSR_SHIFT, 1, 0),
+SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
+	   ARIZONA_OUT4_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
+	   ARIZONA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+	     ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("OUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+	     ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+	   ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+	     ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+	     ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+		 ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("OUT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+		 ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+	       ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+		 ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+		 ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+
+SOC_DOUBLE_R_RANGE_TLV("HPOUT1 Volume", ARIZONA_OUTPUT_PATH_CONFIG_1L,
+		       ARIZONA_OUTPUT_PATH_CONFIG_1R,
+		       ARIZONA_OUT1L_PGA_VOL_SHIFT,
+		       0x34, 0x40, 0, ana_tlv),
+SOC_DOUBLE_R_RANGE_TLV("OUT2 Volume", ARIZONA_OUTPUT_PATH_CONFIG_2L,
+		       ARIZONA_OUTPUT_PATH_CONFIG_2R,
+		       ARIZONA_OUT2L_PGA_VOL_SHIFT,
+		       0x34, 0x40, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("EPOUT Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L,
+		     ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+	   ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+
+ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
+		    0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
+		    ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
+		    ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
+		    ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+		    ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
+		 ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1,
+		 ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1,
+		 ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1,
+		 ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0,
+		 NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0,
+		 NULL, 0),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT,
+		 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT,
+		 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
+		 NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+		     ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+		     ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+		    ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+		    ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+		     ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+		     ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+		    ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
+		    ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4L", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4R", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+ARIZONA_MIXER_WIDGETS(Mic, "Mic"),
+ARIZONA_MIXER_WIDGETS(Noise, "Noise"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+ARIZONA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),
+ARIZONA_MIXER_WIDGETS(OUT2R, "HPOUT2R"),
+ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+
+ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+ARIZONA_MIXER_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MIXER_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MIXER_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MIXER_WIDGETS(ASRC2R, "ASRC2R"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("EPOUTN"),
+SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name)	\
+	{ name, "Noise Generator", "Noise Generator" }, \
+	{ name, "Tone Generator 1", "Tone Generator 1" }, \
+	{ name, "Tone Generator 2", "Tone Generator 2" }, \
+	{ name, "IN1L", "IN1L PGA" }, \
+	{ name, "IN1R", "IN1R PGA" }, \
+	{ name, "IN2L", "IN2L PGA" }, \
+	{ name, "IN2R", "IN2R PGA" }, \
+	{ name, "IN3L", "IN3L PGA" }, \
+	{ name, "IN3R", "IN3R PGA" }, \
+	{ name, "Mic Mute Mixer", "Mic Mute Mixer" }, \
+	{ name, "AIF1RX1", "AIF1RX1" }, \
+	{ name, "AIF1RX2", "AIF1RX2" }, \
+	{ name, "AIF1RX3", "AIF1RX3" }, \
+	{ name, "AIF1RX4", "AIF1RX4" }, \
+	{ name, "AIF1RX5", "AIF1RX5" }, \
+	{ name, "AIF1RX6", "AIF1RX6" }, \
+	{ name, "AIF1RX7", "AIF1RX7" }, \
+	{ name, "AIF1RX8", "AIF1RX8" }, \
+	{ name, "AIF2RX1", "AIF2RX1" }, \
+	{ name, "AIF2RX2", "AIF2RX2" }, \
+	{ name, "AIF3RX1", "AIF3RX1" }, \
+	{ name, "AIF3RX2", "AIF3RX2" }, \
+	{ name, "EQ1", "EQ1" }, \
+	{ name, "EQ2", "EQ2" }, \
+	{ name, "EQ3", "EQ3" }, \
+	{ name, "EQ4", "EQ4" }, \
+	{ name, "DRC1L", "DRC1L" }, \
+	{ name, "DRC1R", "DRC1R" }, \
+	{ name, "DRC2L", "DRC2L" }, \
+	{ name, "DRC2R", "DRC2R" }, \
+	{ name, "LHPF1", "LHPF1" }, \
+	{ name, "LHPF2", "LHPF2" }, \
+	{ name, "LHPF3", "LHPF3" }, \
+	{ name, "LHPF4", "LHPF4" }, \
+	{ name, "ASRC1L", "ASRC1L" }, \
+	{ name, "ASRC1R", "ASRC1R" }, \
+	{ name, "ASRC2L", "ASRC2L" }, \
+	{ name, "ASRC2R", "ASRC2R" }
+
+static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
+	{ "AIF2 Capture", NULL, "DBVDD2" },
+	{ "AIF2 Playback", NULL, "DBVDD2" },
+
+	{ "AIF3 Capture", NULL, "DBVDD3" },
+	{ "AIF3 Playback", NULL, "DBVDD3" },
+
+	{ "OUT1L", NULL, "CPVDD" },
+	{ "OUT1R", NULL, "CPVDD" },
+	{ "OUT2L", NULL, "CPVDD" },
+	{ "OUT2R", NULL, "CPVDD" },
+	{ "OUT3L", NULL, "CPVDD" },
+
+	{ "OUT4L", NULL, "SPKVDDL" },
+	{ "OUT4R", NULL, "SPKVDDR" },
+
+	{ "OUT1L", NULL, "SYSCLK" },
+	{ "OUT1R", NULL, "SYSCLK" },
+	{ "OUT2L", NULL, "SYSCLK" },
+	{ "OUT2R", NULL, "SYSCLK" },
+	{ "OUT3L", NULL, "SYSCLK" },
+	{ "OUT4L", NULL, "SYSCLK" },
+	{ "OUT4R", NULL, "SYSCLK" },
+	{ "OUT5L", NULL, "SYSCLK" },
+	{ "OUT5R", NULL, "SYSCLK" },
+
+	{ "MICBIAS1", NULL, "MICVDD" },
+	{ "MICBIAS2", NULL, "MICVDD" },
+	{ "MICBIAS3", NULL, "MICVDD" },
+
+	{ "Noise Generator", NULL, "NOISE" },
+	{ "Tone Generator 1", NULL, "TONE" },
+	{ "Tone Generator 2", NULL, "TONE" },
+
+	{ "Mic Mute Mixer", NULL, "Noise Mixer" },
+	{ "Mic Mute Mixer", NULL, "Mic Mixer" },
+
+	{ "AIF1 Capture", NULL, "AIF1TX1" },
+	{ "AIF1 Capture", NULL, "AIF1TX2" },
+	{ "AIF1 Capture", NULL, "AIF1TX3" },
+	{ "AIF1 Capture", NULL, "AIF1TX4" },
+	{ "AIF1 Capture", NULL, "AIF1TX5" },
+	{ "AIF1 Capture", NULL, "AIF1TX6" },
+	{ "AIF1 Capture", NULL, "AIF1TX7" },
+	{ "AIF1 Capture", NULL, "AIF1TX8" },
+
+	{ "AIF1RX1", NULL, "AIF1 Playback" },
+	{ "AIF1RX2", NULL, "AIF1 Playback" },
+	{ "AIF1RX3", NULL, "AIF1 Playback" },
+	{ "AIF1RX4", NULL, "AIF1 Playback" },
+	{ "AIF1RX5", NULL, "AIF1 Playback" },
+	{ "AIF1RX6", NULL, "AIF1 Playback" },
+	{ "AIF1RX7", NULL, "AIF1 Playback" },
+	{ "AIF1RX8", NULL, "AIF1 Playback" },
+
+	{ "AIF2 Capture", NULL, "AIF2TX1" },
+	{ "AIF2 Capture", NULL, "AIF2TX2" },
+
+	{ "AIF2RX1", NULL, "AIF2 Playback" },
+	{ "AIF2RX2", NULL, "AIF2 Playback" },
+
+	{ "AIF3 Capture", NULL, "AIF3TX1" },
+	{ "AIF3 Capture", NULL, "AIF3TX2" },
+
+	{ "AIF3RX1", NULL, "AIF3 Playback" },
+	{ "AIF3RX2", NULL, "AIF3 Playback" },
+
+	{ "AIF1 Playback", NULL, "SYSCLK" },
+	{ "AIF2 Playback", NULL, "SYSCLK" },
+	{ "AIF3 Playback", NULL, "SYSCLK" },
+
+	{ "AIF1 Capture", NULL, "SYSCLK" },
+	{ "AIF2 Capture", NULL, "SYSCLK" },
+	{ "AIF3 Capture", NULL, "SYSCLK" },
+
+	ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+	ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+	ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
+	ARIZONA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
+	ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"),
+
+	ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+	ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
+	ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+	ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+	ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+	ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+	ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+	ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+	ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+	ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+	ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+	ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+	ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+	ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+	ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+	ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+
+	ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+	ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+	ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
+	ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
+	ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
+	ARIZONA_MIXER_ROUTES("EQ4", "EQ4"),
+
+	ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
+	ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
+	ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"),
+	ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+	ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+	ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+	ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+	ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+	ARIZONA_MIXER_ROUTES("ASRC1L", "ASRC1L"),
+	ARIZONA_MIXER_ROUTES("ASRC1R", "ASRC1R"),
+	ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"),
+	ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"),
+
+	{ "HPOUT1L", NULL, "OUT1L" },
+	{ "HPOUT1R", NULL, "OUT1R" },
+
+	{ "HPOUT2L", NULL, "OUT2L" },
+	{ "HPOUT2R", NULL, "OUT2R" },
+
+	{ "EPOUTN", NULL, "OUT3L" },
+	{ "EPOUTP", NULL, "OUT3L" },
+
+	{ "SPKOUTLN", NULL, "OUT4L" },
+	{ "SPKOUTLP", NULL, "OUT4L" },
+
+	{ "SPKOUTRN", NULL, "OUT4R" },
+	{ "SPKOUTRP", NULL, "OUT4R" },
+
+	{ "SPKDAT1L", NULL, "OUT5L" },
+	{ "SPKDAT1R", NULL, "OUT5R" },
+};
+
+static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+			  unsigned int Fref, unsigned int Fout)
+{
+	struct wm5102_priv *wm5102 = snd_soc_codec_get_drvdata(codec);
+
+	switch (fll_id) {
+	case WM5102_FLL1:
+		return arizona_set_fll(&wm5102->fll[0], source, Fref, Fout);
+	case WM5102_FLL2:
+		return arizona_set_fll(&wm5102->fll[1], source, Fref, Fout);
+	default:
+		return -EINVAL;
+	}
+}
+
+#define WM5102_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM5102_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_driver wm5102_dai[] = {
+	{
+		.name = "wm5102-aif1",
+		.id = 1,
+		.base = ARIZONA_AIF1_BCLK_CTRL,
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = WM5102_RATES,
+			.formats = WM5102_FORMATS,
+		},
+		.capture = {
+			 .stream_name = "AIF1 Capture",
+			 .channels_min = 1,
+			 .channels_max = 8,
+			 .rates = WM5102_RATES,
+			 .formats = WM5102_FORMATS,
+		 },
+		.ops = &arizona_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "wm5102-aif2",
+		.id = 2,
+		.base = ARIZONA_AIF2_BCLK_CTRL,
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = WM5102_RATES,
+			.formats = WM5102_FORMATS,
+		},
+		.capture = {
+			 .stream_name = "AIF2 Capture",
+			 .channels_min = 1,
+			 .channels_max = 2,
+			 .rates = WM5102_RATES,
+			 .formats = WM5102_FORMATS,
+		 },
+		.ops = &arizona_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "wm5102-aif3",
+		.id = 3,
+		.base = ARIZONA_AIF3_BCLK_CTRL,
+		.playback = {
+			.stream_name = "AIF3 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = WM5102_RATES,
+			.formats = WM5102_FORMATS,
+		},
+		.capture = {
+			 .stream_name = "AIF3 Capture",
+			 .channels_min = 1,
+			 .channels_max = 2,
+			 .rates = WM5102_RATES,
+			 .formats = WM5102_FORMATS,
+		 },
+		.ops = &arizona_dai_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static int wm5102_codec_probe(struct snd_soc_codec *codec)
+{
+	struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	codec->control_data = priv->core.arizona->regmap;
+	return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+}
+
+#define WM5102_DIG_VU 0x0200
+
+static unsigned int wm5102_digital_vu[] = {
+	ARIZONA_ADC_DIGITAL_VOLUME_1L,
+	ARIZONA_ADC_DIGITAL_VOLUME_1R,
+	ARIZONA_ADC_DIGITAL_VOLUME_2L,
+	ARIZONA_ADC_DIGITAL_VOLUME_2R,
+	ARIZONA_ADC_DIGITAL_VOLUME_3L,
+	ARIZONA_ADC_DIGITAL_VOLUME_3R,
+
+	ARIZONA_DAC_DIGITAL_VOLUME_1L,
+	ARIZONA_DAC_DIGITAL_VOLUME_1R,
+	ARIZONA_DAC_DIGITAL_VOLUME_2L,
+	ARIZONA_DAC_DIGITAL_VOLUME_2R,
+	ARIZONA_DAC_DIGITAL_VOLUME_3L,
+	ARIZONA_DAC_DIGITAL_VOLUME_3R,
+	ARIZONA_DAC_DIGITAL_VOLUME_4L,
+	ARIZONA_DAC_DIGITAL_VOLUME_4R,
+	ARIZONA_DAC_DIGITAL_VOLUME_5L,
+	ARIZONA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm5102 = {
+	.probe = wm5102_codec_probe,
+
+	.idle_bias_off = true,
+
+	.set_sysclk = arizona_set_sysclk,
+	.set_pll = wm5102_set_fll,
+
+	.controls = wm5102_snd_controls,
+	.num_controls = ARRAY_SIZE(wm5102_snd_controls),
+	.dapm_widgets = wm5102_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(wm5102_dapm_widgets),
+	.dapm_routes = wm5102_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes),
+};
+
+static int __devinit wm5102_probe(struct platform_device *pdev)
+{
+	struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+	struct wm5102_priv *wm5102;
+	int i;
+
+	wm5102 = devm_kzalloc(&pdev->dev, sizeof(struct wm5102_priv),
+			      GFP_KERNEL);
+	if (wm5102 == NULL)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, wm5102);
+
+	wm5102->core.arizona = arizona;
+
+	for (i = 0; i < ARRAY_SIZE(wm5102->fll); i++)
+		wm5102->fll[i].vco_mult = 1;
+
+	arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+			 ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+			 &wm5102->fll[0]);
+	arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+			 ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+			 &wm5102->fll[1]);
+
+	for (i = 0; i < ARRAY_SIZE(wm5102_dai); i++)
+		arizona_init_dai(&wm5102->core, i);
+
+	/* Latch volume update bits */
+	for (i = 0; i < ARRAY_SIZE(wm5102_digital_vu); i++)
+		regmap_update_bits(arizona->regmap, wm5102_digital_vu[i],
+				   WM5102_DIG_VU, WM5102_DIG_VU);
+
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_idle(&pdev->dev);
+
+	return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102,
+				      wm5102_dai, ARRAY_SIZE(wm5102_dai));
+}
+
+static int __devexit wm5102_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_codec(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver wm5102_codec_driver = {
+	.driver = {
+		.name = "wm5102-codec",
+		.owner = THIS_MODULE,
+	},
+	.probe = wm5102_probe,
+	.remove = __devexit_p(wm5102_remove),
+};
+
+module_platform_driver(wm5102_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM5102 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm5102-codec");

+ 21 - 0
sound/soc/codecs/wm5102.h

@@ -0,0 +1,21 @@
+/*
+ * wm5102.h  --  WM5102 ALSA SoC Audio driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM5102_H
+#define _WM5102_H
+
+#include "arizona.h"
+
+#define WM5102_FLL1 1
+#define WM5102_FLL2 2
+
+#endif

+ 950 - 0
sound/soc/codecs/wm5110.c

@@ -0,0 +1,950 @@
+/*
+ * wm5110.c  --  WM5110 ALSA SoC Audio driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+#include "wm5110.h"
+
+struct wm5110_priv {
+	struct arizona_priv core;
+	struct arizona_fll fll[2];
+};
+
+static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+
+static const struct snd_kcontrol_new wm5110_snd_controls[] = {
+SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
+	   ARIZONA_IN1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
+	   ARIZONA_IN2_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
+	   ARIZONA_IN3_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL,
+	   ARIZONA_IN4_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL,
+		       ARIZONA_IN1R_CONTROL,
+		       ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL,
+		       ARIZONA_IN2R_CONTROL,
+		       ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL,
+		       ARIZONA_IN3R_CONTROL,
+		       ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+	     ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+	     ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+	     ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("IN4 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L,
+	     ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+		 ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+		 ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+		 ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("IN4 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L,
+		 ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_DIG_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+
+ARIZONA_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+
+SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT,
+	       24, 0, eq_tlv),
+
+ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
+		   ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
+SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5,
+		   ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_ENA),
+
+ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+
+SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+
+ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR,
+	       ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv),
+
+ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT2L", ARIZONA_OUT2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT2R", ARIZONA_OUT2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT2L", ARIZONA_OUT6LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT2R", ARIZONA_OUT6RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("HPOUT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_1L,
+	   ARIZONA_OUT1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("OUT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_2L,
+	   ARIZONA_OUT2_OSR_SHIFT, 1, 0),
+SOC_SINGLE("EPOUT High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L,
+	   ARIZONA_OUT3_OSR_SHIFT, 1, 0),
+SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
+	   ARIZONA_OUT4_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
+	   ARIZONA_OUT5_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_6L,
+	   ARIZONA_OUT6_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+	     ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("OUT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+	     ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+	   ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+	     ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+	     ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT2 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_6L,
+	     ARIZONA_DAC_DIGITAL_VOLUME_6R, ARIZONA_OUT6L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+		 ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("OUT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+		 ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+	       ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+		 ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+		 ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT2 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_6L,
+		 ARIZONA_DAC_DIGITAL_VOLUME_6R, ARIZONA_OUT6L_VOL_SHIFT,
+		 0xbf, 0, digital_tlv),
+
+SOC_DOUBLE_R_RANGE_TLV("HPOUT1 Volume", ARIZONA_OUTPUT_PATH_CONFIG_1L,
+		       ARIZONA_OUTPUT_PATH_CONFIG_1R,
+		       ARIZONA_OUT1L_PGA_VOL_SHIFT,
+		       0x34, 0x40, 0, ana_tlv),
+SOC_DOUBLE_R_RANGE_TLV("OUT2 Volume", ARIZONA_OUTPUT_PATH_CONFIG_2L,
+		       ARIZONA_OUTPUT_PATH_CONFIG_2R,
+		       ARIZONA_OUT2L_PGA_VOL_SHIFT,
+		       0x34, 0x40, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("EPOUT Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L,
+		     ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv),
+
+SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+	   ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT,
+	   ARIZONA_SPK2R_MUTE_SHIFT, 1, 1),
+
+ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1R, ARIZONA_OUT5RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT2L, ARIZONA_OUT6LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT2R, ARIZONA_OUT6RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
+		    0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
+		    ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+SND_SOC_DAPM_INPUT("IN3L"),
+SND_SOC_DAPM_INPUT("IN3R"),
+SND_SOC_DAPM_INPUT("IN4L"),
+SND_SOC_DAPM_INPUT("IN4R"),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN4R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4R_ENA_SHIFT,
+		   0, NULL, 0, arizona_in_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
+		    ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
+		    ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+		    ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
+		 ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1,
+		 ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1,
+		 ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1,
+		 ARIZONA_MICMUTE_MIX_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0,
+		 NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0,
+		 NULL, 0),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT,
+		 0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT,
+		 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
+		 NULL, 0),
+SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
+		 NULL, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+		     ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+		    ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+		     ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+		     ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+		    ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+		    ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+		     ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+		     ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+		    ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
+		    ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4L", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT4R", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT6L", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT6L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT6R", ARIZONA_OUTPUT_ENABLES_1,
+		   ARIZONA_OUT6R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+		   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"),
+ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"),
+ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+ARIZONA_MIXER_WIDGETS(Mic, "Mic"),
+ARIZONA_MIXER_WIDGETS(Noise, "Noise"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+ARIZONA_MIXER_WIDGETS(OUT2L, "HPOUT2L"),
+ARIZONA_MIXER_WIDGETS(OUT2R, "HPOUT2R"),
+ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+ARIZONA_MIXER_WIDGETS(SPKDAT2L, "SPKDAT2L"),
+ARIZONA_MIXER_WIDGETS(SPKDAT2R, "SPKDAT2R"),
+
+ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+ARIZONA_MIXER_WIDGETS(AIF1TX7, "AIF1TX7"),
+ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+
+ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+ARIZONA_MIXER_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MIXER_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MIXER_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MIXER_WIDGETS(ASRC2R, "ASRC2R"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("HPOUT2L"),
+SND_SOC_DAPM_OUTPUT("HPOUT2R"),
+SND_SOC_DAPM_OUTPUT("EPOUTN"),
+SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+SND_SOC_DAPM_OUTPUT("SPKDAT2L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT2R"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name)	\
+	{ name, "Noise Generator", "Noise Generator" }, \
+	{ name, "Tone Generator 1", "Tone Generator 1" }, \
+	{ name, "Tone Generator 2", "Tone Generator 2" }, \
+	{ name, "IN1L", "IN1L PGA" }, \
+	{ name, "IN1R", "IN1R PGA" }, \
+	{ name, "IN2L", "IN2L PGA" }, \
+	{ name, "IN2R", "IN2R PGA" }, \
+	{ name, "IN3L", "IN3L PGA" }, \
+	{ name, "IN3R", "IN3R PGA" }, \
+	{ name, "IN4L", "IN4L PGA" }, \
+	{ name, "IN4R", "IN4R PGA" }, \
+	{ name, "Mic Mute Mixer", "Mic Mute Mixer" }, \
+	{ name, "AIF1RX1", "AIF1RX1" }, \
+	{ name, "AIF1RX2", "AIF1RX2" }, \
+	{ name, "AIF1RX3", "AIF1RX3" }, \
+	{ name, "AIF1RX4", "AIF1RX4" }, \
+	{ name, "AIF1RX5", "AIF1RX5" }, \
+	{ name, "AIF1RX6", "AIF1RX6" }, \
+	{ name, "AIF1RX7", "AIF1RX7" }, \
+	{ name, "AIF1RX8", "AIF1RX8" }, \
+	{ name, "AIF2RX1", "AIF2RX1" }, \
+	{ name, "AIF2RX2", "AIF2RX2" }, \
+	{ name, "AIF3RX1", "AIF3RX1" }, \
+	{ name, "AIF3RX2", "AIF3RX2" }, \
+	{ name, "EQ1", "EQ1" }, \
+	{ name, "EQ2", "EQ2" }, \
+	{ name, "EQ3", "EQ3" }, \
+	{ name, "EQ4", "EQ4" }, \
+	{ name, "DRC1L", "DRC1L" }, \
+	{ name, "DRC1R", "DRC1R" }, \
+	{ name, "DRC2L", "DRC2L" }, \
+	{ name, "DRC2R", "DRC2R" }, \
+	{ name, "LHPF1", "LHPF1" }, \
+	{ name, "LHPF2", "LHPF2" }, \
+	{ name, "LHPF3", "LHPF3" }, \
+	{ name, "LHPF4", "LHPF4" }, \
+	{ name, "ASRC1L", "ASRC1L" }, \
+	{ name, "ASRC1R", "ASRC1R" }, \
+	{ name, "ASRC2L", "ASRC2L" }, \
+	{ name, "ASRC2R", "ASRC2R" }
+
+static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
+	{ "AIF2 Capture", NULL, "DBVDD2" },
+	{ "AIF2 Playback", NULL, "DBVDD2" },
+
+	{ "AIF3 Capture", NULL, "DBVDD3" },
+	{ "AIF3 Playback", NULL, "DBVDD3" },
+
+	{ "OUT1L", NULL, "CPVDD" },
+	{ "OUT1R", NULL, "CPVDD" },
+	{ "OUT2L", NULL, "CPVDD" },
+	{ "OUT2R", NULL, "CPVDD" },
+	{ "OUT3L", NULL, "CPVDD" },
+
+	{ "OUT4L", NULL, "SPKVDDL" },
+	{ "OUT4R", NULL, "SPKVDDR" },
+
+	{ "OUT1L", NULL, "SYSCLK" },
+	{ "OUT1R", NULL, "SYSCLK" },
+	{ "OUT2L", NULL, "SYSCLK" },
+	{ "OUT2R", NULL, "SYSCLK" },
+	{ "OUT3L", NULL, "SYSCLK" },
+	{ "OUT4L", NULL, "SYSCLK" },
+	{ "OUT4R", NULL, "SYSCLK" },
+	{ "OUT5L", NULL, "SYSCLK" },
+	{ "OUT5R", NULL, "SYSCLK" },
+	{ "OUT6L", NULL, "SYSCLK" },
+	{ "OUT6R", NULL, "SYSCLK" },
+
+	{ "MICBIAS1", NULL, "MICVDD" },
+	{ "MICBIAS2", NULL, "MICVDD" },
+	{ "MICBIAS3", NULL, "MICVDD" },
+
+	{ "Noise Generator", NULL, "NOISE" },
+	{ "Tone Generator 1", NULL, "TONE" },
+	{ "Tone Generator 2", NULL, "TONE" },
+
+	{ "Mic Mute Mixer", NULL, "Noise Mixer" },
+	{ "Mic Mute Mixer", NULL, "Mic Mixer" },
+
+	{ "AIF1 Capture", NULL, "AIF1TX1" },
+	{ "AIF1 Capture", NULL, "AIF1TX2" },
+	{ "AIF1 Capture", NULL, "AIF1TX3" },
+	{ "AIF1 Capture", NULL, "AIF1TX4" },
+	{ "AIF1 Capture", NULL, "AIF1TX5" },
+	{ "AIF1 Capture", NULL, "AIF1TX6" },
+	{ "AIF1 Capture", NULL, "AIF1TX7" },
+	{ "AIF1 Capture", NULL, "AIF1TX8" },
+
+	{ "AIF1RX1", NULL, "AIF1 Playback" },
+	{ "AIF1RX2", NULL, "AIF1 Playback" },
+	{ "AIF1RX3", NULL, "AIF1 Playback" },
+	{ "AIF1RX4", NULL, "AIF1 Playback" },
+	{ "AIF1RX5", NULL, "AIF1 Playback" },
+	{ "AIF1RX6", NULL, "AIF1 Playback" },
+	{ "AIF1RX7", NULL, "AIF1 Playback" },
+	{ "AIF1RX8", NULL, "AIF1 Playback" },
+
+	{ "AIF2 Capture", NULL, "AIF2TX1" },
+	{ "AIF2 Capture", NULL, "AIF2TX2" },
+
+	{ "AIF2RX1", NULL, "AIF2 Playback" },
+	{ "AIF2RX2", NULL, "AIF2 Playback" },
+
+	{ "AIF3 Capture", NULL, "AIF3TX1" },
+	{ "AIF3 Capture", NULL, "AIF3TX2" },
+
+	{ "AIF3RX1", NULL, "AIF3 Playback" },
+	{ "AIF3RX2", NULL, "AIF3 Playback" },
+
+	{ "AIF1 Playback", NULL, "SYSCLK" },
+	{ "AIF2 Playback", NULL, "SYSCLK" },
+	{ "AIF3 Playback", NULL, "SYSCLK" },
+
+	{ "AIF1 Capture", NULL, "SYSCLK" },
+	{ "AIF2 Capture", NULL, "SYSCLK" },
+	{ "AIF3 Capture", NULL, "SYSCLK" },
+
+	ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+	ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+	ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
+	ARIZONA_MIXER_ROUTES("OUT2R", "HPOUT2R"),
+	ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"),
+
+	ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+	ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
+	ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+	ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+	ARIZONA_MIXER_ROUTES("OUT6L", "SPKDAT2L"),
+	ARIZONA_MIXER_ROUTES("OUT6R", "SPKDAT2R"),
+
+	ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+	ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+	ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+	ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+	ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+	ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+	ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+	ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+	ARIZONA_MIXER_ROUTES("AIF1TX7", "AIF1TX7"),
+	ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+	ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+	ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+
+	ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+	ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+	ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
+	ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
+	ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
+	ARIZONA_MIXER_ROUTES("EQ4", "EQ4"),
+
+	ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
+	ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
+	ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"),
+	ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+	ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+	ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+	ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+	ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+	ARIZONA_MIXER_ROUTES("ASRC1L", "ASRC1L"),
+	ARIZONA_MIXER_ROUTES("ASRC1R", "ASRC1R"),
+	ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"),
+	ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"),
+
+	{ "HPOUT1L", NULL, "OUT1L" },
+	{ "HPOUT1R", NULL, "OUT1R" },
+
+	{ "HPOUT2L", NULL, "OUT2L" },
+	{ "HPOUT2R", NULL, "OUT2R" },
+
+	{ "EPOUTN", NULL, "OUT3L" },
+	{ "EPOUTP", NULL, "OUT3L" },
+
+	{ "SPKOUTLN", NULL, "OUT4L" },
+	{ "SPKOUTLP", NULL, "OUT4L" },
+
+	{ "SPKOUTRN", NULL, "OUT4R" },
+	{ "SPKOUTRP", NULL, "OUT4R" },
+
+	{ "SPKDAT1L", NULL, "OUT5L" },
+	{ "SPKDAT1R", NULL, "OUT5R" },
+
+	{ "SPKDAT2L", NULL, "OUT6L" },
+	{ "SPKDAT2R", NULL, "OUT6R" },
+};
+
+static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+			  unsigned int Fref, unsigned int Fout)
+{
+	struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec);
+
+	switch (fll_id) {
+	case WM5110_FLL1:
+		return arizona_set_fll(&wm5110->fll[0], source, Fref, Fout);
+	case WM5110_FLL2:
+		return arizona_set_fll(&wm5110->fll[1], source, Fref, Fout);
+	default:
+		return -EINVAL;
+	}
+}
+
+#define WM5110_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM5110_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_driver wm5110_dai[] = {
+	{
+		.name = "wm5110-aif1",
+		.id = 1,
+		.base = ARIZONA_AIF1_BCLK_CTRL,
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = WM5110_RATES,
+			.formats = WM5110_FORMATS,
+		},
+		.capture = {
+			 .stream_name = "AIF1 Capture",
+			 .channels_min = 1,
+			 .channels_max = 8,
+			 .rates = WM5110_RATES,
+			 .formats = WM5110_FORMATS,
+		 },
+		.ops = &arizona_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "wm5110-aif2",
+		.id = 2,
+		.base = ARIZONA_AIF2_BCLK_CTRL,
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = WM5110_RATES,
+			.formats = WM5110_FORMATS,
+		},
+		.capture = {
+			 .stream_name = "AIF2 Capture",
+			 .channels_min = 1,
+			 .channels_max = 2,
+			 .rates = WM5110_RATES,
+			 .formats = WM5110_FORMATS,
+		 },
+		.ops = &arizona_dai_ops,
+		.symmetric_rates = 1,
+	},
+	{
+		.name = "wm5110-aif3",
+		.id = 3,
+		.base = ARIZONA_AIF3_BCLK_CTRL,
+		.playback = {
+			.stream_name = "AIF3 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = WM5110_RATES,
+			.formats = WM5110_FORMATS,
+		},
+		.capture = {
+			 .stream_name = "AIF3 Capture",
+			 .channels_min = 1,
+			 .channels_max = 2,
+			 .rates = WM5110_RATES,
+			 .formats = WM5110_FORMATS,
+		 },
+		.ops = &arizona_dai_ops,
+		.symmetric_rates = 1,
+	},
+};
+
+static int wm5110_codec_probe(struct snd_soc_codec *codec)
+{
+	struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	codec->control_data = priv->core.arizona->regmap;
+	return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+}
+
+#define WM5110_DIG_VU 0x0200
+
+static unsigned int wm5110_digital_vu[] = {
+	ARIZONA_ADC_DIGITAL_VOLUME_1L,
+	ARIZONA_ADC_DIGITAL_VOLUME_1R,
+	ARIZONA_ADC_DIGITAL_VOLUME_2L,
+	ARIZONA_ADC_DIGITAL_VOLUME_2R,
+	ARIZONA_ADC_DIGITAL_VOLUME_3L,
+	ARIZONA_ADC_DIGITAL_VOLUME_3R,
+
+	ARIZONA_DAC_DIGITAL_VOLUME_1L,
+	ARIZONA_DAC_DIGITAL_VOLUME_1R,
+	ARIZONA_DAC_DIGITAL_VOLUME_2L,
+	ARIZONA_DAC_DIGITAL_VOLUME_2R,
+	ARIZONA_DAC_DIGITAL_VOLUME_3L,
+	ARIZONA_DAC_DIGITAL_VOLUME_3R,
+	ARIZONA_DAC_DIGITAL_VOLUME_4L,
+	ARIZONA_DAC_DIGITAL_VOLUME_4R,
+	ARIZONA_DAC_DIGITAL_VOLUME_5L,
+	ARIZONA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
+	.probe = wm5110_codec_probe,
+
+	.idle_bias_off = true,
+
+	.set_sysclk = arizona_set_sysclk,
+	.set_pll = wm5110_set_fll,
+
+	.controls = wm5110_snd_controls,
+	.num_controls = ARRAY_SIZE(wm5110_snd_controls),
+	.dapm_widgets = wm5110_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(wm5110_dapm_widgets),
+	.dapm_routes = wm5110_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(wm5110_dapm_routes),
+};
+
+static int __devinit wm5110_probe(struct platform_device *pdev)
+{
+	struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+	struct wm5110_priv *wm5110;
+	int i;
+
+	wm5110 = devm_kzalloc(&pdev->dev, sizeof(struct wm5110_priv),
+			      GFP_KERNEL);
+	if (wm5110 == NULL)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, wm5110);
+
+	wm5110->core.arizona = arizona;
+
+	for (i = 0; i < ARRAY_SIZE(wm5110->fll); i++)
+		wm5110->fll[i].vco_mult = 3;
+
+	arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+			 ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+			 &wm5110->fll[0]);
+	arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+			 ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+			 &wm5110->fll[1]);
+
+	for (i = 0; i < ARRAY_SIZE(wm5110_dai); i++)
+		arizona_init_dai(&wm5110->core, i);
+
+	/* Latch volume update bits */
+	for (i = 0; i < ARRAY_SIZE(wm5110_digital_vu); i++)
+		regmap_update_bits(arizona->regmap, wm5110_digital_vu[i],
+				   WM5110_DIG_VU, WM5110_DIG_VU);
+
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_idle(&pdev->dev);
+
+	return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
+				      wm5110_dai, ARRAY_SIZE(wm5110_dai));
+}
+
+static int __devexit wm5110_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_codec(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver wm5110_codec_driver = {
+	.driver = {
+		.name = "wm5110-codec",
+		.owner = THIS_MODULE,
+	},
+	.probe = wm5110_probe,
+	.remove = __devexit_p(wm5110_remove),
+};
+
+module_platform_driver(wm5110_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM5110 driver");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm5110-codec");

+ 21 - 0
sound/soc/codecs/wm5110.h

@@ -0,0 +1,21 @@
+/*
+ * wm5110.h  --  WM5110 ALSA SoC Audio driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _WM5110_H
+#define _WM5110_H
+
+#include "arizona.h"
+
+#define WM5110_FLL1 1
+#define WM5110_FLL2 2
+
+#endif

+ 4 - 18
sound/soc/codecs/wm8350.c

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8350.c -- WM8350 ALSA SoC audio driver
  * wm8350.c -- WM8350 ALSA SoC audio driver
  *
  *
- * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC.
+ * Copyright (C) 2007-12 Wolfson Microelectronics PLC.
  *
  *
  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
  *
  *
@@ -71,20 +71,6 @@ struct wm8350_data {
 	int fll_freq_in;
 	int fll_freq_in;
 };
 };
 
 
-static unsigned int wm8350_codec_read(struct snd_soc_codec *codec,
-				      unsigned int reg)
-{
-	struct wm8350 *wm8350 = codec->control_data;
-	return wm8350_reg_read(wm8350, reg);
-}
-
-static int wm8350_codec_write(struct snd_soc_codec *codec, unsigned int reg,
-			      unsigned int value)
-{
-	struct wm8350 *wm8350 = codec->control_data;
-	return wm8350_reg_write(wm8350, reg, value);
-}
-
 /*
 /*
  * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown.
  * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown.
  */
  */
@@ -1519,7 +1505,9 @@ static  int wm8350_codec_probe(struct snd_soc_codec *codec)
 	if (ret != 0)
 	if (ret != 0)
 		return ret;
 		return ret;
 
 
-	codec->control_data = wm8350;
+	codec->control_data = wm8350->regmap;
+
+	snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP);
 
 
 	/* Put the codec into reset if it wasn't already */
 	/* Put the codec into reset if it wasn't already */
 	wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
 	wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
@@ -1629,8 +1617,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8350 = {
 	.remove =	wm8350_codec_remove,
 	.remove =	wm8350_codec_remove,
 	.suspend = 	wm8350_suspend,
 	.suspend = 	wm8350_suspend,
 	.resume =	wm8350_resume,
 	.resume =	wm8350_resume,
-	.read = wm8350_codec_read,
-	.write = wm8350_codec_write,
 	.set_bias_level = wm8350_set_bias_level,
 	.set_bias_level = wm8350_set_bias_level,
 
 
 	.controls = wm8350_snd_controls,
 	.controls = wm8350_snd_controls,

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

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8400.c  --  WM8400 ALSA Soc Audio driver
  * wm8400.c  --  WM8400 ALSA Soc Audio driver
  *
  *
- * Copyright 2008, 2009 Wolfson Microelectronics PLC.
+ * Copyright 2008-11 Wolfson Microelectronics PLC.
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  This program is free software; you can redistribute  it and/or modify it

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

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8580.c  --  WM8580 ALSA Soc Audio driver
  * wm8580.c  --  WM8580 ALSA Soc Audio driver
  *
  *
- * Copyright 2008, 2009 Wolfson Microelectronics PLC.
+ * Copyright 2008-11 Wolfson Microelectronics PLC.
  *
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  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
  *  under  the terms of  the GNU General  Public License as published by the

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

@@ -2,6 +2,7 @@
  * wm8731.c  --  WM8731 ALSA SoC Audio driver
  * wm8731.c  --  WM8731 ALSA SoC Audio driver
  *
  *
  * Copyright 2005 Openedhand Ltd.
  * Copyright 2005 Openedhand Ltd.
+ * Copyright 2006-12 Wolfson Microelectronics, plc
  *
  *
  * Author: Richard Purdie <richard@openedhand.com>
  * Author: Richard Purdie <richard@openedhand.com>
  *
  *

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

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8741.c  --  WM8741 ALSA SoC Audio driver
  * wm8741.c  --  WM8741 ALSA SoC Audio driver
  *
  *
- * Copyright 2010 Wolfson Microelectronics plc
+ * Copyright 2010-1 Wolfson Microelectronics plc
  *
  *
  * Author: Ian Lartey <ian@opensource.wolfsonmicro.com>
  * Author: Ian Lartey <ian@opensource.wolfsonmicro.com>
  *
  *

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

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8753.c  --  WM8753 ALSA Soc Audio driver
  * wm8753.c  --  WM8753 ALSA Soc Audio driver
  *
  *
- * Copyright 2003 Wolfson Microelectronics PLC.
+ * Copyright 2003-11 Wolfson Microelectronics PLC.
  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
  *
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  This program is free software; you can redistribute  it and/or modify it

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

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8776.c  --  WM8776 ALSA SoC Audio driver
  * wm8776.c  --  WM8776 ALSA SoC Audio driver
  *
  *
- * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2009-12 Wolfson Microelectronics plc
  *
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *

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

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8804.c  --  WM8804 S/PDIF transceiver driver
  * wm8804.c  --  WM8804 S/PDIF transceiver driver
  *
  *
- * Copyright 2010 Wolfson Microelectronics plc
+ * Copyright 2010-11 Wolfson Microelectronics plc
  *
  *
  * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
  * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
  *
  *

+ 162 - 154
sound/soc/codecs/wm8903.c

@@ -1,8 +1,8 @@
 /*
 /*
  * wm8903.c  --  WM8903 ALSA SoC Audio driver
  * wm8903.c  --  WM8903 ALSA SoC Audio driver
  *
  *
- * Copyright 2008 Wolfson Microelectronics
- * Copyright 2011 NVIDIA, Inc.
+ * Copyright 2008-12 Wolfson Microelectronics
+ * Copyright 2011-2012 NVIDIA, Inc.
  *
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *
@@ -116,6 +116,7 @@ static const struct reg_default wm8903_reg_defaults[] = {
 
 
 struct wm8903_priv {
 struct wm8903_priv {
 	struct wm8903_platform_data *pdata;
 	struct wm8903_platform_data *pdata;
+	struct device *dev;
 	struct snd_soc_codec *codec;
 	struct snd_soc_codec *codec;
 	struct regmap *regmap;
 	struct regmap *regmap;
 
 
@@ -1635,17 +1636,27 @@ EXPORT_SYMBOL_GPL(wm8903_mic_detect);
 
 
 static irqreturn_t wm8903_irq(int irq, void *data)
 static irqreturn_t wm8903_irq(int irq, void *data)
 {
 {
-	struct snd_soc_codec *codec = data;
-	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
-	int mic_report;
-	int int_pol;
-	int int_val = 0;
-	int mask = ~snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1_MASK);
+	struct wm8903_priv *wm8903 = data;
+	int mic_report, ret;
+	unsigned int int_val, mask, int_pol;
 
 
-	int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask;
+	ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_STATUS_1_MASK,
+			  &mask);
+	if (ret != 0) {
+		dev_err(wm8903->dev, "Failed to read IRQ mask: %d\n", ret);
+		return IRQ_NONE;
+	}
+
+	ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_STATUS_1, &int_val);
+	if (ret != 0) {
+		dev_err(wm8903->dev, "Failed to read IRQ status: %d\n", ret);
+		return IRQ_NONE;
+	}
+
+	int_val &= ~mask;
 
 
 	if (int_val & WM8903_WSEQ_BUSY_EINT) {
 	if (int_val & WM8903_WSEQ_BUSY_EINT) {
-		dev_warn(codec->dev, "Write sequencer done\n");
+		dev_warn(wm8903->dev, "Write sequencer done\n");
 	}
 	}
 
 
 	/*
 	/*
@@ -1656,22 +1667,28 @@ static irqreturn_t wm8903_irq(int irq, void *data)
 	 * the polarity register.
 	 * the polarity register.
 	 */
 	 */
 	mic_report = wm8903->mic_last_report;
 	mic_report = wm8903->mic_last_report;
-	int_pol = snd_soc_read(codec, WM8903_INTERRUPT_POLARITY_1);
+	ret = regmap_read(wm8903->regmap, WM8903_INTERRUPT_POLARITY_1,
+			  &int_pol);
+	if (ret != 0) {
+		dev_err(wm8903->dev, "Failed to read interrupt polarity: %d\n",
+			ret);
+		return IRQ_HANDLED;
+	}
 
 
 #ifndef CONFIG_SND_SOC_WM8903_MODULE
 #ifndef CONFIG_SND_SOC_WM8903_MODULE
 	if (int_val & (WM8903_MICSHRT_EINT | WM8903_MICDET_EINT))
 	if (int_val & (WM8903_MICSHRT_EINT | WM8903_MICDET_EINT))
-		trace_snd_soc_jack_irq(dev_name(codec->dev));
+		trace_snd_soc_jack_irq(dev_name(wm8903->dev));
 #endif
 #endif
 
 
 	if (int_val & WM8903_MICSHRT_EINT) {
 	if (int_val & WM8903_MICSHRT_EINT) {
-		dev_dbg(codec->dev, "Microphone short (pol=%x)\n", int_pol);
+		dev_dbg(wm8903->dev, "Microphone short (pol=%x)\n", int_pol);
 
 
 		mic_report ^= wm8903->mic_short;
 		mic_report ^= wm8903->mic_short;
 		int_pol ^= WM8903_MICSHRT_INV;
 		int_pol ^= WM8903_MICSHRT_INV;
 	}
 	}
 
 
 	if (int_val & WM8903_MICDET_EINT) {
 	if (int_val & WM8903_MICDET_EINT) {
-		dev_dbg(codec->dev, "Microphone detect (pol=%x)\n", int_pol);
+		dev_dbg(wm8903->dev, "Microphone detect (pol=%x)\n", int_pol);
 
 
 		mic_report ^= wm8903->mic_det;
 		mic_report ^= wm8903->mic_det;
 		int_pol ^= WM8903_MICDET_INV;
 		int_pol ^= WM8903_MICDET_INV;
@@ -1679,8 +1696,8 @@ static irqreturn_t wm8903_irq(int irq, void *data)
 		msleep(wm8903->mic_delay);
 		msleep(wm8903->mic_delay);
 	}
 	}
 
 
-	snd_soc_update_bits(codec, WM8903_INTERRUPT_POLARITY_1,
-			    WM8903_MICSHRT_INV | WM8903_MICDET_INV, int_pol);
+	regmap_update_bits(wm8903->regmap, WM8903_INTERRUPT_POLARITY_1,
+			   WM8903_MICSHRT_INV | WM8903_MICDET_INV, int_pol);
 
 
 	snd_soc_jack_report(wm8903->mic_jack, mic_report,
 	snd_soc_jack_report(wm8903->mic_jack, mic_report,
 			    wm8903->mic_short | wm8903->mic_det);
 			    wm8903->mic_short | wm8903->mic_det);
@@ -1774,7 +1791,6 @@ static int wm8903_gpio_request(struct gpio_chip *chip, unsigned offset)
 static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
 static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
 {
 {
 	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
 	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
-	struct snd_soc_codec *codec = wm8903->codec;
 	unsigned int mask, val;
 	unsigned int mask, val;
 	int ret;
 	int ret;
 
 
@@ -1782,8 +1798,8 @@ static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
 	val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) |
 	val = (WM8903_GPn_FN_GPIO_INPUT << WM8903_GP1_FN_SHIFT) |
 		WM8903_GP1_DIR;
 		WM8903_GP1_DIR;
 
 
-	ret = snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
-				  mask, val);
+	ret = regmap_update_bits(wm8903->regmap,
+				 WM8903_GPIO_CONTROL_1 + offset, mask, val);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -1793,10 +1809,9 @@ static int wm8903_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
 static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
 static int wm8903_gpio_get(struct gpio_chip *chip, unsigned offset)
 {
 {
 	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
 	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
-	struct snd_soc_codec *codec = wm8903->codec;
-	int reg;
+	unsigned int reg;
 
 
-	reg = snd_soc_read(codec, WM8903_GPIO_CONTROL_1 + offset);
+	regmap_read(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset, &reg);
 
 
 	return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT;
 	return (reg & WM8903_GP1_LVL_MASK) >> WM8903_GP1_LVL_SHIFT;
 }
 }
@@ -1805,7 +1820,6 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip,
 				     unsigned offset, int value)
 				     unsigned offset, int value)
 {
 {
 	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
 	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
-	struct snd_soc_codec *codec = wm8903->codec;
 	unsigned int mask, val;
 	unsigned int mask, val;
 	int ret;
 	int ret;
 
 
@@ -1813,8 +1827,8 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip,
 	val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) |
 	val = (WM8903_GPn_FN_GPIO_OUTPUT << WM8903_GP1_FN_SHIFT) |
 		(value << WM8903_GP2_LVL_SHIFT);
 		(value << WM8903_GP2_LVL_SHIFT);
 
 
-	ret = snd_soc_update_bits(codec, WM8903_GPIO_CONTROL_1 + offset,
-				  mask, val);
+	ret = regmap_update_bits(wm8903->regmap,
+				 WM8903_GPIO_CONTROL_1 + offset, mask, val);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
@@ -1824,11 +1838,10 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip,
 static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
 {
 {
 	struct wm8903_priv *wm8903 = gpio_to_wm8903(chip);
 	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);
+	regmap_update_bits(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset,
+			   WM8903_GP1_LVL_MASK,
+			   !!value << WM8903_GP1_LVL_SHIFT);
 }
 }
 
 
 static struct gpio_chip wm8903_template_chip = {
 static struct gpio_chip wm8903_template_chip = {
@@ -1842,15 +1855,14 @@ static struct gpio_chip wm8903_template_chip = {
 	.can_sleep		= 1,
 	.can_sleep		= 1,
 };
 };
 
 
-static void wm8903_init_gpio(struct snd_soc_codec *codec)
+static void wm8903_init_gpio(struct wm8903_priv *wm8903)
 {
 {
-	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
 	struct wm8903_platform_data *pdata = wm8903->pdata;
 	struct wm8903_platform_data *pdata = wm8903->pdata;
 	int ret;
 	int ret;
 
 
 	wm8903->gpio_chip = wm8903_template_chip;
 	wm8903->gpio_chip = wm8903_template_chip;
 	wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO;
 	wm8903->gpio_chip.ngpio = WM8903_NUM_GPIO;
-	wm8903->gpio_chip.dev = codec->dev;
+	wm8903->gpio_chip.dev = wm8903->dev;
 
 
 	if (pdata->gpio_base)
 	if (pdata->gpio_base)
 		wm8903->gpio_chip.base = pdata->gpio_base;
 		wm8903->gpio_chip.base = pdata->gpio_base;
@@ -1859,24 +1871,23 @@ static void wm8903_init_gpio(struct snd_soc_codec *codec)
 
 
 	ret = gpiochip_add(&wm8903->gpio_chip);
 	ret = gpiochip_add(&wm8903->gpio_chip);
 	if (ret != 0)
 	if (ret != 0)
-		dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
+		dev_err(wm8903->dev, "Failed to add GPIOs: %d\n", ret);
 }
 }
 
 
-static void wm8903_free_gpio(struct snd_soc_codec *codec)
+static void wm8903_free_gpio(struct wm8903_priv *wm8903)
 {
 {
-	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
 	int ret;
 	int ret;
 
 
 	ret = gpiochip_remove(&wm8903->gpio_chip);
 	ret = gpiochip_remove(&wm8903->gpio_chip);
 	if (ret != 0)
 	if (ret != 0)
-		dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
+		dev_err(wm8903->dev, "Failed to remove GPIOs: %d\n", ret);
 }
 }
 #else
 #else
-static void wm8903_init_gpio(struct snd_soc_codec *codec)
+static void wm8903_init_gpio(struct wm8903_priv *wm8903)
 {
 {
 }
 }
 
 
-static void wm8903_free_gpio(struct snd_soc_codec *codec)
+static void wm8903_free_gpio(struct wm8903_priv *wm8903)
 {
 {
 }
 }
 #endif
 #endif
@@ -1884,11 +1895,7 @@ static void wm8903_free_gpio(struct snd_soc_codec *codec)
 static int wm8903_probe(struct snd_soc_codec *codec)
 static int wm8903_probe(struct snd_soc_codec *codec)
 {
 {
 	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
 	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
-	struct wm8903_platform_data *pdata = wm8903->pdata;
-	int ret, i;
-	int trigger, irq_pol;
-	u16 val;
-	bool mic_gpio = false;
+	int ret;
 
 
 	wm8903->codec = codec;
 	wm8903->codec = codec;
 	codec->control_data = wm8903->regmap;
 	codec->control_data = wm8903->regmap;
@@ -1899,121 +1906,16 @@ static int wm8903_probe(struct snd_soc_codec *codec)
 		return ret;
 		return ret;
 	}
 	}
 
 
-	/* Set up GPIOs, detect if any are MIC detect outputs */
-	for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) {
-		if ((!pdata->gpio_cfg[i]) ||
-		    (pdata->gpio_cfg[i] > WM8903_GPIO_CONFIG_ZERO))
-			continue;
-
-		snd_soc_write(codec, WM8903_GPIO_CONTROL_1 + i,
-				pdata->gpio_cfg[i] & 0x7fff);
-
-		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;
-		}
-	}
-
-	/* Set up microphone detection */
-	snd_soc_write(codec, WM8903_MIC_BIAS_CONTROL_0,
-			pdata->micdet_cfg);
-
-	/* Microphone detection needs the WSEQ clock */
-	if (pdata->micdet_cfg)
-		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;
-
-	if (wm8903->irq) {
-		if (pdata->irq_active_low) {
-			trigger = IRQF_TRIGGER_LOW;
-			irq_pol = WM8903_IRQ_POL;
-		} else {
-			trigger = IRQF_TRIGGER_HIGH;
-			irq_pol = 0;
-		}
-
-		snd_soc_update_bits(codec, WM8903_INTERRUPT_CONTROL,
-				    WM8903_IRQ_POL, irq_pol);
-		
-		ret = request_threaded_irq(wm8903->irq, NULL, wm8903_irq,
-					   trigger | IRQF_ONESHOT,
-					   "wm8903", codec);
-		if (ret != 0) {
-			dev_err(codec->dev, "Failed to request IRQ: %d\n",
-				ret);
-			return ret;
-		}
-
-		/* Enable write sequencer interrupts */
-		snd_soc_update_bits(codec, WM8903_INTERRUPT_STATUS_1_MASK,
-				    WM8903_IM_WSEQ_BUSY_EINT, 0);
-	}
-
 	/* power on device */
 	/* power on device */
 	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 	wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
 
-	/* Latch volume update bits */
-	val = snd_soc_read(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT);
-	val |= WM8903_ADCVU;
-	snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_LEFT, val);
-	snd_soc_write(codec, WM8903_ADC_DIGITAL_VOLUME_RIGHT, val);
-
-	val = snd_soc_read(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT);
-	val |= WM8903_DACVU;
-	snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_LEFT, val);
-	snd_soc_write(codec, WM8903_DAC_DIGITAL_VOLUME_RIGHT, val);
-
-	val = snd_soc_read(codec, WM8903_ANALOGUE_OUT1_LEFT);
-	val |= WM8903_HPOUTVU;
-	snd_soc_write(codec, WM8903_ANALOGUE_OUT1_LEFT, val);
-	snd_soc_write(codec, WM8903_ANALOGUE_OUT1_RIGHT, val);
-
-	val = snd_soc_read(codec, WM8903_ANALOGUE_OUT2_LEFT);
-	val |= WM8903_LINEOUTVU;
-	snd_soc_write(codec, WM8903_ANALOGUE_OUT2_LEFT, val);
-	snd_soc_write(codec, WM8903_ANALOGUE_OUT2_RIGHT, val);
-
-	val = snd_soc_read(codec, WM8903_ANALOGUE_OUT3_LEFT);
-	val |= WM8903_SPKVU;
-	snd_soc_write(codec, WM8903_ANALOGUE_OUT3_LEFT, val);
-	snd_soc_write(codec, WM8903_ANALOGUE_OUT3_RIGHT, val);
-
-	/* Enable DAC soft mute by default */
-	snd_soc_update_bits(codec, WM8903_DAC_DIGITAL_1,
-			    WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE,
-			    WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE);
-
-	wm8903_init_gpio(codec);
-
 	return ret;
 	return ret;
 }
 }
 
 
 /* power down chip */
 /* power down chip */
 static int wm8903_remove(struct snd_soc_codec *codec)
 static int wm8903_remove(struct snd_soc_codec *codec)
 {
 {
-	struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
-
-	wm8903_free_gpio(codec);
 	wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
 	wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	if (wm8903->irq)
-		free_irq(wm8903->irq, codec);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -2123,15 +2025,18 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
 {
 {
 	struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev);
 	struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev);
 	struct wm8903_priv *wm8903;
 	struct wm8903_priv *wm8903;
-	unsigned int val;
-	int ret;
+	int trigger;
+	bool mic_gpio = false;
+	unsigned int val, irq_pol;
+	int ret, i;
 
 
 	wm8903 = devm_kzalloc(&i2c->dev,  sizeof(struct wm8903_priv),
 	wm8903 = devm_kzalloc(&i2c->dev,  sizeof(struct wm8903_priv),
 			      GFP_KERNEL);
 			      GFP_KERNEL);
 	if (wm8903 == NULL)
 	if (wm8903 == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
+	wm8903->dev = &i2c->dev;
 
 
-	wm8903->regmap = regmap_init_i2c(i2c, &wm8903_regmap);
+	wm8903->regmap = devm_regmap_init_i2c(i2c, &wm8903_regmap);
 	if (IS_ERR(wm8903->regmap)) {
 	if (IS_ERR(wm8903->regmap)) {
 		ret = PTR_ERR(wm8903->regmap);
 		ret = PTR_ERR(wm8903->regmap);
 		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
 		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -2140,7 +2045,6 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
 	}
 	}
 
 
 	i2c_set_clientdata(i2c, wm8903);
 	i2c_set_clientdata(i2c, wm8903);
-	wm8903->irq = i2c->irq;
 
 
 	/* If no platform data was supplied, create storage for defaults */
 	/* If no platform data was supplied, create storage for defaults */
 	if (pdata) {
 	if (pdata) {
@@ -2167,6 +2071,8 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
 		}
 		}
 	}
 	}
 
 
+	pdata = wm8903->pdata;
+
 	ret = regmap_read(wm8903->regmap, WM8903_SW_RESET_AND_ID, &val);
 	ret = regmap_read(wm8903->regmap, WM8903_SW_RESET_AND_ID, &val);
 	if (ret != 0) {
 	if (ret != 0) {
 		dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret);
 		dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret);
@@ -2189,6 +2095,107 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
 	/* Reset the device */
 	/* Reset the device */
 	regmap_write(wm8903->regmap, WM8903_SW_RESET_AND_ID, 0x8903);
 	regmap_write(wm8903->regmap, WM8903_SW_RESET_AND_ID, 0x8903);
 
 
+	wm8903_init_gpio(wm8903);
+
+	/* Set up GPIO pin state, detect if any are MIC detect outputs */
+	for (i = 0; i < ARRAY_SIZE(pdata->gpio_cfg); i++) {
+		if ((!pdata->gpio_cfg[i]) ||
+		    (pdata->gpio_cfg[i] > WM8903_GPIO_CONFIG_ZERO))
+			continue;
+
+		regmap_write(wm8903->regmap, WM8903_GPIO_CONTROL_1 + i,
+				pdata->gpio_cfg[i] & 0x7fff);
+
+		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;
+		}
+	}
+
+	/* Set up microphone detection */
+	regmap_write(wm8903->regmap, WM8903_MIC_BIAS_CONTROL_0,
+		     pdata->micdet_cfg);
+
+	/* Microphone detection needs the WSEQ clock */
+	if (pdata->micdet_cfg)
+		regmap_update_bits(wm8903->regmap, 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;
+
+	if (i2c->irq) {
+		if (pdata->irq_active_low) {
+			trigger = IRQF_TRIGGER_LOW;
+			irq_pol = WM8903_IRQ_POL;
+		} else {
+			trigger = IRQF_TRIGGER_HIGH;
+			irq_pol = 0;
+		}
+
+		regmap_update_bits(wm8903->regmap, WM8903_INTERRUPT_CONTROL,
+				   WM8903_IRQ_POL, irq_pol);
+
+		ret = request_threaded_irq(i2c->irq, NULL, wm8903_irq,
+					   trigger | IRQF_ONESHOT,
+					   "wm8903", wm8903);
+		if (ret != 0) {
+			dev_err(wm8903->dev, "Failed to request IRQ: %d\n",
+				ret);
+			return ret;
+		}
+
+		/* Enable write sequencer interrupts */
+		regmap_update_bits(wm8903->regmap,
+				   WM8903_INTERRUPT_STATUS_1_MASK,
+				   WM8903_IM_WSEQ_BUSY_EINT, 0);
+	}
+
+	/* Latch volume update bits */
+	regmap_update_bits(wm8903->regmap, WM8903_ADC_DIGITAL_VOLUME_LEFT,
+			   WM8903_ADCVU, WM8903_ADCVU);
+	regmap_update_bits(wm8903->regmap, WM8903_ADC_DIGITAL_VOLUME_RIGHT,
+			   WM8903_ADCVU, WM8903_ADCVU);
+
+	regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_VOLUME_LEFT,
+			   WM8903_DACVU, WM8903_DACVU);
+	regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_VOLUME_RIGHT,
+			   WM8903_DACVU, WM8903_DACVU);
+
+	regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT1_LEFT,
+			   WM8903_HPOUTVU, WM8903_HPOUTVU);
+	regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT1_RIGHT,
+			   WM8903_HPOUTVU, WM8903_HPOUTVU);
+
+	regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT2_LEFT,
+			   WM8903_LINEOUTVU, WM8903_LINEOUTVU);
+	regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT2_RIGHT,
+			   WM8903_LINEOUTVU, WM8903_LINEOUTVU);
+
+	regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT3_LEFT,
+			   WM8903_SPKVU, WM8903_SPKVU);
+	regmap_update_bits(wm8903->regmap, WM8903_ANALOGUE_OUT3_RIGHT,
+			   WM8903_SPKVU, WM8903_SPKVU);
+
+	/* Enable DAC soft mute by default */
+	regmap_update_bits(wm8903->regmap, WM8903_DAC_DIGITAL_1,
+			   WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE,
+			   WM8903_DAC_MUTEMODE | WM8903_DAC_MUTE);
+
 	ret = snd_soc_register_codec(&i2c->dev,
 	ret = snd_soc_register_codec(&i2c->dev,
 			&soc_codec_dev_wm8903, &wm8903_dai, 1);
 			&soc_codec_dev_wm8903, &wm8903_dai, 1);
 	if (ret != 0)
 	if (ret != 0)
@@ -2196,7 +2203,6 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
 
 
 	return 0;
 	return 0;
 err:
 err:
-	regmap_exit(wm8903->regmap);
 	return ret;
 	return ret;
 }
 }
 
 
@@ -2204,7 +2210,9 @@ static __devexit int wm8903_i2c_remove(struct i2c_client *client)
 {
 {
 	struct wm8903_priv *wm8903 = i2c_get_clientdata(client);
 	struct wm8903_priv *wm8903 = i2c_get_clientdata(client);
 
 
-	regmap_exit(wm8903->regmap);
+	if (client->irq)
+		free_irq(client->irq, wm8903);
+	wm8903_free_gpio(wm8903);
 	snd_soc_unregister_codec(&client->dev);
 	snd_soc_unregister_codec(&client->dev);
 
 
 	return 0;
 	return 0;

+ 109 - 163
sound/soc/codecs/wm8904.c

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8904.c  --  WM8904 ALSA SoC Audio driver
  * wm8904.c  --  WM8904 ALSA SoC Audio driver
  *
  *
- * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2009-12 Wolfson Microelectronics plc
  *
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *
@@ -314,11 +314,6 @@ static bool wm8904_readable_register(struct device *dev, unsigned int reg)
 	}
 	}
 }
 }
 
 
-static int wm8904_reset(struct snd_soc_codec *codec)
-{
-	return snd_soc_write(codec, WM8904_SW_RESET_AND_ID, 0);
-}
-
 static int wm8904_configure_clocking(struct snd_soc_codec *codec)
 static int wm8904_configure_clocking(struct snd_soc_codec *codec)
 {
 {
 	struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
 	struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
@@ -1945,25 +1940,6 @@ static struct snd_soc_dai_driver wm8904_dai = {
 	.symmetric_rates = 1,
 	.symmetric_rates = 1,
 };
 };
 
 
-#ifdef CONFIG_PM
-static int wm8904_suspend(struct snd_soc_codec *codec)
-{
-	wm8904_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-	return 0;
-}
-
-static int wm8904_resume(struct snd_soc_codec *codec)
-{
-	wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	return 0;
-}
-#else
-#define wm8904_suspend NULL
-#define wm8904_resume NULL
-#endif
-
 static void wm8904_handle_retune_mobile_pdata(struct snd_soc_codec *codec)
 static void wm8904_handle_retune_mobile_pdata(struct snd_soc_codec *codec)
 {
 {
 	struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
 	struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
@@ -2078,8 +2054,7 @@ static void wm8904_handle_pdata(struct snd_soc_codec *codec)
 static int wm8904_probe(struct snd_soc_codec *codec)
 static int wm8904_probe(struct snd_soc_codec *codec)
 {
 {
 	struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
 	struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
-	struct wm8904_pdata *pdata = wm8904->pdata;
-	int ret, i;
+	int ret;
 
 
 	codec->control_data = wm8904->regmap;
 	codec->control_data = wm8904->regmap;
 
 
@@ -2101,127 +2076,17 @@ static int wm8904_probe(struct snd_soc_codec *codec)
 		return ret;
 		return ret;
 	}
 	}
 
 
-	for (i = 0; i < ARRAY_SIZE(wm8904->supplies); i++)
-		wm8904->supplies[i].supply = wm8904_supply_names[i];
-
-	ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8904->supplies),
-				 wm8904->supplies);
-	if (ret != 0) {
-		dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
-		return ret;
-	}
-
-	ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies),
-				    wm8904->supplies);
-	if (ret != 0) {
-		dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
-		goto err_get;
-	}
-
-	ret = snd_soc_read(codec, WM8904_SW_RESET_AND_ID);
-	if (ret < 0) {
-		dev_err(codec->dev, "Failed to read ID register\n");
-		goto err_enable;
-	}
-	if (ret != 0x8904) {
-		dev_err(codec->dev, "Device is not a WM8904, ID is %x\n", ret);
-		ret = -EINVAL;
-		goto err_enable;
-	}
-
-	ret = snd_soc_read(codec, WM8904_REVISION);
-	if (ret < 0) {
-		dev_err(codec->dev, "Failed to read device revision: %d\n",
-			ret);
-		goto err_enable;
-	}
-	dev_info(codec->dev, "revision %c\n", ret + 'A');
-
-	ret = wm8904_reset(codec);
-	if (ret < 0) {
-		dev_err(codec->dev, "Failed to issue reset\n");
-		goto err_enable;
-	}
-
-	regcache_cache_only(wm8904->regmap, true);
-	/* Change some default settings - latch VU and enable ZC */
-	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) {
-		for (i = 0; i < WM8904_GPIO_REGS; i++) {
-			if (!pdata->gpio_cfg[i])
-				continue;
-
-			regmap_update_bits(wm8904->regmap,
-					   WM8904_GPIO_CONTROL_1 + i,
-					   0xffff,
-					   pdata->gpio_cfg[i]);
-		}
-
-		/* Zero is the default value for these anyway */
-		for (i = 0; i < WM8904_MIC_REGS; i++)
-			regmap_update_bits(wm8904->regmap,
-					   WM8904_MIC_BIAS_CONTROL_0 + i,
-					   0xffff,
-					   pdata->mic_cfg[i]);
-	}
-
-	/* Set Class W by default - this will be managed by the Class
-	 * G widget at runtime where bypass paths are available.
-	 */
-	snd_soc_update_bits(codec, WM8904_CLASS_W_0,
-			    WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR);
-
-	/* Use normal bias source */
-	snd_soc_update_bits(codec, WM8904_BIAS_CONTROL_0,
-			    WM8904_POBCTRL, 0);
-
-	wm8904_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-	/* Bias level configuration will have done an extra enable */
-	regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
-
 	wm8904_handle_pdata(codec);
 	wm8904_handle_pdata(codec);
 
 
 	wm8904_add_widgets(codec);
 	wm8904_add_widgets(codec);
 
 
 	return 0;
 	return 0;
-
-err_enable:
-	regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
-err_get:
-	regulator_bulk_free(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
-	return ret;
 }
 }
 
 
 static int wm8904_remove(struct snd_soc_codec *codec)
 static int wm8904_remove(struct snd_soc_codec *codec)
 {
 {
 	struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
 	struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
 
 
-	wm8904_set_bias_level(codec, SND_SOC_BIAS_OFF);
-	regulator_bulk_free(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
 	kfree(wm8904->retune_mobile_texts);
 	kfree(wm8904->retune_mobile_texts);
 	kfree(wm8904->drc_texts);
 	kfree(wm8904->drc_texts);
 
 
@@ -2231,8 +2096,6 @@ static int wm8904_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_wm8904 = {
 static struct snd_soc_codec_driver soc_codec_dev_wm8904 = {
 	.probe =	wm8904_probe,
 	.probe =	wm8904_probe,
 	.remove =	wm8904_remove,
 	.remove =	wm8904_remove,
-	.suspend =	wm8904_suspend,
-	.resume =	wm8904_resume,
 	.set_bias_level = wm8904_set_bias_level,
 	.set_bias_level = wm8904_set_bias_level,
 	.idle_bias_off = true,
 	.idle_bias_off = true,
 };
 };
@@ -2254,14 +2117,15 @@ static __devinit int wm8904_i2c_probe(struct i2c_client *i2c,
 				      const struct i2c_device_id *id)
 				      const struct i2c_device_id *id)
 {
 {
 	struct wm8904_priv *wm8904;
 	struct wm8904_priv *wm8904;
-	int ret;
+	unsigned int val;
+	int ret, i;
 
 
 	wm8904 = devm_kzalloc(&i2c->dev, sizeof(struct wm8904_priv),
 	wm8904 = devm_kzalloc(&i2c->dev, sizeof(struct wm8904_priv),
 			      GFP_KERNEL);
 			      GFP_KERNEL);
 	if (wm8904 == NULL)
 	if (wm8904 == NULL)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	wm8904->regmap = regmap_init_i2c(i2c, &wm8904_regmap);
+	wm8904->regmap = devm_regmap_init_i2c(i2c, &wm8904_regmap);
 	if (IS_ERR(wm8904->regmap)) {
 	if (IS_ERR(wm8904->regmap)) {
 		ret = PTR_ERR(wm8904->regmap);
 		ret = PTR_ERR(wm8904->regmap);
 		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
 		dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -2273,23 +2137,121 @@ static __devinit int wm8904_i2c_probe(struct i2c_client *i2c,
 	i2c_set_clientdata(i2c, wm8904);
 	i2c_set_clientdata(i2c, wm8904);
 	wm8904->pdata = i2c->dev.platform_data;
 	wm8904->pdata = i2c->dev.platform_data;
 
 
+	for (i = 0; i < ARRAY_SIZE(wm8904->supplies); i++)
+		wm8904->supplies[i].supply = wm8904_supply_names[i];
+
+	ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8904->supplies),
+				      wm8904->supplies);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_bulk_enable(ARRAY_SIZE(wm8904->supplies),
+				    wm8904->supplies);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+		return ret;
+	}
+
+	ret = regmap_read(wm8904->regmap, WM8904_SW_RESET_AND_ID, &val);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "Failed to read ID register: %d\n", ret);
+		goto err_enable;
+	}
+	if (val != 0x8904) {
+		dev_err(&i2c->dev, "Device is not a WM8904, ID is %x\n", val);
+		ret = -EINVAL;
+		goto err_enable;
+	}
+
+	ret = regmap_read(wm8904->regmap, WM8904_REVISION, &val);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "Failed to read device revision: %d\n",
+			ret);
+		goto err_enable;
+	}
+	dev_info(&i2c->dev, "revision %c\n", val + 'A');
+
+	ret = regmap_write(wm8904->regmap, WM8904_SW_RESET_AND_ID, 0);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+		goto err_enable;
+	}
+
+	/* Change some default settings - latch VU and enable ZC */
+	regmap_update_bits(wm8904->regmap, WM8904_ADC_DIGITAL_VOLUME_LEFT,
+			   WM8904_ADC_VU, WM8904_ADC_VU);
+	regmap_update_bits(wm8904->regmap, WM8904_ADC_DIGITAL_VOLUME_RIGHT,
+			   WM8904_ADC_VU, WM8904_ADC_VU);
+	regmap_update_bits(wm8904->regmap, WM8904_DAC_DIGITAL_VOLUME_LEFT,
+			   WM8904_DAC_VU, WM8904_DAC_VU);
+	regmap_update_bits(wm8904->regmap, WM8904_DAC_DIGITAL_VOLUME_RIGHT,
+			   WM8904_DAC_VU, WM8904_DAC_VU);
+	regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT1_LEFT,
+			   WM8904_HPOUT_VU | WM8904_HPOUTLZC,
+			   WM8904_HPOUT_VU | WM8904_HPOUTLZC);
+	regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT1_RIGHT,
+			   WM8904_HPOUT_VU | WM8904_HPOUTRZC,
+			   WM8904_HPOUT_VU | WM8904_HPOUTRZC);
+	regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT2_LEFT,
+			   WM8904_LINEOUT_VU | WM8904_LINEOUTLZC,
+			   WM8904_LINEOUT_VU | WM8904_LINEOUTLZC);
+	regmap_update_bits(wm8904->regmap, WM8904_ANALOGUE_OUT2_RIGHT,
+			   WM8904_LINEOUT_VU | WM8904_LINEOUTRZC,
+			   WM8904_LINEOUT_VU | WM8904_LINEOUTRZC);
+	regmap_update_bits(wm8904->regmap, WM8904_CLOCK_RATES_0,
+			   WM8904_SR_MODE, 0);
+
+	/* Apply configuration from the platform data. */
+	if (wm8904->pdata) {
+		for (i = 0; i < WM8904_GPIO_REGS; i++) {
+			if (!wm8904->pdata->gpio_cfg[i])
+				continue;
+
+			regmap_update_bits(wm8904->regmap,
+					   WM8904_GPIO_CONTROL_1 + i,
+					   0xffff,
+					   wm8904->pdata->gpio_cfg[i]);
+		}
+
+		/* Zero is the default value for these anyway */
+		for (i = 0; i < WM8904_MIC_REGS; i++)
+			regmap_update_bits(wm8904->regmap,
+					   WM8904_MIC_BIAS_CONTROL_0 + i,
+					   0xffff,
+					   wm8904->pdata->mic_cfg[i]);
+	}
+
+	/* Set Class W by default - this will be managed by the Class
+	 * G widget at runtime where bypass paths are available.
+	 */
+	regmap_update_bits(wm8904->regmap, WM8904_CLASS_W_0,
+			    WM8904_CP_DYN_PWR, WM8904_CP_DYN_PWR);
+
+	/* Use normal bias source */
+	regmap_update_bits(wm8904->regmap, WM8904_BIAS_CONTROL_0,
+			    WM8904_POBCTRL, 0);
+
+	/* Can leave the device powered off until we need it */
+	regcache_cache_only(wm8904->regmap, true);
+	regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
+
 	ret = snd_soc_register_codec(&i2c->dev,
 	ret = snd_soc_register_codec(&i2c->dev,
 			&soc_codec_dev_wm8904, &wm8904_dai, 1);
 			&soc_codec_dev_wm8904, &wm8904_dai, 1);
 	if (ret != 0)
 	if (ret != 0)
-		goto err;
+		return ret;
 
 
 	return 0;
 	return 0;
 
 
-err:
-	regmap_exit(wm8904->regmap);
+err_enable:
+	regulator_bulk_disable(ARRAY_SIZE(wm8904->supplies), wm8904->supplies);
 	return ret;
 	return ret;
 }
 }
 
 
 static __devexit int wm8904_i2c_remove(struct i2c_client *client)
 static __devexit int wm8904_i2c_remove(struct i2c_client *client)
 {
 {
-	struct wm8904_priv *wm8904 = i2c_get_clientdata(client);
 	snd_soc_unregister_codec(&client->dev);
 	snd_soc_unregister_codec(&client->dev);
-	regmap_exit(wm8904->regmap);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -2311,23 +2273,7 @@ static struct i2c_driver wm8904_i2c_driver = {
 	.id_table = wm8904_i2c_id,
 	.id_table = wm8904_i2c_id,
 };
 };
 
 
-static int __init wm8904_modinit(void)
-{
-	int ret = 0;
-	ret = i2c_add_driver(&wm8904_i2c_driver);
-	if (ret != 0) {
-		printk(KERN_ERR "Failed to register wm8904 I2C driver: %d\n",
-		       ret);
-	}
-	return ret;
-}
-module_init(wm8904_modinit);
-
-static void __exit wm8904_exit(void)
-{
-	i2c_del_driver(&wm8904_i2c_driver);
-}
-module_exit(wm8904_exit);
+module_i2c_driver(wm8904_i2c_driver);
 
 
 MODULE_DESCRIPTION("ASoC WM8904 driver");
 MODULE_DESCRIPTION("ASoC WM8904 driver");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");

+ 2 - 0
sound/soc/codecs/wm8960.c

@@ -1,6 +1,8 @@
 /*
 /*
  * wm8960.c  --  WM8960 ALSA SoC Audio driver
  * wm8960.c  --  WM8960 ALSA SoC Audio driver
  *
  *
+ * Copyright 2007-11 Wolfson Microelectronics, plc
+ *
  * Author: Liam Girdwood
  * Author: Liam Girdwood
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify

+ 2 - 0
sound/soc/codecs/wm8961.c

@@ -1,6 +1,8 @@
 /*
 /*
  * wm8961.c  --  WM8961 ALSA SoC Audio driver
  * wm8961.c  --  WM8961 ALSA SoC Audio driver
  *
  *
+ * Copyright 2009-10 Wolfson Microelectronics, plc
+ *
  * Author: Mark Brown
  * Author: Mark Brown
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * This program is free software; you can redistribute it and/or modify

+ 7 - 1
sound/soc/codecs/wm8962.c

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8962.c  --  WM8962 ALSA SoC Audio driver
  * wm8962.c  --  WM8962 ALSA SoC Audio driver
  *
  *
- * Copyright 2010 Wolfson Microelectronics plc
+ * Copyright 2010-2 Wolfson Microelectronics plc
  *
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *
@@ -2580,6 +2580,9 @@ static int wm8962_hw_params(struct snd_pcm_substream *substream,
 			    WM8962_SAMPLE_RATE_INT_MODE |
 			    WM8962_SAMPLE_RATE_INT_MODE |
 			    WM8962_SAMPLE_RATE_MASK, adctl3);
 			    WM8962_SAMPLE_RATE_MASK, adctl3);
 
 
+	dev_dbg(codec->dev, "hw_params set BCLK %dHz LRCLK %dHz\n",
+		wm8962->bclk, wm8962->lrclk);
+
 	if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
 	if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
 		wm8962_configure_bclk(codec);
 		wm8962_configure_bclk(codec);
 
 
@@ -3722,6 +3725,9 @@ static int wm8962_runtime_resume(struct device *dev)
 	}
 	}
 
 
 	regcache_cache_only(wm8962->regmap, false);
 	regcache_cache_only(wm8962->regmap, false);
+
+	wm8962_reset(wm8962);
+
 	regcache_sync(wm8962->regmap);
 	regcache_sync(wm8962->regmap);
 
 
 	regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP,
 	regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP,

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

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8993.c -- WM8993 ALSA SoC audio driver
  * wm8993.c -- WM8993 ALSA SoC audio driver
  *
  *
- * Copyright 2009, 2010 Wolfson Microelectronics plc
+ * Copyright 2009-12 Wolfson Microelectronics plc
  *
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *

+ 5 - 41
sound/soc/codecs/wm8994.c

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8994.c  --  WM8994 ALSA SoC Audio driver
  * wm8994.c  --  WM8994 ALSA SoC Audio driver
  *
  *
- * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2009-12 Wolfson Microelectronics plc
  *
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *
@@ -2967,23 +2967,8 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
 static int wm8994_codec_suspend(struct snd_soc_codec *codec)
 static int wm8994_codec_suspend(struct snd_soc_codec *codec)
 {
 {
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 	struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
-	struct wm8994 *control = wm8994->wm8994;
 	int i, ret;
 	int i, ret;
 
 
-	switch (control->type) {
-	case WM8994:
-		snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, 0);
-		break;
-	case WM1811:
-		snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
-				    WM1811_JACKDET_MODE_MASK, 0);
-		/* Fall through */
-	case WM8958:
-		snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
-				    WM8958_MICD_ENA, 0);
-		break;
-	}
-
 	for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
 	for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) {
 		memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i],
 		memcpy(&wm8994->fll_suspend[i], &wm8994->fll[i],
 		       sizeof(struct wm8994_fll_config));
 		       sizeof(struct wm8994_fll_config));
@@ -3033,28 +3018,6 @@ static int wm8994_codec_resume(struct snd_soc_codec *codec)
 				 i + 1, ret);
 				 i + 1, ret);
 	}
 	}
 
 
-	switch (control->type) {
-	case WM8994:
-		if (wm8994->micdet[0].jack || wm8994->micdet[1].jack)
-			snd_soc_update_bits(codec, WM8994_MICBIAS,
-					    WM8994_MICD_ENA, WM8994_MICD_ENA);
-		break;
-	case WM1811:
-		if (wm8994->jackdet && wm8994->jack_cb) {
-			/* Restart from idle */
-			snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
-					    WM1811_JACKDET_MODE_MASK,
-					    WM1811_JACKDET_MODE_JACK);
-			break;
-		}
-		break;
-	case WM8958:
-		if (wm8994->jack_cb)
-			snd_soc_update_bits(codec, WM8958_MIC_DETECT_1,
-					    WM8958_MICD_ENA, WM8958_MICD_ENA);
-		break;
-	}
-
 	return 0;
 	return 0;
 }
 }
 #else
 #else
@@ -3729,9 +3692,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 
 
 	if (wm8994->pdata && wm8994->pdata->micdet_irq)
 	if (wm8994->pdata && wm8994->pdata->micdet_irq)
 		wm8994->micdet_irq = 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_enable(codec->dev);
 	pm_runtime_idle(codec->dev);
 	pm_runtime_idle(codec->dev);
@@ -3870,6 +3830,10 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 				dev_warn(codec->dev,
 				dev_warn(codec->dev,
 					 "Failed to request Mic detect IRQ: %d\n",
 					 "Failed to request Mic detect IRQ: %d\n",
 					 ret);
 					 ret);
+		} else {
+			wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_MIC1_DET,
+					   wm8958_mic_irq, "Mic detect",
+					   wm8994);
 		}
 		}
 	}
 	}
 
 

+ 215 - 372
sound/soc/codecs/wm8996.c

@@ -1,7 +1,7 @@
 /*
 /*
  * wm8996.c - WM8996 audio codec interface
  * wm8996.c - WM8996 audio codec interface
  *
  *
- * Copyright 2011 Wolfson Microelectronics PLC.
+ * Copyright 2011-2 Wolfson Microelectronics PLC.
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  This program is free software; you can redistribute  it and/or modify it
@@ -296,184 +296,6 @@ static struct reg_default wm8996_reg[] = {
 	{ WM8996_RIGHT_PDM_SPEAKER, 0x1 },
 	{ WM8996_RIGHT_PDM_SPEAKER, 0x1 },
 	{ WM8996_PDM_SPEAKER_MUTE_SEQUENCE, 0x69 },
 	{ WM8996_PDM_SPEAKER_MUTE_SEQUENCE, 0x69 },
 	{ WM8996_PDM_SPEAKER_VOLUME, 0x66 },
 	{ WM8996_PDM_SPEAKER_VOLUME, 0x66 },
-	{ WM8996_WRITE_SEQUENCER_0, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_1, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_3, 0x6 },
-	{ WM8996_WRITE_SEQUENCER_4, 0x40 },
-	{ WM8996_WRITE_SEQUENCER_5, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_6, 0xf },
-	{ WM8996_WRITE_SEQUENCER_7, 0x6 },
-	{ WM8996_WRITE_SEQUENCER_8, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_9, 0x3 },
-	{ WM8996_WRITE_SEQUENCER_10, 0x104 },
-	{ WM8996_WRITE_SEQUENCER_12, 0x60 },
-	{ WM8996_WRITE_SEQUENCER_13, 0x11 },
-	{ WM8996_WRITE_SEQUENCER_14, 0x401 },
-	{ WM8996_WRITE_SEQUENCER_16, 0x50 },
-	{ WM8996_WRITE_SEQUENCER_17, 0x3 },
-	{ WM8996_WRITE_SEQUENCER_18, 0x100 },
-	{ WM8996_WRITE_SEQUENCER_20, 0x51 },
-	{ WM8996_WRITE_SEQUENCER_21, 0x3 },
-	{ WM8996_WRITE_SEQUENCER_22, 0x104 },
-	{ WM8996_WRITE_SEQUENCER_23, 0xa },
-	{ WM8996_WRITE_SEQUENCER_24, 0x60 },
-	{ WM8996_WRITE_SEQUENCER_25, 0x3b },
-	{ WM8996_WRITE_SEQUENCER_26, 0x502 },
-	{ WM8996_WRITE_SEQUENCER_27, 0x100 },
-	{ WM8996_WRITE_SEQUENCER_28, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_32, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_36, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_40, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_44, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_48, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_52, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_56, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_60, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_64, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_65, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_67, 0x6 },
-	{ WM8996_WRITE_SEQUENCER_68, 0x40 },
-	{ WM8996_WRITE_SEQUENCER_69, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_70, 0xf },
-	{ WM8996_WRITE_SEQUENCER_71, 0x6 },
-	{ WM8996_WRITE_SEQUENCER_72, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_73, 0x3 },
-	{ WM8996_WRITE_SEQUENCER_74, 0x104 },
-	{ WM8996_WRITE_SEQUENCER_76, 0x60 },
-	{ WM8996_WRITE_SEQUENCER_77, 0x11 },
-	{ WM8996_WRITE_SEQUENCER_78, 0x401 },
-	{ WM8996_WRITE_SEQUENCER_80, 0x50 },
-	{ WM8996_WRITE_SEQUENCER_81, 0x3 },
-	{ WM8996_WRITE_SEQUENCER_82, 0x100 },
-	{ WM8996_WRITE_SEQUENCER_84, 0x60 },
-	{ WM8996_WRITE_SEQUENCER_85, 0x3b },
-	{ WM8996_WRITE_SEQUENCER_86, 0x502 },
-	{ WM8996_WRITE_SEQUENCER_87, 0x100 },
-	{ WM8996_WRITE_SEQUENCER_88, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_92, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_96, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_100, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_104, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_108, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_112, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_116, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_120, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_124, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_128, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_129, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_131, 0x6 },
-	{ WM8996_WRITE_SEQUENCER_132, 0x40 },
-	{ WM8996_WRITE_SEQUENCER_133, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_134, 0xf },
-	{ WM8996_WRITE_SEQUENCER_135, 0x6 },
-	{ WM8996_WRITE_SEQUENCER_136, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_137, 0x3 },
-	{ WM8996_WRITE_SEQUENCER_138, 0x106 },
-	{ WM8996_WRITE_SEQUENCER_140, 0x61 },
-	{ WM8996_WRITE_SEQUENCER_141, 0x11 },
-	{ WM8996_WRITE_SEQUENCER_142, 0x401 },
-	{ WM8996_WRITE_SEQUENCER_144, 0x50 },
-	{ WM8996_WRITE_SEQUENCER_145, 0x3 },
-	{ WM8996_WRITE_SEQUENCER_146, 0x102 },
-	{ WM8996_WRITE_SEQUENCER_148, 0x51 },
-	{ WM8996_WRITE_SEQUENCER_149, 0x3 },
-	{ WM8996_WRITE_SEQUENCER_150, 0x106 },
-	{ WM8996_WRITE_SEQUENCER_151, 0xa },
-	{ WM8996_WRITE_SEQUENCER_152, 0x61 },
-	{ WM8996_WRITE_SEQUENCER_153, 0x3b },
-	{ WM8996_WRITE_SEQUENCER_154, 0x502 },
-	{ WM8996_WRITE_SEQUENCER_155, 0x100 },
-	{ WM8996_WRITE_SEQUENCER_156, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_160, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_164, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_168, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_172, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_176, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_180, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_184, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_188, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_192, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_193, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_195, 0x6 },
-	{ WM8996_WRITE_SEQUENCER_196, 0x40 },
-	{ WM8996_WRITE_SEQUENCER_197, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_198, 0xf },
-	{ WM8996_WRITE_SEQUENCER_199, 0x6 },
-	{ WM8996_WRITE_SEQUENCER_200, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_201, 0x3 },
-	{ WM8996_WRITE_SEQUENCER_202, 0x106 },
-	{ WM8996_WRITE_SEQUENCER_204, 0x61 },
-	{ WM8996_WRITE_SEQUENCER_205, 0x11 },
-	{ WM8996_WRITE_SEQUENCER_206, 0x401 },
-	{ WM8996_WRITE_SEQUENCER_208, 0x50 },
-	{ WM8996_WRITE_SEQUENCER_209, 0x3 },
-	{ WM8996_WRITE_SEQUENCER_210, 0x102 },
-	{ WM8996_WRITE_SEQUENCER_212, 0x61 },
-	{ WM8996_WRITE_SEQUENCER_213, 0x3b },
-	{ WM8996_WRITE_SEQUENCER_214, 0x502 },
-	{ WM8996_WRITE_SEQUENCER_215, 0x100 },
-	{ WM8996_WRITE_SEQUENCER_216, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_220, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_224, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_228, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_232, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_236, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_240, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_244, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_248, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_252, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_256, 0x60 },
-	{ WM8996_WRITE_SEQUENCER_258, 0x601 },
-	{ WM8996_WRITE_SEQUENCER_260, 0x50 },
-	{ WM8996_WRITE_SEQUENCER_262, 0x100 },
-	{ WM8996_WRITE_SEQUENCER_264, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_266, 0x104 },
-	{ WM8996_WRITE_SEQUENCER_267, 0x100 },
-	{ WM8996_WRITE_SEQUENCER_268, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_272, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_276, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_280, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_284, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_288, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_292, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_296, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_300, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_304, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_308, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_312, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_316, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_320, 0x61 },
-	{ WM8996_WRITE_SEQUENCER_322, 0x601 },
-	{ WM8996_WRITE_SEQUENCER_324, 0x50 },
-	{ WM8996_WRITE_SEQUENCER_326, 0x102 },
-	{ WM8996_WRITE_SEQUENCER_328, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_330, 0x106 },
-	{ WM8996_WRITE_SEQUENCER_331, 0x100 },
-	{ WM8996_WRITE_SEQUENCER_332, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_336, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_340, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_344, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_348, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_352, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_356, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_360, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_364, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_368, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_372, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_376, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_380, 0x2fff },
-	{ WM8996_WRITE_SEQUENCER_384, 0x60 },
-	{ WM8996_WRITE_SEQUENCER_386, 0x601 },
-	{ WM8996_WRITE_SEQUENCER_388, 0x61 },
-	{ WM8996_WRITE_SEQUENCER_390, 0x601 },
-	{ WM8996_WRITE_SEQUENCER_392, 0x50 },
-	{ WM8996_WRITE_SEQUENCER_394, 0x300 },
-	{ WM8996_WRITE_SEQUENCER_396, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_398, 0x304 },
-	{ WM8996_WRITE_SEQUENCER_400, 0x40 },
-	{ WM8996_WRITE_SEQUENCER_402, 0xf },
-	{ WM8996_WRITE_SEQUENCER_404, 0x1 },
-	{ WM8996_WRITE_SEQUENCER_407, 0x100 },
 };
 };
 
 
 static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0);
 static const DECLARE_TLV_DB_SCALE(inpga_tlv, 0, 100, 0);
@@ -1706,18 +1528,6 @@ static bool wm8996_volatile_register(struct device *dev, unsigned int reg)
 	}
 	}
 }
 }
 
 
-static int wm8996_reset(struct wm8996_priv *wm8996)
-{
-	if (wm8996->pdata.ldo_ena > 0) {
-		gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
-		gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 1);
-		return 0;
-	} else {
-		return regmap_write(wm8996->regmap, WM8996_SOFTWARE_RESET,
-				    0x8915);
-	}
-}
-
 static const int bclk_divs[] = {
 static const int bclk_divs[] = {
 	1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
 	1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96
 };
 };
@@ -1809,8 +1619,10 @@ static int wm8996_set_bias_level(struct snd_soc_codec *codec,
 
 
 	case SND_SOC_BIAS_OFF:
 	case SND_SOC_BIAS_OFF:
 		regcache_cache_only(codec->control_data, true);
 		regcache_cache_only(codec->control_data, true);
-		if (wm8996->pdata.ldo_ena >= 0)
+		if (wm8996->pdata.ldo_ena >= 0) {
 			gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
 			gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+			regcache_cache_only(codec->control_data, true);
+		}
 		regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies),
 		regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies),
 				       wm8996->supplies);
 				       wm8996->supplies);
 		break;
 		break;
@@ -2807,7 +2619,7 @@ static int wm8996_probe(struct snd_soc_codec *codec)
 	int ret;
 	int ret;
 	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
 	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
 	struct i2c_client *i2c = to_i2c_client(codec->dev);
 	struct i2c_client *i2c = to_i2c_client(codec->dev);
-	int i, irq_flags;
+	int irq_flags;
 
 
 	wm8996->codec = codec;
 	wm8996->codec = codec;
 
 
@@ -2822,177 +2634,12 @@ static int wm8996_probe(struct snd_soc_codec *codec)
 		goto err;
 		goto err;
 	}
 	}
 
 
-	wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0;
-	wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1;
-	wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2;
-
-	/* This should really be moved into the regulator core */
-	for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) {
-		ret = regulator_register_notifier(wm8996->supplies[i].consumer,
-						  &wm8996->disable_nb[i]);
-		if (ret != 0) {
-			dev_err(codec->dev,
-				"Failed to register regulator notifier: %d\n",
-				ret);
-		}
-	}
-
-	/* Apply platform data settings */
-	snd_soc_update_bits(codec, WM8996_LINE_INPUT_CONTROL,
-			    WM8996_INL_MODE_MASK | WM8996_INR_MODE_MASK,
-			    wm8996->pdata.inl_mode << WM8996_INL_MODE_SHIFT |
-			    wm8996->pdata.inr_mode);
-
-	for (i = 0; i < ARRAY_SIZE(wm8996->pdata.gpio_default); i++) {
-		if (!wm8996->pdata.gpio_default[i])
-			continue;
-
-		snd_soc_write(codec, WM8996_GPIO_1 + i,
-			      wm8996->pdata.gpio_default[i] & 0xffff);
-	}
-
-	if (wm8996->pdata.spkmute_seq)
-		snd_soc_update_bits(codec, WM8996_PDM_SPEAKER_MUTE_SEQUENCE,
-				    WM8996_SPK_MUTE_ENDIAN |
-				    WM8996_SPK_MUTE_SEQ1_MASK,
-				    wm8996->pdata.spkmute_seq);
-
-	snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_2,
-			    WM8996_MICD_BIAS_SRC | WM8996_HPOUT1FB_SRC |
-			    WM8996_MICD_SRC, wm8996->pdata.micdet_def);
-
-	/* Latch volume update bits */
-	snd_soc_update_bits(codec, WM8996_LEFT_LINE_INPUT_VOLUME,
-			    WM8996_IN1_VU, WM8996_IN1_VU);
-	snd_soc_update_bits(codec, WM8996_RIGHT_LINE_INPUT_VOLUME,
-			    WM8996_IN1_VU, WM8996_IN1_VU);
-
-	snd_soc_update_bits(codec, WM8996_DAC1_LEFT_VOLUME,
-			    WM8996_DAC1_VU, WM8996_DAC1_VU);
-	snd_soc_update_bits(codec, WM8996_DAC1_RIGHT_VOLUME,
-			    WM8996_DAC1_VU, WM8996_DAC1_VU);
-	snd_soc_update_bits(codec, WM8996_DAC2_LEFT_VOLUME,
-			    WM8996_DAC2_VU, WM8996_DAC2_VU);
-	snd_soc_update_bits(codec, WM8996_DAC2_RIGHT_VOLUME,
-			    WM8996_DAC2_VU, WM8996_DAC2_VU);
-
-	snd_soc_update_bits(codec, WM8996_OUTPUT1_LEFT_VOLUME,
-			    WM8996_DAC1_VU, WM8996_DAC1_VU);
-	snd_soc_update_bits(codec, WM8996_OUTPUT1_RIGHT_VOLUME,
-			    WM8996_DAC1_VU, WM8996_DAC1_VU);
-	snd_soc_update_bits(codec, WM8996_OUTPUT2_LEFT_VOLUME,
-			    WM8996_DAC2_VU, WM8996_DAC2_VU);
-	snd_soc_update_bits(codec, WM8996_OUTPUT2_RIGHT_VOLUME,
-			    WM8996_DAC2_VU, WM8996_DAC2_VU);
-
-	snd_soc_update_bits(codec, WM8996_DSP1_TX_LEFT_VOLUME,
-			    WM8996_DSP1TX_VU, WM8996_DSP1TX_VU);
-	snd_soc_update_bits(codec, WM8996_DSP1_TX_RIGHT_VOLUME,
-			    WM8996_DSP1TX_VU, WM8996_DSP1TX_VU);
-	snd_soc_update_bits(codec, WM8996_DSP2_TX_LEFT_VOLUME,
-			    WM8996_DSP2TX_VU, WM8996_DSP2TX_VU);
-	snd_soc_update_bits(codec, WM8996_DSP2_TX_RIGHT_VOLUME,
-			    WM8996_DSP2TX_VU, WM8996_DSP2TX_VU);
-
-	snd_soc_update_bits(codec, WM8996_DSP1_RX_LEFT_VOLUME,
-			    WM8996_DSP1RX_VU, WM8996_DSP1RX_VU);
-	snd_soc_update_bits(codec, WM8996_DSP1_RX_RIGHT_VOLUME,
-			    WM8996_DSP1RX_VU, WM8996_DSP1RX_VU);
-	snd_soc_update_bits(codec, WM8996_DSP2_RX_LEFT_VOLUME,
-			    WM8996_DSP2RX_VU, WM8996_DSP2RX_VU);
-	snd_soc_update_bits(codec, WM8996_DSP2_RX_RIGHT_VOLUME,
-			    WM8996_DSP2RX_VU, WM8996_DSP2RX_VU);
-
-	/* No support currently for the underclocked TDM modes and
-	 * pick a default TDM layout with each channel pair working with
-	 * slots 0 and 1. */
-	snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_0_CONFIGURATION,
-			    WM8996_AIF1RX_CHAN0_SLOTS_MASK |
-			    WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF1RX_CHAN0_SLOTS_SHIFT | 0);
-	snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_1_CONFIGURATION,
-			    WM8996_AIF1RX_CHAN1_SLOTS_MASK |
-			    WM8996_AIF1RX_CHAN1_START_SLOT_MASK,
-			    1 << WM8996_AIF1RX_CHAN1_SLOTS_SHIFT | 1);
-	snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_2_CONFIGURATION,
-			    WM8996_AIF1RX_CHAN2_SLOTS_MASK |
-			    WM8996_AIF1RX_CHAN2_START_SLOT_MASK,
-			    1 << WM8996_AIF1RX_CHAN2_SLOTS_SHIFT | 0);
-	snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_3_CONFIGURATION,
-			    WM8996_AIF1RX_CHAN3_SLOTS_MASK |
-			    WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF1RX_CHAN3_SLOTS_SHIFT | 1);
-	snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_4_CONFIGURATION,
-			    WM8996_AIF1RX_CHAN4_SLOTS_MASK |
-			    WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF1RX_CHAN4_SLOTS_SHIFT | 0);
-	snd_soc_update_bits(codec, WM8996_AIF1RX_CHANNEL_5_CONFIGURATION,
-			    WM8996_AIF1RX_CHAN5_SLOTS_MASK |
-			    WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF1RX_CHAN5_SLOTS_SHIFT | 1);
-
-	snd_soc_update_bits(codec, WM8996_AIF2RX_CHANNEL_0_CONFIGURATION,
-			    WM8996_AIF2RX_CHAN0_SLOTS_MASK |
-			    WM8996_AIF2RX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF2RX_CHAN0_SLOTS_SHIFT | 0);
-	snd_soc_update_bits(codec, WM8996_AIF2RX_CHANNEL_1_CONFIGURATION,
-			    WM8996_AIF2RX_CHAN1_SLOTS_MASK |
-			    WM8996_AIF2RX_CHAN1_START_SLOT_MASK,
-			    1 << WM8996_AIF2RX_CHAN1_SLOTS_SHIFT | 1);
-
-	snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_0_CONFIGURATION,
-			    WM8996_AIF1TX_CHAN0_SLOTS_MASK |
-			    WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF1TX_CHAN0_SLOTS_SHIFT | 0);
-	snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_1_CONFIGURATION,
-			    WM8996_AIF1TX_CHAN1_SLOTS_MASK |
-			    WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
-	snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_2_CONFIGURATION,
-			    WM8996_AIF1TX_CHAN2_SLOTS_MASK |
-			    WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF1TX_CHAN2_SLOTS_SHIFT | 0);
-	snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_3_CONFIGURATION,
-			    WM8996_AIF1TX_CHAN3_SLOTS_MASK |
-			    WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF1TX_CHAN3_SLOTS_SHIFT | 1);
-	snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_4_CONFIGURATION,
-			    WM8996_AIF1TX_CHAN4_SLOTS_MASK |
-			    WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF1TX_CHAN4_SLOTS_SHIFT | 0);
-	snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_5_CONFIGURATION,
-			    WM8996_AIF1TX_CHAN5_SLOTS_MASK |
-			    WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF1TX_CHAN5_SLOTS_SHIFT | 1);
-
-	snd_soc_update_bits(codec, WM8996_AIF2TX_CHANNEL_0_CONFIGURATION,
-			    WM8996_AIF2TX_CHAN0_SLOTS_MASK |
-			    WM8996_AIF2TX_CHAN0_START_SLOT_MASK,
-			    1 << WM8996_AIF2TX_CHAN0_SLOTS_SHIFT | 0);
-	snd_soc_update_bits(codec, WM8996_AIF1TX_CHANNEL_1_CONFIGURATION,
-			    WM8996_AIF2TX_CHAN1_SLOTS_MASK |
-			    WM8996_AIF2TX_CHAN1_START_SLOT_MASK,
-			    1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
-
 	if (wm8996->pdata.num_retune_mobile_cfgs)
 	if (wm8996->pdata.num_retune_mobile_cfgs)
 		wm8996_retune_mobile_pdata(codec);
 		wm8996_retune_mobile_pdata(codec);
 	else
 	else
 		snd_soc_add_codec_controls(codec, wm8996_eq_controls,
 		snd_soc_add_codec_controls(codec, wm8996_eq_controls,
 				     ARRAY_SIZE(wm8996_eq_controls));
 				     ARRAY_SIZE(wm8996_eq_controls));
 
 
-	/* If the TX LRCLK pins are not in LRCLK mode configure the
-	 * AIFs to source their clocks from the RX LRCLKs.
-	 */
-	if ((snd_soc_read(codec, WM8996_GPIO_1)))
-		snd_soc_update_bits(codec, WM8996_AIF1_TX_LRCLK_2,
-				    WM8996_AIF1TX_LRCLK_MODE,
-				    WM8996_AIF1TX_LRCLK_MODE);
-
-	if ((snd_soc_read(codec, WM8996_GPIO_2)))
-		snd_soc_update_bits(codec, WM8996_AIF2_TX_LRCLK_2,
-				    WM8996_AIF2TX_LRCLK_MODE,
-				    WM8996_AIF2TX_LRCLK_MODE);
-
 	if (i2c->irq) {
 	if (i2c->irq) {
 		if (wm8996->pdata.irq_flags)
 		if (wm8996->pdata.irq_flags)
 			irq_flags = wm8996->pdata.irq_flags;
 			irq_flags = wm8996->pdata.irq_flags;
@@ -3036,9 +2683,7 @@ err:
 
 
 static int wm8996_remove(struct snd_soc_codec *codec)
 static int wm8996_remove(struct snd_soc_codec *codec)
 {
 {
-	struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
 	struct i2c_client *i2c = to_i2c_client(codec->dev);
 	struct i2c_client *i2c = to_i2c_client(codec->dev);
-	int i;
 
 
 	snd_soc_update_bits(codec, WM8996_INTERRUPT_CONTROL,
 	snd_soc_update_bits(codec, WM8996_INTERRUPT_CONTROL,
 			    WM8996_IM_IRQ, WM8996_IM_IRQ);
 			    WM8996_IM_IRQ, WM8996_IM_IRQ);
@@ -3046,10 +2691,6 @@ static int wm8996_remove(struct snd_soc_codec *codec)
 	if (i2c->irq)
 	if (i2c->irq)
 		free_irq(i2c->irq, codec);
 		free_irq(i2c->irq, codec);
 
 
-	for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++)
-		regulator_unregister_notifier(wm8996->supplies[i].consumer,
-					      &wm8996->disable_nb[i]);
-
 	return 0;
 	return 0;
 }
 }
 
 
@@ -3163,6 +2804,21 @@ static __devinit int wm8996_i2c_probe(struct i2c_client *i2c,
 		goto err_gpio;
 		goto err_gpio;
 	}
 	}
 
 
+	wm8996->disable_nb[0].notifier_call = wm8996_regulator_event_0;
+	wm8996->disable_nb[1].notifier_call = wm8996_regulator_event_1;
+	wm8996->disable_nb[2].notifier_call = wm8996_regulator_event_2;
+
+	/* This should really be moved into the regulator core */
+	for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++) {
+		ret = regulator_register_notifier(wm8996->supplies[i].consumer,
+						  &wm8996->disable_nb[i]);
+		if (ret != 0) {
+			dev_err(&i2c->dev,
+				"Failed to register regulator notifier: %d\n",
+				ret);
+		}
+	}
+
 	ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies),
 	ret = regulator_bulk_enable(ARRAY_SIZE(wm8996->supplies),
 				    wm8996->supplies);
 				    wm8996->supplies);
 	if (ret != 0) {
 	if (ret != 0) {
@@ -3175,7 +2831,7 @@ static __devinit int wm8996_i2c_probe(struct i2c_client *i2c,
 		msleep(5);
 		msleep(5);
 	}
 	}
 
 
-	wm8996->regmap = regmap_init_i2c(i2c, &wm8996_regmap);
+	wm8996->regmap = devm_regmap_init_i2c(i2c, &wm8996_regmap);
 	if (IS_ERR(wm8996->regmap)) {
 	if (IS_ERR(wm8996->regmap)) {
 		ret = PTR_ERR(wm8996->regmap);
 		ret = PTR_ERR(wm8996->regmap);
 		dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
 		dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
@@ -3203,15 +2859,199 @@ static __devinit int wm8996_i2c_probe(struct i2c_client *i2c,
 	dev_info(&i2c->dev, "revision %c\n",
 	dev_info(&i2c->dev, "revision %c\n",
 		 (reg & WM8996_CHIP_REV_MASK) + 'A');
 		 (reg & WM8996_CHIP_REV_MASK) + 'A');
 
 
-	ret = wm8996_reset(wm8996);
-	if (ret < 0) {
-		dev_err(&i2c->dev, "Failed to issue reset\n");
-		goto err_regmap;
+	if (wm8996->pdata.ldo_ena > 0) {
+		gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
+		regcache_cache_only(wm8996->regmap, true);
+	} else {
+		ret = regmap_write(wm8996->regmap, WM8996_SOFTWARE_RESET,
+				   0x8915);
+		if (ret != 0) {
+			dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
+			goto err_regmap;
+		}
 	}
 	}
 
 
-	regcache_cache_only(wm8996->regmap, true);
 	regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
 	regulator_bulk_disable(ARRAY_SIZE(wm8996->supplies), wm8996->supplies);
 
 
+	/* Apply platform data settings */
+	regmap_update_bits(wm8996->regmap, WM8996_LINE_INPUT_CONTROL,
+			   WM8996_INL_MODE_MASK | WM8996_INR_MODE_MASK,
+			   wm8996->pdata.inl_mode << WM8996_INL_MODE_SHIFT |
+			   wm8996->pdata.inr_mode);
+
+	for (i = 0; i < ARRAY_SIZE(wm8996->pdata.gpio_default); i++) {
+		if (!wm8996->pdata.gpio_default[i])
+			continue;
+
+		regmap_write(wm8996->regmap, WM8996_GPIO_1 + i,
+			     wm8996->pdata.gpio_default[i] & 0xffff);
+	}
+
+	if (wm8996->pdata.spkmute_seq)
+		regmap_update_bits(wm8996->regmap,
+				   WM8996_PDM_SPEAKER_MUTE_SEQUENCE,
+				   WM8996_SPK_MUTE_ENDIAN |
+				   WM8996_SPK_MUTE_SEQ1_MASK,
+				   wm8996->pdata.spkmute_seq);
+
+	regmap_update_bits(wm8996->regmap, WM8996_ACCESSORY_DETECT_MODE_2,
+			   WM8996_MICD_BIAS_SRC | WM8996_HPOUT1FB_SRC |
+			   WM8996_MICD_SRC, wm8996->pdata.micdet_def);
+
+	/* Latch volume update bits */
+	regmap_update_bits(wm8996->regmap, WM8996_LEFT_LINE_INPUT_VOLUME,
+			   WM8996_IN1_VU, WM8996_IN1_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_RIGHT_LINE_INPUT_VOLUME,
+			   WM8996_IN1_VU, WM8996_IN1_VU);
+
+	regmap_update_bits(wm8996->regmap, WM8996_DAC1_LEFT_VOLUME,
+			   WM8996_DAC1_VU, WM8996_DAC1_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_DAC1_RIGHT_VOLUME,
+			   WM8996_DAC1_VU, WM8996_DAC1_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_DAC2_LEFT_VOLUME,
+			   WM8996_DAC2_VU, WM8996_DAC2_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_DAC2_RIGHT_VOLUME,
+			   WM8996_DAC2_VU, WM8996_DAC2_VU);
+
+	regmap_update_bits(wm8996->regmap, WM8996_OUTPUT1_LEFT_VOLUME,
+			   WM8996_DAC1_VU, WM8996_DAC1_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_OUTPUT1_RIGHT_VOLUME,
+			   WM8996_DAC1_VU, WM8996_DAC1_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_OUTPUT2_LEFT_VOLUME,
+			   WM8996_DAC2_VU, WM8996_DAC2_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_OUTPUT2_RIGHT_VOLUME,
+			   WM8996_DAC2_VU, WM8996_DAC2_VU);
+
+	regmap_update_bits(wm8996->regmap, WM8996_DSP1_TX_LEFT_VOLUME,
+			   WM8996_DSP1TX_VU, WM8996_DSP1TX_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_DSP1_TX_RIGHT_VOLUME,
+			   WM8996_DSP1TX_VU, WM8996_DSP1TX_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_DSP2_TX_LEFT_VOLUME,
+			   WM8996_DSP2TX_VU, WM8996_DSP2TX_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_DSP2_TX_RIGHT_VOLUME,
+			   WM8996_DSP2TX_VU, WM8996_DSP2TX_VU);
+
+	regmap_update_bits(wm8996->regmap, WM8996_DSP1_RX_LEFT_VOLUME,
+			   WM8996_DSP1RX_VU, WM8996_DSP1RX_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_DSP1_RX_RIGHT_VOLUME,
+			   WM8996_DSP1RX_VU, WM8996_DSP1RX_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_DSP2_RX_LEFT_VOLUME,
+			   WM8996_DSP2RX_VU, WM8996_DSP2RX_VU);
+	regmap_update_bits(wm8996->regmap, WM8996_DSP2_RX_RIGHT_VOLUME,
+			   WM8996_DSP2RX_VU, WM8996_DSP2RX_VU);
+
+	/* No support currently for the underclocked TDM modes and
+	 * pick a default TDM layout with each channel pair working with
+	 * slots 0 and 1. */
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1RX_CHANNEL_0_CONFIGURATION,
+			   WM8996_AIF1RX_CHAN0_SLOTS_MASK |
+			   WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF1RX_CHAN0_SLOTS_SHIFT | 0);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1RX_CHANNEL_1_CONFIGURATION,
+			   WM8996_AIF1RX_CHAN1_SLOTS_MASK |
+			   WM8996_AIF1RX_CHAN1_START_SLOT_MASK,
+			   1 << WM8996_AIF1RX_CHAN1_SLOTS_SHIFT | 1);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1RX_CHANNEL_2_CONFIGURATION,
+			   WM8996_AIF1RX_CHAN2_SLOTS_MASK |
+			   WM8996_AIF1RX_CHAN2_START_SLOT_MASK,
+			   1 << WM8996_AIF1RX_CHAN2_SLOTS_SHIFT | 0);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1RX_CHANNEL_3_CONFIGURATION,
+			   WM8996_AIF1RX_CHAN3_SLOTS_MASK |
+			   WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF1RX_CHAN3_SLOTS_SHIFT | 1);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1RX_CHANNEL_4_CONFIGURATION,
+			   WM8996_AIF1RX_CHAN4_SLOTS_MASK |
+			   WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF1RX_CHAN4_SLOTS_SHIFT | 0);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1RX_CHANNEL_5_CONFIGURATION,
+			   WM8996_AIF1RX_CHAN5_SLOTS_MASK |
+			   WM8996_AIF1RX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF1RX_CHAN5_SLOTS_SHIFT | 1);
+
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF2RX_CHANNEL_0_CONFIGURATION,
+			   WM8996_AIF2RX_CHAN0_SLOTS_MASK |
+			   WM8996_AIF2RX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF2RX_CHAN0_SLOTS_SHIFT | 0);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF2RX_CHANNEL_1_CONFIGURATION,
+			   WM8996_AIF2RX_CHAN1_SLOTS_MASK |
+			   WM8996_AIF2RX_CHAN1_START_SLOT_MASK,
+			   1 << WM8996_AIF2RX_CHAN1_SLOTS_SHIFT | 1);
+
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1TX_CHANNEL_0_CONFIGURATION,
+			   WM8996_AIF1TX_CHAN0_SLOTS_MASK |
+			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF1TX_CHAN0_SLOTS_SHIFT | 0);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1TX_CHANNEL_1_CONFIGURATION,
+			   WM8996_AIF1TX_CHAN1_SLOTS_MASK |
+			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1TX_CHANNEL_2_CONFIGURATION,
+			   WM8996_AIF1TX_CHAN2_SLOTS_MASK |
+			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF1TX_CHAN2_SLOTS_SHIFT | 0);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1TX_CHANNEL_3_CONFIGURATION,
+			   WM8996_AIF1TX_CHAN3_SLOTS_MASK |
+			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF1TX_CHAN3_SLOTS_SHIFT | 1);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1TX_CHANNEL_4_CONFIGURATION,
+			   WM8996_AIF1TX_CHAN4_SLOTS_MASK |
+			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF1TX_CHAN4_SLOTS_SHIFT | 0);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1TX_CHANNEL_5_CONFIGURATION,
+			   WM8996_AIF1TX_CHAN5_SLOTS_MASK |
+			   WM8996_AIF1TX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF1TX_CHAN5_SLOTS_SHIFT | 1);
+
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF2TX_CHANNEL_0_CONFIGURATION,
+			   WM8996_AIF2TX_CHAN0_SLOTS_MASK |
+			   WM8996_AIF2TX_CHAN0_START_SLOT_MASK,
+			   1 << WM8996_AIF2TX_CHAN0_SLOTS_SHIFT | 0);
+	regmap_update_bits(wm8996->regmap,
+			   WM8996_AIF1TX_CHANNEL_1_CONFIGURATION,
+			   WM8996_AIF2TX_CHAN1_SLOTS_MASK |
+			   WM8996_AIF2TX_CHAN1_START_SLOT_MASK,
+			   1 << WM8996_AIF1TX_CHAN1_SLOTS_SHIFT | 1);
+
+	/* If the TX LRCLK pins are not in LRCLK mode configure the
+	 * AIFs to source their clocks from the RX LRCLKs.
+	 */
+	ret = regmap_read(wm8996->regmap, WM8996_GPIO_1, &reg);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to read GPIO1: %d\n", ret);
+		goto err_regmap;
+	}
+
+	if (reg & WM8996_GP1_FN_MASK)
+		regmap_update_bits(wm8996->regmap, WM8996_AIF1_TX_LRCLK_2,
+				   WM8996_AIF1TX_LRCLK_MODE,
+				   WM8996_AIF1TX_LRCLK_MODE);
+
+	ret = regmap_read(wm8996->regmap, WM8996_GPIO_2, &reg);
+	if (ret != 0) {
+		dev_err(&i2c->dev, "Failed to read GPIO2: %d\n", ret);
+		goto err_regmap;
+	}
+
+	if (reg & WM8996_GP2_FN_MASK)
+		regmap_update_bits(wm8996->regmap, WM8996_AIF2_TX_LRCLK_2,
+				   WM8996_AIF2TX_LRCLK_MODE,
+				   WM8996_AIF2TX_LRCLK_MODE);
+
 	wm8996_init_gpio(wm8996);
 	wm8996_init_gpio(wm8996);
 
 
 	ret = snd_soc_register_codec(&i2c->dev,
 	ret = snd_soc_register_codec(&i2c->dev,
@@ -3225,7 +3065,6 @@ static __devinit int wm8996_i2c_probe(struct i2c_client *i2c,
 err_gpiolib:
 err_gpiolib:
 	wm8996_free_gpio(wm8996);
 	wm8996_free_gpio(wm8996);
 err_regmap:
 err_regmap:
-	regmap_exit(wm8996->regmap);
 err_enable:
 err_enable:
 	if (wm8996->pdata.ldo_ena > 0)
 	if (wm8996->pdata.ldo_ena > 0)
 		gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
 		gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
@@ -3241,14 +3080,18 @@ err:
 static __devexit int wm8996_i2c_remove(struct i2c_client *client)
 static __devexit int wm8996_i2c_remove(struct i2c_client *client)
 {
 {
 	struct wm8996_priv *wm8996 = i2c_get_clientdata(client);
 	struct wm8996_priv *wm8996 = i2c_get_clientdata(client);
+	int i;
 
 
 	snd_soc_unregister_codec(&client->dev);
 	snd_soc_unregister_codec(&client->dev);
 	wm8996_free_gpio(wm8996);
 	wm8996_free_gpio(wm8996);
-	regmap_exit(wm8996->regmap);
 	if (wm8996->pdata.ldo_ena > 0) {
 	if (wm8996->pdata.ldo_ena > 0) {
 		gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
 		gpio_set_value_cansleep(wm8996->pdata.ldo_ena, 0);
 		gpio_free(wm8996->pdata.ldo_ena);
 		gpio_free(wm8996->pdata.ldo_ena);
 	}
 	}
+	for (i = 0; i < ARRAY_SIZE(wm8996->supplies); i++)
+		regulator_unregister_notifier(wm8996->supplies[i].consumer,
+					      &wm8996->disable_nb[i]);
+
 	return 0;
 	return 0;
 }
 }
 
 

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

@@ -3,7 +3,7 @@
  *
  *
  * Author: Mark Brown
  * Author: Mark Brown
  *
  *
- * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2009-12 Wolfson Microelectronics plc
  *
  *
  * This program is free software; you can redistribute it and/or modify
  * 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
  * it under the terms of the GNU General Public License version 2 as

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

@@ -1,7 +1,7 @@
 /*
 /*
  * ALSA SoC WM9090 driver
  * ALSA SoC WM9090 driver
  *
  *
- * Copyright 2009, 2010 Wolfson Microelectronics
+ * Copyright 2009-12 Wolfson Microelectronics
  *
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *

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

@@ -1,7 +1,7 @@
 /*
 /*
  * wm9712.c  --  ALSA Soc WM9712 codec support
  * wm9712.c  --  ALSA Soc WM9712 codec support
  *
  *
- * Copyright 2006 Wolfson Microelectronics PLC.
+ * Copyright 2006-12 Wolfson Microelectronics PLC.
  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
  *
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  This program is free software; you can redistribute  it and/or modify it

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

@@ -1,7 +1,7 @@
 /*
 /*
  * wm9713.c  --  ALSA Soc WM9713 codec support
  * wm9713.c  --  ALSA Soc WM9713 codec support
  *
  *
- * Copyright 2006 Wolfson Microelectronics PLC.
+ * Copyright 2006-10 Wolfson Microelectronics PLC.
  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
  * Author: Liam Girdwood <lrg@slimlogic.co.uk>
  *
  *
  *  This program is free software; you can redistribute  it and/or modify it
  *  This program is free software; you can redistribute  it and/or modify it

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

@@ -1,7 +1,7 @@
 /*
 /*
  * wm_hubs.c  --  WM8993/4 common code
  * wm_hubs.c  --  WM8993/4 common code
  *
  *
- * Copyright 2009 Wolfson Microelectronics plc
+ * Copyright 2009-12 Wolfson Microelectronics plc
  *
  *
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
  *
  *

+ 9 - 0
sound/soc/dwc/Kconfig

@@ -0,0 +1,9 @@
+config SND_DESIGNWARE_I2S
+	tristate "Synopsys I2S Device Driver"
+	depends on CLKDEV_LOOKUP
+	help
+	 Say Y or M if you want to add support for I2S driver for
+	 Synopsys desigwnware I2S device. The device supports upto
+	 maximum of 8 channels each for play and record.
+
+

+ 3 - 0
sound/soc/dwc/Makefile

@@ -0,0 +1,3 @@
+# SYNOPSYS Platform Support
+obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_i2s.o
+

+ 455 - 0
sound/soc/dwc/designware_i2s.c

@@ -0,0 +1,455 @@
+/*
+ * ALSA SoC Synopsys I2S Audio Layer
+ *
+ * sound/soc/spear/designware_i2s.c
+ *
+ * Copyright (C) 2010 ST Microelectronics
+ * Rajeev Kumar <rajeev-dlh.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/designware_i2s.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+/* common register for all channel */
+#define IER		0x000
+#define IRER		0x004
+#define ITER		0x008
+#define CER		0x00C
+#define CCR		0x010
+#define RXFFR		0x014
+#define TXFFR		0x018
+
+/* I2STxRxRegisters for all channels */
+#define LRBR_LTHR(x)	(0x40 * x + 0x020)
+#define RRBR_RTHR(x)	(0x40 * x + 0x024)
+#define RER(x)		(0x40 * x + 0x028)
+#define TER(x)		(0x40 * x + 0x02C)
+#define RCR(x)		(0x40 * x + 0x030)
+#define TCR(x)		(0x40 * x + 0x034)
+#define ISR(x)		(0x40 * x + 0x038)
+#define IMR(x)		(0x40 * x + 0x03C)
+#define ROR(x)		(0x40 * x + 0x040)
+#define TOR(x)		(0x40 * x + 0x044)
+#define RFCR(x)		(0x40 * x + 0x048)
+#define TFCR(x)		(0x40 * x + 0x04C)
+#define RFF(x)		(0x40 * x + 0x050)
+#define TFF(x)		(0x40 * x + 0x054)
+
+/* I2SCOMPRegisters */
+#define I2S_COMP_PARAM_2	0x01F0
+#define I2S_COMP_PARAM_1	0x01F4
+#define I2S_COMP_VERSION	0x01F8
+#define I2S_COMP_TYPE		0x01FC
+
+#define MAX_CHANNEL_NUM		8
+#define MIN_CHANNEL_NUM		2
+
+struct dw_i2s_dev {
+	void __iomem *i2s_base;
+	struct clk *clk;
+	int active;
+	unsigned int capability;
+	struct device *dev;
+
+	/* data related to DMA transfers b/w i2s and DMAC */
+	struct i2s_dma_data play_dma_data;
+	struct i2s_dma_data capture_dma_data;
+	struct i2s_clk_config_data config;
+	int (*i2s_clk_cfg)(struct i2s_clk_config_data *config);
+};
+
+static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val)
+{
+	writel(val, io_base + reg);
+}
+
+static inline u32 i2s_read_reg(void __iomem *io_base, int reg)
+{
+	return readl(io_base + reg);
+}
+
+static inline void i2s_disable_channels(struct dw_i2s_dev *dev, u32 stream)
+{
+	u32 i = 0;
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		for (i = 0; i < 4; i++)
+			i2s_write_reg(dev->i2s_base, TER(i), 0);
+	} else {
+		for (i = 0; i < 4; i++)
+			i2s_write_reg(dev->i2s_base, RER(i), 0);
+	}
+}
+
+static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
+{
+	u32 i = 0;
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		for (i = 0; i < 4; i++)
+			i2s_write_reg(dev->i2s_base, TOR(i), 0);
+	} else {
+		for (i = 0; i < 4; i++)
+			i2s_write_reg(dev->i2s_base, ROR(i), 0);
+	}
+}
+
+static void i2s_start(struct dw_i2s_dev *dev,
+		      struct snd_pcm_substream *substream)
+{
+
+	i2s_write_reg(dev->i2s_base, IER, 1);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		i2s_write_reg(dev->i2s_base, ITER, 1);
+	else
+		i2s_write_reg(dev->i2s_base, IRER, 1);
+
+	i2s_write_reg(dev->i2s_base, CER, 1);
+}
+
+static void i2s_stop(struct dw_i2s_dev *dev,
+		struct snd_pcm_substream *substream)
+{
+	u32 i = 0, irq;
+
+	i2s_clear_irqs(dev, substream->stream);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		i2s_write_reg(dev->i2s_base, ITER, 0);
+
+		for (i = 0; i < 4; i++) {
+			irq = i2s_read_reg(dev->i2s_base, IMR(i));
+			i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x30);
+		}
+	} else {
+		i2s_write_reg(dev->i2s_base, IRER, 0);
+
+		for (i = 0; i < 4; i++) {
+			irq = i2s_read_reg(dev->i2s_base, IMR(i));
+			i2s_write_reg(dev->i2s_base, IMR(i), irq | 0x03);
+		}
+	}
+
+	if (!dev->active) {
+		i2s_write_reg(dev->i2s_base, CER, 0);
+		i2s_write_reg(dev->i2s_base, IER, 0);
+	}
+}
+
+static int dw_i2s_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *cpu_dai)
+{
+	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+	struct i2s_dma_data *dma_data = NULL;
+
+	if (!(dev->capability & DWC_I2S_RECORD) &&
+			(substream->stream == SNDRV_PCM_STREAM_CAPTURE))
+		return -EINVAL;
+
+	if (!(dev->capability & DWC_I2S_PLAY) &&
+			(substream->stream == SNDRV_PCM_STREAM_PLAYBACK))
+		return -EINVAL;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dma_data = &dev->play_dma_data;
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		dma_data = &dev->capture_dma_data;
+
+	snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
+
+	return 0;
+}
+
+static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+	struct i2s_clk_config_data *config = &dev->config;
+	u32 ccr, xfer_resolution, ch_reg, irq;
+	int ret;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		config->data_width = 16;
+		ccr = 0x00;
+		xfer_resolution = 0x02;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		config->data_width = 24;
+		ccr = 0x08;
+		xfer_resolution = 0x04;
+		break;
+
+	case SNDRV_PCM_FORMAT_S32_LE:
+		config->data_width = 32;
+		ccr = 0x10;
+		xfer_resolution = 0x05;
+		break;
+
+	default:
+		dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt");
+		return -EINVAL;
+	}
+
+	config->chan_nr = params_channels(params);
+
+	switch (config->chan_nr) {
+	case EIGHT_CHANNEL_SUPPORT:
+		ch_reg = 3;
+	case SIX_CHANNEL_SUPPORT:
+		ch_reg = 2;
+	case FOUR_CHANNEL_SUPPORT:
+		ch_reg = 1;
+	case TWO_CHANNEL_SUPPORT:
+		ch_reg = 0;
+		break;
+	default:
+		dev_err(dev->dev, "channel not supported\n");
+	}
+
+	i2s_disable_channels(dev, substream->stream);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		i2s_write_reg(dev->i2s_base, TCR(ch_reg), xfer_resolution);
+		i2s_write_reg(dev->i2s_base, TFCR(ch_reg), 0x02);
+		irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
+		i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x30);
+		i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
+	} else {
+		i2s_write_reg(dev->i2s_base, RCR(ch_reg), xfer_resolution);
+		i2s_write_reg(dev->i2s_base, RFCR(ch_reg), 0x07);
+		irq = i2s_read_reg(dev->i2s_base, IMR(ch_reg));
+		i2s_write_reg(dev->i2s_base, IMR(ch_reg), irq & ~0x03);
+		i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
+	}
+
+	i2s_write_reg(dev->i2s_base, CCR, ccr);
+
+	config->sample_rate = params_rate(params);
+
+	if (!dev->i2s_clk_cfg)
+		return -EINVAL;
+
+	ret = dev->i2s_clk_cfg(config);
+	if (ret < 0) {
+		dev_err(dev->dev, "runtime audio clk config fail\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void dw_i2s_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int dw_i2s_trigger(struct snd_pcm_substream *substream,
+		int cmd, struct snd_soc_dai *dai)
+{
+	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		dev->active++;
+		i2s_start(dev, substream);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		dev->active--;
+		i2s_stop(dev, substream);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static struct snd_soc_dai_ops dw_i2s_dai_ops = {
+	.startup	= dw_i2s_startup,
+	.shutdown	= dw_i2s_shutdown,
+	.hw_params	= dw_i2s_hw_params,
+	.trigger	= dw_i2s_trigger,
+};
+
+#ifdef CONFIG_PM
+
+static int dw_i2s_suspend(struct snd_soc_dai *dai)
+{
+	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+	clk_disable(dev->clk);
+	return 0;
+}
+
+static int dw_i2s_resume(struct snd_soc_dai *dai)
+{
+	struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+
+	clk_enable(dev->clk);
+	return 0;
+}
+
+#else
+#define dw_i2s_suspend	NULL
+#define dw_i2s_resume	NULL
+#endif
+
+static int dw_i2s_probe(struct platform_device *pdev)
+{
+	const struct i2s_platform_data *pdata = pdev->dev.platform_data;
+	struct dw_i2s_dev *dev;
+	struct resource *res;
+	int ret;
+	unsigned int cap;
+	struct snd_soc_dai_driver *dw_i2s_dai;
+
+	if (!pdata) {
+		dev_err(&pdev->dev, "Invalid platform data\n");
+		return -EINVAL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "no i2s resource defined\n");
+		return -ENODEV;
+	}
+
+	if (!devm_request_mem_region(&pdev->dev, res->start,
+				resource_size(res), pdev->name)) {
+		dev_err(&pdev->dev, "i2s region already claimed\n");
+		return -EBUSY;
+	}
+
+	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		dev_warn(&pdev->dev, "kzalloc fail\n");
+		return -ENOMEM;
+	}
+
+	dev->i2s_base = devm_ioremap(&pdev->dev, res->start,
+			resource_size(res));
+	if (!dev->i2s_base) {
+		dev_err(&pdev->dev, "ioremap fail for i2s_region\n");
+		return -ENOMEM;
+	}
+
+	cap = pdata->cap;
+	dev->capability = cap;
+	dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
+
+	/* Set DMA slaves info */
+
+	dev->play_dma_data.data = pdata->play_dma_data;
+	dev->capture_dma_data.data = pdata->capture_dma_data;
+	dev->play_dma_data.addr = res->start + I2S_TXDMA;
+	dev->capture_dma_data.addr = res->start + I2S_RXDMA;
+	dev->play_dma_data.max_burst = 16;
+	dev->capture_dma_data.max_burst = 16;
+	dev->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+	dev->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+	dev->play_dma_data.filter = pdata->filter;
+	dev->capture_dma_data.filter = pdata->filter;
+
+	dev->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(dev->clk))
+		return  PTR_ERR(dev->clk);
+
+	ret = clk_enable(dev->clk);
+	if (ret < 0)
+		goto err_clk_put;
+
+	dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
+	if (!dw_i2s_dai) {
+		dev_err(&pdev->dev, "mem allocation failed for dai driver\n");
+		ret = -ENOMEM;
+		goto err_clk_disable;
+	}
+
+	if (cap & DWC_I2S_PLAY) {
+		dev_dbg(&pdev->dev, " SPEAr: play supported\n");
+		dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
+		dw_i2s_dai->playback.channels_max = pdata->channel;
+		dw_i2s_dai->playback.formats = pdata->snd_fmts;
+		dw_i2s_dai->playback.rates = pdata->snd_rates;
+	}
+
+	if (cap & DWC_I2S_RECORD) {
+		dev_dbg(&pdev->dev, "SPEAr: record supported\n");
+		dw_i2s_dai->capture.channels_min = MIN_CHANNEL_NUM;
+		dw_i2s_dai->capture.channels_max = pdata->channel;
+		dw_i2s_dai->capture.formats = pdata->snd_fmts;
+		dw_i2s_dai->capture.rates = pdata->snd_rates;
+	}
+
+	dw_i2s_dai->ops = &dw_i2s_dai_ops;
+	dw_i2s_dai->suspend = dw_i2s_suspend;
+	dw_i2s_dai->resume = dw_i2s_resume;
+
+	dev->dev = &pdev->dev;
+	dev_set_drvdata(&pdev->dev, dev);
+	ret = snd_soc_register_dai(&pdev->dev, dw_i2s_dai);
+	if (ret != 0) {
+		dev_err(&pdev->dev, "not able to register dai\n");
+		goto err_set_drvdata;
+	}
+
+	return 0;
+
+err_set_drvdata:
+	dev_set_drvdata(&pdev->dev, NULL);
+err_clk_disable:
+	clk_disable(dev->clk);
+err_clk_put:
+	clk_put(dev->clk);
+	return ret;
+}
+
+static int dw_i2s_remove(struct platform_device *pdev)
+{
+	struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
+
+	snd_soc_unregister_dai(&pdev->dev);
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	clk_put(dev->clk);
+
+	return 0;
+}
+
+static struct platform_driver dw_i2s_driver = {
+	.probe		= dw_i2s_probe,
+	.remove		= dw_i2s_remove,
+	.driver		= {
+		.name	= "designware-i2s",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(dw_i2s_driver);
+
+MODULE_AUTHOR("Rajeev Kumar <rajeev-dlh.kumar@st.com>");
+MODULE_DESCRIPTION("DESIGNWARE I2S SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:designware_i2s");

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

@@ -136,7 +136,7 @@ static struct snd_pcm_ops ep93xx_pcm_ops = {
 	.hw_params	= ep93xx_pcm_hw_params,
 	.hw_params	= ep93xx_pcm_hw_params,
 	.hw_free	= ep93xx_pcm_hw_free,
 	.hw_free	= ep93xx_pcm_hw_free,
 	.trigger	= snd_dmaengine_pcm_trigger,
 	.trigger	= snd_dmaengine_pcm_trigger,
-	.pointer	= snd_dmaengine_pcm_pointer,
+	.pointer	= snd_dmaengine_pcm_pointer_no_residue,
 	.mmap		= ep93xx_pcm_mmap,
 	.mmap		= ep93xx_pcm_mmap,
 };
 };
 
 

+ 1 - 1
sound/soc/fsl/imx-audmux.c

@@ -156,7 +156,7 @@ static void __init audmux_debugfs_init(void)
 		return;
 		return;
 	}
 	}
 
 
-	for (i = 0; i < MX31_AUDMUX_PORT6_SSI_PINS_6 + 1; i++) {
+	for (i = 0; i < MX31_AUDMUX_PORT7_SSI_PINS_7 + 1; i++) {
 		snprintf(buf, sizeof(buf), "ssi%d", i);
 		snprintf(buf, sizeof(buf), "ssi%d", i);
 		if (!debugfs_create_file(buf, 0444, audmux_debugfs_root,
 		if (!debugfs_create_file(buf, 0444, audmux_debugfs_root,
 					 (void *)i, &audmux_debugfs_fops))
 					 (void *)i, &audmux_debugfs_fops))

+ 1 - 0
sound/soc/fsl/imx-audmux.h

@@ -14,6 +14,7 @@
 #define MX31_AUDMUX_PORT4_SSI_PINS_4	3
 #define MX31_AUDMUX_PORT4_SSI_PINS_4	3
 #define MX31_AUDMUX_PORT5_SSI_PINS_5	4
 #define MX31_AUDMUX_PORT5_SSI_PINS_5	4
 #define MX31_AUDMUX_PORT6_SSI_PINS_6	5
 #define MX31_AUDMUX_PORT6_SSI_PINS_6	5
+#define MX31_AUDMUX_PORT7_SSI_PINS_7	6
 
 
 #define MX51_AUDMUX_PORT1_SSI0		0
 #define MX51_AUDMUX_PORT1_SSI0		0
 #define MX51_AUDMUX_PORT2_SSI1		1
 #define MX51_AUDMUX_PORT2_SSI1		1

+ 33 - 16
sound/soc/fsl/imx-mc13783.c

@@ -111,22 +111,39 @@ static int __devinit imx_mc13783_probe(struct platform_device *pdev)
 		return ret;
 		return ret;
 	}
 	}
 
 
-	imx_audmux_v2_configure_port(MX31_AUDMUX_PORT4_SSI_PINS_4,
-		IMX_AUDMUX_V2_PTCR_SYN,
-		IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0) |
-		IMX_AUDMUX_V2_PDCR_MODE(1) |
-		IMX_AUDMUX_V2_PDCR_INMMASK(0xfc));
-	imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0,
-		IMX_AUDMUX_V2_PTCR_SYN |
-		IMX_AUDMUX_V2_PTCR_TFSDIR |
-		IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
-		IMX_AUDMUX_V2_PTCR_TCLKDIR |
-		IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
-		IMX_AUDMUX_V2_PTCR_RFSDIR |
-		IMX_AUDMUX_V2_PTCR_RFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
-		IMX_AUDMUX_V2_PTCR_RCLKDIR |
-		IMX_AUDMUX_V2_PTCR_RCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4),
-		IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT4_SSI_PINS_4));
+	if (machine_is_mx31_3ds()) {
+		imx_audmux_v2_configure_port(MX31_AUDMUX_PORT4_SSI_PINS_4,
+			IMX_AUDMUX_V2_PTCR_SYN,
+			IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0) |
+			IMX_AUDMUX_V2_PDCR_MODE(1) |
+			IMX_AUDMUX_V2_PDCR_INMMASK(0xfc));
+		imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0,
+			IMX_AUDMUX_V2_PTCR_SYN |
+			IMX_AUDMUX_V2_PTCR_TFSDIR |
+			IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
+			IMX_AUDMUX_V2_PTCR_TCLKDIR |
+			IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
+			IMX_AUDMUX_V2_PTCR_RFSDIR |
+			IMX_AUDMUX_V2_PTCR_RFSEL(MX31_AUDMUX_PORT4_SSI_PINS_4) |
+			IMX_AUDMUX_V2_PTCR_RCLKDIR |
+			IMX_AUDMUX_V2_PTCR_RCSEL(MX31_AUDMUX_PORT4_SSI_PINS_4),
+			IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT4_SSI_PINS_4));
+	} else if (machine_is_mx27_3ds()) {
+		imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0,
+			IMX_AUDMUX_V1_PCR_SYN |
+			IMX_AUDMUX_V1_PCR_TFSDIR |
+			IMX_AUDMUX_V1_PCR_TCLKDIR |
+			IMX_AUDMUX_V1_PCR_RFSDIR |
+			IMX_AUDMUX_V1_PCR_RCLKDIR |
+			IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) |
+			IMX_AUDMUX_V1_PCR_RFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) |
+			IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4)
+		);
+		imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR3_SSI_PINS_4,
+			IMX_AUDMUX_V1_PCR_SYN |
+			IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0)
+		);
+	}
 
 
 	return ret;
 	return ret;
 }
 }

+ 1 - 1
sound/soc/fsl/imx-pcm-dma.c

@@ -141,7 +141,7 @@ static struct snd_pcm_ops imx_pcm_ops = {
 	.ioctl		= snd_pcm_lib_ioctl,
 	.ioctl		= snd_pcm_lib_ioctl,
 	.hw_params	= snd_imx_pcm_hw_params,
 	.hw_params	= snd_imx_pcm_hw_params,
 	.trigger	= snd_dmaengine_pcm_trigger,
 	.trigger	= snd_dmaengine_pcm_trigger,
-	.pointer	= snd_dmaengine_pcm_pointer,
+	.pointer	= snd_dmaengine_pcm_pointer_no_residue,
 	.mmap		= snd_imx_pcm_mmap,
 	.mmap		= snd_imx_pcm_mmap,
 };
 };
 
 

+ 1 - 2
sound/soc/fsl/imx-sgtl5000.c

@@ -95,8 +95,7 @@ static int __devinit imx_sgtl5000_probe(struct platform_device *pdev)
 		return ret;
 		return ret;
 	}
 	}
 	imx_audmux_v2_configure_port(ext_port,
 	imx_audmux_v2_configure_port(ext_port,
-			IMX_AUDMUX_V2_PTCR_SYN |
-			IMX_AUDMUX_V2_PTCR_TCSEL(int_port),
+			IMX_AUDMUX_V2_PTCR_SYN,
 			IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
 			IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
 	if (ret) {
 	if (ret) {
 		dev_err(&pdev->dev, "audmux external port setup failed\n");
 		dev_err(&pdev->dev, "audmux external port setup failed\n");

+ 1 - 1
sound/soc/mxs/mxs-pcm.c

@@ -141,7 +141,7 @@ static struct snd_pcm_ops mxs_pcm_ops = {
 	.ioctl		= snd_pcm_lib_ioctl,
 	.ioctl		= snd_pcm_lib_ioctl,
 	.hw_params	= snd_mxs_pcm_hw_params,
 	.hw_params	= snd_mxs_pcm_hw_params,
 	.trigger	= snd_dmaengine_pcm_trigger,
 	.trigger	= snd_dmaengine_pcm_trigger,
-	.pointer	= snd_dmaengine_pcm_pointer,
+	.pointer	= snd_dmaengine_pcm_pointer_no_residue,
 	.mmap		= snd_mxs_pcm_mmap,
 	.mmap		= snd_mxs_pcm_mmap,
 };
 };
 
 

+ 1 - 1
sound/soc/mxs/mxs-sgtl5000.c

@@ -133,7 +133,7 @@ static int __devinit mxs_sgtl5000_probe_dt(struct platform_device *pdev)
 		mxs_sgtl5000_dai[i].codec_name = NULL;
 		mxs_sgtl5000_dai[i].codec_name = NULL;
 		mxs_sgtl5000_dai[i].codec_of_node = codec_np;
 		mxs_sgtl5000_dai[i].codec_of_node = codec_np;
 		mxs_sgtl5000_dai[i].cpu_dai_name = NULL;
 		mxs_sgtl5000_dai[i].cpu_dai_name = NULL;
-		mxs_sgtl5000_dai[i].cpu_dai_of_node = saif_np[i];
+		mxs_sgtl5000_dai[i].cpu_of_node = saif_np[i];
 		mxs_sgtl5000_dai[i].platform_name = NULL;
 		mxs_sgtl5000_dai[i].platform_name = NULL;
 		mxs_sgtl5000_dai[i].platform_of_node = saif_np[i];
 		mxs_sgtl5000_dai[i].platform_of_node = saif_np[i];
 	}
 	}

+ 42 - 0
sound/soc/pxa/Kconfig

@@ -8,6 +8,15 @@ config SND_PXA2XX_SOC
 	  the PXA2xx AC97, I2S or SSP interface. You will also need
 	  the PXA2xx AC97, I2S or SSP interface. You will also need
 	  to select the audio interfaces to support below.
 	  to select the audio interfaces to support below.
 
 
+config SND_MMP_SOC
+	bool "Soc Audio for Marvell MMP chips"
+	depends on ARCH_MMP
+	select SND_SOC_DMAENGINE_PCM
+	select SND_ARM
+	help
+	  Say Y if you want to add support for codecs attached to
+	  the MMP SSPA interface.
+
 config SND_PXA2XX_AC97
 config SND_PXA2XX_AC97
 	tristate
 	tristate
 	select SND_AC97_CODEC
 	select SND_AC97_CODEC
@@ -26,6 +35,9 @@ config SND_PXA_SOC_SSP
 	tristate
 	tristate
 	select PXA_SSP
 	select PXA_SSP
 
 
+config SND_MMP_SOC_SSPA
+	tristate
+
 config SND_PXA2XX_SOC_CORGI
 config SND_PXA2XX_SOC_CORGI
 	tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
 	tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
 	depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx
 	depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx
@@ -138,6 +150,26 @@ config SND_SOC_TAVOREVB3
 	  Say Y if you want to add support for SoC audio on the
 	  Say Y if you want to add support for SoC audio on the
 	  Marvell Saarb reference platform.
 	  Marvell Saarb reference platform.
 
 
+config SND_PXA910_SOC
+	tristate "SoC Audio for Marvell PXA910 chip"
+	depends on ARCH_MMP && SND
+	select SND_PCM
+	help
+	  Say Y if you want to add support for SoC audio on the
+	  Marvell PXA910 reference platform.
+
+config SND_SOC_TTC_DKB
+	bool "SoC Audio support for TTC DKB"
+	depends on SND_PXA910_SOC && MACH_TTC_DKB
+	select PXA_SSP
+	select SND_PXA_SOC_SSP
+	select SND_MMP_SOC
+	select MFD_88PM860X
+	select SND_SOC_88PM860X
+	help
+	  Say Y if you want to add support for SoC audio on TTC DKB
+
+
 config SND_SOC_ZYLONITE
 config SND_SOC_ZYLONITE
 	tristate "SoC Audio support for Marvell Zylonite"
 	tristate "SoC Audio support for Marvell Zylonite"
 	depends on SND_PXA2XX_SOC && MACH_ZYLONITE
 	depends on SND_PXA2XX_SOC && MACH_ZYLONITE
@@ -194,3 +226,13 @@ config SND_PXA2XX_SOC_IMOTE2
        help
        help
          Say Y if you want to add support for SoC audio on the
          Say Y if you want to add support for SoC audio on the
 	 IMote 2.
 	 IMote 2.
+
+config SND_MMP_SOC_BROWNSTONE
+	tristate "SoC Audio support for Marvell Brownstone"
+	depends on SND_MMP_SOC && MACH_BROWNSTONE
+	select SND_MMP_SOC_SSPA
+	select MFD_WM8994
+	select SND_SOC_WM8994
+	help
+	  Say Y if you want to add support for SoC audio on the
+	  Marvell Brownstone reference platform.

+ 8 - 0
sound/soc/pxa/Makefile

@@ -3,11 +3,15 @@ snd-soc-pxa2xx-objs := pxa2xx-pcm.o
 snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
 snd-soc-pxa2xx-ac97-objs := pxa2xx-ac97.o
 snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
 snd-soc-pxa2xx-i2s-objs := pxa2xx-i2s.o
 snd-soc-pxa-ssp-objs := pxa-ssp.o
 snd-soc-pxa-ssp-objs := pxa-ssp.o
+snd-soc-mmp-objs := mmp-pcm.o
+snd-soc-mmp-sspa-objs := mmp-sspa.o
 
 
 obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o
 obj-$(CONFIG_SND_PXA2XX_SOC) += snd-soc-pxa2xx.o
 obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o
 obj-$(CONFIG_SND_PXA2XX_SOC_AC97) += snd-soc-pxa2xx-ac97.o
 obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
 obj-$(CONFIG_SND_PXA2XX_SOC_I2S) += snd-soc-pxa2xx-i2s.o
 obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o
 obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o
+obj-$(CONFIG_SND_MMP_SOC) += snd-soc-mmp.o
+obj-$(CONFIG_SND_MMP_SOC_SSPA) += snd-soc-mmp-sspa.o
 
 
 # PXA Machine Support
 # PXA Machine Support
 snd-soc-corgi-objs := corgi.o
 snd-soc-corgi-objs := corgi.o
@@ -28,6 +32,8 @@ snd-soc-mioa701-objs := mioa701_wm9713.o
 snd-soc-z2-objs := z2.o
 snd-soc-z2-objs := z2.o
 snd-soc-imote2-objs := imote2.o
 snd-soc-imote2-objs := imote2.o
 snd-soc-raumfeld-objs := raumfeld.o
 snd-soc-raumfeld-objs := raumfeld.o
+snd-soc-brownstone-objs := brownstone.o
+snd-soc-ttc-dkb-objs := ttc-dkb.o
 
 
 obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
 obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
 obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
 obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
@@ -47,3 +53,5 @@ obj-$(CONFIG_SND_SOC_TAVOREVB3) += snd-soc-tavorevb3.o
 obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
 obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o
 obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o
 obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o
 obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o
 obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o
+obj-$(CONFIG_SND_MMP_SOC_BROWNSTONE) += snd-soc-brownstone.o
+obj-$(CONFIG_SND_SOC_TTC_DKB) += snd-soc-ttc-dkb.o

+ 174 - 0
sound/soc/pxa/brownstone.c

@@ -0,0 +1,174 @@
+/*
+ * linux/sound/soc/pxa/brownstone.c
+ *
+ * Copyright (C) 2011 Marvell International Ltd.
+ *
+ * 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 <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "../codecs/wm8994.h"
+#include "mmp-sspa.h"
+
+static const struct snd_kcontrol_new brownstone_dapm_control[] = {
+	SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static const struct snd_soc_dapm_widget brownstone_dapm_widgets[] = {
+	SND_SOC_DAPM_SPK("Ext Spk", NULL),
+	SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Main Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route brownstone_audio_map[] = {
+	{"Ext Spk", NULL, "SPKOUTLP"},
+	{"Ext Spk", NULL, "SPKOUTLN"},
+	{"Ext Spk", NULL, "SPKOUTRP"},
+	{"Ext Spk", NULL, "SPKOUTRN"},
+
+	{"Headset Stereophone", NULL, "HPOUT1L"},
+	{"Headset Stereophone", NULL, "HPOUT1R"},
+
+	{"IN1RN", NULL, "Headset Mic"},
+
+	{"DMIC1DAT", NULL, "MICBIAS1"},
+	{"MICBIAS1", NULL, "Main Mic"},
+};
+
+static int brownstone_wm8994_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+	snd_soc_dapm_enable_pin(dapm, "Ext Spk");
+	snd_soc_dapm_enable_pin(dapm, "Headset Stereophone");
+	snd_soc_dapm_enable_pin(dapm, "Headset Mic");
+	snd_soc_dapm_enable_pin(dapm, "Main Mic");
+
+	/* set endpoints to not connected */
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "HPOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
+	snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
+	snd_soc_dapm_nc_pin(dapm, "IN1LN");
+	snd_soc_dapm_nc_pin(dapm, "IN1LP");
+	snd_soc_dapm_nc_pin(dapm, "IN1RP");
+	snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
+	snd_soc_dapm_nc_pin(dapm, "IN2RN");
+	snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
+	snd_soc_dapm_nc_pin(dapm, "IN2LN");
+
+	snd_soc_dapm_sync(dapm);
+
+	return 0;
+}
+
+static int brownstone_wm8994_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 freq_out, sspa_mclk, sysclk;
+	int sspa_div;
+
+	if (params_rate(params) > 11025) {
+		freq_out  = params_rate(params) * 512;
+		sysclk    = params_rate(params) * 256;
+		sspa_mclk = params_rate(params) * 64;
+	} else {
+		freq_out  = params_rate(params) * 1024;
+		sysclk    = params_rate(params) * 512;
+		sspa_mclk = params_rate(params) * 64;
+	}
+	sspa_div = freq_out;
+	do_div(sspa_div, sspa_mclk);
+
+	snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0);
+	snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk);
+	snd_soc_dai_set_pll(cpu_dai, MMP_SSPA_CLK, 0, freq_out, sspa_mclk);
+
+	/* set wm8994 sysclk */
+	snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1, sysclk, 0);
+
+	return 0;
+}
+
+/* machine stream operations */
+static struct snd_soc_ops brownstone_ops = {
+	.hw_params = brownstone_wm8994_hw_params,
+};
+
+static struct snd_soc_dai_link brownstone_wm8994_dai[] = {
+{
+	.name		= "WM8994",
+	.stream_name	= "WM8994 HiFi",
+	.cpu_dai_name	= "mmp-sspa-dai.0",
+	.codec_dai_name	= "wm8994-aif1",
+	.platform_name	= "mmp-pcm-audio",
+	.codec_name	= "wm8994-codec",
+	.dai_fmt	= SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+				SND_SOC_DAIFMT_CBS_CFS,
+	.ops		= &brownstone_ops,
+	.init		= brownstone_wm8994_init,
+},
+};
+
+/* audio machine driver */
+static struct snd_soc_card brownstone = {
+	.name         = "brownstone",
+	.dai_link     = brownstone_wm8994_dai,
+	.num_links    = ARRAY_SIZE(brownstone_wm8994_dai),
+
+	.controls = brownstone_dapm_control,
+	.num_controls = ARRAY_SIZE(brownstone_dapm_control),
+	.dapm_widgets = brownstone_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(brownstone_dapm_widgets),
+	.dapm_routes = brownstone_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(brownstone_audio_map),
+};
+
+static int __devinit brownstone_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	brownstone.dev = &pdev->dev;
+	ret = snd_soc_register_card(&brownstone);
+	if (ret)
+		dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+				ret);
+	return ret;
+}
+
+static int __devexit brownstone_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_card(&brownstone);
+	return 0;
+}
+
+static struct platform_driver mmp_driver = {
+	.driver		= {
+		.name	= "brownstone-audio",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= brownstone_probe,
+	.remove		= __devexit_p(brownstone_remove),
+};
+
+module_platform_driver(mmp_driver);
+
+MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
+MODULE_DESCRIPTION("ALSA SoC Brownstone");
+MODULE_LICENSE("GPL");

+ 297 - 0
sound/soc/pxa/mmp-pcm.c

@@ -0,0 +1,297 @@
+/*
+ * linux/sound/soc/pxa/mmp-pcm.c
+ *
+ * Copyright (C) 2011 Marvell International Ltd.
+ *
+ * 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/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/platform_data/mmp_audio.h>
+#include <sound/pxa2xx-lib.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <mach/sram.h>
+#include <sound/dmaengine_pcm.h>
+
+struct mmp_dma_data {
+	int ssp_id;
+	struct resource *dma_res;
+};
+
+#define MMP_PCM_INFO (SNDRV_PCM_INFO_MMAP |	\
+		SNDRV_PCM_INFO_MMAP_VALID |	\
+		SNDRV_PCM_INFO_INTERLEAVED |	\
+		SNDRV_PCM_INFO_PAUSE |		\
+		SNDRV_PCM_INFO_RESUME)
+
+#define MMP_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+			 SNDRV_PCM_FMTBIT_S24_LE | \
+			 SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_pcm_hardware mmp_pcm_hardware[] = {
+	{
+		.info			= MMP_PCM_INFO,
+		.formats		= MMP_PCM_FORMATS,
+		.period_bytes_min	= 1024,
+		.period_bytes_max	= 2048,
+		.periods_min		= 2,
+		.periods_max		= 32,
+		.buffer_bytes_max	= 4096,
+		.fifo_size		= 32,
+	},
+	{
+		.info			= MMP_PCM_INFO,
+		.formats		= MMP_PCM_FORMATS,
+		.period_bytes_min	= 1024,
+		.period_bytes_max	= 2048,
+		.periods_min		= 2,
+		.periods_max		= 32,
+		.buffer_bytes_max	= 4096,
+		.fifo_size		= 32,
+	},
+};
+
+static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params)
+{
+	struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct pxa2xx_pcm_dma_params *dma_params;
+	struct dma_slave_config slave_config;
+	int ret;
+
+	dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+	if (!dma_params)
+		return 0;
+
+	ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config);
+	if (ret)
+		return ret;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		slave_config.dst_addr     = dma_params->dev_addr;
+		slave_config.dst_maxburst = 4;
+	} else {
+		slave_config.src_addr	  = dma_params->dev_addr;
+		slave_config.src_maxburst = 4;
+	}
+
+	ret = dmaengine_slave_config(chan, &slave_config);
+	if (ret)
+		return ret;
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	return 0;
+}
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+	struct mmp_dma_data *dma_data = param;
+	bool found = false;
+	char *devname;
+
+	devname = kasprintf(GFP_KERNEL, "%s.%d", dma_data->dma_res->name,
+		dma_data->ssp_id);
+	if ((strcmp(dev_name(chan->device->dev), devname) == 0) &&
+		(chan->chan_id == dma_data->dma_res->start)) {
+		found = true;
+	}
+
+	kfree(devname);
+	return found;
+}
+
+static int mmp_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct platform_device *pdev = to_platform_device(rtd->platform->dev);
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct mmp_dma_data *dma_data;
+	struct resource *r;
+	int ret;
+
+	r = platform_get_resource(pdev, IORESOURCE_DMA, substream->stream);
+	if (!r)
+		return -EBUSY;
+
+	snd_soc_set_runtime_hwparams(substream,
+				&mmp_pcm_hardware[substream->stream]);
+	dma_data = devm_kzalloc(&pdev->dev,
+			sizeof(struct mmp_dma_data), GFP_KERNEL);
+	if (dma_data == NULL)
+		return -ENOMEM;
+
+	dma_data->dma_res = r;
+	dma_data->ssp_id = cpu_dai->id;
+
+	ret = snd_dmaengine_pcm_open(substream, filter, dma_data);
+	if (ret) {
+		devm_kfree(&pdev->dev, dma_data);
+		return ret;
+	}
+
+	snd_dmaengine_pcm_set_data(substream, dma_data);
+	return 0;
+}
+
+static int mmp_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct mmp_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream);
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct platform_device *pdev = to_platform_device(rtd->platform->dev);
+
+	snd_dmaengine_pcm_close(substream);
+	devm_kfree(&pdev->dev, dma_data);
+	return 0;
+}
+
+static int mmp_pcm_mmap(struct snd_pcm_substream *substream,
+			 struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	unsigned long off = vma->vm_pgoff;
+
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+	return remap_pfn_range(vma, vma->vm_start,
+		__phys_to_pfn(runtime->dma_addr) + off,
+		vma->vm_end - vma->vm_start, vma->vm_page_prot);
+}
+
+struct snd_pcm_ops mmp_pcm_ops = {
+	.open		= mmp_pcm_open,
+	.close		= mmp_pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= mmp_pcm_hw_params,
+	.trigger	= snd_dmaengine_pcm_trigger,
+	.pointer	= snd_dmaengine_pcm_pointer,
+	.mmap		= mmp_pcm_mmap,
+};
+
+static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_dma_buffer *buf;
+	int stream;
+	struct gen_pool *gpool;
+
+	gpool = sram_get_gpool("asram");
+	if (!gpool)
+		return;
+
+	for (stream = 0; stream < 2; stream++) {
+		size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
+
+		substream = pcm->streams[stream].substream;
+		if (!substream)
+			continue;
+
+		buf = &substream->dma_buffer;
+		if (!buf->area)
+			continue;
+		gen_pool_free(gpool, (unsigned long)buf->area, size);
+		buf->area = NULL;
+	}
+
+	return;
+}
+
+static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
+								int stream)
+{
+	struct snd_dma_buffer *buf = &substream->dma_buffer;
+	size_t size = mmp_pcm_hardware[stream].buffer_bytes_max;
+	struct gen_pool *gpool;
+
+	buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	buf->dev.dev = substream->pcm->card->dev;
+	buf->private_data = NULL;
+
+	gpool = sram_get_gpool("asram");
+	if (!gpool)
+		return -ENOMEM;
+
+	buf->area = (unsigned char *)gen_pool_alloc(gpool, size);
+	if (!buf->area)
+		return -ENOMEM;
+	buf->addr = gen_pool_virt_to_phys(gpool, (unsigned long)buf->area);
+	buf->bytes = size;
+	return 0;
+}
+
+int mmp_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm_substream *substream;
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret = 0, stream;
+
+	for (stream = 0; stream < 2; stream++) {
+		substream = pcm->streams[stream].substream;
+
+		ret = mmp_pcm_preallocate_dma_buffer(substream,	stream);
+		if (ret)
+			goto err;
+	}
+
+	return 0;
+
+err:
+	mmp_pcm_free_dma_buffers(pcm);
+	return ret;
+}
+
+struct snd_soc_platform_driver mmp_soc_platform = {
+	.ops		= &mmp_pcm_ops,
+	.pcm_new	= mmp_pcm_new,
+	.pcm_free	= mmp_pcm_free_dma_buffers,
+};
+
+static __devinit int mmp_pcm_probe(struct platform_device *pdev)
+{
+	struct mmp_audio_platdata *pdata = pdev->dev.platform_data;
+
+	if (pdata) {
+		mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].buffer_bytes_max =
+						pdata->buffer_max_playback;
+		mmp_pcm_hardware[SNDRV_PCM_STREAM_PLAYBACK].period_bytes_max =
+						pdata->period_max_playback;
+		mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].buffer_bytes_max =
+						pdata->buffer_max_capture;
+		mmp_pcm_hardware[SNDRV_PCM_STREAM_CAPTURE].period_bytes_max =
+						pdata->period_max_capture;
+	}
+	return snd_soc_register_platform(&pdev->dev, &mmp_soc_platform);
+}
+
+static int __devexit mmp_pcm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver mmp_pcm_driver = {
+	.driver = {
+		.name = "mmp-pcm-audio",
+		.owner = THIS_MODULE,
+	},
+
+	.probe = mmp_pcm_probe,
+	.remove = __devexit_p(mmp_pcm_remove),
+};
+
+module_platform_driver(mmp_pcm_driver);
+
+MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
+MODULE_DESCRIPTION("MMP Soc Audio DMA module");
+MODULE_LICENSE("GPL");

+ 480 - 0
sound/soc/pxa/mmp-sspa.c

@@ -0,0 +1,480 @@
+/*
+ * linux/sound/soc/pxa/mmp-sspa.c
+ * Base on pxa2xx-ssp.c
+ *
+ * Copyright (C) 2011 Marvell International Ltd.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/pxa2xx_ssp.h>
+#include <linux/io.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/pxa2xx-lib.h>
+#include "mmp-sspa.h"
+
+/*
+ * SSPA audio private data
+ */
+struct sspa_priv {
+	struct ssp_device *sspa;
+	struct pxa2xx_pcm_dma_params *dma_params;
+	struct clk *audio_clk;
+	struct clk *sysclk;
+	int dai_fmt;
+	int running_cnt;
+};
+
+static void mmp_sspa_write_reg(struct ssp_device *sspa, u32 reg, u32 val)
+{
+	__raw_writel(val, sspa->mmio_base + reg);
+}
+
+static u32 mmp_sspa_read_reg(struct ssp_device *sspa, u32 reg)
+{
+	return __raw_readl(sspa->mmio_base + reg);
+}
+
+static void mmp_sspa_tx_enable(struct ssp_device *sspa)
+{
+	unsigned int sspa_sp;
+
+	sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP);
+	sspa_sp |= SSPA_SP_S_EN;
+	sspa_sp |= SSPA_SP_WEN;
+	mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+}
+
+static void mmp_sspa_tx_disable(struct ssp_device *sspa)
+{
+	unsigned int sspa_sp;
+
+	sspa_sp = mmp_sspa_read_reg(sspa, SSPA_TXSP);
+	sspa_sp &= ~SSPA_SP_S_EN;
+	sspa_sp |= SSPA_SP_WEN;
+	mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+}
+
+static void mmp_sspa_rx_enable(struct ssp_device *sspa)
+{
+	unsigned int sspa_sp;
+
+	sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP);
+	sspa_sp |= SSPA_SP_S_EN;
+	sspa_sp |= SSPA_SP_WEN;
+	mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
+}
+
+static void mmp_sspa_rx_disable(struct ssp_device *sspa)
+{
+	unsigned int sspa_sp;
+
+	sspa_sp = mmp_sspa_read_reg(sspa, SSPA_RXSP);
+	sspa_sp &= ~SSPA_SP_S_EN;
+	sspa_sp |= SSPA_SP_WEN;
+	mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
+}
+
+static int mmp_sspa_startup(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct sspa_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	clk_enable(priv->sysclk);
+	clk_enable(priv->sspa->clk);
+
+	return 0;
+}
+
+static void mmp_sspa_shutdown(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	struct sspa_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+	clk_disable(priv->sspa->clk);
+	clk_disable(priv->sysclk);
+
+	return;
+}
+
+/*
+ * Set the SSP ports SYSCLK.
+ */
+static int mmp_sspa_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
+				    int clk_id, unsigned int freq, int dir)
+{
+	struct sspa_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+	int ret = 0;
+
+	switch (clk_id) {
+	case MMP_SSPA_CLK_AUDIO:
+		ret = clk_set_rate(priv->audio_clk, freq);
+		if (ret)
+			return ret;
+		break;
+	case MMP_SSPA_CLK_PLL:
+	case MMP_SSPA_CLK_VCXO:
+		/* not support yet */
+		return -EINVAL;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mmp_sspa_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
+				 int source, unsigned int freq_in,
+				 unsigned int freq_out)
+{
+	struct sspa_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
+	int ret = 0;
+
+	switch (pll_id) {
+	case MMP_SYSCLK:
+		ret = clk_set_rate(priv->sysclk, freq_out);
+		if (ret)
+			return ret;
+		break;
+	case MMP_SSPA_CLK:
+		ret = clk_set_rate(priv->sspa->clk, freq_out);
+		if (ret)
+			return ret;
+		break;
+	default:
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/*
+ * Set up the sspa dai format. The sspa port must be inactive
+ * before calling this function as the physical
+ * interface format is changed.
+ */
+static int mmp_sspa_set_dai_fmt(struct snd_soc_dai *cpu_dai,
+				 unsigned int fmt)
+{
+	struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(cpu_dai);
+	struct ssp_device *sspa = sspa_priv->sspa;
+	u32 sspa_sp, sspa_ctrl;
+
+	/* check if we need to change anything at all */
+	if (sspa_priv->dai_fmt == fmt)
+		return 0;
+
+	/* we can only change the settings if the port is not in use */
+	if ((mmp_sspa_read_reg(sspa, SSPA_TXSP) & SSPA_SP_S_EN) ||
+	    (mmp_sspa_read_reg(sspa, SSPA_RXSP) & SSPA_SP_S_EN)) {
+		dev_err(&sspa->pdev->dev,
+			"can't change hardware dai format: stream is in use\n");
+		return -EINVAL;
+	}
+
+	/* reset port settings */
+	sspa_sp   = SSPA_SP_WEN | SSPA_SP_S_RST | SSPA_SP_FFLUSH;
+	sspa_ctrl = 0;
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		sspa_sp |= SSPA_SP_MSL;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+	case SND_SOC_DAIFMT_NB_NF:
+		sspa_sp |= SSPA_SP_FSP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		sspa_sp |= SSPA_TXSP_FPER(63);
+		sspa_sp |= SSPA_SP_FWID(31);
+		sspa_ctrl |= SSPA_CTL_XDATDLY(1);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+	mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
+
+	sspa_sp &= ~(SSPA_SP_S_RST | SSPA_SP_FFLUSH);
+	mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+	mmp_sspa_write_reg(sspa, SSPA_RXSP, sspa_sp);
+
+	/*
+	 * FIXME: hw issue, for the tx serial port,
+	 * can not config the master/slave mode;
+	 * so must clean this bit.
+	 * The master/slave mode has been set in the
+	 * rx port.
+	 */
+	sspa_sp &= ~SSPA_SP_MSL;
+	mmp_sspa_write_reg(sspa, SSPA_TXSP, sspa_sp);
+
+	mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl);
+	mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl);
+
+	/* Since we are configuring the timings for the format by hand
+	 * we have to defer some things until hw_params() where we
+	 * know parameters like the sample size.
+	 */
+	sspa_priv->dai_fmt = fmt;
+	return 0;
+}
+
+/*
+ * Set the SSPA audio DMA parameters and sample size.
+ * Can be called multiple times by oss emulation.
+ */
+static int mmp_sspa_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_dai *cpu_dai = rtd->cpu_dai;
+	struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai);
+	struct ssp_device *sspa = sspa_priv->sspa;
+	struct pxa2xx_pcm_dma_params *dma_params;
+	u32 sspa_ctrl;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_TXCTL);
+	else
+		sspa_ctrl = mmp_sspa_read_reg(sspa, SSPA_RXCTL);
+
+	sspa_ctrl &= ~SSPA_CTL_XFRLEN1_MASK;
+	sspa_ctrl |= SSPA_CTL_XFRLEN1(params_channels(params) - 1);
+	sspa_ctrl &= ~SSPA_CTL_XWDLEN1_MASK;
+	sspa_ctrl |= SSPA_CTL_XWDLEN1(SSPA_CTL_32_BITS);
+	sspa_ctrl &= ~SSPA_CTL_XSSZ1_MASK;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S8:
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_8_BITS);
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_16_BITS);
+		break;
+	case SNDRV_PCM_FORMAT_S20_3LE:
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_20_BITS);
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_24_BITS);
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		sspa_ctrl |= SSPA_CTL_XSSZ1(SSPA_CTL_32_BITS);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		mmp_sspa_write_reg(sspa, SSPA_TXCTL, sspa_ctrl);
+		mmp_sspa_write_reg(sspa, SSPA_TXFIFO_LL, 0x1);
+	} else {
+		mmp_sspa_write_reg(sspa, SSPA_RXCTL, sspa_ctrl);
+		mmp_sspa_write_reg(sspa, SSPA_RXFIFO_UL, 0x0);
+	}
+
+	dma_params = &sspa_priv->dma_params[substream->stream];
+	dma_params->dev_addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+				(sspa->phys_base + SSPA_TXD) :
+				(sspa->phys_base + SSPA_RXD);
+	snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params);
+	return 0;
+}
+
+static int mmp_sspa_trigger(struct snd_pcm_substream *substream, int cmd,
+			     struct snd_soc_dai *dai)
+{
+	struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai);
+	struct ssp_device *sspa = sspa_priv->sspa;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		/*
+		 * whatever playback or capture, must enable rx.
+		 * this is a hw issue, so need check if rx has been
+		 * enabled or not; if has been enabled by another
+		 * stream, do not enable again.
+		 */
+		if (!sspa_priv->running_cnt)
+			mmp_sspa_rx_enable(sspa);
+
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			mmp_sspa_tx_enable(sspa);
+
+		sspa_priv->running_cnt++;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		sspa_priv->running_cnt--;
+
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			mmp_sspa_tx_disable(sspa);
+
+		/* have no capture stream, disable rx port */
+		if (!sspa_priv->running_cnt)
+			mmp_sspa_rx_disable(sspa);
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int mmp_sspa_probe(struct snd_soc_dai *dai)
+{
+	struct sspa_priv *priv = dev_get_drvdata(dai->dev);
+
+	snd_soc_dai_set_drvdata(dai, priv);
+	return 0;
+
+}
+
+#define MMP_SSPA_RATES SNDRV_PCM_RATE_8000_192000
+#define MMP_SSPA_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
+		SNDRV_PCM_FMTBIT_S16_LE | \
+		SNDRV_PCM_FMTBIT_S24_LE | \
+		SNDRV_PCM_FMTBIT_S24_LE | \
+		SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops mmp_sspa_dai_ops = {
+	.startup	= mmp_sspa_startup,
+	.shutdown	= mmp_sspa_shutdown,
+	.trigger	= mmp_sspa_trigger,
+	.hw_params	= mmp_sspa_hw_params,
+	.set_sysclk	= mmp_sspa_set_dai_sysclk,
+	.set_pll	= mmp_sspa_set_dai_pll,
+	.set_fmt	= mmp_sspa_set_dai_fmt,
+};
+
+struct snd_soc_dai_driver mmp_sspa_dai = {
+	.probe = mmp_sspa_probe,
+	.playback = {
+		.channels_min = 1,
+		.channels_max = 128,
+		.rates = MMP_SSPA_RATES,
+		.formats = MMP_SSPA_FORMATS,
+	},
+	.capture = {
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = MMP_SSPA_RATES,
+		.formats = MMP_SSPA_FORMATS,
+	},
+	.ops = &mmp_sspa_dai_ops,
+};
+
+static __devinit int asoc_mmp_sspa_probe(struct platform_device *pdev)
+{
+	struct sspa_priv *priv;
+	struct resource *res;
+
+	priv = devm_kzalloc(&pdev->dev,
+				sizeof(struct sspa_priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->sspa = devm_kzalloc(&pdev->dev,
+				sizeof(struct ssp_device), GFP_KERNEL);
+	if (priv->sspa == NULL)
+		return -ENOMEM;
+
+	priv->dma_params = devm_kzalloc(&pdev->dev,
+			2 * sizeof(struct pxa2xx_pcm_dma_params), GFP_KERNEL);
+	if (priv->dma_params == NULL)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL)
+		return -ENOMEM;
+
+	priv->sspa->mmio_base = devm_request_and_ioremap(&pdev->dev, res);
+	if (priv->sspa->mmio_base == NULL)
+		return -ENODEV;
+
+	priv->sspa->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->sspa->clk))
+		return PTR_ERR(priv->sspa->clk);
+
+	priv->audio_clk = clk_get(NULL, "mmp-audio");
+	if (IS_ERR(priv->audio_clk))
+		return PTR_ERR(priv->audio_clk);
+
+	priv->sysclk = clk_get(NULL, "mmp-sysclk");
+	if (IS_ERR(priv->sysclk)) {
+		clk_put(priv->audio_clk);
+		return PTR_ERR(priv->sysclk);
+	}
+	clk_enable(priv->audio_clk);
+	priv->dai_fmt = (unsigned int) -1;
+	platform_set_drvdata(pdev, priv);
+
+	return snd_soc_register_dai(&pdev->dev, &mmp_sspa_dai);
+}
+
+static int __devexit asoc_mmp_sspa_remove(struct platform_device *pdev)
+{
+	struct sspa_priv *priv = platform_get_drvdata(pdev);
+
+	clk_disable(priv->audio_clk);
+	clk_put(priv->audio_clk);
+	clk_put(priv->sysclk);
+	snd_soc_unregister_dai(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver asoc_mmp_sspa_driver = {
+	.driver = {
+		.name = "mmp-sspa-dai",
+		.owner = THIS_MODULE,
+	},
+	.probe = asoc_mmp_sspa_probe,
+	.remove = __devexit_p(asoc_mmp_sspa_remove),
+};
+
+module_platform_driver(asoc_mmp_sspa_driver);
+
+MODULE_AUTHOR("Leo Yan <leoy@marvell.com>");
+MODULE_DESCRIPTION("MMP SSPA SoC Interface");
+MODULE_LICENSE("GPL");

+ 92 - 0
sound/soc/pxa/mmp-sspa.h

@@ -0,0 +1,92 @@
+/*
+ * linux/sound/soc/pxa/mmp-sspa.h
+ *
+ * Copyright (C) 2011 Marvell International Ltd.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#ifndef _MMP_SSPA_H
+#define _MMP_SSPA_H
+
+/*
+ * SSPA Registers
+ */
+#define SSPA_RXD		(0x00)
+#define SSPA_RXID		(0x04)
+#define SSPA_RXCTL		(0x08)
+#define SSPA_RXSP		(0x0c)
+#define SSPA_RXFIFO_UL		(0x10)
+#define SSPA_RXINT_MASK		(0x14)
+#define SSPA_RXC		(0x18)
+#define SSPA_RXFIFO_NOFS	(0x1c)
+#define SSPA_RXFIFO_SIZE	(0x20)
+
+#define SSPA_TXD		(0x80)
+#define SSPA_TXID		(0x84)
+#define SSPA_TXCTL		(0x88)
+#define SSPA_TXSP		(0x8c)
+#define SSPA_TXFIFO_LL		(0x90)
+#define SSPA_TXINT_MASK		(0x94)
+#define SSPA_TXC		(0x98)
+#define SSPA_TXFIFO_NOFS	(0x9c)
+#define SSPA_TXFIFO_SIZE	(0xa0)
+
+/* SSPA Control Register */
+#define	SSPA_CTL_XPH		(1 << 31)	/* Read Phase */
+#define	SSPA_CTL_XFIG		(1 << 15)	/* Transmit Zeros when FIFO Empty */
+#define	SSPA_CTL_JST		(1 << 3)	/* Audio Sample Justification */
+#define	SSPA_CTL_XFRLEN2_MASK	(7 << 24)
+#define	SSPA_CTL_XFRLEN2(x)	((x) << 24)	/* Transmit Frame Length in Phase 2 */
+#define	SSPA_CTL_XWDLEN2_MASK	(7 << 21)
+#define	SSPA_CTL_XWDLEN2(x)	((x) << 21)	/* Transmit Word Length in Phase 2 */
+#define	SSPA_CTL_XDATDLY(x)	((x) << 19)	/* Tansmit Data Delay */
+#define	SSPA_CTL_XSSZ2_MASK	(7 << 16)
+#define	SSPA_CTL_XSSZ2(x)	((x) << 16)	/* Transmit Sample Audio Size */
+#define	SSPA_CTL_XFRLEN1_MASK	(7 << 8)
+#define	SSPA_CTL_XFRLEN1(x)	((x) << 8)	/* Transmit Frame Length in Phase 1 */
+#define	SSPA_CTL_XWDLEN1_MASK	(7 << 5)
+#define	SSPA_CTL_XWDLEN1(x)	((x) << 5)	/* Transmit Word Length in Phase 1 */
+#define	SSPA_CTL_XSSZ1_MASK	(7 << 0)
+#define	SSPA_CTL_XSSZ1(x)	((x) << 0)	/* XSSZ1 */
+
+#define SSPA_CTL_8_BITS		(0x0)		/* Sample Size */
+#define SSPA_CTL_12_BITS	(0x1)
+#define SSPA_CTL_16_BITS	(0x2)
+#define SSPA_CTL_20_BITS	(0x3)
+#define SSPA_CTL_24_BITS	(0x4)
+#define SSPA_CTL_32_BITS	(0x5)
+
+/* SSPA Serial Port Register */
+#define	SSPA_SP_WEN		(1 << 31)	/* Write Configuration Enable */
+#define	SSPA_SP_MSL		(1 << 18)	/* Master Slave Configuration */
+#define	SSPA_SP_CLKP		(1 << 17)	/* CLKP Polarity Clock Edge Select */
+#define	SSPA_SP_FSP		(1 << 16)	/* FSP Polarity Clock Edge Select */
+#define	SSPA_SP_FFLUSH		(1 << 2)	/* FIFO Flush */
+#define	SSPA_SP_S_RST		(1 << 1)	/* Active High Reset Signal */
+#define	SSPA_SP_S_EN		(1 << 0)	/* Serial Clock Domain Enable */
+#define	SSPA_SP_FWID(x)		((x) << 20)	/* Frame-Sync Width */
+#define	SSPA_TXSP_FPER(x)	((x) << 4)	/* Frame-Sync Active */
+
+/* sspa clock sources */
+#define MMP_SSPA_CLK_PLL	0
+#define MMP_SSPA_CLK_VCXO	1
+#define MMP_SSPA_CLK_AUDIO	3
+
+/* sspa pll id */
+#define MMP_SYSCLK		0
+#define MMP_SSPA_CLK		1
+
+#endif /* _MMP_SSPA_H */

+ 173 - 0
sound/soc/pxa/ttc-dkb.c

@@ -0,0 +1,173 @@
+/*
+ * linux/sound/soc/pxa/ttc_dkb.c
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <asm/mach-types.h>
+#include <sound/pcm_params.h>
+#include "../codecs/88pm860x-codec.h"
+
+static struct snd_soc_jack hs_jack, mic_jack;
+
+static struct snd_soc_jack_pin hs_jack_pins[] = {
+	{ .pin = "Headset Stereophone",	.mask = SND_JACK_HEADPHONE, },
+};
+
+static struct snd_soc_jack_pin mic_jack_pins[] = {
+	{ .pin = "Headset Mic 2",	.mask = SND_JACK_MICROPHONE, },
+};
+
+/* ttc machine dapm widgets */
+static const struct snd_soc_dapm_widget ttc_dapm_widgets[] = {
+	SND_SOC_DAPM_HP("Headset Stereophone", NULL),
+	SND_SOC_DAPM_LINE("Lineout Out 1", NULL),
+	SND_SOC_DAPM_LINE("Lineout Out 2", NULL),
+	SND_SOC_DAPM_SPK("Ext Speaker", NULL),
+	SND_SOC_DAPM_MIC("Ext Mic 1", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic 2", NULL),
+	SND_SOC_DAPM_MIC("Ext Mic 3", NULL),
+};
+
+/* ttc machine audio map */
+static const struct snd_soc_dapm_route ttc_audio_map[] = {
+	{"Headset Stereophone", NULL, "HS1"},
+	{"Headset Stereophone", NULL, "HS2"},
+
+	{"Ext Speaker", NULL, "LSP"},
+	{"Ext Speaker", NULL, "LSN"},
+
+	{"Lineout Out 1", NULL, "LINEOUT1"},
+	{"Lineout Out 2", NULL, "LINEOUT2"},
+
+	{"MIC1P", NULL, "Mic1 Bias"},
+	{"MIC1N", NULL, "Mic1 Bias"},
+	{"Mic1 Bias", NULL, "Ext Mic 1"},
+
+	{"MIC2P", NULL, "Mic1 Bias"},
+	{"MIC2N", NULL, "Mic1 Bias"},
+	{"Mic1 Bias", NULL, "Headset Mic 2"},
+
+	{"MIC3P", NULL, "Mic3 Bias"},
+	{"MIC3N", NULL, "Mic3 Bias"},
+	{"Mic3 Bias", NULL, "Ext Mic 3"},
+};
+
+static int ttc_pm860x_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+	/* connected pins */
+	snd_soc_dapm_enable_pin(dapm, "Ext Speaker");
+	snd_soc_dapm_enable_pin(dapm, "Ext Mic 1");
+	snd_soc_dapm_enable_pin(dapm, "Ext Mic 3");
+	snd_soc_dapm_disable_pin(dapm, "Headset Mic 2");
+	snd_soc_dapm_disable_pin(dapm, "Headset Stereophone");
+
+	/* Headset jack detection */
+	snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE
+			| SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2,
+			&hs_jack);
+	snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
+			      hs_jack_pins);
+	snd_soc_jack_new(codec, "Microphone Jack", SND_JACK_MICROPHONE,
+			 &mic_jack);
+	snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins),
+			      mic_jack_pins);
+
+	/* headphone, microphone detection & headset short detection */
+	pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE,
+			      SND_JACK_BTN_0, SND_JACK_BTN_1, SND_JACK_BTN_2);
+	pm860x_mic_jack_detect(codec, &hs_jack, SND_JACK_MICROPHONE);
+
+	return 0;
+}
+
+/* ttc/td-dkb digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link ttc_pm860x_hifi_dai[] = {
+{
+	 .name = "88pm860x i2s",
+	 .stream_name = "audio playback",
+	 .codec_name = "88pm860x-codec",
+	 .platform_name = "mmp-pcm-audio",
+	 .cpu_dai_name = "pxa-ssp-dai.1",
+	 .codec_dai_name = "88pm860x-i2s",
+	 .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+			SND_SOC_DAIFMT_CBM_CFM,
+	 .init = ttc_pm860x_init,
+},
+};
+
+/* ttc/td audio machine driver */
+static struct snd_soc_card ttc_dkb_card = {
+	.name = "ttc-dkb-hifi",
+	.dai_link = ttc_pm860x_hifi_dai,
+	.num_links = ARRAY_SIZE(ttc_pm860x_hifi_dai),
+
+	.dapm_widgets = ttc_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(ttc_dapm_widgets),
+	.dapm_routes = ttc_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(ttc_audio_map),
+};
+
+static int __devinit ttc_dkb_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = &ttc_dkb_card;
+	int ret;
+
+	card->dev = &pdev->dev;
+
+	ret = snd_soc_register_card(card);
+	if (ret)
+		dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+			ret);
+
+	return ret;
+}
+
+static int __devexit ttc_dkb_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(card);
+
+	return 0;
+}
+
+static struct platform_driver ttc_dkb_driver = {
+	.driver		= {
+		.name	= "ttc-dkb-audio",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ttc_dkb_probe,
+	.remove		= __devexit_p(ttc_dkb_remove),
+};
+
+module_platform_driver(ttc_dkb_driver);
+
+/* Module information */
+MODULE_AUTHOR("Qiao Zhou, <zhouqiao@marvell.com>");
+MODULE_DESCRIPTION("ALSA SoC TTC DKB");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ttc-dkb-audio");

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

@@ -211,6 +211,11 @@ static int bbclk_ev(struct snd_soc_dapm_widget *w,
 	return 0;
 	return 0;
 }
 }
 
 
+static const struct snd_kcontrol_new controls[] = {
+	SOC_DAPM_PIN_SWITCH("WM1250 Input"),
+	SOC_DAPM_PIN_SWITCH("WM1250 Output"),
+};
+
 static struct snd_soc_dapm_widget widgets[] = {
 static struct snd_soc_dapm_widget widgets[] = {
 	SND_SOC_DAPM_HP("Headphone", NULL),
 	SND_SOC_DAPM_HP("Headphone", NULL),
 
 
@@ -282,6 +287,8 @@ static struct snd_soc_card littlemill = {
 	.set_bias_level = littlemill_set_bias_level,
 	.set_bias_level = littlemill_set_bias_level,
 	.set_bias_level_post = littlemill_set_bias_level_post,
 	.set_bias_level_post = littlemill_set_bias_level_post,
 
 
+	.controls = controls,
+	.num_controls = ARRAY_SIZE(controls),
 	.dapm_widgets = widgets,
 	.dapm_widgets = widgets,
 	.num_dapm_widgets = ARRAY_SIZE(widgets),
 	.num_dapm_widgets = ARRAY_SIZE(widgets),
 	.dapm_routes = audio_paths,
 	.dapm_routes = audio_paths,

+ 3 - 7
sound/soc/samsung/s3c2412-i2s.c

@@ -25,7 +25,6 @@
 #include <sound/soc.h>
 #include <sound/soc.h>
 #include <sound/pcm_params.h>
 #include <sound/pcm_params.h>
 
 
-#include <mach/regs-gpio.h>
 #include <mach/dma.h>
 #include <mach/dma.h>
 
 
 #include "dma.h"
 #include "dma.h"
@@ -83,12 +82,9 @@ static int s3c2412_i2s_probe(struct snd_soc_dai *dai)
 
 
 	s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk;
 	s3c2412_i2s.iis_cclk = s3c2412_i2s.iis_pclk;
 
 
-	/* Configure the I2S pins in correct mode */
-	s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
-	s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
-	s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
-	s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
-	s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
+	/* Configure the I2S pins (GPE0...GPE4) in correct mode */
+	s3c_gpio_cfgall_range(S3C2410_GPE(0), 5, S3C_GPIO_SFN(2),
+			      S3C_GPIO_PULL_NONE);
 
 
 	return 0;
 	return 0;
 }
 }

+ 3 - 7
sound/soc/samsung/s3c24xx-i2s.c

@@ -23,7 +23,6 @@
 #include <sound/soc.h>
 #include <sound/soc.h>
 #include <sound/pcm_params.h>
 #include <sound/pcm_params.h>
 
 
-#include <mach/regs-gpio.h>
 #include <mach/dma.h>
 #include <mach/dma.h>
 #include <plat/regs-iis.h>
 #include <plat/regs-iis.h>
 
 
@@ -391,12 +390,9 @@ static int s3c24xx_i2s_probe(struct snd_soc_dai *dai)
 	}
 	}
 	clk_enable(s3c24xx_i2s.iis_clk);
 	clk_enable(s3c24xx_i2s.iis_clk);
 
 
-	/* Configure the I2S pins in correct mode */
-	s3c2410_gpio_cfgpin(S3C2410_GPE0, S3C2410_GPE0_I2SLRCK);
-	s3c2410_gpio_cfgpin(S3C2410_GPE1, S3C2410_GPE1_I2SSCLK);
-	s3c2410_gpio_cfgpin(S3C2410_GPE2, S3C2410_GPE2_CDCLK);
-	s3c2410_gpio_cfgpin(S3C2410_GPE3, S3C2410_GPE3_I2SSDI);
-	s3c2410_gpio_cfgpin(S3C2410_GPE4, S3C2410_GPE4_I2SSDO);
+	/* Configure the I2S pins (GPE0...GPE4) in correct mode */
+	s3c_gpio_cfgall_range(S3C2410_GPE(0), 5, S3C_GPIO_SFN(2),
+			      S3C_GPIO_PULL_NONE);
 
 
 	writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
 	writel(S3C2410_IISCON_IISEN, s3c24xx_i2s.regs + S3C2410_IISCON);
 
 

+ 23 - 13
sound/soc/samsung/smdk_wm8994.c

@@ -149,31 +149,41 @@ static struct snd_soc_card smdk = {
 	.num_links = ARRAY_SIZE(smdk_dai),
 	.num_links = ARRAY_SIZE(smdk_dai),
 };
 };
 
 
-static struct platform_device *smdk_snd_device;
 
 
-static int __init smdk_audio_init(void)
+static int __devinit smdk_audio_probe(struct platform_device *pdev)
 {
 {
 	int ret;
 	int ret;
+	struct snd_soc_card *card = &smdk;
 
 
-	smdk_snd_device = platform_device_alloc("soc-audio", -1);
-	if (!smdk_snd_device)
-		return -ENOMEM;
+	card->dev = &pdev->dev;
+	ret = snd_soc_register_card(card);
 
 
-	platform_set_drvdata(smdk_snd_device, &smdk);
-
-	ret = platform_device_add(smdk_snd_device);
 	if (ret)
 	if (ret)
-		platform_device_put(smdk_snd_device);
+		dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
 
 
 	return ret;
 	return ret;
 }
 }
-module_init(smdk_audio_init);
 
 
-static void __exit smdk_audio_exit(void)
+static int __devexit smdk_audio_remove(struct platform_device *pdev)
 {
 {
-	platform_device_unregister(smdk_snd_device);
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+	snd_soc_unregister_card(card);
+
+	return 0;
 }
 }
-module_exit(smdk_audio_exit);
+
+static struct platform_driver smdk_audio_driver = {
+	.driver		= {
+		.name	= "smdk-audio",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= smdk_audio_probe,
+	.remove		= __devexit_p(smdk_audio_remove),
+};
+
+module_platform_driver(smdk_audio_driver);
 
 
 MODULE_DESCRIPTION("ALSA SoC SMDK WM8994");
 MODULE_DESCRIPTION("ALSA SoC SMDK WM8994");
 MODULE_LICENSE("GPL");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:smdk-audio");

+ 24 - 24
sound/soc/sh/fsi.c

@@ -247,7 +247,7 @@ struct fsi_priv {
 struct fsi_stream_handler {
 struct fsi_stream_handler {
 	int (*init)(struct fsi_priv *fsi, struct fsi_stream *io);
 	int (*init)(struct fsi_priv *fsi, struct fsi_stream *io);
 	int (*quit)(struct fsi_priv *fsi, struct fsi_stream *io);
 	int (*quit)(struct fsi_priv *fsi, struct fsi_stream *io);
-	int (*probe)(struct fsi_priv *fsi, struct fsi_stream *io);
+	int (*probe)(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev);
 	int (*transfer)(struct fsi_priv *fsi, struct fsi_stream *io);
 	int (*transfer)(struct fsi_priv *fsi, struct fsi_stream *io);
 	int (*remove)(struct fsi_priv *fsi, struct fsi_stream *io);
 	int (*remove)(struct fsi_priv *fsi, struct fsi_stream *io);
 	void (*start_stop)(struct fsi_priv *fsi, struct fsi_stream *io,
 	void (*start_stop)(struct fsi_priv *fsi, struct fsi_stream *io,
@@ -571,16 +571,16 @@ static int fsi_stream_transfer(struct fsi_stream *io)
 #define fsi_stream_stop(fsi, io)\
 #define fsi_stream_stop(fsi, io)\
 	fsi_stream_handler_call(io, start_stop, fsi, io, 0)
 	fsi_stream_handler_call(io, start_stop, fsi, io, 0)
 
 
-static int fsi_stream_probe(struct fsi_priv *fsi)
+static int fsi_stream_probe(struct fsi_priv *fsi, struct device *dev)
 {
 {
 	struct fsi_stream *io;
 	struct fsi_stream *io;
 	int ret1, ret2;
 	int ret1, ret2;
 
 
 	io = &fsi->playback;
 	io = &fsi->playback;
-	ret1 = fsi_stream_handler_call(io, probe, fsi, io);
+	ret1 = fsi_stream_handler_call(io, probe, fsi, io, dev);
 
 
 	io = &fsi->capture;
 	io = &fsi->capture;
-	ret2 = fsi_stream_handler_call(io, probe, fsi, io);
+	ret2 = fsi_stream_handler_call(io, probe, fsi, io, dev);
 
 
 	if (ret1 < 0)
 	if (ret1 < 0)
 		return ret1;
 		return ret1;
@@ -1089,13 +1089,10 @@ static void fsi_dma_do_tasklet(unsigned long data)
 {
 {
 	struct fsi_stream *io = (struct fsi_stream *)data;
 	struct fsi_stream *io = (struct fsi_stream *)data;
 	struct fsi_priv *fsi = fsi_stream_to_priv(io);
 	struct fsi_priv *fsi = fsi_stream_to_priv(io);
-	struct dma_chan *chan;
 	struct snd_soc_dai *dai;
 	struct snd_soc_dai *dai;
 	struct dma_async_tx_descriptor *desc;
 	struct dma_async_tx_descriptor *desc;
-	struct scatterlist sg;
 	struct snd_pcm_runtime *runtime;
 	struct snd_pcm_runtime *runtime;
 	enum dma_data_direction dir;
 	enum dma_data_direction dir;
-	dma_cookie_t cookie;
 	int is_play = fsi_stream_is_play(fsi, io);
 	int is_play = fsi_stream_is_play(fsi, io);
 	int len;
 	int len;
 	dma_addr_t buf;
 	dma_addr_t buf;
@@ -1104,7 +1101,6 @@ static void fsi_dma_do_tasklet(unsigned long data)
 		return;
 		return;
 
 
 	dai	= fsi_get_dai(io->substream);
 	dai	= fsi_get_dai(io->substream);
-	chan	= io->chan;
 	runtime	= io->substream->runtime;
 	runtime	= io->substream->runtime;
 	dir	= is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
 	dir	= is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
 	len	= samples_to_bytes(runtime, io->period_samples);
 	len	= samples_to_bytes(runtime, io->period_samples);
@@ -1112,14 +1108,8 @@ static void fsi_dma_do_tasklet(unsigned long data)
 
 
 	dma_sync_single_for_device(dai->dev, buf, len, dir);
 	dma_sync_single_for_device(dai->dev, buf, len, dir);
 
 
-	sg_init_table(&sg, 1);
-	sg_set_page(&sg, pfn_to_page(PFN_DOWN(buf)),
-		    len , offset_in_page(buf));
-	sg_dma_address(&sg) = buf;
-	sg_dma_len(&sg) = len;
-
-	desc = dmaengine_prep_slave_sg(chan, &sg, 1, dir,
-				       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	desc = dmaengine_prep_slave_single(io->chan, buf, len, dir,
+					   DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
 	if (!desc) {
 	if (!desc) {
 		dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n");
 		dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n");
 		return;
 		return;
@@ -1128,13 +1118,12 @@ static void fsi_dma_do_tasklet(unsigned long data)
 	desc->callback		= fsi_dma_complete;
 	desc->callback		= fsi_dma_complete;
 	desc->callback_param	= io;
 	desc->callback_param	= io;
 
 
-	cookie = desc->tx_submit(desc);
-	if (cookie < 0) {
+	if (dmaengine_submit(desc) < 0) {
 		dev_err(dai->dev, "tx_submit() fail\n");
 		dev_err(dai->dev, "tx_submit() fail\n");
 		return;
 		return;
 	}
 	}
 
 
-	dma_async_issue_pending(chan);
+	dma_async_issue_pending(io->chan);
 
 
 	/*
 	/*
 	 * FIXME
 	 * FIXME
@@ -1184,7 +1173,7 @@ static void fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
 		fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
 		fsi_master_mask_set(master, CLK_RST, clk, (enable) ? clk : 0);
 }
 }
 
 
-static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io)
+static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev)
 {
 {
 	dma_cap_mask_t mask;
 	dma_cap_mask_t mask;
 
 
@@ -1192,8 +1181,19 @@ static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io)
 	dma_cap_set(DMA_SLAVE, mask);
 	dma_cap_set(DMA_SLAVE, mask);
 
 
 	io->chan = dma_request_channel(mask, fsi_dma_filter, &io->slave);
 	io->chan = dma_request_channel(mask, fsi_dma_filter, &io->slave);
-	if (!io->chan)
-		return -EIO;
+	if (!io->chan) {
+
+		/* switch to PIO handler */
+		if (fsi_stream_is_play(fsi, io))
+			fsi->playback.handler	= &fsi_pio_push_handler;
+		else
+			fsi->capture.handler	= &fsi_pio_pop_handler;
+
+		dev_info(dev, "switch handler (dma => pio)\n");
+
+		/* probe again */
+		return fsi_stream_probe(fsi, dev);
+	}
 
 
 	tasklet_init(&io->tasklet, fsi_dma_do_tasklet, (unsigned long)io);
 	tasklet_init(&io->tasklet, fsi_dma_do_tasklet, (unsigned long)io);
 
 
@@ -1683,7 +1683,7 @@ static int fsi_probe(struct platform_device *pdev)
 	master->fsia.master	= master;
 	master->fsia.master	= master;
 	master->fsia.info	= &info->port_a;
 	master->fsia.info	= &info->port_a;
 	fsi_handler_init(&master->fsia);
 	fsi_handler_init(&master->fsia);
-	ret = fsi_stream_probe(&master->fsia);
+	ret = fsi_stream_probe(&master->fsia, &pdev->dev);
 	if (ret < 0) {
 	if (ret < 0) {
 		dev_err(&pdev->dev, "FSIA stream probe failed\n");
 		dev_err(&pdev->dev, "FSIA stream probe failed\n");
 		goto exit_iounmap;
 		goto exit_iounmap;
@@ -1694,7 +1694,7 @@ static int fsi_probe(struct platform_device *pdev)
 	master->fsib.master	= master;
 	master->fsib.master	= master;
 	master->fsib.info	= &info->port_b;
 	master->fsib.info	= &info->port_b;
 	fsi_handler_init(&master->fsib);
 	fsi_handler_init(&master->fsib);
-	ret = fsi_stream_probe(&master->fsib);
+	ret = fsi_stream_probe(&master->fsib, &pdev->dev);
 	if (ret < 0) {
 	if (ret < 0) {
 		dev_err(&pdev->dev, "FSIB stream probe failed\n");
 		dev_err(&pdev->dev, "FSIB stream probe failed\n");
 		goto exit_fsia;
 		goto exit_fsia;

+ 264 - 64
sound/soc/soc-core.c

@@ -812,13 +812,15 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
 
 
 	/* Find CPU DAI from registered DAIs*/
 	/* Find CPU DAI from registered DAIs*/
 	list_for_each_entry(cpu_dai, &dai_list, list) {
 	list_for_each_entry(cpu_dai, &dai_list, list) {
-		if (dai_link->cpu_dai_of_node) {
-			if (cpu_dai->dev->of_node != dai_link->cpu_dai_of_node)
-				continue;
-		} else {
-			if (strcmp(cpu_dai->name, dai_link->cpu_dai_name))
-				continue;
-		}
+		if (dai_link->cpu_of_node &&
+		    (cpu_dai->dev->of_node != dai_link->cpu_of_node))
+			continue;
+		if (dai_link->cpu_name &&
+		    strcmp(dev_name(cpu_dai->dev), dai_link->cpu_name))
+			continue;
+		if (dai_link->cpu_dai_name &&
+		    strcmp(cpu_dai->name, dai_link->cpu_dai_name))
+			continue;
 
 
 		rtd->cpu_dai = cpu_dai;
 		rtd->cpu_dai = cpu_dai;
 	}
 	}
@@ -896,6 +898,28 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
 	return 0;
 	return 0;
 }
 }
 
 
+static int soc_remove_platform(struct snd_soc_platform *platform)
+{
+	int ret;
+
+	if (platform->driver->remove) {
+		ret = platform->driver->remove(platform);
+		if (ret < 0)
+			pr_err("asoc: failed to remove %s: %d\n",
+				platform->name, ret);
+	}
+
+	/* Make sure all DAPM widgets are freed */
+	snd_soc_dapm_free(&platform->dapm);
+
+	soc_cleanup_platform_debugfs(platform);
+	platform->probed = 0;
+	list_del(&platform->card_list);
+	module_put(platform->dev->driver->owner);
+
+	return 0;
+}
+
 static void soc_remove_codec(struct snd_soc_codec *codec)
 static void soc_remove_codec(struct snd_soc_codec *codec)
 {
 {
 	int err;
 	int err;
@@ -917,11 +941,9 @@ static void soc_remove_codec(struct snd_soc_codec *codec)
 	module_put(codec->dev->driver->owner);
 	module_put(codec->dev->driver->owner);
 }
 }
 
 
-static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
+static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
 {
 {
 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
-	struct snd_soc_codec *codec = rtd->codec;
-	struct snd_soc_platform *platform = rtd->platform;
 	struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
 	struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
 	int err;
 	int err;
 
 
@@ -946,30 +968,6 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
 		list_del(&codec_dai->card_list);
 		list_del(&codec_dai->card_list);
 	}
 	}
 
 
-	/* remove the platform */
-	if (platform && platform->probed &&
-			platform->driver->remove_order == order) {
-		if (platform->driver->remove) {
-			err = platform->driver->remove(platform);
-			if (err < 0)
-				pr_err("asoc: failed to remove %s: %d\n",
-							platform->name, err);
-		}
-
-		/* Make sure all DAPM widgets are freed */
-		snd_soc_dapm_free(&platform->dapm);
-
-		soc_cleanup_platform_debugfs(platform);
-		platform->probed = 0;
-		list_del(&platform->card_list);
-		module_put(platform->dev->driver->owner);
-	}
-
-	/* remove the CODEC */
-	if (codec && codec->probed &&
-			codec->driver->remove_order == order)
-		soc_remove_codec(codec);
-
 	/* remove the cpu_dai */
 	/* remove the cpu_dai */
 	if (cpu_dai && cpu_dai->probed &&
 	if (cpu_dai && cpu_dai->probed &&
 			cpu_dai->driver->remove_order == order) {
 			cpu_dai->driver->remove_order == order) {
@@ -981,7 +979,43 @@ static void soc_remove_dai_link(struct snd_soc_card *card, int num, int order)
 		}
 		}
 		cpu_dai->probed = 0;
 		cpu_dai->probed = 0;
 		list_del(&cpu_dai->card_list);
 		list_del(&cpu_dai->card_list);
-		module_put(cpu_dai->dev->driver->owner);
+
+		if (!cpu_dai->codec) {
+			snd_soc_dapm_free(&cpu_dai->dapm);
+			module_put(cpu_dai->dev->driver->owner);
+		}
+	}
+}
+
+static void soc_remove_link_components(struct snd_soc_card *card, int num,
+				       int order)
+{
+	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_platform *platform = rtd->platform;
+	struct snd_soc_codec *codec;
+
+	/* remove the platform */
+	if (platform && platform->probed &&
+	    platform->driver->remove_order == order) {
+		soc_remove_platform(platform);
+	}
+
+	/* remove the CODEC-side CODEC */
+	if (codec_dai) {
+		codec = codec_dai->codec;
+		if (codec && codec->probed &&
+		    codec->driver->remove_order == order)
+			soc_remove_codec(codec);
+	}
+
+	/* remove any CPU-side CODEC */
+	if (cpu_dai) {
+		codec = cpu_dai->codec;
+		if (codec && codec->probed &&
+		    codec->driver->remove_order == order)
+			soc_remove_codec(codec);
 	}
 	}
 }
 }
 
 
@@ -992,8 +1026,15 @@ static void soc_remove_dai_links(struct snd_soc_card *card)
 	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
 	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
 			order++) {
 			order++) {
 		for (dai = 0; dai < card->num_rtd; dai++)
 		for (dai = 0; dai < card->num_rtd; dai++)
-			soc_remove_dai_link(card, dai, order);
+			soc_remove_link_dais(card, dai, order);
 	}
 	}
+
+	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+			order++) {
+		for (dai = 0; dai < card->num_rtd; dai++)
+			soc_remove_link_components(card, dai, order);
+	}
+
 	card->num_rtd = 0;
 	card->num_rtd = 0;
 }
 }
 
 
@@ -1054,6 +1095,10 @@ static int soc_probe_codec(struct snd_soc_card *card,
 		}
 		}
 	}
 	}
 
 
+	/* If the driver didn't set I/O up try regmap */
+	if (!codec->control_data)
+		snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
+
 	if (driver->controls)
 	if (driver->controls)
 		snd_soc_add_codec_controls(codec, driver->controls,
 		snd_soc_add_codec_controls(codec, driver->controls,
 				     driver->num_controls);
 				     driver->num_controls);
@@ -1230,7 +1275,44 @@ out:
 	return 0;
 	return 0;
 }
 }
 
 
-static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
+static int soc_probe_link_components(struct snd_soc_card *card, int num,
+				     int order)
+{
+	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_platform *platform = rtd->platform;
+	int ret;
+
+	/* probe the CPU-side component, if it is a CODEC */
+	if (cpu_dai->codec &&
+	    !cpu_dai->codec->probed &&
+	    cpu_dai->codec->driver->probe_order == order) {
+		ret = soc_probe_codec(card, cpu_dai->codec);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* probe the CODEC-side component */
+	if (!codec_dai->codec->probed &&
+	    codec_dai->codec->driver->probe_order == order) {
+		ret = soc_probe_codec(card, codec_dai->codec);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* probe the platform */
+	if (!platform->probed &&
+	    platform->driver->probe_order == order) {
+		ret = soc_probe_platform(card, platform);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
 {
 {
 	struct snd_soc_dai_link *dai_link = &card->dai_link[num];
 	struct snd_soc_dai_link *dai_link = &card->dai_link[num];
 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
 	struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
@@ -1255,11 +1337,14 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
 	/* probe the cpu_dai */
 	/* probe the cpu_dai */
 	if (!cpu_dai->probed &&
 	if (!cpu_dai->probed &&
 			cpu_dai->driver->probe_order == order) {
 			cpu_dai->driver->probe_order == order) {
-		cpu_dai->dapm.card = card;
-		if (!try_module_get(cpu_dai->dev->driver->owner))
-			return -ENODEV;
+		if (!cpu_dai->codec) {
+			cpu_dai->dapm.card = card;
+			if (!try_module_get(cpu_dai->dev->driver->owner))
+				return -ENODEV;
 
 
-		snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai);
+			list_add(&cpu_dai->dapm.list, &card->dapm_list);
+			snd_soc_dapm_new_dai_widgets(&cpu_dai->dapm, cpu_dai);
+		}
 
 
 		if (cpu_dai->driver->probe) {
 		if (cpu_dai->driver->probe) {
 			ret = cpu_dai->driver->probe(cpu_dai);
 			ret = cpu_dai->driver->probe(cpu_dai);
@@ -1275,22 +1360,6 @@ static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
 		list_add(&cpu_dai->card_list, &card->dai_dev_list);
 		list_add(&cpu_dai->card_list, &card->dai_dev_list);
 	}
 	}
 
 
-	/* probe the CODEC */
-	if (!codec->probed &&
-			codec->driver->probe_order == order) {
-		ret = soc_probe_codec(card, codec);
-		if (ret < 0)
-			return ret;
-	}
-
-	/* probe the platform */
-	if (!platform->probed &&
-			platform->driver->probe_order == order) {
-		ret = soc_probe_platform(card, platform);
-		if (ret < 0)
-			return ret;
-	}
-
 	/* probe the CODEC DAI */
 	/* probe the CODEC DAI */
 	if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
 	if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
 		if (codec_dai->driver->probe) {
 		if (codec_dai->driver->probe) {
@@ -1565,14 +1634,27 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
 			goto card_probe_error;
 			goto card_probe_error;
 	}
 	}
 
 
-	/* early DAI link probe */
+	/* probe all components used by DAI links on this card */
 	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
 	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
 			order++) {
 			order++) {
 		for (i = 0; i < card->num_links; i++) {
 		for (i = 0; i < card->num_links; i++) {
-			ret = soc_probe_dai_link(card, i, order);
+			ret = soc_probe_link_components(card, i, order);
 			if (ret < 0) {
 			if (ret < 0) {
 				pr_err("asoc: failed to instantiate card %s: %d\n",
 				pr_err("asoc: failed to instantiate card %s: %d\n",
-			       card->name, ret);
+				       card->name, ret);
+				goto probe_dai_err;
+			}
+		}
+	}
+
+	/* probe all DAI links on this card */
+	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
+			order++) {
+		for (i = 0; i < card->num_links; i++) {
+			ret = soc_probe_link_dais(card, i, order);
+			if (ret < 0) {
+				pr_err("asoc: failed to instantiate card %s: %d\n",
+				       card->name, ret);
 				goto probe_dai_err;
 				goto probe_dai_err;
 			}
 			}
 		}
 		}
@@ -2789,6 +2871,104 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
 }
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
 
 
+/**
+ * snd_soc_info_volsw_range - single mixer info callback with range.
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information, within a range, about a single
+ * mixer control.
+ *
+ * returns 0 for success.
+ */
+int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	int platform_max;
+	int min = mc->min;
+
+	if (!mc->platform_max)
+		mc->platform_max = mc->max;
+	platform_max = mc->platform_max;
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = platform_max - min;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
+
+/**
+ * snd_soc_put_volsw_range - single mixer put value callback with range.
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value, within a range, for a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	int min = mc->min;
+	int max = mc->max;
+	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
+	unsigned int val, val_mask;
+
+	val = ((ucontrol->value.integer.value[0] + min) & mask);
+	if (invert)
+		val = max - val;
+	val_mask = mask << shift;
+	val = val << shift;
+
+	return snd_soc_update_bits_locked(codec, reg, val_mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
+
+/**
+ * snd_soc_get_volsw_range - single mixer get callback with range
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value, within a range, of a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+	unsigned int reg = mc->reg;
+	unsigned int shift = mc->shift;
+	int min = mc->min;
+	int max = mc->max;
+	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
+
+	ucontrol->value.integer.value[0] =
+		(snd_soc_read(codec, reg) >> shift) & mask;
+	if (invert)
+		ucontrol->value.integer.value[0] =
+			max - ucontrol->value.integer.value[0];
+	ucontrol->value.integer.value[0] =
+		ucontrol->value.integer.value[0] - min;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
+
 /**
 /**
  * snd_soc_limit_volume - Set new limit to an existing volume control.
  * snd_soc_limit_volume - Set new limit to an existing volume control.
  *
  *
@@ -3346,6 +3526,12 @@ int snd_soc_register_card(struct snd_soc_card *card)
 				link->name);
 				link->name);
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
+		/* Codec DAI name must be specified */
+		if (!link->codec_dai_name) {
+			dev_err(card->dev, "codec_dai_name not set for %s\n",
+				link->name);
+			return -EINVAL;
+		}
 
 
 		/*
 		/*
 		 * Platform may be specified by either name or OF node, but
 		 * Platform may be specified by either name or OF node, but
@@ -3358,12 +3544,24 @@ int snd_soc_register_card(struct snd_soc_card *card)
 		}
 		}
 
 
 		/*
 		/*
-		 * CPU DAI must be specified by 1 of name or OF node,
-		 * not both or neither.
+		 * CPU device may be specified by either name or OF node, but
+		 * can be left unspecified, and will be matched based on DAI
+		 * name alone..
+		 */
+		if (link->cpu_name && link->cpu_of_node) {
+			dev_err(card->dev,
+				"Neither/both cpu name/of_node are set for %s\n",
+				link->name);
+			return -EINVAL;
+		}
+		/*
+		 * At least one of CPU DAI name or CPU device name/node must be
+		 * specified
 		 */
 		 */
-		if (!!link->cpu_dai_name == !!link->cpu_dai_of_node) {
+		if (!link->cpu_dai_name &&
+		    !(link->cpu_name || link->cpu_of_node)) {
 			dev_err(card->dev,
 			dev_err(card->dev,
-				"Neither/both cpu_dai name/of_node are set for %s\n",
+				"Neither cpu_dai_name nor cpu_name/of_node are set for %s\n",
 				link->name);
 				link->name);
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
@@ -3938,6 +4136,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
 			dev_err(card->dev,
 			dev_err(card->dev,
 				"Property '%s' index %d could not be read: %d\n",
 				"Property '%s' index %d could not be read: %d\n",
 				propname, 2 * i, ret);
 				propname, 2 * i, ret);
+			kfree(routes);
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
 		ret = of_property_read_string_index(np, propname,
 		ret = of_property_read_string_index(np, propname,
@@ -3946,6 +4145,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
 			dev_err(card->dev,
 			dev_err(card->dev,
 				"Property '%s' index %d could not be read: %d\n",
 				"Property '%s' index %d could not be read: %d\n",
 				propname, (2 * i) + 1, ret);
 				propname, (2 * i) + 1, ret);
+			kfree(routes);
 			return -EINVAL;
 			return -EINVAL;
 		}
 		}
 	}
 	}

+ 143 - 21
sound/soc/soc-dapm.c

@@ -35,6 +35,7 @@
 #include <linux/debugfs.h>
 #include <linux/debugfs.h>
 #include <linux/pm_runtime.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 #include <linux/regulator/consumer.h>
+#include <linux/clk.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm.h>
@@ -51,6 +52,7 @@ static int dapm_up_seq[] = {
 	[snd_soc_dapm_pre] = 0,
 	[snd_soc_dapm_pre] = 0,
 	[snd_soc_dapm_supply] = 1,
 	[snd_soc_dapm_supply] = 1,
 	[snd_soc_dapm_regulator_supply] = 1,
 	[snd_soc_dapm_regulator_supply] = 1,
+	[snd_soc_dapm_clock_supply] = 1,
 	[snd_soc_dapm_micbias] = 2,
 	[snd_soc_dapm_micbias] = 2,
 	[snd_soc_dapm_dai_link] = 2,
 	[snd_soc_dapm_dai_link] = 2,
 	[snd_soc_dapm_dai] = 3,
 	[snd_soc_dapm_dai] = 3,
@@ -92,6 +94,7 @@ static int dapm_down_seq[] = {
 	[snd_soc_dapm_aif_out] = 10,
 	[snd_soc_dapm_aif_out] = 10,
 	[snd_soc_dapm_dai] = 10,
 	[snd_soc_dapm_dai] = 10,
 	[snd_soc_dapm_dai_link] = 11,
 	[snd_soc_dapm_dai_link] = 11,
+	[snd_soc_dapm_clock_supply] = 12,
 	[snd_soc_dapm_regulator_supply] = 12,
 	[snd_soc_dapm_regulator_supply] = 12,
 	[snd_soc_dapm_supply] = 12,
 	[snd_soc_dapm_supply] = 12,
 	[snd_soc_dapm_post] = 13,
 	[snd_soc_dapm_post] = 13,
@@ -391,6 +394,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
 	case snd_soc_dapm_vmid:
 	case snd_soc_dapm_vmid:
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_regulator_supply:
 	case snd_soc_dapm_regulator_supply:
+	case snd_soc_dapm_clock_supply:
 	case snd_soc_dapm_aif_in:
 	case snd_soc_dapm_aif_in:
 	case snd_soc_dapm_aif_out:
 	case snd_soc_dapm_aif_out:
 	case snd_soc_dapm_dai:
 	case snd_soc_dapm_dai:
@@ -764,6 +768,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
 	switch (widget->id) {
 	switch (widget->id) {
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_regulator_supply:
 	case snd_soc_dapm_regulator_supply:
+	case snd_soc_dapm_clock_supply:
 		return 0;
 		return 0;
 	default:
 	default:
 		break;
 		break;
@@ -850,6 +855,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
 	switch (widget->id) {
 	switch (widget->id) {
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_regulator_supply:
 	case snd_soc_dapm_regulator_supply:
+	case snd_soc_dapm_clock_supply:
 		return 0;
 		return 0;
 	default:
 	default:
 		break;
 		break;
@@ -996,6 +1002,27 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
 }
 }
 EXPORT_SYMBOL_GPL(dapm_regulator_event);
 EXPORT_SYMBOL_GPL(dapm_regulator_event);
 
 
+/*
+ * Handler for clock supply widget.
+ */
+int dapm_clock_event(struct snd_soc_dapm_widget *w,
+		   struct snd_kcontrol *kcontrol, int event)
+{
+	if (!w->clk)
+		return -EIO;
+
+#ifdef CONFIG_HAVE_CLK
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		return clk_enable(w->clk);
+	} else {
+		clk_disable(w->clk);
+		return 0;
+	}
+#endif
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dapm_clock_event);
+
 static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
 static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
 {
 {
 	if (w->power_checked)
 	if (w->power_checked)
@@ -1487,6 +1514,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
 	switch (w->id) {
 	switch (w->id) {
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_regulator_supply:
 	case snd_soc_dapm_regulator_supply:
+	case snd_soc_dapm_clock_supply:
 		/* Supplies can't affect their outputs, only their inputs */
 		/* Supplies can't affect their outputs, only their inputs */
 		break;
 		break;
 	default:
 	default:
@@ -1587,6 +1615,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
 				break;
 				break;
 			case snd_soc_dapm_supply:
 			case snd_soc_dapm_supply:
 			case snd_soc_dapm_regulator_supply:
 			case snd_soc_dapm_regulator_supply:
+			case snd_soc_dapm_clock_supply:
 			case snd_soc_dapm_micbias:
 			case snd_soc_dapm_micbias:
 				if (d->target_bias_level < SND_SOC_BIAS_STANDBY)
 				if (d->target_bias_level < SND_SOC_BIAS_STANDBY)
 					d->target_bias_level = SND_SOC_BIAS_STANDBY;
 					d->target_bias_level = SND_SOC_BIAS_STANDBY;
@@ -1941,6 +1970,7 @@ static ssize_t dapm_widget_show(struct device *dev,
 		case snd_soc_dapm_mixer_named_ctl:
 		case snd_soc_dapm_mixer_named_ctl:
 		case snd_soc_dapm_supply:
 		case snd_soc_dapm_supply:
 		case snd_soc_dapm_regulator_supply:
 		case snd_soc_dapm_regulator_supply:
+		case snd_soc_dapm_clock_supply:
 			if (w->name)
 			if (w->name)
 				count += sprintf(buf + count, "%s: %s\n",
 				count += sprintf(buf + count, "%s: %s\n",
 					w->name, w->power ? "On":"Off");
 					w->name, w->power ? "On":"Off");
@@ -2187,6 +2217,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
 	case snd_soc_dapm_post:
 	case snd_soc_dapm_post:
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_regulator_supply:
 	case snd_soc_dapm_regulator_supply:
+	case snd_soc_dapm_clock_supply:
 	case snd_soc_dapm_aif_in:
 	case snd_soc_dapm_aif_in:
 	case snd_soc_dapm_aif_out:
 	case snd_soc_dapm_aif_out:
 	case snd_soc_dapm_dai:
 	case snd_soc_dapm_dai:
@@ -2221,6 +2252,10 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
 		path->connect = 0;
 		path->connect = 0;
 		return 0;
 		return 0;
 	}
 	}
+
+	dapm_mark_dirty(wsource, "Route added");
+	dapm_mark_dirty(wsink, "Route added");
+
 	return 0;
 	return 0;
 
 
 err:
 err:
@@ -2230,6 +2265,59 @@ err:
 	return ret;
 	return ret;
 }
 }
 
 
+static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
+				  const struct snd_soc_dapm_route *route)
+{
+	struct snd_soc_dapm_path *path, *p;
+	const char *sink;
+	const char *source;
+	char prefixed_sink[80];
+	char prefixed_source[80];
+
+	if (route->control) {
+		dev_err(dapm->dev,
+			"Removal of routes with controls not supported\n");
+		return -EINVAL;
+	}
+
+	if (dapm->codec && dapm->codec->name_prefix) {
+		snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
+			 dapm->codec->name_prefix, route->sink);
+		sink = prefixed_sink;
+		snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
+			 dapm->codec->name_prefix, route->source);
+		source = prefixed_source;
+	} else {
+		sink = route->sink;
+		source = route->source;
+	}
+
+	path = NULL;
+	list_for_each_entry(p, &dapm->card->paths, list) {
+		if (strcmp(p->source->name, source) != 0)
+			continue;
+		if (strcmp(p->sink->name, sink) != 0)
+			continue;
+		path = p;
+		break;
+	}
+
+	if (path) {
+		dapm_mark_dirty(path->source, "Route removed");
+		dapm_mark_dirty(path->sink, "Route removed");
+
+		list_del(&path->list);
+		list_del(&path->list_sink);
+		list_del(&path->list_source);
+		kfree(path);
+	} else {
+		dev_warn(dapm->dev, "Route %s->%s does not exist\n",
+			 source, sink);
+	}
+
+	return 0;
+}
+
 /**
 /**
  * snd_soc_dapm_add_routes - Add routes between DAPM widgets
  * snd_soc_dapm_add_routes - Add routes between DAPM widgets
  * @dapm: DAPM context
  * @dapm: DAPM context
@@ -2246,15 +2334,15 @@ err:
 int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 			    const struct snd_soc_dapm_route *route, int num)
 			    const struct snd_soc_dapm_route *route, int num)
 {
 {
-	int i, ret = 0;
+	int i, r, ret = 0;
 
 
 	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
 	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
 	for (i = 0; i < num; i++) {
 	for (i = 0; i < num; i++) {
-		ret = snd_soc_dapm_add_route(dapm, route);
-		if (ret < 0) {
+		r = snd_soc_dapm_add_route(dapm, route);
+		if (r < 0) {
 			dev_err(dapm->dev, "Failed to add route %s->%s\n",
 			dev_err(dapm->dev, "Failed to add route %s->%s\n",
 				route->source, route->sink);
 				route->source, route->sink);
-			break;
+			ret = r;
 		}
 		}
 		route++;
 		route++;
 	}
 	}
@@ -2264,6 +2352,30 @@ int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
 }
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
 EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
 
 
+/**
+ * snd_soc_dapm_del_routes - Remove routes between DAPM widgets
+ * @dapm: DAPM context
+ * @route: audio routes
+ * @num: number of routes
+ *
+ * Removes routes from the DAPM context.
+ */
+int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
+			    const struct snd_soc_dapm_route *route, int num)
+{
+	int i, ret = 0;
+
+	mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+	for (i = 0; i < num; i++) {
+		snd_soc_dapm_del_route(dapm, route);
+		route++;
+	}
+	mutex_unlock(&dapm->card->dapm_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes);
+
 static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,
 static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,
 				   const struct snd_soc_dapm_route *route)
 				   const struct snd_soc_dapm_route *route)
 {
 {
@@ -2434,23 +2546,20 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
 		(struct soc_mixer_control *)kcontrol->private_value;
 		(struct soc_mixer_control *)kcontrol->private_value;
 	unsigned int reg = mc->reg;
 	unsigned int reg = mc->reg;
 	unsigned int shift = mc->shift;
 	unsigned int shift = mc->shift;
-	unsigned int rshift = mc->rshift;
 	int max = mc->max;
 	int max = mc->max;
-	unsigned int invert = mc->invert;
 	unsigned int mask = (1 << fls(max)) - 1;
 	unsigned int mask = (1 << fls(max)) - 1;
+	unsigned int invert = mc->invert;
+
+	if (snd_soc_volsw_is_stereo(mc))
+		dev_warn(widget->dapm->dev,
+			 "Control '%s' is stereo, which is not supported\n",
+			 kcontrol->id.name);
 
 
 	ucontrol->value.integer.value[0] =
 	ucontrol->value.integer.value[0] =
 		(snd_soc_read(widget->codec, reg) >> shift) & mask;
 		(snd_soc_read(widget->codec, reg) >> shift) & mask;
-	if (shift != rshift)
-		ucontrol->value.integer.value[1] =
-			(snd_soc_read(widget->codec, reg) >> rshift) & mask;
-	if (invert) {
+	if (invert)
 		ucontrol->value.integer.value[0] =
 		ucontrol->value.integer.value[0] =
 			max - ucontrol->value.integer.value[0];
 			max - ucontrol->value.integer.value[0];
-		if (shift != rshift)
-			ucontrol->value.integer.value[1] =
-				max - ucontrol->value.integer.value[1];
-	}
 
 
 	return 0;
 	return 0;
 }
 }
@@ -2484,20 +2593,19 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
 	struct snd_soc_dapm_update update;
 	struct snd_soc_dapm_update update;
 	int wi;
 	int wi;
 
 
+	if (snd_soc_volsw_is_stereo(mc))
+		dev_warn(widget->dapm->dev,
+			 "Control '%s' is stereo, which is not supported\n",
+			 kcontrol->id.name);
+
 	val = (ucontrol->value.integer.value[0] & mask);
 	val = (ucontrol->value.integer.value[0] & mask);
+	connect = !!val;
 
 
 	if (invert)
 	if (invert)
 		val = max - val;
 		val = max - val;
 	mask = mask << shift;
 	mask = mask << shift;
 	val = val << shift;
 	val = val << shift;
 
 
-	if (val)
-		/* new connection */
-		connect = invert ? 0 : 1;
-	else
-		/* old connection must be powered down */
-		connect = invert ? 1 : 0;
-
 	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
 
 	change = snd_soc_test_bits(widget->codec, reg, mask, val);
 	change = snd_soc_test_bits(widget->codec, reg, mask, val);
@@ -2873,6 +2981,19 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 			return NULL;
 			return NULL;
 		}
 		}
 		break;
 		break;
+	case snd_soc_dapm_clock_supply:
+#ifdef CONFIG_CLKDEV_LOOKUP
+		w->clk = devm_clk_get(dapm->dev, w->name);
+		if (IS_ERR(w->clk)) {
+			ret = PTR_ERR(w->clk);
+			dev_err(dapm->dev, "Failed to request %s: %d\n",
+				w->name, ret);
+			return NULL;
+		}
+#else
+		return NULL;
+#endif
+		break;
 	default:
 	default:
 		break;
 		break;
 	}
 	}
@@ -2924,6 +3045,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
 		break;
 		break;
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_supply:
 	case snd_soc_dapm_regulator_supply:
 	case snd_soc_dapm_regulator_supply:
+	case snd_soc_dapm_clock_supply:
 		w->power_check = dapm_supply_check_power;
 		w->power_check = dapm_supply_check_power;
 		break;
 		break;
 	case snd_soc_dapm_dai:
 	case snd_soc_dapm_dai:

+ 30 - 3
sound/soc/soc-dmaengine-pcm.c

@@ -30,6 +30,7 @@
 
 
 struct dmaengine_pcm_runtime_data {
 struct dmaengine_pcm_runtime_data {
 	struct dma_chan *dma_chan;
 	struct dma_chan *dma_chan;
+	dma_cookie_t cookie;
 
 
 	unsigned int pos;
 	unsigned int pos;
 
 
@@ -153,7 +154,7 @@ static int dmaengine_pcm_prepare_and_submit(struct snd_pcm_substream *substream)
 
 
 	desc->callback = dmaengine_pcm_dma_complete;
 	desc->callback = dmaengine_pcm_dma_complete;
 	desc->callback_param = substream;
 	desc->callback_param = substream;
-	dmaengine_submit(desc);
+	prtd->cookie = dmaengine_submit(desc);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -199,6 +200,20 @@ int snd_dmaengine_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 }
 }
 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
 
 
+/**
+ * snd_dmaengine_pcm_pointer_no_residue - dmaengine based PCM pointer implementation
+ * @substream: PCM substream
+ *
+ * This function is deprecated and should not be used by new drivers, as its
+ * results may be unreliable.
+ */
+snd_pcm_uframes_t snd_dmaengine_pcm_pointer_no_residue(struct snd_pcm_substream *substream)
+{
+	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
+	return bytes_to_frames(substream->runtime, prtd->pos);
+}
+EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer_no_residue);
+
 /**
 /**
  * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation
  * snd_dmaengine_pcm_pointer - dmaengine based PCM pointer implementation
  * @substream: PCM substream
  * @substream: PCM substream
@@ -209,7 +224,19 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_trigger);
 snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
 snd_pcm_uframes_t snd_dmaengine_pcm_pointer(struct snd_pcm_substream *substream)
 {
 {
 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
 	struct dmaengine_pcm_runtime_data *prtd = substream_to_prtd(substream);
-	return bytes_to_frames(substream->runtime, prtd->pos);
+	struct dma_tx_state state;
+	enum dma_status status;
+	unsigned int buf_size;
+	unsigned int pos = 0;
+
+	status = dmaengine_tx_status(prtd->dma_chan, prtd->cookie, &state);
+	if (status == DMA_IN_PROGRESS || status == DMA_PAUSED) {
+		buf_size = snd_pcm_lib_buffer_bytes(substream);
+		if (state.residue > 0 && state.residue <= buf_size)
+			pos = buf_size - state.residue;
+	}
+
+	return bytes_to_frames(substream->runtime, pos);
 }
 }
 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
 EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_pointer);
 
 
@@ -243,7 +270,7 @@ static int dmaengine_pcm_request_channel(struct dmaengine_pcm_runtime_data *prtd
  * Note that this function will use private_data field of the substream's
  * Note that this function will use private_data field of the substream's
  * runtime. So it is not availabe to your pcm driver implementation. If you need
  * runtime. So it is not availabe to your pcm driver implementation. If you need
  * to keep additional data attached to a substream use
  * to keep additional data attached to a substream use
- * snd_dmaeinge_pcm_{set,get}_data.
+ * snd_dmaengine_pcm_{set,get}_data.
  */
  */
 int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
 int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream,
 	dma_filter_fn filter_fn, void *filter_data)
 	dma_filter_fn filter_fn, void *filter_data)

+ 10 - 5
sound/soc/soc-io.c

@@ -142,11 +142,16 @@ int snd_soc_codec_set_cache_io(struct snd_soc_codec *codec,
 	case SND_SOC_REGMAP:
 	case SND_SOC_REGMAP:
 		/* Device has made its own regmap arrangements */
 		/* Device has made its own regmap arrangements */
 		codec->using_regmap = true;
 		codec->using_regmap = true;
-
-		ret = regmap_get_val_bytes(codec->control_data);
-		/* Errors are legitimate for non-integer byte multiples */
-		if (ret > 0)
-			codec->val_bytes = ret;
+		if (!codec->control_data)
+			codec->control_data = dev_get_regmap(codec->dev, NULL);
+
+		if (codec->control_data) {
+			ret = regmap_get_val_bytes(codec->control_data);
+			/* Errors are legitimate for non-integer byte
+			 * multiples */
+			if (ret > 0)
+				codec->val_bytes = ret;
+		}
 		break;
 		break;
 
 
 	default:
 	default:

+ 5 - 7
sound/soc/soc-pcm.c

@@ -1955,10 +1955,8 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
 	fe->dpcm[stream].runtime = fe_substream->runtime;
 	fe->dpcm[stream].runtime = fe_substream->runtime;
 
 
 	if (dpcm_path_get(fe, stream, &list) <= 0) {
 	if (dpcm_path_get(fe, stream, &list) <= 0) {
-		dev_warn(fe->dev, "asoc: %s no valid %s route\n",
+		dev_dbg(fe->dev, "asoc: %s no valid %s route\n",
 			fe->dai_link->name, stream ? "capture" : "playback");
 			fe->dai_link->name, stream ? "capture" : "playback");
-			mutex_unlock(&fe->card->mutex);
-			return -EINVAL;
 	}
 	}
 
 
 	/* calculate valid and active FE <-> BE dpcms */
 	/* calculate valid and active FE <-> BE dpcms */
@@ -2003,7 +2001,6 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
 /* create a new pcm */
 /* create a new pcm */
 int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
 int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
 {
 {
-	struct snd_soc_codec *codec = rtd->codec;
 	struct snd_soc_platform *platform = rtd->platform;
 	struct snd_soc_platform *platform = rtd->platform;
 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
 	struct snd_soc_dai *codec_dai = rtd->codec_dai;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
@@ -2042,7 +2039,8 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
 			capture, &pcm);
 			capture, &pcm);
 	}
 	}
 	if (ret < 0) {
 	if (ret < 0) {
-		printk(KERN_ERR "asoc: can't create pcm for codec %s\n", codec->name);
+		dev_err(rtd->card->dev, "can't create pcm for %s\n",
+			rtd->dai_link->name);
 		return ret;
 		return ret;
 	}
 	}
 	dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num, new_name);
 	dev_dbg(rtd->card->dev, "registered pcm #%d %s\n",num, new_name);
@@ -2099,14 +2097,14 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
 	if (platform->driver->pcm_new) {
 	if (platform->driver->pcm_new) {
 		ret = platform->driver->pcm_new(rtd);
 		ret = platform->driver->pcm_new(rtd);
 		if (ret < 0) {
 		if (ret < 0) {
-			pr_err("asoc: platform pcm constructor failed\n");
+			dev_err(platform->dev, "pcm constructor failed\n");
 			return ret;
 			return ret;
 		}
 		}
 	}
 	}
 
 
 	pcm->private_free = platform->driver->pcm_free;
 	pcm->private_free = platform->driver->pcm_free;
 out:
 out:
-	printk(KERN_INFO "asoc: %s <-> %s mapping ok\n", codec_dai->name,
+	dev_info(rtd->card->dev, " %s <-> %s mapping ok\n", codec_dai->name,
 		cpu_dai->name);
 		cpu_dai->name);
 	return ret;
 	return ret;
 }
 }

+ 297 - 0
sound/soc/spear/spdif_in.c

@@ -0,0 +1,297 @@
+/*
+ * ALSA SoC SPDIF In Audio Layer for spear processors
+ *
+ * Copyright (C) 2012 ST Microelectronics
+ * Vipin Kumar <vipin.kumar@st.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/spear_dma.h>
+#include <sound/spear_spdif.h>
+#include "spdif_in_regs.h"
+
+struct spdif_in_params {
+	u32 format;
+};
+
+struct spdif_in_dev {
+	struct clk *clk;
+	struct spear_dma_data dma_params;
+	struct spdif_in_params saved_params;
+	void *io_base;
+	struct device *dev;
+	void (*reset_perip)(void);
+	int irq;
+};
+
+static void spdif_in_configure(struct spdif_in_dev *host)
+{
+	u32 ctrl = SPDIF_IN_PRTYEN | SPDIF_IN_STATEN | SPDIF_IN_USREN |
+		SPDIF_IN_VALEN | SPDIF_IN_BLKEN;
+	ctrl |= SPDIF_MODE_16BIT | SPDIF_FIFO_THRES_16;
+
+	writel(ctrl, host->io_base + SPDIF_IN_CTRL);
+	writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK);
+}
+
+static int spdif_in_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *cpu_dai)
+{
+	struct spdif_in_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
+
+	if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
+		return -EINVAL;
+
+	snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&host->dma_params);
+	return 0;
+}
+
+static void spdif_in_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai);
+
+	if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
+		return;
+
+	writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK);
+	snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static void spdif_in_format(struct spdif_in_dev *host, u32 format)
+{
+	u32 ctrl = readl(host->io_base + SPDIF_IN_CTRL);
+
+	switch (format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		ctrl |= SPDIF_XTRACT_16BIT;
+		break;
+
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		ctrl &= ~SPDIF_XTRACT_16BIT;
+		break;
+	}
+
+	writel(ctrl, host->io_base + SPDIF_IN_CTRL);
+}
+
+static int spdif_in_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai);
+	u32 format;
+
+	if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
+		return -EINVAL;
+
+	format = params_format(params);
+	host->saved_params.format = format;
+
+	return 0;
+}
+
+static int spdif_in_trigger(struct snd_pcm_substream *substream, int cmd,
+		struct snd_soc_dai *dai)
+{
+	struct spdif_in_dev *host = snd_soc_dai_get_drvdata(dai);
+	u32 ctrl;
+	int ret = 0;
+
+	if (substream->stream != SNDRV_PCM_STREAM_CAPTURE)
+		return -EINVAL;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		clk_enable(host->clk);
+		spdif_in_configure(host);
+		spdif_in_format(host, host->saved_params.format);
+
+		ctrl = readl(host->io_base + SPDIF_IN_CTRL);
+		ctrl |= SPDIF_IN_SAMPLE | SPDIF_IN_ENB;
+		writel(ctrl, host->io_base + SPDIF_IN_CTRL);
+		writel(0xF, host->io_base + SPDIF_IN_IRQ_MASK);
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		ctrl = readl(host->io_base + SPDIF_IN_CTRL);
+		ctrl &= ~(SPDIF_IN_SAMPLE | SPDIF_IN_ENB);
+		writel(ctrl, host->io_base + SPDIF_IN_CTRL);
+		writel(0x0, host->io_base + SPDIF_IN_IRQ_MASK);
+
+		if (host->reset_perip)
+			host->reset_perip();
+		clk_disable(host->clk);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static struct snd_soc_dai_ops spdif_in_dai_ops = {
+	.startup	= spdif_in_startup,
+	.shutdown	= spdif_in_shutdown,
+	.trigger	= spdif_in_trigger,
+	.hw_params	= spdif_in_hw_params,
+};
+
+struct snd_soc_dai_driver spdif_in_dai = {
+	.capture = {
+		.channels_min = 2,
+		.channels_max = 2,
+		.rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
+				 SNDRV_PCM_RATE_192000),
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | \
+			   SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+	},
+	.ops = &spdif_in_dai_ops,
+};
+
+static irqreturn_t spdif_in_irq(int irq, void *arg)
+{
+	struct spdif_in_dev *host = (struct spdif_in_dev *)arg;
+
+	u32 irq_status = readl(host->io_base + SPDIF_IN_IRQ);
+
+	if (!irq_status)
+		return IRQ_NONE;
+
+	if (irq_status & SPDIF_IRQ_FIFOWRITE)
+		dev_err(host->dev, "spdif in: fifo write error");
+	if (irq_status & SPDIF_IRQ_EMPTYFIFOREAD)
+		dev_err(host->dev, "spdif in: empty fifo read error");
+	if (irq_status & SPDIF_IRQ_FIFOFULL)
+		dev_err(host->dev, "spdif in: fifo full error");
+	if (irq_status & SPDIF_IRQ_OUTOFRANGE)
+		dev_err(host->dev, "spdif in: out of range error");
+
+	writel(0, host->io_base + SPDIF_IN_IRQ);
+
+	return IRQ_HANDLED;
+}
+
+static int spdif_in_probe(struct platform_device *pdev)
+{
+	struct spdif_in_dev *host;
+	struct spear_spdif_platform_data *pdata;
+	struct resource *res, *res_fifo;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+
+	res_fifo = platform_get_resource(pdev, IORESOURCE_IO, 0);
+	if (!res_fifo)
+		return -EINVAL;
+
+	if (!devm_request_mem_region(&pdev->dev, res->start,
+				resource_size(res), pdev->name)) {
+		dev_warn(&pdev->dev, "Failed to get memory resourse\n");
+		return -ENOENT;
+	}
+
+	host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
+	if (!host) {
+		dev_warn(&pdev->dev, "kzalloc fail\n");
+		return -ENOMEM;
+	}
+
+	host->io_base = devm_ioremap(&pdev->dev, res->start,
+				resource_size(res));
+	if (!host->io_base) {
+		dev_warn(&pdev->dev, "ioremap failed\n");
+		return -ENOMEM;
+	}
+
+	host->irq = platform_get_irq(pdev, 0);
+	if (host->irq < 0)
+		return -EINVAL;
+
+	host->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(host->clk))
+		return PTR_ERR(host->clk);
+
+	pdata = dev_get_platdata(&pdev->dev);
+
+	if (!pdata)
+		return -EINVAL;
+
+	host->dma_params.data = pdata->dma_params;
+	host->dma_params.addr = res_fifo->start;
+	host->dma_params.max_burst = 16;
+	host->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	host->dma_params.filter = pdata->filter;
+	host->reset_perip = pdata->reset_perip;
+
+	host->dev = &pdev->dev;
+	dev_set_drvdata(&pdev->dev, host);
+
+	ret = devm_request_irq(&pdev->dev, host->irq, spdif_in_irq, 0,
+			"spdif-in", host);
+	if (ret) {
+		clk_put(host->clk);
+		dev_warn(&pdev->dev, "request_irq failed\n");
+		return ret;
+	}
+
+	ret = snd_soc_register_dai(&pdev->dev, &spdif_in_dai);
+	if (ret != 0) {
+		clk_put(host->clk);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int spdif_in_remove(struct platform_device *pdev)
+{
+	struct spdif_in_dev *host = dev_get_drvdata(&pdev->dev);
+
+	snd_soc_unregister_dai(&pdev->dev);
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	clk_put(host->clk);
+
+	return 0;
+}
+
+
+static struct platform_driver spdif_in_driver = {
+	.probe		= spdif_in_probe,
+	.remove		= spdif_in_remove,
+	.driver		= {
+		.name	= "spdif-in",
+		.owner	= THIS_MODULE,
+	},
+};
+
+module_platform_driver(spdif_in_driver);
+
+MODULE_AUTHOR("Vipin Kumar <vipin.kumar@st.com>");
+MODULE_DESCRIPTION("SPEAr SPDIF IN SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:spdif_in");

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.